summaryrefslogtreecommitdiff
path: root/src/main/java/dev/dnpm
diff options
context:
space:
mode:
authorPaul-Christian Volkmer2025-12-03 12:07:42 +0100
committerGitHub2025-12-03 12:07:42 +0100
commitb56b8c1b6cc9a3e8bcd19adde2b832af15d3a526 (patch)
treea086d03bd187ea2401a4c31343a629f69167fdc6 /src/main/java/dev/dnpm
parentf3b062725fca472bff95e157ba75d973865da6ff (diff)
feat: simple HTTP GET based consent fetch (#208)
Diffstat (limited to 'src/main/java/dev/dnpm')
-rw-r--r--src/main/java/dev/dnpm/etl/processor/consent/AbstractConsentService.java50
-rw-r--r--src/main/java/dev/dnpm/etl/processor/consent/GicsConsentService.java42
-rw-r--r--src/main/java/dev/dnpm/etl/processor/consent/GicsGetBroadConsentService.java134
3 files changed, 187 insertions, 39 deletions
diff --git a/src/main/java/dev/dnpm/etl/processor/consent/AbstractConsentService.java b/src/main/java/dev/dnpm/etl/processor/consent/AbstractConsentService.java
new file mode 100644
index 0000000..10330a4
--- /dev/null
+++ b/src/main/java/dev/dnpm/etl/processor/consent/AbstractConsentService.java
@@ -0,0 +1,50 @@
+package dev.dnpm.etl.processor.consent;
+
+import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.parser.DataFormatException;
+import org.hl7.fhir.r4.model.OperationOutcome;
+import org.hl7.fhir.r4.model.Parameters;
+import org.jspecify.annotations.Nullable;
+import org.slf4j.Logger;
+
+public abstract class AbstractConsentService implements IConsentService {
+
+ protected final Logger log;
+ protected final FhirContext fhirContext;
+
+ protected AbstractConsentService(FhirContext fhirContext, Logger log) {
+ this.fhirContext = fhirContext;
+ this.log = log;
+ }
+
+ protected TtpConsentStatus evaluateConsentResponse(@Nullable String consentStatusResponse) {
+ if (consentStatusResponse == null) {
+ return TtpConsentStatus.FAILED_TO_ASK;
+ }
+ try {
+ var response = fhirContext.newJsonParser().parseResource(consentStatusResponse);
+
+ if (response instanceof Parameters responseParameters) {
+
+ var responseValue = responseParameters.getParameter("consented").getValue();
+ var isConsented = responseValue.castToBoolean(responseValue);
+ if (!isConsented.hasValue()) {
+ return TtpConsentStatus.FAILED_TO_ASK;
+ }
+ if (isConsented.booleanValue()) {
+ return TtpConsentStatus.BROAD_CONSENT_GIVEN;
+ } else {
+ return TtpConsentStatus.BROAD_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));
+ }
+ } catch (DataFormatException dfe) {
+ log.error("failed to parse response to FHIR R4 resource.", dfe);
+ }
+ return TtpConsentStatus.FAILED_TO_ASK;
+ }
+}
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 72238a3..fbc61a4 100644
--- a/src/main/java/dev/dnpm/etl/processor/consent/GicsConsentService.java
+++ b/src/main/java/dev/dnpm/etl/processor/consent/GicsConsentService.java
@@ -1,7 +1,5 @@
package dev.dnpm.etl.processor.consent;
-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 java.net.URI;
@@ -15,7 +13,6 @@ import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent;
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;
-import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
@@ -31,9 +28,7 @@ import org.springframework.web.client.RestTemplate;
*
* @since 0.11
*/
-public class GicsConsentService implements IConsentService {
-
- private final Logger log = LoggerFactory.getLogger(GicsConsentService.class);
+public class GicsConsentService extends AbstractConsentService {
public static final String IS_CONSENTED_ENDPOINT = "/$isConsented";
public static final String IS_POLICY_STATES_FOR_PERSON_ENDPOINT =
@@ -43,7 +38,6 @@ public class GicsConsentService implements IConsentService {
private final RetryTemplate retryTemplate;
private final RestTemplate restTemplate;
- private final FhirContext fhirContext;
private final GIcsConfigProperties gIcsConfigProperties;
public GicsConsentService(
@@ -51,9 +45,10 @@ public class GicsConsentService implements IConsentService {
RetryTemplate retryTemplate,
RestTemplate restTemplate,
AppFhirConfig appFhirConfig) {
+ super(appFhirConfig.fhirContext(), LoggerFactory.getLogger(GicsConsentService.class));
+
this.retryTemplate = retryTemplate;
this.restTemplate = restTemplate;
- this.fhirContext = appFhirConfig.fhirContext();
this.gIcsConfigProperties = gIcsConfigProperties;
log.info("GicsConsentService initialized...");
}
@@ -272,37 +267,6 @@ public class GicsConsentService implements IConsentService {
return requestParameter;
}
- private TtpConsentStatus evaluateConsentResponse(@Nullable String consentStatusResponse) {
- if (consentStatusResponse == null) {
- return TtpConsentStatus.FAILED_TO_ASK;
- }
- try {
- var response = fhirContext.newJsonParser().parseResource(consentStatusResponse);
-
- if (response instanceof Parameters responseParameters) {
-
- var responseValue = responseParameters.getParameter("consented").getValue();
- var isConsented = responseValue.castToBoolean(responseValue);
- if (!isConsented.hasValue()) {
- return TtpConsentStatus.FAILED_TO_ASK;
- }
- if (isConsented.booleanValue()) {
- return TtpConsentStatus.BROAD_CONSENT_GIVEN;
- } else {
- return TtpConsentStatus.BROAD_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));
- }
- } catch (DataFormatException dfe) {
- log.error("failed to parse response to FHIR R4 resource.", dfe);
- }
- return TtpConsentStatus.FAILED_TO_ASK;
- }
-
@Override
@NonNull
public Bundle getConsent(
diff --git a/src/main/java/dev/dnpm/etl/processor/consent/GicsGetBroadConsentService.java b/src/main/java/dev/dnpm/etl/processor/consent/GicsGetBroadConsentService.java
new file mode 100644
index 0000000..246d84c
--- /dev/null
+++ b/src/main/java/dev/dnpm/etl/processor/consent/GicsGetBroadConsentService.java
@@ -0,0 +1,134 @@
+package dev.dnpm.etl.processor.consent;
+
+import dev.dnpm.etl.processor.config.AppFhirConfig;
+import dev.dnpm.etl.processor.config.GIcsConfigProperties;
+import java.net.URISyntaxException;
+import java.util.Date;
+import org.apache.hc.core5.net.URIBuilder;
+import org.hl7.fhir.r4.model.Bundle;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.retry.TerminatedRetryException;
+import org.springframework.retry.support.RetryTemplate;
+import org.springframework.web.client.RestClientException;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * Service to request Broad Consent only from remote gICS installation using REST/HTTP GET request
+ *
+ * @since 0.12
+ */
+@NullMarked
+public class GicsGetBroadConsentService extends AbstractConsentService {
+
+ private final RetryTemplate retryTemplate;
+ private final RestTemplate restTemplate;
+ private final GIcsConfigProperties gIcsConfigProperties;
+
+ public GicsGetBroadConsentService(
+ GIcsConfigProperties gIcsConfigProperties,
+ RetryTemplate retryTemplate,
+ RestTemplate restTemplate,
+ AppFhirConfig appFhirConfig) {
+ super(appFhirConfig.fhirContext(), LoggerFactory.getLogger(GicsGetBroadConsentService.class));
+
+ this.retryTemplate = retryTemplate;
+ this.restTemplate = restTemplate;
+ this.gIcsConfigProperties = gIcsConfigProperties;
+
+ if (null == this.gIcsConfigProperties.getUri()) {
+ throw new IllegalStateException("Missing gICS URI configuration");
+ }
+
+ log.info("GicsGetBroadConsentService initialized...");
+ }
+
+ @Override
+ public TtpConsentStatus getTtpBroadConsentStatus(String personIdentifierValue) {
+ var consentStatusResponse =
+ requestResponse(
+ personIdentifierValue, this.gIcsConfigProperties.getBroadConsentDomainName());
+ return evaluateConsentResponse(consentStatusResponse);
+ }
+
+ @Override
+ public Bundle getConsent(
+ String personIdentifierValue, Date requestDate, ConsentDomain consentDomain) {
+ return fhirContext
+ .newJsonParser()
+ .parseResource(
+ Bundle.class,
+ requestResponse(
+ personIdentifierValue, gIcsConfigProperties.getBroadConsentDomainName()));
+ }
+
+ @Nullable
+ private String requestResponse(String personIdentifierValue, String consentDomain) {
+ if (null == this.gIcsConfigProperties.getUri()) {
+ throw new IllegalStateException("Missing gICS URI configuration");
+ }
+
+ final var patientIdentifierQueryValue =
+ "%s|%s"
+ .formatted(
+ this.gIcsConfigProperties.getPersonIdentifierSystem(), personIdentifierValue);
+
+ try {
+ final var uri =
+ new URIBuilder(gIcsConfigProperties.getUri())
+ .appendPathSegments("Consent")
+ .addParameter("domain:identifier", consentDomain)
+ .addParameter(
+ "category",
+ "http://fhir.de/ConsentManagement/CodeSystem/ResultType|consent-status")
+ .addParameter("patient.identifier", patientIdentifierQueryValue)
+ .build();
+
+ final var requestHeaders = new HttpHeaders();
+
+ if (null != gIcsConfigProperties.getUsername()
+ && null != gIcsConfigProperties.getPassword()
+ && !gIcsConfigProperties.getUsername().isBlank()
+ && !gIcsConfigProperties.getPassword().isBlank()) {
+ requestHeaders.setBasicAuth(
+ gIcsConfigProperties.getUsername(), gIcsConfigProperties.getPassword());
+ }
+
+ final var response =
+ this.retryTemplate.execute(
+ retryContext ->
+ this.restTemplate.exchange(
+ uri, HttpMethod.GET, new HttpEntity<>(requestHeaders), String.class));
+ if (response.getStatusCode().is2xxSuccessful()) {
+ return response.getBody();
+ } else {
+ var msg =
+ String.format(
+ "Trusted party system reached but request failed! code: '%s' response: '%s'",
+ response.getStatusCode(), response.getBody());
+ log.error(msg);
+ return null;
+ }
+ } catch (RestClientException e) {
+ var msg = String.format("Get consents status request failed reason: '%s", 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());
+ log.error(msg);
+ return null;
+ } catch (URISyntaxException e) {
+ var msg = String.format("Invalid URI for consents status request: '%s", e.getMessage());
+ log.error(msg);
+ return null;
+ }
+ }
+}