summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul-Christian Volkmer2025-11-11 09:36:17 +0100
committerGitHub2025-11-11 09:36:17 +0100
commit8c3143081f5f535671bc7b0c7d98302549b9a1da (patch)
tree1402d033ef7b13ab6befddf2b8d1f385f79d410b
parent76a16e22bb9fba21d02b0584ec83e046196da045 (diff)
chore: use spotless for kotlin code (#191)
-rw-r--r--build.gradle.kts13
-rw-r--r--src/integrationTest/kotlin/dev/dnpm/etl/processor/AbstractTestcontainerTest.kt17
-rw-r--r--src/integrationTest/kotlin/dev/dnpm/etl/processor/EtlProcessorApplicationTests.kt159
-rw-r--r--src/integrationTest/kotlin/dev/dnpm/etl/processor/EtlProcessorArchTest.kt68
-rw-r--r--src/integrationTest/kotlin/dev/dnpm/etl/processor/config/AppConfigurationTest.kt250
-rw-r--r--src/integrationTest/kotlin/dev/dnpm/etl/processor/config/GPasConfigPropertiesTest.kt24
-rw-r--r--src/integrationTest/kotlin/dev/dnpm/etl/processor/helpers.kt9
-rw-r--r--src/integrationTest/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt283
-rw-r--r--src/integrationTest/kotlin/dev/dnpm/etl/processor/monitoring/RequestRepositoryTest.kt35
-rw-r--r--src/integrationTest/kotlin/dev/dnpm/etl/processor/pseudonym/GpasPseudonymGeneratorTest.kt92
-rw-r--r--src/integrationTest/kotlin/dev/dnpm/etl/processor/services/RequestServiceIntegrationTest.kt164
-rw-r--r--src/integrationTest/kotlin/dev/dnpm/etl/processor/web/ConfigControllerTest.kt505
-rw-r--r--src/integrationTest/kotlin/dev/dnpm/etl/processor/web/HomeControllerTest.kt440
-rw-r--r--src/integrationTest/kotlin/dev/dnpm/etl/processor/web/LoginControllerTest.kt34
-rw-r--r--src/integrationTest/kotlin/dev/dnpm/etl/processor/web/StatisticsControllerTest.kt24
-rw-r--r--src/integrationTest/kotlin/dev/dnpm/etl/processor/web/StatisticsRestControllerTest.kt239
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/Exceptions.kt2
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/ServletInitializer.kt7
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/config/AppConfigProperties.kt74
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/config/AppConfiguration.kt435
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/config/AppFhirConfig.kt8
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/config/AppJdbcConfiguration.kt14
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/config/AppKafkaConfiguration.kt34
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/config/AppRestConfiguration.kt20
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/config/AppSecurityConfiguration.kt259
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/config/ConsentResourceDeserializer.kt9
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/config/ConsentResourceSerializer.kt6
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/config/FhirResourceModule.kt3
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/config/JacksonConfig.kt26
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/consent/ConsentEvaluator.kt35
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/input/KafkaInputListener.kt36
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/input/MtbFileRestController.kt52
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/monitoring/ConnectionCheckService.kt428
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/monitoring/ReportService.kt92
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/monitoring/Request.kt133
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/monitoring/RequestStatus.kt8
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/monitoring/RequestType.kt6
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/output/KafkaMtbFileSender.kt39
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/output/MtbFileSender.kt18
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/output/MtbRequest.kt8
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/output/RestDipMtbFileSender.kt16
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/output/RestMtbFileSender.kt41
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/pseudonym/AnonymizingGenerator.kt17
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/pseudonym/GpasSoapPseudonymGenerator.kt14
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/pseudonym/GpasSoapService.kt10
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/pseudonym/PseudonymizeService.kt22
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/pseudonym/extensions.kt456
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/security/TokenService.kt73
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/security/UserRole.kt19
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/security/UserRoleService.kt21
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/services/ConsentProcessor.kt465
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/services/RequestProcessor.kt303
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/services/RequestService.kt66
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/services/ResponseProcessor.kt75
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/services/TransformationService.kt73
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/services/kafka/KafkaResponseProcessor.kt86
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/types.kt29
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/web/ApplicationControllerAdvice.kt8
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/web/ConfigController.kt336
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/web/HomeController.kt21
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/web/LoginController.kt11
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/web/StatisticsController.kt4
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/web/StatisticsRestController.kt210
-rw-r--r--src/test/kotlin/dev/dnpm/etl/processor/consent/ConsentProcessorTest.kt58
-rw-r--r--src/test/kotlin/dev/dnpm/etl/processor/consent/Dnpm21BasedConsentEvaluatorTest.kt461
-rw-r--r--src/test/kotlin/dev/dnpm/etl/processor/helpers.kt7
-rw-r--r--src/test/kotlin/dev/dnpm/etl/processor/input/KafkaInputListenerTest.kt337
-rw-r--r--src/test/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt325
-rw-r--r--src/test/kotlin/dev/dnpm/etl/processor/monitoring/ConnectionCheckServiceTest.kt139
-rw-r--r--src/test/kotlin/dev/dnpm/etl/processor/monitoring/ReportServiceTest.kt46
-rw-r--r--src/test/kotlin/dev/dnpm/etl/processor/output/KafkaMtbFileSenderTest.kt343
-rw-r--r--src/test/kotlin/dev/dnpm/etl/processor/output/RestDipMtbFileSenderTest.kt430
-rw-r--r--src/test/kotlin/dev/dnpm/etl/processor/pseudonym/ExtensionsTest.kt367
-rw-r--r--src/test/kotlin/dev/dnpm/etl/processor/pseudonym/PseudonymizeServiceTest.kt105
-rw-r--r--src/test/kotlin/dev/dnpm/etl/processor/security/TokenServiceTest.kt174
-rw-r--r--src/test/kotlin/dev/dnpm/etl/processor/security/UserRoleServiceTest.kt211
-rw-r--r--src/test/kotlin/dev/dnpm/etl/processor/services/ConsentProcessorTest.kt378
-rw-r--r--src/test/kotlin/dev/dnpm/etl/processor/services/ReportServiceTest.kt109
-rw-r--r--src/test/kotlin/dev/dnpm/etl/processor/services/RequestProcessorTest.kt676
-rw-r--r--src/test/kotlin/dev/dnpm/etl/processor/services/RequestServiceTest.kt184
-rw-r--r--src/test/kotlin/dev/dnpm/etl/processor/services/ResponseProcessorTest.kt141
-rw-r--r--src/test/kotlin/dev/dnpm/etl/processor/services/TransformationServiceTest.kt209
-rw-r--r--src/test/kotlin/dev/dnpm/etl/processor/services/kafka/KafkaResponseProcessorTest.kt69
83 files changed, 5601 insertions, 5652 deletions
diff --git a/build.gradle.kts b/build.gradle.kts
index 07979b5..683de60 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -203,4 +203,17 @@ spotless {
removeUnusedImports()
googleJavaFormat()
}
+ kotlin {
+ ktlint()
+ suppressLintsFor {
+ step = "ktlint"
+ shortCode = "standard:filename"
+ }
+ suppressLintsFor {
+ step = "ktlint"
+ shortCode = "standard:no-wildcard-imports"
+ }
+ leadingTabsToSpaces()
+ endWithNewline()
+ }
} \ No newline at end of file
diff --git a/src/integrationTest/kotlin/dev/dnpm/etl/processor/AbstractTestcontainerTest.kt b/src/integrationTest/kotlin/dev/dnpm/etl/processor/AbstractTestcontainerTest.kt
index 13b57d0..136691e 100644
--- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/AbstractTestcontainerTest.kt
+++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/AbstractTestcontainerTest.kt
@@ -25,13 +25,13 @@ import org.testcontainers.containers.PostgreSQLContainer
import org.testcontainers.junit.jupiter.Container
abstract class AbstractTestcontainerTest {
-
companion object {
@Container
- val dbContainer = CustomPostgreSQLContainer("postgres:10-alpine")
- .withDatabaseName("test")
- .withUsername("test")
- .withPassword("test") ?: throw RuntimeException("Failed to create testcontainer!")
+ val dbContainer =
+ CustomPostgreSQLContainer("postgres:10-alpine")
+ .withDatabaseName("test")
+ .withUsername("test")
+ .withPassword("test") ?: throw RuntimeException("Failed to create testcontainer!")
@DynamicPropertySource
@JvmStatic
@@ -41,11 +41,12 @@ abstract class AbstractTestcontainerTest {
registry.add("spring.datasource.password", dbContainer::getPassword)
}
}
-
}
-class CustomPostgreSQLContainer(dockerImageName: String) : PostgreSQLContainer<CustomPostgreSQLContainer>(dockerImageName) {
+class CustomPostgreSQLContainer(
+ dockerImageName: String,
+) : PostgreSQLContainer<CustomPostgreSQLContainer>(dockerImageName) {
override fun stop() {
// Keep Testcontainer alive until JVM destroys it
}
-} \ No newline at end of file
+}
diff --git a/src/integrationTest/kotlin/dev/dnpm/etl/processor/EtlProcessorApplicationTests.kt b/src/integrationTest/kotlin/dev/dnpm/etl/processor/EtlProcessorApplicationTests.kt
index 1206c99..7826702 100644
--- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/EtlProcessorApplicationTests.kt
+++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/EtlProcessorApplicationTests.kt
@@ -48,98 +48,95 @@ import org.testcontainers.junit.jupiter.Testcontainers
@SpringBootTest
@MockitoBean(types = [MtbFileSender::class])
@TestPropertySource(
- properties = [
- "app.rest.uri=http://example.com",
- "app.pseudonymize.generator=buildin",
- "app.consent.service=none"
- ]
+ properties =
+ [
+ "app.rest.uri=http://example.com",
+ "app.pseudonymize.generator=buildin",
+ "app.consent.service=none",
+ ]
)
class EtlProcessorApplicationTests : AbstractTestcontainerTest() {
- @Test
- fun contextLoadsIfMtbFileSenderConfigured(@Autowired context: ApplicationContext) {
- // Simply check bean configuration
- assertThat(context).isNotNull
- }
+ @Test
+ fun contextLoadsIfMtbFileSenderConfigured(@Autowired context: ApplicationContext) {
+ // Simply check bean configuration
+ assertThat(context).isNotNull
+ }
- @Nested
- @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
- @AutoConfigureMockMvc
- @TestPropertySource(
- properties = [
- "app.pseudonymize.generator=buildin",
- "app.consent.service=none",
- "app.transformations[0].path=diagnoses[*].code.version",
- "app.transformations[0].from=2013",
- "app.transformations[0].to=2014",
- ]
- )
- inner class TransformationTest {
+ @Nested
+ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
+ @AutoConfigureMockMvc
+ @TestPropertySource(
+ properties =
+ [
+ "app.pseudonymize.generator=buildin",
+ "app.consent.service=none",
+ "app.transformations[0].path=diagnoses[*].code.version",
+ "app.transformations[0].from=2013",
+ "app.transformations[0].to=2014",
+ ]
+ )
+ inner class TransformationTest {
- @MockitoBean
- private lateinit var mtbFileSender: MtbFileSender
+ @MockitoBean private lateinit var mtbFileSender: MtbFileSender
- @Autowired
- private lateinit var mockMvc: MockMvc
+ @Autowired private lateinit var mockMvc: MockMvc
- @Autowired
- private lateinit var objectMapper: ObjectMapper
+ @Autowired private lateinit var objectMapper: ObjectMapper
- @BeforeEach
- fun setup(@Autowired requestRepository: RequestRepository) {
- requestRepository.deleteAll()
- }
+ @BeforeEach
+ fun setup(@Autowired requestRepository: RequestRepository) {
+ requestRepository.deleteAll()
+ }
- @Test
- fun mtbFileIsTransformed() {
- doAnswer {
- MtbFileSender.Response(RequestStatus.SUCCESS)
- }.whenever(mtbFileSender).send(any<DnpmV2MtbFileRequest>())
+ @Test
+ fun mtbFileIsTransformed() {
+ doAnswer { MtbFileSender.Response(RequestStatus.SUCCESS) }
+ .whenever(mtbFileSender)
+ .send(any<DnpmV2MtbFileRequest>())
- val mtbFile = Mtb.builder()
- .patient(
- Patient.builder()
- .id("TEST_12345678")
- .build()
- )
- .metadata(
- MvhMetadata
- .builder()
- .modelProjectConsent(
- ModelProjectConsent
- .builder()
- .provisions(
- listOf(Provision.builder().type(ConsentProvision.PERMIT).purpose(ModelProjectConsentPurpose.SEQUENCING).build())
- ).build()
- )
- .build()
- )
- .diagnoses(
- listOf(
- MtbDiagnosis.builder()
- .id("1234")
- .patient(Reference.builder().id("TEST_12345678").build())
- .code(Coding.builder().code("F79.9").version("2013").build())
- .build(),
- )
- )
- .build()
+ val mtbFile =
+ Mtb.builder()
+ .patient(Patient.builder().id("TEST_12345678").build())
+ .metadata(
+ MvhMetadata.builder()
+ .modelProjectConsent(
+ ModelProjectConsent.builder()
+ .provisions(
+ listOf(
+ Provision.builder()
+ .type(ConsentProvision.PERMIT)
+ .purpose(ModelProjectConsentPurpose.SEQUENCING)
+ .build()
+ )
+ )
+ .build()
+ )
+ .build()
+ )
+ .diagnoses(
+ listOf(
+ MtbDiagnosis.builder()
+ .id("1234")
+ .patient(Reference.builder().id("TEST_12345678").build())
+ .code(Coding.builder().code("F79.9").version("2013").build())
+ .build(),
+ )
+ )
+ .build()
- mockMvc.post("/mtbfile") {
- content = objectMapper.writeValueAsString(mtbFile)
- contentType = MediaType.APPLICATION_JSON
- }.andExpect {
- status {
- isAccepted()
- }
- }
+ mockMvc
+ .post("/mtbfile") {
+ content = objectMapper.writeValueAsString(mtbFile)
+ contentType = MediaType.APPLICATION_JSON
+ }
+ .andExpect { status { isAccepted() } }
- val captor = argumentCaptor<DnpmV2MtbFileRequest>()
- verify(mtbFileSender).send(captor.capture())
- assertThat(captor.firstValue.content.diagnoses).hasSize(1).allMatch { diagnosis ->
- diagnosis.code.version == "2014"
- }
- }
+ val captor = argumentCaptor<DnpmV2MtbFileRequest>()
+ verify(mtbFileSender).send(captor.capture())
+ assertThat(captor.firstValue.content.diagnoses).hasSize(1).allMatch { diagnosis ->
+ diagnosis.code.version == "2014"
+ }
}
-
+ }
}
diff --git a/src/integrationTest/kotlin/dev/dnpm/etl/processor/EtlProcessorArchTest.kt b/src/integrationTest/kotlin/dev/dnpm/etl/processor/EtlProcessorArchTest.kt
index 308d0cc..1ebf458 100644
--- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/EtlProcessorArchTest.kt
+++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/EtlProcessorArchTest.kt
@@ -9,65 +9,79 @@ import org.junit.jupiter.api.Test
import org.springframework.data.repository.Repository
class EtlProcessorArchTest {
-
private lateinit var noTestClasses: JavaClasses
@BeforeEach
fun setUp() {
- this.noTestClasses = ClassFileImporter()
- .withImportOption { !(it.contains("/test/") || it.contains("/integrationTest/")) }
- .importPackages("dev.dnpm.etl.processor")
+ this.noTestClasses =
+ ClassFileImporter()
+ .withImportOption { !(it.contains("/test/") || it.contains("/integrationTest/")) }
+ .importPackages("dev.dnpm.etl.processor")
}
@Test
fun noClassesInInputPackageShouldDependOnMonitoringPackage() {
- val rule = noClasses()
- .that()
- .resideInAPackage("..input")
- .should().dependOnClassesThat()
- .resideInAnyPackage("..monitoring")
+ val rule =
+ noClasses()
+ .that()
+ .resideInAPackage("..input")
+ .should()
+ .dependOnClassesThat()
+ .resideInAnyPackage("..monitoring")
rule.check(noTestClasses)
}
@Test
fun noClassesInInputPackageShouldDependOnRepositories() {
- val rule = noClasses()
- .that()
- .resideInAPackage("..input")
- .should().dependOnClassesThat().haveSimpleNameEndingWith("Repository")
+ val rule =
+ noClasses()
+ .that()
+ .resideInAPackage("..input")
+ .should()
+ .dependOnClassesThat()
+ .haveSimpleNameEndingWith("Repository")
rule.check(noTestClasses)
}
@Test
fun noClassesInOutputPackageShouldDependOnRepositories() {
- val rule = noClasses()
- .that()
- .resideInAPackage("..output")
- .should().dependOnClassesThat().haveSimpleNameEndingWith("Repository")
+ val rule =
+ noClasses()
+ .that()
+ .resideInAPackage("..output")
+ .should()
+ .dependOnClassesThat()
+ .haveSimpleNameEndingWith("Repository")
rule.check(noTestClasses)
}
@Test
fun noClassesInWebPackageShouldDependOnRepositories() {
- val rule = noClasses()
- .that()
- .resideInAPackage("..web")
- .should().dependOnClassesThat().haveSimpleNameEndingWith("Repository")
+ val rule =
+ noClasses()
+ .that()
+ .resideInAPackage("..web")
+ .should()
+ .dependOnClassesThat()
+ .haveSimpleNameEndingWith("Repository")
rule.check(noTestClasses)
}
@Test
fun repositoryClassNamesShouldEndWithRepository() {
- val rule = classes()
- .that()
- .areInterfaces().and().areAssignableTo(Repository::class.java)
- .should().haveSimpleNameEndingWith("Repository")
+ val rule =
+ classes()
+ .that()
+ .areInterfaces()
+ .and()
+ .areAssignableTo(Repository::class.java)
+ .should()
+ .haveSimpleNameEndingWith("Repository")
rule.check(noTestClasses)
}
-
-} \ No newline at end of file
+}
diff --git a/src/integrationTest/kotlin/dev/dnpm/etl/processor/config/AppConfigurationTest.kt b/src/integrationTest/kotlin/dev/dnpm/etl/processor/config/AppConfigurationTest.kt
index 5e25428..f9fe2d4 100644
--- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/config/AppConfigurationTest.kt
+++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/config/AppConfigurationTest.kt
@@ -50,127 +50,136 @@ import org.springframework.test.context.bean.override.mockito.MockitoBean
@SpringBootTest
@ContextConfiguration(
- classes = [
- AppConfiguration::class,
- AppSecurityConfiguration::class,
- KafkaAutoConfiguration::class,
- AppKafkaConfiguration::class,
- AppRestConfiguration::class,
- ConsentEvaluator::class
- ]
+ classes =
+ [
+ AppConfiguration::class,
+ AppSecurityConfiguration::class,
+ KafkaAutoConfiguration::class,
+ AppKafkaConfiguration::class,
+ AppRestConfiguration::class,
+ ConsentEvaluator::class,
+ ],
)
@MockitoBean(types = [ObjectMapper::class])
@TestPropertySource(
- properties = [
- "app.pseudonymize.generator=BUILDIN",
- ]
+ properties =
+ [
+ "app.pseudonymize.generator=BUILDIN",
+ ],
)
class AppConfigurationTest {
-
@Nested
- @TestPropertySource(
- properties = [
- "app.rest.uri=http://localhost:9000"
- ]
- )
- inner class AppConfigurationRestTest(private val context: ApplicationContext) {
-
+ @TestPropertySource(properties = ["app.rest.uri=http://localhost:9000"])
+ inner class AppConfigurationRestTest(
+ private val context: ApplicationContext,
+ ) {
@Test
fun shouldUseRestMtbFileSenderNotKafkaMtbFileSender() {
assertThat(context.getBean(RestMtbFileSender::class.java)).isNotNull
- assertThrows<NoSuchBeanDefinitionException> { context.getBean(KafkaMtbFileSender::class.java) }
+ assertThrows<NoSuchBeanDefinitionException> {
+ context.getBean(KafkaMtbFileSender::class.java)
+ }
}
-
}
@Nested
@TestPropertySource(
- properties = [
- "app.kafka.servers=localhost:9092",
- "app.kafka.output-topic=test",
- "app.kafka.output-response-topic=test-response",
- "app.kafka.group-id=test"
- ]
+ properties =
+ [
+ "app.kafka.servers=localhost:9092",
+ "app.kafka.output-topic=test",
+ "app.kafka.output-response-topic=test-response",
+ "app.kafka.group-id=test",
+ ],
)
@MockitoBean(types = [RequestRepository::class])
- inner class AppConfigurationKafkaTest(private val context: ApplicationContext) {
-
+ inner class AppConfigurationKafkaTest(
+ private val context: ApplicationContext,
+ ) {
@Test
fun shouldUseKafkaMtbFileSenderNotRestMtbFileSender() {
assertThrows<NoSuchBeanDefinitionException> { context.getBean(RestMtbFileSender::class.java) }
assertThat(context.getBean(KafkaMtbFileSender::class.java)).isNotNull
}
-
}
@Nested
@TestPropertySource(
- properties = [
- "app.rest.uri=http://localhost:9000",
- "app.kafka.servers=localhost:9092",
- "app.kafka.output-topic=test",
- "app.kafka.output-response-topic=test-response",
- "app.kafka.group-id=test"
- ]
+ properties =
+ [
+ "app.rest.uri=http://localhost:9000",
+ "app.kafka.servers=localhost:9092",
+ "app.kafka.output-topic=test",
+ "app.kafka.output-response-topic=test-response",
+ "app.kafka.group-id=test",
+ ],
)
- inner class AppConfigurationRestInPrecedenceTest(private val context: ApplicationContext) {
-
+ inner class AppConfigurationRestInPrecedenceTest(
+ private val context: ApplicationContext,
+ ) {
@Test
fun shouldUseRestMtbFileSenderNotKafkaMtbFileSender() {
assertThat(context.getBean(RestMtbFileSender::class.java)).isNotNull
- assertThrows<NoSuchBeanDefinitionException> { context.getBean(KafkaMtbFileSender::class.java) }
+ assertThrows<NoSuchBeanDefinitionException> {
+ context.getBean(KafkaMtbFileSender::class.java)
+ }
}
-
}
@Nested
@TestPropertySource(
- properties = [
- "app.kafka.servers=localhost:9092",
- "app.kafka.output-topic=test",
- "app.kafka.output-response-topic=test-response",
- "app.kafka.group-id=test"
- ]
+ properties =
+ [
+ "app.kafka.servers=localhost:9092",
+ "app.kafka.output-topic=test",
+ "app.kafka.output-response-topic=test-response",
+ "app.kafka.group-id=test",
+ ],
)
- inner class AppConfigurationWithoutKafkaInputTest(private val context: ApplicationContext) {
-
+ inner class AppConfigurationWithoutKafkaInputTest(
+ private val context: ApplicationContext,
+ ) {
@Test
fun shouldNotUseKafkaInputListener() {
- assertThrows<NoSuchBeanDefinitionException> { context.getBean(KafkaInputListener::class.java) }
+ assertThrows<NoSuchBeanDefinitionException> {
+ context.getBean(KafkaInputListener::class.java)
+ }
}
-
}
@Nested
@TestPropertySource(
- properties = [
- "app.kafka.servers=localhost:9092",
- "app.kafka.input-topic=test_input",
- "app.kafka.output-topic=test",
- "app.kafka.output-response-topic=test-response",
- "app.kafka.group-id=test"
- ]
+ properties =
+ [
+ "app.kafka.servers=localhost:9092",
+ "app.kafka.input-topic=test_input",
+ "app.kafka.output-topic=test",
+ "app.kafka.output-response-topic=test-response",
+ "app.kafka.group-id=test",
+ ],
)
@MockitoBean(types = [RequestProcessor::class])
- inner class AppConfigurationUsingKafkaInputTest(private val context: ApplicationContext) {
-
+ inner class AppConfigurationUsingKafkaInputTest(
+ private val context: ApplicationContext,
+ ) {
@Test
fun shouldUseKafkaInputListener() {
assertThat(context.getBean(KafkaInputListener::class.java)).isNotNull
}
-
}
@Nested
@TestPropertySource(
- properties = [
- "app.transformations[0].path=consent.status",
- "app.transformations[0].from=rejected",
- "app.transformations[0].to=accept",
- ]
+ properties =
+ [
+ "app.transformations[0].path=consent.status",
+ "app.transformations[0].from=rejected",
+ "app.transformations[0].to=accept",
+ ],
)
- inner class AppConfigurationTransformationTest(private val context: ApplicationContext) {
-
+ inner class AppConfigurationTransformationTest(
+ private val context: ApplicationContext,
+ ) {
@Test
fun shouldRecognizeTransformations() {
val appConfigProperties = context.getBean(AppConfigProperties::class.java)
@@ -178,109 +187,87 @@ class AppConfigurationTest {
assertThat(appConfigProperties).isNotNull
assertThat(appConfigProperties.transformations).hasSize(1)
}
-
}
@Nested
inner class AppConfigurationPseudonymizeTest {
-
@Nested
- @TestPropertySource(
- properties = [
- "app.pseudonymize.generator=buildin"
- ]
- )
- inner class AppConfigurationPseudonymizeGeneratorBuildinTest(private val context: ApplicationContext) {
-
+ @TestPropertySource(properties = ["app.pseudonymize.generator=buildin"])
+ inner class AppConfigurationPseudonymizeGeneratorBuildinTest(
+ private val context: ApplicationContext,
+ ) {
@Test
fun shouldUseConfiguredGenerator() {
assertThat(context.getBean(AnonymizingGenerator::class.java)).isNotNull
}
-
}
@Nested
@TestPropertySource(
- properties = [
- "app.pseudonymize.generator=gpas",
- "app.pseudonymize.gpas.uri=http://localhost/"
- ]
+ properties =
+ ["app.pseudonymize.generator=gpas", "app.pseudonymize.gpas.uri=http://localhost/"],
)
- inner class AppConfigurationPseudonymizeGeneratorGpasTest(private val context: ApplicationContext) {
-
+ inner class AppConfigurationPseudonymizeGeneratorGpasTest(
+ private val context: ApplicationContext,
+ ) {
@Test
fun shouldUseConfiguredGenerator() {
assertThat(context.getBean(GpasPseudonymGenerator::class.java)).isNotNull
}
-
}
@Nested
@TestPropertySource(
- properties = [
- "app.pseudonymize.generator=gpas",
- "app.pseudonymize.gpas.soap-endpoint=http://localhost/"
- ]
+ properties =
+ [
+ "app.pseudonymize.generator=gpas",
+ "app.pseudonymize.gpas.soap-endpoint=http://localhost/",
+ ],
)
- inner class AppConfigurationPseudonymizeGeneratorGpasSoapTest(private val context: ApplicationContext) {
-
+ inner class AppConfigurationPseudonymizeGeneratorGpasSoapTest(
+ private val context: ApplicationContext,
+ ) {
@Test
fun shouldUseConfiguredGenerator() {
assertThat(context.getBean(GpasSoapPseudonymGenerator::class.java)).isNotNull
}
-
}
@Nested
- @TestPropertySource(
- properties = [
- "app.security.enable-tokens=true"
- ]
- )
+ @TestPropertySource(properties = ["app.security.enable-tokens=true"])
@MockitoBean(
- types = [
- InMemoryUserDetailsManager::class,
- PasswordEncoder::class,
- TokenRepository::class
- ]
+ types = [InMemoryUserDetailsManager::class, PasswordEncoder::class, TokenRepository::class],
)
- inner class AppConfigurationTokenEnabledTest(private val context: ApplicationContext) {
-
+ inner class AppConfigurationTokenEnabledTest(
+ private val context: ApplicationContext,
+ ) {
@Test
fun checkTokenService() {
assertThat(context.getBean(TokenService::class.java)).isNotNull
}
-
}
@Nested
@MockitoBean(
- types = [
- InMemoryUserDetailsManager::class,
- PasswordEncoder::class,
- TokenRepository::class
- ]
+ types = [InMemoryUserDetailsManager::class, PasswordEncoder::class, TokenRepository::class],
)
- inner class AppConfigurationTokenDisabledTest(private val context: ApplicationContext) {
-
+ inner class AppConfigurationTokenDisabledTest(
+ private val context: ApplicationContext,
+ ) {
@Test
fun checkTokenService() {
assertThrows<NoSuchBeanDefinitionException> { context.getBean(TokenService::class.java) }
}
-
}
-
}
@Nested
@TestPropertySource(
- properties = [
- "app.rest.uri=http://localhost:9000",
- "app.max-retry-attempts=5"
- ]
+ properties = ["app.rest.uri=http://localhost:9000", "app.max-retry-attempts=5"],
)
- inner class AppConfigurationRetryTest(private val context: ApplicationContext) {
-
+ inner class AppConfigurationRetryTest(
+ private val context: ApplicationContext,
+ ) {
private val maxRetryAttempts = 5
@Test
@@ -295,33 +282,32 @@ class AppConfigurationTest {
}
}
}
-
}
@Nested
@TestPropertySource(
- properties = [
- "app.consent.service=GICS",
- "app.consent.gics.uri=http://localhost:9000",
- ]
+ properties =
+ [
+ "app.consent.service=GICS",
+ "app.consent.gics.uri=http://localhost:9000",
+ ],
)
- inner class AppConfigurationConsentGicsTest(private val context: ApplicationContext) {
-
+ inner class AppConfigurationConsentGicsTest(
+ private val context: ApplicationContext,
+ ) {
@Test
fun shouldUseConfiguredGenerator() {
assertThat(context.getBean(GicsConsentService::class.java)).isNotNull
}
-
}
@Nested
- inner class AppConfigurationConsentBuildinTest(private val context: ApplicationContext) {
-
+ inner class AppConfigurationConsentBuildinTest(
+ private val context: ApplicationContext,
+ ) {
@Test
fun shouldUseConfiguredGenerator() {
assertThat(context.getBean(MtbFileConsentService::class.java)).isNotNull
}
-
}
-
}
diff --git a/src/integrationTest/kotlin/dev/dnpm/etl/processor/config/GPasConfigPropertiesTest.kt b/src/integrationTest/kotlin/dev/dnpm/etl/processor/config/GPasConfigPropertiesTest.kt
index 66e0660..bda5b79 100644
--- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/config/GPasConfigPropertiesTest.kt
+++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/config/GPasConfigPropertiesTest.kt
@@ -7,17 +7,16 @@ import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.context.runner.ApplicationContextRunner
import org.springframework.test.context.ContextConfiguration
-
@SpringBootTest
@ContextConfiguration(
- classes = [
- GPasConfigProperties::class,
- ]
+ classes =
+ [
+ GPasConfigProperties::class,
+ ],
)
class GPasConfigPropertiesTest {
-
@EnableConfigurationProperties(GPasConfigProperties::class)
- class TestConfig {}
+ class TestConfig
@Test
fun shouldUseConfiguredPatientDomainIfPidDomainGiven() {
@@ -25,9 +24,8 @@ class GPasConfigPropertiesTest {
.withUserConfiguration(TestConfig::class.java)
.withPropertyValues(
"app.pseudonymize.gpas.uri=http://localhost/",
- "app.pseudonymize.gpas.pid-domain=test-pid-domain"
- )
- .run { context ->
+ "app.pseudonymize.gpas.pid-domain=test-pid-domain",
+ ).run { context ->
val properties = context.getBean(GPasConfigProperties::class.java)
assertThat(properties).isNotNull
@@ -41,14 +39,12 @@ class GPasConfigPropertiesTest {
.withUserConfiguration(TestConfig::class.java)
.withPropertyValues(
"app.pseudonymize.gpas.uri=http://localhost/",
- "app.pseudonymize.gpas.patient-domain=test-patient-domain"
- )
- .run { context ->
+ "app.pseudonymize.gpas.patient-domain=test-patient-domain",
+ ).run { context ->
val properties = context.getBean(GPasConfigProperties::class.java)
assertThat(properties).isNotNull
assertThat(properties.patientDomain).isEqualTo("test-patient-domain")
}
}
-
-} \ No newline at end of file
+}
diff --git a/src/integrationTest/kotlin/dev/dnpm/etl/processor/helpers.kt b/src/integrationTest/kotlin/dev/dnpm/etl/processor/helpers.kt
index 6ca420f..5ee6922 100644
--- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/helpers.kt
+++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/helpers.kt
@@ -23,8 +23,7 @@ import org.mockito.ArgumentMatchers
@Suppress("UNCHECKED_CAST")
inline fun <reified T> anyValueClass(): T {
- val unboxedClass = T::class.java.declaredFields.first().type
- return ArgumentMatchers.any(unboxedClass as Class<T>)
- ?: T::class.java.getDeclaredMethod("box-impl", unboxedClass)
- .invoke(null, null) as T
-} \ No newline at end of file
+ val unboxedClass = T::class.java.declaredFields.first().type
+ return ArgumentMatchers.any(unboxedClass as Class<T>)
+ ?: T::class.java.getDeclaredMethod("box-impl", unboxedClass).invoke(null, null) as T
+}
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 5f2d7ca..e966898 100644
--- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt
+++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt
@@ -29,6 +29,8 @@ 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 java.time.Instant
+import java.util.*
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
@@ -48,180 +50,177 @@ 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])
@ContextConfiguration(
- classes = [
- MtbFileRestController::class,
- AppSecurityConfiguration::class,
- MtbFileConsentService::class
- ]
+ classes =
+ [
+ MtbFileRestController::class,
+ AppSecurityConfiguration::class,
+ MtbFileConsentService::class,
+ ]
)
-@MockitoBean(types = [TokenRepository::class, RequestProcessor::class, ConsentEvaluator::class])
+@MockitoBean(types = [TokenRepository::class, RequestProcessor::class, ConsentEvaluator::class])
@TestPropertySource(
- properties = [
- "app.pseudonymize.generator=BUILDIN",
- "app.security.admin-user=admin",
- "app.security.admin-password={noop}very-secret",
- "app.security.enable-tokens=true"
- ]
+ properties =
+ [
+ "app.pseudonymize.generator=BUILDIN",
+ "app.security.admin-user=admin",
+ "app.security.admin-password={noop}very-secret",
+ "app.security.enable-tokens=true",
+ ]
)
class MtbFileRestControllerTest {
- lateinit var mockMvc: MockMvc
- lateinit var requestProcessor: RequestProcessor
- lateinit var consentEvaluator: ConsentEvaluator
-
- @BeforeEach
- fun setup(
- @Autowired mockMvc: MockMvc,
- @Autowired requestProcessor: RequestProcessor,
- @Autowired consentEvaluator: ConsentEvaluator
- ) {
- this.mockMvc = mockMvc
- this.requestProcessor = requestProcessor
- this.consentEvaluator = consentEvaluator
-
- doAnswer {
- ConsentEvaluation(TtpConsentStatus.BROAD_CONSENT_GIVEN, true)
- }.whenever(consentEvaluator).check(any())
- }
-
- @Test
- fun testShouldGrantPermissionToSendMtbFile() {
- mockMvc.post("/mtbfile") {
- with(user("onkostarserver").roles("MTBFILE"))
- contentType = MediaType.APPLICATION_JSON
- content = ObjectMapper().writeValueAsString(mtbFile)
- }.andExpect {
- status { isAccepted() }
+ lateinit var mockMvc: MockMvc
+ lateinit var requestProcessor: RequestProcessor
+ lateinit var consentEvaluator: ConsentEvaluator
+
+ @BeforeEach
+ fun setup(
+ @Autowired mockMvc: MockMvc,
+ @Autowired requestProcessor: RequestProcessor,
+ @Autowired consentEvaluator: ConsentEvaluator,
+ ) {
+ this.mockMvc = mockMvc
+ this.requestProcessor = requestProcessor
+ this.consentEvaluator = consentEvaluator
+
+ doAnswer { ConsentEvaluation(TtpConsentStatus.BROAD_CONSENT_GIVEN, true) }
+ .whenever(consentEvaluator)
+ .check(any())
+ }
+
+ @Test
+ fun testShouldGrantPermissionToSendMtbFile() {
+ mockMvc
+ .post("/mtbfile") {
+ with(user("onkostarserver").roles("MTBFILE"))
+ contentType = MediaType.APPLICATION_JSON
+ content = ObjectMapper().writeValueAsString(mtbFile)
}
-
- verify(requestProcessor, times(1)).processMtbFile(any<Mtb>())
- }
-
+ .andExpect { status { isAccepted() } }
+
+ verify(requestProcessor, times(1)).processMtbFile(any<Mtb>())
+ }
+
+ @Test
+ fun testShouldGrantPermissionToSendMtbFileToAdminUser() {
+ mockMvc
+ .post("/mtbfile") {
+ with(user("onkostarserver").roles("ADMIN"))
+ contentType = MediaType.APPLICATION_JSON
+ content = ObjectMapper().writeValueAsString(mtbFile)
+ }
+ .andExpect { status { isAccepted() } }
+
+ verify(requestProcessor, times(1)).processMtbFile(any<Mtb>())
+ }
+
+ @Test
+ fun testShouldDenyPermissionToSendMtbFile() {
+ mockMvc
+ .post("/mtbfile") {
+ with(anonymous())
+ contentType = MediaType.APPLICATION_JSON
+ content = ObjectMapper().writeValueAsString(mtbFile)
+ }
+ .andExpect { status { isUnauthorized() } }
+
+ verify(requestProcessor, never()).processMtbFile(any<Mtb>())
+ }
+
+ @Test
+ fun testShouldDenyPermissionToSendMtbFileForUser() {
+ mockMvc
+ .post("/mtbfile") {
+ with(user("fakeuser").roles("USER"))
+ contentType = MediaType.APPLICATION_JSON
+ content = ObjectMapper().writeValueAsString(mtbFile)
+ }
+ .andExpect { status { isForbidden() } }
+
+ verify(requestProcessor, never()).processMtbFile(any<Mtb>())
+ }
+
+ @Test
+ fun testShouldGrantPermissionToDeletePatientData() {
+ mockMvc
+ .delete("/mtbfile/12345678") { with(user("onkostarserver").roles("MTBFILE")) }
+ .andExpect { status { isAccepted() } }
+
+ verify(requestProcessor, times(1))
+ .processDeletion(anyValueClass(), eq(TtpConsentStatus.UNKNOWN_CHECK_FILE))
+ }
+
+ @Test
+ fun testShouldDenyPermissionToDeletePatientData() {
+ mockMvc
+ .delete("/mtbfile/12345678") { with(anonymous()) }
+ .andExpect { status { isUnauthorized() } }
+
+ verify(requestProcessor, never()).processDeletion(anyValueClass(), any())
+ }
+
+ @Nested
+ @MockitoBean(types = [UserRoleRepository::class, ClientRegistrationRepository::class])
+ @TestPropertySource(
+ properties =
+ [
+ "app.pseudonymize.generator=BUILDIN",
+ "app.security.admin-user=admin",
+ "app.security.admin-password={noop}very-secret",
+ "app.security.enable-tokens=true",
+ "app.security.enable-oidc=true",
+ ]
+ )
+ inner class WithOidcEnabled {
@Test
fun testShouldGrantPermissionToSendMtbFileToAdminUser() {
- mockMvc.post("/mtbfile") {
+ mockMvc
+ .post("/mtbfile") {
with(user("onkostarserver").roles("ADMIN"))
contentType = MediaType.APPLICATION_JSON
content = ObjectMapper().writeValueAsString(mtbFile)
- }.andExpect {
- status { isAccepted() }
- }
-
- verify(requestProcessor, times(1)).processMtbFile(any<Mtb>())
- }
-
- @Test
- fun testShouldDenyPermissionToSendMtbFile() {
- mockMvc.post("/mtbfile") {
- with(anonymous())
- contentType = MediaType.APPLICATION_JSON
- content = ObjectMapper().writeValueAsString(mtbFile)
- }.andExpect {
- status { isUnauthorized() }
- }
+ }
+ .andExpect { status { isAccepted() } }
- verify(requestProcessor, never()).processMtbFile(any<Mtb>())
+ verify(requestProcessor, times(1)).processMtbFile(any<Mtb>())
}
@Test
- fun testShouldDenyPermissionToSendMtbFileForUser() {
- mockMvc.post("/mtbfile") {
- with(user("fakeuser").roles("USER"))
+ fun testShouldGrantPermissionToSendMtbFileToUser() {
+ mockMvc
+ .post("/mtbfile") {
+ with(user("onkostarserver").roles("USER"))
contentType = MediaType.APPLICATION_JSON
content = ObjectMapper().writeValueAsString(mtbFile)
- }.andExpect {
- status { isForbidden() }
- }
-
- verify(requestProcessor, never()).processMtbFile(any<Mtb>())
- }
-
- @Test
- fun testShouldGrantPermissionToDeletePatientData() {
- mockMvc.delete("/mtbfile/12345678") {
- with(user("onkostarserver").roles("MTBFILE"))
- }.andExpect {
- status { isAccepted() }
- }
+ }
+ .andExpect { status { isAccepted() } }
- verify(requestProcessor, times(1)).processDeletion(anyValueClass(), eq(TtpConsentStatus.UNKNOWN_CHECK_FILE))
+ verify(requestProcessor, times(1)).processMtbFile(any<Mtb>())
}
+ }
- @Test
- fun testShouldDenyPermissionToDeletePatientData() {
- mockMvc.delete("/mtbfile/12345678") {
- with(anonymous())
- }.andExpect {
- status { isUnauthorized() }
- }
+ companion object {
- verify(requestProcessor, never()).processDeletion(anyValueClass(), any())
- }
-
- @Nested
- @MockitoBean(types = [UserRoleRepository::class, ClientRegistrationRepository::class])
- @TestPropertySource(
- properties = [
- "app.pseudonymize.generator=BUILDIN",
- "app.security.admin-user=admin",
- "app.security.admin-password={noop}very-secret",
- "app.security.enable-tokens=true",
- "app.security.enable-oidc=true"
- ]
- )
- inner class WithOidcEnabled {
- @Test
- fun testShouldGrantPermissionToSendMtbFileToAdminUser() {
- mockMvc.post("/mtbfile") {
- with(user("onkostarserver").roles("ADMIN"))
- contentType = MediaType.APPLICATION_JSON
- content = ObjectMapper().writeValueAsString(mtbFile)
- }.andExpect {
- status { isAccepted() }
- }
-
- verify(requestProcessor, times(1)).processMtbFile(any<Mtb>())
- }
-
- @Test
- fun testShouldGrantPermissionToSendMtbFileToUser() {
- mockMvc.post("/mtbfile") {
- with(user("onkostarserver").roles("USER"))
- contentType = MediaType.APPLICATION_JSON
- content = ObjectMapper().writeValueAsString(mtbFile)
- }.andExpect {
- status { isAccepted() }
- }
-
- verify(requestProcessor, times(1)).processMtbFile(any<Mtb>())
- }
- }
-
- companion object {
-
- val mtbFile = Mtb.builder()
- .patient(
- Patient.builder()
- .id("PID")
- .build()
- )
+ val mtbFile =
+ Mtb.builder()
+ .patient(Patient.builder().id("PID").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())
+ .period(
+ PeriodDate.builder()
+ .start(Date.from(Instant.parse("2023-08-08T02:00:00.00Z")))
+ .build()
+ )
.build()
)
)
.build()
-
- }
-
+ }
}
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 428bca9..428a99d 100644
--- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/monitoring/RequestRepositoryTest.kt
+++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/monitoring/RequestRepositoryTest.kt
@@ -21,6 +21,7 @@ package dev.dnpm.etl.processor.monitoring
import dev.dnpm.etl.processor.*
import dev.dnpm.etl.processor.output.MtbFileSender
+import java.time.Instant
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
@@ -32,7 +33,6 @@ import org.springframework.test.context.bean.override.mockito.MockitoBean
import org.springframework.test.context.junit.jupiter.SpringExtension
import org.springframework.transaction.annotation.Transactional
import org.testcontainers.junit.jupiter.Testcontainers
-import java.time.Instant
@Testcontainers
@ExtendWith(SpringExtension::class)
@@ -41,35 +41,30 @@ import java.time.Instant
@Transactional
@MockitoBean(types = [MtbFileSender::class])
@TestPropertySource(
- properties = [
- "app.pseudonymize.generator=buildin",
- "app.rest.uri=http://example.com"
- ]
+ properties = ["app.pseudonymize.generator=buildin", "app.rest.uri=http://example.com"]
)
class RequestRepositoryTest : AbstractTestcontainerTest() {
- private lateinit var requestRepository: RequestRepository
+ private lateinit var requestRepository: RequestRepository
- @BeforeEach
- fun setUp(
- @Autowired requestRepository: RequestRepository
- ) {
- this.requestRepository = requestRepository
- }
+ @BeforeEach
+ fun setUp(@Autowired requestRepository: RequestRepository) {
+ this.requestRepository = requestRepository
+ }
- @Test
- fun shouldSaveRequest() {
- val request = Request(
+ @Test
+ fun shouldSaveRequest() {
+ val request =
+ Request(
randomRequestId(),
PatientPseudonym("TEST_12345678901"),
PatientId("P1"),
Fingerprint("0123456789abcdef1"),
RequestType.MTB_FILE,
RequestStatus.WARNING,
- Instant.parse("2023-07-07T00:00:00Z")
+ Instant.parse("2023-07-07T00:00:00Z"),
)
- requestRepository.save(request)
- }
-
-} \ No newline at end of file
+ requestRepository.save(request)
+ }
+}
diff --git a/src/integrationTest/kotlin/dev/dnpm/etl/processor/pseudonym/GpasPseudonymGeneratorTest.kt b/src/integrationTest/kotlin/dev/dnpm/etl/processor/pseudonym/GpasPseudonymGeneratorTest.kt
index 578810e..cccd614 100644
--- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/pseudonym/GpasPseudonymGeneratorTest.kt
+++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/pseudonym/GpasPseudonymGeneratorTest.kt
@@ -41,7 +41,6 @@ import java.io.IOException
import java.net.URI
class GpasPseudonymGeneratorTest {
-
private lateinit var mockRestServiceServer: MockRestServiceServer
private lateinit var generator: GpasPseudonymGenerator
private lateinit var restTemplate: RestTemplate
@@ -50,14 +49,8 @@ class GpasPseudonymGeneratorTest {
@BeforeEach
fun setup() {
val retryTemplate = RetryTemplateBuilder().customPolicy(SimpleRetryPolicy(1)).build()
- val gPasConfigProperties = GPasConfigProperties(
- CONFIGURED_URI,
- null,
- null,
- "test", "test2",
- null,
- null
- )
+ val gPasConfigProperties =
+ GPasConfigProperties(CONFIGURED_URI, null, null, "test", "test2", null, null)
this.restTemplate = RestTemplate()
this.mockRestServiceServer = MockRestServiceServer.createServer(restTemplate)
@@ -71,13 +64,8 @@ class GpasPseudonymGeneratorTest {
.expect(method(HttpMethod.POST))
.andExpect(requestTo(EXPECTED_URI))
.andRespond {
- withStatus(HttpStatus.OK).body(
- getDummyResponseBody(
- "1234",
- "test",
- "test1234ABCDEF567890"
- )
- )
+ withStatus(HttpStatus.OK)
+ .body(getDummyResponseBody("1234", "test", "test1234ABCDEF567890"))
.createResponse(it)
}
@@ -89,9 +77,7 @@ class GpasPseudonymGeneratorTest {
this.mockRestServiceServer
.expect(method(HttpMethod.POST))
.andExpect(requestTo(EXPECTED_URI))
- .andRespond {
- withException(IOException("Simulated IO error")).createResponse(it)
- }
+ .andRespond { withException(IOException("Simulated IO error")).createResponse(it) }
assertThrows<PseudonymRequestFailed> { this.generator.generate("ID1234") }
}
@@ -105,52 +91,56 @@ class GpasPseudonymGeneratorTest {
withStatus(HttpStatus.FOUND)
.header(
HttpHeaders.LOCATION,
- $$"https://localhost/ttp-fhir/fhir/gpas/$pseudonymizeAllowCreate"
- )
- .createResponse(it)
+ $$"https://localhost/ttp-fhir/fhir/gpas/$pseudonymizeAllowCreate",
+ ).createResponse(it)
}
assertThrows<PseudonymRequestFailed> { this.generator.generate("ID1234") }
}
companion object {
-
const val CONFIGURED_URI = "https://localhost/ttp-fhir/fhir/gpas"
- val EXPECTED_URI = URIBuilder(URI.create(CONFIGURED_URI)).appendPath($$"$pseudonymizeAllowCreate")
- .build()!!
+ val EXPECTED_URI =
+ URIBuilder(URI.create(CONFIGURED_URI)).appendPath($$"$pseudonymizeAllowCreate").build()!!
- fun getDummyResponseBody(original: String, target: String, pseudonym: String) = """{
- "resourceType": "Parameters",
- "parameter": [
+ fun getDummyResponseBody(
+ original: String,
+ target: String,
+ pseudonym: String,
+ ) = """
{
- "name": "pseudonym",
- "part": [
- {
- "name": "original",
- "valueIdentifier": {
- "system": "https://ths-greifswald.de/gpas",
- "value": "$original"
- }
- },
- {
- "name": "target",
- "valueIdentifier": {
- "system": "https://ths-greifswald.de/gpas",
- "value": "$target"
- }
- },
+ "resourceType": "Parameters",
+ "parameter": [
{
"name": "pseudonym",
- "valueIdentifier": {
- "system": "https://ths-greifswald.de/gpas",
- "value": "$pseudonym"
- }
+ "part": [
+ {
+ "name": "original",
+ "valueIdentifier": {
+ "system": "https://ths-greifswald.de/gpas",
+ "value": "$original"
+ }
+ },
+ {
+ "name": "target",
+ "valueIdentifier": {
+ "system": "https://ths-greifswald.de/gpas",
+ "value": "$target"
+ }
+ },
+ {
+ "name": "pseudonym",
+ "valueIdentifier": {
+ "system": "https://ths-greifswald.de/gpas",
+ "value": "$pseudonym"
+ }
+ }
+ ]
}
]
}
- ]
- }""".trimIndent()
-
+
+ """.trimIndent()
}
}
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 9fcdc16..d9489f2 100644
--- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/services/RequestServiceIntegrationTest.kt
+++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/services/RequestServiceIntegrationTest.kt
@@ -25,6 +25,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 java.time.Instant
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
@@ -36,7 +37,6 @@ import org.springframework.test.context.bean.override.mockito.MockitoBean
import org.springframework.test.context.junit.jupiter.SpringExtension
import org.springframework.transaction.annotation.Transactional
import org.testcontainers.junit.jupiter.Testcontainers
-import java.time.Instant
@Testcontainers
@ExtendWith(SpringExtension::class)
@@ -44,101 +44,95 @@ import java.time.Instant
@Transactional
@MockitoBean(types = [MtbFileSender::class])
@TestPropertySource(
- properties = [
- "app.pseudonymize.generator=buildin",
- "app.rest.uri=http://example.com"
- ]
+ properties = ["app.pseudonymize.generator=buildin", "app.rest.uri=http://example.com"]
)
class RequestServiceIntegrationTest : AbstractTestcontainerTest() {
- private lateinit var requestRepository: RequestRepository
-
- private lateinit var requestService: RequestService
-
- @BeforeEach
- fun setup(
- @Autowired requestRepository: RequestRepository
- ) {
- this.requestRepository = requestRepository
- this.requestService = RequestService(requestRepository)
- }
-
- @Test
- fun shouldResultInEmptyRequestList() {
- val actual = requestService.allRequestsByPatientPseudonym(TEST_PATIENT_PSEUDONYM)
-
- assertThat(actual).isEmpty()
- }
-
- private fun setupTestData() {
- // Prepare DB
- this.requestRepository.saveAll(
- listOf(
- Request(
- randomRequestId(),
- PatientPseudonym("TEST_12345678901"),
- PatientId("P1"),
- Fingerprint("0123456789abcdef1"),
- RequestType.MTB_FILE,
- RequestStatus.SUCCESS,
- Instant.parse("2023-07-07T02:00:00Z")
- ),
- // Should be ignored - wrong patient ID -->
- Request(
- randomRequestId(),
- PatientPseudonym("TEST_12345678902"),
- PatientId("P2"),
- Fingerprint("0123456789abcdef2"),
- RequestType.MTB_FILE,
- RequestStatus.WARNING,
- Instant.parse("2023-08-08T00:00:00Z")
- ),
- // <--
- Request(
- randomRequestId(),
- PatientPseudonym("TEST_12345678901"),
- PatientId("P2"),
- Fingerprint("0123456789abcdee1"),
- RequestType.DELETE,
- RequestStatus.SUCCESS,
- Instant.parse("2023-08-08T02:00:00Z")
- )
- )
+ private lateinit var requestRepository: RequestRepository
+
+ private lateinit var requestService: RequestService
+
+ @BeforeEach
+ fun setup(@Autowired requestRepository: RequestRepository) {
+ this.requestRepository = requestRepository
+ this.requestService = RequestService(requestRepository)
+ }
+
+ @Test
+ fun shouldResultInEmptyRequestList() {
+ val actual = requestService.allRequestsByPatientPseudonym(TEST_PATIENT_PSEUDONYM)
+
+ assertThat(actual).isEmpty()
+ }
+
+ private fun setupTestData() {
+ // Prepare DB
+ this.requestRepository.saveAll(
+ listOf(
+ Request(
+ randomRequestId(),
+ PatientPseudonym("TEST_12345678901"),
+ PatientId("P1"),
+ Fingerprint("0123456789abcdef1"),
+ RequestType.MTB_FILE,
+ RequestStatus.SUCCESS,
+ Instant.parse("2023-07-07T02:00:00Z"),
+ ),
+ // Should be ignored - wrong patient ID -->
+ Request(
+ randomRequestId(),
+ PatientPseudonym("TEST_12345678902"),
+ PatientId("P2"),
+ Fingerprint("0123456789abcdef2"),
+ RequestType.MTB_FILE,
+ RequestStatus.WARNING,
+ Instant.parse("2023-08-08T00:00:00Z"),
+ ),
+ // <--
+ Request(
+ randomRequestId(),
+ PatientPseudonym("TEST_12345678901"),
+ PatientId("P2"),
+ Fingerprint("0123456789abcdee1"),
+ RequestType.DELETE,
+ RequestStatus.SUCCESS,
+ Instant.parse("2023-08-08T02:00:00Z"),
+ ),
)
- }
-
- @Test
- fun shouldResultInSortedRequestList() {
- setupTestData()
+ )
+ }
- val actual = requestService.allRequestsByPatientPseudonym(TEST_PATIENT_PSEUDONYM)
+ @Test
+ fun shouldResultInSortedRequestList() {
+ setupTestData()
- assertThat(actual).hasSize(2)
- assertThat(actual[0].fingerprint).isEqualTo(Fingerprint("0123456789abcdee1"))
- assertThat(actual[1].fingerprint).isEqualTo(Fingerprint("0123456789abcdef1"))
- }
+ val actual = requestService.allRequestsByPatientPseudonym(TEST_PATIENT_PSEUDONYM)
- @Test
- fun shouldReturnDeleteRequestAsLastRequest() {
- setupTestData()
+ assertThat(actual).hasSize(2)
+ assertThat(actual[0].fingerprint).isEqualTo(Fingerprint("0123456789abcdee1"))
+ assertThat(actual[1].fingerprint).isEqualTo(Fingerprint("0123456789abcdef1"))
+ }
- val actual = requestService.isLastRequestWithKnownStatusDeletion(TEST_PATIENT_PSEUDONYM)
+ @Test
+ fun shouldReturnDeleteRequestAsLastRequest() {
+ setupTestData()
- assertThat(actual).isTrue()
- }
+ val actual = requestService.isLastRequestWithKnownStatusDeletion(TEST_PATIENT_PSEUDONYM)
- @Test
- fun shouldReturnLastMtbFileRequest() {
- setupTestData()
+ assertThat(actual).isTrue()
+ }
- val actual = requestService.lastMtbFileRequestForPatientPseudonym(TEST_PATIENT_PSEUDONYM)
+ @Test
+ fun shouldReturnLastMtbFileRequest() {
+ setupTestData()
- assertThat(actual).isNotNull
- assertThat(actual?.fingerprint).isEqualTo(Fingerprint("0123456789abcdef1"))
- }
+ val actual = requestService.lastMtbFileRequestForPatientPseudonym(TEST_PATIENT_PSEUDONYM)
- companion object {
- val TEST_PATIENT_PSEUDONYM = PatientPseudonym("TEST_12345678901")
- }
+ assertThat(actual).isNotNull
+ assertThat(actual?.fingerprint).isEqualTo(Fingerprint("0123456789abcdef1"))
+ }
-} \ No newline at end of file
+ companion object {
+ val TEST_PATIENT_PSEUDONYM = PatientPseudonym("TEST_12345678901")
+ }
+}
diff --git a/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/ConfigControllerTest.kt b/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/ConfigControllerTest.kt
index 8e5d38e..c081319 100644
--- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/ConfigControllerTest.kt
+++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/ConfigControllerTest.kt
@@ -32,6 +32,7 @@ import dev.dnpm.etl.processor.security.TokenService
import dev.dnpm.etl.processor.security.UserRoleService
import dev.dnpm.etl.processor.services.RequestProcessor
import dev.dnpm.etl.processor.services.TransformationService
+import java.time.Instant
import org.assertj.core.api.Assertions.assertThat
import org.htmlunit.WebClient
import org.htmlunit.html.HtmlPage
@@ -64,320 +65,288 @@ import org.springframework.test.web.servlet.htmlunit.MockMvcWebClientBuilder
import org.springframework.web.context.WebApplicationContext
import reactor.core.publisher.Sinks
import reactor.test.StepVerifier
-import java.time.Instant
abstract class MockSink : Sinks.Many<Boolean>
@WebMvcTest(controllers = [ConfigController::class])
@ExtendWith(value = [MockitoExtension::class, SpringExtension::class])
@ContextConfiguration(
- classes = [
- ConfigController::class,
- AppConfiguration::class,
- AppSecurityConfiguration::class
- ]
-)
-@TestPropertySource(
- properties = [
- "app.pseudonymize.generator=BUILDIN"
- ]
+ classes = [ConfigController::class, AppConfiguration::class, AppSecurityConfiguration::class]
)
+@TestPropertySource(properties = ["app.pseudonymize.generator=BUILDIN"])
@MockitoBean(name = "configsUpdateProducer", types = [MockSink::class])
@MockitoBean(
- types = [
- Generator::class,
- MtbFileSender::class,
- RequestProcessor::class,
- TransformationService::class,
- GPasConnectionCheckService::class,
- RestConnectionCheckService::class,
- GIcsConnectionCheckService::class
- ]
+ types =
+ [
+ Generator::class,
+ MtbFileSender::class,
+ RequestProcessor::class,
+ TransformationService::class,
+ GPasConnectionCheckService::class,
+ RestConnectionCheckService::class,
+ GIcsConnectionCheckService::class,
+ ]
)
class ConfigControllerTest {
- private lateinit var mockMvc: MockMvc
- private lateinit var webClient: WebClient
+ private lateinit var mockMvc: MockMvc
+ private lateinit var webClient: WebClient
+
+ private lateinit var requestProcessor: RequestProcessor
+ private lateinit var connectionCheckUpdateProducer: Sinks.Many<ConnectionCheckResult>
+
+ @BeforeEach
+ fun setup(
+ @Autowired mockMvc: MockMvc,
+ @Autowired requestProcessor: RequestProcessor,
+ @Autowired connectionCheckUpdateProducer: Sinks.Many<ConnectionCheckResult>,
+ ) {
+ this.mockMvc = mockMvc
+ this.webClient = MockMvcWebClientBuilder.mockMvcSetup(mockMvc).build()
+ this.requestProcessor = requestProcessor
+ this.connectionCheckUpdateProducer = connectionCheckUpdateProducer
+
+ webClient.options.isThrowExceptionOnScriptError = false
+ }
+
+ @Test
+ fun testShouldRequestConfigPageIfLoggedIn() {
+ mockMvc
+ .get("/configs") {
+ with(user("admin").roles("ADMIN"))
+ accept(MediaType.TEXT_HTML)
+ }
+ .andExpect {
+ status { isOk() }
+ view { name("configs") }
+ }
+ }
+
+ @Test
+ fun testShouldRedirectToLoginPageIfNotLoggedIn() {
+ mockMvc
+ .get("/configs") {
+ with(anonymous())
+ accept(MediaType.TEXT_HTML)
+ }
+ .andExpect {
+ status { isFound() }
+ header { stringValues(HttpHeaders.LOCATION, "http://localhost/login") }
+ }
+ }
- private lateinit var requestProcessor: RequestProcessor
- private lateinit var connectionCheckUpdateProducer: Sinks.Many<ConnectionCheckResult>
+ @Nested
+ @TestPropertySource(
+ properties = ["app.security.enable-tokens=true", "app.security.admin-user=admin"]
+ )
+ @MockitoBean(types = [TokenService::class])
+ inner class WithTokensEnabled {
+ private lateinit var tokenService: TokenService
@BeforeEach
- fun setup(
- @Autowired mockMvc: MockMvc,
- @Autowired requestProcessor: RequestProcessor,
- @Autowired connectionCheckUpdateProducer: Sinks.Many<ConnectionCheckResult>
- ) {
- this.mockMvc = mockMvc
- this.webClient = MockMvcWebClientBuilder.mockMvcSetup(mockMvc).build()
- this.requestProcessor = requestProcessor
- this.connectionCheckUpdateProducer = connectionCheckUpdateProducer
-
- webClient.options.isThrowExceptionOnScriptError = false
+ fun setup(@Autowired tokenService: TokenService) {
+ webClient.options.isThrowExceptionOnScriptError = false
+
+ this.tokenService = tokenService
}
@Test
- fun testShouldRequestConfigPageIfLoggedIn() {
- mockMvc.get("/configs") {
+ fun testShouldSaveNewToken() {
+ mockMvc
+ .post("/configs/tokens") {
with(user("admin").roles("ADMIN"))
accept(MediaType.TEXT_HTML)
- }.andExpect {
- status { isOk() }
- view { name("configs") }
- }
+ contentType = MediaType.APPLICATION_FORM_URLENCODED
+ content = "name=Testtoken"
+ }
+ .andExpect {
+ status { is2xxSuccessful() }
+ view { name("configs/tokens") }
+ }
+
+ val captor = argumentCaptor<String>()
+ verify(tokenService, times(1)).addToken(captor.capture())
+
+ assertThat(captor.firstValue).isEqualTo("Testtoken")
}
@Test
- fun testShouldRedirectToLoginPageIfNotLoggedIn() {
- mockMvc.get("/configs") {
- with(anonymous())
+ fun testShouldNotSaveTokenWithExstingName() {
+ whenever(tokenService.addToken(anyString()))
+ .thenReturn(Result.failure(RuntimeException("Testfailure")))
+
+ mockMvc
+ .post("/configs/tokens") {
+ with(user("admin").roles("ADMIN"))
accept(MediaType.TEXT_HTML)
- }.andExpect {
- status { isFound() }
- header {
- stringValues(HttpHeaders.LOCATION, "http://localhost/login")
- }
- }
+ contentType = MediaType.APPLICATION_FORM_URLENCODED
+ content = "name=Testtoken"
+ }
+ .andExpect {
+ status { is2xxSuccessful() }
+ view { name("configs/tokens") }
+ }
+
+ val captor = argumentCaptor<String>()
+ verify(tokenService, times(1)).addToken(captor.capture())
+
+ assertThat(captor.firstValue).isEqualTo("Testtoken")
}
- @Nested
- @TestPropertySource(
- properties = [
- "app.security.enable-tokens=true",
- "app.security.admin-user=admin"
- ]
- )
- @MockitoBean(
- types = [
- TokenService::class
- ]
- )
- inner class WithTokensEnabled {
- private lateinit var tokenService: TokenService
-
- @BeforeEach
- fun setup(
- @Autowired tokenService: TokenService
- ) {
- webClient.options.isThrowExceptionOnScriptError = false
-
- this.tokenService = tokenService
- }
-
- @Test
- fun testShouldSaveNewToken() {
- mockMvc.post("/configs/tokens") {
- with(user("admin").roles("ADMIN"))
- accept(MediaType.TEXT_HTML)
- contentType = MediaType.APPLICATION_FORM_URLENCODED
- content = "name=Testtoken"
- }.andExpect {
- status { is2xxSuccessful() }
- view { name("configs/tokens") }
- }
-
- val captor = argumentCaptor<String>()
- verify(tokenService, times(1)).addToken(captor.capture())
-
- assertThat(captor.firstValue).isEqualTo("Testtoken")
- }
-
- @Test
- fun testShouldNotSaveTokenWithExstingName() {
- whenever(tokenService.addToken(anyString())).thenReturn(
- Result.failure(
- RuntimeException(
- "Testfailure"
- )
- )
- )
-
- mockMvc.post("/configs/tokens") {
- with(user("admin").roles("ADMIN"))
- accept(MediaType.TEXT_HTML)
- contentType = MediaType.APPLICATION_FORM_URLENCODED
- content = "name=Testtoken"
- }.andExpect {
- status { is2xxSuccessful() }
- view { name("configs/tokens") }
- }
-
- val captor = argumentCaptor<String>()
- verify(tokenService, times(1)).addToken(captor.capture())
-
- assertThat(captor.firstValue).isEqualTo("Testtoken")
- }
-
- @Test
- fun testShouldDeleteToken() {
- mockMvc.delete("/configs/tokens/42") {
- with(user("admin").roles("ADMIN"))
- accept(MediaType.TEXT_HTML)
- }.andExpect {
- status { is2xxSuccessful() }
- view { name("configs/tokens") }
- }
-
- val captor = argumentCaptor<Long>()
- verify(tokenService, times(1)).deleteToken(captor.capture())
+ @Test
+ fun testShouldDeleteToken() {
+ mockMvc
+ .delete("/configs/tokens/42") {
+ with(user("admin").roles("ADMIN"))
+ accept(MediaType.TEXT_HTML)
+ }
+ .andExpect {
+ status { is2xxSuccessful() }
+ view { name("configs/tokens") }
+ }
- assertThat(captor.firstValue).isEqualTo(42)
- }
+ val captor = argumentCaptor<Long>()
+ verify(tokenService, times(1)).deleteToken(captor.capture())
- @Test
- @WithMockUser(username = "admin", roles = ["ADMIN"])
- fun testShouldRenderConfigPageWithTokens() {
- val page = webClient.getPage<HtmlPage>("http://localhost/configs")
- assertThat(
- page.getElementById("tokens")
- ).isNotNull
- }
+ assertThat(captor.firstValue).isEqualTo(42)
}
- @Nested
- @TestPropertySource(
- properties = [
- "app.security.enable-tokens=false"
- ]
- )
- inner class WithTokensDisabled {
- @BeforeEach
- fun setup() {
- webClient.options.isThrowExceptionOnScriptError = false
- }
-
- @Test
- @WithMockUser(username = "admin", roles = ["ADMIN"])
- fun testShouldRenderConfigPageWithoutTokens() {
- val page = webClient.getPage<HtmlPage>("http://localhost/configs")
- assertThat(
- page.getElementById("tokens")
- ).isNull()
- }
+ @Test
+ @WithMockUser(username = "admin", roles = ["ADMIN"])
+ fun testShouldRenderConfigPageWithTokens() {
+ val page = webClient.getPage<HtmlPage>("http://localhost/configs")
+ assertThat(page.getElementById("tokens")).isNotNull
}
+ }
- @Nested
- @TestPropertySource(
- properties = [
- "app.security.enable-tokens=false",
- "app.security.admin-user=admin",
- "app.security.admin-password={noop}very-secret"
- ]
- )
- @MockitoBean(
- types = [
- UserRoleService::class
- ]
- )
- inner class WithUserRolesEnabled {
- private lateinit var userRoleService: UserRoleService
+ @Nested
+ @TestPropertySource(properties = ["app.security.enable-tokens=false"])
+ inner class WithTokensDisabled {
+ @BeforeEach
+ fun setup() {
+ webClient.options.isThrowExceptionOnScriptError = false
+ }
- @BeforeEach
- fun setup(
- @Autowired userRoleService: UserRoleService
- ) {
- webClient.options.isThrowExceptionOnScriptError = false
+ @Test
+ @WithMockUser(username = "admin", roles = ["ADMIN"])
+ fun testShouldRenderConfigPageWithoutTokens() {
+ val page = webClient.getPage<HtmlPage>("http://localhost/configs")
+ assertThat(page.getElementById("tokens")).isNull()
+ }
+ }
+
+ @Nested
+ @TestPropertySource(
+ properties =
+ [
+ "app.security.enable-tokens=false",
+ "app.security.admin-user=admin",
+ "app.security.admin-password={noop}very-secret",
+ ]
+ )
+ @MockitoBean(types = [UserRoleService::class])
+ inner class WithUserRolesEnabled {
+ private lateinit var userRoleService: UserRoleService
- this.userRoleService = userRoleService
- }
+ @BeforeEach
+ fun setup(@Autowired userRoleService: UserRoleService) {
+ webClient.options.isThrowExceptionOnScriptError = false
- @Test
- fun testShouldDeleteUserRole() {
- mockMvc.delete("/configs/userroles/42") {
- with(user("admin").roles("ADMIN"))
- accept(MediaType.TEXT_HTML)
- }.andExpect {
- status { is2xxSuccessful() }
- view { name("configs/userroles") }
- }
+ this.userRoleService = userRoleService
+ }
- val captor = argumentCaptor<Long>()
- verify(userRoleService, times(1)).deleteUserRole(captor.capture())
+ @Test
+ fun testShouldDeleteUserRole() {
+ mockMvc
+ .delete("/configs/userroles/42") {
+ with(user("admin").roles("ADMIN"))
+ accept(MediaType.TEXT_HTML)
+ }
+ .andExpect {
+ status { is2xxSuccessful() }
+ view { name("configs/userroles") }
+ }
- assertThat(captor.firstValue).isEqualTo(42)
- }
+ val captor = argumentCaptor<Long>()
+ verify(userRoleService, times(1)).deleteUserRole(captor.capture())
- @Test
- fun testShouldUpdateUserRole() {
- mockMvc.put("/configs/userroles/42") {
- with(user("admin").roles("ADMIN"))
- accept(MediaType.TEXT_HTML)
- contentType = MediaType.APPLICATION_FORM_URLENCODED
- content = "role=ADMIN"
- }.andExpect {
- status { is2xxSuccessful() }
- view { name("configs/userroles") }
- }
-
- val idCaptor = argumentCaptor<Long>()
- val roleCaptor = argumentCaptor<Role>()
- verify(userRoleService, times(1)).updateUserRole(
- idCaptor.capture(),
- roleCaptor.capture()
- )
-
- assertThat(idCaptor.firstValue).isEqualTo(42)
- assertThat(roleCaptor.firstValue).isEqualTo(Role.ADMIN)
- }
+ assertThat(captor.firstValue).isEqualTo(42)
+ }
- @Test
- @WithMockUser(username = "admin", roles = ["ADMIN"])
- fun testShouldRenderConfigPageWithUserRoles() {
- val page = webClient.getPage<HtmlPage>("http://localhost/configs")
- assertThat(
- page.getElementById("userroles")
- ).isNotNull
- }
+ @Test
+ fun testShouldUpdateUserRole() {
+ mockMvc
+ .put("/configs/userroles/42") {
+ with(user("admin").roles("ADMIN"))
+ accept(MediaType.TEXT_HTML)
+ contentType = MediaType.APPLICATION_FORM_URLENCODED
+ content = "role=ADMIN"
+ }
+ .andExpect {
+ status { is2xxSuccessful() }
+ view { name("configs/userroles") }
+ }
+
+ val idCaptor = argumentCaptor<Long>()
+ val roleCaptor = argumentCaptor<Role>()
+ verify(userRoleService, times(1)).updateUserRole(idCaptor.capture(), roleCaptor.capture())
+
+ assertThat(idCaptor.firstValue).isEqualTo(42)
+ assertThat(roleCaptor.firstValue).isEqualTo(Role.ADMIN)
}
- @Nested
- inner class WithUserRolesDisabled {
- @BeforeEach
- fun setup() {
- webClient.options.isThrowExceptionOnScriptError = false
- }
+ @Test
+ @WithMockUser(username = "admin", roles = ["ADMIN"])
+ fun testShouldRenderConfigPageWithUserRoles() {
+ val page = webClient.getPage<HtmlPage>("http://localhost/configs")
+ assertThat(page.getElementById("userroles")).isNotNull
+ }
+ }
- @Test
- fun testShouldRenderConfigPageWithoutUserRoles() {
- val page = webClient.getPage<HtmlPage>("http://localhost/configs")
- assertThat(
- page.getElementById("userroles")
- ).isNull()
- }
+ @Nested
+ inner class WithUserRolesDisabled {
+ @BeforeEach
+ fun setup() {
+ webClient.options.isThrowExceptionOnScriptError = false
}
- @Nested
- inner class SseTest {
- private lateinit var webClient: WebTestClient
+ @Test
+ fun testShouldRenderConfigPageWithoutUserRoles() {
+ val page = webClient.getPage<HtmlPage>("http://localhost/configs")
+ assertThat(page.getElementById("userroles")).isNull()
+ }
+ }
- @BeforeEach
- fun setup(
- applicationContext: WebApplicationContext
- ) {
- this.webClient = MockMvcWebTestClient
- .bindToApplicationContext(applicationContext).build()
- }
+ @Nested
+ inner class SseTest {
+ private lateinit var webClient: WebTestClient
- @Test
- fun testShouldRequestGPasSSE() {
- val expectedEvent =
- ConnectionCheckResult.GPasConnectionCheckResult(true, Instant.now(), Instant.now())
-
- connectionCheckUpdateProducer.tryEmitNext(expectedEvent)
- connectionCheckUpdateProducer.emitComplete { _, _ -> true }
-
- val result =
- webClient.get().uri("http://localhost/configs/events").accept(TEXT_EVENT_STREAM)
- .exchange()
- .expectStatus().isOk()
- .expectHeader().contentType(TEXT_EVENT_STREAM)
- .returnResult(ConnectionCheckResult.GPasConnectionCheckResult::class.java)
-
- StepVerifier.create(result.responseBody)
- .expectNext(expectedEvent)
- .expectComplete()
- .verify()
- }
+ @BeforeEach
+ fun setup(applicationContext: WebApplicationContext) {
+ this.webClient = MockMvcWebTestClient.bindToApplicationContext(applicationContext).build()
}
+ @Test
+ fun testShouldRequestGPasSSE() {
+ val expectedEvent =
+ ConnectionCheckResult.GPasConnectionCheckResult(true, Instant.now(), Instant.now())
+
+ connectionCheckUpdateProducer.tryEmitNext(expectedEvent)
+ connectionCheckUpdateProducer.emitComplete { _, _ -> true }
+
+ val result =
+ webClient
+ .get()
+ .uri("http://localhost/configs/events")
+ .accept(TEXT_EVENT_STREAM)
+ .exchange()
+ .expectStatus()
+ .isOk()
+ .expectHeader()
+ .contentType(TEXT_EVENT_STREAM)
+ .returnResult(ConnectionCheckResult.GPasConnectionCheckResult::class.java)
+
+ StepVerifier.create(result.responseBody).expectNext(expectedEvent).expectComplete().verify()
+ }
+ }
}
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 628112c..33fc9d2 100644
--- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/HomeControllerTest.kt
+++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/HomeControllerTest.kt
@@ -27,6 +27,9 @@ 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.services.RequestService
+import java.io.IOException
+import java.time.Instant
+import java.util.*
import org.assertj.core.api.Assertions.assertThat
import org.htmlunit.WebClient
import org.htmlunit.html.HtmlPage
@@ -51,261 +54,248 @@ import org.springframework.test.context.junit.jupiter.SpringExtension
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.get
import org.springframework.test.web.servlet.htmlunit.MockMvcWebClientBuilder
-import java.io.IOException
-import java.time.Instant
-import java.util.*
@WebMvcTest(controllers = [HomeController::class])
@ExtendWith(value = [MockitoExtension::class, SpringExtension::class])
@ContextConfiguration(
- classes = [
- HomeController::class,
- AppConfiguration::class,
- AppSecurityConfiguration::class
- ]
+ classes = [HomeController::class, AppConfiguration::class, AppSecurityConfiguration::class]
)
@TestPropertySource(
- properties = [
- "app.pseudonymize.generator=BUILDIN",
- "app.security.admin-user=admin",
- "app.security.admin-password={noop}very-secret"
- ]
-)
-@MockitoBean(
- types = [RequestService::class]
+ properties =
+ [
+ "app.pseudonymize.generator=BUILDIN",
+ "app.security.admin-user=admin",
+ "app.security.admin-password={noop}very-secret",
+ ]
)
+@MockitoBean(types = [RequestService::class])
class HomeControllerTest {
- private lateinit var mockMvc: MockMvc
- private lateinit var webClient: WebClient
+ private lateinit var mockMvc: MockMvc
+ private lateinit var webClient: WebClient
- @BeforeEach
- fun setup(
- @Autowired mockMvc: MockMvc,
- @Autowired requestService: RequestService
- ) {
- this.mockMvc = mockMvc
- this.webClient = MockMvcWebClientBuilder.mockMvcSetup(mockMvc).build()
+ @BeforeEach
+ fun setup(@Autowired mockMvc: MockMvc, @Autowired requestService: RequestService) {
+ this.mockMvc = mockMvc
+ this.webClient = MockMvcWebClientBuilder.mockMvcSetup(mockMvc).build()
- whenever(requestService.findAll(any<Pageable>())).thenReturn(Page.empty())
- }
+ whenever(requestService.findAll(any<Pageable>())).thenReturn(Page.empty())
+ }
- @Test
- fun testShouldRequestHomePage() {
- mockMvc.get("/").andExpect {
- status { isOk() }
- view { name("index") }
- }
+ @Test
+ fun testShouldRequestHomePage() {
+ mockMvc.get("/").andExpect {
+ status { isOk() }
+ view { name("index") }
}
+ }
- @Nested
- inner class WithRequests {
-
- private lateinit var requestService: RequestService
-
- @BeforeEach
- fun setup(
- @Autowired requestService: RequestService
- ) {
- this.requestService = requestService
- }
-
- @Test
- fun testShouldShowHomePage() {
- whenever(requestService.findAll(any<Pageable>())).thenReturn(
- PageImpl(
- listOf(
- Request(
- 2L,
- randomRequestId(),
- PatientPseudonym("PSEUDO1"),
- PatientId("PATIENT1"),
- Fingerprint("ashdkasdh"),
- RequestType.MTB_FILE,
- RequestStatus.SUCCESS
- ),
- Request(
- 1L,
- randomRequestId(),
- PatientPseudonym("PSEUDO1"),
- PatientId("PATIENT1"),
- Fingerprint("asdasdasd"),
- RequestType.MTB_FILE,
- RequestStatus.ERROR
- )
- )
- )
- )
-
- val page = webClient.getPage<HtmlPage>("http://localhost/")
- assertThat(page.querySelectorAll("tbody tr")).hasSize(2)
- assertThat(page.querySelectorAll("div.notification.info")).isEmpty()
- }
-
- @Test
- @WithMockUser(username = "admin", roles = ["ADMIN"])
- fun testShouldShowRequestDetails() {
- val requestId = randomRequestId()
-
- whenever(requestService.findByUuid(anyValueClass())).thenReturn(
- Optional.of(
- Request(
- 2L,
- requestId,
- PatientPseudonym("PSEUDO1"),
- PatientId("PATIENT1"),
- Fingerprint("ashdkasdh"),
- RequestType.MTB_FILE,
- RequestStatus.SUCCESS,
- Instant.now(),
- Report("Test")
- )
- )
- )
-
- val page = webClient.getPage<HtmlPage>("http://localhost/report/${requestId.value}")
- assertThat(page.querySelectorAll("tbody tr")).hasSize(1)
- assertThat(page.querySelectorAll("div.notification.info")).isEmpty()
- }
-
- @Test
- @WithMockUser(username = "admin", roles = ["ADMIN"])
- fun testShouldShowPatientDetails() {
- whenever(requestService.findRequestByPatientId(anyValueClass(), any<Pageable>())).thenReturn(
- PageImpl(
- listOf(
- Request(
- 2L,
- randomRequestId(),
- PatientPseudonym("PSEUDO1"),
- PatientId("PATIENT1"),
- Fingerprint("ashdkasdh"),
- RequestType.MTB_FILE,
- RequestStatus.SUCCESS
- ),
- Request(
- 1L,
- randomRequestId(),
- PatientPseudonym("PSEUDO1"),
- PatientId("PATIENT1"),
- Fingerprint("asdasdasd"),
- RequestType.MTB_FILE,
- RequestStatus.ERROR
- )
- )
- )
- )
-
- val page = webClient.getPage<HtmlPage>("http://localhost/patient/PSEUDO1")
- assertThat(page.querySelectorAll("tbody tr")).hasSize(2)
- assertThat(page.querySelectorAll("div.notification.info")).isEmpty()
- }
+ @Nested
+ inner class WithRequests {
- @Test
- @WithMockUser(username = "admin", roles = ["ADMIN"])
- fun testShouldShowPatientPseudonym() {
- whenever(requestService.findRequestByPatientId(anyValueClass(), any<Pageable>())).thenReturn(
- PageImpl(
- listOf(
- Request(
- 2L,
- randomRequestId(),
- PatientPseudonym("PSEUDO1"),
- PatientId("PATIENT1"),
- Fingerprint("ashdkasdh"),
- RequestType.MTB_FILE,
- RequestStatus.SUCCESS
- ),
- Request(
- 1L,
- randomRequestId(),
- PatientPseudonym("PSEUDO1"),
- PatientId("PATIENT1"),
- Fingerprint("asdasdasd"),
- RequestType.MTB_FILE,
- RequestStatus.ERROR
- )
- )
- )
- )
+ private lateinit var requestService: RequestService
- val page = webClient.getPage<HtmlPage>("http://localhost/patient/PSEUDO1")
- assertThat(page.querySelectorAll("h2 > span")).hasSize(1)
- assertThat(page.querySelectorAll("h2 > span").first().textContent).isEqualTo("PSEUDO1")
- }
+ @BeforeEach
+ fun setup(@Autowired requestService: RequestService) {
+ this.requestService = requestService
+ }
+ @Test
+ fun testShouldShowHomePage() {
+ whenever(requestService.findAll(any<Pageable>()))
+ .thenReturn(
+ PageImpl(
+ listOf(
+ Request(
+ 2L,
+ randomRequestId(),
+ PatientPseudonym("PSEUDO1"),
+ PatientId("PATIENT1"),
+ Fingerprint("ashdkasdh"),
+ RequestType.MTB_FILE,
+ RequestStatus.SUCCESS,
+ ),
+ Request(
+ 1L,
+ randomRequestId(),
+ PatientPseudonym("PSEUDO1"),
+ PatientId("PATIENT1"),
+ Fingerprint("asdasdasd"),
+ RequestType.MTB_FILE,
+ RequestStatus.ERROR,
+ ),
+ )
+ )
+ )
+
+ val page = webClient.getPage<HtmlPage>("http://localhost/")
+ assertThat(page.querySelectorAll("tbody tr")).hasSize(2)
+ assertThat(page.querySelectorAll("div.notification.info")).isEmpty()
}
- @Nested
- inner class WithoutRequests {
+ @Test
+ @WithMockUser(username = "admin", roles = ["ADMIN"])
+ fun testShouldShowRequestDetails() {
+ val requestId = randomRequestId()
+
+ whenever(requestService.findByUuid(anyValueClass()))
+ .thenReturn(
+ Optional.of(
+ Request(
+ 2L,
+ requestId,
+ PatientPseudonym("PSEUDO1"),
+ PatientId("PATIENT1"),
+ Fingerprint("ashdkasdh"),
+ RequestType.MTB_FILE,
+ RequestStatus.SUCCESS,
+ Instant.now(),
+ Report("Test"),
+ )
+ )
+ )
+
+ val page = webClient.getPage<HtmlPage>("http://localhost/report/${requestId.value}")
+ assertThat(page.querySelectorAll("tbody tr")).hasSize(1)
+ assertThat(page.querySelectorAll("div.notification.info")).isEmpty()
+ }
- private lateinit var requestService: RequestService
+ @Test
+ @WithMockUser(username = "admin", roles = ["ADMIN"])
+ fun testShouldShowPatientDetails() {
+ whenever(requestService.findRequestByPatientId(anyValueClass(), any<Pageable>()))
+ .thenReturn(
+ PageImpl(
+ listOf(
+ Request(
+ 2L,
+ randomRequestId(),
+ PatientPseudonym("PSEUDO1"),
+ PatientId("PATIENT1"),
+ Fingerprint("ashdkasdh"),
+ RequestType.MTB_FILE,
+ RequestStatus.SUCCESS,
+ ),
+ Request(
+ 1L,
+ randomRequestId(),
+ PatientPseudonym("PSEUDO1"),
+ PatientId("PATIENT1"),
+ Fingerprint("asdasdasd"),
+ RequestType.MTB_FILE,
+ RequestStatus.ERROR,
+ ),
+ )
+ )
+ )
+
+ val page = webClient.getPage<HtmlPage>("http://localhost/patient/PSEUDO1")
+ assertThat(page.querySelectorAll("tbody tr")).hasSize(2)
+ assertThat(page.querySelectorAll("div.notification.info")).isEmpty()
+ }
- @BeforeEach
- fun setup(
- @Autowired requestService: RequestService
- ) {
- this.requestService = requestService
+ @Test
+ @WithMockUser(username = "admin", roles = ["ADMIN"])
+ fun testShouldShowPatientPseudonym() {
+ whenever(requestService.findRequestByPatientId(anyValueClass(), any<Pageable>()))
+ .thenReturn(
+ PageImpl(
+ listOf(
+ Request(
+ 2L,
+ randomRequestId(),
+ PatientPseudonym("PSEUDO1"),
+ PatientId("PATIENT1"),
+ Fingerprint("ashdkasdh"),
+ RequestType.MTB_FILE,
+ RequestStatus.SUCCESS,
+ ),
+ Request(
+ 1L,
+ randomRequestId(),
+ PatientPseudonym("PSEUDO1"),
+ PatientId("PATIENT1"),
+ Fingerprint("asdasdasd"),
+ RequestType.MTB_FILE,
+ RequestStatus.ERROR,
+ ),
+ )
+ )
+ )
+
+ val page = webClient.getPage<HtmlPage>("http://localhost/patient/PSEUDO1")
+ assertThat(page.querySelectorAll("h2 > span")).hasSize(1)
+ assertThat(page.querySelectorAll("h2 > span").first().textContent).isEqualTo("PSEUDO1")
+ }
+ }
- whenever(requestService.findAll(any<Pageable>())).thenReturn(Page.empty())
- }
+ @Nested
+ inner class WithoutRequests {
- @Test
- fun testShouldShowHomePage() {
- val page = webClient.getPage<HtmlPage>("http://localhost/")
- assertThat(page.querySelectorAll("tbody tr")).isEmpty()
- assertThat(page.querySelectorAll("div.notification.info")).hasSize(1)
- }
+ private lateinit var requestService: RequestService
- @Test
- @WithMockUser(username = "admin", roles = ["ADMIN"])
- fun testShouldThrowNotFoundExceptionForUnknownReport() {
- val requestId = randomRequestId()
+ @BeforeEach
+ fun setup(@Autowired requestService: RequestService) {
+ this.requestService = requestService
- whenever(requestService.findByUuid(anyValueClass())).thenReturn(
- Optional.empty()
- )
+ whenever(requestService.findAll(any<Pageable>())).thenReturn(Page.empty())
+ }
- assertThrows<IOException> {
- webClient.getPage<HtmlPage>("http://localhost/report/${requestId.value}")
- }.also {
- assertThat(it).hasRootCauseInstanceOf(NotFoundException::class.java)
- }
- }
+ @Test
+ fun testShouldShowHomePage() {
+ val page = webClient.getPage<HtmlPage>("http://localhost/")
+ assertThat(page.querySelectorAll("tbody tr")).isEmpty()
+ assertThat(page.querySelectorAll("div.notification.info")).hasSize(1)
+ }
- @Test
- @WithMockUser(username = "admin", roles = ["ADMIN"])
- fun testShouldShowEmptyPatientDetails() {
- whenever(requestService.findRequestByPatientId(anyValueClass(), any<Pageable>())).thenReturn(Page.empty())
+ @Test
+ @WithMockUser(username = "admin", roles = ["ADMIN"])
+ fun testShouldThrowNotFoundExceptionForUnknownReport() {
+ val requestId = randomRequestId()
- val page = webClient.getPage<HtmlPage>("http://localhost/patient/PSEUDO1")
- assertThat(page.querySelectorAll("tbody tr")).isEmpty()
- assertThat(page.querySelectorAll("div.notification.info")).hasSize(1)
- }
+ whenever(requestService.findByUuid(anyValueClass())).thenReturn(Optional.empty())
- @Test
- @WithMockUser(username = "admin", roles = ["ADMIN"])
- fun testShouldShowNoConsentStatusBadge() {
- whenever(requestService.findRequestByPatientId(anyValueClass(), any<Pageable>())).thenReturn(
- PageImpl(
- listOf(
- Request(
- 1L,
- randomRequestId(),
- PatientPseudonym("PSEUDO1"),
- PatientId("PATIENT1"),
- Fingerprint("ashdkasdh"),
- RequestType.MTB_FILE,
- RequestStatus.NO_CONSENT
- )
- )
- )
- )
+ assertThrows<IOException> {
+ webClient.getPage<HtmlPage>("http://localhost/report/${requestId.value}")
+ }
+ .also { assertThat(it).hasRootCauseInstanceOf(NotFoundException::class.java) }
+ }
- val page = webClient.getPage<HtmlPage>("http://localhost/patient/PSEUDO1")
- assertThat(page.querySelectorAll("tbody tr")).hasSize(1)
- assertThat(page.querySelectorAll("tbody tr > td > small").first().textContent).isEqualTo("NO_CONSENT")
- }
+ @Test
+ @WithMockUser(username = "admin", roles = ["ADMIN"])
+ fun testShouldShowEmptyPatientDetails() {
+ whenever(requestService.findRequestByPatientId(anyValueClass(), any<Pageable>()))
+ .thenReturn(Page.empty())
+
+ val page = webClient.getPage<HtmlPage>("http://localhost/patient/PSEUDO1")
+ assertThat(page.querySelectorAll("tbody tr")).isEmpty()
+ assertThat(page.querySelectorAll("div.notification.info")).hasSize(1)
}
+ @Test
+ @WithMockUser(username = "admin", roles = ["ADMIN"])
+ fun testShouldShowNoConsentStatusBadge() {
+ whenever(requestService.findRequestByPatientId(anyValueClass(), any<Pageable>()))
+ .thenReturn(
+ PageImpl(
+ listOf(
+ Request(
+ 1L,
+ randomRequestId(),
+ PatientPseudonym("PSEUDO1"),
+ PatientId("PATIENT1"),
+ Fingerprint("ashdkasdh"),
+ RequestType.MTB_FILE,
+ RequestStatus.NO_CONSENT,
+ )
+ )
+ )
+ )
+
+ val page = webClient.getPage<HtmlPage>("http://localhost/patient/PSEUDO1")
+ assertThat(page.querySelectorAll("tbody tr")).hasSize(1)
+ assertThat(page.querySelectorAll("tbody tr > td > small").first().textContent)
+ .isEqualTo("NO_CONSENT")
+ }
+ }
}
diff --git a/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/LoginControllerTest.kt b/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/LoginControllerTest.kt
index 54ad6e8..bd8014a 100644
--- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/LoginControllerTest.kt
+++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/LoginControllerTest.kt
@@ -42,30 +42,26 @@ import org.springframework.test.web.servlet.htmlunit.MockMvcWebClientBuilder
@WebMvcTest(controllers = [LoginController::class])
@ExtendWith(value = [MockitoExtension::class, SpringExtension::class])
@ContextConfiguration(
- classes = [
- LoginController::class,
- AppConfiguration::class,
- AppSecurityConfiguration::class
- ]
+ classes = [LoginController::class, AppConfiguration::class, AppSecurityConfiguration::class],
)
@TestPropertySource(
- properties = [
- "app.pseudonymize.generator=BUILDIN",
- "app.security.admin-user=admin",
- "app.security.admin-password={noop}very-secret",
- "app.security.enable-tokens=true"
- ]
-)
-@MockitoBean(
- types = [TokenService::class]
+ properties =
+ [
+ "app.pseudonymize.generator=BUILDIN",
+ "app.security.admin-user=admin",
+ "app.security.admin-password={noop}very-secret",
+ "app.security.enable-tokens=true",
+ ],
)
+@MockitoBean(types = [TokenService::class])
class LoginControllerTest {
-
private lateinit var mockMvc: MockMvc
private lateinit var webClient: WebClient
@BeforeEach
- fun setup(@Autowired mockMvc: MockMvc) {
+ fun setup(
+ @Autowired mockMvc: MockMvc,
+ ) {
this.mockMvc = mockMvc
this.webClient = MockMvcWebClientBuilder.mockMvcSetup(mockMvc).build()
}
@@ -82,7 +78,11 @@ class LoginControllerTest {
fun testShouldShowLoginForm() {
val page = webClient.getPage<HtmlPage>("http://localhost/login")
assertThat(
- page.getElementsByTagName("main").first().firstElementChild.getAttribute("class")
+ page
+ .getElementsByTagName("main")
+ .first()
+ .firstElementChild
+ .getAttribute("class"),
).isEqualTo("login-form")
}
}
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 b55a702..f91e3cf 100644
--- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/StatisticsControllerTest.kt
+++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/StatisticsControllerTest.kt
@@ -38,26 +38,25 @@ import org.springframework.test.web.servlet.htmlunit.MockMvcWebClientBuilder
@WebMvcTest(controllers = [StatisticsController::class])
@ExtendWith(value = [MockitoExtension::class, SpringExtension::class])
@ContextConfiguration(
- classes = [
- StatisticsController::class,
- AppConfiguration::class,
- AppSecurityConfiguration::class
- ]
+ classes =
+ [StatisticsController::class, AppConfiguration::class, AppSecurityConfiguration::class],
)
@TestPropertySource(
- properties = [
- "app.pseudonymize.generator=BUILDIN",
- "app.security.admin-user=admin",
- "app.security.admin-password={noop}very-secret"
- ]
+ properties =
+ [
+ "app.pseudonymize.generator=BUILDIN",
+ "app.security.admin-user=admin",
+ "app.security.admin-password={noop}very-secret",
+ ],
)
class StatisticsControllerTest {
-
private lateinit var mockMvc: MockMvc
private lateinit var webClient: WebClient
@BeforeEach
- fun setup(@Autowired mockMvc: MockMvc) {
+ fun setup(
+ @Autowired mockMvc: MockMvc,
+ ) {
this.mockMvc = mockMvc
this.webClient = MockMvcWebClientBuilder.mockMvcSetup(mockMvc).build()
}
@@ -69,5 +68,4 @@ class StatisticsControllerTest {
view { name("statistics") }
}
}
-
}
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 f0c3b63..16a9464 100644
--- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/StatisticsRestControllerTest.kt
+++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/StatisticsRestControllerTest.kt
@@ -57,28 +57,22 @@ import java.time.Instant
import java.time.ZoneId
import java.time.temporal.ChronoUnit
-
@WebMvcTest(controllers = [StatisticsRestController::class])
@ExtendWith(value = [MockitoExtension::class, SpringExtension::class])
@ContextConfiguration(
- classes = [
- StatisticsRestController::class,
- AppConfiguration::class,
- AppSecurityConfiguration::class
- ]
+ classes =
+ [StatisticsRestController::class, AppConfiguration::class, AppSecurityConfiguration::class],
)
@TestPropertySource(
- properties = [
- "app.pseudonymize.generator=BUILDIN",
- "app.security.admin-user=admin",
- "app.security.admin-password={noop}very-secret"
- ]
-)
-@MockitoBean(
- types = [RequestService::class]
+ properties =
+ [
+ "app.pseudonymize.generator=BUILDIN",
+ "app.security.admin-user=admin",
+ "app.security.admin-password={noop}very-secret",
+ ],
)
+@MockitoBean(types = [RequestService::class])
class StatisticsRestControllerTest {
-
private lateinit var mockMvc: MockMvc
private lateinit var statisticsUpdateProducer: Sinks.Many<Any>
@@ -88,7 +82,7 @@ class StatisticsRestControllerTest {
fun setup(
@Autowired mockMvc: MockMvc,
@Autowired statisticsUpdateProducer: Sinks.Many<Any>,
- @Autowired requestService: RequestService
+ @Autowired requestService: RequestService,
) {
this.mockMvc = mockMvc
this.statisticsUpdateProducer = statisticsUpdateProducer
@@ -100,40 +94,38 @@ class StatisticsRestControllerTest {
@Test
fun testShouldRequestStatesForMtbFiles() {
doAnswer { _ ->
- listOf(
- CountedState(42, RequestStatus.WARNING),
- CountedState(1, RequestStatus.UNKNOWN)
- )
- }.whenever(requestService).countStates()
+ listOf(CountedState(42, RequestStatus.WARNING), CountedState(1, RequestStatus.UNKNOWN))
+ }.whenever(requestService)
+ .countStates()
mockMvc.get("/statistics/requeststates").andExpect {
- status { isOk() }.also {
- jsonPath("$", hasSize<Int>(2))
- jsonPath("$[0].name", equalTo(RequestStatus.WARNING.name))
- jsonPath("$[0].value", equalTo(42))
- jsonPath("$[1].name", equalTo(RequestStatus.UNKNOWN.name))
- jsonPath("$[1].value", equalTo(1))
- }
+ status { isOk() }
+ .also {
+ jsonPath("$", hasSize<Int>(2))
+ jsonPath("$[0].name", equalTo(RequestStatus.WARNING.name))
+ jsonPath("$[0].value", equalTo(42))
+ jsonPath("$[1].name", equalTo(RequestStatus.UNKNOWN.name))
+ jsonPath("$[1].value", equalTo(1))
+ }
}
}
@Test
fun testShouldRequestStatesForDeletes() {
doAnswer { _ ->
- listOf(
- CountedState(42, RequestStatus.SUCCESS),
- CountedState(1, RequestStatus.ERROR)
- )
- }.whenever(requestService).countDeleteStates()
+ listOf(CountedState(42, RequestStatus.SUCCESS), CountedState(1, RequestStatus.ERROR))
+ }.whenever(requestService)
+ .countDeleteStates()
mockMvc.get("/statistics/requeststates?delete=true").andExpect {
- status { isOk() }.also {
- jsonPath("$", hasSize<Int>(2))
- jsonPath("$[0].name", equalTo(RequestStatus.SUCCESS.name))
- jsonPath("$[0].value", equalTo(42))
- jsonPath("$[1].name", equalTo(RequestStatus.ERROR.name))
- jsonPath("$[1].value", equalTo(1))
- }
+ status { isOk() }
+ .also {
+ jsonPath("$", hasSize<Int>(2))
+ jsonPath("$[0].name", equalTo(RequestStatus.SUCCESS.name))
+ jsonPath("$[0].value", equalTo(42))
+ jsonPath("$[1].name", equalTo(RequestStatus.ERROR.name))
+ jsonPath("$[1].value", equalTo(1))
+ }
}
}
}
@@ -143,47 +135,44 @@ class StatisticsRestControllerTest {
@Test
fun testShouldRequestPatientStatesForMtbFiles() {
doAnswer { _ ->
- listOf(
- CountedState(42, RequestStatus.WARNING),
- CountedState(1, RequestStatus.UNKNOWN)
- )
- }.whenever(requestService).findPatientUniqueStates()
+ listOf(CountedState(42, RequestStatus.WARNING), CountedState(1, RequestStatus.UNKNOWN))
+ }.whenever(requestService)
+ .findPatientUniqueStates()
mockMvc.get("/statistics/requestpatientstates").andExpect {
- status { isOk() }.also {
- jsonPath("$", hasSize<Int>(2))
- jsonPath("$[0].name", equalTo(RequestStatus.WARNING.name))
- jsonPath("$[0].value", equalTo(42))
- jsonPath("$[1].name", equalTo(RequestStatus.UNKNOWN.name))
- jsonPath("$[1].value", equalTo(1))
- }
+ status { isOk() }
+ .also {
+ jsonPath("$", hasSize<Int>(2))
+ jsonPath("$[0].name", equalTo(RequestStatus.WARNING.name))
+ jsonPath("$[0].value", equalTo(42))
+ jsonPath("$[1].name", equalTo(RequestStatus.UNKNOWN.name))
+ jsonPath("$[1].value", equalTo(1))
+ }
}
}
@Test
fun testShouldRequestPatientStatesForDeletes() {
doAnswer { _ ->
- listOf(
- CountedState(42, RequestStatus.SUCCESS),
- CountedState(1, RequestStatus.ERROR)
- )
- }.whenever(requestService).findPatientUniqueDeleteStates()
+ listOf(CountedState(42, RequestStatus.SUCCESS), CountedState(1, RequestStatus.ERROR))
+ }.whenever(requestService)
+ .findPatientUniqueDeleteStates()
mockMvc.get("/statistics/requestpatientstates?delete=true").andExpect {
- status { isOk() }.also {
- jsonPath("$", hasSize<Int>(2))
- jsonPath("$[0].name", equalTo(RequestStatus.SUCCESS.name))
- jsonPath("$[0].value", equalTo(42))
- jsonPath("$[1].name", equalTo(RequestStatus.ERROR.name))
- jsonPath("$[1].value", equalTo(1))
- }
+ status { isOk() }
+ .also {
+ jsonPath("$", hasSize<Int>(2))
+ jsonPath("$[0].name", equalTo(RequestStatus.SUCCESS.name))
+ jsonPath("$[0].value", equalTo(42))
+ jsonPath("$[1].name", equalTo(RequestStatus.ERROR.name))
+ jsonPath("$[1].value", equalTo(1))
+ }
}
}
}
@Nested
inner class LastMonthStatesTest {
-
@BeforeEach
fun setup() {
val zoneId = ZoneId.of("Europe/Berlin")
@@ -197,7 +186,12 @@ class StatisticsRestControllerTest {
Fingerprint("0123456789abcdef1"),
RequestType.MTB_FILE,
RequestStatus.SUCCESS,
- Instant.now().atZone(zoneId).truncatedTo(ChronoUnit.DAYS).minus(2, ChronoUnit.DAYS).toInstant()
+ Instant
+ .now()
+ .atZone(zoneId)
+ .truncatedTo(ChronoUnit.DAYS)
+ .minus(2, ChronoUnit.DAYS)
+ .toInstant(),
),
Request(
2,
@@ -207,7 +201,12 @@ class StatisticsRestControllerTest {
Fingerprint("0123456789abcdef2"),
RequestType.MTB_FILE,
RequestStatus.WARNING,
- Instant.now().atZone(zoneId).truncatedTo(ChronoUnit.DAYS).minus(2, ChronoUnit.DAYS).toInstant()
+ Instant
+ .now()
+ .atZone(zoneId)
+ .truncatedTo(ChronoUnit.DAYS)
+ .minus(2, ChronoUnit.DAYS)
+ .toInstant(),
),
Request(
3,
@@ -217,7 +216,12 @@ class StatisticsRestControllerTest {
Fingerprint("0123456789abcdee1"),
RequestType.DELETE,
RequestStatus.ERROR,
- Instant.now().atZone(zoneId).truncatedTo(ChronoUnit.DAYS).minus(1, ChronoUnit.DAYS).toInstant()
+ Instant
+ .now()
+ .atZone(zoneId)
+ .truncatedTo(ChronoUnit.DAYS)
+ .minus(1, ChronoUnit.DAYS)
+ .toInstant(),
),
Request(
4,
@@ -227,7 +231,12 @@ class StatisticsRestControllerTest {
Fingerprint("0123456789abcdef2"),
RequestType.MTB_FILE,
RequestStatus.DUPLICATION,
- Instant.now().atZone(zoneId).truncatedTo(ChronoUnit.DAYS).minus(1, ChronoUnit.DAYS).toInstant()
+ Instant
+ .now()
+ .atZone(zoneId)
+ .truncatedTo(ChronoUnit.DAYS)
+ .minus(1, ChronoUnit.DAYS)
+ .toInstant(),
),
Request(
5,
@@ -237,49 +246,54 @@ class StatisticsRestControllerTest {
Fingerprint("0123456789abcdef2"),
RequestType.DELETE,
RequestStatus.UNKNOWN,
- Instant.now().atZone(zoneId).truncatedTo(ChronoUnit.DAYS).toInstant()
+ Instant
+ .now()
+ .atZone(zoneId)
+ .truncatedTo(ChronoUnit.DAYS)
+ .toInstant(),
),
)
- }.whenever(requestService).findAll()
+ }.whenever(requestService)
+ .findAll()
}
@Test
fun testShouldRequestLastMonthForMtbFiles() {
mockMvc.get("/statistics/requestslastmonth").andExpect {
- status { isOk() }.also {
- jsonPath("$", hasSize<Int>(31))
- }.also {
- jsonPath("$[28].nameValues.error", equalTo(0))
- jsonPath("$[28].nameValues.warning", equalTo(1))
- jsonPath("$[28].nameValues.success", equalTo(1))
- jsonPath("$[28].nameValues.duplication", equalTo(0))
- jsonPath("$[28].nameValues.unknown", equalTo(0))
- jsonPath("$[29].nameValues.error", equalTo(0))
- jsonPath("$[29].nameValues.warning", equalTo(0))
- jsonPath("$[29].nameValues.success", equalTo(0))
- jsonPath("$[29].nameValues.duplication", equalTo(1))
- jsonPath("$[29].nameValues.unknown", equalTo(0))
- }
+ status { isOk() }
+ .also { jsonPath("$", hasSize<Int>(31)) }
+ .also {
+ jsonPath("$[28].nameValues.error", equalTo(0))
+ jsonPath("$[28].nameValues.warning", equalTo(1))
+ jsonPath("$[28].nameValues.success", equalTo(1))
+ jsonPath("$[28].nameValues.duplication", equalTo(0))
+ jsonPath("$[28].nameValues.unknown", equalTo(0))
+ jsonPath("$[29].nameValues.error", equalTo(0))
+ jsonPath("$[29].nameValues.warning", equalTo(0))
+ jsonPath("$[29].nameValues.success", equalTo(0))
+ jsonPath("$[29].nameValues.duplication", equalTo(1))
+ jsonPath("$[29].nameValues.unknown", equalTo(0))
+ }
}
}
@Test
fun testShouldRequestLastMonthForDeletes() {
mockMvc.get("/statistics/requestslastmonth?delete=true").andExpect {
- status { isOk() }.also {
- jsonPath("$", hasSize<Int>(31))
- }.also {
- jsonPath("$[29].nameValues.error", equalTo(1))
- jsonPath("$[29].nameValues.warning", equalTo(0))
- jsonPath("$[29].nameValues.success", equalTo(0))
- jsonPath("$[29].nameValues.duplication", equalTo(0))
- jsonPath("$[29].nameValues.unknown", equalTo(0))
- jsonPath("$[30].nameValues.error", equalTo(0))
- jsonPath("$[30].nameValues.warning", equalTo(0))
- jsonPath("$[30].nameValues.success", equalTo(0))
- jsonPath("$[30].nameValues.duplication", equalTo(0))
- jsonPath("$[30].nameValues.unknown", equalTo(1))
- }
+ status { isOk() }
+ .also { jsonPath("$", hasSize<Int>(31)) }
+ .also {
+ jsonPath("$[29].nameValues.error", equalTo(1))
+ jsonPath("$[29].nameValues.warning", equalTo(0))
+ jsonPath("$[29].nameValues.success", equalTo(0))
+ jsonPath("$[29].nameValues.duplication", equalTo(0))
+ jsonPath("$[29].nameValues.unknown", equalTo(0))
+ jsonPath("$[30].nameValues.error", equalTo(0))
+ jsonPath("$[30].nameValues.warning", equalTo(0))
+ jsonPath("$[30].nameValues.success", equalTo(0))
+ jsonPath("$[30].nameValues.duplication", equalTo(0))
+ jsonPath("$[30].nameValues.unknown", equalTo(1))
+ }
}
}
}
@@ -289,26 +303,27 @@ class StatisticsRestControllerTest {
private lateinit var webClient: WebTestClient
@BeforeEach
- fun setup(
- applicationContext: WebApplicationContext,
- ) {
- this.webClient = MockMvcWebTestClient
- .bindToApplicationContext(applicationContext).build()
+ fun setup(applicationContext: WebApplicationContext) {
+ this.webClient = MockMvcWebTestClient.bindToApplicationContext(applicationContext).build()
}
@Test
fun testShouldRequestSSE() {
statisticsUpdateProducer.emitComplete { _, _ -> true }
- val result = webClient.get().uri("http://localhost/statistics/events").accept(TEXT_EVENT_STREAM).exchange()
- .expectStatus().isOk()
- .expectHeader().contentType(TEXT_EVENT_STREAM)
- .returnResult(String::class.java)
+ val result =
+ webClient
+ .get()
+ .uri("http://localhost/statistics/events")
+ .accept(TEXT_EVENT_STREAM)
+ .exchange()
+ .expectStatus()
+ .isOk()
+ .expectHeader()
+ .contentType(TEXT_EVENT_STREAM)
+ .returnResult(String::class.java)
- StepVerifier.create(result.responseBody)
- .expectComplete()
- .verify()
+ StepVerifier.create(result.responseBody).expectComplete().verify()
}
}
-
}
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/Exceptions.kt b/src/main/kotlin/dev/dnpm/etl/processor/Exceptions.kt
index 32d0954..1c590fc 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/Exceptions.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/Exceptions.kt
@@ -19,4 +19,4 @@
package dev.dnpm.etl.processor
-class NotFoundException : RuntimeException() \ No newline at end of file
+class NotFoundException : RuntimeException()
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/ServletInitializer.kt b/src/main/kotlin/dev/dnpm/etl/processor/ServletInitializer.kt
index 2618b09..e35cddf 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/ServletInitializer.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/ServletInitializer.kt
@@ -23,9 +23,6 @@ import org.springframework.boot.builder.SpringApplicationBuilder
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer
class ServletInitializer : SpringBootServletInitializer() {
-
- override fun configure(application: SpringApplicationBuilder): SpringApplicationBuilder {
- return application.sources(EtlProcessorApplication::class.java)
- }
-
+ override fun configure(application: SpringApplicationBuilder): SpringApplicationBuilder =
+ application.sources(EtlProcessorApplication::class.java)
}
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 57ab06c..8786c34 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfigProperties.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfigProperties.kt
@@ -28,7 +28,7 @@ data class AppConfigProperties(
var transformations: List<TransformationProperties> = listOf(),
var maxRetryAttempts: Int = 3,
var duplicationDetection: Boolean = true,
- var genomDeTestSubmission: Boolean = false
+ var genomDeTestSubmission: Boolean = false,
) {
companion object {
const val NAME = "app"
@@ -49,8 +49,7 @@ data class PseudonymizeConfigProperties(
data class GPasConfigProperties(
val uri: String?,
val soapEndpoint: String?,
- @get:DeprecatedConfigurationProperty(since = "0.12")
- val pidDomain: String?,
+ @get:DeprecatedConfigurationProperty(since = "0.12") val pidDomain: String?,
val patientDomain: String = pidDomain ?: "etl-processor",
val genomDeTanDomain: String = "ccdn",
val username: String?,
@@ -63,7 +62,7 @@ data class GPasConfigProperties(
@ConfigurationProperties(ConsentConfigProperties.NAME)
data class ConsentConfigProperties(
- var service: ConsentService = ConsentService.NONE
+ var service: ConsentService = ConsentService.NONE,
) {
companion object {
const val NAME = "app.consent"
@@ -72,60 +71,33 @@ data class ConsentConfigProperties(
@ConfigurationProperties(GIcsConfigProperties.NAME)
data class GIcsConfigProperties(
- /**
- * Base URL to gICS System
- *
- */
+ /** Base URL to gICS System */
val uri: String?,
val username: String? = null,
val password: String? = null,
-
/**
* gICS specific system
- * **/
+ * *
+ */
val personIdentifierSystem: String =
"https://ths-greifswald.de/fhir/gics/identifiers/Patienten-ID",
-
- /**
- * Domain of broad consent resources
- **/
+ /** Domain of broad consent resources */
val broadConsentDomainName: String = "MII",
-
- /**
- * Domain of Modelvorhaben 64e consent resources
- **/
+ /** Domain of Modelvorhaben 64e consent resources */
val genomDeConsentDomainName: String = "GenomDE_MV",
-
- /**
- * Value to expect in case of positiv consent
- */
+ /** Value to expect in case of positiv consent */
val broadConsentPolicyCode: String = "2.16.840.1.113883.3.1937.777.24.5.3.6",
-
- /**
- * Consent Policy which should be used for consent check
- */
+ /** Consent Policy which should be used for consent check */
val broadConsentPolicySystem: String = "urn:oid:2.16.840.1.113883.3.1937.777.24.5.3",
-
- /**
- * Consent Policy uri for MII Broad Consent Version
- */
+ /** Consent Policy uri for MII Broad Consent Version */
val broadConsentPolicyUri: String = "urn:oid:2.16.840.1.113883.3.1937.777.24.2.1790",
-
- /**
- * Value to expect in case of positiv consent
- */
+ /** Value to expect in case of positiv consent */
val genomeDePolicyCode: String = "sequencing",
-
- /**
- * Consent Policy which should be used for consent check
- */
- val genomeDePolicySystem: String = "https://ths-greifswald.de/fhir/CodeSystem/gics/Policy/GenomDE_MV",
-
- /**
- * Consent version (fixed version)
- *
- */
- val genomeDeConsentVersion: String = "2.0"
+ /** Consent Policy which should be used for consent check */
+ val genomeDePolicySystem: String =
+ "https://ths-greifswald.de/fhir/CodeSystem/gics/Policy/GenomDE_MV",
+ /** Consent version (fixed version) */
+ val genomeDeConsentVersion: String = "2.0",
) {
companion object {
const val NAME = "app.consent.gics"
@@ -136,7 +108,7 @@ data class GIcsConfigProperties(
data class RestTargetProperties(
val uri: String?,
val username: String?,
- val password: String?
+ val password: String?,
) {
companion object {
const val NAME = "app.rest"
@@ -149,7 +121,7 @@ data class KafkaProperties(
val outputTopic: String = "etl-processor",
val outputResponseTopic: String = "${outputTopic}_response",
val groupId: String = "${outputTopic}_group",
- val servers: String = ""
+ val servers: String = "",
) {
companion object {
const val NAME = "app.kafka"
@@ -162,7 +134,7 @@ data class SecurityConfigProperties(
val adminPassword: String?,
val enableTokens: Boolean = false,
val enableOidc: Boolean = false,
- val defaultNewUserRole: Role = Role.USER
+ val defaultNewUserRole: Role = Role.USER,
) {
companion object {
const val NAME = "app.security"
@@ -171,16 +143,16 @@ data class SecurityConfigProperties(
enum class PseudonymGenerator {
BUILDIN,
- GPAS
+ GPAS,
}
enum class ConsentService {
NONE,
- GICS
+ GICS,
}
data class TransformationProperties(
val path: String,
val from: String,
- val to: String
+ val to: String,
)
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfiguration.kt b/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfiguration.kt
index de302fd..36b0a75 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfiguration.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfiguration.kt
@@ -30,6 +30,8 @@ import dev.dnpm.etl.processor.security.TokenService
import dev.dnpm.etl.processor.services.ConsentProcessor
import dev.dnpm.etl.processor.services.Transformation
import dev.dnpm.etl.processor.services.TransformationService
+import kotlin.time.Duration.Companion.seconds
+import kotlin.time.toJavaDuration
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean
import org.slf4j.LoggerFactory
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition
@@ -53,258 +55,251 @@ import org.springframework.security.provisioning.InMemoryUserDetailsManager
import org.springframework.web.client.HttpClientErrorException
import org.springframework.web.client.RestTemplate
import reactor.core.publisher.Sinks
-import kotlin.time.Duration.Companion.seconds
-import kotlin.time.toJavaDuration
-
@Configuration
@EnableConfigurationProperties(
- value = [
- AppConfigProperties::class,
- PseudonymizeConfigProperties::class,
- GPasConfigProperties::class,
- ConsentConfigProperties::class,
- GIcsConfigProperties::class
- ]
+ value =
+ [
+ AppConfigProperties::class,
+ PseudonymizeConfigProperties::class,
+ GPasConfigProperties::class,
+ ConsentConfigProperties::class,
+ GIcsConfigProperties::class,
+ ]
)
@EnableScheduling
class AppConfiguration {
- private val logger = LoggerFactory.getLogger(AppConfiguration::class.java)
+ private val logger = LoggerFactory.getLogger(AppConfiguration::class.java)
- @Bean
- fun restTemplate(): RestTemplate {
- return RestTemplate()
- }
+ @Bean
+ fun restTemplate(): RestTemplate {
+ return RestTemplate()
+ }
- @Bean
- fun appFhirConfig(): AppFhirConfig {
- return AppFhirConfig()
- }
+ @Bean
+ fun appFhirConfig(): AppFhirConfig {
+ return AppFhirConfig()
+ }
- @ConditionalOnProperty(value = ["app.pseudonymize.generator"], havingValue = "GPAS")
- @ConditionalOnProperty(value = ["app.pseudonymize.gpas.soap-endpoint"])
- @Bean
- fun gpasSoapProxyFactoryBean(gpasConfigProperties: GPasConfigProperties): JaxWsProxyFactoryBean {
- val proxyFactory = JaxWsProxyFactoryBean()
- proxyFactory.serviceClass = GpasSoapService::class.java
- proxyFactory.address = gpasConfigProperties.soapEndpoint
- return proxyFactory
- }
+ @ConditionalOnProperty(value = ["app.pseudonymize.generator"], havingValue = "GPAS")
+ @ConditionalOnProperty(value = ["app.pseudonymize.gpas.soap-endpoint"])
+ @Bean
+ fun gpasSoapProxyFactoryBean(gpasConfigProperties: GPasConfigProperties): JaxWsProxyFactoryBean {
+ val proxyFactory = JaxWsProxyFactoryBean()
+ proxyFactory.serviceClass = GpasSoapService::class.java
+ proxyFactory.address = gpasConfigProperties.soapEndpoint
+ return proxyFactory
+ }
- @ConditionalOnProperty(value = ["app.pseudonymize.generator"], havingValue = "GPAS")
- @ConditionalOnProperty(value = ["app.pseudonymize.gpas.soap-endpoint"])
- @Bean
- fun gpasSoapProxy(gpasConfigProperties: GPasConfigProperties): GpasSoapService {
- return gpasSoapProxyFactoryBean(gpasConfigProperties).create() as GpasSoapService
- }
+ @ConditionalOnProperty(value = ["app.pseudonymize.generator"], havingValue = "GPAS")
+ @ConditionalOnProperty(value = ["app.pseudonymize.gpas.soap-endpoint"])
+ @Bean
+ fun gpasSoapProxy(gpasConfigProperties: GPasConfigProperties): GpasSoapService {
+ return gpasSoapProxyFactoryBean(gpasConfigProperties).create() as GpasSoapService
+ }
- @ConditionalOnProperty(value = ["app.pseudonymize.generator"], havingValue = "GPAS")
- @ConditionalOnProperty(value = ["app.pseudonymize.gpas.soap-endpoint"])
- @Bean
- fun gpasSoapPseudonymGenerator(
- configProperties: GPasConfigProperties,
- retryTemplate: RetryTemplate,
- gpasSoapService: GpasSoapService,
- appFhirConfig: AppFhirConfig
- ): Generator {
- logger.info("Selected 'GpasSoapPseudonym Generator'")
- return GpasSoapPseudonymGenerator(configProperties, retryTemplate, gpasSoapService, appFhirConfig)
- }
-
- @ConditionalOnProperty(value = ["app.pseudonymize.generator"], havingValue = "GPAS")
- @ConditionalOnProperty(value = ["app.pseudonymize.gpas.uri"])
- @Bean
- fun gpasPseudonymGenerator(
- configProperties: GPasConfigProperties,
- retryTemplate: RetryTemplate,
- restTemplate: RestTemplate,
- appFhirConfig: AppFhirConfig
- ): Generator {
- logger.info("Selected 'GpasPseudonym Generator'")
- return GpasPseudonymGenerator(configProperties, retryTemplate, restTemplate, appFhirConfig)
- }
-
- @ConditionalOnProperty(
- value = ["app.pseudonymize.generator"],
- havingValue = "BUILDIN",
- matchIfMissing = true
+ @ConditionalOnProperty(value = ["app.pseudonymize.generator"], havingValue = "GPAS")
+ @ConditionalOnProperty(value = ["app.pseudonymize.gpas.soap-endpoint"])
+ @Bean
+ fun gpasSoapPseudonymGenerator(
+ configProperties: GPasConfigProperties,
+ retryTemplate: RetryTemplate,
+ gpasSoapService: GpasSoapService,
+ appFhirConfig: AppFhirConfig,
+ ): Generator {
+ logger.info("Selected 'GpasSoapPseudonym Generator'")
+ return GpasSoapPseudonymGenerator(
+ configProperties,
+ retryTemplate,
+ gpasSoapService,
+ appFhirConfig,
)
- @Bean
- fun buildinPseudonymGenerator(): Generator {
- logger.info("Selected 'BUILDIN Pseudonym Generator'")
- return AnonymizingGenerator()
- }
+ }
- @Bean
- fun pseudonymizeService(
- generator: Generator,
- pseudonymizeConfigProperties: PseudonymizeConfigProperties
- ): PseudonymizeService {
- return PseudonymizeService(generator, pseudonymizeConfigProperties)
- }
+ @ConditionalOnProperty(value = ["app.pseudonymize.generator"], havingValue = "GPAS")
+ @ConditionalOnProperty(value = ["app.pseudonymize.gpas.uri"])
+ @Bean
+ fun gpasPseudonymGenerator(
+ configProperties: GPasConfigProperties,
+ retryTemplate: RetryTemplate,
+ restTemplate: RestTemplate,
+ appFhirConfig: AppFhirConfig,
+ ): Generator {
+ logger.info("Selected 'GpasPseudonym Generator'")
+ return GpasPseudonymGenerator(configProperties, retryTemplate, restTemplate, appFhirConfig)
+ }
- @Bean
- fun reportService(): ReportService {
- return ReportService(getObjectMapper())
- }
+ @ConditionalOnProperty(
+ value = ["app.pseudonymize.generator"],
+ havingValue = "BUILDIN",
+ matchIfMissing = true,
+ )
+ @Bean
+ fun buildinPseudonymGenerator(): Generator {
+ logger.info("Selected 'BUILDIN Pseudonym Generator'")
+ return AnonymizingGenerator()
+ }
- @Bean
- fun getObjectMapper(): ObjectMapper {
- return JacksonConfig().objectMapper()
- }
+ @Bean
+ fun pseudonymizeService(
+ generator: Generator,
+ pseudonymizeConfigProperties: PseudonymizeConfigProperties,
+ ): PseudonymizeService {
+ return PseudonymizeService(generator, pseudonymizeConfigProperties)
+ }
- @Bean
- fun transformationService(
- configProperties: AppConfigProperties
- ): TransformationService {
- logger.info("Apply ${configProperties.transformations.size} transformation rules")
- return TransformationService(getObjectMapper(), configProperties.transformations.map {
- Transformation.of(it.path) from it.from to it.to
- })
- }
+ @Bean
+ fun reportService(): ReportService {
+ return ReportService(getObjectMapper())
+ }
- @Bean
- fun retryTemplate(configProperties: AppConfigProperties): RetryTemplate {
- return RetryTemplateBuilder()
- .notRetryOn(IllegalArgumentException::class.java)
- .notRetryOn(HttpClientErrorException.BadRequest::class.java)
- .notRetryOn(HttpClientErrorException.UnprocessableEntity::class.java)
- .exponentialBackoff(2.seconds.toJavaDuration(), 1.25, 5.seconds.toJavaDuration())
- .customPolicy(SimpleRetryPolicy(configProperties.maxRetryAttempts))
- .withListener(object : RetryListener {
- override fun <T : Any, E : Throwable> onError(
- context: RetryContext,
- callback: RetryCallback<T, E>,
- throwable: Throwable
- ) {
- logger.warn(
- "Error occured: {}. Retrying {}",
- throwable.message,
- context.retryCount
- )
- }
- })
- .build()
- }
+ @Bean
+ fun getObjectMapper(): ObjectMapper {
+ return JacksonConfig().objectMapper()
+ }
- @ConditionalOnProperty(value = ["app.security.enable-tokens"], havingValue = "true")
- @Bean
- fun tokenService(
- userDetailsManager: InMemoryUserDetailsManager,
- passwordEncoder: PasswordEncoder,
- tokenRepository: TokenRepository
- ): TokenService {
- return TokenService(userDetailsManager, passwordEncoder, tokenRepository)
- }
+ @Bean
+ fun transformationService(configProperties: AppConfigProperties): TransformationService {
+ logger.info("Apply ${configProperties.transformations.size} transformation rules")
+ return TransformationService(
+ getObjectMapper(),
+ configProperties.transformations.map { Transformation.of(it.path) from it.from to it.to },
+ )
+ }
- @Bean
- fun statisticsUpdateProducer(): Sinks.Many<Any> {
- return Sinks.many().multicast().directBestEffort()
- }
+ @Bean
+ fun retryTemplate(configProperties: AppConfigProperties): RetryTemplate {
+ return RetryTemplateBuilder()
+ .notRetryOn(IllegalArgumentException::class.java)
+ .notRetryOn(HttpClientErrorException.BadRequest::class.java)
+ .notRetryOn(HttpClientErrorException.UnprocessableEntity::class.java)
+ .exponentialBackoff(2.seconds.toJavaDuration(), 1.25, 5.seconds.toJavaDuration())
+ .customPolicy(SimpleRetryPolicy(configProperties.maxRetryAttempts))
+ .withListener(
+ object : RetryListener {
+ override fun <T : Any, E : Throwable> onError(
+ context: RetryContext,
+ callback: RetryCallback<T, E>,
+ throwable: Throwable,
+ ) {
+ logger.warn("Error occured: {}. Retrying {}", throwable.message, context.retryCount)
+ }
+ }
+ )
+ .build()
+ }
- @Bean
- fun connectionCheckUpdateProducer(): Sinks.Many<ConnectionCheckResult> {
- return Sinks.many().multicast().onBackpressureBuffer()
- }
+ @ConditionalOnProperty(value = ["app.security.enable-tokens"], havingValue = "true")
+ @Bean
+ fun tokenService(
+ userDetailsManager: InMemoryUserDetailsManager,
+ passwordEncoder: PasswordEncoder,
+ tokenRepository: TokenRepository,
+ ): TokenService {
+ return TokenService(userDetailsManager, passwordEncoder, tokenRepository)
+ }
- @ConditionalOnProperty(value = ["app.pseudonymize.generator"], havingValue = "GPAS")
- @Bean
- fun gPasConnectionCheckService(
- restTemplate: RestTemplate,
- gPasConfigProperties: GPasConfigProperties,
- connectionCheckUpdateProducer: Sinks.Many<ConnectionCheckResult>
- ): ConnectionCheckService {
- return GPasConnectionCheckService(
- restTemplate,
- gPasConfigProperties,
- connectionCheckUpdateProducer
- )
- }
+ @Bean
+ fun statisticsUpdateProducer(): Sinks.Many<Any> {
+ return Sinks.many().multicast().directBestEffort()
+ }
- @ConditionalOnProperty(value = ["app.pseudonymizer"], havingValue = "GPAS")
- @ConditionalOnMissingBean
- @Bean
- fun gPasConnectionCheckServiceOnDeprecatedProperty(
- restTemplate: RestTemplate,
- gPasConfigProperties: GPasConfigProperties,
- connectionCheckUpdateProducer: Sinks.Many<ConnectionCheckResult>
- ): ConnectionCheckService {
- return GPasConnectionCheckService(
- restTemplate,
- gPasConfigProperties,
- connectionCheckUpdateProducer
- )
- }
+ @Bean
+ fun connectionCheckUpdateProducer(): Sinks.Many<ConnectionCheckResult> {
+ return Sinks.many().multicast().onBackpressureBuffer()
+ }
- @Bean
- fun jdbcConfiguration(): AbstractJdbcConfiguration {
- return AppJdbcConfiguration()
- }
+ @ConditionalOnProperty(value = ["app.pseudonymize.generator"], havingValue = "GPAS")
+ @Bean
+ fun gPasConnectionCheckService(
+ restTemplate: RestTemplate,
+ gPasConfigProperties: GPasConfigProperties,
+ connectionCheckUpdateProducer: Sinks.Many<ConnectionCheckResult>,
+ ): ConnectionCheckService {
+ return GPasConnectionCheckService(
+ restTemplate,
+ gPasConfigProperties,
+ connectionCheckUpdateProducer,
+ )
+ }
- @Conditional(GicsEnabledCondition::class)
- @Bean
- fun gicsConsentService(
- gIcsConfigProperties: GIcsConfigProperties,
- retryTemplate: RetryTemplate,
- restTemplate: RestTemplate,
- appFhirConfig: AppFhirConfig
- ): IConsentService {
- return GicsConsentService(
- gIcsConfigProperties,
- retryTemplate,
- restTemplate,
- appFhirConfig
- )
- }
+ @ConditionalOnProperty(value = ["app.pseudonymizer"], havingValue = "GPAS")
+ @ConditionalOnMissingBean
+ @Bean
+ fun gPasConnectionCheckServiceOnDeprecatedProperty(
+ restTemplate: RestTemplate,
+ gPasConfigProperties: GPasConfigProperties,
+ connectionCheckUpdateProducer: Sinks.Many<ConnectionCheckResult>,
+ ): ConnectionCheckService {
+ return GPasConnectionCheckService(
+ restTemplate,
+ gPasConfigProperties,
+ connectionCheckUpdateProducer,
+ )
+ }
- @Conditional(GicsEnabledCondition::class)
- @Bean
- fun consentProcessor(
- configProperties: AppConfigProperties,
- gIcsConfigProperties: GIcsConfigProperties,
- getObjectMapper: ObjectMapper,
- appFhirConfig: AppFhirConfig,
- gicsConsentService: IConsentService
- ): ConsentProcessor {
- return ConsentProcessor(
- configProperties,
- gIcsConfigProperties,
- getObjectMapper,
- appFhirConfig.fhirContext(),
- gicsConsentService
- )
- }
+ @Bean
+ fun jdbcConfiguration(): AbstractJdbcConfiguration {
+ return AppJdbcConfiguration()
+ }
- @Conditional(GicsEnabledCondition::class)
- @Bean
- fun gIcsConnectionCheckService(
- restTemplate: RestTemplate,
- gIcsConfigProperties: GIcsConfigProperties,
- connectionCheckUpdateProducer: Sinks.Many<ConnectionCheckResult>
- ): ConnectionCheckService {
- return GIcsConnectionCheckService(
- restTemplate,
- gIcsConfigProperties,
- connectionCheckUpdateProducer
- )
- }
+ @Conditional(GicsEnabledCondition::class)
+ @Bean
+ fun gicsConsentService(
+ gIcsConfigProperties: GIcsConfigProperties,
+ retryTemplate: RetryTemplate,
+ restTemplate: RestTemplate,
+ appFhirConfig: AppFhirConfig,
+ ): IConsentService {
+ return GicsConsentService(gIcsConfigProperties, retryTemplate, restTemplate, appFhirConfig)
+ }
- @Bean
- @ConditionalOnMissingBean
- fun iGetConsentService(): IConsentService {
- return MtbFileConsentService()
- }
+ @Conditional(GicsEnabledCondition::class)
+ @Bean
+ fun consentProcessor(
+ configProperties: AppConfigProperties,
+ gIcsConfigProperties: GIcsConfigProperties,
+ getObjectMapper: ObjectMapper,
+ appFhirConfig: AppFhirConfig,
+ gicsConsentService: IConsentService,
+ ): ConsentProcessor {
+ return ConsentProcessor(
+ configProperties,
+ gIcsConfigProperties,
+ getObjectMapper,
+ appFhirConfig.fhirContext(),
+ gicsConsentService,
+ )
+ }
+ @Conditional(GicsEnabledCondition::class)
+ @Bean
+ fun gIcsConnectionCheckService(
+ restTemplate: RestTemplate,
+ gIcsConfigProperties: GIcsConfigProperties,
+ connectionCheckUpdateProducer: Sinks.Many<ConnectionCheckResult>,
+ ): ConnectionCheckService {
+ return GIcsConnectionCheckService(
+ restTemplate,
+ gIcsConfigProperties,
+ connectionCheckUpdateProducer,
+ )
+ }
+
+ @Bean
+ @ConditionalOnMissingBean
+ fun iGetConsentService(): IConsentService {
+ return MtbFileConsentService()
+ }
}
class GicsEnabledCondition :
AnyNestedCondition(ConfigurationCondition.ConfigurationPhase.REGISTER_BEAN) {
- @ConditionalOnProperty(name = ["app.consent.service"], havingValue = "gics")
- @ConditionalOnProperty(name = ["app.consent.gics.uri"])
- class OnGicsServiceSelected {
- // Just for Condition
- }
-
+ @ConditionalOnProperty(name = ["app.consent.service"], havingValue = "gics")
+ @ConditionalOnProperty(name = ["app.consent.gics.uri"])
+ class OnGicsServiceSelected {
+ // Just for Condition
+ }
}
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/config/AppFhirConfig.kt b/src/main/kotlin/dev/dnpm/etl/processor/config/AppFhirConfig.kt
index 2b5ff8f..052822e 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/config/AppFhirConfig.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/config/AppFhirConfig.kt
@@ -4,13 +4,9 @@ import ca.uhn.fhir.context.FhirContext
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
-
@Configuration
class AppFhirConfig {
private val fhirCtx: FhirContext = FhirContext.forR4()
- @Bean
- fun fhirContext(): FhirContext {
- return fhirCtx
- }
-} \ No newline at end of file
+ @Bean fun fhirContext(): FhirContext = fhirCtx
+}
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/config/AppJdbcConfiguration.kt b/src/main/kotlin/dev/dnpm/etl/processor/config/AppJdbcConfiguration.kt
index 898982c..769faf3 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/config/AppJdbcConfiguration.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/config/AppJdbcConfiguration.kt
@@ -7,19 +7,13 @@ import org.springframework.data.jdbc.repository.config.AbstractJdbcConfiguration
@Configuration
class AppJdbcConfiguration : AbstractJdbcConfiguration() {
- override fun userConverters(): MutableList<*> {
- return mutableListOf(StringToFingerprintConverter(), FingerprintToStringConverter())
- }
+ override fun userConverters(): MutableList<*> = mutableListOf(StringToFingerprintConverter(), FingerprintToStringConverter())
}
class StringToFingerprintConverter : Converter<String, Fingerprint> {
- override fun convert(source: String): Fingerprint {
- return Fingerprint(source)
- }
+ override fun convert(source: String): Fingerprint = Fingerprint(source)
}
class FingerprintToStringConverter : Converter<Fingerprint, String> {
- override fun convert(source: Fingerprint): String {
- return source.value
- }
-} \ No newline at end of file
+ override fun convert(source: Fingerprint): String = source.value
+}
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/config/AppKafkaConfiguration.kt b/src/main/kotlin/dev/dnpm/etl/processor/config/AppKafkaConfiguration.kt
index 6551713..2f89dea 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/config/AppKafkaConfiguration.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/config/AppKafkaConfiguration.kt
@@ -45,14 +45,11 @@ import org.springframework.retry.support.RetryTemplate
import reactor.core.publisher.Sinks
@Configuration
-@EnableConfigurationProperties(
- value = [KafkaProperties::class]
-)
+@EnableConfigurationProperties(value = [KafkaProperties::class])
@ConditionalOnProperty(value = ["app.kafka.servers"])
@ConditionalOnMissingBean(MtbFileSender::class)
@Order(-5)
class AppKafkaConfiguration {
-
private val logger = LoggerFactory.getLogger(AppKafkaConfiguration::class.java)
@Bean
@@ -60,7 +57,7 @@ class AppKafkaConfiguration {
kafkaTemplate: KafkaTemplate<String, String>,
kafkaProperties: KafkaProperties,
retryTemplate: RetryTemplate,
- objectMapper: ObjectMapper
+ objectMapper: ObjectMapper,
): MtbFileSender {
logger.info("Selected 'KafkaMtbFileSender'")
return KafkaMtbFileSender(kafkaTemplate, kafkaProperties, retryTemplate, objectMapper)
@@ -70,7 +67,7 @@ class AppKafkaConfiguration {
fun kafkaResponseListenerContainer(
consumerFactory: ConsumerFactory<String, String>,
kafkaProperties: KafkaProperties,
- kafkaResponseProcessor: KafkaResponseProcessor
+ kafkaResponseProcessor: KafkaResponseProcessor,
): KafkaMessageListenerContainer<String, String> {
val containerProperties = ContainerProperties(kafkaProperties.outputResponseTopic)
containerProperties.messageListener = kafkaResponseProcessor
@@ -80,17 +77,15 @@ class AppKafkaConfiguration {
@Bean
fun kafkaResponseProcessor(
applicationEventPublisher: ApplicationEventPublisher,
- objectMapper: ObjectMapper
- ): KafkaResponseProcessor {
- return KafkaResponseProcessor(applicationEventPublisher, objectMapper)
- }
+ objectMapper: ObjectMapper,
+ ): KafkaResponseProcessor = KafkaResponseProcessor(applicationEventPublisher, objectMapper)
@Bean
@ConditionalOnProperty(value = ["app.kafka.input-topic"])
fun kafkaInputListenerContainer(
consumerFactory: ConsumerFactory<String, String>,
kafkaProperties: KafkaProperties,
- kafkaInputListener: KafkaInputListener
+ kafkaInputListener: KafkaInputListener,
): KafkaMessageListenerContainer<String, String> {
val containerProperties = ContainerProperties(kafkaProperties.inputTopic)
containerProperties.messageListener = kafkaInputListener
@@ -102,17 +97,16 @@ class AppKafkaConfiguration {
fun kafkaInputListener(
requestProcessor: RequestProcessor,
objectMapper: ObjectMapper,
- consentEvaluator: ConsentEvaluator
- ): KafkaInputListener {
- return KafkaInputListener(requestProcessor, consentEvaluator, objectMapper)
- }
+ consentEvaluator: ConsentEvaluator,
+ ): KafkaInputListener = KafkaInputListener(requestProcessor, consentEvaluator, objectMapper)
@Bean
fun kafkaConnectionCheckService(
consumerFactory: ConsumerFactory<String, String>,
- connectionCheckUpdateProducer: Sinks.Many<ConnectionCheckResult>
- ): ConnectionCheckService {
- return KafkaConnectionCheckService(consumerFactory.createConsumer(), connectionCheckUpdateProducer)
- }
-
+ connectionCheckUpdateProducer: Sinks.Many<ConnectionCheckResult>,
+ ): ConnectionCheckService =
+ KafkaConnectionCheckService(
+ consumerFactory.createConsumer(),
+ connectionCheckUpdateProducer,
+ )
}
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 62c25bc..565209e 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/config/AppRestConfiguration.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/config/AppRestConfiguration.kt
@@ -37,16 +37,11 @@ import org.springframework.web.client.RestTemplate
import reactor.core.publisher.Sinks
@Configuration
-@EnableConfigurationProperties(
- value = [
- RestTargetProperties::class
- ]
-)
+@EnableConfigurationProperties(value = [RestTargetProperties::class])
@ConditionalOnProperty(value = ["app.rest.uri"])
@ConditionalOnMissingBean(MtbFileSender::class)
@Order(-10)
class AppRestConfiguration {
-
private val logger = LoggerFactory.getLogger(AppRestConfiguration::class.java)
@Bean
@@ -64,10 +59,11 @@ class AppRestConfiguration {
fun restConnectionCheckService(
restTemplate: RestTemplate,
restTargetProperties: RestTargetProperties,
- connectionCheckUpdateProducer: Sinks.Many<ConnectionCheckResult>
- ): ConnectionCheckService {
- return RestConnectionCheckService(restTemplate, restTargetProperties, connectionCheckUpdateProducer)
- }
-
+ connectionCheckUpdateProducer: Sinks.Many<ConnectionCheckResult>,
+ ): ConnectionCheckService =
+ RestConnectionCheckService(
+ restTemplate,
+ restTargetProperties,
+ connectionCheckUpdateProducer,
+ )
}
-
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/config/AppSecurityConfiguration.kt b/src/main/kotlin/dev/dnpm/etl/processor/config/AppSecurityConfiguration.kt
index b2bd044..d44303b 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/config/AppSecurityConfiguration.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/config/AppSecurityConfiguration.kt
@@ -22,6 +22,7 @@ package dev.dnpm.etl.processor.config
import dev.dnpm.etl.processor.security.UserRole
import dev.dnpm.etl.processor.security.UserRoleRepository
import dev.dnpm.etl.processor.security.UserRoleService
+import java.util.*
import org.slf4j.LoggerFactory
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.boot.context.properties.EnableConfigurationProperties
@@ -41,158 +42,146 @@ import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority
import org.springframework.security.provisioning.InMemoryUserDetailsManager
import org.springframework.security.web.SecurityFilterChain
-import java.util.*
-
private const val LOGIN_PATH = "/login"
@Configuration
-@EnableConfigurationProperties(
- value = [
- SecurityConfigProperties::class
- ]
-)
+@EnableConfigurationProperties(value = [SecurityConfigProperties::class])
@ConditionalOnProperty(value = ["app.security.admin-user"])
@EnableWebSecurity
-class AppSecurityConfiguration(
- private val securityConfigProperties: SecurityConfigProperties
-) {
+class AppSecurityConfiguration(private val securityConfigProperties: SecurityConfigProperties) {
- private val logger = LoggerFactory.getLogger(AppSecurityConfiguration::class.java)
+ private val logger = LoggerFactory.getLogger(AppSecurityConfiguration::class.java)
- @Bean
- fun userDetailsService(passwordEncoder: PasswordEncoder): InMemoryUserDetailsManager {
- val adminUser = if (securityConfigProperties.adminUser.isNullOrBlank()) {
- logger.warn("Using random Admin User: admin")
- "admin"
+ @Bean
+ fun userDetailsService(passwordEncoder: PasswordEncoder): InMemoryUserDetailsManager {
+ val adminUser =
+ if (securityConfigProperties.adminUser.isNullOrBlank()) {
+ logger.warn("Using random Admin User: admin")
+ "admin"
} else {
- securityConfigProperties.adminUser
+ securityConfigProperties.adminUser
}
- val adminPassword = if (securityConfigProperties.adminPassword.isNullOrBlank()) {
- val random = UUID.randomUUID().toString()
- logger.warn("Using random Admin Passwort: {}", random)
- passwordEncoder.encode(random)
+ val adminPassword =
+ if (securityConfigProperties.adminPassword.isNullOrBlank()) {
+ val random = UUID.randomUUID().toString()
+ logger.warn("Using random Admin Passwort: {}", random)
+ passwordEncoder.encode(random)
} else {
- securityConfigProperties.adminPassword
+ securityConfigProperties.adminPassword
}
- val user: UserDetails = User.withUsername(adminUser)
- .password(adminPassword)
- .roles("ADMIN")
- .build()
-
- return InMemoryUserDetailsManager(user)
- }
-
- @Bean
- @ConditionalOnProperty(value = ["app.security.enable-oidc"], havingValue = "true")
- fun filterChainOidc(
- http: HttpSecurity,
- passwordEncoder: PasswordEncoder,
- userRoleRepository: UserRoleRepository,
- sessionRegistry: SessionRegistry
- ): SecurityFilterChain {
- http {
- authorizeHttpRequests {
- authorize("/configs/**", hasRole("ADMIN"))
- authorize("/mtbfile/**", hasAnyRole("MTBFILE", "ADMIN", "USER"))
- authorize("/mtb/**", hasAnyRole("MTBFILE", "ADMIN", "USER"))
- authorize("/report/**", hasAnyRole("ADMIN", "USER"))
- authorize("*.css", permitAll)
- authorize("*.ico", permitAll)
- authorize("*.jpeg", permitAll)
- authorize("*.js", permitAll)
- authorize("*.svg", permitAll)
- authorize("*.css", permitAll)
- authorize("/login/**", permitAll)
- authorize(anyRequest, permitAll)
- }
- httpBasic {
- realmName = "ETL-Processor"
- }
- formLogin {
- loginPage = LOGIN_PATH
- }
- oauth2Login {
- loginPage = LOGIN_PATH
- }
- sessionManagement {
- sessionConcurrency {
- maximumSessions = 1
- expiredUrl = "$LOGIN_PATH?expired"
- }
- sessionFixation {
- newSession()
- }
- }
- csrf { disable() }
+ val user: UserDetails =
+ User.withUsername(adminUser).password(adminPassword).roles("ADMIN").build()
+
+ return InMemoryUserDetailsManager(user)
+ }
+
+ @Bean
+ @ConditionalOnProperty(value = ["app.security.enable-oidc"], havingValue = "true")
+ fun filterChainOidc(
+ http: HttpSecurity,
+ passwordEncoder: PasswordEncoder,
+ userRoleRepository: UserRoleRepository,
+ sessionRegistry: SessionRegistry,
+ ): SecurityFilterChain {
+ http {
+ authorizeHttpRequests {
+ authorize("/configs/**", hasRole("ADMIN"))
+ authorize("/mtbfile/**", hasAnyRole("MTBFILE", "ADMIN", "USER"))
+ authorize("/mtb/**", hasAnyRole("MTBFILE", "ADMIN", "USER"))
+ authorize("/report/**", hasAnyRole("ADMIN", "USER"))
+ authorize("*.css", permitAll)
+ authorize("*.ico", permitAll)
+ authorize("*.jpeg", permitAll)
+ authorize("*.js", permitAll)
+ authorize("*.svg", permitAll)
+ authorize("*.css", permitAll)
+ authorize("/login/**", permitAll)
+ authorize(anyRequest, permitAll)
+ }
+ httpBasic { realmName = "ETL-Processor" }
+ formLogin { loginPage = LOGIN_PATH }
+ oauth2Login { loginPage = LOGIN_PATH }
+ sessionManagement {
+ sessionConcurrency {
+ maximumSessions = 1
+ expiredUrl = "$LOGIN_PATH?expired"
}
- return http.build()
+ sessionFixation { newSession() }
+ }
+ csrf { disable() }
}
-
- @Bean
- @ConditionalOnProperty(value = ["app.security.enable-oidc"], havingValue = "true")
- fun grantedAuthoritiesMapper(
- userRoleRepository: UserRoleRepository,
- appSecurityConfigProperties: SecurityConfigProperties
- ): GrantedAuthoritiesMapper {
- return GrantedAuthoritiesMapper { grantedAuthority ->
- grantedAuthority.filterIsInstance<OidcUserAuthority>()
- .onEach {
- val userRole = userRoleRepository.findByUsername(it.userInfo.preferredUsername)
- if (userRole.isEmpty) {
- userRoleRepository.save(
- UserRole(
- null,
- it.userInfo.preferredUsername,
- appSecurityConfigProperties.defaultNewUserRole
- )
- )
- }
- }
- .map {
- val userRole = userRoleRepository.findByUsername(it.userInfo.preferredUsername)
- SimpleGrantedAuthority("ROLE_${userRole.get().role.toString().uppercase()}")
- }
- }
- }
-
- @Bean
- @ConditionalOnProperty(value = ["app.security.enable-oidc"], havingValue = "false", matchIfMissing = true)
- fun filterChain(http: HttpSecurity, passwordEncoder: PasswordEncoder): SecurityFilterChain {
- http {
- authorizeHttpRequests {
- authorize("/configs/**", hasRole("ADMIN"))
- authorize("/mtbfile/**", hasAnyRole("MTBFILE", "ADMIN"))
- authorize("/mtb/**", hasAnyRole("MTBFILE", "ADMIN"))
- authorize("/report/**", hasRole("ADMIN"))
- authorize(anyRequest, permitAll)
+ return http.build()
+ }
+
+ @Bean
+ @ConditionalOnProperty(value = ["app.security.enable-oidc"], havingValue = "true")
+ fun grantedAuthoritiesMapper(
+ userRoleRepository: UserRoleRepository,
+ appSecurityConfigProperties: SecurityConfigProperties,
+ ): GrantedAuthoritiesMapper {
+ return GrantedAuthoritiesMapper { grantedAuthority ->
+ grantedAuthority
+ .filterIsInstance<OidcUserAuthority>()
+ .onEach {
+ val userRole = userRoleRepository.findByUsername(it.userInfo.preferredUsername)
+ if (userRole.isEmpty) {
+ userRoleRepository.save(
+ UserRole(
+ null,
+ it.userInfo.preferredUsername,
+ appSecurityConfigProperties.defaultNewUserRole,
+ )
+ )
}
- httpBasic {
- realmName = "ETL-Processor"
- }
- formLogin {
- loginPage = LOGIN_PATH
- }
- csrf { disable() }
- }
- return http.build()
+ }
+ .map {
+ val userRole = userRoleRepository.findByUsername(it.userInfo.preferredUsername)
+ SimpleGrantedAuthority("ROLE_${userRole.get().role.toString().uppercase()}")
+ }
}
-
- @Bean
- fun sessionRegistry(): SessionRegistry {
- return SessionRegistryImpl()
- }
-
- @Bean
- fun passwordEncoder(): PasswordEncoder {
- return PasswordEncoderFactories.createDelegatingPasswordEncoder()
- }
-
- @Bean
- @ConditionalOnProperty(value = ["app.security.enable-oidc"], havingValue = "true")
- fun userRoleService(userRoleRepository: UserRoleRepository, sessionRegistry: SessionRegistry): UserRoleService {
- return UserRoleService(userRoleRepository, sessionRegistry)
+ }
+
+ @Bean
+ @ConditionalOnProperty(
+ value = ["app.security.enable-oidc"],
+ havingValue = "false",
+ matchIfMissing = true,
+ )
+ fun filterChain(http: HttpSecurity, passwordEncoder: PasswordEncoder): SecurityFilterChain {
+ http {
+ authorizeHttpRequests {
+ authorize("/configs/**", hasRole("ADMIN"))
+ authorize("/mtbfile/**", hasAnyRole("MTBFILE", "ADMIN"))
+ authorize("/mtb/**", hasAnyRole("MTBFILE", "ADMIN"))
+ authorize("/report/**", hasRole("ADMIN"))
+ authorize(anyRequest, permitAll)
+ }
+ httpBasic { realmName = "ETL-Processor" }
+ formLogin { loginPage = LOGIN_PATH }
+ csrf { disable() }
}
+ return http.build()
+ }
+
+ @Bean
+ fun sessionRegistry(): SessionRegistry {
+ return SessionRegistryImpl()
+ }
+
+ @Bean
+ fun passwordEncoder(): PasswordEncoder {
+ return PasswordEncoderFactories.createDelegatingPasswordEncoder()
+ }
+
+ @Bean
+ @ConditionalOnProperty(value = ["app.security.enable-oidc"], havingValue = "true")
+ fun userRoleService(
+ userRoleRepository: UserRoleRepository,
+ sessionRegistry: SessionRegistry,
+ ): UserRoleService {
+ return UserRoleService(userRoleRepository, sessionRegistry)
+ }
}
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/config/ConsentResourceDeserializer.kt b/src/main/kotlin/dev/dnpm/etl/processor/config/ConsentResourceDeserializer.kt
index 5469b1b..48163a1 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/config/ConsentResourceDeserializer.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/config/ConsentResourceDeserializer.kt
@@ -1,18 +1,19 @@
package dev.dnpm.etl.processor.config
import com.fasterxml.jackson.core.JsonParser
-
import com.fasterxml.jackson.databind.DeserializationContext
import com.fasterxml.jackson.databind.JsonDeserializer
import com.fasterxml.jackson.databind.JsonNode
import org.hl7.fhir.r4.model.Consent
class ConsentResourceDeserializer : JsonDeserializer<Consent>() {
- override fun deserialize(p: JsonParser?, ctxt: DeserializationContext?): Consent {
-
+ override fun deserialize(
+ p: JsonParser?,
+ ctxt: DeserializationContext?,
+ ): Consent {
val jsonNode = p?.readValueAsTree<JsonNode>()
val json = jsonNode?.toString()
return JacksonConfig.fhirContext().newJsonParser().parseResource(json) as Consent
}
-} \ No newline at end of file
+}
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/config/ConsentResourceSerializer.kt b/src/main/kotlin/dev/dnpm/etl/processor/config/ConsentResourceSerializer.kt
index 812ce44..b4f29a4 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/config/ConsentResourceSerializer.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/config/ConsentResourceSerializer.kt
@@ -7,9 +7,11 @@ import org.hl7.fhir.r4.model.Consent
class ConsentResourceSerializer : JsonSerializer<Consent>() {
override fun serialize(
- value: Consent, gen: JsonGenerator, serializers: SerializerProvider
+ value: Consent,
+ gen: JsonGenerator,
+ serializers: SerializerProvider,
) {
val json = JacksonConfig.fhirContext().newJsonParser().encodeResourceToString(value)
gen.writeRawValue(json)
}
-} \ No newline at end of file
+}
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/config/FhirResourceModule.kt b/src/main/kotlin/dev/dnpm/etl/processor/config/FhirResourceModule.kt
index 2ae0dd3..45a3d93 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/config/FhirResourceModule.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/config/FhirResourceModule.kt
@@ -1,6 +1,5 @@
package dev.dnpm.etl.processor.config
-
import com.fasterxml.jackson.databind.module.SimpleModule
import org.hl7.fhir.r4.model.Consent
@@ -9,4 +8,4 @@ class FhirResourceModule : SimpleModule() {
addSerializer(Consent::class.java, ConsentResourceSerializer())
addDeserializer(Consent::class.java, ConsentResourceDeserializer())
}
-} \ No newline at end of file
+}
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/config/JacksonConfig.kt b/src/main/kotlin/dev/dnpm/etl/processor/config/JacksonConfig.kt
index 282f69e..2480de8 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/config/JacksonConfig.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/config/JacksonConfig.kt
@@ -2,33 +2,27 @@ package dev.dnpm.etl.processor.config
import ca.uhn.fhir.context.FhirContext
import com.fasterxml.jackson.annotation.JsonInclude
-import org.springframework.context.annotation.Bean
-import org.springframework.context.annotation.Configuration
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.SerializationFeature
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
+import org.springframework.context.annotation.Bean
+import org.springframework.context.annotation.Configuration
@Configuration
class JacksonConfig {
-
companion object {
var fhirContext: FhirContext = FhirContext.forR4()
- @JvmStatic
- fun fhirContext(): FhirContext {
- return fhirContext
- }
+ @JvmStatic fun fhirContext(): FhirContext = fhirContext
}
@Bean
- fun objectMapper(): ObjectMapper = ObjectMapper().registerModule(FhirResourceModule())
- .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
- .registerModule(
- JavaTimeModule()
- )
- .registerModule(
- Jdk8Module()
- )
- .setSerializationInclusion(JsonInclude.Include.NON_NULL)
+ fun objectMapper(): ObjectMapper =
+ ObjectMapper()
+ .registerModule(FhirResourceModule())
+ .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
+ .registerModule(JavaTimeModule())
+ .registerModule(Jdk8Module())
+ .setSerializationInclusion(JsonInclude.Include.NON_NULL)
}
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/consent/ConsentEvaluator.kt b/src/main/kotlin/dev/dnpm/etl/processor/consent/ConsentEvaluator.kt
index 195346d..58f647f 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/consent/ConsentEvaluator.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/consent/ConsentEvaluator.kt
@@ -24,38 +24,35 @@ import dev.pcvolkmer.mv64e.mtb.ModelProjectConsentPurpose
import dev.pcvolkmer.mv64e.mtb.Mtb
import org.springframework.stereotype.Service
-/**
- * Evaluates consent using provided consent service and file based consent information
- */
+/** Evaluates consent using provided consent service and file based consent information */
@Service
class ConsentEvaluator(
- private val consentService: IConsentService
+ private val consentService: IConsentService,
) {
fun check(mtbFile: Mtb): ConsentEvaluation {
val ttpConsentStatus = consentService.getTtpBroadConsentStatus(mtbFile.patient.id)
- val consentGiven = ttpConsentStatus == TtpConsentStatus.BROAD_CONSENT_GIVEN
- || ttpConsentStatus == TtpConsentStatus.GENOM_DE_CONSENT_SEQUENCING_PERMIT
+ val consentGiven =
+ ttpConsentStatus == TtpConsentStatus.BROAD_CONSENT_GIVEN ||
+ ttpConsentStatus == TtpConsentStatus.GENOM_DE_CONSENT_SEQUENCING_PERMIT ||
// Aktuell nur Modellvorhaben Consent im File
- || ttpConsentStatus == TtpConsentStatus.UNKNOWN_CHECK_FILE && mtbFile.metadata?.modelProjectConsent?.provisions?.any {
- it.purpose == ModelProjectConsentPurpose.SEQUENCING
- && it.type == ConsentProvision.PERMIT
+ ttpConsentStatus == TtpConsentStatus.UNKNOWN_CHECK_FILE &&
+ mtbFile.metadata?.modelProjectConsent?.provisions?.any {
+ it.purpose == ModelProjectConsentPurpose.SEQUENCING &&
+ it.type == ConsentProvision.PERMIT
} == true
return ConsentEvaluation(ttpConsentStatus, consentGiven)
}
}
-data class ConsentEvaluation(private val ttpConsentStatus: TtpConsentStatus, private val consentGiven: Boolean) {
- /**
- * Checks if any required consent is present
- */
- fun hasConsent(): Boolean {
- return consentGiven
- }
+data class ConsentEvaluation(
+ private val ttpConsentStatus: TtpConsentStatus,
+ private val consentGiven: Boolean,
+) {
+ /** Checks if any required consent is present */
+ fun hasConsent(): Boolean = consentGiven
- /**
- * Returns the consent status
- */
+ /** Returns the consent status */
fun getStatus(): TtpConsentStatus {
if (ttpConsentStatus == TtpConsentStatus.UNKNOWN_CHECK_FILE) {
// in case ttp check is disabled - we propagate rejected status anyway
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 d53eb7e..2f6b2bb 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/input/KafkaInputListener.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/input/KafkaInputListener.kt
@@ -36,7 +36,7 @@ import java.nio.charset.Charset
class KafkaInputListener(
private val requestProcessor: RequestProcessor,
private val consentEvaluator: ConsentEvaluator,
- private val objectMapper: ObjectMapper
+ private val objectMapper: ObjectMapper,
) : MessageListener<String, String> {
private val logger = LoggerFactory.getLogger(KafkaInputListener::class.java)
@@ -45,29 +45,40 @@ class KafkaInputListener(
MediaType.APPLICATION_JSON_VALUE -> handleDnpmV2Message(record)
CustomMediaType.APPLICATION_VND_DNPM_V2_MTB_JSON_VALUE -> handleDnpmV2Message(record)
else -> {
- /* ignore other messages */
+ // ignore other messages
}
}
}
private fun guessMimeType(record: ConsumerRecord<String, String>): String? {
- if (record.headers().headers("contentType").toList().isEmpty()) {
+ if (record
+ .headers()
+ .headers("contentType")
+ .toList()
+ .isEmpty()
+ ) {
// Fallback if no contentType set (old behavior)
return MediaType.APPLICATION_JSON_VALUE
}
- return record.headers().headers("contentType")?.firstOrNull()?.value()?.toString(Charset.forName("UTF-8"))
+ return record
+ .headers()
+ .headers("contentType")
+ ?.firstOrNull()
+ ?.value()
+ ?.toString(Charset.forName("UTF-8"))
}
private fun handleDnpmV2Message(record: ConsumerRecord<String, String>) {
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) {
- RequestId(String(firstRequestIdHeader.value()))
- } else {
- RequestId("")
- }
+ val requestId =
+ if (null != firstRequestIdHeader) {
+ RequestId(String(firstRequestIdHeader.value()))
+ } else {
+ RequestId("")
+ }
if (consentEvaluator.check(mtbFile).hasConsent()) {
logger.debug("Accepted MTB File for processing")
@@ -81,13 +92,8 @@ class KafkaInputListener(
if (requestId.isBlank()) {
requestProcessor.processDeletion(patientId, TtpConsentStatus.UNKNOWN_CHECK_FILE)
} else {
- requestProcessor.processDeletion(
- patientId,
- requestId,
- TtpConsentStatus.UNKNOWN_CHECK_FILE
- )
+ requestProcessor.processDeletion(patientId, requestId, TtpConsentStatus.UNKNOWN_CHECK_FILE)
}
}
}
-
}
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 e154536..5a43242 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/input/MtbFileRestController.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/input/MtbFileRestController.kt
@@ -34,34 +34,36 @@ import org.springframework.web.bind.annotation.*
@RequestMapping(path = ["mtbfile", "mtb"])
class MtbFileRestController(
private val requestProcessor: RequestProcessor,
- private val consentEvaluator: ConsentEvaluator
+ private val consentEvaluator: ConsentEvaluator,
) {
- 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")
- }
-
- @PostMapping(consumes = [MediaType.APPLICATION_JSON_VALUE, CustomMediaType.APPLICATION_VND_DNPM_V2_MTB_JSON_VALUE])
- fun mtbFile(@RequestBody mtbFile: Mtb): ResponseEntity<Unit> {
- val consentEvaluation = consentEvaluator.check(mtbFile)
- if (consentEvaluation.hasConsent()) {
- logger.debug("Accepted MTB File (DNPM V2) for processing")
- requestProcessor.processMtbFile(mtbFile)
- } else {
- logger.debug("Accepted MTB File (DNPM V2) and process deletion")
- val patientId = PatientId(mtbFile.patient.id)
- requestProcessor.processDeletion(patientId, consentEvaluation.getStatus())
- }
- return ResponseEntity.accepted().build()
- }
+ @GetMapping
+ fun info(): ResponseEntity<String> {
+ return ResponseEntity.ok("Test")
+ }
- @DeleteMapping(path = ["{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()
+ @PostMapping(
+ consumes =
+ [MediaType.APPLICATION_JSON_VALUE, CustomMediaType.APPLICATION_VND_DNPM_V2_MTB_JSON_VALUE]
+ )
+ fun mtbFile(@RequestBody mtbFile: Mtb): ResponseEntity<Unit> {
+ val consentEvaluation = consentEvaluator.check(mtbFile)
+ if (consentEvaluation.hasConsent()) {
+ logger.debug("Accepted MTB File (DNPM V2) for processing")
+ requestProcessor.processMtbFile(mtbFile)
+ } else {
+ logger.debug("Accepted MTB File (DNPM V2) and process deletion")
+ val patientId = PatientId(mtbFile.patient.id)
+ requestProcessor.processDeletion(patientId, consentEvaluation.getStatus())
}
+ return ResponseEntity.accepted().build()
+ }
+ @DeleteMapping(path = ["{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/monitoring/ConnectionCheckService.kt b/src/main/kotlin/dev/dnpm/etl/processor/monitoring/ConnectionCheckService.kt
index 6e97865..085d0a3 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/monitoring/ConnectionCheckService.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/monitoring/ConnectionCheckService.kt
@@ -17,13 +17,15 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
-
package dev.dnpm.etl.processor.monitoring
import dev.dnpm.etl.processor.config.GIcsConfigProperties
import dev.dnpm.etl.processor.config.GPasConfigProperties
import dev.dnpm.etl.processor.config.RestTargetProperties
import jakarta.annotation.PostConstruct
+import java.time.Instant
+import kotlin.time.Duration.Companion.seconds
+import kotlin.time.toJavaDuration
import org.apache.kafka.clients.consumer.Consumer
import org.apache.kafka.common.errors.TimeoutException
import org.slf4j.LoggerFactory
@@ -33,254 +35,288 @@ import org.springframework.scheduling.annotation.Scheduled
import org.springframework.web.client.RestTemplate
import org.springframework.web.util.UriComponentsBuilder
import reactor.core.publisher.Sinks
-import java.time.Instant
-import kotlin.time.Duration.Companion.seconds
-import kotlin.time.toJavaDuration
fun interface ConnectionCheckService {
- fun connectionAvailable(): ConnectionCheckResult
-
+ fun connectionAvailable(): ConnectionCheckResult
}
interface OutputConnectionCheckService : ConnectionCheckService
sealed class ConnectionCheckResult {
- abstract val available: Boolean
+ abstract val available: Boolean
- abstract val timestamp: Instant
+ abstract val timestamp: Instant
- abstract val lastChange: Instant
+ abstract val lastChange: Instant
- data class KafkaConnectionCheckResult(
- override val available: Boolean,
- override val timestamp: Instant,
- override val lastChange: Instant
- ) : ConnectionCheckResult()
+ data class KafkaConnectionCheckResult(
+ override val available: Boolean,
+ override val timestamp: Instant,
+ override val lastChange: Instant,
+ ) : ConnectionCheckResult()
- data class RestConnectionCheckResult(
- override val available: Boolean,
- override val timestamp: Instant,
- override val lastChange: Instant
- ) : ConnectionCheckResult()
+ data class RestConnectionCheckResult(
+ override val available: Boolean,
+ override val timestamp: Instant,
+ override val lastChange: Instant,
+ ) : ConnectionCheckResult()
- data class GPasConnectionCheckResult(
- override val available: Boolean,
- override val timestamp: Instant,
- override val lastChange: Instant
- ) : ConnectionCheckResult()
+ data class GPasConnectionCheckResult(
+ override val available: Boolean,
+ override val timestamp: Instant,
+ override val lastChange: Instant,
+ ) : ConnectionCheckResult()
- data class GIcsConnectionCheckResult(
- override val available: Boolean,
- override val timestamp: Instant,
- override val lastChange: Instant
- ) : ConnectionCheckResult()
+ data class GIcsConnectionCheckResult(
+ override val available: Boolean,
+ override val timestamp: Instant,
+ override val lastChange: Instant,
+ ) : ConnectionCheckResult()
}
class KafkaConnectionCheckService(
private val consumer: Consumer<String, String>,
@param:Qualifier("connectionCheckUpdateProducer")
- private val connectionCheckUpdateProducer: Sinks.Many<ConnectionCheckResult>
+ private val connectionCheckUpdateProducer: Sinks.Many<ConnectionCheckResult>,
) : OutputConnectionCheckService {
- private val logger = LoggerFactory.getLogger(javaClass)
- private var result = ConnectionCheckResult.KafkaConnectionCheckResult(false, Instant.now(), Instant.now())
-
- @PostConstruct
- @Scheduled(cron = "0 * * * * *")
- fun check() {
- result = try {
- val available = null != consumer.listTopics(5.seconds.toJavaDuration())
- ConnectionCheckResult.KafkaConnectionCheckResult(
- available,
- Instant.now(),
- if (result.available == available) { result.lastChange } else { Instant.now() }
- )
+ private val logger = LoggerFactory.getLogger(javaClass)
+ private var result =
+ ConnectionCheckResult.KafkaConnectionCheckResult(false, Instant.now(), Instant.now())
+
+ @PostConstruct
+ @Scheduled(cron = "0 * * * * *")
+ fun check() {
+ result =
+ try {
+ val available = null != consumer.listTopics(5.seconds.toJavaDuration())
+ ConnectionCheckResult.KafkaConnectionCheckResult(
+ available,
+ Instant.now(),
+ if (result.available == available) {
+ result.lastChange
+ } else {
+ Instant.now()
+ },
+ )
} catch (ex: TimeoutException) {
- logger.error("Connection-Timeout error: {}", ex.message)
- ConnectionCheckResult.KafkaConnectionCheckResult(
- false,
- Instant.now(),
- if (!result.available) { result.lastChange } else { Instant.now() }
- )
+ logger.error("Connection-Timeout error: {}", ex.message)
+ ConnectionCheckResult.KafkaConnectionCheckResult(
+ false,
+ Instant.now(),
+ if (!result.available) {
+ result.lastChange
+ } else {
+ Instant.now()
+ },
+ )
}
- connectionCheckUpdateProducer.emitNext(
- result,
- Sinks.EmitFailureHandler.FAIL_FAST
- )
- }
-
- override fun connectionAvailable(): ConnectionCheckResult.KafkaConnectionCheckResult {
- return this.result
- }
+ connectionCheckUpdateProducer.emitNext(result, Sinks.EmitFailureHandler.FAIL_FAST)
+ }
+ override fun connectionAvailable(): ConnectionCheckResult.KafkaConnectionCheckResult {
+ return this.result
+ }
}
class RestConnectionCheckService(
private val restTemplate: RestTemplate,
private val restTargetProperties: RestTargetProperties,
@param:Qualifier("connectionCheckUpdateProducer")
- private val connectionCheckUpdateProducer: Sinks.Many<ConnectionCheckResult>
+ private val connectionCheckUpdateProducer: Sinks.Many<ConnectionCheckResult>,
) : OutputConnectionCheckService {
- private val logger = LoggerFactory.getLogger(javaClass)
- private var result = ConnectionCheckResult.RestConnectionCheckResult(false, Instant.now(), Instant.now())
-
- @PostConstruct
- @Scheduled(cron = "0 * * * * *")
- fun check() {
- result = try {
- val statusCode = restTemplate.getForEntity(
- UriComponentsBuilder.fromUriString(restTargetProperties.uri.toString())
- .pathSegment("mtb")
- .pathSegment("kaplan-meier")
- .pathSegment("config")
- .toUriString(),
- String::class.java
- ).statusCode
- val available = statusCode == HttpStatus.OK
-
- if (available.not()) {
- logger.error("Invalid response code {}, expected HTTP status 200", statusCode)
- }
- ConnectionCheckResult.RestConnectionCheckResult(
- available,
- Instant.now(),
- if (result.available == available) { result.lastChange } else { Instant.now() }
- )
+ private val logger = LoggerFactory.getLogger(javaClass)
+ private var result =
+ ConnectionCheckResult.RestConnectionCheckResult(false, Instant.now(), Instant.now())
+
+ @PostConstruct
+ @Scheduled(cron = "0 * * * * *")
+ fun check() {
+ result =
+ try {
+ val statusCode =
+ restTemplate
+ .getForEntity(
+ UriComponentsBuilder.fromUriString(restTargetProperties.uri.toString())
+ .pathSegment("mtb")
+ .pathSegment("kaplan-meier")
+ .pathSegment("config")
+ .toUriString(),
+ String::class.java,
+ )
+ .statusCode
+ val available = statusCode == HttpStatus.OK
+
+ if (available.not()) {
+ logger.error("Invalid response code {}, expected HTTP status 200", statusCode)
+ }
+ ConnectionCheckResult.RestConnectionCheckResult(
+ available,
+ Instant.now(),
+ if (result.available == available) {
+ result.lastChange
+ } else {
+ Instant.now()
+ },
+ )
} catch (ex: Exception) {
- logger.error("Connection-Check error: {}", ex.message)
- ConnectionCheckResult.RestConnectionCheckResult(
- false,
- Instant.now(),
- if (!result.available) { result.lastChange } else { Instant.now() }
- )
+ logger.error("Connection-Check error: {}", ex.message)
+ ConnectionCheckResult.RestConnectionCheckResult(
+ false,
+ Instant.now(),
+ if (!result.available) {
+ result.lastChange
+ } else {
+ Instant.now()
+ },
+ )
}
- connectionCheckUpdateProducer.emitNext(
- result,
- Sinks.EmitFailureHandler.FAIL_FAST
- )
- }
-
- override fun connectionAvailable(): ConnectionCheckResult.RestConnectionCheckResult {
- return this.result
- }
+ connectionCheckUpdateProducer.emitNext(result, Sinks.EmitFailureHandler.FAIL_FAST)
+ }
+
+ override fun connectionAvailable(): ConnectionCheckResult.RestConnectionCheckResult {
+ return this.result
+ }
}
class GPasConnectionCheckService(
private val restTemplate: RestTemplate,
private val gPasConfigProperties: GPasConfigProperties,
@param:Qualifier("connectionCheckUpdateProducer")
- private val connectionCheckUpdateProducer: Sinks.Many<ConnectionCheckResult>
+ private val connectionCheckUpdateProducer: Sinks.Many<ConnectionCheckResult>,
) : ConnectionCheckService {
- private val logger = LoggerFactory.getLogger(javaClass)
- private var result = ConnectionCheckResult.GPasConnectionCheckResult(false, Instant.now(), Instant.now())
-
- @PostConstruct
- @Scheduled(cron = "0 * * * * *")
- fun check() {
- result = try {
- val uri = UriComponentsBuilder.fromUriString(
- gPasConfigProperties.uri.toString()).path("/metadata").build().toUri()
-
- val headers = HttpHeaders()
- headers.contentType = MediaType.APPLICATION_JSON
- if (!gPasConfigProperties.username.isNullOrBlank() && !gPasConfigProperties.password.isNullOrBlank()) {
- headers.setBasicAuth(gPasConfigProperties.username, gPasConfigProperties.password)
- }
-
- val statusCode = restTemplate.exchange(
- uri,
- HttpMethod.GET,
- HttpEntity<Void>(headers),
- Void::class.java
- ).statusCode
- val available = statusCode == HttpStatus.OK
-
- if (available.not()) {
- logger.error("Invalid response code {}, expected HTTP status 200", statusCode)
- }
- ConnectionCheckResult.GPasConnectionCheckResult(
- available,
- Instant.now(),
- if (result.available == available) { result.lastChange } else { Instant.now() }
- )
+ private val logger = LoggerFactory.getLogger(javaClass)
+ private var result =
+ ConnectionCheckResult.GPasConnectionCheckResult(false, Instant.now(), Instant.now())
+
+ @PostConstruct
+ @Scheduled(cron = "0 * * * * *")
+ fun check() {
+ result =
+ try {
+ val uri =
+ UriComponentsBuilder.fromUriString(gPasConfigProperties.uri.toString())
+ .path("/metadata")
+ .build()
+ .toUri()
+
+ val headers = HttpHeaders()
+ headers.contentType = MediaType.APPLICATION_JSON
+ if (
+ !gPasConfigProperties.username.isNullOrBlank() &&
+ !gPasConfigProperties.password.isNullOrBlank()
+ ) {
+ headers.setBasicAuth(gPasConfigProperties.username, gPasConfigProperties.password)
+ }
+
+ val statusCode =
+ restTemplate
+ .exchange(uri, HttpMethod.GET, HttpEntity<Void>(headers), Void::class.java)
+ .statusCode
+ val available = statusCode == HttpStatus.OK
+
+ if (available.not()) {
+ logger.error("Invalid response code {}, expected HTTP status 200", statusCode)
+ }
+ ConnectionCheckResult.GPasConnectionCheckResult(
+ available,
+ Instant.now(),
+ if (result.available == available) {
+ result.lastChange
+ } else {
+ Instant.now()
+ },
+ )
} catch (ex: Exception) {
- logger.error("Connection-Check error: {}", ex.message)
- ConnectionCheckResult.GPasConnectionCheckResult(
- false,
- Instant.now(),
- if (!result.available) { result.lastChange } else { Instant.now() }
- )
+ logger.error("Connection-Check error: {}", ex.message)
+ ConnectionCheckResult.GPasConnectionCheckResult(
+ false,
+ Instant.now(),
+ if (!result.available) {
+ result.lastChange
+ } else {
+ Instant.now()
+ },
+ )
}
- connectionCheckUpdateProducer.emitNext(
- result,
- Sinks.EmitFailureHandler.FAIL_FAST
- )
- }
-
- override fun connectionAvailable(): ConnectionCheckResult.GPasConnectionCheckResult {
- return this.result
- }
+ connectionCheckUpdateProducer.emitNext(result, Sinks.EmitFailureHandler.FAIL_FAST)
+ }
+
+ override fun connectionAvailable(): ConnectionCheckResult.GPasConnectionCheckResult {
+ return this.result
+ }
}
class GIcsConnectionCheckService(
private val restTemplate: RestTemplate,
private val gIcsConfigProperties: GIcsConfigProperties,
@param:Qualifier("connectionCheckUpdateProducer")
- private val connectionCheckUpdateProducer: Sinks.Many<ConnectionCheckResult>
+ private val connectionCheckUpdateProducer: Sinks.Many<ConnectionCheckResult>,
) : ConnectionCheckService {
- private val logger = LoggerFactory.getLogger(javaClass)
- private var result = ConnectionCheckResult.GIcsConnectionCheckResult(false, Instant.now(), Instant.now())
-
- @PostConstruct
- @Scheduled(cron = "0 * * * * *")
- fun check() {
- result = try {
-
- val uri = UriComponentsBuilder.fromUriString(
- gIcsConfigProperties.uri.toString()).path("/metadata").build().toUri()
-
- val headers = HttpHeaders()
- headers.contentType = MediaType.APPLICATION_JSON
- if (!gIcsConfigProperties.username.isNullOrBlank() && !gIcsConfigProperties.password.isNullOrBlank()) {
- headers.setBasicAuth(gIcsConfigProperties.username, gIcsConfigProperties.password)
- }
-
- val statusCode = restTemplate.exchange(
- uri,
- HttpMethod.GET,
- HttpEntity<Void>(headers),
- Void::class.java
- ).statusCode
- val available = statusCode == HttpStatus.OK
-
- if (available.not()) {
- logger.error("Invalid response code {}, expected HTTP status 200", statusCode)
- }
- ConnectionCheckResult.GIcsConnectionCheckResult(
- available,
- Instant.now(),
- if (result.available == available) { result.lastChange } else { Instant.now() }
- )
+ private val logger = LoggerFactory.getLogger(javaClass)
+ private var result =
+ ConnectionCheckResult.GIcsConnectionCheckResult(false, Instant.now(), Instant.now())
+
+ @PostConstruct
+ @Scheduled(cron = "0 * * * * *")
+ fun check() {
+ result =
+ try {
+
+ val uri =
+ UriComponentsBuilder.fromUriString(gIcsConfigProperties.uri.toString())
+ .path("/metadata")
+ .build()
+ .toUri()
+
+ val headers = HttpHeaders()
+ headers.contentType = MediaType.APPLICATION_JSON
+ if (
+ !gIcsConfigProperties.username.isNullOrBlank() &&
+ !gIcsConfigProperties.password.isNullOrBlank()
+ ) {
+ headers.setBasicAuth(gIcsConfigProperties.username, gIcsConfigProperties.password)
+ }
+
+ val statusCode =
+ restTemplate
+ .exchange(uri, HttpMethod.GET, HttpEntity<Void>(headers), Void::class.java)
+ .statusCode
+ val available = statusCode == HttpStatus.OK
+
+ if (available.not()) {
+ logger.error("Invalid response code {}, expected HTTP status 200", statusCode)
+ }
+ ConnectionCheckResult.GIcsConnectionCheckResult(
+ available,
+ Instant.now(),
+ if (result.available == available) {
+ result.lastChange
+ } else {
+ Instant.now()
+ },
+ )
} catch (ex: Exception) {
- logger.error("Connection-Check error: {}", ex.message)
- ConnectionCheckResult.GIcsConnectionCheckResult(
- false,
- Instant.now(),
- if (!result.available) { result.lastChange } else { Instant.now() }
- )
+ logger.error("Connection-Check error: {}", ex.message)
+ ConnectionCheckResult.GIcsConnectionCheckResult(
+ false,
+ Instant.now(),
+ if (!result.available) {
+ result.lastChange
+ } else {
+ Instant.now()
+ },
+ )
}
- connectionCheckUpdateProducer.emitNext(
- result,
- Sinks.EmitFailureHandler.FAIL_FAST
- )
- }
-
- override fun connectionAvailable(): ConnectionCheckResult.GIcsConnectionCheckResult {
- return this.result
- }
+ connectionCheckUpdateProducer.emitNext(result, Sinks.EmitFailureHandler.FAIL_FAST)
+ }
+
+ override fun connectionAvailable(): ConnectionCheckResult.GIcsConnectionCheckResult {
+ return this.result
+ }
}
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/monitoring/ReportService.kt b/src/main/kotlin/dev/dnpm/etl/processor/monitoring/ReportService.kt
index dd5c44a..c54aa7a 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/monitoring/ReportService.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/monitoring/ReportService.kt
@@ -30,61 +30,53 @@ import dev.dnpm.etl.processor.monitoring.ReportService.Issue
import dev.dnpm.etl.processor.monitoring.ReportService.Severity
import java.util.*
-class ReportService(
- private val objectMapper: ObjectMapper
-) {
+class ReportService(private val objectMapper: ObjectMapper) {
- fun deserialize(dataQualityReport: String?): List<Issue> {
- if (dataQualityReport.isNullOrBlank()) {
- return listOf()
- }
- return try {
- objectMapper
- .readValue(dataQualityReport, DataQualityReport::class.java)
- .issues
- .sortedBy { it.severity }
- } catch (e: Exception) {
- val otherIssue =
- Issue(Severity.ERROR, "Not parsable data quality report '$dataQualityReport'")
- return when (e) {
- is JsonMappingException -> listOf(otherIssue)
- is JsonParseException -> listOf(otherIssue)
- else -> throw e
- }
- }
+ fun deserialize(dataQualityReport: String?): List<Issue> {
+ if (dataQualityReport.isNullOrBlank()) {
+ return listOf()
}
+ return try {
+ objectMapper.readValue(dataQualityReport, DataQualityReport::class.java).issues.sortedBy {
+ it.severity
+ }
+ } catch (e: Exception) {
+ val otherIssue =
+ Issue(Severity.ERROR, "Not parsable data quality report '$dataQualityReport'")
+ return when (e) {
+ is JsonMappingException -> listOf(otherIssue)
+ is JsonParseException -> listOf(otherIssue)
+ else -> throw e
+ }
+ }
+ }
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ private data class DataQualityReport(
+ @param:JsonProperty(value = "issues") val issues: List<Issue>
+ )
- @JsonIgnoreProperties(ignoreUnknown = true)
- private data class DataQualityReport(
- @param:JsonProperty(value = "issues")
- val issues: List<Issue>
- )
-
- @JsonIgnoreProperties(ignoreUnknown = true)
- data class Issue(
- @param:JsonProperty(value = "severity")
- val severity: Severity,
- @param:JsonProperty(value = "message")
- @param:JsonAlias("details")
- val message: String,
- @param:JsonProperty(value = "path")
- val path: Optional<String> = Optional.empty()
- )
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ data class Issue(
+ @param:JsonProperty(value = "severity") val severity: Severity,
+ @param:JsonProperty(value = "message") @param:JsonAlias("details") val message: String,
+ @param:JsonProperty(value = "path") val path: Optional<String> = Optional.empty(),
+ )
- enum class Severity(@JsonValue val value: String) {
- FATAL("fatal"),
- ERROR("error"),
- WARNING("warning"),
- INFO("info")
- }
+ enum class Severity(@JsonValue val value: String) {
+ FATAL("fatal"),
+ ERROR("error"),
+ WARNING("warning"),
+ INFO("info"),
+ }
}
fun List<Issue>.asRequestStatus(): RequestStatus {
- val severity = this.minOfOrNull { it.severity }
- return when (severity) {
- Severity.FATAL, Severity.ERROR -> RequestStatus.ERROR
- Severity.WARNING -> RequestStatus.WARNING
- else -> RequestStatus.SUCCESS
- }
-} \ No newline at end of file
+ val severity = this.minOfOrNull { it.severity }
+ return when (severity) {
+ Severity.FATAL,
+ Severity.ERROR -> RequestStatus.ERROR
+ Severity.WARNING -> RequestStatus.WARNING
+ else -> RequestStatus.SUCCESS
+ }
+}
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 f2509dd..71731f1 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,9 @@
package dev.dnpm.etl.processor.monitoring
import dev.dnpm.etl.processor.*
+import java.time.Instant
+import java.time.temporal.ChronoUnit
+import java.util.*
import org.springframework.data.annotation.Id
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
@@ -29,9 +32,6 @@ import org.springframework.data.relational.core.mapping.Embedded
import org.springframework.data.relational.core.mapping.Table
import org.springframework.data.repository.CrudRepository
import org.springframework.data.repository.PagingAndSortingRepository
-import java.time.Instant
-import java.time.temporal.ChronoUnit
-import java.util.*
@Table("request")
data class Request(
@@ -39,46 +39,38 @@ data class Request(
val uuid: RequestId = randomRequestId(),
val patientPseudonym: PatientPseudonym,
val pid: PatientId,
- @Column("fingerprint")
- val fingerprint: Fingerprint,
+ @Column("fingerprint") val fingerprint: Fingerprint,
val type: RequestType,
var status: RequestStatus,
var processedAt: Instant = Instant.now(),
- @Embedded.Nullable var report: Report? = null
+ @Embedded.Nullable var report: Report? = null,
) {
- constructor(
- uuid: RequestId,
- patientPseudonym: PatientPseudonym,
- pid: PatientId,
- fingerprint: Fingerprint,
- type: RequestType,
- status: RequestStatus
- ) :
- this(null, uuid, patientPseudonym, pid, fingerprint, type, status, Instant.now())
-
- constructor(
- uuid: RequestId,
- patientPseudonym: PatientPseudonym,
- pid: PatientId,
- fingerprint: Fingerprint,
- type: RequestType,
- status: RequestStatus,
- processedAt: Instant
- ) :
- this(null, uuid, patientPseudonym, pid, fingerprint, type, status, processedAt)
-
- fun isPendingUnknown(): Boolean {
- return this.status == RequestStatus.UNKNOWN && this.processedAt.isBefore(
- Instant.now().minus(10, ChronoUnit.MINUTES)
- )
- }
+ constructor(
+ uuid: RequestId,
+ patientPseudonym: PatientPseudonym,
+ pid: PatientId,
+ fingerprint: Fingerprint,
+ type: RequestType,
+ status: RequestStatus,
+ ) : this(null, uuid, patientPseudonym, pid, fingerprint, type, status, Instant.now())
+
+ constructor(
+ uuid: RequestId,
+ patientPseudonym: PatientPseudonym,
+ pid: PatientId,
+ fingerprint: Fingerprint,
+ type: RequestType,
+ status: RequestStatus,
+ processedAt: Instant,
+ ) : this(null, uuid, patientPseudonym, pid, fingerprint, type, status, processedAt)
+
+ fun isPendingUnknown(): Boolean {
+ return this.status == RequestStatus.UNKNOWN &&
+ this.processedAt.isBefore(Instant.now().minus(10, ChronoUnit.MINUTES))
+ }
}
-@JvmRecord
-data class Report(
- val description: String,
- val dataQualityReport: String = ""
-)
+@JvmRecord data class Report(val description: String, val dataQualityReport: String = "")
@JvmRecord
data class CountedState(
@@ -86,34 +78,41 @@ data class CountedState(
val status: RequestStatus,
)
-interface RequestRepository : CrudRepository<Request, Long>, PagingAndSortingRepository<Request, Long> {
-
- fun findAllByPatientPseudonymOrderByProcessedAtDesc(patientId: PatientPseudonym): List<Request>
-
- fun findByUuidEquals(uuid: RequestId): Optional<Request>
-
- fun findRequestByPatientPseudonym(patientPseudonym: PatientPseudonym, pageable: Pageable): Page<Request>
-
- @Query("SELECT count(*) AS count, status FROM request WHERE type = 'MTB_FILE' GROUP BY status ORDER BY status, count DESC;")
- fun countStates(): List<CountedState>
-
- @Query(
- "SELECT count(*) AS count, status FROM (" +
- "SELECT status, rank() OVER (PARTITION BY patient_pseudonym ORDER BY processed_at DESC) AS rank FROM request " +
- "WHERE type = 'MTB_FILE' AND status NOT IN ('DUPLICATION') " +
- ") rank WHERE rank = 1 GROUP BY status ORDER BY status, count DESC;"
- )
- fun findPatientUniqueStates(): List<CountedState>
-
- @Query("SELECT count(*) AS count, status FROM request WHERE type = 'DELETE' GROUP BY status ORDER BY status, count DESC;")
- fun countDeleteStates(): List<CountedState>
-
- @Query(
- "SELECT count(*) AS count, status FROM (" +
- "SELECT status, rank() OVER (PARTITION BY patient_pseudonym ORDER BY processed_at DESC) AS rank FROM request " +
- "WHERE type = 'DELETE'" +
- ") rank WHERE rank = 1 GROUP BY status ORDER BY status, count DESC;"
- )
- fun findPatientUniqueDeleteStates(): List<CountedState>
-
+interface RequestRepository :
+ CrudRepository<Request, Long>, PagingAndSortingRepository<Request, Long> {
+
+ fun findAllByPatientPseudonymOrderByProcessedAtDesc(patientId: PatientPseudonym): List<Request>
+
+ fun findByUuidEquals(uuid: RequestId): Optional<Request>
+
+ fun findRequestByPatientPseudonym(
+ patientPseudonym: PatientPseudonym,
+ pageable: Pageable,
+ ): Page<Request>
+
+ @Query(
+ "SELECT count(*) AS count, status FROM request WHERE type = 'MTB_FILE' GROUP BY status ORDER BY status, count DESC;"
+ )
+ fun countStates(): List<CountedState>
+
+ @Query(
+ "SELECT count(*) AS count, status FROM (" +
+ "SELECT status, rank() OVER (PARTITION BY patient_pseudonym ORDER BY processed_at DESC) AS rank FROM request " +
+ "WHERE type = 'MTB_FILE' AND status NOT IN ('DUPLICATION') " +
+ ") rank WHERE rank = 1 GROUP BY status ORDER BY status, count DESC;"
+ )
+ fun findPatientUniqueStates(): List<CountedState>
+
+ @Query(
+ "SELECT count(*) AS count, status FROM request WHERE type = 'DELETE' GROUP BY status ORDER BY status, count DESC;"
+ )
+ fun countDeleteStates(): List<CountedState>
+
+ @Query(
+ "SELECT count(*) AS count, status FROM (" +
+ "SELECT status, rank() OVER (PARTITION BY patient_pseudonym ORDER BY processed_at DESC) AS rank FROM request " +
+ "WHERE type = 'DELETE'" +
+ ") rank WHERE rank = 1 GROUP BY status ORDER BY status, count DESC;"
+ )
+ fun findPatientUniqueDeleteStates(): List<CountedState>
}
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 0c8adb1..5487a05 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/monitoring/RequestStatus.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/monitoring/RequestStatus.kt
@@ -19,11 +19,13 @@
package dev.dnpm.etl.processor.monitoring
-enum class RequestStatus(val value: String) {
+enum class RequestStatus(
+ val value: String,
+) {
SUCCESS("success"),
WARNING("warning"),
ERROR("error"),
UNKNOWN("unknown"),
DUPLICATION("duplication"),
- NO_CONSENT("no-consent")
-} \ No newline at end of file
+ NO_CONSENT("no-consent"),
+}
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/monitoring/RequestType.kt b/src/main/kotlin/dev/dnpm/etl/processor/monitoring/RequestType.kt
index cb43d7f..ef7f1e3 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/monitoring/RequestType.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/monitoring/RequestType.kt
@@ -19,7 +19,9 @@
package dev.dnpm.etl.processor.monitoring
-enum class RequestType(val value: String) {
+enum class RequestType(
+ val value: String,
+) {
MTB_FILE("mtb_file"),
DELETE("delete"),
-} \ 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 ef46c0a..71e4a78 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/output/KafkaMtbFileSender.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/output/KafkaMtbFileSender.kt
@@ -34,9 +34,8 @@ class KafkaMtbFileSender(
private val kafkaTemplate: KafkaTemplate<String, String>,
private val kafkaProperties: KafkaProperties,
private val retryTemplate: RetryTemplate,
- private val objectMapper: ObjectMapper
+ private val objectMapper: ObjectMapper,
) : MtbFileSender {
-
private val logger = LoggerFactory.getLogger(KafkaMtbFileSender::class.java)
override fun <T> send(request: MtbFileRequest<T>): MtbFileSender.Response {
@@ -50,11 +49,13 @@ class KafkaMtbFileSender(
)
record.headers().add("requestId", request.requestId.value.toByteArray())
when (request) {
- is DnpmV2MtbFileRequest -> record.headers()
- .add(
- "contentType",
- CustomMediaType.APPLICATION_VND_DNPM_V2_MTB_JSON_VALUE.toByteArray()
- )
+ is DnpmV2MtbFileRequest ->
+ record
+ .headers()
+ .add(
+ "contentType",
+ CustomMediaType.APPLICATION_VND_DNPM_V2_MTB_JSON_VALUE.toByteArray(),
+ )
}
val result = kafkaTemplate.send(record)
@@ -72,9 +73,7 @@ class KafkaMtbFileSender(
}
override fun send(request: DeleteRequest): MtbFileSender.Response {
- val dummyMtbFile = Mtb.builder()
- .metadata(MvhMetadata())
- .build()
+ val dummyMtbFile = Mtb.builder().metadata(MvhMetadata()).build()
return try {
return retryTemplate.execute<MtbFileSender.Response, Exception> {
@@ -83,11 +82,8 @@ class KafkaMtbFileSender(
kafkaProperties.outputTopic,
key(request),
objectMapper.writeValueAsString(
- DnpmV2MtbFileRequest(
- request.requestId,
- dummyMtbFile
- )
- )
+ DnpmV2MtbFileRequest(request.requestId, dummyMtbFile),
+ ),
)
record.headers().add("requestId", request.requestId.value.toByteArray())
val result = kafkaTemplate.send(record)
@@ -104,15 +100,14 @@ class KafkaMtbFileSender(
}
}
- override fun endpoint(): String {
- return "${this.kafkaProperties.servers} (${this.kafkaProperties.outputTopic}/${this.kafkaProperties.outputResponseTopic})"
- }
+ override fun endpoint(): String =
+ "${this.kafkaProperties.servers} (${this.kafkaProperties.outputTopic}/${this.kafkaProperties.outputResponseTopic})"
- private fun key(request: MtbRequest): String {
- return when (request) {
+ private fun key(request: MtbRequest): String =
+ when (request) {
is DnpmV2MtbFileRequest -> "{\"pid\": \"${request.content.patient.id}\"}"
is DeleteRequest -> "{\"pid\": \"${request.patientId.value}\"}"
- else -> throw IllegalArgumentException("Unsupported request type: ${request::class.simpleName}")
+ else ->
+ throw IllegalArgumentException("Unsupported request type: ${request::class.simpleName}")
}
- }
}
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 285ce07..c81b572 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/output/MtbFileSender.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/output/MtbFileSender.kt
@@ -29,18 +29,18 @@ interface MtbFileSender {
fun endpoint(): String
- data class Response(val status: RequestStatus, val body: String = "")
+ data class Response(
+ val status: RequestStatus,
+ val body: String = "",
+ )
}
-fun Int.asRequestStatus(): RequestStatus {
- return when (this) {
+fun Int.asRequestStatus(): RequestStatus =
+ when (this) {
200 -> RequestStatus.SUCCESS
201 -> RequestStatus.WARNING
- in 400 .. 999 -> RequestStatus.ERROR
- else -> RequestStatus.UNKNOWN
+ in 400..999 -> RequestStatus.ERROR
+ else -> RequestStatus.UNKNOWN
}
-}
-fun HttpStatusCode.asRequestStatus(): RequestStatus {
- return this.value().asRequestStatus()
-}
+fun HttpStatusCode.asRequestStatus(): RequestStatus = this.value().asRequestStatus()
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 7512200..b228c4c 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/output/MtbRequest.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/output/MtbRequest.kt
@@ -36,14 +36,12 @@ sealed interface MtbFileRequest<out T> : MtbRequest {
data class DnpmV2MtbFileRequest(
override val requestId: RequestId,
- override val content: Mtb
+ override val content: Mtb,
) : MtbFileRequest<Mtb> {
- override fun patientPseudonym(): PatientPseudonym {
- return PatientPseudonym(content.patient.id)
- }
+ override fun patientPseudonym(): PatientPseudonym = PatientPseudonym(content.patient.id)
}
data class DeleteRequest(
override val requestId: RequestId,
- val patientId: PatientPseudonym
+ val patientId: PatientPseudonym,
) : MtbRequest
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/output/RestDipMtbFileSender.kt b/src/main/kotlin/dev/dnpm/etl/processor/output/RestDipMtbFileSender.kt
index 1e6a5a7..5aad133 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/output/RestDipMtbFileSender.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/output/RestDipMtbFileSender.kt
@@ -30,26 +30,22 @@ class RestDipMtbFileSender(
restTemplate: RestTemplate,
private val restTargetProperties: RestTargetProperties,
retryTemplate: RetryTemplate,
- reportService: ReportService
+ reportService: ReportService,
) : RestMtbFileSender(restTemplate, restTargetProperties, retryTemplate, reportService) {
-
- override fun sendUrl(): String {
- return UriComponentsBuilder
+ override fun sendUrl(): String =
+ UriComponentsBuilder
.fromUriString(restTargetProperties.uri.toString())
.pathSegment("mtb")
.pathSegment("etl")
.pathSegment("patient-record")
.toUriString()
- }
- override fun deleteUrl(patientId: PatientPseudonym): String {
- return UriComponentsBuilder
+ override fun deleteUrl(patientId: PatientPseudonym): String =
+ UriComponentsBuilder
.fromUriString(restTargetProperties.uri.toString())
.pathSegment("mtb")
.pathSegment("etl")
.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 ec6ff85..4120d4a 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/output/RestMtbFileSender.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/output/RestMtbFileSender.kt
@@ -38,9 +38,8 @@ abstract class RestMtbFileSender(
private val restTemplate: RestTemplate,
private val restTargetProperties: RestTargetProperties,
private val retryTemplate: RetryTemplate,
- private val reportService: ReportService
+ private val reportService: ReportService,
) : MtbFileSender {
-
private val logger = LoggerFactory.getLogger(RestMtbFileSender::class.java)
abstract fun sendUrl(): String
@@ -52,27 +51,29 @@ abstract class RestMtbFileSender(
return retryTemplate.execute<MtbFileSender.Response, Exception> {
val headers = getHttpHeaders(request)
val entityReq = HttpEntity(request.content, headers)
- val response = restTemplate.postForEntity(
- sendUrl(),
- entityReq,
- String::class.java
- )
+ val response = restTemplate.postForEntity(sendUrl(), entityReq, String::class.java)
if (!response.statusCode.is2xxSuccessful) {
logger.warn("Error sending to remote system: {}", response.body)
return@execute MtbFileSender.Response(
reportService.deserialize(response.body).asRequestStatus(),
- "Status-Code: ${response.statusCode.value()}"
+ "Status-Code: ${response.statusCode.value()}",
)
}
logger.debug("Sent file via RestMtbFileSender")
- return@execute MtbFileSender.Response(reportService.deserialize(response.body).asRequestStatus(), response.body.orEmpty())
+ return@execute MtbFileSender.Response(
+ reportService.deserialize(response.body).asRequestStatus(),
+ response.body.orEmpty(),
+ )
}
} catch (e: IllegalArgumentException) {
logger.error("Not a valid URI to export to: '{}'", restTargetProperties.uri!!)
} catch (e: RestClientResponseException) {
logger.info(restTargetProperties.uri!!.toString())
logger.error("Request data not accepted by remote system", e)
- return MtbFileSender.Response(reportService.deserialize(e.responseBodyAsString).asRequestStatus(), e.responseBodyAsString)
+ return MtbFileSender.Response(
+ reportService.deserialize(e.responseBodyAsString).asRequestStatus(),
+ e.responseBodyAsString,
+ )
}
return MtbFileSender.Response(RequestStatus.ERROR, "Sonstiger Fehler bei der Übertragung")
}
@@ -82,11 +83,7 @@ abstract class RestMtbFileSender(
return retryTemplate.execute<MtbFileSender.Response, Exception> {
val headers = getHttpHeaders(request)
val entityReq = HttpEntity(null, headers)
- restTemplate.delete(
- deleteUrl(request.patientId),
- entityReq,
- String::class.java
- )
+ restTemplate.delete(deleteUrl(request.patientId), entityReq, String::class.java)
logger.debug("Sent file via RestMtbFileSender")
return@execute MtbFileSender.Response(RequestStatus.SUCCESS)
}
@@ -99,18 +96,17 @@ abstract class RestMtbFileSender(
return MtbFileSender.Response(RequestStatus.ERROR, "Sonstiger Fehler bei der Übertragung")
}
- override fun endpoint(): String {
- return this.restTargetProperties.uri.orEmpty()
- }
+ override fun endpoint(): String = this.restTargetProperties.uri.orEmpty()
private fun getHttpHeaders(request: MtbRequest): HttpHeaders {
val username = restTargetProperties.username
val password = restTargetProperties.password
val headers = HttpHeaders()
- headers.contentType = when (request) {
- is DnpmV2MtbFileRequest -> CustomMediaType.APPLICATION_VND_DNPM_V2_MTB_JSON
- else -> MediaType.APPLICATION_JSON
- }
+ headers.contentType =
+ when (request) {
+ is DnpmV2MtbFileRequest -> CustomMediaType.APPLICATION_VND_DNPM_V2_MTB_JSON
+ else -> MediaType.APPLICATION_JSON
+ }
if (username.isNullOrBlank() || password.isNullOrBlank()) {
return headers
@@ -119,5 +115,4 @@ abstract class RestMtbFileSender(
headers.setBasicAuth(username, password)
return headers
}
-
}
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/AnonymizingGenerator.kt b/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/AnonymizingGenerator.kt
index dcb438f..90d867f 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/AnonymizingGenerator.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/AnonymizingGenerator.kt
@@ -24,24 +24,17 @@ import org.apache.commons.codec.digest.DigestUtils
import java.security.SecureRandom
class AnonymizingGenerator : Generator {
- companion object fun getSecureRandom() : SecureRandom {
- return SecureRandom()
- }
+ companion object
- override fun generate(id: String): String {
- return Base32().encodeAsString(DigestUtils.sha256(id))
- .substring(0..41)
- .lowercase()
- }
+ fun getSecureRandom(): SecureRandom = SecureRandom()
+
+ override fun generate(id: String): String = Base32().encodeAsString(DigestUtils.sha256(id)).substring(0..41).lowercase()
@OptIn(ExperimentalStdlibApi::class)
override fun generateGenomDeTan(id: String): String {
-
val bytes = ByteArray(64 / 2)
getSecureRandom().nextBytes(bytes)
return bytes.joinToString("") { "%02x".format(it) }
-
}
-
-} \ No newline at end of file
+}
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/GpasSoapPseudonymGenerator.kt b/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/GpasSoapPseudonymGenerator.kt
index 8215d23..089736c 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/GpasSoapPseudonymGenerator.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/GpasSoapPseudonymGenerator.kt
@@ -8,19 +8,15 @@ class GpasSoapPseudonymGenerator(
private val gpasCfg: GPasConfigProperties,
private val retryTemplate: RetryTemplate,
private val gpasSoapService: GpasSoapService,
- private val appFhirConfig: AppFhirConfig
+ private val appFhirConfig: AppFhirConfig,
) : Generator {
-
- override fun generate(id: String): String {
- return retryTemplate.execute<String, Exception> {
+ override fun generate(id: String): String =
+ retryTemplate.execute<String, Exception> {
gpasSoapService.getOrCreatePseudonymFor(id, gpasCfg.patientDomain)
}
- }
- override fun generateGenomDeTan(id: String): String {
- return retryTemplate.execute<String, Exception> {
+ override fun generateGenomDeTan(id: String): String =
+ retryTemplate.execute<String, Exception> {
gpasSoapService.createPseudonymsFor(id, gpasCfg.genomDeTanDomain, 1).first()
}
- }
}
-
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/GpasSoapService.kt b/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/GpasSoapService.kt
index 0909924..f1121b8 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/GpasSoapService.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/GpasSoapService.kt
@@ -8,15 +8,14 @@ import jakarta.xml.bind.annotation.XmlElementWrapper
@WebService(
name = "PSNManagerBeanService",
- targetNamespace ="http://psn.ttp.ganimed.icmvc.emau.org/"
+ targetNamespace = "http://psn.ttp.ganimed.icmvc.emau.org/",
)
interface GpasSoapService {
-
@WebMethod(operationName = "getOrCreatePseudonymFor")
@WebResult(name = "psn")
fun getOrCreatePseudonymFor(
@WebParam(name = "value") value: String,
- @WebParam(name = "domainName") domainName: String
+ @WebParam(name = "domainName") domainName: String,
): String
@WebMethod(operationName = "createPseudonymsFor")
@@ -25,7 +24,6 @@ interface GpasSoapService {
fun createPseudonymsFor(
@WebParam(name = "value") value: String,
@WebParam(name = "domainName") domainName: String,
- @WebParam(name = "number") minNumber: Int
+ @WebParam(name = "number") minNumber: Int,
): List<String>
-
-} \ No newline at end of file
+}
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 96225a9..77ab87d 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/PseudonymizeService.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/PseudonymizeService.kt
@@ -25,22 +25,16 @@ import dev.dnpm.etl.processor.config.PseudonymizeConfigProperties
class PseudonymizeService(
private val generator: Generator,
- private val configProperties: PseudonymizeConfigProperties
+ private val configProperties: PseudonymizeConfigProperties,
) {
-
- fun patientPseudonym(patientId: PatientId): PatientPseudonym {
- return when (generator) {
+ fun patientPseudonym(patientId: PatientId): PatientPseudonym =
+ when (generator) {
is GpasPseudonymGenerator -> PatientPseudonym(generator.generate(patientId.value))
- else -> PatientPseudonym("${configProperties.prefix}_${generator.generate(patientId.value)}")
+ else ->
+ PatientPseudonym("${configProperties.prefix}_${generator.generate(patientId.value)}")
}
- }
-
- fun genomDeTan(patientId: PatientId): String {
- return generator.generateGenomDeTan(patientId.value)
- }
- fun prefix(): String {
- return configProperties.prefix
- }
+ fun genomDeTan(patientId: PatientId): String = generator.generateGenomDeTan(patientId.value)
-} \ No newline at end of file
+ fun prefix(): String = configProperties.prefix
+}
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 8721cbe..48ac58a 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/extensions.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/extensions.kt
@@ -25,282 +25,246 @@ import dev.pcvolkmer.mv64e.mtb.Mtb
import dev.pcvolkmer.mv64e.mtb.MvhMetadata
import org.apache.commons.codec.digest.DigestUtils
-/** Replaces patient ID with generated patient pseudonym
- *
- * @since 0.11.0
+/**
+ * Replaces patient ID with generated patient pseudonym
*
* @param pseudonymizeService The pseudonymizeService to be used
* @return The MTB file containing patient pseudonymes
+ * @since 0.11.0
*/
infix fun Mtb.pseudonymizeWith(pseudonymizeService: PseudonymizeService) {
- val patientPseudonym = pseudonymizeService.patientPseudonym(PatientId(this.patient.id)).value
-
- this.episodesOfCare?.forEach { it.patient?.id = patientPseudonym }
- this.carePlans?.forEach {
- it.patient.id = patientPseudonym
- it.rebiopsyRequests?.forEach { it.patient?.id = patientPseudonym }
- it.histologyReevaluationRequests?.forEach { it.patient?.id = patientPseudonym }
- it.medicationRecommendations?.forEach { it.patient?.id = patientPseudonym }
- it.studyEnrollmentRecommendations?.forEach { it.patient?.id = patientPseudonym }
- it.procedureRecommendations?.forEach { it.patient?.id = patientPseudonym }
- it.geneticCounselingRecommendation?.patient?.id = patientPseudonym
- }
- this.diagnoses?.forEach { it.patient?.id = patientPseudonym }
- this.guidelineTherapies?.forEach { it.patient?.id = patientPseudonym }
- this.guidelineProcedures?.forEach { it.patient?.id = patientPseudonym }
- this.patient.id = patientPseudonym
- this.claims?.forEach { it.patient?.id = patientPseudonym }
- this.claimResponses?.forEach { it.patient?.id = patientPseudonym }
- this.diagnoses?.forEach { it.patient?.id = patientPseudonym }
- this.familyMemberHistories?.forEach { it.patient?.id = patientPseudonym }
- this.histologyReports?.forEach {
- it.patient.id = patientPseudonym
- it.results.tumorMorphology?.patient?.id = patientPseudonym
- it.results.tumorCellContent?.patient?.id = patientPseudonym
- }
- this.ngsReports?.forEach {
- it.patient?.id = patientPseudonym
- it.results?.simpleVariants?.forEach { it.patient?.id = patientPseudonym }
- it.results?.copyNumberVariants?.forEach { it.patient?.id = patientPseudonym }
- it.results?.dnaFusions?.forEach { it.patient?.id = patientPseudonym }
- it.results?.rnaFusions?.forEach { it.patient?.id = patientPseudonym }
- it.results?.tumorCellContent?.patient?.id = patientPseudonym
- it.results?.brcaness?.patient?.id = patientPseudonym
- it.results?.tmb?.patient?.id = patientPseudonym
- it.results?.hrdScore?.patient?.id = patientPseudonym
- }
- this.ihcReports?.forEach {
- it.patient?.id = patientPseudonym
- it.results?.msiMmr?.forEach { it.patient?.id = patientPseudonym }
- it.results?.proteinExpression?.forEach { it.patient?.id = patientPseudonym }
- }
- this.responses?.forEach { it.patient?.id = patientPseudonym }
- this.specimens?.forEach { it.patient?.id = patientPseudonym }
- this.priorDiagnosticReports?.forEach { it.patient?.id = patientPseudonym }
- this.performanceStatus?.forEach { it.patient?.id = patientPseudonym }
- this.systemicTherapies?.forEach {
- it.history?.forEach {
- it.patient?.id = patientPseudonym
- }
- }
- this.followUps?.forEach {
- it.patient?.id = patientPseudonym
- }
-
- this.msiFindings?.forEach { it -> it.patient.id = patientPseudonym }
-
- this.metadata?.researchConsents?.forEach { it ->
- val entry = it ?: return@forEach
- if (entry.contains("patient")) {
- // here we expect only a patient reference any other data like display
- // need to be removed, since may contain unsecure data
- entry.remove("patient")
- entry["patient"] = mapOf("reference" to "Patient/$patientPseudonym")
- }
+ val patientPseudonym = pseudonymizeService.patientPseudonym(PatientId(this.patient.id)).value
+
+ this.episodesOfCare?.forEach { it.patient?.id = patientPseudonym }
+ this.carePlans?.forEach {
+ it.patient.id = patientPseudonym
+ it.rebiopsyRequests?.forEach { it.patient?.id = patientPseudonym }
+ it.histologyReevaluationRequests?.forEach { it.patient?.id = patientPseudonym }
+ it.medicationRecommendations?.forEach { it.patient?.id = patientPseudonym }
+ it.studyEnrollmentRecommendations?.forEach { it.patient?.id = patientPseudonym }
+ it.procedureRecommendations?.forEach { it.patient?.id = patientPseudonym }
+ it.geneticCounselingRecommendation?.patient?.id = patientPseudonym
+ }
+ this.diagnoses?.forEach { it.patient?.id = patientPseudonym }
+ this.guidelineTherapies?.forEach { it.patient?.id = patientPseudonym }
+ this.guidelineProcedures?.forEach { it.patient?.id = patientPseudonym }
+ this.patient.id = patientPseudonym
+ this.claims?.forEach { it.patient?.id = patientPseudonym }
+ this.claimResponses?.forEach { it.patient?.id = patientPseudonym }
+ this.diagnoses?.forEach { it.patient?.id = patientPseudonym }
+ this.familyMemberHistories?.forEach { it.patient?.id = patientPseudonym }
+ this.histologyReports?.forEach {
+ it.patient.id = patientPseudonym
+ it.results.tumorMorphology?.patient?.id = patientPseudonym
+ it.results.tumorCellContent?.patient?.id = patientPseudonym
+ }
+ this.ngsReports?.forEach {
+ it.patient?.id = patientPseudonym
+ it.results?.simpleVariants?.forEach { it.patient?.id = patientPseudonym }
+ it.results?.copyNumberVariants?.forEach { it.patient?.id = patientPseudonym }
+ it.results?.dnaFusions?.forEach { it.patient?.id = patientPseudonym }
+ it.results?.rnaFusions?.forEach { it.patient?.id = patientPseudonym }
+ it.results?.tumorCellContent?.patient?.id = patientPseudonym
+ it.results?.brcaness?.patient?.id = patientPseudonym
+ it.results?.tmb?.patient?.id = patientPseudonym
+ it.results?.hrdScore?.patient?.id = patientPseudonym
+ }
+ this.ihcReports?.forEach {
+ it.patient?.id = patientPseudonym
+ it.results?.msiMmr?.forEach { it.patient?.id = patientPseudonym }
+ it.results?.proteinExpression?.forEach { it.patient?.id = patientPseudonym }
+ }
+ this.responses?.forEach { it.patient?.id = patientPseudonym }
+ this.specimens?.forEach { it.patient?.id = patientPseudonym }
+ this.priorDiagnosticReports?.forEach { it.patient?.id = patientPseudonym }
+ this.performanceStatus?.forEach { it.patient?.id = patientPseudonym }
+ this.systemicTherapies?.forEach { it.history?.forEach { it.patient?.id = patientPseudonym } }
+ this.followUps?.forEach { it.patient?.id = patientPseudonym }
+
+ this.msiFindings?.forEach { it -> it.patient.id = patientPseudonym }
+
+ this.metadata?.researchConsents?.forEach { it ->
+ val entry = it ?: return@forEach
+ if (entry.contains("patient")) {
+ // here we expect only a patient reference any other data like display
+ // need to be removed, since may contain unsecure data
+ entry.remove("patient")
+ entry["patient"] = mapOf("reference" to "Patient/$patientPseudonym")
}
+ }
}
/**
* Creates new hash of content IDs with given prefix except for patient IDs
*
- * @since 0.11.0
- *
* @param pseudonymizeService The pseudonymizeService to be used
* @return The MTB file containing rehashed content IDs
+ * @since 0.11.0
*/
infix fun Mtb.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.episodesOfCare?.forEach {
- it?.apply { id = id?.let(::anonymize) }
- it.diagnoses?.forEach { it ->
- it?.id = it.id?.let(::anonymize)
- }
- }
-
- this.carePlans?.onEach { carePlan ->
- carePlan?.apply {
- this.id = id?.let { anonymize(it) }
-
- this.geneticCounselingRecommendation?.apply {
- this.id = this.id?.let(::anonymize)
- }
- this.rebiopsyRequests?.forEach { it ->
- it.id = it.id?.let(::anonymize)
- it.tumorEntity?.id = it.tumorEntity?.id?.let(::anonymize)
- }
- this.histologyReevaluationRequests?.forEach { it ->
- it.id = it?.id?.let(::anonymize)
- it.specimen?.id = it.specimen?.id?.let(::anonymize)
- }
-
- this.medicationRecommendations?.forEach { it ->
- it.id = it?.id?.let(::anonymize)
- it.supportingVariants?.forEach { it ->
- it.variant?.id = it.variant?.id?.let(::anonymize)
- }
- it.reason?.id = it.reason?.id?.let(::anonymize)
- }
- this.reason?.id = this.reason?.id?.let(::anonymize)
- this.studyEnrollmentRecommendations?.forEach { it ->
- it?.reason?.id = it.reason?.id?.let(::anonymize)
- }
- this.procedureRecommendations?.forEach { it ->
- it.id = it?.id?.let(::anonymize)
- it.supportingVariants?.forEach { it ->
- it.variant?.id = it.variant?.id?.let(::anonymize)
- }
-
- it.reason?.id = it.reason?.id?.let(::anonymize)
-
- }
- this.studyEnrollmentRecommendations?.forEach { it ->
- it.id = it?.id?.let(::anonymize)
- it.supportingVariants.forEach { it ->
- it.variant?.id = it?.variant?.id?.let(::anonymize)
- }
- }
- }
- }
-
-
- this.responses?.forEach { it ->
-
- it?.id = it.id?.let(::anonymize)
- it?.therapy?.id = it.therapy?.id?.let(::anonymize)
-
- }
-
- this.diagnoses?.forEach { it ->
-
+ val prefix = pseudonymizeService.prefix()
+
+ fun anonymize(id: String): String {
+ val hash = DigestUtils.sha256Hex("$prefix-$id").substring(0, 41).lowercase()
+ return "$prefix$hash"
+ }
+
+ this.episodesOfCare?.forEach {
+ it?.apply { id = id?.let(::anonymize) }
+ it.diagnoses?.forEach { it -> it?.id = it.id?.let(::anonymize) }
+ }
+
+ this.carePlans?.onEach { carePlan ->
+ carePlan?.apply {
+ this.id = id?.let { anonymize(it) }
+
+ this.geneticCounselingRecommendation?.apply { this.id = this.id?.let(::anonymize) }
+ this.rebiopsyRequests?.forEach { it ->
+ it.id = it.id?.let(::anonymize)
+ it.tumorEntity?.id = it.tumorEntity?.id?.let(::anonymize)
+ }
+ this.histologyReevaluationRequests?.forEach { it ->
it.id = it?.id?.let(::anonymize)
- it.histology?.forEach { it -> it.id = it?.id?.let(::anonymize) }
- }
-
- this.ngsReports?.forEach { it ->
- it.id = it?.id?.let(::anonymize)
- it.results?.tumorCellContent?.id = it.results.tumorCellContent?.id?.let(::anonymize)
- it.results?.tumorCellContent?.specimen?.id =
- it.results?.tumorCellContent?.specimen?.id?.let(::anonymize)
- it.results?.rnaFusions?.forEach { it ->
- it?.id = it.id?.let(::anonymize)
- }
- it.results?.simpleVariants?.forEach { it ->
- it?.id = it.id?.let(::anonymize)
- it?.transcriptId?.value = it.transcriptId?.value?.let(::anonymize)
- }
- it.results?.tmb?.id = it.results?.tmb?.id?.let(::anonymize)
- it.results?.tmb?.specimen?.id = it.results?.tmb?.specimen?.id?.let(::anonymize)
-
- it.results?.brcaness?.id = it.results?.brcaness?.id?.let(::anonymize)
- it.results?.brcaness?.specimen?.id = it.results?.brcaness?.specimen?.id?.let(::anonymize)
- it.results?.copyNumberVariants?.forEach { it -> it?.id = it.id?.let(::anonymize) }
- it.results?.hrdScore?.id = it.results?.hrdScore?.id?.let(::anonymize)
- it.results?.hrdScore?.specimen?.id = it.results?.hrdScore?.specimen?.id?.let(::anonymize)
- it.results?.rnaSeqs?.forEach { it -> it?.id = it.id?.let(::anonymize) }
- it.results?.dnaFusions?.forEach { it -> it?.id = it.id?.let(::anonymize) }
- it.specimen?.id = it?.specimen?.id?.let(::anonymize)
-
- }
-
- this.histologyReports?.forEach { it ->
- it.id = it?.id?.let(::anonymize)
- it.results?.tumorCellContent?.id = it.results?.tumorCellContent?.id?.let(::anonymize)
- it.results?.tumorCellContent?.specimen?.id =
- it.results?.tumorCellContent?.specimen?.id?.let(::anonymize)
-
- it.results?.tumorMorphology?.id = it.results?.tumorMorphology?.id?.let(::anonymize)
- it.results?.tumorMorphology?.specimen?.id =
- it.results?.tumorMorphology?.specimen?.id?.let(::anonymize)
it.specimen?.id = it.specimen?.id?.let(::anonymize)
+ }
- }
- this.claimResponses?.forEach { it ->
- it.id = it?.id?.let(::anonymize)
- it.claim?.id = it.claim?.id?.let(::anonymize)
- }
- this.claims?.forEach { it ->
-
- it.id = it?.id?.let(::anonymize)
- it.recommendation?.id = it.recommendation?.id?.let(::anonymize)
-
- }
- this.familyMemberHistories?.forEach { it -> it.id = it?.id?.let(::anonymize) }
- this.guidelineProcedures?.forEach { it ->
+ this.medicationRecommendations?.forEach { it ->
it.id = it?.id?.let(::anonymize)
+ it.supportingVariants?.forEach { it -> it.variant?.id = it.variant?.id?.let(::anonymize) }
it.reason?.id = it.reason?.id?.let(::anonymize)
- it.basedOn?.id = it.basedOn?.id?.let(::anonymize)
-
- }
-
- this.guidelineTherapies?.forEach { it ->
- it.id = it?.id?.let(::anonymize)
- it.reason?.id = it.reason?.id?.let(::anonymize)
- it.basedOn?.id = it.basedOn?.id?.let(::anonymize)
- }
- this.ihcReports?.forEach { it ->
- it.id = it?.id?.let(::anonymize)
- it.specimen?.id = it.specimen?.id?.let(::anonymize)
- it.results?.proteinExpression?.forEach { it -> it?.id = it.id.let(::anonymize) }
- }
-
- this.msiFindings?.forEach { it ->
-
+ }
+ this.reason?.id = this.reason?.id?.let(::anonymize)
+ this.studyEnrollmentRecommendations?.forEach { it ->
+ it?.reason?.id = it.reason?.id?.let(::anonymize)
+ }
+ this.procedureRecommendations?.forEach { it ->
it.id = it?.id?.let(::anonymize)
- it.specimen?.id = it.specimen?.id?.let(::anonymize)
- }
-
- this.performanceStatus?.forEach { it -> it.id = it?.id?.let(::anonymize) }
-
- this.priorDiagnosticReports?.forEach { it ->
+ it.supportingVariants?.forEach { it -> it.variant?.id = it.variant?.id?.let(::anonymize) }
+ it.reason?.id = it.reason?.id?.let(::anonymize)
+ }
+ this.studyEnrollmentRecommendations?.forEach { it ->
it.id = it?.id?.let(::anonymize)
- it.specimen?.id = it.specimen?.id?.let(::anonymize)
+ it.supportingVariants.forEach { it -> it.variant?.id = it?.variant?.id?.let(::anonymize) }
+ }
}
-
- this.specimens?.forEach { it ->
-
- it.id = it?.id?.let(::anonymize)
- it.diagnosis?.id = it.diagnosis?.id?.let(::anonymize)
-
+ }
+
+ this.responses?.forEach { it ->
+ it?.id = it.id?.let(::anonymize)
+ it?.therapy?.id = it.therapy?.id?.let(::anonymize)
+ }
+
+ this.diagnoses?.forEach { it ->
+ it.id = it?.id?.let(::anonymize)
+ it.histology?.forEach { it -> it.id = it?.id?.let(::anonymize) }
+ }
+
+ this.ngsReports?.forEach { it ->
+ it.id = it?.id?.let(::anonymize)
+ it.results?.tumorCellContent?.id = it.results.tumorCellContent?.id?.let(::anonymize)
+ it.results?.tumorCellContent?.specimen?.id =
+ it.results?.tumorCellContent?.specimen?.id?.let(::anonymize)
+ it.results?.rnaFusions?.forEach { it -> it?.id = it.id?.let(::anonymize) }
+ it.results?.simpleVariants?.forEach { it ->
+ it?.id = it.id?.let(::anonymize)
+ it?.transcriptId?.value = it.transcriptId?.value?.let(::anonymize)
}
-
- this.systemicTherapies?.forEach { it ->
-
- it.history?.forEach { it ->
-
- it.id = it?.id?.let(::anonymize)
- it.reason?.id = it.reason?.id?.let(::anonymize)
- it.basedOn?.id = it.basedOn?.id?.let(::anonymize)
- }
-
+ it.results?.tmb?.id = it.results?.tmb?.id?.let(::anonymize)
+ it.results?.tmb?.specimen?.id = it.results?.tmb?.specimen?.id?.let(::anonymize)
+
+ it.results?.brcaness?.id = it.results?.brcaness?.id?.let(::anonymize)
+ it.results?.brcaness?.specimen?.id = it.results?.brcaness?.specimen?.id?.let(::anonymize)
+ it.results?.copyNumberVariants?.forEach { it -> it?.id = it.id?.let(::anonymize) }
+ it.results?.hrdScore?.id = it.results?.hrdScore?.id?.let(::anonymize)
+ it.results?.hrdScore?.specimen?.id = it.results?.hrdScore?.specimen?.id?.let(::anonymize)
+ it.results?.rnaSeqs?.forEach { it -> it?.id = it.id?.let(::anonymize) }
+ it.results?.dnaFusions?.forEach { it -> it?.id = it.id?.let(::anonymize) }
+ it.specimen?.id = it?.specimen?.id?.let(::anonymize)
+ }
+
+ this.histologyReports?.forEach { it ->
+ it.id = it?.id?.let(::anonymize)
+ it.results?.tumorCellContent?.id = it.results?.tumorCellContent?.id?.let(::anonymize)
+ it.results?.tumorCellContent?.specimen?.id =
+ it.results?.tumorCellContent?.specimen?.id?.let(::anonymize)
+
+ it.results?.tumorMorphology?.id = it.results?.tumorMorphology?.id?.let(::anonymize)
+ it.results?.tumorMorphology?.specimen?.id =
+ it.results?.tumorMorphology?.specimen?.id?.let(::anonymize)
+ it.specimen?.id = it.specimen?.id?.let(::anonymize)
+ }
+ this.claimResponses?.forEach { it ->
+ it.id = it?.id?.let(::anonymize)
+ it.claim?.id = it.claim?.id?.let(::anonymize)
+ }
+ this.claims?.forEach { it ->
+ it.id = it?.id?.let(::anonymize)
+ it.recommendation?.id = it.recommendation?.id?.let(::anonymize)
+ }
+ this.familyMemberHistories?.forEach { it -> it.id = it?.id?.let(::anonymize) }
+ this.guidelineProcedures?.forEach { it ->
+ it.id = it?.id?.let(::anonymize)
+ it.reason?.id = it.reason?.id?.let(::anonymize)
+ it.basedOn?.id = it.basedOn?.id?.let(::anonymize)
+ }
+
+ this.guidelineTherapies?.forEach { it ->
+ it.id = it?.id?.let(::anonymize)
+ it.reason?.id = it.reason?.id?.let(::anonymize)
+ it.basedOn?.id = it.basedOn?.id?.let(::anonymize)
+ }
+ this.ihcReports?.forEach { it ->
+ it.id = it?.id?.let(::anonymize)
+ it.specimen?.id = it.specimen?.id?.let(::anonymize)
+ it.results?.proteinExpression?.forEach { it -> it?.id = it.id.let(::anonymize) }
+ }
+
+ this.msiFindings?.forEach { it ->
+ it.id = it?.id?.let(::anonymize)
+ it.specimen?.id = it.specimen?.id?.let(::anonymize)
+ }
+
+ this.performanceStatus?.forEach { it -> it.id = it?.id?.let(::anonymize) }
+
+ this.priorDiagnosticReports?.forEach { it ->
+ it.id = it?.id?.let(::anonymize)
+ it.specimen?.id = it.specimen?.id?.let(::anonymize)
+ }
+
+ this.specimens?.forEach { it ->
+ it.id = it?.id?.let(::anonymize)
+ it.diagnosis?.id = it.diagnosis?.id?.let(::anonymize)
+ }
+
+ this.systemicTherapies?.forEach { it ->
+ it.history?.forEach { it ->
+ it.id = it?.id?.let(::anonymize)
+ it.reason?.id = it.reason?.id?.let(::anonymize)
+ it.basedOn?.id = it.basedOn?.id?.let(::anonymize)
}
+ }
}
fun Mtb.ensureMetaDataIsInitialized() {
- // init metadata if necessary
- if (this.metadata == null) {
- val mvhMetadata = MvhMetadata.builder().build()
- this.metadata = mvhMetadata
- }
- if (this.metadata.researchConsents == null) {
- this.metadata.researchConsents = mutableListOf()
- }
- if (this.metadata.modelProjectConsent == null) {
- this.metadata.modelProjectConsent = ModelProjectConsent()
- this.metadata.modelProjectConsent.provisions = mutableListOf()
- } else if (this.metadata.modelProjectConsent.provisions != null) {
- // make sure list can be changed
- this.metadata.modelProjectConsent.provisions =
- this.metadata.modelProjectConsent.provisions.toMutableList()
- }
+ // init metadata if necessary
+ if (this.metadata == null) {
+ val mvhMetadata = MvhMetadata.builder().build()
+ this.metadata = mvhMetadata
+ }
+ if (this.metadata.researchConsents == null) {
+ this.metadata.researchConsents = mutableListOf()
+ }
+ if (this.metadata.modelProjectConsent == null) {
+ this.metadata.modelProjectConsent = ModelProjectConsent()
+ this.metadata.modelProjectConsent.provisions = mutableListOf()
+ } else if (this.metadata.modelProjectConsent.provisions != null) {
+ // make sure list can be changed
+ this.metadata.modelProjectConsent.provisions =
+ this.metadata.modelProjectConsent.provisions.toMutableList()
+ }
}
infix fun Mtb.addGenomDeTan(pseudonymizeService: PseudonymizeService) {
- this.metadata?.transferTan = pseudonymizeService.genomDeTan(PatientId(this.patient.id))
+ this.metadata?.transferTan = pseudonymizeService.genomDeTan(PatientId(this.patient.id))
}
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/security/TokenService.kt b/src/main/kotlin/dev/dnpm/etl/processor/security/TokenService.kt
index 44b04e8..fdaa7d2 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/security/TokenService.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/security/TokenService.kt
@@ -20,6 +20,8 @@
package dev.dnpm.etl.processor.security
import jakarta.annotation.PostConstruct
+import java.time.Instant
+import java.util.*
import org.springframework.data.annotation.Id
import org.springframework.data.relational.core.mapping.Table
import org.springframework.data.repository.CrudRepository
@@ -27,57 +29,50 @@ import org.springframework.data.repository.findByIdOrNull
import org.springframework.security.core.userdetails.User
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.security.provisioning.InMemoryUserDetailsManager
-import java.time.Instant
-import java.util.*
class TokenService(
private val userDetailsManager: InMemoryUserDetailsManager,
private val passwordEncoder: PasswordEncoder,
- private val tokenRepository: TokenRepository
+ private val tokenRepository: TokenRepository,
) {
- @PostConstruct
- fun setup() {
- tokenRepository.findAll().forEach {
- userDetailsManager.createUser(
- User.withUsername(it.username)
- .password(it.password)
- .roles("MTBFILE")
- .build()
- )
- }
+ @PostConstruct
+ fun setup() {
+ tokenRepository.findAll().forEach {
+ userDetailsManager.createUser(
+ User.withUsername(it.username).password(it.password).roles("MTBFILE").build()
+ )
}
+ }
- fun addToken(name: String): Result<String> {
- val username = name.lowercase().replace("""[^a-z0-9]""".toRegex(), "")
- if (userDetailsManager.userExists(username)) {
- return Result.failure(RuntimeException("Cannot use token name"))
- }
+ fun addToken(name: String): Result<String> {
+ val username = name.lowercase().replace("""[^a-z0-9]""".toRegex(), "")
+ if (userDetailsManager.userExists(username)) {
+ return Result.failure(RuntimeException("Cannot use token name"))
+ }
- val password = Base64.getEncoder().encodeToString(UUID.randomUUID().toString().encodeToByteArray())
- val encodedPassword = passwordEncoder.encode(password).toString()
+ val password =
+ Base64.getEncoder().encodeToString(UUID.randomUUID().toString().encodeToByteArray())
+ val encodedPassword = passwordEncoder.encode(password).toString()
- userDetailsManager.createUser(
- User.withUsername(username)
- .password(encodedPassword)
- .roles("MTBFILE")
- .build()
- )
+ userDetailsManager.createUser(
+ User.withUsername(username).password(encodedPassword).roles("MTBFILE").build()
+ )
- tokenRepository.save(Token(name = name, username = username, password = encodedPassword))
+ tokenRepository.save(Token(name = name, username = username, password = encodedPassword))
- return Result.success("$username:$password")
- }
+ return Result.success("$username:$password")
+ }
- fun deleteToken(id: Long) {
- val token = tokenRepository.findByIdOrNull(id) ?: return
- userDetailsManager.deleteUser(token.username)
- tokenRepository.delete(token)
- }
+ fun deleteToken(id: Long) {
+ val token = tokenRepository.findByIdOrNull(id) ?: return
+ userDetailsManager.deleteUser(token.username)
+ tokenRepository.delete(token)
+ }
- fun findAll(): List<Token> {
- return tokenRepository.findAll().toList()
- }
+ fun findAll(): List<Token> {
+ return tokenRepository.findAll().toList()
+ }
}
@Table("token")
@@ -86,7 +81,7 @@ data class Token(
val name: String,
val username: String,
val password: String,
- val createdAt: Instant = Instant.now()
+ val createdAt: Instant = Instant.now(),
)
-interface TokenRepository : CrudRepository<Token, Long> \ No newline at end of file
+interface TokenRepository : CrudRepository<Token, Long>
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/security/UserRole.kt b/src/main/kotlin/dev/dnpm/etl/processor/security/UserRole.kt
index a1d45c8..bfe966a 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/security/UserRole.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/security/UserRole.kt
@@ -20,26 +20,21 @@
package dev.dnpm.etl.processor.security
+import java.util.*
import org.springframework.data.annotation.Id
import org.springframework.data.relational.core.mapping.Table
import org.springframework.data.repository.CrudRepository
-import java.util.*
@Table("user_role")
-data class UserRole(
- @Id val id: Long? = null,
- val username: String,
- var role: Role = Role.GUEST
-)
+data class UserRole(@Id val id: Long? = null, val username: String, var role: Role = Role.GUEST)
enum class Role(val value: String) {
- GUEST("guest"),
- USER("user"),
- ADMIN("admin")
+ GUEST("guest"),
+ USER("user"),
+ ADMIN("admin"),
}
interface UserRoleRepository : CrudRepository<UserRole, Long> {
- fun findByUsername(username: String): Optional<UserRole>
-
-} \ No newline at end of file
+ fun findByUsername(username: String): Optional<UserRole>
+}
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/security/UserRoleService.kt b/src/main/kotlin/dev/dnpm/etl/processor/security/UserRoleService.kt
index 174f8a9..bf46b84 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/security/UserRoleService.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/security/UserRoleService.kt
@@ -25,9 +25,12 @@ import org.springframework.security.oauth2.core.oidc.user.OidcUser
class UserRoleService(
private val userRoleRepository: UserRoleRepository,
- private val sessionRegistry: SessionRegistry
+ private val sessionRegistry: SessionRegistry,
) {
- fun updateUserRole(id: Long, role: Role) {
+ fun updateUserRole(
+ id: Long,
+ role: Role,
+ ) {
val userRole = userRoleRepository.findByIdOrNull(id) ?: return
userRole.role = role
userRoleRepository.save(userRole)
@@ -40,19 +43,13 @@ class UserRoleService(
expireSessionFor(userRole.username)
}
- fun findAll(): List<UserRole> {
- return userRoleRepository.findAll().toList()
- }
+ fun findAll(): List<UserRole> = userRoleRepository.findAll().toList()
private fun expireSessionFor(username: String) {
sessionRegistry.allPrincipals
.filterIsInstance<OidcUser>()
.filter { it.preferredUsername == username }
- .flatMap {
- sessionRegistry.getAllSessions(it, true)
- }
- .onEach {
- it.expireNow()
- }
+ .flatMap { sessionRegistry.getAllSessions(it, true) }
+ .onEach { it.expireNow() }
}
-} \ No newline at end of file
+}
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/services/ConsentProcessor.kt b/src/main/kotlin/dev/dnpm/etl/processor/services/ConsentProcessor.kt
index b420d1f..8437962 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/services/ConsentProcessor.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/services/ConsentProcessor.kt
@@ -11,6 +11,10 @@ import dev.dnpm.etl.processor.consent.IConsentService
import dev.dnpm.etl.processor.consent.MtbFileConsentService
import dev.dnpm.etl.processor.pseudonym.ensureMetaDataIsInitialized
import dev.pcvolkmer.mv64e.mtb.*
+import java.io.IOException
+import java.time.Clock
+import java.time.Instant
+import java.util.*
import org.apache.commons.lang3.NotImplementedException
import org.hl7.fhir.r4.model.*
import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent
@@ -19,10 +23,6 @@ import org.hl7.fhir.r4.model.Consent.ProvisionComponent
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service
-import java.io.IOException
-import java.time.Clock
-import java.time.Instant
-import java.util.*
@Service
class ConsentProcessor(
@@ -30,248 +30,271 @@ class ConsentProcessor(
private val gIcsConfigProperties: GIcsConfigProperties,
private val objectMapper: ObjectMapper,
private val fhirContext: FhirContext,
- private val consentService: IConsentService
+ private val consentService: IConsentService,
) {
- private var logger: Logger = LoggerFactory.getLogger("ConsentProcessor")
-
- /**
- * In case an instance of {@link ICheckConsent} is active, consent will be embedded and checked.
- *
- * Logic:
- * * <c>true</c> IF consent check is disabled.
- * * <c>true</c> IF broad consent (BC) has been given.
- * * <c>true</c> BC has been asked AND declined but genomDe consent has been consented.
- * * ELSE <c>false</c> is returned.
- *
- * @param mtbFile File v2 (will be enriched with consent data)
- * @return true if consent is given
- *
- */
- fun consentGatedCheckAndTryEmbedding(mtbFile: Mtb): Boolean {
- if (consentService is MtbFileConsentService) {
- // consent check is disabled
- return true
- }
-
- mtbFile.ensureMetaDataIsInitialized()
-
- val personIdentifierValue = mtbFile.patient.id
- val requestDate = Date.from(Instant.now(Clock.systemUTC()))
+ private var logger: Logger = LoggerFactory.getLogger("ConsentProcessor")
+
+ /**
+ * In case an instance of {@link ICheckConsent} is active, consent will be embedded and checked.
+ *
+ * Logic:
+ * * <c>true</c> IF consent check is disabled.
+ * * <c>true</c> IF broad consent (BC) has been given.
+ * * <c>true</c> BC has been asked AND declined but genomDe consent has been consented.
+ * * ELSE <c>false</c> is returned.
+ *
+ * @param mtbFile File v2 (will be enriched with consent data)
+ * @return true if consent is given
+ */
+ fun consentGatedCheckAndTryEmbedding(mtbFile: Mtb): Boolean {
+ if (consentService is MtbFileConsentService) {
+ // consent check is disabled
+ return true
+ }
- // 1. Broad consent Entry exists?
- // 1.1. -> yes and research consent is given -> send mtb file
- // 1.2. -> no -> return status error - consent has not been asked
- // 2. -> Broad consent found but rejected -> is GenomDe consent provision 'sequencing' given?
- // 2.1 -> yes -> send mtb file
- // 2.2 -> no -> warn/info no consent given
+ mtbFile.ensureMetaDataIsInitialized()
- /*
- * broad consent
- */
- val broadConsent = consentService.getConsent(
- personIdentifierValue, requestDate, ConsentDomain.BROAD_CONSENT
- )
- val broadConsentHasBeenAsked = broadConsent.entry.isNotEmpty()
+ val personIdentifierValue = mtbFile.patient.id
+ val requestDate = Date.from(Instant.now(Clock.systemUTC()))
- // fast exit - if patient has not been asked, we can skip and exit
- if (!broadConsentHasBeenAsked) return false
+ // 1. Broad consent Entry exists?
+ // 1.1. -> yes and research consent is given -> send mtb file
+ // 1.2. -> no -> return status error - consent has not been asked
+ // 2. -> Broad consent found but rejected -> is GenomDe consent provision 'sequencing' given?
+ // 2.1 -> yes -> send mtb file
+ // 2.2 -> no -> warn/info no consent given
- val genomeDeConsent = consentService.getConsent(
- personIdentifierValue, requestDate, ConsentDomain.MODELLVORHABEN_64E
+ /*
+ * broad consent
+ */
+ val broadConsent =
+ consentService.getConsent(personIdentifierValue, requestDate, ConsentDomain.BROAD_CONSENT)
+ val broadConsentHasBeenAsked = broadConsent.entry.isNotEmpty()
+
+ // fast exit - if patient has not been asked, we can skip and exit
+ if (!broadConsentHasBeenAsked) return false
+
+ val genomeDeConsent =
+ consentService.getConsent(
+ personIdentifierValue,
+ requestDate,
+ ConsentDomain.MODELLVORHABEN_64E,
)
- addGenomeDbProvisions(mtbFile, genomeDeConsent)
+ addGenomeDbProvisions(mtbFile, genomeDeConsent)
- if (genomeDeConsent.entry.isNotEmpty()) setGenomDeSubmissionType(mtbFile)
+ if (genomeDeConsent.entry.isNotEmpty()) setGenomDeSubmissionType(mtbFile)
- embedBroadConsentResources(mtbFile, broadConsent)
+ embedBroadConsentResources(mtbFile, broadConsent)
- val broadConsentStatus = getProvisionTypeByPolicyCode(
- broadConsent, requestDate, ConsentDomain.BROAD_CONSENT
- )
-
- val genomDeSequencingStatus = getProvisionTypeByPolicyCode(
- genomeDeConsent, requestDate, ConsentDomain.MODELLVORHABEN_64E
- )
+ val broadConsentStatus =
+ getProvisionTypeByPolicyCode(broadConsent, requestDate, ConsentDomain.BROAD_CONSENT)
- if (Consent.ConsentProvisionType.NULL == broadConsentStatus) {
- // bc not asked
- return false
- }
- if (Consent.ConsentProvisionType.PERMIT == broadConsentStatus || Consent.ConsentProvisionType.PERMIT == genomDeSequencingStatus) return true
+ val genomDeSequencingStatus =
+ getProvisionTypeByPolicyCode(genomeDeConsent, requestDate, ConsentDomain.MODELLVORHABEN_64E)
- return false
+ if (Consent.ConsentProvisionType.NULL == broadConsentStatus) {
+ // bc not asked
+ return false
}
-
- fun embedBroadConsentResources(mtbFile: Mtb, broadConsent: Bundle) {
- for (entry in broadConsent.entry) {
- val resource = entry.resource
- if (resource is Consent) {
- // since jackson convertValue does not work here,
- // we need another step to back to string, before we convert to object map
- val asJsonString = fhirContext.newJsonParser().encodeResourceToString(resource)
- try {
- val mapOfJson: HashMap<String?, Any?>? =
- objectMapper.readValue<HashMap<String?, Any?>?>(
- asJsonString, object : TypeReference<HashMap<String?, Any?>?>() {})
- mtbFile.metadata.researchConsents.add(mapOfJson)
- } catch (e: JsonProcessingException) {
- throw RuntimeException(e)
- }
- }
+ if (
+ Consent.ConsentProvisionType.PERMIT == broadConsentStatus ||
+ Consent.ConsentProvisionType.PERMIT == genomDeSequencingStatus
+ )
+ return true
+
+ return false
+ }
+
+ fun embedBroadConsentResources(mtbFile: Mtb, broadConsent: Bundle) {
+ for (entry in broadConsent.entry) {
+ val resource = entry.resource
+ if (resource is Consent) {
+ // since jackson convertValue does not work here,
+ // we need another step to back to string, before we convert to object map
+ val asJsonString = fhirContext.newJsonParser().encodeResourceToString(resource)
+ try {
+ val mapOfJson: HashMap<String?, Any?>? =
+ objectMapper.readValue<HashMap<String?, Any?>?>(
+ asJsonString,
+ object : TypeReference<HashMap<String?, Any?>?>() {},
+ )
+ mtbFile.metadata.researchConsents.add(mapOfJson)
+ } catch (e: JsonProcessingException) {
+ throw RuntimeException(e)
}
+ }
}
-
- fun addGenomeDbProvisions(mtbFile: Mtb, consentGnomeDe: Bundle) {
- for (entry in consentGnomeDe.entry) {
- val resource = entry.resource
- if (resource !is Consent) {
- continue
- }
-
- // We expect only one provision in collection, therefore get first or none
- val provisions = resource.provision.provision
- if (provisions.isEmpty()) {
- continue
- }
-
- val provisionComponent: ProvisionComponent = provisions.first()
- val provisionCode = getProvisionCode(provisionComponent)
- if (provisionCode != null) {
- try {
- val modelProjectConsentPurpose =
- ModelProjectConsentPurpose.forValue(provisionCode)
-
- if (ModelProjectConsentPurpose.SEQUENCING == modelProjectConsentPurpose) {
- // CONVENTION: wrapping date is date of SEQUENCING consent
- mtbFile.metadata.modelProjectConsent.date = resource.dateTime
- }
-
- val provision = Provision.builder()
- .type(ConsentProvision.valueOf(provisionComponent.type.name))
- .date(provisionComponent.period.start)
- .purpose(modelProjectConsentPurpose).build()
-
- mtbFile.metadata.modelProjectConsent.provisions.add(provision)
- } catch (ioe: IOException) {
- logger.error(
- "Provision code '$provisionCode' is unknown and cannot be mapped.",
- ioe.toString()
- )
- }
- }
-
- if (mtbFile.metadata.modelProjectConsent.provisions.isNotEmpty()) {
- mtbFile.metadata.modelProjectConsent.version =
- gIcsConfigProperties.genomeDeConsentVersion
- }
+ }
+
+ fun addGenomeDbProvisions(mtbFile: Mtb, consentGnomeDe: Bundle) {
+ for (entry in consentGnomeDe.entry) {
+ val resource = entry.resource
+ if (resource !is Consent) {
+ continue
+ }
+
+ // We expect only one provision in collection, therefore get first or none
+ val provisions = resource.provision.provision
+ if (provisions.isEmpty()) {
+ continue
+ }
+
+ val provisionComponent: ProvisionComponent = provisions.first()
+ val provisionCode = getProvisionCode(provisionComponent)
+ if (provisionCode != null) {
+ try {
+ val modelProjectConsentPurpose = ModelProjectConsentPurpose.forValue(provisionCode)
+
+ if (ModelProjectConsentPurpose.SEQUENCING == modelProjectConsentPurpose) {
+ // CONVENTION: wrapping date is date of SEQUENCING consent
+ mtbFile.metadata.modelProjectConsent.date = resource.dateTime
+ }
+
+ val provision =
+ Provision.builder()
+ .type(ConsentProvision.valueOf(provisionComponent.type.name))
+ .date(provisionComponent.period.start)
+ .purpose(modelProjectConsentPurpose)
+ .build()
+
+ mtbFile.metadata.modelProjectConsent.provisions.add(provision)
+ } catch (ioe: IOException) {
+ logger.error(
+ "Provision code '$provisionCode' is unknown and cannot be mapped.",
+ ioe.toString(),
+ )
}
- }
+ }
- private fun getProvisionCode(provisionComponent: ProvisionComponent): String? {
- var provisionCode: String? = null
- if (provisionComponent.code != null && provisionComponent.code.isNotEmpty()) {
- val codableConcept: CodeableConcept = provisionComponent.code.first()
- if (codableConcept.coding != null && codableConcept.coding.isNotEmpty()) {
- provisionCode = codableConcept.coding.first().code
- }
- }
- return provisionCode
+ if (mtbFile.metadata.modelProjectConsent.provisions.isNotEmpty()) {
+ mtbFile.metadata.modelProjectConsent.version = gIcsConfigProperties.genomeDeConsentVersion
+ }
}
-
- private fun setGenomDeSubmissionType(mtbFile: Mtb) {
- if (appConfigProperties.genomDeTestSubmission) {
- mtbFile.metadata.type = MvhSubmissionType.TEST
- logger.info("genomeDe submission mit TEST")
- } else {
- mtbFile.metadata.type = when (mtbFile.metadata.type) {
- null -> MvhSubmissionType.INITIAL
- else -> mtbFile.metadata.type
- }
- }
+ }
+
+ private fun getProvisionCode(provisionComponent: ProvisionComponent): String? {
+ var provisionCode: String? = null
+ if (provisionComponent.code != null && provisionComponent.code.isNotEmpty()) {
+ val codableConcept: CodeableConcept = provisionComponent.code.first()
+ if (codableConcept.coding != null && codableConcept.coding.isNotEmpty()) {
+ provisionCode = codableConcept.coding.first().code
+ }
}
-
- /**
- * @param consentBundle consent resource
- * @param requestDate date which must be within validation period of provision
- * @return type of provision, will be [org.hl7.fhir.r4.model.Consent.ConsentProvisionType.NULL] if none is found.
- */
- fun getProvisionTypeByPolicyCode(
- consentBundle: Bundle, requestDate: Date?, consentDomain: ConsentDomain
- ): Consent.ConsentProvisionType {
- val code: String?
- val system: String?
- if (ConsentDomain.BROAD_CONSENT == consentDomain) {
- code = gIcsConfigProperties.broadConsentPolicyCode
- system = gIcsConfigProperties.broadConsentPolicySystem
- } else if (ConsentDomain.MODELLVORHABEN_64E == consentDomain) {
- code = gIcsConfigProperties.genomeDePolicyCode
- system = gIcsConfigProperties.genomeDePolicySystem
- } else {
- throw NotImplementedException("unknown consent domain " + consentDomain.name)
- }
-
- val provisionTypeByPolicyCode = getProvisionTypeByPolicyCode(
- consentBundle, code, system, requestDate
- )
- return provisionTypeByPolicyCode
+ return provisionCode
+ }
+
+ private fun setGenomDeSubmissionType(mtbFile: Mtb) {
+ if (appConfigProperties.genomDeTestSubmission) {
+ mtbFile.metadata.type = MvhSubmissionType.TEST
+ logger.info("genomeDe submission mit TEST")
+ } else {
+ mtbFile.metadata.type =
+ when (mtbFile.metadata.type) {
+ null -> MvhSubmissionType.INITIAL
+ else -> mtbFile.metadata.type
+ }
+ }
+ }
+
+ /**
+ * @param consentBundle consent resource
+ * @param requestDate date which must be within validation period of provision
+ * @return type of provision, will be [org.hl7.fhir.r4.model.Consent.ConsentProvisionType.NULL] if
+ * none is found.
+ */
+ fun getProvisionTypeByPolicyCode(
+ consentBundle: Bundle,
+ requestDate: Date?,
+ consentDomain: ConsentDomain,
+ ): Consent.ConsentProvisionType {
+ val code: String?
+ val system: String?
+ if (ConsentDomain.BROAD_CONSENT == consentDomain) {
+ code = gIcsConfigProperties.broadConsentPolicyCode
+ system = gIcsConfigProperties.broadConsentPolicySystem
+ } else if (ConsentDomain.MODELLVORHABEN_64E == consentDomain) {
+ code = gIcsConfigProperties.genomeDePolicyCode
+ system = gIcsConfigProperties.genomeDePolicySystem
+ } else {
+ throw NotImplementedException("unknown consent domain " + consentDomain.name)
}
- /**
- * @param consentBundle consent resource
- * @param targetCode policyRule and provision code value
- * @param targetSystem policyRule and provision system value
- * @param requestDate date which must be within validation period of provision
- * @return type of provision, will be [org.hl7.fhir.r4.model.Consent.ConsentProvisionType.NULL] if none is found.
- */
- fun getProvisionTypeByPolicyCode(
- consentBundle: Bundle, targetCode: String?, targetSystem: String?, requestDate: Date?
- ): Consent.ConsentProvisionType {
- val entriesOfInterest = consentBundle.entry.filter { entry ->
- val isConsentResource =
- entry.resource.isResource && entry.resource.resourceType == ResourceType.Consent
- val consentIsActive = (entry.resource as Consent).status == ConsentState.ACTIVE
-
- val provisions = (entry.resource as Consent).provision.provision
-
- val isValidCoding = checkProvisionExist(
- targetCode, targetSystem, provisions
- )
-
- isConsentResource && consentIsActive && isValidCoding && isRequestDateInRange(requestDate, (entry.resource as Consent).provision.period)
- }.map { entry: BundleEntryComponent ->
- val consent = (entry.getResource() as Consent)
- consent.provision.provision.filter { subProvision ->
- isRequestDateInRange(requestDate, subProvision.period)
- // search coding entries of current provision for code and system
- subProvision.code.map { c -> c.coding }.flatten().any { code ->
- targetCode.equals(code.code) && targetSystem.equals(code.system)
- }
- }.map { subProvision ->
- subProvision
+ val provisionTypeByPolicyCode =
+ getProvisionTypeByPolicyCode(consentBundle, code, system, requestDate)
+ return provisionTypeByPolicyCode
+ }
+
+ /**
+ * @param consentBundle consent resource
+ * @param targetCode policyRule and provision code value
+ * @param targetSystem policyRule and provision system value
+ * @param requestDate date which must be within validation period of provision
+ * @return type of provision, will be [org.hl7.fhir.r4.model.Consent.ConsentProvisionType.NULL] if
+ * none is found.
+ */
+ fun getProvisionTypeByPolicyCode(
+ consentBundle: Bundle,
+ targetCode: String?,
+ targetSystem: String?,
+ requestDate: Date?,
+ ): Consent.ConsentProvisionType {
+ val entriesOfInterest =
+ consentBundle.entry
+ .filter { entry ->
+ val isConsentResource =
+ entry.resource.isResource && entry.resource.resourceType == ResourceType.Consent
+ val consentIsActive = (entry.resource as Consent).status == ConsentState.ACTIVE
+
+ val provisions = (entry.resource as Consent).provision.provision
+
+ val isValidCoding = checkProvisionExist(targetCode, targetSystem, provisions)
+
+ isConsentResource &&
+ consentIsActive &&
+ isValidCoding &&
+ isRequestDateInRange(requestDate, (entry.resource as Consent).provision.period)
}
- }.flatten()
+ .map { entry: BundleEntryComponent ->
+ val consent = (entry.getResource() as Consent)
+ consent.provision.provision
+ .filter { subProvision ->
+ isRequestDateInRange(requestDate, subProvision.period)
+ // search coding entries of current provision for code and system
+ subProvision.code
+ .map { c -> c.coding }
+ .flatten()
+ .any { code ->
+ targetCode.equals(code.code) && targetSystem.equals(code.system)
+ }
+ }
+ .map { subProvision -> subProvision }
+ }
+ .flatten()
- if (entriesOfInterest.isNotEmpty()) {
- return entriesOfInterest.first().type
- }
- return Consent.ConsentProvisionType.NULL
+ if (entriesOfInterest.isNotEmpty()) {
+ return entriesOfInterest.first().type
}
-
- fun checkProvisionExist(
- researchAllowedPolicyOid: String?,
- researchAllowedPolicySystem: String?,
- provisions: Collection<ProvisionComponent>
- ): Boolean {
- return provisions.any { provision ->
- provision.code.any { codeableConcept -> codeableConcept.coding.any { it.system == researchAllowedPolicySystem && it.code == researchAllowedPolicyOid } }
+ return Consent.ConsentProvisionType.NULL
+ }
+
+ fun checkProvisionExist(
+ researchAllowedPolicyOid: String?,
+ researchAllowedPolicySystem: String?,
+ provisions: Collection<ProvisionComponent>,
+ ): Boolean {
+ return provisions.any { provision ->
+ provision.code.any { codeableConcept ->
+ codeableConcept.coding.any {
+ it.system == researchAllowedPolicySystem && it.code == researchAllowedPolicyOid
}
+ }
}
+ }
- fun isRequestDateInRange(requestDate: Date?, provPeriod: Period): Boolean {
- val isRequestDateAfterOrEqualStart = provPeriod.start.compareTo(requestDate)
- val isRequestDateBeforeOrEqualEnd = provPeriod.end.compareTo(requestDate)
- return isRequestDateAfterOrEqualStart <= 0 && isRequestDateBeforeOrEqualEnd >= 0
- }
-
+ fun isRequestDateInRange(requestDate: Date?, provPeriod: Period): Boolean {
+ val isRequestDateAfterOrEqualStart = provPeriod.start.compareTo(requestDate)
+ val isRequestDateBeforeOrEqualEnd = provPeriod.end.compareTo(requestDate)
+ return isRequestDateAfterOrEqualStart <= 0 && isRequestDateBeforeOrEqualEnd >= 0
+ }
}
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 07d8a8d..4721f75 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/services/RequestProcessor.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/services/RequestProcessor.kt
@@ -38,14 +38,14 @@ import dev.dnpm.etl.processor.pseudonym.pseudonymizeWith
import dev.pcvolkmer.mv64e.mtb.ConsentProvision
import dev.pcvolkmer.mv64e.mtb.ModelProjectConsentPurpose
import dev.pcvolkmer.mv64e.mtb.Mtb
+import java.time.Instant
+import java.util.*
import org.apache.commons.codec.binary.Base32
import org.apache.commons.codec.digest.DigestUtils
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.context.ApplicationEventPublisher
import org.springframework.stereotype.Service
-import java.time.Instant
-import java.util.*
@Service
class RequestProcessor(
@@ -56,163 +56,178 @@ class RequestProcessor(
private val objectMapper: ObjectMapper,
private val applicationEventPublisher: ApplicationEventPublisher,
private val appConfigProperties: AppConfigProperties,
- private val consentProcessor: ConsentProcessor?
+ private val consentProcessor: ConsentProcessor?,
) {
- private var logger: Logger = LoggerFactory.getLogger("RequestProcessor")
-
- fun processMtbFile(mtbFile: Mtb) {
- processMtbFile(mtbFile, randomRequestId())
+ private var logger: Logger = LoggerFactory.getLogger("RequestProcessor")
+
+ fun processMtbFile(mtbFile: Mtb) {
+ processMtbFile(mtbFile, randomRequestId())
+ }
+
+ fun processMtbFile(mtbFile: Mtb, requestId: RequestId) {
+ val pid = PatientId(extractPatientIdentifier(mtbFile))
+
+ val isConsentOk =
+ consentProcessor != null && consentProcessor.consentGatedCheckAndTryEmbedding(mtbFile) ||
+ consentProcessor == null
+ if (isConsentOk) {
+ if (isGenomDeConsented(mtbFile)) {
+ mtbFile addGenomDeTan pseudonymizeService
+ }
+ mtbFile pseudonymizeWith pseudonymizeService
+ mtbFile anonymizeContentWith pseudonymizeService
+ val request = DnpmV2MtbFileRequest(requestId, transformationService.transform(mtbFile))
+ saveAndSend(request, pid)
+ } else {
+ logger.warn("consent check failed file will not be processed further!")
+ applicationEventPublisher.publishEvent(
+ ResponseEvent(requestId, Instant.now(), RequestStatus.NO_CONSENT)
+ )
}
+ }
-
- fun processMtbFile(mtbFile: Mtb, requestId: RequestId) {
- val pid = PatientId(extractPatientIdentifier(mtbFile))
-
- val isConsentOk =
- consentProcessor != null && consentProcessor.consentGatedCheckAndTryEmbedding(mtbFile) || consentProcessor == null
- if (isConsentOk) {
- if (isGenomDeConsented(mtbFile)) {
- mtbFile addGenomDeTan pseudonymizeService
- }
- mtbFile pseudonymizeWith pseudonymizeService
- mtbFile anonymizeContentWith pseudonymizeService
- val request = DnpmV2MtbFileRequest(requestId, transformationService.transform(mtbFile))
- saveAndSend(request, pid)
- } else {
- logger.warn("consent check failed file will not be processed further!")
- applicationEventPublisher.publishEvent(
- ResponseEvent(
- requestId, Instant.now(), RequestStatus.NO_CONSENT
- )
- )
- }
- }
-
- private fun isGenomDeConsented(mtbFile: Mtb): Boolean {
- val isModelProjectConsented = mtbFile.metadata?.modelProjectConsent?.provisions?.any { p ->
- p.purpose == ModelProjectConsentPurpose.SEQUENCING && p.type == ConsentProvision.PERMIT
+ private fun isGenomDeConsented(mtbFile: Mtb): Boolean {
+ val isModelProjectConsented =
+ mtbFile.metadata?.modelProjectConsent?.provisions?.any { p ->
+ p.purpose == ModelProjectConsentPurpose.SEQUENCING && p.type == ConsentProvision.PERMIT
} == true
- return isModelProjectConsented
- }
-
- private fun <T> saveAndSend(request: MtbFileRequest<T>, pid: PatientId) {
- requestService.save(
- Request(
- request.requestId,
- request.patientPseudonym(),
- pid,
- fingerprint(request),
- RequestType.MTB_FILE,
- RequestStatus.UNKNOWN
- )
+ return isModelProjectConsented
+ }
+
+ private fun <T> saveAndSend(request: MtbFileRequest<T>, pid: PatientId) {
+ requestService.save(
+ Request(
+ request.requestId,
+ request.patientPseudonym(),
+ pid,
+ fingerprint(request),
+ RequestType.MTB_FILE,
+ RequestStatus.UNKNOWN,
)
+ )
- 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()
- }
- )
- )
+ if (appConfigProperties.duplicationDetection && isDuplication(request)) {
+ applicationEventPublisher.publishEvent(
+ ResponseEvent(request.requestId, Instant.now(), RequestStatus.DUPLICATION)
+ )
+ return
}
- private fun <T> isDuplication(pseudonymizedMtbFileRequest: MtbFileRequest<T>): Boolean {
- val patientPseudonym = when (pseudonymizedMtbFileRequest) {
- is DnpmV2MtbFileRequest -> PatientPseudonym(pseudonymizedMtbFileRequest.content.patient.id)
- }
-
- val lastMtbFileRequestForPatient =
- requestService.lastMtbFileRequestForPatientPseudonym(patientPseudonym)
- val isLastRequestDeletion =
- requestService.isLastRequestWithKnownStatusDeletion(patientPseudonym)
-
- return null != lastMtbFileRequestForPatient && !isLastRequestDeletion && lastMtbFileRequestForPatient.fingerprint == fingerprint(
- pseudonymizedMtbFileRequest
+ 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()
+ },
)
- }
-
- 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!")
- }
- }
-
- requestService.save(
- Request(
- requestId,
- patientPseudonym,
- patientId,
- fingerprint(patientPseudonym.value),
- RequestType.DELETE,
- requestStatus
- )
- )
-
- 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,
- report = Report("Fehler bei der Pseudonymisierung")
- )
- )
+ )
+ }
+
+ private fun <T> isDuplication(pseudonymizedMtbFileRequest: MtbFileRequest<T>): Boolean {
+ val patientPseudonym =
+ when (pseudonymizedMtbFileRequest) {
+ is DnpmV2MtbFileRequest ->
+ PatientPseudonym(pseudonymizedMtbFileRequest.content.patient.id)
}
- }
- private fun <T> fingerprint(request: MtbFileRequest<T>): Fingerprint {
- return when (request) {
- is DnpmV2MtbFileRequest -> fingerprint(objectMapper.writeValueAsString(request.content))
- }
+ 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!"
+ )
+ }
+ }
+
+ requestService.save(
+ Request(
+ requestId,
+ patientPseudonym,
+ patientId,
+ fingerprint(patientPseudonym.value),
+ RequestType.DELETE,
+ requestStatus,
+ )
+ )
+
+ 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,
+ report = Report("Fehler bei der Pseudonymisierung"),
+ )
+ )
}
+ }
- private fun fingerprint(s: String): Fingerprint {
- return Fingerprint(
- Base32().encodeAsString(DigestUtils.sha256(s)).replace("=", "").lowercase()
- )
+ 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()
+ )
+ }
}
private fun extractPatientIdentifier(mtbFile: Mtb): String = mtbFile.patient.id
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 757b353..e7cb95f 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/services/RequestService.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/services/RequestService.kt
@@ -22,55 +22,63 @@ package dev.dnpm.etl.processor.services
import dev.dnpm.etl.processor.PatientPseudonym
import dev.dnpm.etl.processor.RequestId
import dev.dnpm.etl.processor.monitoring.*
+import java.util.*
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.stereotype.Service
-import java.util.*
@Service
-class RequestService(
- private val requestRepository: RequestRepository
-) {
+class RequestService(private val requestRepository: RequestRepository) {
- fun save(request: Request) = requestRepository.save(request)
+ fun save(request: Request) = requestRepository.save(request)
- fun findAll(): Iterable<Request> = requestRepository.findAll()
+ fun findAll(): Iterable<Request> = requestRepository.findAll()
- fun findAll(pageable: Pageable): Page<Request> = requestRepository.findAll(pageable)
+ fun findAll(pageable: Pageable): Page<Request> = requestRepository.findAll(pageable)
- fun findByUuid(uuid: RequestId): Optional<Request> =
- requestRepository.findByUuidEquals(uuid)
+ fun findByUuid(uuid: RequestId): Optional<Request> = requestRepository.findByUuidEquals(uuid)
- fun findRequestByPatientId(patientPseudonym: PatientPseudonym, pageable: Pageable): Page<Request> = requestRepository.findRequestByPatientPseudonym(patientPseudonym, pageable)
+ fun findRequestByPatientId(
+ patientPseudonym: PatientPseudonym,
+ pageable: Pageable,
+ ): Page<Request> = requestRepository.findRequestByPatientPseudonym(patientPseudonym, pageable)
- fun allRequestsByPatientPseudonym(patientPseudonym: PatientPseudonym) = requestRepository
- .findAllByPatientPseudonymOrderByProcessedAtDesc(patientPseudonym)
+ fun allRequestsByPatientPseudonym(patientPseudonym: PatientPseudonym) =
+ requestRepository.findAllByPatientPseudonymOrderByProcessedAtDesc(patientPseudonym)
- fun lastMtbFileRequestForPatientPseudonym(patientPseudonym: PatientPseudonym) =
- Companion.lastMtbFileRequestForPatientPseudonym(allRequestsByPatientPseudonym(patientPseudonym))
+ fun lastMtbFileRequestForPatientPseudonym(patientPseudonym: PatientPseudonym) =
+ Companion.lastMtbFileRequestForPatientPseudonym(
+ allRequestsByPatientPseudonym(patientPseudonym)
+ )
- fun isLastRequestWithKnownStatusDeletion(patientPseudonym: PatientPseudonym) =
- Companion.isLastRequestWithKnownStatusDeletion(allRequestsByPatientPseudonym(patientPseudonym))
+ fun isLastRequestWithKnownStatusDeletion(patientPseudonym: PatientPseudonym) =
+ Companion.isLastRequestWithKnownStatusDeletion(
+ allRequestsByPatientPseudonym(patientPseudonym)
+ )
- fun countStates(): Iterable<CountedState> = requestRepository.countStates()
+ fun countStates(): Iterable<CountedState> = requestRepository.countStates()
- fun countDeleteStates(): Iterable<CountedState> = requestRepository.countDeleteStates()
+ fun countDeleteStates(): Iterable<CountedState> = requestRepository.countDeleteStates()
- fun findPatientUniqueStates(): List<CountedState> = requestRepository.findPatientUniqueStates()
+ fun findPatientUniqueStates(): List<CountedState> = requestRepository.findPatientUniqueStates()
- fun findPatientUniqueDeleteStates(): List<CountedState> = requestRepository.findPatientUniqueDeleteStates()
+ fun findPatientUniqueDeleteStates(): List<CountedState> =
+ requestRepository.findPatientUniqueDeleteStates()
- companion object {
+ companion object {
- fun lastMtbFileRequestForPatientPseudonym(allRequests: List<Request>) = allRequests
+ fun lastMtbFileRequestForPatientPseudonym(allRequests: List<Request>) =
+ allRequests
.filter { it.type == RequestType.MTB_FILE }
.sortedByDescending { it.processedAt }
- .firstOrNull { it.status == RequestStatus.SUCCESS || it.status == RequestStatus.WARNING }
+ .firstOrNull {
+ it.status == RequestStatus.SUCCESS || it.status == RequestStatus.WARNING
+ }
- fun isLastRequestWithKnownStatusDeletion(allRequests: List<Request>) = allRequests
+ fun isLastRequestWithKnownStatusDeletion(allRequests: List<Request>) =
+ allRequests
.filter { it.status != RequestStatus.UNKNOWN }
- .maxByOrNull { it.processedAt }?.type == RequestType.DELETE
-
- }
-
-} \ No newline at end of file
+ .maxByOrNull { it.processedAt }
+ ?.type == RequestType.DELETE
+ }
+}
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 fb82647..190cefe 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/services/ResponseProcessor.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/services/ResponseProcessor.kt
@@ -22,79 +22,76 @@ 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 java.time.Instant
+import java.util.*
import org.slf4j.LoggerFactory
import org.springframework.context.event.EventListener
import org.springframework.stereotype.Service
import reactor.core.publisher.Sinks
-import java.time.Instant
-import java.util.*
@Service
class ResponseProcessor(
private val requestService: RequestService,
- private val statisticsUpdateProducer: Sinks.Many<Any>
+ private val statisticsUpdateProducer: Sinks.Many<Any>,
) {
- private val logger = LoggerFactory.getLogger(ResponseProcessor::class.java)
+ private val logger = LoggerFactory.getLogger(ResponseProcessor::class.java)
- @EventListener(classes = [ResponseEvent::class])
- fun handleResponseEvent(event: ResponseEvent) {
- requestService.findByUuid(event.requestUuid).ifPresentOrElse({
- it.processedAt = event.timestamp
- it.status = event.status
+ @EventListener(classes = [ResponseEvent::class])
+ fun handleResponseEvent(event: ResponseEvent) {
+ requestService
+ .findByUuid(event.requestUuid)
+ .ifPresentOrElse(
+ {
+ it.processedAt = event.timestamp
+ it.status = event.status
- when (event.status) {
+ when (event.status) {
RequestStatus.SUCCESS -> {
- it.report = Report(
- "Keine Probleme erkannt",
- )
+ it.report =
+ Report(
+ "Keine Probleme erkannt",
+ )
}
RequestStatus.WARNING -> {
- it.report = Report(
- "Warnungen über mangelhafte Daten",
- event.body.orElse("")
- )
+ it.report = Report("Warnungen über mangelhafte Daten", event.body.orElse(""))
}
RequestStatus.ERROR -> {
- it.report = Report(
- "Fehler bei der Datenübertragung oder Inhalt nicht verarbeitbar",
- event.body.orElse("")
- )
+ it.report =
+ Report(
+ "Fehler bei der Datenübertragung oder Inhalt nicht verarbeitbar",
+ event.body.orElse(""),
+ )
}
RequestStatus.DUPLICATION -> {
- it.report = Report(
- "Duplikat erkannt"
- )
+ it.report = Report("Duplikat erkannt")
}
RequestStatus.NO_CONSENT -> {
- it.report = Report(
- "Einwilligung Status fehlt, widerrufen oder ungeklärt."
- )
+ it.report = Report("Einwilligung Status fehlt, widerrufen oder ungeklärt.")
}
else -> {
- logger.error("Cannot process response: Unknown response!")
- return@ifPresentOrElse
+ logger.error("Cannot process response: Unknown response!")
+ return@ifPresentOrElse
}
- }
-
- requestService.save(it)
+ }
- statisticsUpdateProducer.emitNext("", Sinks.EmitFailureHandler.FAIL_FAST)
- }, {
- logger.error("Response for unknown request '${event.requestUuid}'!")
- })
- }
+ requestService.save(it)
+ statisticsUpdateProducer.emitNext("", Sinks.EmitFailureHandler.FAIL_FAST)
+ },
+ { logger.error("Response for unknown request '${event.requestUuid}'!") },
+ )
+ }
}
data class ResponseEvent(
val requestUuid: RequestId,
val timestamp: Instant,
val status: RequestStatus,
- val body: Optional<String> = Optional.empty()
-) \ No newline at end of file
+ val body: Optional<String> = Optional.empty(),
+)
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 8f1081e..df8ac3d 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/services/TransformationService.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/services/TransformationService.kt
@@ -24,8 +24,10 @@ import com.jayway.jsonpath.JsonPath
import com.jayway.jsonpath.PathNotFoundException
import dev.pcvolkmer.mv64e.mtb.Mtb
-class TransformationService(private val objectMapper: ObjectMapper, private val transformations: List<Transformation>) {
-
+class TransformationService(
+ private val objectMapper: ObjectMapper,
+ private val transformations: List<Transformation>,
+) {
fun transform(mtbFile: Mtb): Mtb {
val json = transform(objectMapper.writeValueAsString(mtbFile))
return objectMapper.readValue(json, Mtb::class.java)
@@ -41,12 +43,24 @@ class TransformationService(private val objectMapper: ObjectMapper, private val
val before = transformation.path.substringBeforeLast(".")
val last = transformation.path.substringAfterLast(".")
- val existingValue = if (transformation.existingValue is Number) transformation.existingValue else transformation.existingValue.toString()
- val newValue = if (transformation.newValue is Number) transformation.newValue else transformation.newValue.toString()
-
- jsonPath.set("$.$before.[?]$last", newValue, {
- it.item(HashMap::class.java)[last] == existingValue
- })
+ val existingValue =
+ if (transformation.existingValue is Number) {
+ transformation.existingValue
+ } else {
+ transformation.existingValue.toString()
+ }
+ val newValue =
+ if (transformation.newValue is Number) {
+ transformation.newValue
+ } else {
+ transformation.newValue.toString()
+ }
+
+ jsonPath.set(
+ "$.$before.[?]$last",
+ newValue,
+ { it.item(HashMap::class.java)[last] == existingValue },
+ )
} catch (e: PathNotFoundException) {
// Ignore
}
@@ -57,35 +71,30 @@ class TransformationService(private val objectMapper: ObjectMapper, private val
return json
}
- fun getTransformations(): List<Transformation> {
- return this.transformations
- }
-
+ fun getTransformations(): List<Transformation> = this.transformations
}
-class Transformation private constructor(val path: String) {
-
- lateinit var existingValue: Any
- private set
- lateinit var newValue: Any
- private set
-
- infix fun from(value: Any): Transformation {
- this.existingValue = value
- return this
- }
+class Transformation
+ private constructor(
+ val path: String,
+ ) {
+ lateinit var existingValue: Any
+ private set
- infix fun to(value: Any): Transformation {
- this.newValue = value
- return this
- }
+ lateinit var newValue: Any
+ private set
- companion object {
+ infix fun from(value: Any): Transformation {
+ this.existingValue = value
+ return this
+ }
- fun of(path: String): Transformation {
- return Transformation(path)
+ infix fun to(value: Any): Transformation {
+ this.newValue = value
+ return this
}
+ companion object {
+ fun of(path: String): Transformation = Transformation(path)
+ }
}
-
-}
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 fd6a9b4..e70f1e7 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
@@ -26,56 +26,64 @@ 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
+import java.time.Instant
+import java.util.*
import org.apache.kafka.clients.consumer.ConsumerRecord
import org.slf4j.LoggerFactory
import org.springframework.context.ApplicationEventPublisher
import org.springframework.kafka.listener.MessageListener
-import java.time.Instant
-import java.util.*
class KafkaResponseProcessor(
private val eventPublisher: ApplicationEventPublisher,
- private val objectMapper: ObjectMapper
+ private val objectMapper: ObjectMapper,
) : MessageListener<String, String> {
- private val logger = LoggerFactory.getLogger(KafkaResponseProcessor::class.java)
+ private val logger = LoggerFactory.getLogger(KafkaResponseProcessor::class.java)
- override fun onMessage(data: ConsumerRecord<String, String>) {
- try {
- Optional.of(objectMapper.readValue(data.value(), ResponseBody::class.java))
+ override fun onMessage(data: ConsumerRecord<String, String>) {
+ try {
+ Optional.of(objectMapper.readValue(data.value(), ResponseBody::class.java))
} catch (e: Exception) {
- logger.error("Cannot process Kafka response", e)
- Optional.empty()
- }.ifPresentOrElse({ responseBody ->
- val event = ResponseEvent(
- RequestId(responseBody.requestId),
- Instant.ofEpochMilli(data.timestamp()),
- responseBody.statusCode.asRequestStatus(),
- when (responseBody.statusCode.asRequestStatus()) {
- RequestStatus.SUCCESS -> {
- Optional.empty()
- }
-
- RequestStatus.WARNING, RequestStatus.ERROR -> {
- Optional.of(objectMapper.writeValueAsString(responseBody.statusBody))
- }
+ logger.error("Cannot process Kafka response", e)
+ Optional.empty()
+ }
+ .ifPresentOrElse(
+ { responseBody ->
+ val event =
+ ResponseEvent(
+ RequestId(responseBody.requestId),
+ Instant.ofEpochMilli(data.timestamp()),
+ responseBody.statusCode.asRequestStatus(),
+ when (responseBody.statusCode.asRequestStatus()) {
+ RequestStatus.SUCCESS -> {
+ Optional.empty()
+ }
- else -> {
- logger.error("Kafka response: Unknown response code '{}'!", responseBody.statusCode)
- Optional.empty()
- }
- }
- )
- eventPublisher.publishEvent(event)
- }, {
- logger.error("No requestId in Kafka response")
- })
- }
+ RequestStatus.WARNING,
+ RequestStatus.ERROR -> {
+ Optional.of(objectMapper.writeValueAsString(responseBody.statusBody))
+ }
- data class ResponseBody(
- @param:JsonProperty("request_id") @param:JsonAlias("requestId") val requestId: String,
- @param:JsonProperty("status_code") @param:JsonAlias("statusCode") val statusCode: Int,
- @param:JsonProperty("status_body") @param:JsonAlias("statusBody") val statusBody: Map<String, Any>
- )
+ else -> {
+ logger.error(
+ "Kafka response: Unknown response code '{}'!",
+ responseBody.statusCode,
+ )
+ Optional.empty()
+ }
+ },
+ )
+ eventPublisher.publishEvent(event)
+ },
+ { logger.error("No requestId in Kafka response") },
+ )
+ }
-} \ No newline at end of file
+ data class ResponseBody(
+ @param:JsonProperty("request_id") @param:JsonAlias("requestId") val requestId: String,
+ @param:JsonProperty("status_code") @param:JsonAlias("statusCode") val statusCode: Int,
+ @param:JsonProperty("status_body")
+ @param:JsonAlias("statusBody")
+ val statusBody: Map<String, Any>,
+ )
+}
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/types.kt b/src/main/kotlin/dev/dnpm/etl/processor/types.kt
index 90fa7cb..c7aa110 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/types.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/types.kt
@@ -19,33 +19,30 @@
package dev.dnpm.etl.processor
-import org.springframework.http.MediaType
import java.util.*
+import org.springframework.http.MediaType
class Fingerprint(val value: String) {
- override fun hashCode() = value.hashCode()
+ override fun hashCode() = value.hashCode()
- override fun equals(other: Any?) = other is Fingerprint && other.value == value
+ override fun equals(other: Any?) = other is Fingerprint && other.value == value
- companion object {
- fun empty() = Fingerprint("")
- }
+ companion object {
+ fun empty() = Fingerprint("")
+ }
}
@JvmInline
value class RequestId(val value: String) {
- fun isBlank() = value.isBlank()
-
+ fun isBlank() = value.isBlank()
}
fun randomRequestId() = RequestId(UUID.randomUUID().toString())
-@JvmInline
-value class PatientId(val value: String)
+@JvmInline value class PatientId(val value: String)
-@JvmInline
-value class PatientPseudonym(val value: String)
+@JvmInline value class PatientPseudonym(val value: String)
fun emptyPatientPseudonym() = PatientPseudonym("")
@@ -55,9 +52,9 @@ fun emptyPatientPseudonym() = PatientPseudonym("")
* @since 0.11.0
*/
object CustomMediaType {
- val APPLICATION_VND_DNPM_V2_MTB_JSON = MediaType("application", "vnd.dnpm.v2.mtb+json")
- const val APPLICATION_VND_DNPM_V2_MTB_JSON_VALUE = "application/vnd.dnpm.v2.mtb+json"
+ val APPLICATION_VND_DNPM_V2_MTB_JSON = MediaType("application", "vnd.dnpm.v2.mtb+json")
+ const val APPLICATION_VND_DNPM_V2_MTB_JSON_VALUE = "application/vnd.dnpm.v2.mtb+json"
- val APPLICATION_VND_DNPM_V2_RD_JSON = MediaType("application", "vnd.dnpm.v2.rd+json")
- const val APPLICATION_VND_DNPM_V2_RD_JSON_VALUE = "application/vnd.dnpm.v2.rd+json"
+ val APPLICATION_VND_DNPM_V2_RD_JSON = MediaType("application", "vnd.dnpm.v2.rd+json")
+ const val APPLICATION_VND_DNPM_V2_RD_JSON_VALUE = "application/vnd.dnpm.v2.rd+json"
}
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/web/ApplicationControllerAdvice.kt b/src/main/kotlin/dev/dnpm/etl/processor/web/ApplicationControllerAdvice.kt
index bdca57e..0dec30e 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/web/ApplicationControllerAdvice.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/web/ApplicationControllerAdvice.kt
@@ -27,11 +27,7 @@ import org.springframework.web.bind.annotation.ResponseStatus
@ControllerAdvice
class ApplicationControllerAdvice {
-
@ExceptionHandler(NotFoundException::class)
@ResponseStatus(HttpStatus.NOT_FOUND)
- fun handleNotFoundException(e: NotFoundException): String {
- return "errors/404"
- }
-
-} \ No newline at end of file
+ fun handleNotFoundException(e: NotFoundException): String = "errors/404"
+}
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/web/ConfigController.kt b/src/main/kotlin/dev/dnpm/etl/processor/web/ConfigController.kt
index 44571d4..b77bdf9 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/web/ConfigController.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/web/ConfigController.kt
@@ -23,11 +23,11 @@ import dev.dnpm.etl.processor.monitoring.*
import dev.dnpm.etl.processor.output.MtbFileSender
import dev.dnpm.etl.processor.pseudonym.Generator
import dev.dnpm.etl.processor.security.Role
-import dev.dnpm.etl.processor.security.UserRole
import dev.dnpm.etl.processor.security.Token
import dev.dnpm.etl.processor.security.TokenService
-import dev.dnpm.etl.processor.services.TransformationService
+import dev.dnpm.etl.processor.security.UserRole
import dev.dnpm.etl.processor.security.UserRoleService
+import dev.dnpm.etl.processor.services.TransformationService
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.http.MediaType
import org.springframework.http.codec.ServerSentEvent
@@ -47,175 +47,193 @@ class ConfigController(
private val mtbFileSender: MtbFileSender,
private val connectionCheckServices: List<ConnectionCheckService>,
private val tokenService: TokenService?,
- private val userRoleService: UserRoleService?
+ private val userRoleService: UserRoleService?,
) {
- @GetMapping
- fun index(model: Model): String {
- val outputConnectionAvailable =
- connectionCheckServices.filterIsInstance<OutputConnectionCheckService>().firstOrNull()?.connectionAvailable()
-
- val gPasConnectionAvailable =
- connectionCheckServices.filterIsInstance<GPasConnectionCheckService>().firstOrNull()?.connectionAvailable()
-
- val gIcsConnectionAvailable =
- connectionCheckServices.filterIsInstance<GIcsConnectionCheckService>().firstOrNull()?.connectionAvailable()
-
- model.addAttribute("pseudonymGenerator", pseudonymGenerator.javaClass.simpleName)
- model.addAttribute("mtbFileSender", mtbFileSender.javaClass.simpleName)
- model.addAttribute("mtbFileEndpoint", mtbFileSender.endpoint())
- model.addAttribute("outputConnectionAvailable", outputConnectionAvailable)
- model.addAttribute("gPasConnectionAvailable", gPasConnectionAvailable)
- model.addAttribute("gIcsConnectionAvailable", gIcsConnectionAvailable)
- model.addAttribute("tokensEnabled", tokenService != null)
- if (tokenService != null) {
- model.addAttribute("tokens", tokenService.findAll())
- } else {
- model.addAttribute("tokens", emptyList<Token>())
- }
- model.addAttribute("transformations", transformationService.getTransformations())
- if (userRoleService != null) {
- model.addAttribute("userRolesEnabled", true)
- model.addAttribute("userRoles", userRoleService.findAll())
- } else {
- model.addAttribute("userRolesEnabled", false)
- model.addAttribute("userRoles", emptyList<UserRole>())
- }
- return "configs"
+ @GetMapping
+ fun index(model: Model): String {
+ val outputConnectionAvailable =
+ connectionCheckServices
+ .filterIsInstance<OutputConnectionCheckService>()
+ .firstOrNull()
+ ?.connectionAvailable()
+
+ val gPasConnectionAvailable =
+ connectionCheckServices
+ .filterIsInstance<GPasConnectionCheckService>()
+ .firstOrNull()
+ ?.connectionAvailable()
+
+ val gIcsConnectionAvailable =
+ connectionCheckServices
+ .filterIsInstance<GIcsConnectionCheckService>()
+ .firstOrNull()
+ ?.connectionAvailable()
+
+ model.addAttribute("pseudonymGenerator", pseudonymGenerator.javaClass.simpleName)
+ model.addAttribute("mtbFileSender", mtbFileSender.javaClass.simpleName)
+ model.addAttribute("mtbFileEndpoint", mtbFileSender.endpoint())
+ model.addAttribute("outputConnectionAvailable", outputConnectionAvailable)
+ model.addAttribute("gPasConnectionAvailable", gPasConnectionAvailable)
+ model.addAttribute("gIcsConnectionAvailable", gIcsConnectionAvailable)
+ model.addAttribute("tokensEnabled", tokenService != null)
+ if (tokenService != null) {
+ model.addAttribute("tokens", tokenService.findAll())
+ } else {
+ model.addAttribute("tokens", emptyList<Token>())
}
-
- @GetMapping(params = ["outputConnectionAvailable"])
- fun outputConnectionAvailable(model: Model): String {
- val outputConnectionAvailable =
- connectionCheckServices.filterIsInstance<OutputConnectionCheckService>().first().connectionAvailable()
-
- model.addAttribute("mtbFileSender", mtbFileSender.javaClass.simpleName)
- model.addAttribute("mtbFileEndpoint", mtbFileSender.endpoint())
- model.addAttribute("outputConnectionAvailable", outputConnectionAvailable)
- if (tokenService != null) {
- model.addAttribute("tokensEnabled", true)
- model.addAttribute("tokens", tokenService.findAll())
- } else {
- model.addAttribute("tokens", listOf<Token>())
- }
-
- return "configs/outputConnectionAvailable"
+ model.addAttribute("transformations", transformationService.getTransformations())
+ if (userRoleService != null) {
+ model.addAttribute("userRolesEnabled", true)
+ model.addAttribute("userRoles", userRoleService.findAll())
+ } else {
+ model.addAttribute("userRolesEnabled", false)
+ model.addAttribute("userRoles", emptyList<UserRole>())
}
-
- @GetMapping(params = ["gPasConnectionAvailable"])
- fun gPasConnectionAvailable(model: Model): String {
- val gPasConnectionAvailable =
- connectionCheckServices.filterIsInstance<GPasConnectionCheckService>().firstOrNull()?.connectionAvailable()
-
- model.addAttribute("mtbFileSender", mtbFileSender.javaClass.simpleName)
- model.addAttribute("mtbFileEndpoint", mtbFileSender.endpoint())
- model.addAttribute("gPasConnectionAvailable", gPasConnectionAvailable)
- if (tokenService != null) {
- model.addAttribute("tokensEnabled", true)
- model.addAttribute("tokens", tokenService.findAll())
- } else {
- model.addAttribute("tokens", listOf<Token>())
- }
-
- return "configs/gPasConnectionAvailable"
+ return "configs"
+ }
+
+ @GetMapping(params = ["outputConnectionAvailable"])
+ fun outputConnectionAvailable(model: Model): String {
+ val outputConnectionAvailable =
+ connectionCheckServices
+ .filterIsInstance<OutputConnectionCheckService>()
+ .first()
+ .connectionAvailable()
+
+ model.addAttribute("mtbFileSender", mtbFileSender.javaClass.simpleName)
+ model.addAttribute("mtbFileEndpoint", mtbFileSender.endpoint())
+ model.addAttribute("outputConnectionAvailable", outputConnectionAvailable)
+ if (tokenService != null) {
+ model.addAttribute("tokensEnabled", true)
+ model.addAttribute("tokens", tokenService.findAll())
+ } else {
+ model.addAttribute("tokens", listOf<Token>())
}
- @GetMapping(params = ["gIcsConnectionAvailable"])
- fun gIcsConnectionAvailable(model: Model): String {
- val gIcsConnectionAvailable =
- connectionCheckServices.filterIsInstance<GIcsConnectionCheckService>().firstOrNull()?.connectionAvailable()
-
- model.addAttribute("mtbFileSender", mtbFileSender.javaClass.simpleName)
- model.addAttribute("mtbFileEndpoint", mtbFileSender.endpoint())
- model.addAttribute("gIcsConnectionAvailable", gIcsConnectionAvailable)
- if (tokenService != null) {
- model.addAttribute("tokensEnabled", true)
- model.addAttribute("tokens", tokenService.findAll())
- } else {
- model.addAttribute("tokens", listOf<Token>())
- }
-
- return "configs/gIcsConnectionAvailable"
+ return "configs/outputConnectionAvailable"
+ }
+
+ @GetMapping(params = ["gPasConnectionAvailable"])
+ fun gPasConnectionAvailable(model: Model): String {
+ val gPasConnectionAvailable =
+ connectionCheckServices
+ .filterIsInstance<GPasConnectionCheckService>()
+ .firstOrNull()
+ ?.connectionAvailable()
+
+ model.addAttribute("mtbFileSender", mtbFileSender.javaClass.simpleName)
+ model.addAttribute("mtbFileEndpoint", mtbFileSender.endpoint())
+ model.addAttribute("gPasConnectionAvailable", gPasConnectionAvailable)
+ if (tokenService != null) {
+ model.addAttribute("tokensEnabled", true)
+ model.addAttribute("tokens", tokenService.findAll())
+ } else {
+ model.addAttribute("tokens", listOf<Token>())
}
- @PostMapping(path = ["tokens"])
- fun addToken(@ModelAttribute("name") name: String, model: Model): String {
- if (tokenService == null) {
- model.addAttribute("tokensEnabled", false)
- model.addAttribute("success", false)
- } else {
- model.addAttribute("tokensEnabled", true)
- val result = tokenService.addToken(name)
- result.onSuccess {
- model.addAttribute("newTokenValue", it)
- model.addAttribute("success", true)
- }
- result.onFailure {
- model.addAttribute("success", false)
- }
- model.addAttribute("tokens", tokenService.findAll())
- }
-
- return "configs/tokens"
+ return "configs/gPasConnectionAvailable"
+ }
+
+ @GetMapping(params = ["gIcsConnectionAvailable"])
+ fun gIcsConnectionAvailable(model: Model): String {
+ val gIcsConnectionAvailable =
+ connectionCheckServices
+ .filterIsInstance<GIcsConnectionCheckService>()
+ .firstOrNull()
+ ?.connectionAvailable()
+
+ model.addAttribute("mtbFileSender", mtbFileSender.javaClass.simpleName)
+ model.addAttribute("mtbFileEndpoint", mtbFileSender.endpoint())
+ model.addAttribute("gIcsConnectionAvailable", gIcsConnectionAvailable)
+ if (tokenService != null) {
+ model.addAttribute("tokensEnabled", true)
+ model.addAttribute("tokens", tokenService.findAll())
+ } else {
+ model.addAttribute("tokens", listOf<Token>())
}
- @DeleteMapping(path = ["tokens/{id}"])
- fun deleteToken(@PathVariable id: Long, model: Model): String {
- if (tokenService != null) {
- tokenService.deleteToken(id)
-
- model.addAttribute("tokensEnabled", true)
- model.addAttribute("tokens", tokenService.findAll())
- } else {
- model.addAttribute("tokensEnabled", false)
- model.addAttribute("tokens", listOf<Token>())
- }
- return "configs/tokens"
+ return "configs/gIcsConnectionAvailable"
+ }
+
+ @PostMapping(path = ["tokens"])
+ fun addToken(@ModelAttribute("name") name: String, model: Model): String {
+ if (tokenService == null) {
+ model.addAttribute("tokensEnabled", false)
+ model.addAttribute("success", false)
+ } else {
+ model.addAttribute("tokensEnabled", true)
+ val result = tokenService.addToken(name)
+ result.onSuccess {
+ model.addAttribute("newTokenValue", it)
+ model.addAttribute("success", true)
+ }
+ result.onFailure { model.addAttribute("success", false) }
+ model.addAttribute("tokens", tokenService.findAll())
}
- @DeleteMapping(path = ["userroles/{id}"])
- fun deleteUserRole(@PathVariable id: Long, model: Model): String {
- if (userRoleService != null) {
- userRoleService.deleteUserRole(id)
-
- model.addAttribute("userRolesEnabled", true)
- model.addAttribute("userRoles", userRoleService.findAll())
- } else {
- model.addAttribute("userRolesEnabled", false)
- model.addAttribute("userRoles", emptyList<UserRole>())
- }
- return "configs/userroles"
- }
+ return "configs/tokens"
+ }
- @PutMapping(path = ["userroles/{id}"])
- fun updateUserRole(@PathVariable id: Long, @ModelAttribute("role") role: Role, model: Model): String {
- if (userRoleService != null) {
- userRoleService.updateUserRole(id, role)
-
- model.addAttribute("userRolesEnabled", true)
- model.addAttribute("userRoles", userRoleService.findAll())
- } else {
- model.addAttribute("userRolesEnabled", false)
- model.addAttribute("userRoles", emptyList<UserRole>())
- }
- return "configs/userroles"
- }
+ @DeleteMapping(path = ["tokens/{id}"])
+ fun deleteToken(@PathVariable id: Long, model: Model): String {
+ if (tokenService != null) {
+ tokenService.deleteToken(id)
- @GetMapping(path = ["events"], produces = [MediaType.TEXT_EVENT_STREAM_VALUE])
- @ResponseBody
- fun events(): Flux<ServerSentEvent<Any>> {
- return connectionCheckUpdateProducer.asFlux().map {
- val event = when (it) {
- is ConnectionCheckResult.KafkaConnectionCheckResult -> "output-connection-check"
- is ConnectionCheckResult.RestConnectionCheckResult -> "output-connection-check"
- is ConnectionCheckResult.GPasConnectionCheckResult -> "gpas-connection-check"
- is ConnectionCheckResult.GIcsConnectionCheckResult -> "gics-connection-check"
- }
-
- ServerSentEvent.builder<Any>()
- .event(event).id("none").data(it)
- .build()
- }
+ model.addAttribute("tokensEnabled", true)
+ model.addAttribute("tokens", tokenService.findAll())
+ } else {
+ model.addAttribute("tokensEnabled", false)
+ model.addAttribute("tokens", listOf<Token>())
}
-
-} \ No newline at end of file
+ return "configs/tokens"
+ }
+
+ @DeleteMapping(path = ["userroles/{id}"])
+ fun deleteUserRole(@PathVariable id: Long, model: Model): String {
+ if (userRoleService != null) {
+ userRoleService.deleteUserRole(id)
+
+ model.addAttribute("userRolesEnabled", true)
+ model.addAttribute("userRoles", userRoleService.findAll())
+ } else {
+ model.addAttribute("userRolesEnabled", false)
+ model.addAttribute("userRoles", emptyList<UserRole>())
+ }
+ return "configs/userroles"
+ }
+
+ @PutMapping(path = ["userroles/{id}"])
+ fun updateUserRole(
+ @PathVariable id: Long,
+ @ModelAttribute("role") role: Role,
+ model: Model,
+ ): String {
+ if (userRoleService != null) {
+ userRoleService.updateUserRole(id, role)
+
+ model.addAttribute("userRolesEnabled", true)
+ model.addAttribute("userRoles", userRoleService.findAll())
+ } else {
+ model.addAttribute("userRolesEnabled", false)
+ model.addAttribute("userRoles", emptyList<UserRole>())
+ }
+ return "configs/userroles"
+ }
+
+ @GetMapping(path = ["events"], produces = [MediaType.TEXT_EVENT_STREAM_VALUE])
+ @ResponseBody
+ fun events(): Flux<ServerSentEvent<Any>> {
+ return connectionCheckUpdateProducer.asFlux().map {
+ val event =
+ when (it) {
+ is ConnectionCheckResult.KafkaConnectionCheckResult -> "output-connection-check"
+ is ConnectionCheckResult.RestConnectionCheckResult -> "output-connection-check"
+ is ConnectionCheckResult.GPasConnectionCheckResult -> "gpas-connection-check"
+ is ConnectionCheckResult.GIcsConnectionCheckResult -> "gics-connection-check"
+ }
+
+ ServerSentEvent.builder<Any>().event(event).id("none").data(it).build()
+ }
+ }
+}
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 54920b1..082cd20 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/web/HomeController.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/web/HomeController.kt
@@ -37,13 +37,13 @@ import org.springframework.web.bind.annotation.RequestMapping
@RequestMapping(path = ["/"])
class HomeController(
private val requestService: RequestService,
- private val reportService: ReportService
+ private val reportService: ReportService,
) {
-
@GetMapping
fun index(
- @PageableDefault(page = 0, size = 20, sort = ["processedAt"], direction = Sort.Direction.DESC) pageable: Pageable,
- model: Model
+ @PageableDefault(page = 0, size = 20, sort = ["processedAt"], direction = Sort.Direction.DESC)
+ pageable: Pageable,
+ model: Model,
): String {
val requests = requestService.findAll(pageable)
model.addAttribute("requests", requests)
@@ -54,8 +54,9 @@ class HomeController(
@GetMapping(path = ["patient/{patientPseudonym}"])
fun byPatient(
@PathVariable patientPseudonym: PatientPseudonym,
- @PageableDefault(page = 0, size = 20, sort = ["processedAt"], direction = Sort.Direction.DESC) pageable: Pageable,
- model: Model
+ @PageableDefault(page = 0, size = 20, sort = ["processedAt"], direction = Sort.Direction.DESC)
+ pageable: Pageable,
+ model: Model,
): String {
val requests = requestService.findRequestByPatientId(patientPseudonym, pageable)
model.addAttribute("patientPseudonym", patientPseudonym.value)
@@ -65,12 +66,14 @@ class HomeController(
}
@GetMapping(path = ["/report/{id}"])
- fun report(@PathVariable id: RequestId, model: Model): String {
+ fun report(
+ @PathVariable id: RequestId,
+ model: Model,
+ ): String {
val request = requestService.findByUuid(id).orElse(null) ?: throw NotFoundException()
model.addAttribute("request", request)
model.addAttribute("issues", reportService.deserialize(request.report?.dataQualityReport))
return "report"
}
-
-} \ No newline at end of file
+}
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/web/LoginController.kt b/src/main/kotlin/dev/dnpm/etl/processor/web/LoginController.kt
index 20837bb..7821bf9 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/web/LoginController.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/web/LoginController.kt
@@ -28,20 +28,21 @@ import org.springframework.web.bind.annotation.GetMapping
@Controller
class LoginController(
private val securityConfigProperties: SecurityConfigProperties?,
- private val oAuth2ClientProperties: OAuth2ClientProperties?
+ private val oAuth2ClientProperties: OAuth2ClientProperties?,
) {
-
@GetMapping(path = ["/login"])
fun login(model: Model): String {
if (securityConfigProperties?.enableOidc == true) {
model.addAttribute(
"oidcLogins",
- oAuth2ClientProperties?.registration?.map { (key, value) -> Pair(key, value.clientName) }.orEmpty()
+ oAuth2ClientProperties
+ ?.registration
+ ?.map { (key, value) -> Pair(key, value.clientName) }
+ .orEmpty(),
)
} else {
model.addAttribute("oidcLogins", emptyList<Pair<String, String>>())
}
return "login"
}
-
-} \ No newline at end of file
+}
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/web/StatisticsController.kt b/src/main/kotlin/dev/dnpm/etl/processor/web/StatisticsController.kt
index adc1e2b..e48d5df 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/web/StatisticsController.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/web/StatisticsController.kt
@@ -28,11 +28,9 @@ import java.time.Instant
@Controller
@RequestMapping(path = ["/statistics"])
class StatisticsController {
-
@GetMapping
fun index(model: Model): String {
model.addAttribute("now", Instant.now())
return "statistics"
}
-
-} \ No newline at end of file
+}
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/web/StatisticsRestController.kt b/src/main/kotlin/dev/dnpm/etl/processor/web/StatisticsRestController.kt
index 8372274..c99a4a3 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/web/StatisticsRestController.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/web/StatisticsRestController.kt
@@ -41,140 +41,168 @@ import java.time.temporal.ChronoUnit
class StatisticsRestController(
@param:Qualifier("statisticsUpdateProducer")
private val statisticsUpdateProducer: Sinks.Many<Any>,
- private val requestService: RequestService
+ private val requestService: RequestService,
) {
-
@GetMapping(path = ["requeststates"])
- fun requestStates(@RequestParam(required = false, defaultValue = "false") delete: Boolean): List<NameValue> {
- val states = if (delete) {
- requestService.countDeleteStates()
- } else {
- requestService.countStates()
- }
+ fun requestStates(
+ @RequestParam(required = false, defaultValue = "false") delete: Boolean,
+ ): List<NameValue> {
+ val states =
+ if (delete) {
+ requestService.countDeleteStates()
+ } else {
+ requestService.countStates()
+ }
return states
.map {
- val color = when (it.status) {
- RequestStatus.ERROR -> "red"
- RequestStatus.WARNING -> "darkorange"
- RequestStatus.SUCCESS -> "green"
- else -> "slategray"
- }
+ val color =
+ when (it.status) {
+ RequestStatus.ERROR -> "red"
+ RequestStatus.WARNING -> "darkorange"
+ RequestStatus.SUCCESS -> "green"
+ else -> "slategray"
+ }
NameValue(it.status.toString(), it.count, color)
- }
- .sortedByDescending { it.value }
+ }.sortedByDescending { it.value }
}
@GetMapping(path = ["requestslastmonth"])
fun requestsLastMonth(
- @RequestParam(
- required = false,
- defaultValue = "false"
- ) delete: Boolean
+ @RequestParam(required = false, defaultValue = "false") delete: Boolean,
): List<DateNameValues> {
- val requestType = if (delete) {
- RequestType.DELETE
- } else {
- RequestType.MTB_FILE
- }
+ val requestType =
+ if (delete) {
+ RequestType.DELETE
+ } else {
+ RequestType.MTB_FILE
+ }
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd").withZone(ZoneId.of("Europe/Berlin"))
- val data = requestService.findAll()
- .filter { it.type == requestType }
- .filter { it.processedAt.isAfter(Instant.now().minus(30, ChronoUnit.DAYS)) }
- .groupBy { formatter.format(it.processedAt) }
- .map {
- val requestList = it.value
- .groupBy { request -> request.status }
- .map { request ->
- Pair(request.key, request.value.size)
- }
- .toMap()
- Pair(
- it.key.toString(),
- DateNameValues(
- it.key.toString(), NameValues(
- error = requestList[RequestStatus.ERROR] ?: 0,
- warning = requestList[RequestStatus.WARNING] ?: 0,
- success = requestList[RequestStatus.SUCCESS] ?: 0,
- duplication = requestList[RequestStatus.DUPLICATION] ?: 0,
- unknown = requestList[RequestStatus.UNKNOWN] ?: 0,
- )
+ val data =
+ requestService
+ .findAll()
+ .filter { it.type == requestType }
+ .filter { it.processedAt.isAfter(Instant.now().minus(30, ChronoUnit.DAYS)) }
+ .groupBy { formatter.format(it.processedAt) }
+ .map {
+ val requestList =
+ it.value
+ .groupBy { request -> request.status }
+ .map { request -> Pair(request.key, request.value.size) }
+ .toMap()
+ Pair(
+ it.key.toString(),
+ DateNameValues(
+ it.key.toString(),
+ NameValues(
+ error = requestList[RequestStatus.ERROR] ?: 0,
+ warning = requestList[RequestStatus.WARNING] ?: 0,
+ success = requestList[RequestStatus.SUCCESS] ?: 0,
+ duplication = requestList[RequestStatus.DUPLICATION] ?: 0,
+ unknown = requestList[RequestStatus.UNKNOWN] ?: 0,
+ ),
+ ),
)
- )
- }.toMap()
+ }.toMap()
- return (0L..30L).map { Instant.now().minus(it, ChronoUnit.DAYS) }
+ return (0L..30L)
+ .map { Instant.now().minus(it, ChronoUnit.DAYS) }
.map { formatter.format(it) }
- .map {
- DateNameValues(it, data[it]?.nameValues ?: NameValues())
- }
+ .map { DateNameValues(it, data[it]?.nameValues ?: NameValues()) }
.sortedBy { it.date }
}
@GetMapping(path = ["requestpatientstates"])
- fun requestPatientStates(@RequestParam(required = false, defaultValue = "false") delete: Boolean): List<NameValue> {
- val states = if (delete) {
- requestService.findPatientUniqueDeleteStates()
- } else {
- requestService.findPatientUniqueStates()
- }
+ fun requestPatientStates(
+ @RequestParam(required = false, defaultValue = "false") delete: Boolean,
+ ): List<NameValue> {
+ val states =
+ if (delete) {
+ requestService.findPatientUniqueDeleteStates()
+ } else {
+ requestService.findPatientUniqueStates()
+ }
return states.map {
- val color = when (it.status) {
- RequestStatus.ERROR -> "red"
- RequestStatus.WARNING -> "darkorange"
- RequestStatus.SUCCESS -> "green"
- else -> "slategray"
- }
+ val color =
+ when (it.status) {
+ RequestStatus.ERROR -> "red"
+ RequestStatus.WARNING -> "darkorange"
+ RequestStatus.SUCCESS -> "green"
+ else -> "slategray"
+ }
NameValue(it.status.toString(), it.count, color)
}
}
@GetMapping(path = ["events"], produces = [MediaType.TEXT_EVENT_STREAM_VALUE])
- fun updater(): Flux<ServerSentEvent<Any>> {
- return statisticsUpdateProducer.asFlux().flatMap {
+ fun updater(): Flux<ServerSentEvent<Any>> =
+ statisticsUpdateProducer.asFlux().flatMap {
Flux.fromIterable(
listOf(
- ServerSentEvent.builder<Any>()
- .event("requeststates").id("none").data(this.requestStates(false))
+ ServerSentEvent
+ .builder<Any>()
+ .event("requeststates")
+ .id("none")
+ .data(this.requestStates(false))
.build(),
- ServerSentEvent.builder<Any>()
- .event("requestslastmonth").id("none").data(this.requestsLastMonth(false))
+ ServerSentEvent
+ .builder<Any>()
+ .event("requestslastmonth")
+ .id("none")
+ .data(this.requestsLastMonth(false))
.build(),
- ServerSentEvent.builder<Any>()
- .event("requestpatientstates").id("none").data(this.requestPatientStates(false))
+ ServerSentEvent
+ .builder<Any>()
+ .event("requestpatientstates")
+ .id("none")
+ .data(this.requestPatientStates(false))
.build(),
-
- ServerSentEvent.builder<Any>()
- .event("deleterequeststates").id("none").data(this.requestStates(true))
+ ServerSentEvent
+ .builder<Any>()
+ .event("deleterequeststates")
+ .id("none")
+ .data(this.requestStates(true))
.build(),
- ServerSentEvent.builder<Any>()
- .event("deleterequestslastmonth").id("none").data(this.requestsLastMonth(true))
+ ServerSentEvent
+ .builder<Any>()
+ .event("deleterequestslastmonth")
+ .id("none")
+ .data(this.requestsLastMonth(true))
.build(),
- ServerSentEvent.builder<Any>()
- .event("deleterequestpatientstates").id("none").data(this.requestPatientStates(true))
+ ServerSentEvent
+ .builder<Any>()
+ .event("deleterequestpatientstates")
+ .id("none")
+ .data(this.requestPatientStates(true))
.build(),
-
- ServerSentEvent.builder<Any>()
- .event("newrequest").id("none").data("newrequest")
- .build()
- )
+ ServerSentEvent
+ .builder<Any>()
+ .event("newrequest")
+ .id("none")
+ .data("newrequest")
+ .build(),
+ ),
)
-
}
- }
-
}
-data class NameValue(val name: String, val value: Int, val color: String)
+data class NameValue(
+ val name: String,
+ val value: Int,
+ val color: String,
+)
-data class DateNameValues(val date: String, val nameValues: NameValues)
+data class DateNameValues(
+ val date: String,
+ val nameValues: NameValues,
+)
data class NameValues(
val error: Int = 0,
val warning: Int = 0,
val success: Int = 0,
val duplication: Int = 0,
- val unknown: Int = 0
-) \ No newline at end of file
+ val unknown: Int = 0,
+)
diff --git a/src/test/kotlin/dev/dnpm/etl/processor/consent/ConsentProcessorTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/consent/ConsentProcessorTest.kt
index 5a86a29..1140425 100644
--- a/src/test/kotlin/dev/dnpm/etl/processor/consent/ConsentProcessorTest.kt
+++ b/src/test/kotlin/dev/dnpm/etl/processor/consent/ConsentProcessorTest.kt
@@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.ObjectMapper
import dev.dnpm.etl.processor.config.AppConfigProperties
import dev.dnpm.etl.processor.config.GIcsConfigProperties
import dev.dnpm.etl.processor.services.ConsentProcessor
+import java.util.*
import org.assertj.core.api.Assertions.assertThat
import org.hl7.fhir.r4.model.Bundle
import org.hl7.fhir.r4.model.Consent
@@ -14,45 +15,46 @@ import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.CsvSource
import org.mockito.Mock
import org.mockito.junit.jupiter.MockitoExtension
-import java.util.*
@ExtendWith(MockitoExtension::class)
class ConsentProcessorTest {
- lateinit var consentProcessor: ConsentProcessor
+ lateinit var consentProcessor: ConsentProcessor
- val objectMapper = ObjectMapper()
- val fhirContext = FhirContext.forR4()
+ val objectMapper = ObjectMapper()
+ val fhirContext = FhirContext.forR4()
- @BeforeEach
- fun setup(
- @Mock consentService: IConsentService
- ) {
- val appConfigProperties = AppConfigProperties()
- val gIcsConfigProperties = GIcsConfigProperties("http://localhost")
+ @BeforeEach
+ fun setup(@Mock consentService: IConsentService) {
+ val appConfigProperties = AppConfigProperties()
+ val gIcsConfigProperties = GIcsConfigProperties("http://localhost")
- this.consentProcessor = ConsentProcessor(
+ this.consentProcessor =
+ ConsentProcessor(
appConfigProperties,
gIcsConfigProperties,
objectMapper,
fhirContext,
- consentService
+ consentService,
)
- }
-
- @ParameterizedTest
- @CsvSource(value = [
- "permittedConsentBundle.json,permit",
- "deniedConsentBundle.json,deny"
- ])
- fun checkGetProvisionTypeByPolicyCode(filename: String, expected: String) {
- val bundle = fhirContext.newJsonParser().parseResource(
- this.javaClass.classLoader.getResourceAsStream(filename)
+ }
+
+ @ParameterizedTest
+ @CsvSource(value = ["permittedConsentBundle.json,permit", "deniedConsentBundle.json,deny"])
+ fun checkGetProvisionTypeByPolicyCode(filename: String, expected: String) {
+ val bundle =
+ fhirContext
+ .newJsonParser()
+ .parseResource(this.javaClass.classLoader.getResourceAsStream(filename))
+ assertThat(bundle).isInstanceOf(Bundle::class.java)
+
+ val actual =
+ consentProcessor.getProvisionTypeByPolicyCode(
+ bundle as Bundle,
+ Date(),
+ ConsentDomain.BROAD_CONSENT,
)
- assertThat(bundle).isInstanceOf(Bundle::class.java)
-
- val actual = consentProcessor.getProvisionTypeByPolicyCode(bundle as Bundle, Date(), ConsentDomain.BROAD_CONSENT)
- assertThat(actual).isEqualTo(Consent.ConsentProvisionType.valueOf(expected.uppercase()))
- }
-} \ No newline at end of file
+ assertThat(actual).isEqualTo(Consent.ConsentProvisionType.valueOf(expected.uppercase()))
+ }
+}
diff --git a/src/test/kotlin/dev/dnpm/etl/processor/consent/Dnpm21BasedConsentEvaluatorTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/consent/Dnpm21BasedConsentEvaluatorTest.kt
index adbec2f..85a8b3e 100644
--- a/src/test/kotlin/dev/dnpm/etl/processor/consent/Dnpm21BasedConsentEvaluatorTest.kt
+++ b/src/test/kotlin/dev/dnpm/etl/processor/consent/Dnpm21BasedConsentEvaluatorTest.kt
@@ -21,6 +21,8 @@ package dev.dnpm.etl.processor.consent
import dev.dnpm.etl.processor.ArgProvider
import dev.pcvolkmer.mv64e.mtb.*
+import java.time.Instant
+import java.util.*
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Nested
@@ -32,256 +34,269 @@ import org.mockito.ArgumentMatchers.anyString
import org.mockito.Mock
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.whenever
-import java.time.Instant
-import java.util.*
@ExtendWith(MockitoExtension::class)
class Dnpm21BasedConsentEvaluatorTest {
- @Nested
- inner class WithGicsConsentEnabled {
+ @Nested
+ inner class WithGicsConsentEnabled {
- lateinit var consentService: GicsConsentService
- lateinit var consentEvaluator: ConsentEvaluator
+ lateinit var consentService: GicsConsentService
+ lateinit var consentEvaluator: ConsentEvaluator
- @BeforeEach
- fun setUp(
- @Mock consentService: GicsConsentService
- ) {
- this.consentService = consentService
- this.consentEvaluator = ConsentEvaluator(consentService)
- }
+ @BeforeEach
+ fun setUp(@Mock consentService: GicsConsentService) {
+ this.consentService = consentService
+ this.consentEvaluator = ConsentEvaluator(consentService)
+ }
- @ParameterizedTest
- @ArgumentsSource(WithGicsMtbFileProvider::class)
- fun test(
- mtbFile: Mtb,
- ttpConsentStatus: TtpConsentStatus,
- expectedConsentEvaluation: ConsentEvaluation
- ) {
- whenever(consentService.getTtpBroadConsentStatus(anyString())).thenReturn(
- ttpConsentStatus
- )
- assertThat(consentEvaluator.check(mtbFile)).isEqualTo(expectedConsentEvaluation)
- }
+ @ParameterizedTest
+ @ArgumentsSource(WithGicsMtbFileProvider::class)
+ fun test(
+ mtbFile: Mtb,
+ ttpConsentStatus: TtpConsentStatus,
+ expectedConsentEvaluation: ConsentEvaluation,
+ ) {
+ whenever(consentService.getTtpBroadConsentStatus(anyString())).thenReturn(ttpConsentStatus)
+ assertThat(consentEvaluator.check(mtbFile)).isEqualTo(expectedConsentEvaluation)
}
+ }
- @Nested
- inner class WithFileConsentOnly {
+ @Nested
+ inner class WithFileConsentOnly {
- lateinit var consentService: MtbFileConsentService
- lateinit var consentEvaluator: ConsentEvaluator
+ lateinit var consentService: MtbFileConsentService
+ lateinit var consentEvaluator: ConsentEvaluator
- @BeforeEach
- fun setUp() {
- this.consentService = MtbFileConsentService()
- this.consentEvaluator = ConsentEvaluator(consentService)
- }
+ @BeforeEach
+ fun setUp() {
+ this.consentService = MtbFileConsentService()
+ this.consentEvaluator = ConsentEvaluator(consentService)
+ }
- @ParameterizedTest
- @ArgumentsSource(MtbFileProvider::class)
- fun test(mtbFile: Mtb, expectedConsentEvaluation: ConsentEvaluation) {
- assertThat(consentEvaluator.check(mtbFile)).isEqualTo(expectedConsentEvaluation)
- }
+ @ParameterizedTest
+ @ArgumentsSource(MtbFileProvider::class)
+ fun test(mtbFile: Mtb, expectedConsentEvaluation: ConsentEvaluation) {
+ assertThat(consentEvaluator.check(mtbFile)).isEqualTo(expectedConsentEvaluation)
}
+ }
- // Util classes
+ // Util classes
- class WithGicsMtbFileProvider : ArgProvider(
- // Has file ModelProjectConsent and broad consent => consent given
- Arguments.of(
- buildMtb(ConsentProvision.PERMIT),
- TtpConsentStatus.BROAD_CONSENT_GIVEN,
- ConsentEvaluation(TtpConsentStatus.BROAD_CONSENT_GIVEN, true)
- ),
- // Has file ModelProjectConsent and broad consent missing => no consent given
- Arguments.of(
- buildMtb(ConsentProvision.PERMIT),
- TtpConsentStatus.BROAD_CONSENT_MISSING,
- ConsentEvaluation(TtpConsentStatus.BROAD_CONSENT_MISSING, false)
- ),
- // Has file ModelProjectConsent and broad consent missing or rejected => no consent given
- Arguments.of(
- buildMtb(ConsentProvision.PERMIT),
- TtpConsentStatus.BROAD_CONSENT_MISSING_OR_REJECTED,
- ConsentEvaluation(TtpConsentStatus.BROAD_CONSENT_MISSING_OR_REJECTED, false)
- ),
- // Has file ModelProjectConsent and MV consent => consent given
- Arguments.of(
- buildMtb(ConsentProvision.PERMIT),
- TtpConsentStatus.GENOM_DE_CONSENT_SEQUENCING_PERMIT,
- ConsentEvaluation(TtpConsentStatus.GENOM_DE_CONSENT_SEQUENCING_PERMIT, true)
- ),
- // Has file ModelProjectConsent and MV consent rejected => no consent given
- Arguments.of(
- buildMtb(ConsentProvision.PERMIT),
- TtpConsentStatus.GENOM_DE_SEQUENCING_REJECTED,
- ConsentEvaluation(TtpConsentStatus.GENOM_DE_SEQUENCING_REJECTED, false)
- ),
- // Has file ModelProjectConsent and MV consent missing => no consent given
- Arguments.of(
- buildMtb(ConsentProvision.PERMIT),
- TtpConsentStatus.GENOM_DE_CONSENT_MISSING,
- ConsentEvaluation(TtpConsentStatus.GENOM_DE_CONSENT_MISSING, false)
- ),
- // Has file ModelProjectConsent and no broad consent result => consent given
- Arguments.of(
- buildMtb(ConsentProvision.PERMIT),
- TtpConsentStatus.UNKNOWN_CHECK_FILE,
- ConsentEvaluation(TtpConsentStatus.UNKNOWN_CHECK_FILE, true)
- ),
- // Has file ModelProjectConsent and failed to ask => no consent given
- Arguments.of(
- buildMtb(ConsentProvision.PERMIT),
- TtpConsentStatus.FAILED_TO_ASK,
- ConsentEvaluation(TtpConsentStatus.FAILED_TO_ASK, false)
- ),
- // File ModelProjectConsent rejected and broad consent => consent given
- Arguments.of(
- buildMtb(ConsentProvision.DENY),
- TtpConsentStatus.BROAD_CONSENT_GIVEN,
- ConsentEvaluation(TtpConsentStatus.BROAD_CONSENT_GIVEN, true)
- ),
- // File ModelProjectConsent rejected and broad consent missing => no consent given
- Arguments.of(
- buildMtb(ConsentProvision.DENY),
- TtpConsentStatus.BROAD_CONSENT_MISSING,
- ConsentEvaluation(TtpConsentStatus.BROAD_CONSENT_MISSING, false)
- ),
- // File ModelProjectConsent rejected and broad consent missing or rejected => no consent given
- Arguments.of(
- buildMtb(ConsentProvision.DENY),
- TtpConsentStatus.BROAD_CONSENT_MISSING_OR_REJECTED,
- ConsentEvaluation(TtpConsentStatus.BROAD_CONSENT_MISSING_OR_REJECTED, false)
- ),
- // File ModelProjectConsent rejected and MV consent => consent given
- Arguments.of(
- buildMtb(ConsentProvision.DENY),
- TtpConsentStatus.GENOM_DE_CONSENT_SEQUENCING_PERMIT,
- ConsentEvaluation(TtpConsentStatus.GENOM_DE_CONSENT_SEQUENCING_PERMIT, true)
- ),
- // File ModelProjectConsent rejected and MV consent rejected => no consent given
- Arguments.of(
- buildMtb(ConsentProvision.DENY),
- TtpConsentStatus.GENOM_DE_SEQUENCING_REJECTED,
- ConsentEvaluation(TtpConsentStatus.GENOM_DE_SEQUENCING_REJECTED, false)
- ),
- // File ModelProjectConsent rejected and MV consent missing => no consent given
- Arguments.of(
- buildMtb(ConsentProvision.DENY),
- TtpConsentStatus.GENOM_DE_CONSENT_MISSING,
- ConsentEvaluation(TtpConsentStatus.GENOM_DE_CONSENT_MISSING, false)
- ),
- // File ModelProjectConsent rejected and no broad consent result => no consent given
- Arguments.of(
- buildMtb(ConsentProvision.DENY),
- TtpConsentStatus.UNKNOWN_CHECK_FILE,
- ConsentEvaluation(TtpConsentStatus.UNKNOWN_CHECK_FILE, false)
- ),
- // File ModelProjectConsent rejected and failed to ask => no consent given
- Arguments.of(
- buildMtb(ConsentProvision.DENY),
- TtpConsentStatus.FAILED_TO_ASK,
- ConsentEvaluation(TtpConsentStatus.FAILED_TO_ASK, false)
- )
- ) {
+ class WithGicsMtbFileProvider :
+ ArgProvider(
+ // Has file ModelProjectConsent and broad consent => consent given
+ Arguments.of(
+ buildMtb(ConsentProvision.PERMIT),
+ TtpConsentStatus.BROAD_CONSENT_GIVEN,
+ ConsentEvaluation(TtpConsentStatus.BROAD_CONSENT_GIVEN, true),
+ ),
+ // Has file ModelProjectConsent and broad consent missing => no consent given
+ Arguments.of(
+ buildMtb(ConsentProvision.PERMIT),
+ TtpConsentStatus.BROAD_CONSENT_MISSING,
+ ConsentEvaluation(TtpConsentStatus.BROAD_CONSENT_MISSING, false),
+ ),
+ // Has file ModelProjectConsent and broad consent missing or rejected => no consent given
+ Arguments.of(
+ buildMtb(ConsentProvision.PERMIT),
+ TtpConsentStatus.BROAD_CONSENT_MISSING_OR_REJECTED,
+ ConsentEvaluation(TtpConsentStatus.BROAD_CONSENT_MISSING_OR_REJECTED, false),
+ ),
+ // Has file ModelProjectConsent and MV consent => consent given
+ Arguments.of(
+ buildMtb(ConsentProvision.PERMIT),
+ TtpConsentStatus.GENOM_DE_CONSENT_SEQUENCING_PERMIT,
+ ConsentEvaluation(TtpConsentStatus.GENOM_DE_CONSENT_SEQUENCING_PERMIT, true),
+ ),
+ // Has file ModelProjectConsent and MV consent rejected => no consent given
+ Arguments.of(
+ buildMtb(ConsentProvision.PERMIT),
+ TtpConsentStatus.GENOM_DE_SEQUENCING_REJECTED,
+ ConsentEvaluation(TtpConsentStatus.GENOM_DE_SEQUENCING_REJECTED, false),
+ ),
+ // Has file ModelProjectConsent and MV consent missing => no consent given
+ Arguments.of(
+ buildMtb(ConsentProvision.PERMIT),
+ TtpConsentStatus.GENOM_DE_CONSENT_MISSING,
+ ConsentEvaluation(TtpConsentStatus.GENOM_DE_CONSENT_MISSING, false),
+ ),
+ // Has file ModelProjectConsent and no broad consent result => consent given
+ Arguments.of(
+ buildMtb(ConsentProvision.PERMIT),
+ TtpConsentStatus.UNKNOWN_CHECK_FILE,
+ ConsentEvaluation(TtpConsentStatus.UNKNOWN_CHECK_FILE, true),
+ ),
+ // Has file ModelProjectConsent and failed to ask => no consent given
+ Arguments.of(
+ buildMtb(ConsentProvision.PERMIT),
+ TtpConsentStatus.FAILED_TO_ASK,
+ ConsentEvaluation(TtpConsentStatus.FAILED_TO_ASK, false),
+ ),
+ // File ModelProjectConsent rejected and broad consent => consent given
+ Arguments.of(
+ buildMtb(ConsentProvision.DENY),
+ TtpConsentStatus.BROAD_CONSENT_GIVEN,
+ ConsentEvaluation(TtpConsentStatus.BROAD_CONSENT_GIVEN, true),
+ ),
+ // File ModelProjectConsent rejected and broad consent missing => no consent given
+ Arguments.of(
+ buildMtb(ConsentProvision.DENY),
+ TtpConsentStatus.BROAD_CONSENT_MISSING,
+ ConsentEvaluation(TtpConsentStatus.BROAD_CONSENT_MISSING, false),
+ ),
+ // File ModelProjectConsent rejected and broad consent missing or rejected => no consent
+ // given
+ Arguments.of(
+ buildMtb(ConsentProvision.DENY),
+ TtpConsentStatus.BROAD_CONSENT_MISSING_OR_REJECTED,
+ ConsentEvaluation(TtpConsentStatus.BROAD_CONSENT_MISSING_OR_REJECTED, false),
+ ),
+ // File ModelProjectConsent rejected and MV consent => consent given
+ Arguments.of(
+ buildMtb(ConsentProvision.DENY),
+ TtpConsentStatus.GENOM_DE_CONSENT_SEQUENCING_PERMIT,
+ ConsentEvaluation(TtpConsentStatus.GENOM_DE_CONSENT_SEQUENCING_PERMIT, true),
+ ),
+ // File ModelProjectConsent rejected and MV consent rejected => no consent given
+ Arguments.of(
+ buildMtb(ConsentProvision.DENY),
+ TtpConsentStatus.GENOM_DE_SEQUENCING_REJECTED,
+ ConsentEvaluation(TtpConsentStatus.GENOM_DE_SEQUENCING_REJECTED, false),
+ ),
+ // File ModelProjectConsent rejected and MV consent missing => no consent given
+ Arguments.of(
+ buildMtb(ConsentProvision.DENY),
+ TtpConsentStatus.GENOM_DE_CONSENT_MISSING,
+ ConsentEvaluation(TtpConsentStatus.GENOM_DE_CONSENT_MISSING, false),
+ ),
+ // File ModelProjectConsent rejected and no broad consent result => no consent given
+ Arguments.of(
+ buildMtb(ConsentProvision.DENY),
+ TtpConsentStatus.UNKNOWN_CHECK_FILE,
+ ConsentEvaluation(TtpConsentStatus.UNKNOWN_CHECK_FILE, false),
+ ),
+ // File ModelProjectConsent rejected and failed to ask => no consent given
+ Arguments.of(
+ buildMtb(ConsentProvision.DENY),
+ TtpConsentStatus.FAILED_TO_ASK,
+ ConsentEvaluation(TtpConsentStatus.FAILED_TO_ASK, false),
+ ),
+ ) {
- companion object {
- fun buildMtb(consentProvision: ConsentProvision): Mtb {
- return Mtb.builder()
- .patient(
- Patient.builder().id("TEST_12345678")
- .birthDate(Date.from(Instant.parse("2000-08-08T12:34:56Z"))).gender(
- GenderCoding.builder().code(GenderCodingCode.MALE).build()
- ).build()
- )
- .metadata(
- MvhMetadata.builder().modelProjectConsent(
- ModelProjectConsent.builder().provisions(
+ companion object {
+ fun buildMtb(consentProvision: ConsentProvision): Mtb {
+ return Mtb.builder()
+ .patient(
+ Patient.builder()
+ .id("TEST_12345678")
+ .birthDate(Date.from(Instant.parse("2000-08-08T12:34:56Z")))
+ .gender(GenderCoding.builder().code(GenderCodingCode.MALE).build())
+ .build()
+ )
+ .metadata(
+ MvhMetadata.builder()
+ .modelProjectConsent(
+ ModelProjectConsent.builder()
+ .provisions(
listOf(
- Provision.builder().date(Date()).type(consentProvision)
- .purpose(ModelProjectConsentPurpose.SEQUENCING).build()
+ Provision.builder()
+ .date(Date())
+ .type(consentProvision)
+ .purpose(ModelProjectConsentPurpose.SEQUENCING)
+ .build()
)
- ).build()
- ).build()
- )
- .episodesOfCare(
- listOf(
- MtbEpisodeOfCare.builder().id("1")
- .patient(Reference.builder().id("TEST_12345678").build())
- .build()
- )
+ )
+ .build()
)
.build()
- }
- }
+ )
+ .episodesOfCare(
+ listOf(
+ MtbEpisodeOfCare.builder()
+ .id("1")
+ .patient(Reference.builder().id("TEST_12345678").build())
+ .build()
+ )
+ )
+ .build()
+ }
}
+ }
- class MtbFileProvider : ArgProvider(
- // Has file consent => consent given
- Arguments.of(
- buildMtb(ConsentProvision.PERMIT),
- ConsentEvaluation(TtpConsentStatus.UNKNOWN_CHECK_FILE, true)
- ),
- // File consent rejected => no consent given
- Arguments.of(
- buildMtb(ConsentProvision.DENY),
- ConsentEvaluation(TtpConsentStatus.UNKNOWN_CHECK_FILE, false)
- ),
- // policy REIDENTIFICATION has no effect on ConsentEvaluation
- Arguments.of(
- buildMtb(ModelProjectConsentPurpose.REIDENTIFICATION, ConsentProvision.DENY),
- ConsentEvaluation(TtpConsentStatus.UNKNOWN_CHECK_FILE, false)
- ), Arguments.of(
- buildMtb(ModelProjectConsentPurpose.REIDENTIFICATION, ConsentProvision.PERMIT),
- ConsentEvaluation(TtpConsentStatus.UNKNOWN_CHECK_FILE, false)
- ),
- // policy CASE_IDENTIFICATION has no effect on ConsentEvaluation
- Arguments.of(
- buildMtb(ModelProjectConsentPurpose.CASE_IDENTIFICATION, ConsentProvision.DENY),
- ConsentEvaluation(TtpConsentStatus.UNKNOWN_CHECK_FILE, false)
- ), Arguments.of(
- buildMtb(ModelProjectConsentPurpose.CASE_IDENTIFICATION, ConsentProvision.PERMIT),
- ConsentEvaluation(TtpConsentStatus.UNKNOWN_CHECK_FILE, false)
- )
- ) {
+ class MtbFileProvider :
+ ArgProvider(
+ // Has file consent => consent given
+ Arguments.of(
+ buildMtb(ConsentProvision.PERMIT),
+ ConsentEvaluation(TtpConsentStatus.UNKNOWN_CHECK_FILE, true),
+ ),
+ // File consent rejected => no consent given
+ Arguments.of(
+ buildMtb(ConsentProvision.DENY),
+ ConsentEvaluation(TtpConsentStatus.UNKNOWN_CHECK_FILE, false),
+ ),
+ // policy REIDENTIFICATION has no effect on ConsentEvaluation
+ Arguments.of(
+ buildMtb(ModelProjectConsentPurpose.REIDENTIFICATION, ConsentProvision.DENY),
+ ConsentEvaluation(TtpConsentStatus.UNKNOWN_CHECK_FILE, false),
+ ),
+ Arguments.of(
+ buildMtb(ModelProjectConsentPurpose.REIDENTIFICATION, ConsentProvision.PERMIT),
+ ConsentEvaluation(TtpConsentStatus.UNKNOWN_CHECK_FILE, false),
+ ),
+ // policy CASE_IDENTIFICATION has no effect on ConsentEvaluation
+ Arguments.of(
+ buildMtb(ModelProjectConsentPurpose.CASE_IDENTIFICATION, ConsentProvision.DENY),
+ ConsentEvaluation(TtpConsentStatus.UNKNOWN_CHECK_FILE, false),
+ ),
+ Arguments.of(
+ buildMtb(ModelProjectConsentPurpose.CASE_IDENTIFICATION, ConsentProvision.PERMIT),
+ ConsentEvaluation(TtpConsentStatus.UNKNOWN_CHECK_FILE, false),
+ ),
+ ) {
- companion object {
- fun buildMtb(consentProvision: ConsentProvision): Mtb {
- return buildMtb(ModelProjectConsentPurpose.SEQUENCING, consentProvision)
- }
+ companion object {
+ fun buildMtb(consentProvision: ConsentProvision): Mtb {
+ return buildMtb(ModelProjectConsentPurpose.SEQUENCING, consentProvision)
+ }
- fun buildMtb(
- policy: ModelProjectConsentPurpose,
- consentProvision: ConsentProvision
- ): Mtb {
- return Mtb.builder()
- .patient(
- Patient.builder().id("TEST_12345678")
- .birthDate(Date.from(Instant.parse("2000-08-08T12:34:56Z"))).gender(
- GenderCoding.builder().code(GenderCodingCode.MALE).build()
- ).build()
- )
- .metadata(
- MvhMetadata.builder().modelProjectConsent(
- ModelProjectConsent.builder().provisions(
+ fun buildMtb(policy: ModelProjectConsentPurpose, consentProvision: ConsentProvision): Mtb {
+ return Mtb.builder()
+ .patient(
+ Patient.builder()
+ .id("TEST_12345678")
+ .birthDate(Date.from(Instant.parse("2000-08-08T12:34:56Z")))
+ .gender(GenderCoding.builder().code(GenderCodingCode.MALE).build())
+ .build()
+ )
+ .metadata(
+ MvhMetadata.builder()
+ .modelProjectConsent(
+ ModelProjectConsent.builder()
+ .provisions(
listOf(
- Provision.builder().date(Date()).type(consentProvision)
- .purpose(policy).build()
+ Provision.builder()
+ .date(Date())
+ .type(consentProvision)
+ .purpose(policy)
+ .build()
)
- ).build()
- ).build()
- )
- .episodesOfCare(
- listOf(
- MtbEpisodeOfCare.builder().id("1")
- .patient(Reference.builder().id("TEST_12345678").build())
- .build()
- )
+ )
+ .build()
)
.build()
- }
- }
+ )
+ .episodesOfCare(
+ listOf(
+ MtbEpisodeOfCare.builder()
+ .id("1")
+ .patient(Reference.builder().id("TEST_12345678").build())
+ .build()
+ )
+ )
+ .build()
+ }
}
-
+ }
}
diff --git a/src/test/kotlin/dev/dnpm/etl/processor/helpers.kt b/src/test/kotlin/dev/dnpm/etl/processor/helpers.kt
index 2dfb1e1..495bf38 100644
--- a/src/test/kotlin/dev/dnpm/etl/processor/helpers.kt
+++ b/src/test/kotlin/dev/dnpm/etl/processor/helpers.kt
@@ -19,13 +19,12 @@
package dev.dnpm.etl.processor
+import java.util.stream.Stream
import org.junit.jupiter.api.extension.ExtensionContext
import org.junit.jupiter.params.provider.Arguments
import org.junit.jupiter.params.provider.ArgumentsProvider
-import java.util.stream.Stream
open class ArgProvider(vararg val data: Arguments) : ArgumentsProvider {
- override fun provideArguments(
- context: ExtensionContext?
- ): Stream<out Arguments> = Stream.of(*data)
+ override fun provideArguments(context: ExtensionContext?): Stream<out Arguments> =
+ Stream.of(*data)
}
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 7f07766..da05fba 100644
--- a/src/test/kotlin/dev/dnpm/etl/processor/input/KafkaInputListenerTest.kt
+++ b/src/test/kotlin/dev/dnpm/etl/processor/input/KafkaInputListenerTest.kt
@@ -26,6 +26,7 @@ import dev.dnpm.etl.processor.consent.ConsentEvaluator
import dev.dnpm.etl.processor.consent.TtpConsentStatus
import dev.dnpm.etl.processor.services.RequestProcessor
import dev.pcvolkmer.mv64e.mtb.*
+import java.util.*
import org.apache.kafka.clients.consumer.ConsumerRecord
import org.apache.kafka.common.header.internals.RecordHeader
import org.apache.kafka.common.header.internals.RecordHeaders
@@ -36,267 +37,241 @@ import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.Mock
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.*
-import java.util.*
@ExtendWith(MockitoExtension::class)
class KafkaInputListenerTest {
- private lateinit var requestProcessor: RequestProcessor
- private lateinit var consentEvaluator: ConsentEvaluator
- private lateinit var objectMapper: ObjectMapper
+ private lateinit var requestProcessor: RequestProcessor
+ private lateinit var consentEvaluator: ConsentEvaluator
+ private lateinit var objectMapper: ObjectMapper
- private lateinit var kafkaInputListener: KafkaInputListener
+ private lateinit var kafkaInputListener: KafkaInputListener
- @BeforeEach
- fun setup(
- @Mock requestProcessor: RequestProcessor,
- @Mock consentEvaluator: ConsentEvaluator,
- ) {
- this.requestProcessor = requestProcessor
- this.consentEvaluator = consentEvaluator
- this.objectMapper = ObjectMapper()
+ @BeforeEach
+ fun setup(
+ @Mock requestProcessor: RequestProcessor,
+ @Mock consentEvaluator: ConsentEvaluator,
+ ) {
+ this.requestProcessor = requestProcessor
+ this.consentEvaluator = consentEvaluator
+ this.objectMapper = ObjectMapper()
- this.kafkaInputListener = KafkaInputListener(requestProcessor, consentEvaluator, objectMapper)
- }
+ this.kafkaInputListener = KafkaInputListener(requestProcessor, consentEvaluator, objectMapper)
+ }
- @Test
- fun shouldProcessMtbFileRequest() {
- whenever(consentEvaluator.check(any())).thenReturn(
- ConsentEvaluation(
- TtpConsentStatus.BROAD_CONSENT_GIVEN,
- true
- )
- )
+ @Test
+ fun shouldProcessMtbFileRequest() {
+ whenever(consentEvaluator.check(any()))
+ .thenReturn(ConsentEvaluation(TtpConsentStatus.BROAD_CONSENT_GIVEN, true))
- val mtbFile = Mtb.builder()
+ val mtbFile =
+ Mtb.builder()
.patient(Patient.builder().id("DUMMY_12345678").build())
.metadata(
- MvhMetadata
- .builder()
+ MvhMetadata.builder()
.modelProjectConsent(
- ModelProjectConsent
- .builder()
+ ModelProjectConsent.builder()
.provisions(
listOf(
- Provision.builder().type(ConsentProvision.PERMIT)
- .purpose(ModelProjectConsentPurpose.SEQUENCING).build()
+ Provision.builder()
+ .type(ConsentProvision.PERMIT)
+ .purpose(ModelProjectConsentPurpose.SEQUENCING)
+ .build()
)
- ).build()
+ )
+ .build()
)
.build()
)
.build()
- kafkaInputListener.onMessage(
- ConsumerRecord(
- "testtopic",
- 0,
- 0,
- "",
- this.objectMapper.writeValueAsString(mtbFile)
- )
- )
+ kafkaInputListener.onMessage(
+ ConsumerRecord("testtopic", 0, 0, "", this.objectMapper.writeValueAsString(mtbFile))
+ )
- verify(requestProcessor, times(1)).processMtbFile(any<Mtb>())
- }
+ verify(requestProcessor, times(1)).processMtbFile(any<Mtb>())
+ }
- @Test
- fun shouldProcessDeleteRequest() {
- whenever(consentEvaluator.check(any())).thenReturn(
- ConsentEvaluation(
- TtpConsentStatus.BROAD_CONSENT_GIVEN,
- false
- )
- )
+ @Test
+ fun shouldProcessDeleteRequest() {
+ whenever(consentEvaluator.check(any()))
+ .thenReturn(ConsentEvaluation(TtpConsentStatus.BROAD_CONSENT_GIVEN, false))
- val mtbFile = Mtb.builder()
+ val mtbFile =
+ Mtb.builder()
.patient(Patient.builder().id("DUMMY_12345678").build())
.metadata(
- MvhMetadata
- .builder()
+ MvhMetadata.builder()
.modelProjectConsent(
- ModelProjectConsent
- .builder()
+ ModelProjectConsent.builder()
.provisions(
listOf(
- Provision.builder().type(ConsentProvision.DENY)
- .purpose(ModelProjectConsentPurpose.SEQUENCING).build()
+ Provision.builder()
+ .type(ConsentProvision.DENY)
+ .purpose(ModelProjectConsentPurpose.SEQUENCING)
+ .build()
)
- ).build()
+ )
+ .build()
)
.build()
)
.build()
- kafkaInputListener.onMessage(
- ConsumerRecord(
- "testtopic",
- 0,
- 0,
- "",
- this.objectMapper.writeValueAsString(mtbFile)
- )
- )
+ kafkaInputListener.onMessage(
+ ConsumerRecord("testtopic", 0, 0, "", this.objectMapper.writeValueAsString(mtbFile))
+ )
- verify(requestProcessor, times(1)).processDeletion(
- anyValueClass(),
- eq(TtpConsentStatus.UNKNOWN_CHECK_FILE)
- )
- }
+ verify(requestProcessor, times(1))
+ .processDeletion(anyValueClass(), eq(TtpConsentStatus.UNKNOWN_CHECK_FILE))
+ }
- @Test
- fun shouldProcessMtbFileRequestWithExistingRequestId() {
- whenever(consentEvaluator.check(any())).thenReturn(
- ConsentEvaluation(
- TtpConsentStatus.BROAD_CONSENT_GIVEN,
- true
- )
- )
+ @Test
+ fun shouldProcessMtbFileRequestWithExistingRequestId() {
+ whenever(consentEvaluator.check(any()))
+ .thenReturn(ConsentEvaluation(TtpConsentStatus.BROAD_CONSENT_GIVEN, true))
- val mtbFile = Mtb.builder()
+ val mtbFile =
+ Mtb.builder()
.patient(Patient.builder().id("DUMMY_12345678").build())
.metadata(
- MvhMetadata
- .builder()
+ MvhMetadata.builder()
.modelProjectConsent(
- ModelProjectConsent
- .builder()
+ ModelProjectConsent.builder()
.provisions(
listOf(
- Provision.builder().type(ConsentProvision.PERMIT)
- .purpose(ModelProjectConsentPurpose.SEQUENCING).build()
+ Provision.builder()
+ .type(ConsentProvision.PERMIT)
+ .purpose(ModelProjectConsentPurpose.SEQUENCING)
+ .build()
)
- ).build()
+ )
+ .build()
)
.build()
)
.build()
- val headers = RecordHeaders(listOf(RecordHeader("requestId", UUID.randomUUID().toString().toByteArray())))
- kafkaInputListener.onMessage(
- ConsumerRecord(
- "testtopic",
- 0,
- 0,
- -1L,
- TimestampType.NO_TIMESTAMP_TYPE,
- -1,
- -1,
- "",
- this.objectMapper.writeValueAsString(mtbFile),
- headers,
- Optional.empty()
- )
+ val headers =
+ RecordHeaders(listOf(RecordHeader("requestId", UUID.randomUUID().toString().toByteArray())))
+ kafkaInputListener.onMessage(
+ ConsumerRecord(
+ "testtopic",
+ 0,
+ 0,
+ -1L,
+ TimestampType.NO_TIMESTAMP_TYPE,
+ -1,
+ -1,
+ "",
+ this.objectMapper.writeValueAsString(mtbFile),
+ headers,
+ Optional.empty(),
)
+ )
- verify(requestProcessor, times(1)).processMtbFile(any<Mtb>(), anyValueClass())
- }
+ verify(requestProcessor, times(1)).processMtbFile(any<Mtb>(), anyValueClass())
+ }
- @Test
- fun shouldProcessDeleteRequestWithExistingRequestId() {
- whenever(consentEvaluator.check(any())).thenReturn(
- ConsentEvaluation(
- TtpConsentStatus.BROAD_CONSENT_GIVEN,
- false
- )
- )
+ @Test
+ fun shouldProcessDeleteRequestWithExistingRequestId() {
+ whenever(consentEvaluator.check(any()))
+ .thenReturn(ConsentEvaluation(TtpConsentStatus.BROAD_CONSENT_GIVEN, false))
- val mtbFile = Mtb.builder()
+ val mtbFile =
+ Mtb.builder()
.patient(Patient.builder().id("DUMMY_12345678").build())
.metadata(
- MvhMetadata
- .builder()
+ MvhMetadata.builder()
.modelProjectConsent(
- ModelProjectConsent
- .builder()
+ ModelProjectConsent.builder()
.provisions(
listOf(
- Provision.builder().type(ConsentProvision.DENY)
- .purpose(ModelProjectConsentPurpose.SEQUENCING).build()
+ Provision.builder()
+ .type(ConsentProvision.DENY)
+ .purpose(ModelProjectConsentPurpose.SEQUENCING)
+ .build()
)
- ).build()
+ )
+ .build()
)
.build()
)
.build()
- val headers = RecordHeaders(listOf(RecordHeader("requestId", UUID.randomUUID().toString().toByteArray())))
- 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(
- anyValueClass(), anyValueClass(), eq(
- TtpConsentStatus.UNKNOWN_CHECK_FILE
- )
+ val headers =
+ RecordHeaders(listOf(RecordHeader("requestId", UUID.randomUUID().toString().toByteArray())))
+ 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(anyValueClass(), anyValueClass(), eq(TtpConsentStatus.UNKNOWN_CHECK_FILE))
+ }
- @Test
- fun shouldProcessDnpmV2Request() {
- whenever(consentEvaluator.check(any())).thenReturn(
- ConsentEvaluation(
- TtpConsentStatus.BROAD_CONSENT_GIVEN,
- false
- )
- )
+ @Test
+ fun shouldProcessDnpmV2Request() {
+ whenever(consentEvaluator.check(any()))
+ .thenReturn(ConsentEvaluation(TtpConsentStatus.BROAD_CONSENT_GIVEN, false))
- val mtbFile = Mtb.builder()
+ val mtbFile =
+ Mtb.builder()
.patient(Patient.builder().id("DUMMY_12345678").build())
.metadata(
- MvhMetadata
- .builder()
+ MvhMetadata.builder()
.modelProjectConsent(
- ModelProjectConsent
- .builder()
+ ModelProjectConsent.builder()
.provisions(
listOf(
- Provision.builder().type(ConsentProvision.DENY)
- .purpose(ModelProjectConsentPurpose.SEQUENCING).build()
+ Provision.builder()
+ .type(ConsentProvision.DENY)
+ .purpose(ModelProjectConsentPurpose.SEQUENCING)
+ .build()
)
- ).build()
+ )
+ .build()
)
.build()
)
.build()
- val headers = RecordHeaders(
+ val headers =
+ RecordHeaders(
listOf(
RecordHeader("requestId", UUID.randomUUID().toString().toByteArray()),
- RecordHeader("contentType", CustomMediaType.APPLICATION_VND_DNPM_V2_MTB_JSON_VALUE.toByteArray())
- )
- )
- kafkaInputListener.onMessage(
- ConsumerRecord(
- "testtopic",
- 0,
- 0,
- -1L,
- TimestampType.NO_TIMESTAMP_TYPE,
- -1,
- -1,
- "",
- this.objectMapper.writeValueAsString(mtbFile),
- headers,
- Optional.empty()
+ RecordHeader(
+ "contentType",
+ CustomMediaType.APPLICATION_VND_DNPM_V2_MTB_JSON_VALUE.toByteArray(),
+ ),
)
)
- verify(requestProcessor, times(1)).processDeletion(
- anyValueClass(), anyValueClass(), eq(
- TtpConsentStatus.UNKNOWN_CHECK_FILE
- )
+ 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(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 ae9e4e2..95858f4 100644
--- a/src/test/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt
+++ b/src/test/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt
@@ -27,6 +27,8 @@ import dev.dnpm.etl.processor.consent.ConsentEvaluator
import dev.dnpm.etl.processor.consent.TtpConsentStatus
import dev.dnpm.etl.processor.services.RequestProcessor
import dev.pcvolkmer.mv64e.mtb.*
+import java.time.Instant
+import java.util.*
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
@@ -46,181 +48,176 @@ 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
-import java.time.Instant
-import java.util.*
@ExtendWith(MockitoExtension::class)
class MtbFileRestControllerTest {
- private val objectMapper = ObjectMapper()
-
- @Nested
- inner class RequestsForDnpmDataModel21 {
-
- private lateinit var mockMvc: MockMvc
-
- private lateinit var requestProcessor: RequestProcessor
- private lateinit var consentEvaluator: ConsentEvaluator
-
- @BeforeEach
- fun setup(
- @Mock requestProcessor: RequestProcessor,
- @Mock consentEvaluator: ConsentEvaluator
- ) {
- this.requestProcessor = requestProcessor
- this.consentEvaluator = consentEvaluator
- val controller = MtbFileRestController(
- requestProcessor,
- consentEvaluator
- )
- this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build()
- }
-
- @Test
- fun shouldRespondPostRequest() {
- whenever(consentEvaluator.check(any())).thenReturn(
- ConsentEvaluation(
- TtpConsentStatus.BROAD_CONSENT_GIVEN,
- true
- )
- )
-
- val mtbFileContent =
- ClassPathResource("mv64e-mtb-fake-patient.json").inputStream.readAllBytes().toString(Charsets.UTF_8)
-
- mockMvc.post("/mtb") {
- content = mtbFileContent
- contentType = CustomMediaType.APPLICATION_VND_DNPM_V2_MTB_JSON
- }.andExpect {
- status {
- isAccepted()
- }
- }
-
- verify(requestProcessor, times(1)).processMtbFile(any<Mtb>())
- }
-
- @ParameterizedTest
- @ArgumentsSource(Dnpm21MtbFile::class)
- fun shouldProcessPostRequest(mtb: Mtb, broadConsent: TtpConsentStatus, shouldProcess: String) {
- whenever(consentEvaluator.check(any<Mtb>())).thenReturn(
- ConsentEvaluation(
- broadConsent,
- shouldProcess == "process"
- )
- )
-
- mockMvc.post("/mtbfile") {
- content = objectMapper.writeValueAsString(mtb)
- contentType = CustomMediaType.APPLICATION_VND_DNPM_V2_MTB_JSON
- }.andExpect {
- status {
- isAccepted()
- }
- }
-
- if (shouldProcess == "process") {
- verify(requestProcessor, times(1)).processMtbFile(any<Mtb>())
- } else {
- verify(requestProcessor, times(1)).processDeletion(
- anyValueClass(),
- org.mockito.kotlin.eq(broadConsent)
- )
- }
- }
-
- @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(consentEvaluator, times(0)).check(any<Mtb>())
- }
+ private val objectMapper = ObjectMapper()
+
+ @Nested
+ inner class RequestsForDnpmDataModel21 {
+
+ private lateinit var mockMvc: MockMvc
+
+ private lateinit var requestProcessor: RequestProcessor
+ private lateinit var consentEvaluator: ConsentEvaluator
+
+ @BeforeEach
+ fun setup(@Mock requestProcessor: RequestProcessor, @Mock consentEvaluator: ConsentEvaluator) {
+ this.requestProcessor = requestProcessor
+ this.consentEvaluator = consentEvaluator
+ val controller = MtbFileRestController(requestProcessor, consentEvaluator)
+ this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build()
+ }
+
+ @Test
+ fun shouldRespondPostRequest() {
+ whenever(consentEvaluator.check(any()))
+ .thenReturn(ConsentEvaluation(TtpConsentStatus.BROAD_CONSENT_GIVEN, true))
+
+ val mtbFileContent =
+ ClassPathResource("mv64e-mtb-fake-patient.json")
+ .inputStream
+ .readAllBytes()
+ .toString(Charsets.UTF_8)
+
+ mockMvc
+ .post("/mtb") {
+ content = mtbFileContent
+ contentType = CustomMediaType.APPLICATION_VND_DNPM_V2_MTB_JSON
+ }
+ .andExpect { status { isAccepted() } }
+
+ verify(requestProcessor, times(1)).processMtbFile(any<Mtb>())
}
+
+ @ParameterizedTest
+ @ArgumentsSource(Dnpm21MtbFile::class)
+ fun shouldProcessPostRequest(mtb: Mtb, broadConsent: TtpConsentStatus, shouldProcess: String) {
+ whenever(consentEvaluator.check(any<Mtb>()))
+ .thenReturn(ConsentEvaluation(broadConsent, shouldProcess == "process"))
+
+ mockMvc
+ .post("/mtbfile") {
+ content = objectMapper.writeValueAsString(mtb)
+ contentType = CustomMediaType.APPLICATION_VND_DNPM_V2_MTB_JSON
+ }
+ .andExpect { status { isAccepted() } }
+
+ if (shouldProcess == "process") {
+ verify(requestProcessor, times(1)).processMtbFile(any<Mtb>())
+ } else {
+ verify(requestProcessor, times(1))
+ .processDeletion(anyValueClass(), org.mockito.kotlin.eq(broadConsent))
+ }
+ }
+
+ @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(consentEvaluator, times(0)).check(any<Mtb>())
+ }
+ }
}
-class Dnpm21MtbFile : ArgProvider(
- // No Metadata and no broad consent => delete
- Arguments.of(
- buildMtb(null),
- TtpConsentStatus.BROAD_CONSENT_MISSING_OR_REJECTED,
- "delete"
- ),
- // No Metadata and broad consent given => process
- Arguments.of(
- buildMtb(null),
- TtpConsentStatus.BROAD_CONSENT_GIVEN,
- "process"
- ),
- // No model project consent and no broad consent => delete
- Arguments.of(
- buildMtb(MvhMetadata.builder().modelProjectConsent(ModelProjectConsent.builder().build()).build()),
- TtpConsentStatus.BROAD_CONSENT_MISSING_OR_REJECTED,
- "delete"
- ),
- // No model project consent and broad consent given => process
- Arguments.of(
- buildMtb(MvhMetadata.builder().modelProjectConsent(ModelProjectConsent.builder().build()).build()),
- TtpConsentStatus.BROAD_CONSENT_GIVEN,
- "process"
- ),
- // Model project consent given and no broad consent => process
- Arguments.of(
- buildMtb(
- MvhMetadata.builder().modelProjectConsent(
- ModelProjectConsent.builder().provisions(
- listOf(
- Provision.builder().date(Date()).type(ConsentProvision.PERMIT)
- .purpose(ModelProjectConsentPurpose.SEQUENCING).build()
- )
- ).build()
- ).build()
+class Dnpm21MtbFile :
+ ArgProvider(
+ // No Metadata and no broad consent => delete
+ Arguments.of(buildMtb(null), TtpConsentStatus.BROAD_CONSENT_MISSING_OR_REJECTED, "delete"),
+ // No Metadata and broad consent given => process
+ Arguments.of(buildMtb(null), TtpConsentStatus.BROAD_CONSENT_GIVEN, "process"),
+ // No model project consent and no broad consent => delete
+ Arguments.of(
+ buildMtb(
+ MvhMetadata.builder()
+ .modelProjectConsent(ModelProjectConsent.builder().build())
+ .build()
+ ),
+ TtpConsentStatus.BROAD_CONSENT_MISSING_OR_REJECTED,
+ "delete",
+ ),
+ // No model project consent and broad consent given => process
+ Arguments.of(
+ buildMtb(
+ MvhMetadata.builder()
+ .modelProjectConsent(ModelProjectConsent.builder().build())
+ .build()
+ ),
+ TtpConsentStatus.BROAD_CONSENT_GIVEN,
+ "process",
),
- TtpConsentStatus.UNKNOWN_CHECK_FILE,
- "process"
- ),
- // Model project consent given and broad consent given => process
- Arguments.of(
- buildMtb(
- MvhMetadata.builder().modelProjectConsent(
- ModelProjectConsent.builder().provisions(
- listOf(
- Provision.builder().date(Date()).type(ConsentProvision.PERMIT)
- .purpose(ModelProjectConsentPurpose.SEQUENCING).build()
+ // Model project consent given and no broad consent => process
+ Arguments.of(
+ buildMtb(
+ MvhMetadata.builder()
+ .modelProjectConsent(
+ ModelProjectConsent.builder()
+ .provisions(
+ listOf(
+ Provision.builder()
+ .date(Date())
+ .type(ConsentProvision.PERMIT)
+ .purpose(ModelProjectConsentPurpose.SEQUENCING)
+ .build()
+ )
+ )
+ .build()
)
- ).build()
- ).build()
+ .build()
+ ),
+ TtpConsentStatus.UNKNOWN_CHECK_FILE,
+ "process",
),
- TtpConsentStatus.BROAD_CONSENT_GIVEN,
- "process"
- )
-) {
-
- companion object {
- fun buildMtb(metadata: MvhMetadata?): Mtb {
- return Mtb.builder()
- .patient(
- Patient.builder().id("TEST_12345678")
- .birthDate(Date.from(Instant.parse("2000-08-08T12:34:56Z"))).gender(
- GenderCoding.builder().code(GenderCodingCode.MALE).build()
- ).build()
- )
- .metadata(metadata)
- .episodesOfCare(
- listOf(
- MtbEpisodeOfCare.builder().id("1")
- .patient(Reference.builder().id("TEST_12345678").build())
+ // Model project consent given and broad consent given => process
+ Arguments.of(
+ buildMtb(
+ MvhMetadata.builder()
+ .modelProjectConsent(
+ ModelProjectConsent.builder()
+ .provisions(
+ listOf(
+ Provision.builder()
+ .date(Date())
+ .type(ConsentProvision.PERMIT)
+ .purpose(ModelProjectConsentPurpose.SEQUENCING)
+ .build()
+ )
+ )
.build()
)
- )
- .build()
- }
+ .build()
+ ),
+ TtpConsentStatus.BROAD_CONSENT_GIVEN,
+ "process",
+ ),
+ ) {
+
+ companion object {
+ fun buildMtb(metadata: MvhMetadata?): Mtb {
+ return Mtb.builder()
+ .patient(
+ Patient.builder()
+ .id("TEST_12345678")
+ .birthDate(Date.from(Instant.parse("2000-08-08T12:34:56Z")))
+ .gender(GenderCoding.builder().code(GenderCodingCode.MALE).build())
+ .build()
+ )
+ .metadata(metadata)
+ .episodesOfCare(
+ listOf(
+ MtbEpisodeOfCare.builder()
+ .id("1")
+ .patient(Reference.builder().id("TEST_12345678").build())
+ .build()
+ )
+ )
+ .build()
}
+ }
}
diff --git a/src/test/kotlin/dev/dnpm/etl/processor/monitoring/ConnectionCheckServiceTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/monitoring/ConnectionCheckServiceTest.kt
index a6d855c..a380d2a 100644
--- a/src/test/kotlin/dev/dnpm/etl/processor/monitoring/ConnectionCheckServiceTest.kt
+++ b/src/test/kotlin/dev/dnpm/etl/processor/monitoring/ConnectionCheckServiceTest.kt
@@ -22,10 +22,8 @@ import reactor.test.StepVerifier
@ExtendWith(MockitoExtension::class)
class ConnectionCheckServiceTest {
-
@Nested
inner class RestConnectionCheckServiceTest {
-
lateinit var mockRestServiceServer: MockRestServiceServer
lateinit var service: RestConnectionCheckService
lateinit var sink: Sinks.Many<ConnectionCheckResult>
@@ -33,11 +31,12 @@ class ConnectionCheckServiceTest {
@BeforeEach
fun setUp() {
val restTemplate = RestTemplate()
- val restTargetProperties = RestTargetProperties(
- "http://localhost/api",
- "user",
- "password",
- )
+ val restTargetProperties =
+ RestTargetProperties(
+ "http://localhost/api",
+ "user",
+ "password",
+ )
this.sink = Sinks.many().multicast().onBackpressureBuffer()
this.mockRestServiceServer = MockRestServiceServer.createServer(restTemplate)
@@ -65,12 +64,12 @@ class ConnectionCheckServiceTest {
withSuccess("OK", MediaType.APPLICATION_JSON),
)
- val verifier = StepVerifier.create(sink.asFlux())
- .assertNext {
- assertThat(it.available).isTrue()
- }
- .expectComplete()
- .verifyLater()
+ val verifier =
+ StepVerifier
+ .create(sink.asFlux())
+ .assertNext { assertThat(it.available).isTrue() }
+ .expectComplete()
+ .verifyLater()
this.service.check()
@@ -81,18 +80,14 @@ class ConnectionCheckServiceTest {
@Test
fun shouldEmitUnavailable() {
- this.mockRestServiceServer
- .expect(method(HttpMethod.GET))
- .andRespond(
- withServerError()
- )
+ this.mockRestServiceServer.expect(method(HttpMethod.GET)).andRespond(withServerError())
- val verifier = StepVerifier.create(sink.asFlux())
- .assertNext {
- assertThat(it.available).isFalse()
- }
- .expectComplete()
- .verifyLater()
+ val verifier =
+ StepVerifier
+ .create(sink.asFlux())
+ .assertNext { assertThat(it.available).isFalse() }
+ .expectComplete()
+ .verifyLater()
this.service.check()
@@ -104,7 +99,6 @@ class ConnectionCheckServiceTest {
@Nested
inner class GPasConnectionCheckServiceTest {
-
lateinit var mockRestServiceServer: MockRestServiceServer
lateinit var service: GPasConnectionCheckService
lateinit var sink: Sinks.Many<ConnectionCheckResult>
@@ -112,15 +106,16 @@ class ConnectionCheckServiceTest {
@BeforeEach
fun setUp() {
val restTemplate = RestTemplate()
- val gpasTargetProperties = GPasConfigProperties(
- "http://localhost/gpas",
- null,
- null,
- "patientDomain",
- "genomDeTanDomain",
- "username",
- "password",
- )
+ val gpasTargetProperties =
+ GPasConfigProperties(
+ "http://localhost/gpas",
+ null,
+ null,
+ "patientDomain",
+ "genomDeTanDomain",
+ "username",
+ "password",
+ )
this.sink = Sinks.many().multicast().onBackpressureBuffer()
this.mockRestServiceServer = MockRestServiceServer.createServer(restTemplate)
@@ -149,12 +144,12 @@ class ConnectionCheckServiceTest {
withSuccess("OK", MediaType.APPLICATION_JSON),
)
- val verifier = StepVerifier.create(sink.asFlux())
- .assertNext {
- assertThat(it.available).isTrue()
- }
- .expectComplete()
- .verifyLater()
+ val verifier =
+ StepVerifier
+ .create(sink.asFlux())
+ .assertNext { assertThat(it.available).isTrue() }
+ .expectComplete()
+ .verifyLater()
this.service.check()
@@ -165,18 +160,14 @@ class ConnectionCheckServiceTest {
@Test
fun shouldEmitUnavailable() {
- this.mockRestServiceServer
- .expect(method(HttpMethod.GET))
- .andRespond(
- withServerError()
- )
+ this.mockRestServiceServer.expect(method(HttpMethod.GET)).andRespond(withServerError())
- val verifier = StepVerifier.create(sink.asFlux())
- .assertNext {
- assertThat(it.available).isFalse()
- }
- .expectComplete()
- .verifyLater()
+ val verifier =
+ StepVerifier
+ .create(sink.asFlux())
+ .assertNext { assertThat(it.available).isFalse() }
+ .expectComplete()
+ .verifyLater()
this.service.check()
@@ -188,7 +179,6 @@ class ConnectionCheckServiceTest {
@Nested
inner class GIcsConnectionCheckServiceTest {
-
lateinit var mockRestServiceServer: MockRestServiceServer
lateinit var service: GIcsConnectionCheckService
lateinit var sink: Sinks.Many<ConnectionCheckResult>
@@ -197,11 +187,12 @@ class ConnectionCheckServiceTest {
fun setUp() {
val restTemplate = RestTemplate()
- val gicsTargetProperties = GIcsConfigProperties(
- "http://localhost/gics",
- "username",
- "password",
- )
+ val gicsTargetProperties =
+ GIcsConfigProperties(
+ "http://localhost/gics",
+ "username",
+ "password",
+ )
this.sink = Sinks.many().multicast().onBackpressureBuffer()
this.mockRestServiceServer = MockRestServiceServer.createServer(restTemplate)
@@ -220,7 +211,6 @@ class ConnectionCheckServiceTest {
this.service.check()
this.mockRestServiceServer.verify()
-
}
@Test
@@ -231,12 +221,12 @@ class ConnectionCheckServiceTest {
withSuccess("OK", MediaType.APPLICATION_JSON),
)
- val verifier = StepVerifier.create(sink.asFlux())
- .assertNext {
- assertThat(it.available).isTrue()
- }
- .expectComplete()
- .verifyLater()
+ val verifier =
+ StepVerifier
+ .create(sink.asFlux())
+ .assertNext { assertThat(it.available).isTrue() }
+ .expectComplete()
+ .verifyLater()
this.service.check()
@@ -247,18 +237,14 @@ class ConnectionCheckServiceTest {
@Test
fun shouldEmitUnavailable() {
- this.mockRestServiceServer
- .expect(method(HttpMethod.GET))
- .andRespond(
- withServerError()
- )
+ this.mockRestServiceServer.expect(method(HttpMethod.GET)).andRespond(withServerError())
- val verifier = StepVerifier.create(sink.asFlux())
- .assertNext {
- assertThat(it.available).isFalse()
- }
- .expectComplete()
- .verifyLater()
+ val verifier =
+ StepVerifier
+ .create(sink.asFlux())
+ .assertNext { assertThat(it.available).isFalse() }
+ .expectComplete()
+ .verifyLater()
this.service.check()
@@ -267,5 +253,4 @@ class ConnectionCheckServiceTest {
verifier.verify()
}
}
-
-} \ No newline at end of file
+}
diff --git a/src/test/kotlin/dev/dnpm/etl/processor/monitoring/ReportServiceTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/monitoring/ReportServiceTest.kt
index 4bf1321..74d1138 100644
--- a/src/test/kotlin/dev/dnpm/etl/processor/monitoring/ReportServiceTest.kt
+++ b/src/test/kotlin/dev/dnpm/etl/processor/monitoring/ReportServiceTest.kt
@@ -1,36 +1,36 @@
package dev.dnpm.etl.processor.monitoring
import dev.dnpm.etl.processor.config.JacksonConfig
+import java.util.*
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
-import java.util.*
class ReportServiceTest {
- lateinit var service: ReportService
+ lateinit var service: ReportService
- @BeforeEach
- fun setUp() {
- val jacksonConfig = JacksonConfig()
- service = ReportService(jacksonConfig.objectMapper())
- }
+ @BeforeEach
+ fun setUp() {
+ val jacksonConfig = JacksonConfig()
+ service = ReportService(jacksonConfig.objectMapper())
+ }
- @Test
- fun shouldParseDataQualityReport() {
- val dataQualityReport = Objects.requireNonNull(this.javaClass.classLoader.getResource("dip-response.json"))
+ @Test
+ fun shouldParseDataQualityReport() {
+ val dataQualityReport =
+ Objects.requireNonNull(this.javaClass.classLoader.getResource("dip-response.json"))
.readText()
- val actual = service.deserialize(dataQualityReport)
-
- assertThat(actual).isNotNull
- assertThat(actual).hasSize(6)
- assertThat(actual[0].severity).isEqualTo(ReportService.Severity.FATAL)
- assertThat(actual[1].severity).isEqualTo(ReportService.Severity.ERROR)
- assertThat(actual[2].severity).isEqualTo(ReportService.Severity.WARNING)
- assertThat(actual[3].severity).isEqualTo(ReportService.Severity.WARNING)
- assertThat(actual[4].severity).isEqualTo(ReportService.Severity.WARNING)
- assertThat(actual[5].severity).isEqualTo(ReportService.Severity.INFO)
- }
-
-} \ No newline at end of file
+ val actual = service.deserialize(dataQualityReport)
+
+ assertThat(actual).isNotNull
+ assertThat(actual).hasSize(6)
+ assertThat(actual[0].severity).isEqualTo(ReportService.Severity.FATAL)
+ assertThat(actual[1].severity).isEqualTo(ReportService.Severity.ERROR)
+ assertThat(actual[2].severity).isEqualTo(ReportService.Severity.WARNING)
+ assertThat(actual[3].severity).isEqualTo(ReportService.Severity.WARNING)
+ assertThat(actual[4].severity).isEqualTo(ReportService.Severity.WARNING)
+ assertThat(actual[5].severity).isEqualTo(ReportService.Severity.INFO)
+ }
+}
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 022b8dd..b1cd5fa 100644
--- a/src/test/kotlin/dev/dnpm/etl/processor/output/KafkaMtbFileSenderTest.kt
+++ b/src/test/kotlin/dev/dnpm/etl/processor/output/KafkaMtbFileSenderTest.kt
@@ -26,6 +26,10 @@ import dev.dnpm.etl.processor.RequestId
import dev.dnpm.etl.processor.config.KafkaProperties
import dev.dnpm.etl.processor.monitoring.RequestStatus
import dev.pcvolkmer.mv64e.mtb.*
+import java.time.Instant
+import java.util.*
+import java.util.concurrent.CompletableFuture.completedFuture
+import java.util.concurrent.ExecutionException
import org.apache.kafka.clients.producer.ProducerRecord
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
@@ -41,203 +45,208 @@ import org.springframework.kafka.core.KafkaTemplate
import org.springframework.kafka.support.SendResult
import org.springframework.retry.policy.SimpleRetryPolicy
import org.springframework.retry.support.RetryTemplateBuilder
-import java.time.Instant
-import java.util.*
-import java.util.concurrent.CompletableFuture.completedFuture
-import java.util.concurrent.ExecutionException
@ExtendWith(MockitoExtension::class)
class KafkaMtbFileSenderTest {
- @Nested
- inner class BwhcV1Record {
+ @Nested
+ inner class BwhcV1Record {
- private lateinit var kafkaTemplate: KafkaTemplate<String, String>
+ private lateinit var kafkaTemplate: KafkaTemplate<String, String>
- private lateinit var kafkaMtbFileSender: KafkaMtbFileSender
+ private lateinit var kafkaMtbFileSender: KafkaMtbFileSender
- private lateinit var objectMapper: ObjectMapper
+ private lateinit var objectMapper: ObjectMapper
- @BeforeEach
- fun setup(
- @Mock kafkaTemplate: KafkaTemplate<String, String>
- ) {
- val kafkaProperties = KafkaProperties("testtopic")
- val retryTemplate = RetryTemplateBuilder().customPolicy(SimpleRetryPolicy(1)).build()
+ @BeforeEach
+ fun setup(@Mock kafkaTemplate: KafkaTemplate<String, String>) {
+ val kafkaProperties = KafkaProperties("testtopic")
+ val retryTemplate = RetryTemplateBuilder().customPolicy(SimpleRetryPolicy(1)).build()
- this.objectMapper = ObjectMapper()
- this.kafkaTemplate = kafkaTemplate
+ this.objectMapper = ObjectMapper()
+ this.kafkaTemplate = kafkaTemplate
- this.kafkaMtbFileSender = KafkaMtbFileSender(kafkaTemplate, kafkaProperties, retryTemplate, objectMapper)
- }
-
- @ParameterizedTest
- @MethodSource("dev.dnpm.etl.processor.output.KafkaMtbFileSenderTest#requestWithResponseSource")
- fun shouldSendDeleteRequestAndReturnExpectedState(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(DeleteRequest(TEST_REQUEST_ID, TEST_PATIENT_PSEUDONYM))
- assertThat(response.status).isEqualTo(testData.requestStatus)
- }
-
- @ParameterizedTest
- @MethodSource("dev.dnpm.etl.processor.output.KafkaMtbFileSenderTest#requestWithResponseSource")
- fun shouldRetryOnDeleteKafkaSendError(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(DeleteRequest(TEST_REQUEST_ID, TEST_PATIENT_PSEUDONYM))
+ this.kafkaMtbFileSender =
+ KafkaMtbFileSender(kafkaTemplate, kafkaProperties, retryTemplate, objectMapper)
+ }
- val expectedCount = when (testData.exception) {
- // OK - No Retry
- null -> times(1)
- // Request failed - Retry max 3 times
- else -> times(3)
+ @ParameterizedTest
+ @MethodSource("dev.dnpm.etl.processor.output.KafkaMtbFileSenderTest#requestWithResponseSource")
+ fun shouldSendDeleteRequestAndReturnExpectedState(testData: TestData) {
+ doAnswer {
+ if (null != testData.exception) {
+ throw testData.exception
}
+ completedFuture(SendResult<String, String>(null, null))
+ }
+ .whenever(kafkaTemplate)
+ .send(any<ProducerRecord<String, String>>())
- verify(kafkaTemplate, expectedCount).send(any<ProducerRecord<String, String>>())
- }
-
+ val response = kafkaMtbFileSender.send(DeleteRequest(TEST_REQUEST_ID, TEST_PATIENT_PSEUDONYM))
+ assertThat(response.status).isEqualTo(testData.requestStatus)
}
- @Nested
- inner class DnpmV2Record {
-
- private lateinit var kafkaTemplate: KafkaTemplate<String, String>
+ @ParameterizedTest
+ @MethodSource("dev.dnpm.etl.processor.output.KafkaMtbFileSenderTest#requestWithResponseSource")
+ fun shouldRetryOnDeleteKafkaSendError(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(DeleteRequest(TEST_REQUEST_ID, TEST_PATIENT_PSEUDONYM))
+
+ 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>>())
+ }
+ }
- private lateinit var kafkaMtbFileSender: KafkaMtbFileSender
+ @Nested
+ inner class DnpmV2Record {
- private lateinit var objectMapper: ObjectMapper
+ private lateinit var kafkaTemplate: KafkaTemplate<String, String>
- @BeforeEach
- fun setup(
- @Mock kafkaTemplate: KafkaTemplate<String, String>
- ) {
- val kafkaProperties = KafkaProperties("testtopic")
- val retryTemplate = RetryTemplateBuilder().customPolicy(SimpleRetryPolicy(1)).build()
+ private lateinit var kafkaMtbFileSender: KafkaMtbFileSender
- this.objectMapper = ObjectMapper()
- this.kafkaTemplate = kafkaTemplate
+ private lateinit var objectMapper: ObjectMapper
- this.kafkaMtbFileSender = KafkaMtbFileSender(kafkaTemplate, kafkaProperties, retryTemplate, objectMapper)
- }
+ @BeforeEach
+ fun setup(@Mock kafkaTemplate: KafkaTemplate<String, String>) {
+ val kafkaProperties = KafkaProperties("testtopic")
+ val retryTemplate = RetryTemplateBuilder().customPolicy(SimpleRetryPolicy(1)).build()
- @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(DnpmV2MtbFileRequest(TEST_REQUEST_ID, dnpmV2MtbFile()))
- 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(DnpmV2MtbFileRequest(TEST_REQUEST_ID, dnpmV2MtbFile()))
-
- 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(CustomMediaType.APPLICATION_VND_DNPM_V2_MTB_JSON_VALUE.toByteArray())
- assertThat(captor.firstValue.headers().headers("requestId")).isNotNull
- assertThat(captor.firstValue.headers().headers("requestId")?.firstOrNull()?.value()).isEqualTo(TEST_REQUEST_ID.value.toByteArray())
- assertThat(captor.firstValue.value()).isNotNull
- assertThat(captor.firstValue.value()).isEqualTo(objectMapper.writeValueAsString(dnmpV2kafkaRecordData(TEST_REQUEST_ID)))
- }
-
- @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>>())
+ this.objectMapper = ObjectMapper()
+ this.kafkaTemplate = kafkaTemplate
- kafkaMtbFileSender.send(DnpmV2MtbFileRequest(TEST_REQUEST_ID, dnpmV2MtbFile()))
+ this.kafkaMtbFileSender =
+ KafkaMtbFileSender(kafkaTemplate, kafkaProperties, retryTemplate, objectMapper)
+ }
- val expectedCount = when (testData.exception) {
- // OK - No Retry
- null -> times(1)
- // Request failed - Retry max 3 times
- else -> times(3)
+ @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>>())
- verify(kafkaTemplate, expectedCount).send(any<ProducerRecord<String, String>>())
- }
+ val response = kafkaMtbFileSender.send(DnpmV2MtbFileRequest(TEST_REQUEST_ID, dnpmV2MtbFile()))
+ 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(DnpmV2MtbFileRequest(TEST_REQUEST_ID, dnpmV2MtbFile()))
+
+ 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(CustomMediaType.APPLICATION_VND_DNPM_V2_MTB_JSON_VALUE.toByteArray())
+ assertThat(captor.firstValue.headers().headers("requestId")).isNotNull
+ assertThat(captor.firstValue.headers().headers("requestId")?.firstOrNull()?.value())
+ .isEqualTo(TEST_REQUEST_ID.value.toByteArray())
+ assertThat(captor.firstValue.value()).isNotNull
+ assertThat(captor.firstValue.value())
+ .isEqualTo(objectMapper.writeValueAsString(dnmpV2kafkaRecordData(TEST_REQUEST_ID)))
}
- companion object {
- val TEST_REQUEST_ID = RequestId("TestId")
- val TEST_PATIENT_PSEUDONYM = PatientPseudonym("PID")
-
- fun dnpmV2MtbFile(): Mtb {
- return Mtb().apply {
- this.patient = dev.pcvolkmer.mv64e.mtb.Patient().apply {
- this.id = "PID"
- this.birthDate = Date.from(Instant.now())
- this.gender = GenderCoding().apply {
- this.code = GenderCodingCode.MALE
- }
- }
- this.episodesOfCare = listOf(
- MtbEpisodeOfCare().apply {
- this.id = "1"
- this.patient = Reference().apply {
- this.id = "PID"
- }
- this.period = PeriodDate().apply {
- this.start = Date.from(Instant.now())
- }
- }
- )
+ @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
}
- }
-
- fun dnmpV2kafkaRecordData(requestId: RequestId): Mtb {
- return DnpmV2MtbFileRequest(requestId, dnpmV2MtbFile()).content
- }
-
- data class TestData(val requestStatus: RequestStatus, val exception: Throwable? = null)
-
- @JvmStatic
- fun requestWithResponseSource(): Set<TestData> {
- return setOf(
- TestData(RequestStatus.UNKNOWN),
- TestData(RequestStatus.ERROR, InterruptedException("Test interrupted")),
- TestData(RequestStatus.ERROR, ExecutionException(RuntimeException("Test execution aborted")))
+ completedFuture(SendResult<String, String>(null, null))
+ }
+ .whenever(kafkaTemplate)
+ .send(any<ProducerRecord<String, String>>())
+
+ kafkaMtbFileSender.send(DnpmV2MtbFileRequest(TEST_REQUEST_ID, dnpmV2MtbFile()))
+
+ 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>>())
+ }
+ }
+
+ companion object {
+ val TEST_REQUEST_ID = RequestId("TestId")
+ val TEST_PATIENT_PSEUDONYM = PatientPseudonym("PID")
+
+ fun dnpmV2MtbFile(): Mtb {
+ return Mtb().apply {
+ this.patient =
+ dev.pcvolkmer.mv64e.mtb.Patient().apply {
+ this.id = "PID"
+ this.birthDate = Date.from(Instant.now())
+ this.gender = GenderCoding().apply { this.code = GenderCodingCode.MALE }
+ }
+ this.episodesOfCare =
+ listOf(
+ MtbEpisodeOfCare().apply {
+ this.id = "1"
+ this.patient = Reference().apply { this.id = "PID" }
+ this.period = PeriodDate().apply { this.start = Date.from(Instant.now()) }
+ }
)
- }
+ }
}
+ fun dnmpV2kafkaRecordData(requestId: RequestId): Mtb {
+ return DnpmV2MtbFileRequest(requestId, dnpmV2MtbFile()).content
+ }
+
+ data class TestData(val requestStatus: RequestStatus, val exception: Throwable? = null)
+
+ @JvmStatic
+ fun requestWithResponseSource(): Set<TestData> {
+ return setOf(
+ TestData(RequestStatus.UNKNOWN),
+ TestData(RequestStatus.ERROR, InterruptedException("Test interrupted")),
+ TestData(
+ RequestStatus.ERROR,
+ ExecutionException(RuntimeException("Test execution aborted")),
+ ),
+ )
+ }
+ }
}
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 1b27a62..2ab0218 100644
--- a/src/test/kotlin/dev/dnpm/etl/processor/output/RestDipMtbFileSenderTest.kt
+++ b/src/test/kotlin/dev/dnpm/etl/processor/output/RestDipMtbFileSenderTest.kt
@@ -30,6 +30,8 @@ import dev.dnpm.etl.processor.config.RestTargetProperties
import dev.dnpm.etl.processor.monitoring.ReportService
import dev.dnpm.etl.processor.monitoring.RequestStatus
import dev.pcvolkmer.mv64e.mtb.*
+import java.time.Instant
+import java.util.*
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Nested
@@ -38,7 +40,6 @@ 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.backoff.NoBackOffPolicy
import org.springframework.retry.policy.SimpleRetryPolicy
import org.springframework.retry.support.RetryTemplateBuilder
@@ -47,224 +48,252 @@ 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
-import java.time.Instant
-import java.util.*
class RestDipMtbFileSenderTest {
- @Nested
- inner class DnpmV2ContentRequest {
+ @Nested
+ inner class DnpmV2ContentRequest {
- private lateinit var mockRestServiceServer: MockRestServiceServer
+ private lateinit var mockRestServiceServer: MockRestServiceServer
- private lateinit var restMtbFileSender: RestMtbFileSender
+ private lateinit var restMtbFileSender: RestMtbFileSender
- private var reportService = ReportService(ObjectMapper().registerModule(KotlinModule.Builder().build()))
+ private var reportService =
+ ReportService(ObjectMapper().registerModule(KotlinModule.Builder().build()))
- @BeforeEach
- fun setup() {
- val restTemplate = RestTemplate()
- val restTargetProperties = RestTargetProperties("http://localhost:9000/api", null, null)
- val retryTemplate = RetryTemplateBuilder().customPolicy(SimpleRetryPolicy(1)).build()
+ @BeforeEach
+ fun setup() {
+ val restTemplate = RestTemplate()
+ val restTargetProperties = RestTargetProperties("http://localhost:9000/api", null, null)
+ 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 shouldReturnExpectedResponseForDnpmV2MtbFilePost(requestWithResponse: RequestWithResponse) {
- this.mockRestServiceServer
- .expect(method(HttpMethod.POST))
- .andExpect(requestTo("http://localhost:9000/api/mtb/etl/patient-record"))
- .andExpect(header(HttpHeaders.CONTENT_TYPE, CustomMediaType.APPLICATION_VND_DNPM_V2_MTB_JSON_VALUE))
- .andRespond {
- withStatus(requestWithResponse.httpStatus).body(requestWithResponse.body).createResponse(it)
- }
+ this.mockRestServiceServer = MockRestServiceServer.createServer(restTemplate)
- val response = restMtbFileSender.send(DnpmV2MtbFileRequest(TEST_REQUEST_ID, dnpmV2MtbFile()))
- assertThat(response.status).isEqualTo(requestWithResponse.response.status)
- assertThat(response.body).isEqualTo(requestWithResponse.response.body)
- }
+ this.restMtbFileSender =
+ RestDipMtbFileSender(restTemplate, restTargetProperties, retryTemplate, reportService)
+ }
+ @ParameterizedTest
+ @MethodSource(
+ "dev.dnpm.etl.processor.output.RestDipMtbFileSenderTest#mtbFileRequestWithResponseSource"
+ )
+ fun shouldReturnExpectedResponseForDnpmV2MtbFilePost(requestWithResponse: RequestWithResponse) {
+ this.mockRestServiceServer
+ .expect(method(HttpMethod.POST))
+ .andExpect(requestTo("http://localhost:9000/api/mtb/etl/patient-record"))
+ .andExpect(
+ header(
+ HttpHeaders.CONTENT_TYPE,
+ CustomMediaType.APPLICATION_VND_DNPM_V2_MTB_JSON_VALUE,
+ )
+ )
+ .andRespond {
+ withStatus(requestWithResponse.httpStatus)
+ .body(requestWithResponse.body)
+ .createResponse(it)
+ }
+
+ val response = restMtbFileSender.send(DnpmV2MtbFileRequest(TEST_REQUEST_ID, dnpmV2MtbFile()))
+ assertThat(response.status).isEqualTo(requestWithResponse.response.status)
+ assertThat(response.body).isEqualTo(requestWithResponse.response.body)
}
+ }
- @Nested
- inner class DeleteRequest {
+ @Nested
+ inner class DeleteRequest {
- private lateinit var mockRestServiceServer: MockRestServiceServer
+ private lateinit var mockRestServiceServer: MockRestServiceServer
- private lateinit var restMtbFileSender: RestMtbFileSender
+ private lateinit var restMtbFileSender: RestMtbFileSender
- private var reportService = ReportService(ObjectMapper().registerModule(KotlinModule.Builder().build()))
+ private var reportService =
+ ReportService(ObjectMapper().registerModule(KotlinModule.Builder().build()))
- @BeforeEach
- fun setup() {
- val restTemplate = RestTemplate()
- val restTargetProperties = RestTargetProperties("http://localhost:9000/api", null, null)
- val retryTemplate = RetryTemplateBuilder().customPolicy(SimpleRetryPolicy(1)).build()
+ @BeforeEach
+ fun setup() {
+ val restTemplate = RestTemplate()
+ val restTargetProperties = RestTargetProperties("http://localhost:9000/api", null, null)
+ val retryTemplate = RetryTemplateBuilder().customPolicy(SimpleRetryPolicy(1)).build()
- this.mockRestServiceServer = MockRestServiceServer.createServer(restTemplate)
+ this.mockRestServiceServer = MockRestServiceServer.createServer(restTemplate)
- this.restMtbFileSender =
- RestDipMtbFileSender(restTemplate, restTargetProperties, retryTemplate, reportService)
- }
+ this.restMtbFileSender =
+ RestDipMtbFileSender(restTemplate, restTargetProperties, retryTemplate, reportService)
+ }
- @ParameterizedTest
- @MethodSource("dev.dnpm.etl.processor.output.RestDipMtbFileSenderTest#deleteRequestWithResponseSource")
- fun shouldReturnExpectedResponseForDelete(requestWithResponse: RequestWithResponse) {
- this.mockRestServiceServer
- .expect(method(HttpMethod.DELETE))
- .andExpect(requestTo("http://localhost:9000/api/mtb/etl/patient/${TEST_PATIENT_PSEUDONYM.value}"))
- .andRespond {
- withStatus(requestWithResponse.httpStatus).body(requestWithResponse.body).createResponse(it)
- }
+ @ParameterizedTest
+ @MethodSource(
+ "dev.dnpm.etl.processor.output.RestDipMtbFileSenderTest#deleteRequestWithResponseSource"
+ )
+ fun shouldReturnExpectedResponseForDelete(requestWithResponse: RequestWithResponse) {
+ this.mockRestServiceServer
+ .expect(method(HttpMethod.DELETE))
+ .andExpect(
+ requestTo("http://localhost:9000/api/mtb/etl/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)
+ }
- 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("dev.dnpm.etl.processor.output.RestDipMtbFileSenderTest#deleteRequestWithResponseSource")
- fun shouldRetryOnDeleteHttpRequestError(requestWithResponse: RequestWithResponse) {
- val restTemplate = RestTemplate()
- val restTargetProperties = RestTargetProperties("http://localhost:9000/api", null, null)
- val retryTemplate = AppConfiguration().retryTemplate(AppConfigProperties())
- 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)
+ @ParameterizedTest
+ @MethodSource(
+ "dev.dnpm.etl.processor.output.RestDipMtbFileSenderTest#deleteRequestWithResponseSource"
+ )
+ fun shouldRetryOnDeleteHttpRequestError(requestWithResponse: RequestWithResponse) {
+ val restTemplate = RestTemplate()
+ val restTargetProperties = RestTargetProperties("http://localhost:9000/api", null, null)
+ val retryTemplate = AppConfiguration().retryTemplate(AppConfigProperties())
+ 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.DELETE))
+ .andExpect(
+ requestTo("http://localhost:9000/api/mtb/etl/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")
+
+ fun dnpmV2MtbFile(): Mtb {
+ return Mtb().apply {
+ this.patient =
+ dev.pcvolkmer.mv64e.mtb.Patient().apply {
+ this.id = "PID"
+ this.birthDate = Date.from(Instant.now())
+ this.gender = GenderCoding().apply { this.code = GenderCodingCode.MALE }
}
-
- this.mockRestServiceServer
- .expect(expectedCount, method(HttpMethod.DELETE))
- .andExpect(requestTo("http://localhost:9000/api/mtb/etl/patient/${TEST_PATIENT_PSEUDONYM.value}"))
- .andRespond {
- withStatus(requestWithResponse.httpStatus).body(requestWithResponse.body).createResponse(it)
+ this.episodesOfCare =
+ listOf(
+ MtbEpisodeOfCare().apply {
+ this.id = "1"
+ this.patient = Reference().apply { this.id = "PID" }
+ this.period = PeriodDate().apply { this.start = Date.from(Instant.now()) }
}
+ )
+ }
+ }
- 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)
- }
-
+ 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.ERROR),
+ MtbFileSender.Response(
+ RequestStatus.ERROR,
+ responseBodyWithMaxSeverity(ReportService.Severity.ERROR),
+ ),
+ ),
+ // 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),
+ ),
+ )
}
- 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")
-
- fun dnpmV2MtbFile(): Mtb {
- return Mtb().apply {
- this.patient = dev.pcvolkmer.mv64e.mtb.Patient().apply {
- this.id = "PID"
- this.birthDate = Date.from(Instant.now())
- this.gender = GenderCoding().apply {
- this.code = GenderCodingCode.MALE
- }
- }
- this.episodesOfCare = listOf(
- MtbEpisodeOfCare().apply {
- this.id = "1"
- this.patient = Reference().apply {
- this.id = "PID"
- }
- this.period = PeriodDate().apply {
- this.start = Date.from(Instant.now())
- }
- }
- )
- }
- }
-
- 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.ERROR),
- MtbFileSender.Response(RequestStatus.ERROR, responseBodyWithMaxSeverity(ReportService.Severity.ERROR))
- ),
- // 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)
- )
- )
- }
+ /**
+ * 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 -> """
+ fun responseBodyWithMaxSeverity(severity: ReportService.Severity): String {
+ return when (severity) {
+ ReportService.Severity.INFO ->
+ """
{
"patient": "PID",
"issues": [
@@ -273,7 +302,8 @@ class RestDipMtbFileSenderTest {
}
"""
- ReportService.Severity.WARNING -> """
+ ReportService.Severity.WARNING ->
+ """
{
"patient": "PID",
"issues": [
@@ -283,7 +313,8 @@ class RestDipMtbFileSenderTest {
}
"""
- ReportService.Severity.ERROR -> """
+ ReportService.Severity.ERROR ->
+ """
{
"patient": "PID",
"issues": [
@@ -294,7 +325,8 @@ class RestDipMtbFileSenderTest {
}
"""
- ReportService.Severity.FATAL -> """
+ ReportService.Severity.FATAL ->
+ """
{
"patient": "PID",
"issues": [
@@ -305,9 +337,7 @@ class RestDipMtbFileSenderTest {
]
}
"""
- }
- }
+ }
}
-
-
+ }
}
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 c302362..a0b3b24 100644
--- a/src/test/kotlin/dev/dnpm/etl/processor/pseudonym/ExtensionsTest.kt
+++ b/src/test/kotlin/dev/dnpm/etl/processor/pseudonym/ExtensionsTest.kt
@@ -28,6 +28,8 @@ import dev.dnpm.etl.processor.consent.MtbFileConsentService
import dev.dnpm.etl.processor.services.ConsentProcessor
import dev.dnpm.etl.processor.services.ConsentProcessorTest
import dev.pcvolkmer.mv64e.mtb.*
+import java.time.Instant
+import java.util.*
import org.assertj.core.api.Assertions.assertThat
import org.hl7.fhir.r4.model.Bundle
import org.junit.jupiter.api.Nested
@@ -40,231 +42,212 @@ import org.mockito.kotlin.anyValueClass
import org.mockito.kotlin.doAnswer
import org.mockito.kotlin.whenever
import org.springframework.core.io.ClassPathResource
-import java.time.Instant
-import java.util.*
@ExtendWith(MockitoExtension::class)
class ExtensionsTest {
- fun getObjectMapper(): ObjectMapper {
- return JacksonConfig().objectMapper()
- }
-
- @Nested
- inner class UsingDnpmV2Datamodel {
+ fun getObjectMapper(): ObjectMapper {
+ return JacksonConfig().objectMapper()
+ }
- val FAKE_MTB_FILE_PATH = "mv64e-mtb-fake-patient.json"
- val CLEAN_PATIENT_ID = "644bae7a-56f6-4ee8-b02f-c532e65af5b1"
+ @Nested
+ inner class UsingDnpmV2Datamodel {
- private fun fakeMtbFile(): Mtb {
- val mtbFile = ClassPathResource(FAKE_MTB_FILE_PATH).inputStream
- return getObjectMapper().readValue(mtbFile, Mtb::class.java)
- }
+ val FAKE_MTB_FILE_PATH = "mv64e-mtb-fake-patient.json"
+ val CLEAN_PATIENT_ID = "644bae7a-56f6-4ee8-b02f-c532e65af5b1"
- private fun Mtb.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.ensureMetaDataIsInitialized()
- addConsentData(mtbFile)
+ private fun fakeMtbFile(): Mtb {
+ val mtbFile = ClassPathResource(FAKE_MTB_FILE_PATH).inputStream
+ return getObjectMapper().readValue(mtbFile, Mtb::class.java)
+ }
- mtbFile.pseudonymizeWith(pseudonymizeService)
+ private fun Mtb.serialized(): String {
+ return getObjectMapper().writeValueAsString(this)
+ }
- assertThat(mtbFile.patient.id).isEqualTo("PSEUDO-ID")
- assertThat(mtbFile.serialized()).doesNotContain(CLEAN_PATIENT_ID)
- }
+ @Test
+ fun shouldNotContainCleanPatientId(@Mock pseudonymizeService: PseudonymizeService) {
+ doAnswer {
+ it.arguments[0]
+ "PSEUDO-ID"
+ }
+ .whenever(pseudonymizeService)
+ .patientPseudonym(anyValueClass())
- private fun addConsentData(mtbFile: Mtb) {
- val gIcsConfigProperties = GIcsConfigProperties("", "", "")
- val appConfigProperties = AppConfigProperties(emptyList())
+ val mtbFile = fakeMtbFile()
+ mtbFile.ensureMetaDataIsInitialized()
+ addConsentData(mtbFile)
- val bundle = Bundle()
- val dummyConsent = ConsentProcessorTest.getDummyGenomDeConsent()
- dummyConsent.patient.reference = "Patient/$CLEAN_PATIENT_ID"
- bundle.addEntry().resource = dummyConsent
+ mtbFile.pseudonymizeWith(pseudonymizeService)
- ConsentProcessor(
- appConfigProperties,
- gIcsConfigProperties,
- JacksonConfig().objectMapper(),
- FhirContext.forR4(),
- MtbFileConsentService()
- ).embedBroadConsentResources(mtbFile, bundle)
+ assertThat(mtbFile.patient.id).isEqualTo("PSEUDO-ID")
+ assertThat(mtbFile.serialized()).doesNotContain(CLEAN_PATIENT_ID)
+ }
- }
+ private fun addConsentData(mtbFile: Mtb) {
+ val gIcsConfigProperties = GIcsConfigProperties("", "", "")
+ val appConfigProperties = AppConfigProperties(emptyList())
+
+ val bundle = Bundle()
+ val dummyConsent = ConsentProcessorTest.getDummyGenomDeConsent()
+ dummyConsent.patient.reference = "Patient/$CLEAN_PATIENT_ID"
+ bundle.addEntry().resource = dummyConsent
+
+ ConsentProcessor(
+ appConfigProperties,
+ gIcsConfigProperties,
+ JacksonConfig().objectMapper(),
+ FhirContext.forR4(),
+ MtbFileConsentService(),
+ )
+ .embedBroadConsentResources(mtbFile, bundle)
+ }
- @Test
- fun shouldNotThrowExceptionOnNullValues(@Mock pseudonymizeService: PseudonymizeService) {
- doAnswer {
- it.arguments[0]
- "PSEUDO-ID"
- }.whenever(pseudonymizeService).patientPseudonym(anyValueClass())
-
- doAnswer {
- "TESTDOMAIN"
- }.whenever(pseudonymizeService).prefix()
-
- val mtbFile = Mtb().apply {
- this.patient = Patient().apply {
- this.id = "PID"
- this.birthDate = Date.from(Instant.now())
- this.gender = GenderCoding().apply {
- this.code = GenderCodingCode.MALE
- }
+ @Test
+ fun shouldNotThrowExceptionOnNullValues(@Mock pseudonymizeService: PseudonymizeService) {
+ doAnswer {
+ it.arguments[0]
+ "PSEUDO-ID"
+ }
+ .whenever(pseudonymizeService)
+ .patientPseudonym(anyValueClass())
+
+ doAnswer { "TESTDOMAIN" }.whenever(pseudonymizeService).prefix()
+
+ val mtbFile =
+ Mtb().apply {
+ this.patient =
+ Patient().apply {
+ this.id = "PID"
+ this.birthDate = Date.from(Instant.now())
+ this.gender = GenderCoding().apply { this.code = GenderCodingCode.MALE }
}
- this.episodesOfCare = listOf(
+ this.episodesOfCare =
+ listOf(
MtbEpisodeOfCare().apply {
- this.id = "1"
- this.patient = Reference().apply {
- this.id = "PID"
- }
- this.period = PeriodDate().apply {
- this.start = Date.from(Instant.now())
- }
+ this.id = "1"
+ this.patient = Reference().apply { this.id = "PID" }
+ this.period = PeriodDate().apply { this.start = Date.from(Instant.now()) }
}
)
- }
+ }
- mtbFile.pseudonymizeWith(pseudonymizeService)
- mtbFile.anonymizeContentWith(pseudonymizeService)
+ mtbFile.pseudonymizeWith(pseudonymizeService)
+ mtbFile.anonymizeContentWith(pseudonymizeService)
- assertThat(mtbFile.episodesOfCare).hasSize(1)
- assertThat(mtbFile.episodesOfCare.map { it.id }).isNotNull
- }
-
- @Test
- fun shouldNotContainAnyUuidAfterRehashingOfIds(@Mock pseudonymizeService: PseudonymizeService) {
- doAnswer {
- it.arguments[0]
- "PSEUDO-ID"
- }.whenever(pseudonymizeService).patientPseudonym(anyValueClass())
-
- doAnswer {
- "TESTDOMAIN"
- }.whenever(pseudonymizeService).prefix()
-
- val mtbFile = fakeMtbFile()
-
- /**
- * replace hex values with random long, so our test does not match false positives
- */
- mtbFile.ngsReports.forEach { report ->
- report.results.simpleVariants.forEach { simpleVariant ->
- simpleVariant.externalIds.forEach { extIdValue ->
- extIdValue.value =
- Math.random().toLong().toString()
- }
- }
- }
- mtbFile.ngsReports.forEach { report ->
- report.results.rnaFusions.forEach { simpleVariant ->
- simpleVariant.externalIds.forEach { extIdValue ->
- extIdValue.value =
- Math.random().toLong().toString()
- }
- simpleVariant.fusionPartner3Prime?.transcriptId?.value =
- Math.random().toLong().toString()
- simpleVariant.fusionPartner5Prime?.transcriptId?.value =
- Math.random().toLong().toString()
- simpleVariant.externalIds?.forEach {
- it?.value = Math.random().toLong().toString()
- }
- }
- }
-
- 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 input = mtbFile.serialized()
- val matcher = pattern.matcher(input)
-
- assertThrows<IllegalStateException> {
- matcher.find()
- val posSt = "check at pos: " + matcher.start().toString() + ", " + matcher.end()
- println(posSt + " with " + matcher.group())
- }.also {
- assertThat(it.message).isEqualTo("No match found")
- }
- }
+ assertThat(mtbFile.episodesOfCare).hasSize(1)
+ assertThat(mtbFile.episodesOfCare.map { it.id }).isNotNull
}
@Test
- fun shouldUseSameAnonymIdForDiagnosisAndDiagnosisReferences(@Mock pseudonymizeService: PseudonymizeService) {
-
- doAnswer {
+ fun shouldNotContainAnyUuidAfterRehashingOfIds(@Mock pseudonymizeService: PseudonymizeService) {
+ doAnswer {
it.arguments[0]
"PSEUDO-ID"
- }.whenever(pseudonymizeService).patientPseudonym(anyValueClass())
+ }
+ .whenever(pseudonymizeService)
+ .patientPseudonym(anyValueClass())
+
+ doAnswer { "TESTDOMAIN" }.whenever(pseudonymizeService).prefix()
- doAnswer {
- "TESTDOMAIN"
- }.whenever(pseudonymizeService).prefix()
+ val mtbFile = fakeMtbFile()
- val mtbFile = Mtb().apply {
- this.patient = Patient().apply {
+ /** replace hex values with random long, so our test does not match false positives */
+ mtbFile.ngsReports.forEach { report ->
+ report.results.simpleVariants.forEach { simpleVariant ->
+ simpleVariant.externalIds.forEach { extIdValue ->
+ extIdValue.value = Math.random().toLong().toString()
+ }
+ }
+ }
+ mtbFile.ngsReports.forEach { report ->
+ report.results.rnaFusions.forEach { simpleVariant ->
+ simpleVariant.externalIds.forEach { extIdValue ->
+ extIdValue.value = Math.random().toLong().toString()
+ }
+ simpleVariant.fusionPartner3Prime?.transcriptId?.value = Math.random().toLong().toString()
+ simpleVariant.fusionPartner5Prime?.transcriptId?.value = Math.random().toLong().toString()
+ simpleVariant.externalIds?.forEach { it?.value = Math.random().toLong().toString() }
+ }
+ }
+
+ 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 input = mtbFile.serialized()
+ val matcher = pattern.matcher(input)
+
+ assertThrows<IllegalStateException> {
+ matcher.find()
+ val posSt = "check at pos: " + matcher.start().toString() + ", " + matcher.end()
+ println(posSt + " with " + matcher.group())
+ }
+ .also { assertThat(it.message).isEqualTo("No match found") }
+ }
+ }
+
+ @Test
+ fun shouldUseSameAnonymIdForDiagnosisAndDiagnosisReferences(
+ @Mock pseudonymizeService: PseudonymizeService
+ ) {
+
+ doAnswer {
+ it.arguments[0]
+ "PSEUDO-ID"
+ }
+ .whenever(pseudonymizeService)
+ .patientPseudonym(anyValueClass())
+
+ doAnswer { "TESTDOMAIN" }.whenever(pseudonymizeService).prefix()
+
+ val mtbFile =
+ Mtb().apply {
+ this.patient =
+ Patient().apply {
this.id = "PID"
this.birthDate = Date.from(Instant.now())
- this.gender = GenderCoding().apply {
- this.code = GenderCodingCode.MALE
- }
- }
- this.diagnoses = listOf(
- MtbDiagnosis().apply {
- this.id = "Diagnosis-1"
- }
- )
- this.episodesOfCare = listOf(
- MtbEpisodeOfCare().apply {
+ this.gender = GenderCoding().apply { this.code = GenderCodingCode.MALE }
+ }
+ this.diagnoses = listOf(MtbDiagnosis().apply { this.id = "Diagnosis-1" })
+ this.episodesOfCare =
+ listOf(
+ MtbEpisodeOfCare().apply {
this.id = "Episode-1"
- this.diagnoses = listOf(
- Reference().apply {
- this.id = "Diagnosis-1"
- }
- )
- }
- )
- this.guidelineTherapies = listOf(
- MtbSystemicTherapy().apply {
+ this.diagnoses = listOf(Reference().apply { this.id = "Diagnosis-1" })
+ }
+ )
+ this.guidelineTherapies =
+ listOf(
+ MtbSystemicTherapy().apply {
this.id = "Systemic-Therapy-1"
- this.reason = Reference().apply {
- this.id = "Diagnosis-1"
- }
- }
- )
- this.guidelineProcedures = listOf(
- OncoProcedure().apply {
+ this.reason = Reference().apply { this.id = "Diagnosis-1" }
+ }
+ )
+ this.guidelineProcedures =
+ listOf(
+ OncoProcedure().apply {
this.id = "Onco-Procedure-1"
- this.reason = Reference().apply {
- this.id = "Diagnosis-1"
- }
- }
- )
- this.specimens = listOf(
- TumorSpecimen().apply {
+ this.reason = Reference().apply { this.id = "Diagnosis-1" }
+ }
+ )
+ this.specimens =
+ listOf(
+ TumorSpecimen().apply {
this.id = "Specimen-1"
- this.diagnosis = Reference().apply {
- this.id = "Diagnosis-1"
- }
- }
- )
+ this.diagnosis = Reference().apply { this.id = "Diagnosis-1" }
+ }
+ )
}
- mtbFile.pseudonymizeWith(pseudonymizeService)
- mtbFile.anonymizeContentWith(pseudonymizeService)
+ mtbFile.pseudonymizeWith(pseudonymizeService)
+ mtbFile.anonymizeContentWith(pseudonymizeService)
- assertThat(mtbFile.diagnoses.first().id).isEqualTo(mtbFile.episodesOfCare.first().diagnoses.first().id)
- assertThat(mtbFile.diagnoses.first().id).isEqualTo(mtbFile.guidelineTherapies.first().reason.id)
- assertThat(mtbFile.diagnoses.first().id).isEqualTo(mtbFile.guidelineProcedures.first().reason.id)
- assertThat(mtbFile.diagnoses.first().id).isEqualTo(mtbFile.specimens.first().diagnosis.id)
- }
+ assertThat(mtbFile.diagnoses.first().id)
+ .isEqualTo(mtbFile.episodesOfCare.first().diagnoses.first().id)
+ assertThat(mtbFile.diagnoses.first().id).isEqualTo(mtbFile.guidelineTherapies.first().reason.id)
+ assertThat(mtbFile.diagnoses.first().id)
+ .isEqualTo(mtbFile.guidelineProcedures.first().reason.id)
+ assertThat(mtbFile.diagnoses.first().id).isEqualTo(mtbFile.specimens.first().diagnosis.id)
+ }
}
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 8ee19bc..7da0247 100644
--- a/src/test/kotlin/dev/dnpm/etl/processor/pseudonym/PseudonymizeServiceTest.kt
+++ b/src/test/kotlin/dev/dnpm/etl/processor/pseudonym/PseudonymizeServiceTest.kt
@@ -21,6 +21,8 @@ package dev.dnpm.etl.processor.pseudonym
import dev.dnpm.etl.processor.config.PseudonymizeConfigProperties
import dev.pcvolkmer.mv64e.mtb.*
+import java.time.Instant
+import java.util.*
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
@@ -29,72 +31,67 @@ 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 = Mtb.builder()
- .patient(
- Patient.builder()
- .id("123")
- .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()
-
- @Test
- fun shouldNotUsePseudonymPrefixForGpas(@Mock generator: GpasPseudonymGenerator) {
- doAnswer {
- it.arguments[0]
- }.whenever(generator).generate(anyString())
-
- val pseudonymizeService = PseudonymizeService(generator, PseudonymizeConfigProperties())
-
- mtbFile.pseudonymizeWith(pseudonymizeService)
-
- assertThat(mtbFile.patient.id).isEqualTo("123")
- }
+ private val mtbFile =
+ Mtb.builder()
+ .patient(Patient.builder().id("123").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()
- @Test
- fun sanitizeFileName() {
- val result = GpasPseudonymGenerator.sanitizeValue("l://a\\bs;1*2?3>")
+ @Test
+ fun shouldNotUsePseudonymPrefixForGpas(@Mock generator: GpasPseudonymGenerator) {
+ doAnswer { it.arguments[0] }.whenever(generator).generate(anyString())
- assertThat(result).isEqualTo("l___a_bs_1_2_3_")
- }
+ val pseudonymizeService = PseudonymizeService(generator, PseudonymizeConfigProperties())
- @Test
- fun shouldUsePseudonymPrefixForBuiltin(@Mock generator: AnonymizingGenerator) {
- doAnswer {
- it.arguments[0]
- }.whenever(generator).generate(anyString())
+ mtbFile.pseudonymizeWith(pseudonymizeService)
- val pseudonymizeService = PseudonymizeService(generator, PseudonymizeConfigProperties())
+ assertThat(mtbFile.patient.id).isEqualTo("123")
+ }
- mtbFile.pseudonymizeWith(pseudonymizeService)
+ @Test
+ fun sanitizeFileName() {
+ val result = GpasPseudonymGenerator.sanitizeValue("l://a\\bs;1*2?3>")
- assertThat(mtbFile.patient.id).isEqualTo("UNKNOWN_123")
- }
+ assertThat(result).isEqualTo("l___a_bs_1_2_3_")
+ }
+
+ @Test
+ fun shouldUsePseudonymPrefixForBuiltin(@Mock generator: AnonymizingGenerator) {
+ doAnswer { it.arguments[0] }.whenever(generator).generate(anyString())
+
+ val pseudonymizeService = PseudonymizeService(generator, PseudonymizeConfigProperties())
+
+ mtbFile.pseudonymizeWith(pseudonymizeService)
+
+ assertThat(mtbFile.patient.id).isEqualTo("UNKNOWN_123")
+ }
- @Test
- fun shouldReturnDifferentValues() {
- val ag = AnonymizingGenerator()
+ @Test
+ fun shouldReturnDifferentValues() {
+ val ag = AnonymizingGenerator()
- val tans = HashSet<String>()
+ val tans = HashSet<String>()
- (1..1000).forEach { i ->
- val tan = ag.generateGenomDeTan("12345")
- assertThat(tan).hasSize(64)
- assertThat(tans.add(tan)).`as`("never the same result!").isTrue
- }
+ (1..1000).forEach { i ->
+ val tan = ag.generateGenomDeTan("12345")
+ assertThat(tan).hasSize(64)
+ assertThat(tans.add(tan)).`as`("never the same result!").isTrue
}
+ }
}
diff --git a/src/test/kotlin/dev/dnpm/etl/processor/security/TokenServiceTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/security/TokenServiceTest.kt
index b93e9f5..e9a1650 100644
--- a/src/test/kotlin/dev/dnpm/etl/processor/security/TokenServiceTest.kt
+++ b/src/test/kotlin/dev/dnpm/etl/processor/security/TokenServiceTest.kt
@@ -19,6 +19,8 @@
package dev.dnpm.etl.processor.security
+import java.util.*
+import java.util.function.Consumer
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
@@ -30,124 +32,128 @@ import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.*
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.security.provisioning.InMemoryUserDetailsManager
-import java.util.*
-import java.util.function.Consumer
@ExtendWith(MockitoExtension::class)
class TokenServiceTest {
- private lateinit var userDetailsManager: InMemoryUserDetailsManager
- private lateinit var passwordEncoder: PasswordEncoder
- private lateinit var tokenRepository: TokenRepository
+ private lateinit var userDetailsManager: InMemoryUserDetailsManager
+ private lateinit var passwordEncoder: PasswordEncoder
+ private lateinit var tokenRepository: TokenRepository
- private lateinit var tokenService: TokenService
+ private lateinit var tokenService: TokenService
- @BeforeEach
- fun setup(
- @Mock userDetailsManager: InMemoryUserDetailsManager,
- @Mock passwordEncoder: PasswordEncoder,
- @Mock tokenRepository: TokenRepository
- ) {
- this.userDetailsManager = userDetailsManager
- this.passwordEncoder = passwordEncoder
- this.tokenRepository = tokenRepository
+ @BeforeEach
+ fun setup(
+ @Mock userDetailsManager: InMemoryUserDetailsManager,
+ @Mock passwordEncoder: PasswordEncoder,
+ @Mock tokenRepository: TokenRepository,
+ ) {
+ this.userDetailsManager = userDetailsManager
+ this.passwordEncoder = passwordEncoder
+ this.tokenRepository = tokenRepository
- this.tokenService = TokenService(userDetailsManager, passwordEncoder, tokenRepository)
- }
+ this.tokenService = TokenService(userDetailsManager, passwordEncoder, tokenRepository)
+ }
- @Test
- fun shouldEncodePasswordForNewToken() {
- doAnswer { "{test}verysecret" }.whenever(passwordEncoder).encode(anyString())
+ @Test
+ fun shouldEncodePasswordForNewToken() {
+ doAnswer { "{test}verysecret" }.whenever(passwordEncoder).encode(anyString())
- val actual = this.tokenService.addToken("Test Token")
+ val actual = this.tokenService.addToken("Test Token")
- assertThat(actual).satisfies(
+ assertThat(actual)
+ .satisfies(
Consumer { assertThat(it.isSuccess).isTrue() },
- Consumer { assertThat(it.getOrNull()).matches("testtoken:[A-Za-z0-9]{48}$") }
+ Consumer { assertThat(it.getOrNull()).matches("testtoken:[A-Za-z0-9]{48}$") },
)
- }
+ }
- @Test
- fun shouldContainAlphanumTokenUserPart() {
- doAnswer { "{test}verysecret" }.whenever(passwordEncoder).encode(anyString())
+ @Test
+ fun shouldContainAlphanumTokenUserPart() {
+ doAnswer { "{test}verysecret" }.whenever(passwordEncoder).encode(anyString())
- val actual = this.tokenService.addToken("Test Token")
+ val actual = this.tokenService.addToken("Test Token")
- assertThat(actual).satisfies(
+ assertThat(actual)
+ .satisfies(
Consumer { assertThat(it.isSuccess).isTrue() },
- Consumer { assertThat(it.getOrDefault("")).startsWith("testtoken:") }
+ Consumer { assertThat(it.getOrDefault("")).startsWith("testtoken:") },
)
- }
+ }
- @Test
- fun shouldNotAllowSameTokenUserPartTwice() {
- doReturn(true).whenever(userDetailsManager).userExists(anyString())
+ @Test
+ fun shouldNotAllowSameTokenUserPartTwice() {
+ doReturn(true).whenever(userDetailsManager).userExists(anyString())
- val actual = this.tokenService.addToken("Test Token")
+ val actual = this.tokenService.addToken("Test Token")
- assertThat(actual).satisfies(Consumer { assertThat(it.isFailure).isTrue() })
- verify(tokenRepository, never()).save(any())
- }
+ assertThat(actual).satisfies(Consumer { assertThat(it.isFailure).isTrue() })
+ verify(tokenRepository, never()).save(any())
+ }
- @Test
- fun shouldSaveNewToken() {
- doAnswer { "{test}verysecret" }.whenever(passwordEncoder).encode(anyString())
+ @Test
+ fun shouldSaveNewToken() {
+ doAnswer { "{test}verysecret" }.whenever(passwordEncoder).encode(anyString())
- val actual = this.tokenService.addToken("Test Token")
+ val actual = this.tokenService.addToken("Test Token")
- val captor = argumentCaptor<Token>()
- verify(tokenRepository, times(1)).save(captor.capture())
+ val captor = argumentCaptor<Token>()
+ verify(tokenRepository, times(1)).save(captor.capture())
- assertThat(actual).satisfies(Consumer { assertThat(it.isSuccess).isTrue() })
- assertThat(captor.firstValue).satisfies(
+ assertThat(actual).satisfies(Consumer { assertThat(it.isSuccess).isTrue() })
+ assertThat(captor.firstValue)
+ .satisfies(
Consumer { assertThat(it.name).isEqualTo("Test Token") },
Consumer { assertThat(it.username).isEqualTo("testtoken") },
- Consumer { assertThat(it.password).isEqualTo("{test}verysecret") }
+ Consumer { assertThat(it.password).isEqualTo("{test}verysecret") },
)
- }
-
- @Test
- fun shouldDeleteExistingToken() {
- doAnswer {
- val id = it.arguments[0] as Long
- Optional.of(Token(id, "Test Token", "testtoken", "{test}hsdajfgadskjhfgsdkfjg"))
- }.whenever(tokenRepository).findById(anyLong())
-
- this.tokenService.deleteToken(42)
-
- val stringCaptor = argumentCaptor<String>()
- verify(userDetailsManager, times(1)).deleteUser(stringCaptor.capture())
- assertThat(stringCaptor.firstValue).isEqualTo("testtoken")
-
- val tokenCaptor = argumentCaptor<Token>()
- verify(tokenRepository, times(1)).delete(tokenCaptor.capture())
- assertThat(tokenCaptor.firstValue.id).isEqualTo(42)
- }
-
- @Test
- fun shouldReturnAllTokensFromRepository() {
- val expected = listOf(
+ }
+
+ @Test
+ fun shouldDeleteExistingToken() {
+ doAnswer {
+ val id = it.arguments[0] as Long
+ Optional.of(Token(id, "Test Token", "testtoken", "{test}hsdajfgadskjhfgsdkfjg"))
+ }
+ .whenever(tokenRepository)
+ .findById(anyLong())
+
+ this.tokenService.deleteToken(42)
+
+ val stringCaptor = argumentCaptor<String>()
+ verify(userDetailsManager, times(1)).deleteUser(stringCaptor.capture())
+ assertThat(stringCaptor.firstValue).isEqualTo("testtoken")
+
+ val tokenCaptor = argumentCaptor<Token>()
+ verify(tokenRepository, times(1)).delete(tokenCaptor.capture())
+ assertThat(tokenCaptor.firstValue.id).isEqualTo(42)
+ }
+
+ @Test
+ fun shouldReturnAllTokensFromRepository() {
+ val expected =
+ listOf(
Token(1, "Test Token 1", "testtoken1", "{test}hsdajfgadskjhfgsdkfjg"),
- Token(2, "Test Token 2", "testtoken2", "{test}asdasdasdasdasdasdasd")
+ Token(2, "Test Token 2", "testtoken2", "{test}asdasdasdasdasdasdasd"),
)
- doReturn(expected).whenever(tokenRepository).findAll()
+ doReturn(expected).whenever(tokenRepository).findAll()
- assertThat(tokenService.findAll()).isEqualTo(expected)
- }
+ assertThat(tokenService.findAll()).isEqualTo(expected)
+ }
- @Test
- fun shouldAddAllTokensFromRepositoryToUserDataManager() {
- val expected = listOf(
+ @Test
+ fun shouldAddAllTokensFromRepositoryToUserDataManager() {
+ val expected =
+ listOf(
Token(1, "Test Token 1", "testtoken1", "{test}hsdajfgadskjhfgsdkfjg"),
- Token(2, "Test Token 2", "testtoken2", "{test}asdasdasdasdasdasdasd")
+ Token(2, "Test Token 2", "testtoken2", "{test}asdasdasdasdasdasdasd"),
)
- doReturn(expected).whenever(tokenRepository).findAll()
-
- tokenService.setup()
+ doReturn(expected).whenever(tokenRepository).findAll()
- verify(userDetailsManager, times(expected.size)).createUser(any())
- }
+ tokenService.setup()
-} \ No newline at end of file
+ verify(userDetailsManager, times(expected.size)).createUser(any())
+ }
+}
diff --git a/src/test/kotlin/dev/dnpm/etl/processor/security/UserRoleServiceTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/security/UserRoleServiceTest.kt
index 39ba7c0..7743069 100644
--- a/src/test/kotlin/dev/dnpm/etl/processor/security/UserRoleServiceTest.kt
+++ b/src/test/kotlin/dev/dnpm/etl/processor/security/UserRoleServiceTest.kt
@@ -19,6 +19,8 @@
package dev.dnpm.etl.processor.security
+import java.time.Instant
+import java.util.*
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Nested
@@ -32,161 +34,148 @@ import org.springframework.security.core.session.SessionRegistry
import org.springframework.security.oauth2.core.oidc.OidcIdToken
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser
import org.springframework.security.oauth2.core.oidc.user.OidcUser
-import java.time.Instant
-import java.util.*
@ExtendWith(MockitoExtension::class)
class UserRoleServiceTest {
- private lateinit var userRoleRepository: UserRoleRepository
- private lateinit var sessionRegistry: SessionRegistry
+ private lateinit var userRoleRepository: UserRoleRepository
+ private lateinit var sessionRegistry: SessionRegistry
- private lateinit var userRoleService: UserRoleService
+ private lateinit var userRoleService: UserRoleService
- @BeforeEach
- fun setup(
- @Mock userRoleRepository: UserRoleRepository,
- @Mock sessionRegistry: SessionRegistry
- ) {
- this.userRoleRepository = userRoleRepository
- this.sessionRegistry = sessionRegistry
-
- this.userRoleService = UserRoleService(userRoleRepository, sessionRegistry)
- }
+ @BeforeEach
+ fun setup(@Mock userRoleRepository: UserRoleRepository, @Mock sessionRegistry: SessionRegistry) {
+ this.userRoleRepository = userRoleRepository
+ this.sessionRegistry = sessionRegistry
- @Test
- fun shouldDelegateFindAllToRepository() {
- userRoleService.findAll()
+ this.userRoleService = UserRoleService(userRoleRepository, sessionRegistry)
+ }
- verify(userRoleRepository, times(1)).findAll()
- }
+ @Test
+ fun shouldDelegateFindAllToRepository() {
+ userRoleService.findAll()
- @Nested
- inner class WithExistingUserRole {
+ verify(userRoleRepository, times(1)).findAll()
+ }
- @BeforeEach
- fun setup() {
- doAnswer { invocation ->
- Optional.of(
- UserRole(invocation.getArgument(0), "patrick.tester", Role.USER)
- )
- }.whenever(userRoleRepository).findById(any<Long>())
+ @Nested
+ inner class WithExistingUserRole {
- doAnswer { _ ->
- listOf(
- dummyPrincipal()
- )
- }.whenever(sessionRegistry).allPrincipals
- }
+ @BeforeEach
+ fun setup() {
+ doAnswer { invocation ->
+ Optional.of(UserRole(invocation.getArgument(0), "patrick.tester", Role.USER))
+ }
+ .whenever(userRoleRepository)
+ .findById(any<Long>())
+
+ doAnswer { _ -> listOf(dummyPrincipal()) }.whenever(sessionRegistry).allPrincipals
+ }
- @Test
- fun shouldUpdateUserRole() {
- userRoleService.updateUserRole(1, Role.ADMIN)
+ @Test
+ fun shouldUpdateUserRole() {
+ userRoleService.updateUserRole(1, Role.ADMIN)
- val userRoleCaptor = argumentCaptor<UserRole>()
- verify(userRoleRepository, times(1)).save(userRoleCaptor.capture())
+ val userRoleCaptor = argumentCaptor<UserRole>()
+ verify(userRoleRepository, times(1)).save(userRoleCaptor.capture())
- assertThat(userRoleCaptor.firstValue.id).isEqualTo(1L)
- assertThat(userRoleCaptor.firstValue.role).isEqualTo(Role.ADMIN)
- }
+ assertThat(userRoleCaptor.firstValue.id).isEqualTo(1L)
+ assertThat(userRoleCaptor.firstValue.role).isEqualTo(Role.ADMIN)
+ }
- @Test
- fun shouldExpireSessionOnUpdate() {
- val dummySessions = dummySessions()
- whenever(sessionRegistry.getAllSessions(any(), any<Boolean>())).thenReturn(
- dummySessions
- )
+ @Test
+ fun shouldExpireSessionOnUpdate() {
+ val dummySessions = dummySessions()
+ whenever(sessionRegistry.getAllSessions(any(), any<Boolean>())).thenReturn(dummySessions)
- assertThat(dummySessions.filter { it.isExpired }).hasSize(0)
+ assertThat(dummySessions.filter { it.isExpired }).hasSize(0)
- userRoleService.updateUserRole(1, Role.ADMIN)
+ userRoleService.updateUserRole(1, Role.ADMIN)
- verify(sessionRegistry, times(1)).getAllSessions(any<OidcUser>(), any<Boolean>())
+ verify(sessionRegistry, times(1)).getAllSessions(any<OidcUser>(), any<Boolean>())
- assertThat(dummySessions.filter { it.isExpired }).hasSize(2)
- }
+ assertThat(dummySessions.filter { it.isExpired }).hasSize(2)
+ }
- @Test
- fun shouldDeleteUserRole() {
- userRoleService.deleteUserRole(1)
+ @Test
+ fun shouldDeleteUserRole() {
+ userRoleService.deleteUserRole(1)
- val userRoleCaptor = argumentCaptor<UserRole>()
- verify(userRoleRepository, times(1)).delete(userRoleCaptor.capture())
+ val userRoleCaptor = argumentCaptor<UserRole>()
+ verify(userRoleRepository, times(1)).delete(userRoleCaptor.capture())
- assertThat(userRoleCaptor.firstValue.id).isEqualTo(1L)
- assertThat(userRoleCaptor.firstValue.role).isEqualTo(Role.USER)
- }
+ assertThat(userRoleCaptor.firstValue.id).isEqualTo(1L)
+ assertThat(userRoleCaptor.firstValue.role).isEqualTo(Role.USER)
+ }
- @Test
- fun shouldExpireSessionOnDelete() {
- val dummySessions = dummySessions()
- whenever(sessionRegistry.getAllSessions(any(), any<Boolean>())).thenReturn(
- dummySessions
- )
+ @Test
+ fun shouldExpireSessionOnDelete() {
+ val dummySessions = dummySessions()
+ whenever(sessionRegistry.getAllSessions(any(), any<Boolean>())).thenReturn(dummySessions)
- assertThat(dummySessions.filter { it.isExpired }).hasSize(0)
+ assertThat(dummySessions.filter { it.isExpired }).hasSize(0)
- userRoleService.deleteUserRole(1)
+ userRoleService.deleteUserRole(1)
- verify(sessionRegistry, times(1)).getAllSessions(any<OidcUser>(), any<Boolean>())
+ verify(sessionRegistry, times(1)).getAllSessions(any<OidcUser>(), any<Boolean>())
- assertThat(dummySessions.filter { it.isExpired }).hasSize(2)
- }
+ assertThat(dummySessions.filter { it.isExpired }).hasSize(2)
}
+ }
- @Nested
- inner class WithoutExistingUserRole {
-
- @BeforeEach
- fun setup() {
- doAnswer { _ ->
- Optional.empty<UserRole>()
- }.whenever(userRoleRepository).findById(any<Long>())
- }
+ @Nested
+ inner class WithoutExistingUserRole {
- @Test
- fun shouldNotUpdateUserRole() {
- userRoleService.updateUserRole(1, Role.ADMIN)
+ @BeforeEach
+ fun setup() {
+ doAnswer { _ -> Optional.empty<UserRole>() }
+ .whenever(userRoleRepository)
+ .findById(any<Long>())
+ }
- verify(userRoleRepository, never()).save(any<UserRole>())
- }
+ @Test
+ fun shouldNotUpdateUserRole() {
+ userRoleService.updateUserRole(1, Role.ADMIN)
- @Test
- fun shouldNotExpireSessionOnUpdate() {
- userRoleService.updateUserRole(1, Role.ADMIN)
+ verify(userRoleRepository, never()).save(any<UserRole>())
+ }
- verify(sessionRegistry, never()).getAllSessions(any<OidcUser>(), any<Boolean>())
- }
+ @Test
+ fun shouldNotExpireSessionOnUpdate() {
+ userRoleService.updateUserRole(1, Role.ADMIN)
- @Test
- fun shouldNotDeleteUserRole() {
- userRoleService.deleteUserRole(1)
+ verify(sessionRegistry, never()).getAllSessions(any<OidcUser>(), any<Boolean>())
+ }
- verify(userRoleRepository, never()).delete(any<UserRole>())
- }
+ @Test
+ fun shouldNotDeleteUserRole() {
+ userRoleService.deleteUserRole(1)
- @Test
- fun shouldNotExpireSessionOnDelete() {
- userRoleService.deleteUserRole(1)
+ verify(userRoleRepository, never()).delete(any<UserRole>())
+ }
- verify(sessionRegistry, never()).getAllSessions(any<OidcUser>(), any<Boolean>())
- }
+ @Test
+ fun shouldNotExpireSessionOnDelete() {
+ userRoleService.deleteUserRole(1)
+ verify(sessionRegistry, never()).getAllSessions(any<OidcUser>(), any<Boolean>())
}
+ }
-
- companion object {
- private fun dummyPrincipal() = DefaultOidcUser(
+ companion object {
+ private fun dummyPrincipal() =
+ DefaultOidcUser(
listOf(),
OidcIdToken(
"anytokenvalue",
Instant.now(),
Instant.now().plusSeconds(10),
- mapOf("sub" to "testsub", "preferred_username" to "patrick.tester")
- )
+ mapOf("sub" to "testsub", "preferred_username" to "patrick.tester"),
+ ),
)
- private fun dummySessions() = listOf(
+ private fun dummySessions() =
+ listOf(
SessionInformation(
dummyPrincipal(),
"SESSIONID1",
@@ -196,7 +185,7 @@ class UserRoleServiceTest {
dummyPrincipal(),
"SESSIONID2",
Date.from(Instant.now()),
- )
+ ),
)
- }
-} \ 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 bbc8b1a..3e54e71 100644
--- a/src/test/kotlin/dev/dnpm/etl/processor/services/ConsentProcessorTest.kt
+++ b/src/test/kotlin/dev/dnpm/etl/processor/services/ConsentProcessorTest.kt
@@ -10,6 +10,11 @@ import dev.dnpm.etl.processor.consent.GicsConsentService
import dev.pcvolkmer.mv64e.mtb.Mtb
import dev.pcvolkmer.mv64e.mtb.MvhSubmissionType
import dev.pcvolkmer.mv64e.mtb.Patient
+import java.io.IOException
+import java.io.InputStream
+import java.time.Instant
+import java.time.OffsetDateTime
+import java.util.*
import org.assertj.core.api.Assertions.assertThat
import org.hl7.fhir.r4.model.Bundle
import org.hl7.fhir.r4.model.CodeableConcept
@@ -28,205 +33,194 @@ import org.mockito.kotlin.doAnswer
import org.mockito.kotlin.eq
import org.mockito.kotlin.whenever
import org.springframework.core.io.ClassPathResource
-import java.io.IOException
-import java.io.InputStream
-import java.time.Instant
-import java.time.OffsetDateTime
-import java.util.*
@ExtendWith(MockitoExtension::class)
class ConsentProcessorTest {
- private lateinit var appConfigProperties: AppConfigProperties
- private lateinit var gicsConsentService: GicsConsentService
- private lateinit var objectMapper: ObjectMapper
- private lateinit var gIcsConfigProperties: GIcsConfigProperties
- private lateinit var fhirContext: FhirContext
- private lateinit var consentProcessor: ConsentProcessor
-
- @BeforeEach
- fun setups(
- @Mock gicsConsentService: GicsConsentService,
- ) {
-
- this.gIcsConfigProperties = GIcsConfigProperties("https://gics.example.com")
- val jacksonConfig = JacksonConfig()
- this.objectMapper = jacksonConfig.objectMapper()
- this.fhirContext = JacksonConfig.fhirContext()
- this.gicsConsentService = gicsConsentService
- this.appConfigProperties = AppConfigProperties(emptyList())
- this.consentProcessor =
- ConsentProcessor(
- appConfigProperties,
- gIcsConfigProperties,
- objectMapper,
- fhirContext,
- gicsConsentService
- )
+ private lateinit var appConfigProperties: AppConfigProperties
+ private lateinit var gicsConsentService: GicsConsentService
+ private lateinit var objectMapper: ObjectMapper
+ private lateinit var gIcsConfigProperties: GIcsConfigProperties
+ private lateinit var fhirContext: FhirContext
+ private lateinit var consentProcessor: ConsentProcessor
+
+ @BeforeEach
+ fun setups(
+ @Mock gicsConsentService: GicsConsentService,
+ ) {
+
+ this.gIcsConfigProperties = GIcsConfigProperties("https://gics.example.com")
+ val jacksonConfig = JacksonConfig()
+ this.objectMapper = jacksonConfig.objectMapper()
+ this.fhirContext = JacksonConfig.fhirContext()
+ this.gicsConsentService = gicsConsentService
+ this.appConfigProperties = AppConfigProperties(emptyList())
+ this.consentProcessor =
+ ConsentProcessor(
+ appConfigProperties,
+ gIcsConfigProperties,
+ objectMapper,
+ fhirContext,
+ gicsConsentService,
+ )
+ }
+
+ @Test
+ fun consentOk() {
+ assertThat(consentProcessor.toString()).isNotNull
+ // prep gICS response
+ doAnswer { getDummyBroadConsentBundle() }
+ .whenever(gicsConsentService)
+ .getConsent(any(), any(), eq(ConsentDomain.BROAD_CONSENT))
+
+ doAnswer { Bundle() }
+ .whenever(gicsConsentService)
+ .getConsent(any(), any(), eq(ConsentDomain.MODELLVORHABEN_64E))
+
+ val inputMtb =
+ Mtb.builder()
+ .patient(Patient.builder().id("d611d429-5003-11f0-a144-661e92ac9503").build())
+ .build()
+ val checkResult = consentProcessor.consentGatedCheckAndTryEmbedding(inputMtb)
+
+ assertThat(checkResult).isTrue
+ assertThat(inputMtb.metadata.researchConsents).isNotEmpty
+ }
+
+ companion object {
+ fun getDummyGenomDeConsent(): Consent {
+ val consent = Consent()
+ consent.id = "consent 1 id"
+ consent.patient.reference = "Patient/1234-pat1"
+
+ consent.provision.setType(Consent.ConsentProvisionType.fromCode("deny"))
+ consent.provision.period.start = Date.from(Instant.parse("2025-08-15T00:00:00.00Z"))
+ consent.provision.period.end = Date.from(Instant.parse("3000-01-01T00:00:00.00Z"))
+
+ val addProvision1 = consent.provision.addProvision()
+ addProvision1.setType(Consent.ConsentProvisionType.fromCode("permit"))
+ addProvision1.period.start = Date.from(Instant.parse("2025-08-15T00:00:00.00Z"))
+ addProvision1.period.end = Date.from(Instant.parse("3000-01-01T00:00:00.00Z"))
+ addProvision1.code.addLast(
+ CodeableConcept(
+ Coding(
+ "https://ths-greifswald.de/fhir/CodeSystem/gics/Policy/GenomDE_MV",
+ "Teilnahme",
+ "Teilnahme am Modellvorhaben und Einwilligung zur Genomsequenzierung",
+ )
+ )
+ )
+
+ val addProvision2 = consent.provision.addProvision()
+ addProvision2.setType(Consent.ConsentProvisionType.fromCode("deny"))
+ addProvision2.period.start = Date.from(Instant.parse("2025-08-15T00:00:00.00Z"))
+ addProvision2.period.end = Date.from(Instant.parse("3000-01-01T00:00:00.00Z"))
+ addProvision2.code.addLast(
+ CodeableConcept(
+ Coding(
+ "https://ths-greifswald.de/fhir/CodeSystem/gics/Policy/GenomDE_MV",
+ "Rekontaktierung",
+ "Re-Identifizierung meiner Daten über die Vertrauensstelle beim Robert Koch-Institut und in die erneute Kontaktaufnahme durch meine behandelnde Ärztin oder meinen behandelnden Arzt",
+ )
+ )
+ )
+ return consent
}
-
- @Test
- fun consentOk() {
- assertThat(consentProcessor.toString()).isNotNull
- // prep gICS response
- doAnswer { getDummyBroadConsentBundle() }.whenever(gicsConsentService)
- .getConsent(any(), any(), eq(ConsentDomain.BROAD_CONSENT))
-
- doAnswer { Bundle() }.whenever(gicsConsentService)
- .getConsent(any(), any(), eq(ConsentDomain.MODELLVORHABEN_64E))
-
- val inputMtb = Mtb.builder()
- .patient(Patient.builder().id("d611d429-5003-11f0-a144-661e92ac9503").build()).build()
- val checkResult = consentProcessor.consentGatedCheckAndTryEmbedding(inputMtb)
-
- assertThat(checkResult).isTrue
- assertThat(inputMtb.metadata.researchConsents).isNotEmpty
+ }
+
+ @ParameterizedTest
+ @CsvSource(
+ "2.16.840.1.113883.3.1937.777.24.5.3.8,urn:oid:2.16.840.1.113883.3.1937.777.24.5.3,2025-08-15T00:00:00+02:00,PERMIT,expect permit",
+ "2.16.840.1.113883.3.1937.777.24.5.3.8,urn:oid:2.16.840.1.113883.3.1937.777.24.5.3,2025-08-15T00:00:00+02:00,PERMIT,expect permit date is exactly on start",
+ "2.16.840.1.113883.3.1937.777.24.5.3.8,urn:oid:2.16.840.1.113883.3.1937.777.24.5.3,2055-08-15T00:00:00+02:00,PERMIT,expect permit date is exactly on end",
+ "2.16.840.1.113883.3.1937.777.24.5.3.8,urn:oid:2.16.840.1.113883.3.1937.777.24.5.3,2021-08-15T00:00:00+02:00,NULL,date is before start",
+ "2.16.840.1.113883.3.1937.777.24.5.3.8,urn:oid:2.16.840.1.113883.3.1937.777.24.5.3,2060-08-15T00:00:00+02:00,NULL,date is after end",
+ "2.16.840.1.113883.3.1937.777.24.5.3.27,urn:oid:2.16.840.1.113883.3.1937.777.24.5.3,2025-08-15T00:00:00+02:00,DENY,provision is denied",
+ "unknownCode,urn:oid:2.16.840.1.113883.3.1937.777.24.5.3,2025-08-15T00:00:00+02:00,NULL,code does not exist - therefore expect NULL",
+ "2.16.840.1.113883.3.1937.777.24.5.3.8,XXXX,2025-08-15T00:00:00+02:00,NULL,system not found - therefore expect NULL",
+ )
+ fun getProvisionTypeByPolicyCode(
+ code: String?,
+ system: String?,
+ timeStamp: String,
+ expected: String?,
+ desc: String?,
+ ) {
+ val testData = getDummyBroadConsentBundle()
+
+ val requestDate = Date.from(OffsetDateTime.parse(timeStamp).toInstant())
+
+ val result: Consent.ConsentProvisionType =
+ consentProcessor.getProvisionTypeByPolicyCode(testData, code, system, requestDate)
+ assertThat(result).isNotNull()
+
+ assertThat(result).`as`(desc).isEqualTo(Consent.ConsentProvisionType.valueOf(expected!!))
+ }
+
+ @Test
+ fun getProvisionTypeOnEmptyConsent() {
+ val emptyResources = Bundle().addEntry(Bundle.BundleEntryComponent().setResource(Consent()))
+
+ val requestDate = Date.from(OffsetDateTime.parse("2025-08-15T00:00:00+02:00").toInstant())
+
+ val result: Consent.ConsentProvisionType =
+ consentProcessor.getProvisionTypeByPolicyCode(
+ emptyResources,
+ "anyCode",
+ "anySystem",
+ requestDate,
+ )
+ assertThat(result).isNotNull()
+
+ assertThat(result)
+ .`as`("empty consent resource - expect NULL")
+ .isEqualTo(Consent.ConsentProvisionType.NULL)
+ }
+
+ fun getDummyBroadConsentBundle(): Bundle {
+ val bundle: InputStream?
+ try {
+ bundle = ClassPathResource("fake_broadConsent_gics_response_permit.json").getInputStream()
+ } catch (e: IOException) {
+ throw RuntimeException(e)
}
- companion object {
- fun getDummyGenomDeConsent(): Consent {
- val consent = Consent()
- consent.id = "consent 1 id"
- consent.patient.reference = "Patient/1234-pat1"
-
- consent.provision.setType(
- Consent.ConsentProvisionType.fromCode(
- "deny"
- )
- )
- consent.provision.period.start =
- Date.from(Instant.parse("2025-08-15T00:00:00.00Z"))
- consent.provision.period.end =
- Date.from(Instant.parse("3000-01-01T00:00:00.00Z"))
-
- val addProvision1 = consent.provision.addProvision()
- addProvision1.setType(Consent.ConsentProvisionType.fromCode("permit"))
- addProvision1.period.start = Date.from(Instant.parse("2025-08-15T00:00:00.00Z"))
- addProvision1.period.end = Date.from(Instant.parse("3000-01-01T00:00:00.00Z"))
- addProvision1.code.addLast(
- CodeableConcept(
- Coding(
- "https://ths-greifswald.de/fhir/CodeSystem/gics/Policy/GenomDE_MV",
- "Teilnahme",
- "Teilnahme am Modellvorhaben und Einwilligung zur Genomsequenzierung"
- )
- )
- )
-
- val addProvision2 = consent.provision.addProvision()
- addProvision2.setType(Consent.ConsentProvisionType.fromCode("deny"))
- addProvision2.period.start = Date.from(Instant.parse("2025-08-15T00:00:00.00Z"))
- addProvision2.period.end = Date.from(Instant.parse("3000-01-01T00:00:00.00Z"))
- addProvision2.code.addLast(
- CodeableConcept(
- Coding(
- "https://ths-greifswald.de/fhir/CodeSystem/gics/Policy/GenomDE_MV",
- "Rekontaktierung",
- "Re-Identifizierung meiner Daten über die Vertrauensstelle beim Robert Koch-Institut und in die erneute Kontaktaufnahme durch meine behandelnde Ärztin oder meinen behandelnden Arzt"
- )
- )
- )
- return consent
+ return FhirContext.forR4().newJsonParser().parseResource<Bundle>(Bundle::class.java, bundle)
+ }
+
+ @ParameterizedTest
+ @ValueSource(booleans = [true, false])
+ fun mvSubmissionTypeIsSet(isTestSubmission: Boolean) {
+ appConfigProperties.genomDeTestSubmission = isTestSubmission
+ val fixture =
+ ConsentProcessor(
+ appConfigProperties,
+ gIcsConfigProperties,
+ objectMapper,
+ fhirContext,
+ gicsConsentService,
+ )
+
+ doAnswer { getDummyBroadConsentBundle() }
+ .whenever(gicsConsentService)
+ .getConsent(any(), any(), eq(ConsentDomain.BROAD_CONSENT))
+
+ doAnswer {
+ Bundle().addEntry(Bundle.BundleEntryComponent().setResource(getDummyGenomDeConsent()))
}
+ .whenever(gicsConsentService)
+ .getConsent(any(), any(), eq(ConsentDomain.MODELLVORHABEN_64E))
+
+ val inputMtb =
+ Mtb.builder()
+ .patient(Patient.builder().id("d611d429-5003-11f0-a144-661e92ac9503").build())
+ .build()
+ val checkResult = fixture.consentGatedCheckAndTryEmbedding(inputMtb)
+ assertThat(checkResult).isNotNull
+
+ if (isTestSubmission) assertThat(inputMtb.metadata.type).isEqualTo(MvhSubmissionType.TEST)
+ else {
+ assertThat(inputMtb.metadata.type).isEqualTo(MvhSubmissionType.INITIAL)
}
-
- @ParameterizedTest
- @CsvSource(
- "2.16.840.1.113883.3.1937.777.24.5.3.8,urn:oid:2.16.840.1.113883.3.1937.777.24.5.3,2025-08-15T00:00:00+02:00,PERMIT,expect permit",
- "2.16.840.1.113883.3.1937.777.24.5.3.8,urn:oid:2.16.840.1.113883.3.1937.777.24.5.3,2025-08-15T00:00:00+02:00,PERMIT,expect permit date is exactly on start",
- "2.16.840.1.113883.3.1937.777.24.5.3.8,urn:oid:2.16.840.1.113883.3.1937.777.24.5.3,2055-08-15T00:00:00+02:00,PERMIT,expect permit date is exactly on end",
- "2.16.840.1.113883.3.1937.777.24.5.3.8,urn:oid:2.16.840.1.113883.3.1937.777.24.5.3,2021-08-15T00:00:00+02:00,NULL,date is before start",
- "2.16.840.1.113883.3.1937.777.24.5.3.8,urn:oid:2.16.840.1.113883.3.1937.777.24.5.3,2060-08-15T00:00:00+02:00,NULL,date is after end",
- "2.16.840.1.113883.3.1937.777.24.5.3.27,urn:oid:2.16.840.1.113883.3.1937.777.24.5.3,2025-08-15T00:00:00+02:00,DENY,provision is denied",
- "unknownCode,urn:oid:2.16.840.1.113883.3.1937.777.24.5.3,2025-08-15T00:00:00+02:00,NULL,code does not exist - therefore expect NULL",
- "2.16.840.1.113883.3.1937.777.24.5.3.8,XXXX,2025-08-15T00:00:00+02:00,NULL,system not found - therefore expect NULL",
- )
- fun getProvisionTypeByPolicyCode(
- code: String?, system: String?, timeStamp: String, expected: String?,
- desc: String?
- ) {
- val testData = getDummyBroadConsentBundle()
-
- val requestDate = Date.from(OffsetDateTime.parse(timeStamp).toInstant())
-
- val result: Consent.ConsentProvisionType =
- consentProcessor.getProvisionTypeByPolicyCode(testData, code, system, requestDate)
- assertThat(result).isNotNull()
-
-
- assertThat(result).`as`(desc)
- .isEqualTo(Consent.ConsentProvisionType.valueOf(expected!!))
- }
-
- @Test
- fun getProvisionTypeOnEmptyConsent(
- ) {
- val emptyResources = Bundle().addEntry(Bundle.BundleEntryComponent().setResource(Consent()))
-
- val requestDate = Date.from(OffsetDateTime.parse("2025-08-15T00:00:00+02:00").toInstant())
-
- val result: Consent.ConsentProvisionType =
- consentProcessor.getProvisionTypeByPolicyCode(
- emptyResources,
- "anyCode",
- "anySystem",
- requestDate
- )
- assertThat(result).isNotNull()
-
-
- assertThat(result).`as`("empty consent resource - expect NULL")
- .isEqualTo(Consent.ConsentProvisionType.NULL)
- }
-
- fun getDummyBroadConsentBundle(): Bundle {
- val bundle: InputStream?
- try {
- bundle = ClassPathResource(
- "fake_broadConsent_gics_response_permit.json"
- ).getInputStream()
- } catch (e: IOException) {
- throw RuntimeException(e)
- }
-
- return FhirContext.forR4().newJsonParser()
- .parseResource<Bundle>(Bundle::class.java, bundle)
- }
-
- @ParameterizedTest
- @ValueSource(booleans = [true, false])
- fun mvSubmissionTypeIsSet(isTestSubmission: Boolean) {
- appConfigProperties.genomDeTestSubmission = isTestSubmission
- val fixture =
- ConsentProcessor(
- appConfigProperties,
- gIcsConfigProperties,
- objectMapper,
- fhirContext,
- gicsConsentService
- )
-
- doAnswer { getDummyBroadConsentBundle() }.whenever(gicsConsentService)
- .getConsent(any(), any(), eq(ConsentDomain.BROAD_CONSENT))
-
- doAnswer {
- Bundle().addEntry(
- Bundle.BundleEntryComponent().setResource(getDummyGenomDeConsent())
- )
- }.whenever(gicsConsentService)
- .getConsent(any(), any(), eq(ConsentDomain.MODELLVORHABEN_64E))
-
- val inputMtb = Mtb.builder()
- .patient(Patient.builder().id("d611d429-5003-11f0-a144-661e92ac9503").build()).build()
- val checkResult = fixture.consentGatedCheckAndTryEmbedding(inputMtb)
- assertThat(checkResult).isNotNull
-
- if (isTestSubmission)
- assertThat(inputMtb.metadata.type).isEqualTo(MvhSubmissionType.TEST)
- else {
- assertThat(inputMtb.metadata.type).isEqualTo(MvhSubmissionType.INITIAL)
- }
-
- }
-
+ }
}
diff --git a/src/test/kotlin/dev/dnpm/etl/processor/services/ReportServiceTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/services/ReportServiceTest.kt
index fc95808..4308fed 100644
--- a/src/test/kotlin/dev/dnpm/etl/processor/services/ReportServiceTest.kt
+++ b/src/test/kotlin/dev/dnpm/etl/processor/services/ReportServiceTest.kt
@@ -32,17 +32,18 @@ import org.junit.jupiter.params.provider.Arguments
import org.junit.jupiter.params.provider.MethodSource
class ReportServiceTest {
-
private lateinit var reportService: ReportService
@BeforeEach
fun setup() {
- this.reportService = ReportService(ObjectMapper().registerModule(KotlinModule.Builder().build()))
+ this.reportService =
+ ReportService(ObjectMapper().registerModule(KotlinModule.Builder().build()))
}
@Test
fun shouldParseDataQualityReport() {
- val json = """
+ val json =
+ """
{
"patient": "4711",
"issues": [
@@ -52,7 +53,7 @@ class ReportServiceTest {
{ "severity": "fatal", "message": "Fatal Message" }
]
}
- """.trimIndent()
+ """.trimIndent()
val actual = this.reportService.deserialize(json)
@@ -71,7 +72,10 @@ class ReportServiceTest {
@ParameterizedTest
@MethodSource("testData")
- fun shouldParseDataQualityReport(json: String, requestStatus: RequestStatus) {
+ fun shouldParseDataQualityReport(
+ json: String,
+ requestStatus: RequestStatus,
+ ) {
val actual = this.reportService.deserialize(json)
assertThat(actual.asRequestStatus()).isEqualTo(requestStatus)
}
@@ -88,74 +92,71 @@ class ReportServiceTest {
}
companion object {
-
@JvmStatic
- fun testData(): Set<Arguments> {
- return setOf(
+ fun testData(): Set<Arguments> =
+ setOf(
Arguments.of(
"""
- {
- "patient": "4711",
- "issues": [
- { "severity": "info", "message": "Info Message" },
- { "severity": "warning", "message": "Warning Message" },
- { "severity": "error", "message": "Error Message" },
- { "severity": "fatal", "message": "Fatal Message" }
- ]
- }
+ {
+ "patient": "4711",
+ "issues": [
+ { "severity": "info", "message": "Info Message" },
+ { "severity": "warning", "message": "Warning Message" },
+ { "severity": "error", "message": "Error Message" },
+ { "severity": "fatal", "message": "Fatal Message" }
+ ]
+ }
""".trimIndent(),
- RequestStatus.ERROR
+ RequestStatus.ERROR,
),
Arguments.of(
"""
- {
- "patient": "4711",
- "issues": [
- { "severity": "info", "message": "Info Message" },
- { "severity": "warning", "message": "Warning Message" },
- { "severity": "error", "message": "Error Message" }
- ]
- }
+ {
+ "patient": "4711",
+ "issues": [
+ { "severity": "info", "message": "Info Message" },
+ { "severity": "warning", "message": "Warning Message" },
+ { "severity": "error", "message": "Error Message" }
+ ]
+ }
""".trimIndent(),
- RequestStatus.ERROR
+ RequestStatus.ERROR,
),
Arguments.of(
"""
- {
- "patient": "4711",
- "issues": [
- { "severity": "error", "message": "Error Message" }
- { "severity": "info", "message": "Info Message" }
- ]
- }
+ {
+ "patient": "4711",
+ "issues": [
+ { "severity": "error", "message": "Error Message" }
+ { "severity": "info", "message": "Info Message" }
+ ]
+ }
""".trimIndent(),
- RequestStatus.ERROR
+ RequestStatus.ERROR,
),
Arguments.of(
"""
- {
- "patient": "4711",
- "issues": [
- { "severity": "info", "message": "Info Message" },
- { "severity": "warning", "message": "Warning Message" }
- ]
- }
+ {
+ "patient": "4711",
+ "issues": [
+ { "severity": "info", "message": "Info Message" },
+ { "severity": "warning", "message": "Warning Message" }
+ ]
+ }
""".trimIndent(),
- RequestStatus.WARNING
+ RequestStatus.WARNING,
),
Arguments.of(
"""
- {
- "patient": "4711",
- "issues": [
- { "severity": "info", "message": "Info Message" }
- ]
- }
+ {
+ "patient": "4711",
+ "issues": [
+ { "severity": "info", "message": "Info Message" }
+ ]
+ }
""".trimIndent(),
- RequestStatus.SUCCESS
- )
+ RequestStatus.SUCCESS,
+ ),
)
- }
}
-
-} \ 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 0a42b9b..4bd3fc1 100644
--- a/src/test/kotlin/dev/dnpm/etl/processor/services/RequestProcessorTest.kt
+++ b/src/test/kotlin/dev/dnpm/etl/processor/services/RequestProcessorTest.kt
@@ -35,6 +35,8 @@ 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 java.time.Instant
+import java.util.*
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
@@ -47,42 +49,40 @@ import org.mockito.kotlin.anyValueClass
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)
class RequestProcessorTest {
- private lateinit var pseudonymizeService: PseudonymizeService
- private lateinit var transformationService: TransformationService
- private lateinit var sender: MtbFileSender
- private lateinit var requestService: RequestService
- private lateinit var applicationEventPublisher: ApplicationEventPublisher
- private lateinit var appConfigProperties: AppConfigProperties
- private lateinit var consentProcessor: ConsentProcessor
- private lateinit var requestProcessor: RequestProcessor
-
- @BeforeEach
- fun setup(
- @Mock pseudonymizeService: PseudonymizeService,
- @Mock transformationService: TransformationService,
- @Mock sender: RestMtbFileSender,
- @Mock requestService: RequestService,
- @Mock applicationEventPublisher: ApplicationEventPublisher,
- @Mock consentProcessor: ConsentProcessor
- ) {
- this.pseudonymizeService = pseudonymizeService
- this.transformationService = transformationService
- this.sender = sender
- this.requestService = requestService
- this.applicationEventPublisher = applicationEventPublisher
- this.appConfigProperties = AppConfigProperties()
- this.consentProcessor = consentProcessor
-
- val objectMapper = ObjectMapper()
-
- requestProcessor = RequestProcessor(
+ private lateinit var pseudonymizeService: PseudonymizeService
+ private lateinit var transformationService: TransformationService
+ private lateinit var sender: MtbFileSender
+ private lateinit var requestService: RequestService
+ private lateinit var applicationEventPublisher: ApplicationEventPublisher
+ private lateinit var appConfigProperties: AppConfigProperties
+ private lateinit var consentProcessor: ConsentProcessor
+ private lateinit var requestProcessor: RequestProcessor
+
+ @BeforeEach
+ fun setup(
+ @Mock pseudonymizeService: PseudonymizeService,
+ @Mock transformationService: TransformationService,
+ @Mock sender: RestMtbFileSender,
+ @Mock requestService: RequestService,
+ @Mock applicationEventPublisher: ApplicationEventPublisher,
+ @Mock consentProcessor: ConsentProcessor,
+ ) {
+ this.pseudonymizeService = pseudonymizeService
+ this.transformationService = transformationService
+ this.sender = sender
+ this.requestService = requestService
+ this.applicationEventPublisher = applicationEventPublisher
+ this.appConfigProperties = AppConfigProperties()
+ this.consentProcessor = consentProcessor
+
+ val objectMapper = ObjectMapper()
+
+ requestProcessor =
+ RequestProcessor(
pseudonymizeService,
transformationService,
sender,
@@ -90,224 +90,228 @@ class RequestProcessorTest {
objectMapper,
applicationEventPublisher,
appConfigProperties,
- consentProcessor
+ consentProcessor,
)
- }
-
- @Test
- fun testShouldSendMtbFileDuplicationAndSaveUnknownRequestStatusAtFirst() {
- doAnswer {
- Request(
- 1L,
- randomRequestId(),
- PatientPseudonym("TEST_12345678901"),
- PatientId("P1"),
- Fingerprint("6vkiti5bk6ikwifpajpt7cygmd3dvw54d6lwfhzlynb3pqtzferq"),
- RequestType.MTB_FILE,
- RequestStatus.SUCCESS,
- Instant.parse("2023-08-08T02:00:00Z")
- )
- }.whenever(requestService).lastMtbFileRequestForPatientPseudonym(anyValueClass())
-
- doAnswer {
- false
- }.whenever(requestService).isLastRequestWithKnownStatusDeletion(anyValueClass())
-
- doAnswer {
- it.arguments[0] as String
- }.whenever(pseudonymizeService).patientPseudonym(anyValueClass())
-
- doAnswer {
- it.arguments[0]
- }.whenever(transformationService).transform(any<Mtb>())
-
- whenever(consentProcessor.consentGatedCheckAndTryEmbedding(any())).thenReturn(true)
-
- val mtbFile = Mtb.builder()
- .patient(
- Patient.builder()
- .id("123")
- .build()
- )
+ }
+
+ @Test
+ fun testShouldSendMtbFileDuplicationAndSaveUnknownRequestStatusAtFirst() {
+ doAnswer {
+ Request(
+ 1L,
+ randomRequestId(),
+ PatientPseudonym("TEST_12345678901"),
+ PatientId("P1"),
+ Fingerprint("6vkiti5bk6ikwifpajpt7cygmd3dvw54d6lwfhzlynb3pqtzferq"),
+ RequestType.MTB_FILE,
+ RequestStatus.SUCCESS,
+ Instant.parse("2023-08-08T02:00:00Z"),
+ )
+ }
+ .whenever(requestService)
+ .lastMtbFileRequestForPatientPseudonym(anyValueClass())
+
+ doAnswer { false }
+ .whenever(requestService)
+ .isLastRequestWithKnownStatusDeletion(anyValueClass())
+
+ doAnswer { it.arguments[0] as String }
+ .whenever(pseudonymizeService)
+ .patientPseudonym(anyValueClass())
+
+ doAnswer { it.arguments[0] }.whenever(transformationService).transform(any<Mtb>())
+
+ whenever(consentProcessor.consentGatedCheckAndTryEmbedding(any())).thenReturn(true)
+
+ val mtbFile =
+ Mtb.builder()
+ .patient(Patient.builder().id("123").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())
+ .period(
+ PeriodDate.builder()
+ .start(Date.from(Instant.parse("2023-08-08T02:00:00.00Z")))
+ .build()
+ )
.build()
)
)
.build()
- this.requestProcessor.processMtbFile(mtbFile)
-
- val requestCaptor = argumentCaptor<Request>()
- verify(requestService, times(1)).save(requestCaptor.capture())
- assertThat(requestCaptor.firstValue).isNotNull
- assertThat(requestCaptor.firstValue.status).isEqualTo(RequestStatus.UNKNOWN)
- }
-
- @Test
- fun testShouldDetectMtbFileDuplicationAndSendDuplicationEvent() {
- doAnswer {
- Request(
- 1L,
- randomRequestId(),
- PatientPseudonym("TEST_12345678901"),
- PatientId("P1"),
- Fingerprint("4gcjwtjjtcczybsljxepdfpkaeusvd7g3vogfqpmphyffyzfx7dq"),
- RequestType.MTB_FILE,
- RequestStatus.SUCCESS,
- Instant.parse("2023-08-08T02:00:00Z")
- )
- }.whenever(requestService).lastMtbFileRequestForPatientPseudonym(anyValueClass())
-
- doAnswer {
- false
- }.whenever(requestService).isLastRequestWithKnownStatusDeletion(anyValueClass())
-
- doAnswer {
- it.arguments[0] as String
- }.whenever(pseudonymizeService).patientPseudonym(anyValueClass())
-
- doAnswer {
- it.arguments[0]
- }.whenever(transformationService).transform(any<Mtb>())
-
- whenever(consentProcessor.consentGatedCheckAndTryEmbedding(any())).thenReturn(true)
-
- val mtbFile = Mtb.builder()
- .patient(
- Patient.builder()
- .id("123")
- .build()
- )
+ this.requestProcessor.processMtbFile(mtbFile)
+
+ val requestCaptor = argumentCaptor<Request>()
+ verify(requestService, times(1)).save(requestCaptor.capture())
+ assertThat(requestCaptor.firstValue).isNotNull
+ assertThat(requestCaptor.firstValue.status).isEqualTo(RequestStatus.UNKNOWN)
+ }
+
+ @Test
+ fun testShouldDetectMtbFileDuplicationAndSendDuplicationEvent() {
+ doAnswer {
+ Request(
+ 1L,
+ randomRequestId(),
+ PatientPseudonym("TEST_12345678901"),
+ PatientId("P1"),
+ Fingerprint("4gcjwtjjtcczybsljxepdfpkaeusvd7g3vogfqpmphyffyzfx7dq"),
+ RequestType.MTB_FILE,
+ RequestStatus.SUCCESS,
+ Instant.parse("2023-08-08T02:00:00Z"),
+ )
+ }
+ .whenever(requestService)
+ .lastMtbFileRequestForPatientPseudonym(anyValueClass())
+
+ doAnswer { false }
+ .whenever(requestService)
+ .isLastRequestWithKnownStatusDeletion(anyValueClass())
+
+ doAnswer { it.arguments[0] as String }
+ .whenever(pseudonymizeService)
+ .patientPseudonym(anyValueClass())
+
+ doAnswer { it.arguments[0] }.whenever(transformationService).transform(any<Mtb>())
+
+ whenever(consentProcessor.consentGatedCheckAndTryEmbedding(any())).thenReturn(true)
+
+ val mtbFile =
+ Mtb.builder()
+ .patient(Patient.builder().id("123").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())
+ .period(
+ PeriodDate.builder()
+ .start(Date.from(Instant.parse("2021-01-01T00:00:00.00Z")))
+ .build()
+ )
.build()
)
)
.build()
- this.requestProcessor.processMtbFile(mtbFile)
-
- val eventCaptor = argumentCaptor<ResponseEvent>()
- verify(applicationEventPublisher, times(1)).publishEvent(eventCaptor.capture())
- assertThat(eventCaptor.firstValue).isNotNull
- assertThat(eventCaptor.firstValue.status).isEqualTo(RequestStatus.DUPLICATION)
- }
-
- @Test
- fun testShouldSendMtbFileAndSendSuccessEvent() {
- doAnswer {
- Request(
- 1L,
- randomRequestId(),
- PatientPseudonym("TEST_12345678901"),
- PatientId("P1"),
- Fingerprint("different"),
- RequestType.MTB_FILE,
- RequestStatus.SUCCESS,
- Instant.parse("2023-08-08T02:00:00Z")
- )
- }.whenever(requestService).lastMtbFileRequestForPatientPseudonym(anyValueClass())
-
- doAnswer {
- false
- }.whenever(requestService).isLastRequestWithKnownStatusDeletion(anyValueClass())
-
- doAnswer {
- MtbFileSender.Response(status = RequestStatus.SUCCESS)
- }.whenever(sender).send(any<DnpmV2MtbFileRequest>())
-
- doAnswer {
- it.arguments[0] as String
- }.whenever(pseudonymizeService).patientPseudonym(anyValueClass())
-
- doAnswer {
- it.arguments[0]
- }.whenever(transformationService).transform(any<Mtb>())
-
- whenever(consentProcessor.consentGatedCheckAndTryEmbedding(any())).thenReturn(true)
-
- val mtbFile = Mtb.builder()
- .patient(
- Patient.builder()
- .id("123")
- .build()
- )
+ this.requestProcessor.processMtbFile(mtbFile)
+
+ val eventCaptor = argumentCaptor<ResponseEvent>()
+ verify(applicationEventPublisher, times(1)).publishEvent(eventCaptor.capture())
+ assertThat(eventCaptor.firstValue).isNotNull
+ assertThat(eventCaptor.firstValue.status).isEqualTo(RequestStatus.DUPLICATION)
+ }
+
+ @Test
+ fun testShouldSendMtbFileAndSendSuccessEvent() {
+ doAnswer {
+ Request(
+ 1L,
+ randomRequestId(),
+ PatientPseudonym("TEST_12345678901"),
+ PatientId("P1"),
+ Fingerprint("different"),
+ RequestType.MTB_FILE,
+ RequestStatus.SUCCESS,
+ Instant.parse("2023-08-08T02:00:00Z"),
+ )
+ }
+ .whenever(requestService)
+ .lastMtbFileRequestForPatientPseudonym(anyValueClass())
+
+ doAnswer { false }
+ .whenever(requestService)
+ .isLastRequestWithKnownStatusDeletion(anyValueClass())
+
+ doAnswer { MtbFileSender.Response(status = RequestStatus.SUCCESS) }
+ .whenever(sender)
+ .send(any<DnpmV2MtbFileRequest>())
+
+ doAnswer { it.arguments[0] as String }
+ .whenever(pseudonymizeService)
+ .patientPseudonym(anyValueClass())
+
+ doAnswer { it.arguments[0] }.whenever(transformationService).transform(any<Mtb>())
+
+ whenever(consentProcessor.consentGatedCheckAndTryEmbedding(any())).thenReturn(true)
+
+ val mtbFile =
+ Mtb.builder()
+ .patient(Patient.builder().id("123").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())
+ .period(
+ PeriodDate.builder()
+ .start(Date.from(Instant.parse("2021-01-01T00:00:00.00Z")))
+ .build()
+ )
.build()
)
)
.build()
- this.requestProcessor.processMtbFile(mtbFile)
-
- val eventCaptor = argumentCaptor<ResponseEvent>()
- verify(applicationEventPublisher, times(1)).publishEvent(eventCaptor.capture())
- assertThat(eventCaptor.firstValue).isNotNull
- assertThat(eventCaptor.firstValue.status).isEqualTo(RequestStatus.SUCCESS)
- }
-
- @Test
- fun testShouldSendMtbFileAndSendErrorEvent() {
- doAnswer {
- Request(
- 1L,
- randomRequestId(),
- PatientPseudonym("TEST_12345678901"),
- PatientId("P1"),
- Fingerprint("different"),
- RequestType.MTB_FILE,
- RequestStatus.SUCCESS,
- Instant.parse("2023-08-08T02:00:00Z")
- )
- }.whenever(requestService).lastMtbFileRequestForPatientPseudonym(anyValueClass())
-
- doAnswer {
- false
- }.whenever(requestService).isLastRequestWithKnownStatusDeletion(anyValueClass())
-
- doAnswer {
- MtbFileSender.Response(status = RequestStatus.ERROR)
- }.whenever(sender).send(any<DnpmV2MtbFileRequest>())
-
- doAnswer {
- it.arguments[0] as String
- }.whenever(pseudonymizeService).patientPseudonym(anyValueClass())
-
- doAnswer {
- it.arguments[0]
- }.whenever(transformationService).transform(any<Mtb>())
-
- whenever(consentProcessor.consentGatedCheckAndTryEmbedding(any())).thenReturn(true)
-
- val mtbFile = Mtb.builder()
- .patient(
- Patient.builder()
- .id("123")
- .build()
- )
+ this.requestProcessor.processMtbFile(mtbFile)
+
+ val eventCaptor = argumentCaptor<ResponseEvent>()
+ verify(applicationEventPublisher, times(1)).publishEvent(eventCaptor.capture())
+ assertThat(eventCaptor.firstValue).isNotNull
+ assertThat(eventCaptor.firstValue.status).isEqualTo(RequestStatus.SUCCESS)
+ }
+
+ @Test
+ fun testShouldSendMtbFileAndSendErrorEvent() {
+ doAnswer {
+ Request(
+ 1L,
+ randomRequestId(),
+ PatientPseudonym("TEST_12345678901"),
+ PatientId("P1"),
+ Fingerprint("different"),
+ RequestType.MTB_FILE,
+ RequestStatus.SUCCESS,
+ Instant.parse("2023-08-08T02:00:00Z"),
+ )
+ }
+ .whenever(requestService)
+ .lastMtbFileRequestForPatientPseudonym(anyValueClass())
+
+ doAnswer { false }
+ .whenever(requestService)
+ .isLastRequestWithKnownStatusDeletion(anyValueClass())
+
+ doAnswer { MtbFileSender.Response(status = RequestStatus.ERROR) }
+ .whenever(sender)
+ .send(any<DnpmV2MtbFileRequest>())
+
+ doAnswer { it.arguments[0] as String }
+ .whenever(pseudonymizeService)
+ .patientPseudonym(anyValueClass())
+
+ doAnswer { it.arguments[0] }.whenever(transformationService).transform(any<Mtb>())
+
+ whenever(consentProcessor.consentGatedCheckAndTryEmbedding(any())).thenReturn(true)
+
+ val mtbFile =
+ Mtb.builder()
+ .patient(Patient.builder().id("123").build())
.metadata(
- MvhMetadata
- .builder()
+ MvhMetadata.builder()
.modelProjectConsent(
- ModelProjectConsent
- .builder()
+ ModelProjectConsent.builder()
.provisions(
- listOf(Provision.builder().type(ConsentProvision.PERMIT).purpose(ModelProjectConsentPurpose.SEQUENCING).build())
- ).build()
+ listOf(
+ Provision.builder()
+ .type(ConsentProvision.PERMIT)
+ .purpose(ModelProjectConsentPurpose.SEQUENCING)
+ .build()
+ )
+ )
+ .build()
)
.build()
)
@@ -316,143 +320,139 @@ class RequestProcessorTest {
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())
+ .period(
+ PeriodDate.builder()
+ .start(Date.from(Instant.parse("2021-01-01T00:00:00.00Z")))
+ .build()
+ )
.build()
)
)
.build()
- this.requestProcessor.processMtbFile(mtbFile)
-
- val eventCaptor = argumentCaptor<ResponseEvent>()
- verify(applicationEventPublisher, times(1)).publishEvent(eventCaptor.capture())
- assertThat(eventCaptor.firstValue).isNotNull
- assertThat(eventCaptor.firstValue.status).isEqualTo(RequestStatus.ERROR)
- }
-
- @Test
- fun testShouldSendDeleteRequestAndSaveUnknownRequestStatusAtFirst() {
- doAnswer {
- "PSEUDONYM"
- }.whenever(pseudonymizeService).patientPseudonym(anyValueClass())
-
- doAnswer {
- MtbFileSender.Response(status = RequestStatus.UNKNOWN)
- }.whenever(sender).send(any<DeleteRequest>())
-
- this.requestProcessor.processDeletion(
- TEST_PATIENT_ID,
- isConsented = TtpConsentStatus.UNKNOWN_CHECK_FILE
- )
-
- val requestCaptor = argumentCaptor<Request>()
- verify(requestService, times(1)).save(requestCaptor.capture())
- assertThat(requestCaptor.firstValue).isNotNull
- assertThat(requestCaptor.firstValue.status).isEqualTo(RequestStatus.UNKNOWN)
- }
-
- @Test
- fun testShouldSendDeleteRequestAndSendSuccessEvent() {
- doAnswer {
- "PSEUDONYM"
- }.whenever(pseudonymizeService).patientPseudonym(anyValueClass())
-
- doAnswer {
- MtbFileSender.Response(status = RequestStatus.SUCCESS)
- }.whenever(sender).send(any<DeleteRequest>())
-
- this.requestProcessor.processDeletion(
- TEST_PATIENT_ID,
- isConsented = TtpConsentStatus.UNKNOWN_CHECK_FILE
- )
-
- val eventCaptor = argumentCaptor<ResponseEvent>()
- verify(applicationEventPublisher, times(1)).publishEvent(eventCaptor.capture())
- assertThat(eventCaptor.firstValue).isNotNull
- assertThat(eventCaptor.firstValue.status).isEqualTo(RequestStatus.SUCCESS)
- }
-
- @Test
- fun testShouldSendDeleteRequestAndSendErrorEvent() {
- doAnswer {
- "PSEUDONYM"
- }.whenever(pseudonymizeService).patientPseudonym(anyValueClass())
-
- doAnswer {
- MtbFileSender.Response(status = RequestStatus.ERROR)
- }.whenever(sender).send(any<DeleteRequest>())
-
- this.requestProcessor.processDeletion(
- TEST_PATIENT_ID,
- isConsented = TtpConsentStatus.UNKNOWN_CHECK_FILE
- )
-
- val eventCaptor = argumentCaptor<ResponseEvent>()
- verify(applicationEventPublisher, times(1)).publishEvent(eventCaptor.capture())
- assertThat(eventCaptor.firstValue).isNotNull
- assertThat(eventCaptor.firstValue.status).isEqualTo(RequestStatus.ERROR)
- }
-
- @Test
- fun testShouldSendDeleteRequestWithPseudonymErrorAndSaveErrorRequestStatus() {
- doThrow(RuntimeException()).whenever(pseudonymizeService).patientPseudonym(anyValueClass())
-
- this.requestProcessor.processDeletion(
- TEST_PATIENT_ID,
- isConsented = TtpConsentStatus.UNKNOWN_CHECK_FILE
- )
-
- val requestCaptor = argumentCaptor<Request>()
- verify(requestService, times(1)).save(requestCaptor.capture())
- assertThat(requestCaptor.firstValue).isNotNull
- assertThat(requestCaptor.firstValue.status).isEqualTo(RequestStatus.ERROR)
- }
-
- @Test
- fun testShouldNotDetectMtbFileDuplicationIfDuplicationNotConfigured() {
- this.appConfigProperties.duplicationDetection = false
-
- doAnswer {
- it.arguments[0] as String
- }.whenever(pseudonymizeService).patientPseudonym(anyValueClass())
-
- doAnswer {
- it.arguments[0]
- }.whenever(transformationService).transform(any<Mtb>())
-
- doAnswer {
- MtbFileSender.Response(status = RequestStatus.SUCCESS)
- }.whenever(sender).send(any<DnpmV2MtbFileRequest>())
-
- whenever(consentProcessor.consentGatedCheckAndTryEmbedding(any())).thenReturn(true)
-
- val mtbFile = Mtb.builder()
- .patient(
- Patient.builder()
- .id("123")
- .build()
- )
+ this.requestProcessor.processMtbFile(mtbFile)
+
+ val eventCaptor = argumentCaptor<ResponseEvent>()
+ verify(applicationEventPublisher, times(1)).publishEvent(eventCaptor.capture())
+ assertThat(eventCaptor.firstValue).isNotNull
+ assertThat(eventCaptor.firstValue.status).isEqualTo(RequestStatus.ERROR)
+ }
+
+ @Test
+ fun testShouldSendDeleteRequestAndSaveUnknownRequestStatusAtFirst() {
+ doAnswer { "PSEUDONYM" }.whenever(pseudonymizeService).patientPseudonym(anyValueClass())
+
+ doAnswer { MtbFileSender.Response(status = RequestStatus.UNKNOWN) }
+ .whenever(sender)
+ .send(any<DeleteRequest>())
+
+ this.requestProcessor.processDeletion(
+ TEST_PATIENT_ID,
+ isConsented = TtpConsentStatus.UNKNOWN_CHECK_FILE,
+ )
+
+ val requestCaptor = argumentCaptor<Request>()
+ verify(requestService, times(1)).save(requestCaptor.capture())
+ assertThat(requestCaptor.firstValue).isNotNull
+ assertThat(requestCaptor.firstValue.status).isEqualTo(RequestStatus.UNKNOWN)
+ }
+
+ @Test
+ fun testShouldSendDeleteRequestAndSendSuccessEvent() {
+ doAnswer { "PSEUDONYM" }.whenever(pseudonymizeService).patientPseudonym(anyValueClass())
+
+ doAnswer { MtbFileSender.Response(status = RequestStatus.SUCCESS) }
+ .whenever(sender)
+ .send(any<DeleteRequest>())
+
+ this.requestProcessor.processDeletion(
+ TEST_PATIENT_ID,
+ isConsented = TtpConsentStatus.UNKNOWN_CHECK_FILE,
+ )
+
+ val eventCaptor = argumentCaptor<ResponseEvent>()
+ verify(applicationEventPublisher, times(1)).publishEvent(eventCaptor.capture())
+ assertThat(eventCaptor.firstValue).isNotNull
+ assertThat(eventCaptor.firstValue.status).isEqualTo(RequestStatus.SUCCESS)
+ }
+
+ @Test
+ fun testShouldSendDeleteRequestAndSendErrorEvent() {
+ doAnswer { "PSEUDONYM" }.whenever(pseudonymizeService).patientPseudonym(anyValueClass())
+
+ doAnswer { MtbFileSender.Response(status = RequestStatus.ERROR) }
+ .whenever(sender)
+ .send(any<DeleteRequest>())
+
+ this.requestProcessor.processDeletion(
+ TEST_PATIENT_ID,
+ isConsented = TtpConsentStatus.UNKNOWN_CHECK_FILE,
+ )
+
+ val eventCaptor = argumentCaptor<ResponseEvent>()
+ verify(applicationEventPublisher, times(1)).publishEvent(eventCaptor.capture())
+ assertThat(eventCaptor.firstValue).isNotNull
+ assertThat(eventCaptor.firstValue.status).isEqualTo(RequestStatus.ERROR)
+ }
+
+ @Test
+ fun testShouldSendDeleteRequestWithPseudonymErrorAndSaveErrorRequestStatus() {
+ doThrow(RuntimeException()).whenever(pseudonymizeService).patientPseudonym(anyValueClass())
+
+ this.requestProcessor.processDeletion(
+ TEST_PATIENT_ID,
+ isConsented = TtpConsentStatus.UNKNOWN_CHECK_FILE,
+ )
+
+ val requestCaptor = argumentCaptor<Request>()
+ verify(requestService, times(1)).save(requestCaptor.capture())
+ assertThat(requestCaptor.firstValue).isNotNull
+ assertThat(requestCaptor.firstValue.status).isEqualTo(RequestStatus.ERROR)
+ }
+
+ @Test
+ fun testShouldNotDetectMtbFileDuplicationIfDuplicationNotConfigured() {
+ this.appConfigProperties.duplicationDetection = false
+
+ doAnswer { it.arguments[0] as String }
+ .whenever(pseudonymizeService)
+ .patientPseudonym(anyValueClass())
+
+ doAnswer { it.arguments[0] }.whenever(transformationService).transform(any<Mtb>())
+
+ doAnswer { MtbFileSender.Response(status = RequestStatus.SUCCESS) }
+ .whenever(sender)
+ .send(any<DnpmV2MtbFileRequest>())
+
+ whenever(consentProcessor.consentGatedCheckAndTryEmbedding(any())).thenReturn(true)
+
+ val mtbFile =
+ Mtb.builder()
+ .patient(Patient.builder().id("123").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())
+ .period(
+ PeriodDate.builder()
+ .start(Date.from(Instant.parse("2021-01-01T00:00:00.00Z")))
+ .build()
+ )
.build()
)
)
.build()
- this.requestProcessor.processMtbFile(mtbFile)
-
- val eventCaptor = argumentCaptor<ResponseEvent>()
- verify(applicationEventPublisher, times(1)).publishEvent(eventCaptor.capture())
- assertThat(eventCaptor.firstValue).isNotNull
- assertThat(eventCaptor.firstValue.status).isEqualTo(RequestStatus.SUCCESS)
- }
+ this.requestProcessor.processMtbFile(mtbFile)
- companion object {
- val TEST_PATIENT_ID = PatientId("TEST_12345678901")
- }
+ val eventCaptor = argumentCaptor<ResponseEvent>()
+ verify(applicationEventPublisher, times(1)).publishEvent(eventCaptor.capture())
+ assertThat(eventCaptor.firstValue).isNotNull
+ assertThat(eventCaptor.firstValue.status).isEqualTo(RequestStatus.SUCCESS)
+ }
+ companion object {
+ val TEST_PATIENT_ID = PatientId("TEST_12345678901")
+ }
}
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 c0e4400..bc0286c 100644
--- a/src/test/kotlin/dev/dnpm/etl/processor/services/RequestServiceTest.kt
+++ b/src/test/kotlin/dev/dnpm/etl/processor/services/RequestServiceTest.kt
@@ -24,6 +24,7 @@ import dev.dnpm.etl.processor.monitoring.Request
import dev.dnpm.etl.processor.monitoring.RequestRepository
import dev.dnpm.etl.processor.monitoring.RequestStatus
import dev.dnpm.etl.processor.monitoring.RequestType
+import java.time.Instant
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
@@ -33,37 +34,37 @@ import org.mockito.Mockito.*
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.anyValueClass
import org.mockito.kotlin.whenever
-import java.time.Instant
@ExtendWith(MockitoExtension::class)
class RequestServiceTest {
- private lateinit var requestRepository: RequestRepository
-
- private lateinit var requestService: RequestService
-
- private fun anyRequest() = any(Request::class.java) ?: Request(
- 0L,
- randomRequestId(),
- PatientPseudonym("TEST_dummy"),
- PatientId("PX"),
- Fingerprint("dummy"),
- RequestType.MTB_FILE,
- RequestStatus.SUCCESS,
- Instant.parse("2023-08-08T02:00:00Z")
- )
-
- @BeforeEach
- fun setup(
- @Mock requestRepository: RequestRepository
- ) {
- this.requestRepository = requestRepository
- this.requestService = RequestService(requestRepository)
- }
-
- @Test
- fun shouldIndicateLastRequestIsDeleteRequest() {
- val requests = listOf(
+ private lateinit var requestRepository: RequestRepository
+
+ private lateinit var requestService: RequestService
+
+ private fun anyRequest() =
+ any(Request::class.java)
+ ?: Request(
+ 0L,
+ randomRequestId(),
+ PatientPseudonym("TEST_dummy"),
+ PatientId("PX"),
+ Fingerprint("dummy"),
+ RequestType.MTB_FILE,
+ RequestStatus.SUCCESS,
+ Instant.parse("2023-08-08T02:00:00Z"),
+ )
+
+ @BeforeEach
+ fun setup(@Mock requestRepository: RequestRepository) {
+ this.requestRepository = requestRepository
+ this.requestService = RequestService(requestRepository)
+ }
+
+ @Test
+ fun shouldIndicateLastRequestIsDeleteRequest() {
+ val requests =
+ listOf(
Request(
1L,
randomRequestId(),
@@ -72,7 +73,7 @@ class RequestServiceTest {
Fingerprint("0123456789abcdef1"),
RequestType.MTB_FILE,
RequestStatus.WARNING,
- Instant.parse("2023-07-07T00:00:00Z")
+ Instant.parse("2023-07-07T00:00:00Z"),
),
Request(
2L,
@@ -82,7 +83,7 @@ class RequestServiceTest {
Fingerprint("0123456789abcdefd"),
RequestType.DELETE,
RequestStatus.WARNING,
- Instant.parse("2023-07-07T02:00:00Z")
+ Instant.parse("2023-07-07T02:00:00Z"),
),
Request(
3L,
@@ -92,18 +93,19 @@ class RequestServiceTest {
Fingerprint("0123456789abcdef1"),
RequestType.MTB_FILE,
RequestStatus.UNKNOWN,
- Instant.parse("2023-08-11T00:00:00Z")
- )
+ Instant.parse("2023-08-11T00:00:00Z"),
+ ),
)
- val actual = RequestService.isLastRequestWithKnownStatusDeletion(requests)
+ val actual = RequestService.isLastRequestWithKnownStatusDeletion(requests)
- assertThat(actual).isTrue()
- }
+ assertThat(actual).isTrue()
+ }
- @Test
- fun shouldIndicateLastRequestIsNotDeleteRequest() {
- val requests = listOf(
+ @Test
+ fun shouldIndicateLastRequestIsNotDeleteRequest() {
+ val requests =
+ listOf(
Request(
1L,
randomRequestId(),
@@ -112,7 +114,7 @@ class RequestServiceTest {
Fingerprint("0123456789abcdef1"),
RequestType.MTB_FILE,
RequestStatus.WARNING,
- Instant.parse("2023-07-07T00:00:00Z")
+ Instant.parse("2023-07-07T00:00:00Z"),
),
Request(
2L,
@@ -122,7 +124,7 @@ class RequestServiceTest {
Fingerprint("0123456789abcdef1"),
RequestType.MTB_FILE,
RequestStatus.WARNING,
- Instant.parse("2023-07-07T02:00:00Z")
+ Instant.parse("2023-07-07T02:00:00Z"),
),
Request(
3L,
@@ -132,18 +134,19 @@ class RequestServiceTest {
Fingerprint("0123456789abcdef1"),
RequestType.MTB_FILE,
RequestStatus.UNKNOWN,
- Instant.parse("2023-08-11T00:00:00Z")
- )
+ Instant.parse("2023-08-11T00:00:00Z"),
+ ),
)
- val actual = RequestService.isLastRequestWithKnownStatusDeletion(requests)
+ val actual = RequestService.isLastRequestWithKnownStatusDeletion(requests)
- assertThat(actual).isFalse()
- }
+ assertThat(actual).isFalse()
+ }
- @Test
- fun shouldReturnPatientsLastRequest() {
- val requests = listOf(
+ @Test
+ fun shouldReturnPatientsLastRequest() {
+ val requests =
+ listOf(
Request(
1L,
randomRequestId(),
@@ -152,7 +155,7 @@ class RequestServiceTest {
Fingerprint("0123456789abcdef1"),
RequestType.DELETE,
RequestStatus.SUCCESS,
- Instant.parse("2023-07-07T02:00:00Z")
+ Instant.parse("2023-07-07T02:00:00Z"),
),
Request(
1L,
@@ -162,66 +165,71 @@ class RequestServiceTest {
Fingerprint("0123456789abcdef2"),
RequestType.MTB_FILE,
RequestStatus.WARNING,
- Instant.parse("2023-08-08T00:00:00Z")
- )
+ Instant.parse("2023-08-08T00:00:00Z"),
+ ),
)
- val actual = RequestService.lastMtbFileRequestForPatientPseudonym(requests)
+ val actual = RequestService.lastMtbFileRequestForPatientPseudonym(requests)
- assertThat(actual).isInstanceOf(Request::class.java)
- assertThat(actual?.fingerprint).isEqualTo(Fingerprint("0123456789abcdef2"))
- }
+ assertThat(actual).isInstanceOf(Request::class.java)
+ assertThat(actual?.fingerprint).isEqualTo(Fingerprint("0123456789abcdef2"))
+ }
- @Test
- fun shouldReturnNullIfNoRequests() {
- val requests = listOf<Request>()
+ @Test
+ fun shouldReturnNullIfNoRequests() {
+ val requests = listOf<Request>()
- val actual = RequestService.lastMtbFileRequestForPatientPseudonym(requests)
+ val actual = RequestService.lastMtbFileRequestForPatientPseudonym(requests)
- assertThat(actual).isNull()
- }
+ assertThat(actual).isNull()
+ }
- @Test
- fun saveShouldSaveRequestUsingRepository() {
- doAnswer {
- val obj = it.arguments[0] as Request
- obj.copy(id = 1L)
- }.whenever(requestRepository).save(anyRequest())
+ @Test
+ fun saveShouldSaveRequestUsingRepository() {
+ doAnswer {
+ val obj = it.arguments[0] as Request
+ obj.copy(id = 1L)
+ }
+ .whenever(requestRepository)
+ .save(anyRequest())
- val request = Request(
+ val request =
+ Request(
randomRequestId(),
PatientPseudonym("TEST_12345678901"),
PatientId("P1"),
Fingerprint("0123456789abcdef1"),
RequestType.DELETE,
RequestStatus.SUCCESS,
- Instant.parse("2023-07-07T02:00:00Z")
+ Instant.parse("2023-07-07T02:00:00Z"),
)
- requestService.save(request)
-
- verify(requestRepository, times(1)).save(anyRequest())
- }
+ requestService.save(request)
- @Test
- fun allRequestsByPatientPseudonymShouldRequestAllRequestsForPatientPseudonym() {
- requestService.allRequestsByPatientPseudonym(PatientPseudonym("TEST_12345678901"))
+ verify(requestRepository, times(1)).save(anyRequest())
+ }
- verify(requestRepository, times(1)).findAllByPatientPseudonymOrderByProcessedAtDesc(anyValueClass())
- }
+ @Test
+ fun allRequestsByPatientPseudonymShouldRequestAllRequestsForPatientPseudonym() {
+ requestService.allRequestsByPatientPseudonym(PatientPseudonym("TEST_12345678901"))
- @Test
- fun lastMtbFileRequestForPatientPseudonymShouldRequestAllRequestsForPatientPseudonym() {
- requestService.lastMtbFileRequestForPatientPseudonym(PatientPseudonym("TEST_12345678901"))
+ verify(requestRepository, times(1))
+ .findAllByPatientPseudonymOrderByProcessedAtDesc(anyValueClass())
+ }
- verify(requestRepository, times(1)).findAllByPatientPseudonymOrderByProcessedAtDesc(anyValueClass())
- }
+ @Test
+ fun lastMtbFileRequestForPatientPseudonymShouldRequestAllRequestsForPatientPseudonym() {
+ requestService.lastMtbFileRequestForPatientPseudonym(PatientPseudonym("TEST_12345678901"))
- @Test
- fun isLastRequestDeletionShouldRequestAllRequestsForPatientPseudonym() {
- requestService.isLastRequestWithKnownStatusDeletion(PatientPseudonym("TEST_12345678901"))
+ verify(requestRepository, times(1))
+ .findAllByPatientPseudonymOrderByProcessedAtDesc(anyValueClass())
+ }
- verify(requestRepository, times(1)).findAllByPatientPseudonymOrderByProcessedAtDesc(anyValueClass())
- }
+ @Test
+ fun isLastRequestDeletionShouldRequestAllRequestsForPatientPseudonym() {
+ requestService.isLastRequestWithKnownStatusDeletion(PatientPseudonym("TEST_12345678901"))
-} \ No newline at end of file
+ verify(requestRepository, times(1))
+ .findAllByPatientPseudonymOrderByProcessedAtDesc(anyValueClass())
+ }
+}
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 465d8b8..16a5791 100644
--- a/src/test/kotlin/dev/dnpm/etl/processor/services/ResponseProcessorTest.kt
+++ b/src/test/kotlin/dev/dnpm/etl/processor/services/ResponseProcessorTest.kt
@@ -23,6 +23,8 @@ import dev.dnpm.etl.processor.*
import dev.dnpm.etl.processor.monitoring.Request
import dev.dnpm.etl.processor.monitoring.RequestStatus
import dev.dnpm.etl.processor.monitoring.RequestType
+import java.time.Instant
+import java.util.*
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
@@ -33,105 +35,92 @@ import org.mockito.Mock
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.*
import reactor.core.publisher.Sinks
-import java.time.Instant
-import java.util.*
@ExtendWith(MockitoExtension::class)
class ResponseProcessorTest {
- private lateinit var requestService: RequestService
- private lateinit var statisticsUpdateProducer: Sinks.Many<Any>
-
- private lateinit var responseProcessor: ResponseProcessor
-
- private val testRequest = Request(
- 1L,
- RequestId("TestID1234"),
- PatientPseudonym("PSEUDONYM-A"),
- PatientId("1"),
- Fingerprint("dummyfingerprint"),
- RequestType.MTB_FILE,
- RequestStatus.UNKNOWN
- )
-
- @BeforeEach
- fun setup(
- @Mock requestService: RequestService,
- @Mock statisticsUpdateProducer: Sinks.Many<Any>
- ) {
- this.requestService = requestService
- this.statisticsUpdateProducer = statisticsUpdateProducer
-
- this.responseProcessor = ResponseProcessor(requestService, statisticsUpdateProducer)
- }
+ private lateinit var requestService: RequestService
+ private lateinit var statisticsUpdateProducer: Sinks.Many<Any>
- @Test
- fun shouldNotSaveStatusForUnknownRequest() {
- doAnswer {
- Optional.empty<Request>()
- }.whenever(requestService).findByUuid(anyValueClass())
+ private lateinit var responseProcessor: ResponseProcessor
- val event = ResponseEvent(
- RequestId("TestID1234"),
- Instant.parse("2023-09-09T00:00:00Z"),
- RequestStatus.SUCCESS
- )
+ private val testRequest =
+ Request(
+ 1L,
+ RequestId("TestID1234"),
+ PatientPseudonym("PSEUDONYM-A"),
+ PatientId("1"),
+ Fingerprint("dummyfingerprint"),
+ RequestType.MTB_FILE,
+ RequestStatus.UNKNOWN,
+ )
- this.responseProcessor.handleResponseEvent(event)
+ @BeforeEach
+ fun setup(@Mock requestService: RequestService, @Mock statisticsUpdateProducer: Sinks.Many<Any>) {
+ this.requestService = requestService
+ this.statisticsUpdateProducer = statisticsUpdateProducer
- verify(requestService, never()).save(any())
- }
+ this.responseProcessor = ResponseProcessor(requestService, statisticsUpdateProducer)
+ }
- @Test
- fun shouldNotSaveStatusWithUnknownState() {
- doAnswer {
- Optional.of(testRequest)
- }.whenever(requestService).findByUuid(anyValueClass())
+ @Test
+ fun shouldNotSaveStatusForUnknownRequest() {
+ doAnswer { Optional.empty<Request>() }.whenever(requestService).findByUuid(anyValueClass())
- val event = ResponseEvent(
+ val event =
+ ResponseEvent(
RequestId("TestID1234"),
Instant.parse("2023-09-09T00:00:00Z"),
- RequestStatus.UNKNOWN
+ RequestStatus.SUCCESS,
)
- this.responseProcessor.handleResponseEvent(event)
+ this.responseProcessor.handleResponseEvent(event)
- verify(requestService, never()).save(any<Request>())
- }
+ verify(requestService, never()).save(any())
+ }
- @ParameterizedTest
- @MethodSource("requestStatusSource")
- fun shouldSaveStatusForKnownRequest(requestStatus: RequestStatus) {
- doAnswer {
- Optional.of(testRequest)
- }.whenever(requestService).findByUuid(anyValueClass())
+ @Test
+ fun shouldNotSaveStatusWithUnknownState() {
+ doAnswer { Optional.of(testRequest) }.whenever(requestService).findByUuid(anyValueClass())
- val event = ResponseEvent(
+ val event =
+ ResponseEvent(
RequestId("TestID1234"),
Instant.parse("2023-09-09T00:00:00Z"),
- requestStatus
+ RequestStatus.UNKNOWN,
)
- this.responseProcessor.handleResponseEvent(event)
+ this.responseProcessor.handleResponseEvent(event)
- val captor = argumentCaptor<Request>()
- verify(requestService, times(1)).save(captor.capture())
- assertThat(captor.firstValue).isNotNull
- assertThat(captor.firstValue.status).isEqualTo(requestStatus)
- }
+ verify(requestService, never()).save(any<Request>())
+ }
- companion object {
+ @ParameterizedTest
+ @MethodSource("requestStatusSource")
+ fun shouldSaveStatusForKnownRequest(requestStatus: RequestStatus) {
+ doAnswer { Optional.of(testRequest) }.whenever(requestService).findByUuid(anyValueClass())
- @JvmStatic
- fun requestStatusSource(): Set<RequestStatus> {
- return setOf(
- RequestStatus.SUCCESS,
- RequestStatus.WARNING,
- RequestStatus.ERROR,
- RequestStatus.DUPLICATION
- )
- }
+ val event =
+ ResponseEvent(RequestId("TestID1234"), Instant.parse("2023-09-09T00:00:00Z"), requestStatus)
- }
+ this.responseProcessor.handleResponseEvent(event)
-} \ No newline at end of file
+ val captor = argumentCaptor<Request>()
+ verify(requestService, times(1)).save(captor.capture())
+ assertThat(captor.firstValue).isNotNull
+ assertThat(captor.firstValue.status).isEqualTo(requestStatus)
+ }
+
+ companion object {
+
+ @JvmStatic
+ fun requestStatusSource(): Set<RequestStatus> {
+ return setOf(
+ RequestStatus.SUCCESS,
+ RequestStatus.WARNING,
+ RequestStatus.ERROR,
+ RequestStatus.DUPLICATION,
+ )
+ }
+ }
+}
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 ba9d23f..c6438e8 100644
--- a/src/test/kotlin/dev/dnpm/etl/processor/services/TransformationServiceTest.kt
+++ b/src/test/kotlin/dev/dnpm/etl/processor/services/TransformationServiceTest.kt
@@ -21,106 +21,139 @@ package dev.dnpm.etl.processor.services
import dev.dnpm.etl.processor.config.JacksonConfig
import dev.pcvolkmer.mv64e.mtb.*
+import java.time.Instant
+import java.util.Date
import org.assertj.core.api.Assertions.assertThat
+import org.hl7.fhir.instance.model.api.IBaseResource
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
-import org.hl7.fhir.instance.model.api.IBaseResource
-import java.time.Instant
-import java.util.Date
class TransformationServiceTest {
- private lateinit var service: TransformationService
+ private lateinit var service: TransformationService
- @BeforeEach
- fun setup() {
- this.service = TransformationService(
- JacksonConfig().objectMapper(), listOf(
+ @BeforeEach
+ fun setup() {
+ this.service =
+ TransformationService(
+ JacksonConfig().objectMapper(),
+ listOf(
Transformation.of("diagnoses[*].code.version") from "2013" to "2014",
- )
+ ),
)
- }
-
- @Test
- fun shouldTransformMtbFile() {
- val mtbFile = Mtb.builder().diagnoses(
- listOf(
- MtbDiagnosis.builder().id("1234").code(Coding.builder().code("F79.9").version("2013").build()).build()
+ }
+
+ @Test
+ fun shouldTransformMtbFile() {
+ val mtbFile =
+ Mtb.builder()
+ .diagnoses(
+ listOf(
+ 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].code.version).isEqualTo("2014")
- }
-
- @Test
- fun shouldOnlyTransformGivenValues() {
- val mtbFile = Mtb.builder().diagnoses(
- listOf(
- 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].code.version).isEqualTo("2014")
+ }
+
+ @Test
+ fun shouldOnlyTransformGivenValues() {
+ val mtbFile =
+ Mtb.builder()
+ .diagnoses(
+ listOf(
+ 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)
+ .build()
+
+ val actual = this.service.transform(mtbFile)
+
+ assertThat(actual).isNotNull
+ 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 =
+ Mtb.builder()
+ .diagnoses(
+ listOf(
+ 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].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 shouldTransformConsent() {
+ val mvhMetadata = MvhMetadata.builder().transferTan("transfertan12345").build()
+
+ assertThat(mvhMetadata).isNotNull
+ mvhMetadata.modelProjectConsent =
+ ModelProjectConsent.builder()
+ .date(Date.from(Instant.parse("2025-08-15T00:00:00.00Z")))
+ .version("1")
+ .provisions(
+ listOf(
+ Provision.builder()
+ .type(ConsentProvision.PERMIT)
+ .purpose(ModelProjectConsentPurpose.SEQUENCING)
+ .date(Date.from(Instant.parse("2025-08-15T00:00:00.00Z")))
+ .build(),
+ Provision.builder()
+ .type(ConsentProvision.PERMIT)
+ .purpose(ModelProjectConsentPurpose.REIDENTIFICATION)
+ .date(Date.from(Instant.parse("2025-08-15T00:00:00.00Z")))
+ .build(),
+ Provision.builder()
+ .type(ConsentProvision.DENY)
+ .purpose(ModelProjectConsentPurpose.CASE_IDENTIFICATION)
+ .date(Date.from(Instant.parse("2025-08-15T00:00:00.00Z")))
+ .build(),
+ )
+ )
+ .build()
+ val consent = ConsentProcessorTest.getDummyGenomDeConsent()
- assertThat(actual).isNotNull
- 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")
- }
+ mvhMetadata.researchConsents = mutableListOf()
+ mvhMetadata.researchConsents.add(mapOf(consent.id to consent as IBaseResource))
- @Test
- fun shouldTransformConsentValues() {
- val mtbFile = Mtb.builder().diagnoses(
- listOf(
- 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].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 shouldTransformConsent() {
- val mvhMetadata = MvhMetadata.builder().transferTan("transfertan12345").build()
-
- assertThat(mvhMetadata).isNotNull
- mvhMetadata.modelProjectConsent =
- ModelProjectConsent.builder().date(Date.from(Instant.parse("2025-08-15T00:00:00.00Z")))
- .version("1").provisions(
- listOf(
- Provision.builder().type(ConsentProvision.PERMIT)
- .purpose(ModelProjectConsentPurpose.SEQUENCING)
- .date(Date.from(Instant.parse("2025-08-15T00:00:00.00Z"))).build(),
- Provision.builder().type(ConsentProvision.PERMIT)
- .purpose(ModelProjectConsentPurpose.REIDENTIFICATION)
- .date(Date.from(Instant.parse("2025-08-15T00:00:00.00Z"))).build(),
- Provision.builder().type(ConsentProvision.DENY)
- .purpose(ModelProjectConsentPurpose.CASE_IDENTIFICATION)
- .date(Date.from(Instant.parse("2025-08-15T00:00:00.00Z"))).build()
- )
- ).build()
- val consent = ConsentProcessorTest.getDummyGenomDeConsent()
-
- mvhMetadata.researchConsents = mutableListOf()
- mvhMetadata.researchConsents.add(mapOf(consent.id to consent as IBaseResource))
-
- val mtbFile = Mtb.builder().metadata(mvhMetadata).build()
-
- val transformed = service.transform(mtbFile)
- assertThat(transformed.metadata.modelProjectConsent.date).isNotNull
-
- }
+ val mtbFile = Mtb.builder().metadata(mvhMetadata).build()
+ val transformed = service.transform(mtbFile)
+ assertThat(transformed.metadata.modelProjectConsent.date).isNotNull
+ }
}
diff --git a/src/test/kotlin/dev/dnpm/etl/processor/services/kafka/KafkaResponseProcessorTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/services/kafka/KafkaResponseProcessorTest.kt
index 95bf41b..8d5024a 100644
--- a/src/test/kotlin/dev/dnpm/etl/processor/services/kafka/KafkaResponseProcessorTest.kt
+++ b/src/test/kotlin/dev/dnpm/etl/processor/services/kafka/KafkaResponseProcessorTest.kt
@@ -39,7 +39,6 @@ import org.springframework.http.HttpStatus
@ExtendWith(MockitoExtension::class)
class KafkaResponseProcessorTest {
-
private lateinit var eventPublisher: ApplicationEventPublisher
private lateinit var objectMapper: ObjectMapper
@@ -48,9 +47,9 @@ class KafkaResponseProcessorTest {
private fun createKafkaRecord(
requestId: String,
statusCode: Int = 200,
- statusBody: Map<String, Any>? = mapOf()
- ): ConsumerRecord<String, String> {
- return ConsumerRecord<String, String>(
+ statusBody: Map<String, Any>? = mapOf(),
+ ): ConsumerRecord<String, String> =
+ ConsumerRecord<String, String>(
"test-topic",
0,
0,
@@ -58,14 +57,15 @@ class KafkaResponseProcessorTest {
if (statusBody == null) {
""
} else {
- this.objectMapper.writeValueAsString(KafkaResponseProcessor.ResponseBody(requestId, statusCode, statusBody))
- }
+ this.objectMapper.writeValueAsString(
+ KafkaResponseProcessor.ResponseBody(requestId, statusCode, statusBody),
+ )
+ },
)
- }
@BeforeEach
fun setup(
- @Mock eventPublisher: ApplicationEventPublisher
+ @Mock eventPublisher: ApplicationEventPublisher,
) {
this.eventPublisher = eventPublisher
this.objectMapper = ObjectMapper().registerModule(KotlinModule.Builder().build())
@@ -75,18 +75,19 @@ class KafkaResponseProcessorTest {
@Test
fun shouldNotProcessRecordsWithoutRequestIdInBody() {
- val record = ConsumerRecord<String, String>(
- "test-topic",
- 0,
- 0,
- null,
- """
+ val record =
+ ConsumerRecord<String, String>(
+ "test-topic",
+ 0,
+ 0,
+ null,
+ """
{
"statusCode": 200,
"statusBody": {}
}
- """.trimIndent()
- )
+ """.trimIndent(),
+ )
this.kafkaResponseProcessor.onMessage(record)
@@ -95,19 +96,20 @@ class KafkaResponseProcessorTest {
@Test
fun shouldProcessRecordsWithAliasNames() {
- val record = ConsumerRecord<String, String>(
- "test-topic",
- 0,
- 0,
- null,
- """
+ val record =
+ ConsumerRecord<String, String>(
+ "test-topic",
+ 0,
+ 0,
+ null,
+ """
{
"request_id": "test0123456789",
"status_code": 200,
"status_body": {}
}
- """.trimIndent()
- )
+ """.trimIndent(),
+ )
this.kafkaResponseProcessor.onMessage(record)
@@ -116,7 +118,9 @@ class KafkaResponseProcessorTest {
@Test
fun shouldNotProcessRecordsWithoutValidStatusBody() {
- this.kafkaResponseProcessor.onMessage(createKafkaRecord(requestId = "TestID1234", statusBody = null))
+ this.kafkaResponseProcessor.onMessage(
+ createKafkaRecord(requestId = "TestID1234", statusBody = null),
+ )
verify(eventPublisher, never()).publishEvent(any<ResponseEvent>())
}
@@ -129,21 +133,16 @@ class KafkaResponseProcessorTest {
}
companion object {
-
@JvmStatic
- fun statusCodeSource(): Set<Int> {
- return setOf(
+ fun statusCodeSource(): Set<Int> =
+ setOf(
HttpStatus.OK,
HttpStatus.CREATED,
HttpStatus.BAD_REQUEST,
HttpStatus.NOT_FOUND,
HttpStatus.UNPROCESSABLE_ENTITY,
- HttpStatus.INTERNAL_SERVER_ERROR
- )
- .map { it.value() }
+ HttpStatus.INTERNAL_SERVER_ERROR,
+ ).map { it.value() }
.toSet()
- }
-
}
-
-} \ No newline at end of file
+}