1
0
mirror of https://github.com/pcvolkmer/etl-processor.git synced 2025-07-01 14:12:55 +00:00

refactor: renamed misleading enum value TtpConsentStatus.IGNORED to TtpConsentStatus.UNKNOWN_CHECK_FILE also impl. ConsentCheckedIgnored has been renamed to ConsentCheckFileBased - since consent status should never be ignored.

Also renamed ICheckConsent.isConsented ICheckConsent.getTtpConsentStatus since new method name is more descriptive.
This commit is contained in:
Jakub Lidke
2025-05-12 11:25:45 +02:00
parent 542dc61811
commit 77df6f38ec
15 changed files with 59 additions and 53 deletions

View File

@ -91,7 +91,7 @@ Wurde die Verwendung von gPAS konfiguriert, so sind weitere Angaben zu konfiguri
Ab gIcs Version 2.13.0 kann per [REST-Schnittstelle](https://simplifier.net/guide/ttp-fhir-gateway-ig/ImplementationGuide-markdown-Einwilligungsmanagement-Operations-isConsented?version=current) der Einwilligungsstatus abgefragt werden.
Vor der MTB-Übertragung kann der zum Sendezeitpunkt verfügbarer Einwilligungsstatus über Endpunkt *isConsented* abgefragt werden.
Falls diese Prüfung aktiviert wurde, wird der Einwilligungsstatus dem in der MTB Datei vorgezogen und überschreibt diesen.
Falls diese Prüfung aktiviert wurde, wird der Einwilligungsstatus dem in der MTB Datei vorgezogen und der Wert in der MTB-Datei wird dabei ignoriert.
Falls in diesem Fall die Statusprüfung fehlschlägt, wird Status **abgelehnt** angenommen.
Ist die Prüfung über gIcs deaktiviert, wird der eingetragene Einwilligungsstatus der übermittelten MTB Datei geprüft.

View File

@ -23,7 +23,7 @@ import com.fasterxml.jackson.databind.ObjectMapper
import de.ukw.ccc.bwhc.dto.*
import dev.dnpm.etl.processor.anyValueClass
import dev.dnpm.etl.processor.config.AppSecurityConfiguration
import dev.dnpm.etl.processor.consent.ConsentCheckedIgnored
import dev.dnpm.etl.processor.consent.ConsentCheckFileBased
import dev.dnpm.etl.processor.consent.TtpConsentStatus
import dev.dnpm.etl.processor.consent.ICheckConsent
import dev.dnpm.etl.processor.security.TokenRepository
@ -55,7 +55,7 @@ import org.springframework.test.web.servlet.post
classes = [
MtbFileRestController::class,
AppSecurityConfiguration::class,
ConsentCheckedIgnored::class, ICheckConsent::class
ConsentCheckFileBased::class, ICheckConsent::class
]
)
@MockitoBean(types = [TokenRepository::class, RequestProcessor::class])
@ -143,7 +143,7 @@ class MtbFileRestControllerTest {
status { isAccepted() }
}
verify(requestProcessor, times(1)).processDeletion(anyValueClass(), eq(TtpConsentStatus.IGNORED))
verify(requestProcessor, times(1)).processDeletion(anyValueClass(), eq(TtpConsentStatus.UNKNOWN_CHECK_FILE))
}
@Test

View File

@ -0,0 +1,9 @@
package dev.dnpm.etl.processor.consent;
public class ConsentCheckFileBased implements ICheckConsent{
@Override
public TtpConsentStatus getTtpConsentStatus(String personIdentifierValue) {
return TtpConsentStatus.UNKNOWN_CHECK_FILE;
}
}

View File

@ -1,9 +0,0 @@
package dev.dnpm.etl.processor.consent;
public class ConsentCheckedIgnored implements ICheckConsent{
@Override
public TtpConsentStatus isConsented(String personIdentifierValue) {
return TtpConsentStatus.IGNORED;
}
}

View File

