diff options
Diffstat (limited to 'src/main/kotlin')
4 files changed, 110 insertions, 1 deletions
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 f0766a4..5cec5eb 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfigProperties.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfigProperties.kt @@ -63,6 +63,18 @@ data class GPasConfigProperties( } } +@ConfigurationProperties(VfpsConfigProperties.NAME) +data class VfpsConfigProperties( + val uri: String? = null, + val patientNamespace: String = "etl-processor", + val genomDeTanNamespace: String = "ccdn", + val token: String? = null, +) { + companion object { + const val NAME = "app.pseudonymize.vfps" + } +} + @ConfigurationProperties(ConsentConfigProperties.NAME) data class ConsentConfigProperties( var service: ConsentService = ConsentService.NONE, @@ -177,6 +189,7 @@ data class UserProperties( enum class PseudonymGenerator { BUILDIN, GPAS, + VFPS, } enum class ConsentService { 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 54f4c6f..44fd719 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfiguration.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfiguration.kt @@ -68,6 +68,7 @@ import kotlin.time.toJavaDuration AppConfigProperties::class, PseudonymizeConfigProperties::class, GPasConfigProperties::class, + VfpsConfigProperties::class, ConsentConfigProperties::class, GIcsConfigProperties::class, ] @@ -257,6 +258,17 @@ class AppConfiguration { ) } + @ConditionalOnProperty(value = ["app.pseudonymize.generator"], havingValue = "VFPS") + @Bean + fun vfpsPseudonymGenerator( + configProperties: VfpsConfigProperties, + retryTemplate: RetryTemplate, + restTemplate: RestTemplate, + ): Generator { + logger.info("Selected 'VfpsPseudonym Generator'") + return VfpsPseudonymGenerator(configProperties, retryTemplate, restTemplate) + } + @Bean fun jdbcConfiguration(): AbstractJdbcConfiguration { return AppJdbcConfiguration() 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 9eec9f3..c74345d 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/PseudonymizeService.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/PseudonymizeService.kt @@ -30,7 +30,9 @@ class PseudonymizeService( ) { fun patientPseudonym(patientId: PatientId): PatientPseudonym = when (generator) { - is GpasPseudonymGenerator -> PatientPseudonym(generator.generate(patientId.value)) + is GpasPseudonymGenerator, + is VfpsPseudonymGenerator, + -> PatientPseudonym(generator.generate(patientId.value)) else -> PatientPseudonym("${configProperties.prefix}_${generator.generate(patientId.value)}") } diff --git a/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/VfpsPseudonymGenerator.kt b/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/VfpsPseudonymGenerator.kt new file mode 100644 index 0000000..0a616b5 --- /dev/null +++ b/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/VfpsPseudonymGenerator.kt @@ -0,0 +1,82 @@ +package dev.dnpm.etl.processor.pseudonym + +import dev.dnpm.etl.processor.config.VfpsConfigProperties +import org.springframework.http.HttpEntity +import org.springframework.http.HttpHeaders +import org.springframework.http.MediaType +import org.springframework.retry.support.RetryTemplate +import org.springframework.web.client.RestClientException +import org.springframework.web.client.RestTemplate +import org.springframework.web.client.postForObject +import org.springframework.web.util.UriComponentsBuilder + +class VfpsPseudonymGenerator( + private val configProperties: VfpsConfigProperties, + private val retryTemplate: RetryTemplate, + private val restTemplate: RestTemplate, +) : Generator { + private val uri = + configProperties.uri?.takeIf { it.isNotBlank() } + ?: throw IllegalArgumentException("vFPS URI must be configured") + + private val headers = + HttpHeaders().apply { + contentType = MediaType.APPLICATION_JSON + configProperties.token + ?.takeIf { it.isNotBlank() } + ?.let { setBearerAuth(it) } + } + + override fun generate(id: String): String = + execute { + retryTemplate.execute<String, Exception> { + request(configProperties.patientNamespace, "pseudonyms", id) + } + } + + override fun generateGenomDeTan(id: String): String = + execute { + request(configProperties.genomDeTanNamespace, "pseudonyms:append", id) + } + + private fun request( + namespace: String, + operation: String, + id: String, + ): String { + val url = + UriComponentsBuilder + .fromUriString(uri) + .pathSegment("v1", "namespaces", namespace, operation) + .build() + .encode() + .toUri() + + val response = + restTemplate.postForObject<VfpsResponse>( + url, + HttpEntity(mapOf("originalValue" to id), headers), + ) + + return response + ?.pseudonym + ?.pseudonymValue + ?.takeIf { it.isNotBlank() } + ?: throw PseudonymRequestFailed("No pseudonym returned from vFPS") + } + + private fun execute(action: () -> String): String = + try { + action() + } catch (ex: RestClientException) { + throw PseudonymRequestFailed("vFPS request failed", ex) + } +} + +private data class VfpsResponse( + val pseudonym: VfpsPseudonym? = null, +) + +private data class VfpsPseudonym( + val pseudonymValue: String? = null, +) |
