summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul-Christian Volkmer2026-04-22 15:50:45 +0200
committerGitHub2026-04-22 13:50:45 +0000
commita7d57581591c23fd1fd0ad0f4e8f9c6ce2745750 (patch)
tree28c70a85e124975c513b8a5f411c7d3c6873c99a
parent5d25fc13419493176ff19d115f5015322271a835 (diff)
fix: do not block earlier initial requests with error response (#281)
Requests with errors are blocked within DNPM:DIP and not used for MVH. Any later accepted successful request with SUCCESS or WARNING should be used as ADDITION.
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/services/RequestProcessor.kt13
-rw-r--r--src/test/kotlin/dev/dnpm/etl/processor/services/RequestProcessorTest.kt132
2 files changed, 135 insertions, 10 deletions
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 7325265..f0eed7f 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/services/RequestProcessor.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/services/RequestProcessor.kt
@@ -115,8 +115,8 @@ class RequestProcessor(
if (
appConfigProperties.postInitialSubmissionBlock &&
- hasSuccessfullInitialSubmission(request.patientPseudonym()) &&
- hasUnacceptedInitialSubmission(request.patientPseudonym())
+ hasSuccessfulInitialSubmission(request.patientPseudonym()) &&
+ hasUnacceptedSuccessfulInitialSubmission(request.patientPseudonym())
) {
requestService.save(
Request(
@@ -136,8 +136,8 @@ class RequestProcessor(
if (
appConfigProperties.postInitialSubmissionBlock &&
- hasSuccessfullInitialSubmission(request.patientPseudonym()) &&
- !hasUnacceptedInitialSubmission(request.patientPseudonym())
+ hasSuccessfulInitialSubmission(request.patientPseudonym()) &&
+ !hasUnacceptedSuccessfulInitialSubmission(request.patientPseudonym())
) {
// Use "addition" after "intial" with "Meldebestaetigung"
request.content.metadata?.let {
@@ -184,16 +184,17 @@ class RequestProcessor(
)
}
- private fun hasSuccessfullInitialSubmission(patientPseudonym: PatientPseudonym): Boolean {
+ private fun hasSuccessfulInitialSubmission(patientPseudonym: PatientPseudonym): Boolean {
return this.requestService.allRequestsByPatientPseudonym(patientPseudonym).any {
it.submissionType == SubmissionType.INITIAL &&
(it.status == RequestStatus.SUCCESS || it.status == RequestStatus.WARNING)
}
}
- private fun hasUnacceptedInitialSubmission(patientPseudonym: PatientPseudonym): Boolean {
+ private fun hasUnacceptedSuccessfulInitialSubmission(patientPseudonym: PatientPseudonym): Boolean {
return this.requestService.allRequestsByPatientPseudonym(patientPseudonym).any {
it.submissionType == SubmissionType.INITIAL &&
+ (it.status == RequestStatus.SUCCESS || it.status == RequestStatus.WARNING) &&
!(it.submissionAccepted || it.status == RequestStatus.BLOCKED_INITIAL)
}
}
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 70df248..a291ff7 100644
--- a/src/test/kotlin/dev/dnpm/etl/processor/services/RequestProcessorTest.kt
+++ b/src/test/kotlin/dev/dnpm/etl/processor/services/RequestProcessorTest.kt
@@ -717,7 +717,7 @@ class RequestProcessorTest {
@Test
fun testShouldNotSendMtbFileIfInitialFileWasSent() {
- // One failed attempt and one successful but not accepted
+ // Two failed unaccepted attempts, then and one successful and accepted
val lastRequests =
listOf(
Request(
@@ -730,7 +730,7 @@ class RequestProcessorTest {
SubmissionType.INITIAL,
RequestStatus.ERROR,
Tan.empty(),
- Instant.parse("2026-01-05T09:00:00Z"),
+ Instant.parse("2026-04-22T09:00:00Z"),
submissionAccepted = false,
),
Request(
@@ -738,12 +738,25 @@ class RequestProcessorTest {
randomRequestId(),
PatientPseudonym("TEST_12345678901"),
PatientId("P1"),
+ Fingerprint("initial"),
+ RequestType.MTB_FILE,
+ SubmissionType.INITIAL,
+ RequestStatus.ERROR,
+ Tan.empty(),
+ Instant.parse("2026-04-22T10:00:00Z"),
+ submissionAccepted = false,
+ ),
+ Request(
+ 3L,
+ randomRequestId(),
+ PatientPseudonym("TEST_12345678901"),
+ PatientId("P1"),
Fingerprint("blocked_initial"),
RequestType.MTB_FILE,
SubmissionType.INITIAL,
- RequestStatus.SUCCESS,
+ RequestStatus.WARNING,
Tan.empty(),
- Instant.parse("2026-01-05T10:00:00Z"),
+ Instant.parse("2026-04-22T11:00:00Z"),
submissionAccepted = false,
),
)
@@ -800,6 +813,117 @@ class RequestProcessorTest {
verify(applicationEventPublisher, times(0)).publishEvent(any())
verify(sender, times(0)).send(any<DnpmV2MtbFileRequest>())
}
+
+ @Test
+ fun testShouldSendAdditionMtbFileIfUnacceptedErrorsAndInitialFileWasSent() {
+
+ // One failed attempt and one successful but not accepted
+ val lastRequests =
+ listOf(
+ Request(
+ 1L,
+ randomRequestId(),
+ PatientPseudonym("TEST_12345678901"),
+ PatientId("P1"),
+ Fingerprint("initial"),
+ RequestType.MTB_FILE,
+ SubmissionType.INITIAL,
+ RequestStatus.ERROR,
+ Tan.empty(),
+ Instant.parse("2026-01-05T09:00:00Z"),
+ submissionAccepted = false,
+ ),
+ Request(
+ 2L,
+ randomRequestId(),
+ PatientPseudonym("TEST_12345678901"),
+ PatientId("P1"),
+ Fingerprint("initial"),
+ RequestType.MTB_FILE,
+ SubmissionType.INITIAL,
+ RequestStatus.ERROR,
+ Tan.empty(),
+ Instant.parse("2026-01-05T09:00:00Z"),
+ submissionAccepted = false,
+ ),
+ Request(
+ 3L,
+ randomRequestId(),
+ PatientPseudonym("TEST_12345678901"),
+ PatientId("P1"),
+ Fingerprint("initial"),
+ RequestType.MTB_FILE,
+ SubmissionType.INITIAL,
+ RequestStatus.WARNING,
+ Tan.empty(),
+ Instant.parse("2026-01-05T09:00:00Z"),
+ submissionAccepted = true,
+ )
+ )
+
+ doAnswer { lastRequests }
+ .whenever(requestService)
+ .allRequestsByPatientPseudonym(anyValueClass())
+
+ 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)
+
+ requestProcessor =
+ RequestProcessor(
+ pseudonymizeService,
+ transformationService,
+ sender,
+ requestService,
+ ObjectMapper(),
+ applicationEventPublisher,
+ AppConfigProperties(postInitialSubmissionBlock = true),
+ consentProcessor,
+ )
+
+ val mtbFile =
+ Mtb.builder()
+ .patient(Patient.builder().id("123").build())
+ .metadata(MvhMetadata.builder().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()
+
+ 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)
+ assertThat(requestCaptor.firstValue.submissionType).isEqualTo(SubmissionType.ADDITION)
+
+ val eventCaptor = argumentCaptor<ResponseEvent>()
+ verify(applicationEventPublisher, times(1)).publishEvent(eventCaptor.capture())
+ assertThat(eventCaptor.firstValue.status).isEqualTo(RequestStatus.SUCCESS)
+
+ val sendRequestCaptor = argumentCaptor<DnpmV2MtbFileRequest>()
+ verify(sender, times(1)).send(sendRequestCaptor.capture())
+ assertThat(sendRequestCaptor.firstValue.content.metadata.type).isEqualTo(MvhSubmissionType.ADDITION)
+ }
}
@Test