@ -58,7 +58,7 @@ public class GicsConsentService implements ICheckConsent {
throw new IllegalArgumentException(
"gICS base URL is empty - should call gICS with false configuration.");
}
url = UriComponentsBuilder.fromHttpUrl(gIcsBaseUri).path(IS_CONSENTED_ENDPOINT)
url = UriComponentsBuilder.fromUriString(gIcsBaseUri).path(IS_CONSENTED_ENDPOINT)
.toUriString();
}
return url;
@ -147,7 +147,7 @@ public class GicsConsentService implements ICheckConsent {
}
@Override
public TtpConsentStatus isConsented(String personIdentifierValue) {
public TtpConsentStatus getTtpConsentStatus(String personIdentifierValue) {
var parameter = GicsConsentService.getIsConsentedParam(gIcsConfigProperties,
personIdentifierValue);
@ -176,10 +176,8 @@ public class GicsConsentService implements ICheckConsent {
return TtpConsentStatus.CONSENT_MISSING_OR_REJECTED;
}
} else if (response instanceof OperationOutcome outcome) {
log.error(
"failed to get consent status from ttp. probably configuration error. outcome: ",
fhirContext.newJsonParser().encodeToString(outcome));
log.error("failed to get consent status from ttp. probably configuration error. "
+ "outcome: '{}'", fhirContext.newJsonParser().encodeToString(outcome));
}
} catch (DataFormatException dfe) {

View File

@ -3,6 +3,6 @@ package dev.dnpm.etl.processor.consent;
public interface ICheckConsent {
TtpConsentStatus isConsented(String personIdentifierValue);
TtpConsentStatus getTtpConsentStatus(String personIdentifierValue);
}

View File

@ -7,12 +7,14 @@ public enum TtpConsentStatus {
CONSENTED,
CONSENT_MISSING_OR_REJECTED,
/**
* Due technical problems consent status is unknown
*/
FAILED_TO_ASK,
/**
* We assume received files are consented
* Consent status is validate via file property 'consent.status'
*/
IGNORED
UNKNOWN_CHECK_FILE
}

View File

@ -20,7 +20,7 @@
package dev.dnpm.etl.processor.config
import com.fasterxml.jackson.databind.ObjectMapper
import dev.dnpm.etl.processor.consent.ConsentCheckedIgnored
import dev.dnpm.etl.processor.consent.ConsentCheckFileBased
import dev.dnpm.etl.processor.consent.ICheckConsent
import dev.dnpm.etl.processor.consent.GicsConsentService
import dev.dnpm.etl.processor.monitoring.ConnectionCheckResult
@ -183,7 +183,7 @@ class AppConfiguration {
@Bean
@ConditionalOnMissingBean
fun constService(): ICheckConsent {
return ConsentCheckedIgnored()
return ConsentCheckFileBased()
}
@Bean

View File

@ -77,12 +77,12 @@ class KafkaInputListener(
} else {
logger.debug("Accepted MTB File and process deletion")
if (requestId.isBlank()) {
requestProcessor.processDeletion(patientId, TtpConsentStatus.IGNORED)
requestProcessor.processDeletion(patientId, TtpConsentStatus.UNKNOWN_CHECK_FILE)
} else {
requestProcessor.processDeletion(
patientId,
requestId,
TtpConsentStatus.IGNORED
TtpConsentStatus.UNKNOWN_CHECK_FILE
)
}
}

View File

@ -48,7 +48,7 @@ class MtbFileRestController(
@PostMapping(consumes = [MediaType.APPLICATION_JSON_VALUE])
fun mtbFile(@RequestBody mtbFile: MtbFile): ResponseEntity<Unit> {
val consentStatusBooleanPair = checkConsentStatus(mtbFile)
var ttpConsentStatus = consentStatusBooleanPair.first
val ttpConsentStatus = consentStatusBooleanPair.first
val isConsentOK = consentStatusBooleanPair.second
if (isConsentOK) {
logger.debug("Accepted MTB File (bwHC V1) for processing")
@ -63,14 +63,14 @@ class MtbFileRestController(
}
private fun checkConsentStatus(mtbFile: MtbFile): Pair<TtpConsentStatus, Boolean> {
var ttpConsentStatus = constService.isConsented(mtbFile.patient.id)
var ttpConsentStatus = constService.getTtpConsentStatus(mtbFile.patient.id)
val isConsentOK =
(ttpConsentStatus.equals(TtpConsentStatus.IGNORED) && mtbFile.consent.status == Consent.Status.ACTIVE) ||
(ttpConsentStatus.equals(TtpConsentStatus.UNKNOWN_CHECK_FILE) && mtbFile.consent.status == Consent.Status.ACTIVE) ||
ttpConsentStatus.equals(
TtpConsentStatus.CONSENTED
)
if (ttpConsentStatus.equals(TtpConsentStatus.IGNORED) && mtbFile.consent.status == Consent.Status.REJECTED) {
if (ttpConsentStatus.equals(TtpConsentStatus.UNKNOWN_CHECK_FILE) && mtbFile.consent.status == Consent.Status.REJECTED) {
// in case ttp check is disabled - we propagate rejected status anyway
ttpConsentStatus = TtpConsentStatus.CONSENT_MISSING_OR_REJECTED
}
@ -87,7 +87,7 @@ class MtbFileRestController(
@DeleteMapping(path = ["{patientId}"])
fun deleteData(@PathVariable patientId: String): ResponseEntity<Unit> {
logger.debug("Accepted patient ID to process deletion")
requestProcessor.processDeletion(PatientId(patientId), TtpConsentStatus.IGNORED)
requestProcessor.processDeletion(PatientId(patientId), TtpConsentStatus.UNKNOWN_CHECK_FILE)
return ResponseEntity.accepted().build()
}

View File

@ -140,7 +140,7 @@ class RequestProcessor(
val requestStatus: RequestStatus = when (isConsented) {
TtpConsentStatus.CONSENT_MISSING_OR_REJECTED -> RequestStatus.NO_CONSENT
TtpConsentStatus.FAILED_TO_ASK -> RequestStatus.ERROR
TtpConsentStatus.CONSENTED, TtpConsentStatus.IGNORED -> RequestStatus.UNKNOWN
TtpConsentStatus.CONSENTED, TtpConsentStatus.UNKNOWN_CHECK_FILE -> RequestStatus.UNKNOWN
}
requestService.save(

View File

@ -48,7 +48,7 @@ public class GicsConsentServiceTest {
}
@Test
void isConsented() {
void getTtpConsentStatus() {
final Parameters responseConsented = new Parameters().addParameter(
new ParametersParameterComponent().setName("consented")
.setValue(new BooleanType().setValue(true)));
@ -59,7 +59,7 @@ public class GicsConsentServiceTest {
.encodeResourceToString(responseConsented),
MediaType.APPLICATION_JSON));
var consentStatus = gicsConsentService.isConsented("123456");
var consentStatus = gicsConsentService.getTtpConsentStatus("123456");
assertThat(consentStatus).isEqualTo(TtpConsentStatus.CONSENTED);
}
@ -76,7 +76,7 @@ public class GicsConsentServiceTest {
.encodeResourceToString(responseRevoced),
MediaType.APPLICATION_JSON));
var consentStatus = gicsConsentService.isConsented("123456");
var consentStatus = gicsConsentService.getTtpConsentStatus("123456");
assertThat(consentStatus).isEqualTo(TtpConsentStatus.CONSENT_MISSING_OR_REJECTED);
}
@ -94,7 +94,7 @@ public class GicsConsentServiceTest {
.encodeResourceToString(responseErrorOutcome),
MediaType.APPLICATION_JSON));
var consentStatus = gicsConsentService.isConsented("123456");
var consentStatus = gicsConsentService.getTtpConsentStatus("123456");
assertThat(consentStatus).isEqualTo(TtpConsentStatus.FAILED_TO_ASK);
}

View File

@ -94,7 +94,7 @@ class KafkaInputListenerTest {
verify(requestProcessor, times(1)).processDeletion(
anyValueClass(),
eq(TtpConsentStatus.IGNORED)
eq(TtpConsentStatus.UNKNOWN_CHECK_FILE)
)
}
@ -149,7 +149,7 @@ class KafkaInputListenerTest {
)
)
verify(requestProcessor, times(1)).processDeletion(anyValueClass(), anyValueClass(), eq(
TtpConsentStatus.IGNORED))
TtpConsentStatus.UNKNOWN_CHECK_FILE))
}
@Test
@ -181,7 +181,7 @@ class KafkaInputListenerTest {
)
)
verify(requestProcessor, times(0)).processDeletion(anyValueClass(), anyValueClass(), eq(
TtpConsentStatus.IGNORED))
TtpConsentStatus.UNKNOWN_CHECK_FILE))
}
}

