summaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'src/main')
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/monitoring/ConnectionCheckService.kt107
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/web/ConfigController.kt2
-rw-r--r--src/main/resources/templates/configs/gPasConnectionAvailable.html15
-rw-r--r--src/main/resources/templates/configs/outputConnectionAvailable.html42
4 files changed, 112 insertions, 54 deletions
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 81ad922..1afaa32 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/monitoring/ConnectionCheckService.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/monitoring/ConnectionCheckService.kt
@@ -26,22 +26,18 @@ import jakarta.annotation.PostConstruct
import org.apache.kafka.clients.consumer.Consumer
import org.apache.kafka.common.errors.TimeoutException
import org.springframework.beans.factory.annotation.Qualifier
-import org.springframework.http.HttpEntity
-import org.springframework.http.HttpHeaders
-import org.springframework.http.HttpMethod
-import org.springframework.http.HttpStatus
-import org.springframework.http.MediaType
-import org.springframework.http.RequestEntity
+import org.springframework.http.*
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
interface ConnectionCheckService {
- fun connectionAvailable(): Boolean
+ fun connectionAvailable(): ConnectionCheckResult
}
@@ -51,9 +47,27 @@ sealed class ConnectionCheckResult {
abstract val available: Boolean
- data class KafkaConnectionCheckResult(override val available: Boolean) : ConnectionCheckResult()
- data class RestConnectionCheckResult(override val available: Boolean) : ConnectionCheckResult()
- data class GPasConnectionCheckResult(override val available: Boolean) : ConnectionCheckResult()
+ abstract val timestamp: Instant
+
+ abstract val lastChange: Instant
+
+ 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 GPasConnectionCheckResult(
+ override val available: Boolean,
+ override val timestamp: Instant,
+ override val lastChange: Instant
+ ) : ConnectionCheckResult()
}
class KafkaConnectionCheckService(
@@ -62,25 +76,33 @@ class KafkaConnectionCheckService(
private val connectionCheckUpdateProducer: Sinks.Many<ConnectionCheckResult>
) : OutputConnectionCheckService {
- private var connectionAvailable: Boolean = false
-
+ private var result = ConnectionCheckResult.KafkaConnectionCheckResult(false, Instant.now(), Instant.now())
@PostConstruct
@Scheduled(cron = "0 * * * * *")
fun check() {
- connectionAvailable = try {
- null != consumer.listTopics(5.seconds.toJavaDuration())
+ 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 (e: TimeoutException) {
- false
+ ConnectionCheckResult.KafkaConnectionCheckResult(
+ false,
+ Instant.now(),
+ if (!result.available) { result.lastChange } else { Instant.now() }
+ )
}
connectionCheckUpdateProducer.emitNext(
- ConnectionCheckResult.KafkaConnectionCheckResult(connectionAvailable),
+ result,
Sinks.EmitFailureHandler.FAIL_FAST
)
}
- override fun connectionAvailable(): Boolean {
- return this.connectionAvailable
+ override fun connectionAvailable(): ConnectionCheckResult.KafkaConnectionCheckResult {
+ return this.result
}
}
@@ -92,27 +114,37 @@ class RestConnectionCheckService(
private val connectionCheckUpdateProducer: Sinks.Many<ConnectionCheckResult>
) : OutputConnectionCheckService {
- private var connectionAvailable: Boolean = false
+ private var result = ConnectionCheckResult.RestConnectionCheckResult(false, Instant.now(), Instant.now())
@PostConstruct
@Scheduled(cron = "0 * * * * *")
fun check() {
- connectionAvailable = try {
- restTemplate.getForEntity(
+ result = try {
+ val available = restTemplate.getForEntity(
restTargetProperties.uri?.replace("/etl/api", "").toString(),
String::class.java
).statusCode == HttpStatus.OK
+
+ ConnectionCheckResult.RestConnectionCheckResult(
+ available,
+ Instant.now(),
+ if (result.available == available) { result.lastChange } else { Instant.now() }
+ )
} catch (e: Exception) {
- false
+ ConnectionCheckResult.RestConnectionCheckResult(
+ false,
+ Instant.now(),
+ if (!result.available) { result.lastChange } else { Instant.now() }
+ )
}
connectionCheckUpdateProducer.emitNext(
- ConnectionCheckResult.RestConnectionCheckResult(connectionAvailable),
+ result,
Sinks.EmitFailureHandler.FAIL_FAST
)
}
- override fun connectionAvailable(): Boolean {
- return this.connectionAvailable
+ override fun connectionAvailable(): ConnectionCheckResult.RestConnectionCheckResult {
+ return this.result
}
}
@@ -123,12 +155,12 @@ class GPasConnectionCheckService(
private val connectionCheckUpdateProducer: Sinks.Many<ConnectionCheckResult>
) : ConnectionCheckService {
- private var connectionAvailable: Boolean = false
+ private var result = ConnectionCheckResult.GPasConnectionCheckResult(false, Instant.now(), Instant.now())
@PostConstruct
@Scheduled(cron = "0 * * * * *")
fun check() {
- connectionAvailable = try {
+ result = try {
val uri = UriComponentsBuilder.fromUriString(
gPasConfigProperties.uri?.replace("/\$pseudonymizeAllowCreate", "/\$pseudonymize").toString()
)
@@ -141,22 +173,33 @@ class GPasConnectionCheckService(
if (!gPasConfigProperties.username.isNullOrBlank() && !gPasConfigProperties.password.isNullOrBlank()) {
headers.setBasicAuth(gPasConfigProperties.username, gPasConfigProperties.password)
}
- restTemplate.exchange(
+
+ val available = restTemplate.exchange(
uri,
HttpMethod.GET,
HttpEntity<Void>(headers),
Void::class.java
).statusCode == HttpStatus.OK
+
+ ConnectionCheckResult.GPasConnectionCheckResult(
+ available,
+ Instant.now(),
+ if (result.available == available) { result.lastChange } else { Instant.now() }
+ )
} catch (e: Exception) {
- false
+ ConnectionCheckResult.GPasConnectionCheckResult(
+ false,
+ Instant.now(),
+ if (!result.available) { result.lastChange } else { Instant.now() }
+ )
}
connectionCheckUpdateProducer.emitNext(
- ConnectionCheckResult.GPasConnectionCheckResult(connectionAvailable),
+ result,
Sinks.EmitFailureHandler.FAIL_FAST
)
}
- override fun connectionAvailable(): Boolean {
- return this.connectionAvailable
+ override fun connectionAvailable(): ConnectionCheckResult.GPasConnectionCheckResult {
+ return this.result
}
} \ No newline at end of file
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 eb9d541..36589c8 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/web/ConfigController.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/web/ConfigController.kt
@@ -56,7 +56,7 @@ class ConfigController(
@GetMapping
fun index(model: Model): String {
val outputConnectionAvailable =
- connectionCheckServices.filterIsInstance<OutputConnectionCheckService>().first().connectionAvailable()
+ connectionCheckServices.filterIsInstance<OutputConnectionCheckService>().firstOrNull()?.connectionAvailable()
val gPasConnectionAvailable =
connectionCheckServices.filterIsInstance<GPasConnectionCheckService>().firstOrNull()?.connectionAvailable()
diff --git a/src/main/resources/templates/configs/gPasConnectionAvailable.html b/src/main/resources/templates/configs/gPasConnectionAvailable.html
index 6dccc60..a9a8517 100644
--- a/src/main/resources/templates/configs/gPasConnectionAvailable.html
+++ b/src/main/resources/templates/configs/gPasConnectionAvailable.html
@@ -2,15 +2,20 @@
<h2><span>🟦</span> gPAS nicht konfiguriert - Patienten-IDs werden intern anonymisiert</h2>
</th:block>
<th:block th:if="${gPasConnectionAvailable != null}">
- <h2><span th:if="${gPasConnectionAvailable}">✅</span><span th:if="${not(gPasConnectionAvailable)}">⚡</span> Verbindung zu gPAS</h2>
+ <h2><span th:if="${gPasConnectionAvailable.available}">✅</span><span th:if="${not(gPasConnectionAvailable.available)}">⚡</span> Verbindung zu gPAS</h2>
<div>
- Die Verbindung ist aktuell
- <strong th:if="${gPasConnectionAvailable}" style="color: green">verfügbar.</strong>
- <strong th:if="${not(gPasConnectionAvailable)}" style="color: red">nicht verfügbar.</strong>
+ Stand: <time style="font-weight: bold" th:datetime="${#temporals.formatISO(gPasConnectionAvailable.timestamp)}" th:text="${#temporals.formatISO(gPasConnectionAvailable.timestamp)}"></time>
+ &nbsp;|&nbsp;
+ Letzte Änderung: <time style="font-weight: bold" th:datetime="${#temporals.formatISO(gPasConnectionAvailable.lastChange)}" th:text="${#temporals.formatISO(gPasConnectionAvailable.lastChange)}"></time>
+ </div>
+ <div>
+ <span>Die Verbindung ist aktuell</span>
+ <strong th:if="${gPasConnectionAvailable.available}" style="color: green">verfügbar.</strong>
+ <strong th:if="${not(gPasConnectionAvailable.available)}" style="color: red">nicht verfügbar.</strong>
</div>
<div class="connection-display border">
<img th:src="@{/server.png}" alt="ETL-Processor" />
- <span class="connection" th:classappend="${gPasConnectionAvailable ? 'available' : ''}"></span>
+ <span class="connection" th:classappend="${gPasConnectionAvailable.available ? 'available' : ''}"></span>
<img th:src="@{/server.png}" alt="gPAS" />
<span>ETL-Processor</span>
<span></span>
diff --git a/src/main/resources/templates/configs/outputConnectionAvailable.html b/src/main/resources/templates/configs/outputConnectionAvailable.html
index 2b18b75..4b7f8d1 100644
--- a/src/main/resources/templates/configs/outputConnectionAvailable.html
+++ b/src/main/resources/templates/configs/outputConnectionAvailable.html
@@ -1,16 +1,26 @@
-<h2><span th:if="${outputConnectionAvailable}">✅</span><span th:if="${not(outputConnectionAvailable)}">⚡</span> MTB-File Verbindung</h2>
-<div>
- Verbindung über <code>[[ ${mtbFileSender} ]]</code>. Die Verbindung ist aktuell
- <strong th:if="${outputConnectionAvailable}" style="color: green">verfügbar.</strong>
- <strong th:if="${not(outputConnectionAvailable)}" style="color: red">nicht verfügbar.</strong>
-</div>
-<div class="connection-display border">
- <img th:src="@{/server.png}" alt="ETL-Processor" />
- <span class="connection" th:classappend="${outputConnectionAvailable ? 'available' : ''}"></span>
- <img th:if="${mtbFileSender.startsWith('Rest')}" th:src="@{/server.png}" alt="bwHC-Backend" />
- <img th:if="${mtbFileSender.startsWith('Kafka')}" th:src="@{/kafka.png}" alt="Kafka-Broker" />
- <span>ETL-Processor</span>
- <span></span>
- <span th:if="${mtbFileSender.startsWith('Rest')}">bwHC-Backend</span>
- <span th:if="${mtbFileSender.startsWith('Kafka')}">Kafka-Broker</span>
-</div> \ No newline at end of file
+<th:block th:if="${outputConnectionAvailable == null}">
+ <h2><span>🟦</span> Keine Ausgabenkonfiguration</h2>
+</th:block>
+<th:block th:if="${outputConnectionAvailable != null}">
+ <h2><span th:if="${outputConnectionAvailable.available}">✅</span><span th:if="${not(outputConnectionAvailable.available)}">⚡</span> MTB-File Verbindung</h2>
+ <div>
+ Stand: <time style="font-weight: bold" th:datetime="${#temporals.formatISO(outputConnectionAvailable.timestamp)}" th:text="${#temporals.formatISO(outputConnectionAvailable.timestamp)}"></time>
+ &nbsp;|&nbsp;
+ Letzte Änderung: <time style="font-weight: bold" th:datetime="${#temporals.formatISO(outputConnectionAvailable.lastChange)}" th:text="${#temporals.formatISO(outputConnectionAvailable.lastChange)}"></time>
+ </div>
+ <div>
+ Verbindung über <code>[[ ${mtbFileSender} ]]</code>. Die Verbindung ist aktuell
+ <strong th:if="${outputConnectionAvailable.available}" style="color: green">verfügbar.</strong>
+ <strong th:if="${not(outputConnectionAvailable.available)}" style="color: red">nicht verfügbar.</strong>
+ </div>
+ <div class="connection-display border">
+ <img th:src="@{/server.png}" alt="ETL-Processor" />
+ <span class="connection" th:classappend="${outputConnectionAvailable.available ? 'available' : ''}"></span>
+ <img th:if="${mtbFileSender.startsWith('Rest')}" th:src="@{/server.png}" alt="bwHC-Backend" />
+ <img th:if="${mtbFileSender.startsWith('Kafka')}" th:src="@{/kafka.png}" alt="Kafka-Broker" />
+ <span>ETL-Processor</span>
+ <span></span>
+ <span th:if="${mtbFileSender.startsWith('Rest')}">bwHC-Backend</span>
+ <span th:if="${mtbFileSender.startsWith('Kafka')}">Kafka-Broker</span>
+ </div>
+</th:block> \ No newline at end of file