mirror of
https://github.com/pcvolkmer/etl-processor.git
synced 2025-04-19 17:26:51 +00:00
feat: use issue severity to create status (#90)
This commit is contained in:
parent
98b971d7db
commit
befeef3153
21
README.md
21
README.md
@ -264,6 +264,27 @@ ein Consent-Widerspruch erfolgte.
|
|||||||
|
|
||||||
Dieses Vorgehen empfiehlt sich, wenn Sie gespeicherte Records nachgelagert für andere Auswertungen verwenden möchten.
|
Dieses Vorgehen empfiehlt sich, wenn Sie gespeicherte Records nachgelagert für andere Auswertungen verwenden möchten.
|
||||||
|
|
||||||
|
### Antworten und Statusauswertung
|
||||||
|
|
||||||
|
Anfragen and bwHC-Backend aus Versionen bis 0.9.x wurden wie folgt behandelt:
|
||||||
|
|
||||||
|
| HTTP-Response | Status |
|
||||||
|
|----------------|-----------|
|
||||||
|
| `HTTP 200` | `SUCCESS` |
|
||||||
|
| `HTTP 201` | `WARNING` |
|
||||||
|
| `HTTP 400-...` | `ERROR` |
|
||||||
|
|
||||||
|
Dies konnte dazu führen, dass zwar mit einem `HTTP 201` geantwortet wurde, aber dennoch in der Issue-Liste die
|
||||||
|
Severity `error` aufgetaucht ist.
|
||||||
|
|
||||||
|
Ab Version 0.10 wird die Issue-Liste der Antwort verwendet und die darion enthaltene höchste Severity-Stufe als Ergebnis verwendet.
|
||||||
|
|
||||||
|
| Höchste Severity | Status |
|
||||||
|
|------------------|-----------|
|
||||||
|
| `info` | `SUCCESS` |
|
||||||
|
| `warning` | `WARNING` |
|
||||||
|
| `error`, `fatal` | `ERROR` |
|
||||||
|
|
||||||
## Docker-Images
|
## Docker-Images
|
||||||
|
|
||||||
Diese Anwendung ist auch als Docker-Image verfügbar: https://github.com/pcvolkmer/etl-processor/pkgs/container/etl-processor
|
Diese Anwendung ist auch als Docker-Image verfügbar: https://github.com/pcvolkmer/etl-processor/pkgs/container/etl-processor
|
||||||
|
@ -21,6 +21,7 @@ package dev.dnpm.etl.processor.config
|
|||||||
|
|
||||||
import dev.dnpm.etl.processor.monitoring.ConnectionCheckResult
|
import dev.dnpm.etl.processor.monitoring.ConnectionCheckResult
|
||||||
import dev.dnpm.etl.processor.monitoring.ConnectionCheckService
|
import dev.dnpm.etl.processor.monitoring.ConnectionCheckService
|
||||||
|
import dev.dnpm.etl.processor.monitoring.ReportService
|
||||||
import dev.dnpm.etl.processor.monitoring.RestConnectionCheckService
|
import dev.dnpm.etl.processor.monitoring.RestConnectionCheckService
|
||||||
import dev.dnpm.etl.processor.output.MtbFileSender
|
import dev.dnpm.etl.processor.output.MtbFileSender
|
||||||
import dev.dnpm.etl.processor.output.RestBwhcMtbFileSender
|
import dev.dnpm.etl.processor.output.RestBwhcMtbFileSender
|
||||||
@ -53,15 +54,16 @@ class AppRestConfiguration {
|
|||||||
fun restMtbFileSender(
|
fun restMtbFileSender(
|
||||||
restTemplate: RestTemplate,
|
restTemplate: RestTemplate,
|
||||||
restTargetProperties: RestTargetProperties,
|
restTargetProperties: RestTargetProperties,
|
||||||
retryTemplate: RetryTemplate
|
retryTemplate: RetryTemplate,
|
||||||
|
reportService: ReportService,
|
||||||
): MtbFileSender {
|
): MtbFileSender {
|
||||||
if (restTargetProperties.isBwhc) {
|
if (restTargetProperties.isBwhc) {
|
||||||
logger.info("Selected 'RestBwhcMtbFileSender'")
|
logger.info("Selected 'RestBwhcMtbFileSender'")
|
||||||
return RestBwhcMtbFileSender(restTemplate, restTargetProperties, retryTemplate)
|
return RestBwhcMtbFileSender(restTemplate, restTargetProperties, retryTemplate, reportService)
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("Selected 'RestDipMtbFileSender'")
|
logger.info("Selected 'RestDipMtbFileSender'")
|
||||||
return RestDipMtbFileSender(restTemplate, restTargetProperties, retryTemplate)
|
return RestDipMtbFileSender(restTemplate, restTargetProperties, retryTemplate, reportService)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
@ -25,6 +25,8 @@ import com.fasterxml.jackson.annotation.JsonValue
|
|||||||
import com.fasterxml.jackson.core.JsonParseException
|
import com.fasterxml.jackson.core.JsonParseException
|
||||||
import com.fasterxml.jackson.databind.JsonMappingException
|
import com.fasterxml.jackson.databind.JsonMappingException
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import dev.dnpm.etl.processor.monitoring.ReportService.Issue
|
||||||
|
import dev.dnpm.etl.processor.monitoring.ReportService.Severity
|
||||||
|
|
||||||
class ReportService(
|
class ReportService(
|
||||||
private val objectMapper: ObjectMapper
|
private val objectMapper: ObjectMapper
|
||||||
@ -64,3 +66,12 @@ class ReportService(
|
|||||||
INFO("info")
|
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
|
||||||
|
}
|
||||||
|
}
|
@ -21,6 +21,7 @@ package dev.dnpm.etl.processor.output
|
|||||||
|
|
||||||
import dev.dnpm.etl.processor.PatientPseudonym
|
import dev.dnpm.etl.processor.PatientPseudonym
|
||||||
import dev.dnpm.etl.processor.config.RestTargetProperties
|
import dev.dnpm.etl.processor.config.RestTargetProperties
|
||||||
|
import dev.dnpm.etl.processor.monitoring.ReportService
|
||||||
import org.springframework.retry.support.RetryTemplate
|
import org.springframework.retry.support.RetryTemplate
|
||||||
import org.springframework.web.client.RestTemplate
|
import org.springframework.web.client.RestTemplate
|
||||||
import org.springframework.web.util.UriComponentsBuilder
|
import org.springframework.web.util.UriComponentsBuilder
|
||||||
@ -28,8 +29,9 @@ import org.springframework.web.util.UriComponentsBuilder
|
|||||||
class RestBwhcMtbFileSender(
|
class RestBwhcMtbFileSender(
|
||||||
restTemplate: RestTemplate,
|
restTemplate: RestTemplate,
|
||||||
private val restTargetProperties: RestTargetProperties,
|
private val restTargetProperties: RestTargetProperties,
|
||||||
retryTemplate: RetryTemplate
|
retryTemplate: RetryTemplate,
|
||||||
) : RestMtbFileSender(restTemplate, restTargetProperties, retryTemplate) {
|
reportService: ReportService,
|
||||||
|
) : RestMtbFileSender(restTemplate, restTargetProperties, retryTemplate, reportService) {
|
||||||
|
|
||||||
override fun sendUrl(): String {
|
override fun sendUrl(): String {
|
||||||
return UriComponentsBuilder
|
return UriComponentsBuilder
|
||||||
|
@ -21,6 +21,7 @@ package dev.dnpm.etl.processor.output
|
|||||||
|
|
||||||
import dev.dnpm.etl.processor.PatientPseudonym
|
import dev.dnpm.etl.processor.PatientPseudonym
|
||||||
import dev.dnpm.etl.processor.config.RestTargetProperties
|
import dev.dnpm.etl.processor.config.RestTargetProperties
|
||||||
|
import dev.dnpm.etl.processor.monitoring.ReportService
|
||||||
import org.springframework.retry.support.RetryTemplate
|
import org.springframework.retry.support.RetryTemplate
|
||||||
import org.springframework.web.client.RestTemplate
|
import org.springframework.web.client.RestTemplate
|
||||||
import org.springframework.web.util.UriComponentsBuilder
|
import org.springframework.web.util.UriComponentsBuilder
|
||||||
@ -28,8 +29,9 @@ import org.springframework.web.util.UriComponentsBuilder
|
|||||||
class RestDipMtbFileSender(
|
class RestDipMtbFileSender(
|
||||||
restTemplate: RestTemplate,
|
restTemplate: RestTemplate,
|
||||||
private val restTargetProperties: RestTargetProperties,
|
private val restTargetProperties: RestTargetProperties,
|
||||||
retryTemplate: RetryTemplate
|
retryTemplate: RetryTemplate,
|
||||||
) : RestMtbFileSender(restTemplate, restTargetProperties, retryTemplate) {
|
reportService: ReportService
|
||||||
|
) : RestMtbFileSender(restTemplate, restTargetProperties, retryTemplate, reportService) {
|
||||||
|
|
||||||
override fun sendUrl(): String {
|
override fun sendUrl(): String {
|
||||||
return UriComponentsBuilder
|
return UriComponentsBuilder
|
||||||
|
@ -22,6 +22,8 @@ package dev.dnpm.etl.processor.output
|
|||||||
import dev.dnpm.etl.processor.config.RestTargetProperties
|
import dev.dnpm.etl.processor.config.RestTargetProperties
|
||||||
import dev.dnpm.etl.processor.monitoring.RequestStatus
|
import dev.dnpm.etl.processor.monitoring.RequestStatus
|
||||||
import dev.dnpm.etl.processor.PatientPseudonym
|
import dev.dnpm.etl.processor.PatientPseudonym
|
||||||
|
import dev.dnpm.etl.processor.monitoring.ReportService
|
||||||
|
import dev.dnpm.etl.processor.monitoring.asRequestStatus
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import org.springframework.http.HttpEntity
|
import org.springframework.http.HttpEntity
|
||||||
import org.springframework.http.HttpHeaders
|
import org.springframework.http.HttpHeaders
|
||||||
@ -34,7 +36,8 @@ import org.springframework.web.client.RestTemplate
|
|||||||
abstract class RestMtbFileSender(
|
abstract class RestMtbFileSender(
|
||||||
private val restTemplate: RestTemplate,
|
private val restTemplate: RestTemplate,
|
||||||
private val restTargetProperties: RestTargetProperties,
|
private val restTargetProperties: RestTargetProperties,
|
||||||
private val retryTemplate: RetryTemplate
|
private val retryTemplate: RetryTemplate,
|
||||||
|
private val reportService: ReportService
|
||||||
) : MtbFileSender {
|
) : MtbFileSender {
|
||||||
|
|
||||||
private val logger = LoggerFactory.getLogger(RestMtbFileSender::class.java)
|
private val logger = LoggerFactory.getLogger(RestMtbFileSender::class.java)
|
||||||
@ -56,19 +59,19 @@ abstract class RestMtbFileSender(
|
|||||||
if (!response.statusCode.is2xxSuccessful) {
|
if (!response.statusCode.is2xxSuccessful) {
|
||||||
logger.warn("Error sending to remote system: {}", response.body)
|
logger.warn("Error sending to remote system: {}", response.body)
|
||||||
return@execute MtbFileSender.Response(
|
return@execute MtbFileSender.Response(
|
||||||
response.statusCode.asRequestStatus(),
|
reportService.deserialize(response.body).asRequestStatus(),
|
||||||
"Status-Code: ${response.statusCode.value()}"
|
"Status-Code: ${response.statusCode.value()}"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
logger.debug("Sent file via RestMtbFileSender")
|
logger.debug("Sent file via RestMtbFileSender")
|
||||||
return@execute MtbFileSender.Response(response.statusCode.asRequestStatus(), response.body.orEmpty())
|
return@execute MtbFileSender.Response(reportService.deserialize(response.body).asRequestStatus(), response.body.orEmpty())
|
||||||
}
|
}
|
||||||
} catch (e: IllegalArgumentException) {
|
} catch (e: IllegalArgumentException) {
|
||||||
logger.error("Not a valid URI to export to: '{}'", restTargetProperties.uri!!)
|
logger.error("Not a valid URI to export to: '{}'", restTargetProperties.uri!!)
|
||||||
} catch (e: RestClientResponseException) {
|
} catch (e: RestClientResponseException) {
|
||||||
logger.info(restTargetProperties.uri!!.toString())
|
logger.info(restTargetProperties.uri!!.toString())
|
||||||
logger.error("Request data not accepted by remote system", e)
|
logger.error("Request data not accepted by remote system", e)
|
||||||
return MtbFileSender.Response(e.statusCode.asRequestStatus(), e.responseBodyAsString)
|
return MtbFileSender.Response(reportService.deserialize(e.responseBodyAsString).asRequestStatus(), e.responseBodyAsString)
|
||||||
}
|
}
|
||||||
return MtbFileSender.Response(RequestStatus.ERROR, "Sonstiger Fehler bei der Übertragung")
|
return MtbFileSender.Response(RequestStatus.ERROR, "Sonstiger Fehler bei der Übertragung")
|
||||||
}
|
}
|
||||||
|
@ -19,14 +19,18 @@
|
|||||||
|
|
||||||
package dev.dnpm.etl.processor.output
|
package dev.dnpm.etl.processor.output
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import com.fasterxml.jackson.module.kotlin.KotlinModule
|
||||||
import de.ukw.ccc.bwhc.dto.*
|
import de.ukw.ccc.bwhc.dto.*
|
||||||
import dev.dnpm.etl.processor.PatientPseudonym
|
import dev.dnpm.etl.processor.PatientPseudonym
|
||||||
import dev.dnpm.etl.processor.RequestId
|
import dev.dnpm.etl.processor.RequestId
|
||||||
import dev.dnpm.etl.processor.config.RestTargetProperties
|
import dev.dnpm.etl.processor.config.RestTargetProperties
|
||||||
|
import dev.dnpm.etl.processor.monitoring.ReportService
|
||||||
import dev.dnpm.etl.processor.monitoring.RequestStatus
|
import dev.dnpm.etl.processor.monitoring.RequestStatus
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.junit.jupiter.api.BeforeEach
|
import org.junit.jupiter.api.BeforeEach
|
||||||
import org.junit.jupiter.params.ParameterizedTest
|
import org.junit.jupiter.params.ParameterizedTest
|
||||||
|
import org.junit.jupiter.params.provider.Arguments
|
||||||
import org.junit.jupiter.params.provider.MethodSource
|
import org.junit.jupiter.params.provider.MethodSource
|
||||||
import org.springframework.http.HttpMethod
|
import org.springframework.http.HttpMethod
|
||||||
import org.springframework.http.HttpStatus
|
import org.springframework.http.HttpStatus
|
||||||
@ -45,6 +49,8 @@ class RestBwhcMtbFileSenderTest {
|
|||||||
|
|
||||||
private lateinit var restMtbFileSender: RestMtbFileSender
|
private lateinit var restMtbFileSender: RestMtbFileSender
|
||||||
|
|
||||||
|
private var reportService = ReportService(ObjectMapper().registerModule(KotlinModule.Builder().build()))
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
fun setup() {
|
fun setup() {
|
||||||
val restTemplate = RestTemplate()
|
val restTemplate = RestTemplate()
|
||||||
@ -53,7 +59,8 @@ class RestBwhcMtbFileSenderTest {
|
|||||||
|
|
||||||
this.mockRestServiceServer = MockRestServiceServer.createServer(restTemplate)
|
this.mockRestServiceServer = MockRestServiceServer.createServer(restTemplate)
|
||||||
|
|
||||||
this.restMtbFileSender = RestBwhcMtbFileSender(restTemplate, restTargetProperties, retryTemplate)
|
this.restMtbFileSender =
|
||||||
|
RestBwhcMtbFileSender(restTemplate, restTargetProperties, retryTemplate, reportService)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@ -94,7 +101,8 @@ class RestBwhcMtbFileSenderTest {
|
|||||||
val retryTemplate = RetryTemplateBuilder().customPolicy(SimpleRetryPolicy(3)).build()
|
val retryTemplate = RetryTemplateBuilder().customPolicy(SimpleRetryPolicy(3)).build()
|
||||||
|
|
||||||
this.mockRestServiceServer = MockRestServiceServer.createServer(restTemplate)
|
this.mockRestServiceServer = MockRestServiceServer.createServer(restTemplate)
|
||||||
this.restMtbFileSender = RestBwhcMtbFileSender(restTemplate, restTargetProperties, retryTemplate)
|
this.restMtbFileSender =
|
||||||
|
RestBwhcMtbFileSender(restTemplate, restTargetProperties, retryTemplate, reportService)
|
||||||
|
|
||||||
val expectedCount = when (requestWithResponse.httpStatus) {
|
val expectedCount = when (requestWithResponse.httpStatus) {
|
||||||
// OK - No Retry
|
// OK - No Retry
|
||||||
@ -123,7 +131,8 @@ class RestBwhcMtbFileSenderTest {
|
|||||||
val retryTemplate = RetryTemplateBuilder().customPolicy(SimpleRetryPolicy(3)).build()
|
val retryTemplate = RetryTemplateBuilder().customPolicy(SimpleRetryPolicy(3)).build()
|
||||||
|
|
||||||
this.mockRestServiceServer = MockRestServiceServer.createServer(restTemplate)
|
this.mockRestServiceServer = MockRestServiceServer.createServer(restTemplate)
|
||||||
this.restMtbFileSender = RestBwhcMtbFileSender(restTemplate, restTargetProperties, retryTemplate)
|
this.restMtbFileSender =
|
||||||
|
RestBwhcMtbFileSender(restTemplate, restTargetProperties, retryTemplate, reportService)
|
||||||
|
|
||||||
val expectedCount = when (requestWithResponse.httpStatus) {
|
val expectedCount = when (requestWithResponse.httpStatus) {
|
||||||
// OK - No Retry
|
// OK - No Retry
|
||||||
@ -154,24 +163,6 @@ class RestBwhcMtbFileSenderTest {
|
|||||||
val TEST_REQUEST_ID = RequestId("TestId")
|
val TEST_REQUEST_ID = RequestId("TestId")
|
||||||
val TEST_PATIENT_PSEUDONYM = PatientPseudonym("PID")
|
val TEST_PATIENT_PSEUDONYM = PatientPseudonym("PID")
|
||||||
|
|
||||||
private val warningBody = """
|
|
||||||
{
|
|
||||||
"patient_id": "PID",
|
|
||||||
"issues": [
|
|
||||||
{ "severity": "warning", "message": "Something is not right" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
""".trimIndent()
|
|
||||||
|
|
||||||
private val errorBody = """
|
|
||||||
{
|
|
||||||
"patient_id": "PID",
|
|
||||||
"issues": [
|
|
||||||
{ "severity": "error", "message": "Something is very bad" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
""".trimIndent()
|
|
||||||
|
|
||||||
val mtbFile: MtbFile = MtbFile.builder()
|
val mtbFile: MtbFile = MtbFile.builder()
|
||||||
.withPatient(
|
.withPatient(
|
||||||
Patient.builder()
|
Patient.builder()
|
||||||
@ -205,21 +196,34 @@ class RestBwhcMtbFileSenderTest {
|
|||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun mtbFileRequestWithResponseSource(): Set<RequestWithResponse> {
|
fun mtbFileRequestWithResponseSource(): Set<RequestWithResponse> {
|
||||||
return setOf(
|
return setOf(
|
||||||
RequestWithResponse(HttpStatus.OK, "{}", MtbFileSender.Response(RequestStatus.SUCCESS, "{}")),
|
RequestWithResponse(
|
||||||
|
HttpStatus.OK,
|
||||||
|
responseBodyWithMaxSeverity(ReportService.Severity.INFO),
|
||||||
|
MtbFileSender.Response(
|
||||||
|
RequestStatus.SUCCESS,
|
||||||
|
responseBodyWithMaxSeverity(ReportService.Severity.INFO)
|
||||||
|
)
|
||||||
|
),
|
||||||
RequestWithResponse(
|
RequestWithResponse(
|
||||||
HttpStatus.CREATED,
|
HttpStatus.CREATED,
|
||||||
warningBody,
|
responseBodyWithMaxSeverity(ReportService.Severity.WARNING),
|
||||||
MtbFileSender.Response(RequestStatus.WARNING, warningBody)
|
MtbFileSender.Response(
|
||||||
|
RequestStatus.WARNING,
|
||||||
|
responseBodyWithMaxSeverity(ReportService.Severity.WARNING)
|
||||||
|
)
|
||||||
),
|
),
|
||||||
RequestWithResponse(
|
RequestWithResponse(
|
||||||
HttpStatus.BAD_REQUEST,
|
HttpStatus.BAD_REQUEST,
|
||||||
ERROR_RESPONSE_BODY,
|
responseBodyWithMaxSeverity(ReportService.Severity.ERROR),
|
||||||
MtbFileSender.Response(RequestStatus.ERROR, ERROR_RESPONSE_BODY)
|
MtbFileSender.Response(RequestStatus.ERROR, responseBodyWithMaxSeverity(ReportService.Severity.ERROR))
|
||||||
),
|
),
|
||||||
RequestWithResponse(
|
RequestWithResponse(
|
||||||
HttpStatus.UNPROCESSABLE_ENTITY,
|
HttpStatus.UNPROCESSABLE_ENTITY,
|
||||||
errorBody,
|
responseBodyWithMaxSeverity(ReportService.Severity.FATAL),
|
||||||
MtbFileSender.Response(RequestStatus.ERROR, errorBody)
|
MtbFileSender.Response(
|
||||||
|
RequestStatus.ERROR,
|
||||||
|
responseBodyWithMaxSeverity(ReportService.Severity.FATAL)
|
||||||
|
)
|
||||||
),
|
),
|
||||||
// Some more errors not mentioned in documentation
|
// Some more errors not mentioned in documentation
|
||||||
RequestWithResponse(
|
RequestWithResponse(
|
||||||
@ -256,6 +260,52 @@ class RestBwhcMtbFileSenderTest {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun responseBodyWithMaxSeverity(severity: ReportService.Severity): String {
|
||||||
|
return when (severity) {
|
||||||
|
ReportService.Severity.INFO -> """
|
||||||
|
{
|
||||||
|
"patient": "PID",
|
||||||
|
"issues": [
|
||||||
|
{ "severity": "info", "message": "Info Message" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
ReportService.Severity.WARNING -> """
|
||||||
|
{
|
||||||
|
"patient": "PID",
|
||||||
|
"issues": [
|
||||||
|
{ "severity": "info", "message": "Info Message" },
|
||||||
|
{ "severity": "warning", "message": "Warning Message" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
ReportService.Severity.ERROR -> """
|
||||||
|
{
|
||||||
|
"patient": "PID",
|
||||||
|
"issues": [
|
||||||
|
{ "severity": "info", "message": "Info Message" },
|
||||||
|
{ "severity": "warning", "message": "Warning Message" },
|
||||||
|
{ "severity": "error", "message": "Error Message" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
ReportService.Severity.FATAL -> """
|
||||||
|
{
|
||||||
|
"patient": "PID",
|
||||||
|
"issues": [
|
||||||
|
{ "severity": "info", "message": "Info Message" },
|
||||||
|
{ "severity": "warning", "message": "Warning Message" },
|
||||||
|
{ "severity": "error", "message": "Error Message" },
|
||||||
|
{ "severity": "fatal", "message": "Fatal Message" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -19,13 +19,17 @@
|
|||||||
|
|
||||||
package dev.dnpm.etl.processor.output
|
package dev.dnpm.etl.processor.output
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import com.fasterxml.jackson.module.kotlin.KotlinModule
|
||||||
import de.ukw.ccc.bwhc.dto.*
|
import de.ukw.ccc.bwhc.dto.*
|
||||||
import dev.dnpm.etl.processor.PatientPseudonym
|
import dev.dnpm.etl.processor.PatientPseudonym
|
||||||
import dev.dnpm.etl.processor.RequestId
|
import dev.dnpm.etl.processor.RequestId
|
||||||
import dev.dnpm.etl.processor.config.AppConfigProperties
|
import dev.dnpm.etl.processor.config.AppConfigProperties
|
||||||
import dev.dnpm.etl.processor.config.AppConfiguration
|
import dev.dnpm.etl.processor.config.AppConfiguration
|
||||||
import dev.dnpm.etl.processor.config.RestTargetProperties
|
import dev.dnpm.etl.processor.config.RestTargetProperties
|
||||||
|
import dev.dnpm.etl.processor.monitoring.ReportService
|
||||||
import dev.dnpm.etl.processor.monitoring.RequestStatus
|
import dev.dnpm.etl.processor.monitoring.RequestStatus
|
||||||
|
import dev.dnpm.etl.processor.output.RestBwhcMtbFileSenderTest.Companion
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.junit.jupiter.api.BeforeEach
|
import org.junit.jupiter.api.BeforeEach
|
||||||
import org.junit.jupiter.params.ParameterizedTest
|
import org.junit.jupiter.params.ParameterizedTest
|
||||||
@ -48,6 +52,8 @@ class RestDipMtbFileSenderTest {
|
|||||||
|
|
||||||
private lateinit var restMtbFileSender: RestMtbFileSender
|
private lateinit var restMtbFileSender: RestMtbFileSender
|
||||||
|
|
||||||
|
private var reportService = ReportService(ObjectMapper().registerModule(KotlinModule.Builder().build()))
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
fun setup() {
|
fun setup() {
|
||||||
val restTemplate = RestTemplate()
|
val restTemplate = RestTemplate()
|
||||||
@ -56,7 +62,7 @@ class RestDipMtbFileSenderTest {
|
|||||||
|
|
||||||
this.mockRestServiceServer = MockRestServiceServer.createServer(restTemplate)
|
this.mockRestServiceServer = MockRestServiceServer.createServer(restTemplate)
|
||||||
|
|
||||||
this.restMtbFileSender = RestDipMtbFileSender(restTemplate, restTargetProperties, retryTemplate)
|
this.restMtbFileSender = RestDipMtbFileSender(restTemplate, restTargetProperties, retryTemplate, reportService)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@ -98,11 +104,14 @@ class RestDipMtbFileSenderTest {
|
|||||||
retryTemplate.setBackOffPolicy(NoBackOffPolicy())
|
retryTemplate.setBackOffPolicy(NoBackOffPolicy())
|
||||||
|
|
||||||
this.mockRestServiceServer = MockRestServiceServer.createServer(restTemplate)
|
this.mockRestServiceServer = MockRestServiceServer.createServer(restTemplate)
|
||||||
this.restMtbFileSender = RestDipMtbFileSender(restTemplate, restTargetProperties, retryTemplate)
|
this.restMtbFileSender =
|
||||||
|
RestDipMtbFileSender(restTemplate, restTargetProperties, retryTemplate, reportService)
|
||||||
|
|
||||||
val expectedCount = when (requestWithResponse.httpStatus) {
|
val expectedCount = when (requestWithResponse.httpStatus) {
|
||||||
// OK - No Retry
|
// OK - No Retry
|
||||||
HttpStatus.OK, HttpStatus.CREATED, HttpStatus.UNPROCESSABLE_ENTITY, HttpStatus.BAD_REQUEST -> ExpectedCount.max(1)
|
HttpStatus.OK, HttpStatus.CREATED, HttpStatus.UNPROCESSABLE_ENTITY, HttpStatus.BAD_REQUEST -> ExpectedCount.max(
|
||||||
|
1
|
||||||
|
)
|
||||||
// Request failed - Retry max 3 times
|
// Request failed - Retry max 3 times
|
||||||
else -> ExpectedCount.max(3)
|
else -> ExpectedCount.max(3)
|
||||||
}
|
}
|
||||||
@ -128,11 +137,14 @@ class RestDipMtbFileSenderTest {
|
|||||||
retryTemplate.setBackOffPolicy(NoBackOffPolicy())
|
retryTemplate.setBackOffPolicy(NoBackOffPolicy())
|
||||||
|
|
||||||
this.mockRestServiceServer = MockRestServiceServer.createServer(restTemplate)
|
this.mockRestServiceServer = MockRestServiceServer.createServer(restTemplate)
|
||||||
this.restMtbFileSender = RestDipMtbFileSender(restTemplate, restTargetProperties, retryTemplate)
|
this.restMtbFileSender =
|
||||||
|
RestDipMtbFileSender(restTemplate, restTargetProperties, retryTemplate, reportService)
|
||||||
|
|
||||||
val expectedCount = when (requestWithResponse.httpStatus) {
|
val expectedCount = when (requestWithResponse.httpStatus) {
|
||||||
// OK - No Retry
|
// OK - No Retry
|
||||||
HttpStatus.OK, HttpStatus.CREATED, HttpStatus.UNPROCESSABLE_ENTITY, HttpStatus.BAD_REQUEST -> ExpectedCount.max(1)
|
HttpStatus.OK, HttpStatus.CREATED, HttpStatus.UNPROCESSABLE_ENTITY, HttpStatus.BAD_REQUEST -> ExpectedCount.max(
|
||||||
|
1
|
||||||
|
)
|
||||||
// Request failed - Retry max 3 times
|
// Request failed - Retry max 3 times
|
||||||
else -> ExpectedCount.max(3)
|
else -> ExpectedCount.max(3)
|
||||||
}
|
}
|
||||||
@ -159,24 +171,6 @@ class RestDipMtbFileSenderTest {
|
|||||||
val TEST_REQUEST_ID = RequestId("TestId")
|
val TEST_REQUEST_ID = RequestId("TestId")
|
||||||
val TEST_PATIENT_PSEUDONYM = PatientPseudonym("PID")
|
val TEST_PATIENT_PSEUDONYM = PatientPseudonym("PID")
|
||||||
|
|
||||||
private val warningBody = """
|
|
||||||
{
|
|
||||||
"patient_id": "PID",
|
|
||||||
"issues": [
|
|
||||||
{ "severity": "warning", "message": "Something is not right" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
""".trimIndent()
|
|
||||||
|
|
||||||
private val errorBody = """
|
|
||||||
{
|
|
||||||
"patient_id": "PID",
|
|
||||||
"issues": [
|
|
||||||
{ "severity": "error", "message": "Something is very bad" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
""".trimIndent()
|
|
||||||
|
|
||||||
val mtbFile: MtbFile = MtbFile.builder()
|
val mtbFile: MtbFile = MtbFile.builder()
|
||||||
.withPatient(
|
.withPatient(
|
||||||
Patient.builder()
|
Patient.builder()
|
||||||
@ -210,21 +204,28 @@ class RestDipMtbFileSenderTest {
|
|||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun mtbFileRequestWithResponseSource(): Set<RequestWithResponse> {
|
fun mtbFileRequestWithResponseSource(): Set<RequestWithResponse> {
|
||||||
return setOf(
|
return setOf(
|
||||||
RequestWithResponse(HttpStatus.OK, "{}", MtbFileSender.Response(RequestStatus.SUCCESS, "{}")),
|
RequestWithResponse(
|
||||||
|
HttpStatus.OK,
|
||||||
|
responseBodyWithMaxSeverity(ReportService.Severity.INFO),
|
||||||
|
MtbFileSender.Response(
|
||||||
|
RequestStatus.SUCCESS,
|
||||||
|
responseBodyWithMaxSeverity(ReportService.Severity.INFO)
|
||||||
|
)
|
||||||
|
),
|
||||||
RequestWithResponse(
|
RequestWithResponse(
|
||||||
HttpStatus.CREATED,
|
HttpStatus.CREATED,
|
||||||
warningBody,
|
responseBodyWithMaxSeverity(ReportService.Severity.WARNING),
|
||||||
MtbFileSender.Response(RequestStatus.WARNING, warningBody)
|
MtbFileSender.Response(RequestStatus.WARNING, responseBodyWithMaxSeverity(ReportService.Severity.WARNING))
|
||||||
),
|
),
|
||||||
RequestWithResponse(
|
RequestWithResponse(
|
||||||
HttpStatus.BAD_REQUEST,
|
HttpStatus.BAD_REQUEST,
|
||||||
ERROR_RESPONSE_BODY,
|
responseBodyWithMaxSeverity(ReportService.Severity.ERROR),
|
||||||
MtbFileSender.Response(RequestStatus.ERROR, ERROR_RESPONSE_BODY)
|
MtbFileSender.Response(RequestStatus.ERROR, responseBodyWithMaxSeverity(ReportService.Severity.ERROR))
|
||||||
),
|
),
|
||||||
RequestWithResponse(
|
RequestWithResponse(
|
||||||
HttpStatus.UNPROCESSABLE_ENTITY,
|
HttpStatus.UNPROCESSABLE_ENTITY,
|
||||||
errorBody,
|
responseBodyWithMaxSeverity(ReportService.Severity.ERROR),
|
||||||
MtbFileSender.Response(RequestStatus.ERROR, errorBody)
|
MtbFileSender.Response(RequestStatus.ERROR, responseBodyWithMaxSeverity(ReportService.Severity.ERROR))
|
||||||
),
|
),
|
||||||
// Some more errors not mentioned in documentation
|
// Some more errors not mentioned in documentation
|
||||||
RequestWithResponse(
|
RequestWithResponse(
|
||||||
@ -261,6 +262,52 @@ class RestDipMtbFileSenderTest {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun responseBodyWithMaxSeverity(severity: ReportService.Severity): String {
|
||||||
|
return when (severity) {
|
||||||
|
ReportService.Severity.INFO -> """
|
||||||
|
{
|
||||||
|
"patient": "PID",
|
||||||
|
"issues": [
|
||||||
|
{ "severity": "info", "message": "Info Message" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
ReportService.Severity.WARNING -> """
|
||||||
|
{
|
||||||
|
"patient": "PID",
|
||||||
|
"issues": [
|
||||||
|
{ "severity": "info", "message": "Info Message" },
|
||||||
|
{ "severity": "warning", "message": "Warning Message" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
ReportService.Severity.ERROR -> """
|
||||||
|
{
|
||||||
|
"patient": "PID",
|
||||||
|
"issues": [
|
||||||
|
{ "severity": "info", "message": "Info Message" },
|
||||||
|
{ "severity": "warning", "message": "Warning Message" },
|
||||||
|
{ "severity": "error", "message": "Error Message" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
ReportService.Severity.FATAL -> """
|
||||||
|
{
|
||||||
|
"patient": "PID",
|
||||||
|
"issues": [
|
||||||
|
{ "severity": "info", "message": "Info Message" },
|
||||||
|
{ "severity": "warning", "message": "Warning Message" },
|
||||||
|
{ "severity": "error", "message": "Error Message" },
|
||||||
|
{ "severity": "fatal", "message": "Fatal Message" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* This file is part of ETL-Processor
|
* This file is part of ETL-Processor
|
||||||
*
|
*
|
||||||
* Copyright (c) 2023 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors
|
* Copyright (c) 2025 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU Affero General Public License as published
|
* it under the terms of the GNU Affero General Public License as published
|
||||||
@ -22,9 +22,14 @@ package dev.dnpm.etl.processor.services
|
|||||||
import com.fasterxml.jackson.databind.ObjectMapper
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
import com.fasterxml.jackson.module.kotlin.KotlinModule
|
import com.fasterxml.jackson.module.kotlin.KotlinModule
|
||||||
import dev.dnpm.etl.processor.monitoring.ReportService
|
import dev.dnpm.etl.processor.monitoring.ReportService
|
||||||
|
import dev.dnpm.etl.processor.monitoring.RequestStatus
|
||||||
|
import dev.dnpm.etl.processor.monitoring.asRequestStatus
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.junit.jupiter.api.BeforeEach
|
import org.junit.jupiter.api.BeforeEach
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest
|
||||||
|
import org.junit.jupiter.params.provider.Arguments
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource
|
||||||
|
|
||||||
class ReportServiceTest {
|
class ReportServiceTest {
|
||||||
|
|
||||||
@ -60,6 +65,15 @@ class ReportServiceTest {
|
|||||||
assertThat(actual[2].message).isEqualTo("Warning Message")
|
assertThat(actual[2].message).isEqualTo("Warning Message")
|
||||||
assertThat(actual[3].severity).isEqualTo(ReportService.Severity.INFO)
|
assertThat(actual[3].severity).isEqualTo(ReportService.Severity.INFO)
|
||||||
assertThat(actual[3].message).isEqualTo("Info Message")
|
assertThat(actual[3].message).isEqualTo("Info Message")
|
||||||
|
|
||||||
|
assertThat(actual.asRequestStatus()).isEqualTo(RequestStatus.ERROR)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("testData")
|
||||||
|
fun shouldParseDataQualityReport(json: String, requestStatus: RequestStatus) {
|
||||||
|
val actual = this.reportService.deserialize(json)
|
||||||
|
assertThat(actual.asRequestStatus()).isEqualTo(requestStatus)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -73,4 +87,75 @@ class ReportServiceTest {
|
|||||||
assertThat(actual[0].message).isEqualTo("Not parsable data quality report '$invalidResponse'")
|
assertThat(actual[0].message).isEqualTo("Not parsable data quality report '$invalidResponse'")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun testData(): Set<Arguments> {
|
||||||
|
return 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" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
""".trimIndent(),
|
||||||
|
RequestStatus.ERROR
|
||||||
|
),
|
||||||
|
Arguments.of(
|
||||||
|
"""
|
||||||
|
{
|
||||||
|
"patient": "4711",
|
||||||
|
"issues": [
|
||||||
|
{ "severity": "info", "message": "Info Message" },
|
||||||
|
{ "severity": "warning", "message": "Warning Message" },
|
||||||
|
{ "severity": "error", "message": "Error Message" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
""".trimIndent(),
|
||||||
|
RequestStatus.ERROR
|
||||||
|
),
|
||||||
|
Arguments.of(
|
||||||
|
"""
|
||||||
|
{
|
||||||
|
"patient": "4711",
|
||||||
|
"issues": [
|
||||||
|
{ "severity": "error", "message": "Error Message" }
|
||||||
|
{ "severity": "info", "message": "Info Message" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
""".trimIndent(),
|
||||||
|
RequestStatus.ERROR
|
||||||
|
),
|
||||||
|
Arguments.of(
|
||||||
|
"""
|
||||||
|
{
|
||||||
|
"patient": "4711",
|
||||||
|
"issues": [
|
||||||
|
{ "severity": "info", "message": "Info Message" },
|
||||||
|
{ "severity": "warning", "message": "Warning Message" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
""".trimIndent(),
|
||||||
|
RequestStatus.WARNING
|
||||||
|
),
|
||||||
|
Arguments.of(
|
||||||
|
"""
|
||||||
|
{
|
||||||
|
"patient": "4711",
|
||||||
|
"issues": [
|
||||||
|
{ "severity": "info", "message": "Info Message" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
""".trimIndent(),
|
||||||
|
RequestStatus.SUCCESS
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user