View File

@ -23,7 +23,7 @@ import com.fasterxml.jackson.databind.ObjectMapper
import de.ukw.ccc.bwhc.dto.*
import de.ukw.ccc.bwhc.dto.Consent.Status
import dev.dnpm.etl.processor.CustomMediaType
import dev.dnpm.etl.processor.consent.ConsentCheckedIgnored
import dev.dnpm.etl.processor.consent.ConsentCheckFileBased
import dev.dnpm.etl.processor.consent.GicsConsentService
import dev.dnpm.etl.processor.consent.TtpConsentStatus
import dev.dnpm.etl.processor.services.RequestProcessor
@ -67,7 +67,9 @@ class MtbFileRestControllerTest {
@Mock requestProcessor: RequestProcessor
) {
this.requestProcessor = requestProcessor
val controller = MtbFileRestController(requestProcessor, ConsentCheckedIgnored())
val controller = MtbFileRestController(requestProcessor,
ConsentCheckFileBased()
)
this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build()
}
@ -113,7 +115,7 @@ class MtbFileRestControllerTest {
verify(requestProcessor, times(1)).processDeletion(
anyValueClass(),
org.mockito.kotlin.eq(TtpConsentStatus.IGNORED)
org.mockito.kotlin.eq(TtpConsentStatus.UNKNOWN_CHECK_FILE)
)
}
}
@ -147,7 +149,7 @@ class MtbFileRestControllerTest {
@ValueSource(strings = ["ACTIVE", "REJECTED"])
fun shouldProcessPostRequest(status: String) {
whenever(gicsConsentService.isConsented(any())).thenReturn(TtpConsentStatus.CONSENTED)
whenever(gicsConsentService.getTtpConsentStatus(any())).thenReturn(TtpConsentStatus.CONSENTED)
mockMvc.post("/mtbfile") {
content =
@ -167,7 +169,7 @@ class MtbFileRestControllerTest {
@ValueSource(strings = ["ACTIVE", "REJECTED"])
fun shouldProcessPostRequestWithRejectedConsent(status: String) {
whenever(gicsConsentService.isConsented(any())).thenReturn(TtpConsentStatus.CONSENT_MISSING_OR_REJECTED)
whenever(gicsConsentService.getTtpConsentStatus(any())).thenReturn(TtpConsentStatus.CONSENT_MISSING_OR_REJECTED)
mockMvc.post("/mtbfile") {
content =
@ -197,9 +199,9 @@ class MtbFileRestControllerTest {
verify(requestProcessor, times(1)).processDeletion(
anyValueClass(),
org.mockito.kotlin.eq(TtpConsentStatus.IGNORED)
org.mockito.kotlin.eq(TtpConsentStatus.UNKNOWN_CHECK_FILE)
)
verify(gicsConsentService, times(0)).isConsented(any())
verify(gicsConsentService, times(0)).getTtpConsentStatus(any())
}
}
@ -217,7 +219,9 @@ class MtbFileRestControllerTest {
@Mock requestProcessor: RequestProcessor
) {
this.requestProcessor = requestProcessor
val controller = MtbFileRestController(requestProcessor, ConsentCheckedIgnored())
val controller = MtbFileRestController(requestProcessor,
ConsentCheckFileBased()
)
this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build()
}
@ -264,7 +268,7 @@ class MtbFileRestControllerTest {
verify(requestProcessor, times(1)).processDeletion(
anyValueClass(), org.mockito.kotlin.eq(
TtpConsentStatus.IGNORED
TtpConsentStatus.UNKNOWN_CHECK_FILE
)
)
}
@ -282,7 +286,9 @@ class MtbFileRestControllerTest {
@Mock requestProcessor: RequestProcessor
) {
this.requestProcessor = requestProcessor
val controller = MtbFileRestController(requestProcessor, ConsentCheckedIgnored())
val controller = MtbFileRestController(requestProcessor,
ConsentCheckFileBased()
)
this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build()
}

View File

@ -344,7 +344,7 @@ class RequestProcessorTest {
MtbFileSender.Response(status = RequestStatus.UNKNOWN)
}.whenever(sender).send(any<DeleteRequest>())
this.requestProcessor.processDeletion(TEST_PATIENT_ID, isConsented = TtpConsentStatus.IGNORED)
this.requestProcessor.processDeletion(TEST_PATIENT_ID, isConsented = TtpConsentStatus.UNKNOWN_CHECK_FILE)
val requestCaptor = argumentCaptor<Request>()
verify(requestService, times(1)).save(requestCaptor.capture())
@ -362,7 +362,7 @@ class RequestProcessorTest {
MtbFileSender.Response(status = RequestStatus.SUCCESS)
}.whenever(sender).send(any<DeleteRequest>())
this.requestProcessor.processDeletion(TEST_PATIENT_ID, isConsented = TtpConsentStatus.IGNORED)
this.requestProcessor.processDeletion(TEST_PATIENT_ID, isConsented = TtpConsentStatus.UNKNOWN_CHECK_FILE)
val eventCaptor = argumentCaptor<ResponseEvent>()
verify(applicationEventPublisher, times(1)).publishEvent(eventCaptor.capture())
@ -380,7 +380,7 @@ class RequestProcessorTest {
MtbFileSender.Response(status = RequestStatus.ERROR)
}.whenever(sender).send(any<DeleteRequest>())
this.requestProcessor.processDeletion(TEST_PATIENT_ID, isConsented = TtpConsentStatus.IGNORED)
this.requestProcessor.processDeletion(TEST_PATIENT_ID, isConsented = TtpConsentStatus.UNKNOWN_CHECK_FILE)
val eventCaptor = argumentCaptor<ResponseEvent>()
verify(applicationEventPublisher, times(1)).publishEvent(eventCaptor.capture())
@ -392,7 +392,7 @@ class RequestProcessorTest {
fun testShouldSendDeleteRequestWithPseudonymErrorAndSaveErrorRequestStatus() {
doThrow(RuntimeException()).whenever(pseudonymizeService).patientPseudonym(anyValueClass())
this.requestProcessor.processDeletion(TEST_PATIENT_ID, isConsented = TtpConsentStatus.IGNORED)
this.requestProcessor.processDeletion(TEST_PATIENT_ID, isConsented = TtpConsentStatus.UNKNOWN_CHECK_FILE)
val requestCaptor = argumentCaptor<Request>()
verify(requestService, times(1)).save(requestCaptor.capture())