summaryrefslogtreecommitdiff
path: root/src/main/java/dev/dnpm/etl/processor
diff options
context:
space:
mode:
authorPaul-Christian Volkmer2025-10-22 13:09:50 +0200
committerGitHub2025-10-22 13:09:50 +0200
commit004e1021c8156bf81f85ac5ad1ef6d260392dc6f (patch)
tree9b448c286e5023440a60633e5dc58a25545aac2b /src/main/java/dev/dnpm/etl/processor
parent8ccda539b1923dd793744467b780ffc4c2897e71 (diff)
Merge pull request #154
Diffstat (limited to 'src/main/java/dev/dnpm/etl/processor')
-rw-r--r--src/main/java/dev/dnpm/etl/processor/consent/GicsConsentService.java226
1 files changed, 138 insertions, 88 deletions
diff --git a/src/main/java/dev/dnpm/etl/processor/consent/GicsConsentService.java b/src/main/java/dev/dnpm/etl/processor/consent/GicsConsentService.java
index 95e8e8f..de722e6 100644
--- a/src/main/java/dev/dnpm/etl/processor/consent/GicsConsentService.java
+++ b/src/main/java/dev/dnpm/etl/processor/consent/GicsConsentService.java
@@ -4,6 +4,8 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.DataFormatException;
import dev.dnpm.etl.processor.config.AppFhirConfig;
import dev.dnpm.etl.processor.config.GIcsConfigProperties;
+import kotlin.random.Random;
+import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent;
@@ -34,6 +36,8 @@ public class GicsConsentService implements IConsentService {
public static final String IS_CONSENTED_ENDPOINT = "/$isConsented";
public static final String IS_POLICY_STATES_FOR_PERSON_ENDPOINT = "/$currentPolicyStatesForPerson";
+ private static final String BROAD_CONSENT_PROFILE_URI = "https://www.medizininformatik-initiative.de/fhir/modul-consent/StructureDefinition/mii-pr-consent-einwilligung";
+ private static final String BROAD_CONSENT_POLICY = "urn:oid:2.16.840.1.113883.3.1937.777.24.2.1791";
private final RetryTemplate retryTemplate;
private final RestTemplate restTemplate;
@@ -41,10 +45,10 @@ public class GicsConsentService implements IConsentService {
private final GIcsConfigProperties gIcsConfigProperties;
public GicsConsentService(
- GIcsConfigProperties gIcsConfigProperties,
- RetryTemplate retryTemplate,
- RestTemplate restTemplate,
- AppFhirConfig appFhirConfig
+ GIcsConfigProperties gIcsConfigProperties,
+ RetryTemplate retryTemplate,
+ RestTemplate restTemplate,
+ AppFhirConfig appFhirConfig
) {
this.retryTemplate = retryTemplate;
this.restTemplate = restTemplate;
@@ -54,34 +58,34 @@ public class GicsConsentService implements IConsentService {
}
protected Parameters getFhirRequestParameters(
- String personIdentifierValue
+ String personIdentifierValue
) {
var result = new Parameters();
result.addParameter(
- new ParametersParameterComponent()
- .setName("personIdentifier")
- .setValue(
- new Identifier()
- .setValue(personIdentifierValue)
- .setSystem(this.gIcsConfigProperties.getPersonIdentifierSystem())
- )
+ new ParametersParameterComponent()
+ .setName("personIdentifier")
+ .setValue(
+ new Identifier()
+ .setValue(personIdentifierValue)
+ .setSystem(this.gIcsConfigProperties.getPersonIdentifierSystem())
+ )
);
result.addParameter(
- new ParametersParameterComponent()
- .setName("domain")
- .setValue(
- new StringType()
- .setValue(this.gIcsConfigProperties.getBroadConsentDomainName())
- )
+ new ParametersParameterComponent()
+ .setName("domain")
+ .setValue(
+ new StringType()
+ .setValue(this.gIcsConfigProperties.getBroadConsentDomainName())
+ )
);
result.addParameter(
- new ParametersParameterComponent()
- .setName("policy")
- .setValue(
- new Coding()
- .setCode(this.gIcsConfigProperties.getBroadConsentPolicyCode())
- .setSystem(this.gIcsConfigProperties.getBroadConsentPolicySystem())
- )
+ new ParametersParameterComponent()
+ .setName("policy")
+ .setValue(
+ new Coding()
+ .setCode(this.gIcsConfigProperties.getBroadConsentPolicyCode())
+ .setSystem(this.gIcsConfigProperties.getBroadConsentPolicySystem())
+ )
);
/*
@@ -89,10 +93,10 @@ public class GicsConsentService implements IConsentService {
* 'ignoreVersionNumber'.
*/
result.addParameter(
- new ParametersParameterComponent()
- .setName("version")
- .setValue(new StringType().setValue("1.1")
- )
+ new ParametersParameterComponent()
+ .setName("version")
+ .setValue(new StringType().setValue("1.1")
+ )
);
/* add config parameter with:
@@ -101,17 +105,17 @@ public class GicsConsentService implements IConsentService {
* unknownStateIsConsideredAsDecline -> true
*/
var config = new ParametersParameterComponent()
- .setName("config")
- .addPart(
- new ParametersParameterComponent()
- .setName("ignoreVersionNumber")
- .setValue(new BooleanType().setValue(true))
- )
- .addPart(
- new ParametersParameterComponent()
- .setName("unknownStateIsConsideredAsDecline")
- .setValue(new BooleanType().setValue(false))
- );
+ .setName("config")
+ .addPart(
+ new ParametersParameterComponent()
+ .setName("ignoreVersionNumber")
+ .setValue(new BooleanType().setValue(true))
+ )
+ .addPart(
+ new ParametersParameterComponent()
+ .setName("unknownStateIsConsideredAsDecline")
+ .setValue(new BooleanType().setValue(false))
+ );
result.addParameter(config);
@@ -130,8 +134,8 @@ public class GicsConsentService implements IConsentService {
headers.setContentType(MediaType.APPLICATION_XML);
if (
- StringUtils.isBlank(this.gIcsConfigProperties.getUsername())
- || StringUtils.isBlank(this.gIcsConfigProperties.getPassword())
+ StringUtils.isBlank(this.gIcsConfigProperties.getUsername())
+ || StringUtils.isBlank(this.gIcsConfigProperties.getPassword())
) {
return headers;
}
@@ -145,28 +149,28 @@ public class GicsConsentService implements IConsentService {
HttpEntity<String> requestEntity = new HttpEntity<>(parameterAsXml, this.headersWithHttpBasicAuth());
try {
var responseEntity = retryTemplate.execute(
- ctx -> restTemplate.exchange(endpointUri(endpoint), HttpMethod.POST, requestEntity, String.class)
+ ctx -> restTemplate.exchange(endpointUri(endpoint), HttpMethod.POST, requestEntity, String.class)
);
if (responseEntity.getStatusCode().is2xxSuccessful()) {
return responseEntity.getBody();
} else {
var msg = String.format(
- "Trusted party system reached but request failed! code: '%s' response: '%s'",
- responseEntity.getStatusCode(), responseEntity.getBody());
+ "Trusted party system reached but request failed! code: '%s' response: '%s'",
+ responseEntity.getStatusCode(), responseEntity.getBody());
log.error(msg);
return null;
}
} catch (RestClientException e) {
var msg = String.format("Get consents status request failed reason: '%s",
- e.getMessage());
+ e.getMessage());
log.error(msg);
return null;
} catch (TerminatedRetryException terminatedRetryException) {
var msg = String.format(
- "Get consents status process has been terminated. termination reason: '%s",
- terminatedRetryException.getMessage());
+ "Get consents status process has been terminated. termination reason: '%s",
+ terminatedRetryException.getMessage());
log.error(msg);
return null;
}
@@ -175,45 +179,45 @@ public class GicsConsentService implements IConsentService {
@Override
public TtpConsentStatus getTtpBroadConsentStatus(String personIdentifierValue) {
var consentStatusResponse = callGicsApi(
- getFhirRequestParameters(personIdentifierValue),
- GicsConsentService.IS_CONSENTED_ENDPOINT
+ getFhirRequestParameters(personIdentifierValue),
+ GicsConsentService.IS_CONSENTED_ENDPOINT
);
return evaluateConsentResponse(consentStatusResponse);
}
protected Bundle currentConsentForPersonAndTemplate(
- String personIdentifierValue,
- ConsentDomain consentDomain,
- Date requestDate
+ String personIdentifierValue,
+ ConsentDomain consentDomain,
+ Date requestDate
) {
var requestParameter = buildRequestParameterCurrentPolicyStatesForPerson(
- personIdentifierValue,
- requestDate,
- consentDomain
+ personIdentifierValue,
+ requestDate,
+ consentDomain
);
var consentDataSerialized = callGicsApi(requestParameter,
- GicsConsentService.IS_POLICY_STATES_FOR_PERSON_ENDPOINT);
+ GicsConsentService.IS_POLICY_STATES_FOR_PERSON_ENDPOINT);
if (consentDataSerialized == null) {
// error occurred - should not process further!
throw new IllegalStateException(
- "consent data request failed - stopping processing! - try again or fix other problems first.");
+ "consent data request failed - stopping processing! - try again or fix other problems first.");
}
var iBaseResource = fhirContext.newJsonParser()
- .parseResource(consentDataSerialized);
+ .parseResource(consentDataSerialized);
if (iBaseResource instanceof OperationOutcome) {
// log error - very likely a configuration error
String errorMessage =
- "Consent request failed! Check outcome:\n " + consentDataSerialized;
+ "Consent request failed! Check outcome:\n " + consentDataSerialized;
log.error(errorMessage);
throw new IllegalStateException(errorMessage);
} else if (iBaseResource instanceof Bundle bundle) {
return bundle;
} else {
String errorMessage = "Consent request failed! Unexpected response received! -> "
- + consentDataSerialized;
+ + consentDataSerialized;
log.error(errorMessage);
throw new IllegalStateException(errorMessage);
}
@@ -228,43 +232,43 @@ public class GicsConsentService implements IConsentService {
}
protected Parameters buildRequestParameterCurrentPolicyStatesForPerson(
- String personIdentifierValue,
- Date requestDate,
- ConsentDomain consentDomain
+ String personIdentifierValue,
+ Date requestDate,
+ ConsentDomain consentDomain
) {
var requestParameter = new Parameters();
requestParameter.addParameter(
- new ParametersParameterComponent()
- .setName("personIdentifier")
- .setValue(
- new Identifier()
- .setValue(personIdentifierValue)
- .setSystem(this.gIcsConfigProperties.getPersonIdentifierSystem())
- )
+ new ParametersParameterComponent()
+ .setName("personIdentifier")
+ .setValue(
+ new Identifier()
+ .setValue(personIdentifierValue)
+ .setSystem(this.gIcsConfigProperties.getPersonIdentifierSystem())
+ )
);
requestParameter.addParameter(
- new ParametersParameterComponent()
- .setName("domain")
- .setValue(new StringType().setValue(getConsentDomainName(consentDomain)))
+ new ParametersParameterComponent()
+ .setName("domain")
+ .setValue(new StringType().setValue(getConsentDomainName(consentDomain)))
);
Parameters nestedConfigParameters = new Parameters();
nestedConfigParameters
- .addParameter(
- new ParametersParameterComponent()
- .setName("idMatchingType")
- .setValue(new Coding()
- .setSystem("https://ths-greifswald.de/fhir/CodeSystem/gics/IdMatchingType")
- .setCode("AT_LEAST_ONE")
- )
- )
- .addParameter("ignoreVersionNumber", false)
- .addParameter("unknownStateIsConsideredAsDecline", false)
- .addParameter("requestDate", new DateType().setValue(requestDate));
+ .addParameter(
+ new ParametersParameterComponent()
+ .setName("idMatchingType")
+ .setValue(new Coding()
+ .setSystem("https://ths-greifswald.de/fhir/CodeSystem/gics/IdMatchingType")
+ .setCode("AT_LEAST_ONE")
+ )
+ )
+ .addParameter("ignoreVersionNumber", false)
+ .addParameter("unknownStateIsConsideredAsDecline", false)
+ .addParameter("requestDate", new DateType().setValue(requestDate));
requestParameter.addParameter(
- new ParametersParameterComponent().setName("config").addPart().setResource(nestedConfigParameters)
+ new ParametersParameterComponent().setName("config").addPart().setResource(nestedConfigParameters)
);
return requestParameter;
@@ -291,7 +295,7 @@ public class GicsConsentService implements IConsentService {
}
} else if (response instanceof OperationOutcome outcome) {
log.error("failed to get consent status from ttp. probably configuration error. "
- + "outcome: '{}'", fhirContext.newJsonParser().encodeToString(outcome));
+ + "outcome: '{}'", fhirContext.newJsonParser().encodeToString(outcome));
}
} catch (DataFormatException dfe) {
@@ -302,6 +306,52 @@ public class GicsConsentService implements IConsentService {
@Override
public Bundle getConsent(String patientId, Date requestDate, ConsentDomain consentDomain) {
- return currentConsentForPersonAndTemplate(patientId, consentDomain, requestDate);
+ Bundle gIcsResultBundle = currentConsentForPersonAndTemplate(patientId, consentDomain, requestDate);
+ if (ConsentDomain.BROAD_CONSENT == consentDomain) {
+ return anonymizeBroadConsent(convertGicsResultToMiiBroadConsent(gIcsResultBundle));
+ }
+ return gIcsResultBundle;
+ }
+
+ protected Bundle convertGicsResultToMiiBroadConsent(Bundle gIcsResultBundle) {
+ if (gIcsResultBundle == null
+ || gIcsResultBundle.getEntry().isEmpty()
+ || !(gIcsResultBundle.getEntry().getFirst().getResource() instanceof Consent))
+ return gIcsResultBundle;
+
+ Bundle.BundleEntryComponent bundleEntryComponent = gIcsResultBundle.getEntry().getFirst();
+
+ var consentAsOne = (Consent) bundleEntryComponent.getResource();
+ if (consentAsOne.getPolicy().stream().noneMatch(p -> p.getUri().equals(BROAD_CONSENT_POLICY))) {
+ consentAsOne.addPolicy(new Consent.ConsentPolicyComponent().setUri(BROAD_CONSENT_POLICY));
+ }
+
+ if (consentAsOne.getMeta().getProfile().stream().noneMatch(p -> p.getValue().equals(BROAD_CONSENT_PROFILE_URI))) {
+ consentAsOne.getMeta().addProfile(BROAD_CONSENT_PROFILE_URI);
+ }
+
+ consentAsOne.setPolicyRule(null);
+
+ gIcsResultBundle.getEntry().stream().skip(1).forEach(c -> consentAsOne.getProvision().addProvision(((Consent) c.getResource()).getProvision().getProvisionFirstRep()));
+
+ gIcsResultBundle.getEntry().clear();
+ gIcsResultBundle.addEntry(bundleEntryComponent);
+ return gIcsResultBundle;
+ }
+
+ protected Bundle anonymizeBroadConsent(Bundle bundle) {
+ Bundle.BundleEntryComponent bundleEntryComponent = bundle.getEntry().getFirst();
+ hashBundleEntry(bundleEntryComponent);
+ return bundle;
+ }
+
+ private static void hashBundleEntry(Bundle.BundleEntryComponent entry) {
+ String id = entry.getResource().getIdPart();
+ var hash = DigestUtils.sha256Hex("%s_%s".formatted(Random.Default.toString(), id));
+
+ entry.getResource().setId(hash);
+ entry.setFullUrl(entry.getFullUrl().replace(id, hash));
+ var consent = (Consent) entry.getResource();
+ consent.getSource().setProperty("reference", new StringType("QuestionnaireResponse/%s".formatted(hash)));
}
}