summaryrefslogtreecommitdiff
path: root/src/main/java
diff options
context:
space:
mode:
authorPaul-Christian Volkmer2025-08-15 12:37:42 +0200
committerGitHub2025-08-15 12:37:42 +0200
commit3eb1c79cec3704a5b821377c4df3f8e9f703c8a3 (patch)
treed73809a1a8b28eef3824c2da8db4559f22739ace /src/main/java
parentbe513f305ae4c632aa567e42e9438f233590ab3f (diff)
feat: check consent for DNPM 2.1 requests (#126)
Co-authored-by: Jakub Lidke <jakub.lidke@uni-marburg.de>
Diffstat (limited to 'src/main/java')
-rw-r--r--src/main/java/dev/dnpm/etl/processor/consent/ConsentDomain.java6
-rw-r--r--src/main/java/dev/dnpm/etl/processor/consent/GicsConsentService.java292
-rw-r--r--src/main/java/dev/dnpm/etl/processor/consent/IConsentService.java (renamed from src/main/java/dev/dnpm/etl/processor/consent/IGetConsent.java)2
-rw-r--r--src/main/java/dev/dnpm/etl/processor/consent/MtbFileConsentService.java (renamed from src/main/java/dev/dnpm/etl/processor/consent/ConsentByMtbFile.java)6
4 files changed, 166 insertions, 140 deletions
diff --git a/src/main/java/dev/dnpm/etl/processor/consent/ConsentDomain.java b/src/main/java/dev/dnpm/etl/processor/consent/ConsentDomain.java
index 6d0b160..51bfd50 100644
--- a/src/main/java/dev/dnpm/etl/processor/consent/ConsentDomain.java
+++ b/src/main/java/dev/dnpm/etl/processor/consent/ConsentDomain.java
@@ -4,10 +4,10 @@ public enum ConsentDomain {
/**
* MII Broad consent
*/
- BroadConsent,
+ BROAD_CONSENT,
/**
- * GenomDe Modelvohaben §64e
+ * GenomDe Modellvorhaben §64e
*/
- Modelvorhaben64e
+ MODELLVORHABEN_64E
}
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 6f3c987..95e8e8f 100644
--- a/src/main/java/dev/dnpm/etl/processor/consent/GicsConsentService.java
+++ b/src/main/java/dev/dnpm/etl/processor/consent/GicsConsentService.java
@@ -4,17 +4,9 @@ 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.util.Date;
import org.apache.commons.lang3.StringUtils;
-import org.hl7.fhir.r4.model.BooleanType;
-import org.hl7.fhir.r4.model.Bundle;
-import org.hl7.fhir.r4.model.Coding;
-import org.hl7.fhir.r4.model.DateType;
-import org.hl7.fhir.r4.model.Identifier;
-import org.hl7.fhir.r4.model.OperationOutcome;
-import org.hl7.fhir.r4.model.Parameters;
+import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent;
-import org.hl7.fhir.r4.model.StringType;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -22,112 +14,152 @@ import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
-import org.springframework.http.ResponseEntity;
import org.springframework.retry.TerminatedRetryException;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
+import java.net.URI;
+import java.util.Date;
-public class GicsConsentService implements IGetConsent {
+/**
+ * Service to request Consent from remote gICS installation
+ *
+ * @since 0.11
+ */
+public class GicsConsentService implements IConsentService {
private final Logger log = LoggerFactory.getLogger(GicsConsentService.class);
public static final String IS_CONSENTED_ENDPOINT = "/$isConsented";
public static final String IS_POLICY_STATES_FOR_PERSON_ENDPOINT = "/$currentPolicyStatesForPerson";
+
private final RetryTemplate retryTemplate;
private final RestTemplate restTemplate;
private final FhirContext fhirContext;
- private final HttpHeaders httpHeader;
private final GIcsConfigProperties gIcsConfigProperties;
- private String url;
-
- public GicsConsentService(GIcsConfigProperties gIcsConfigProperties,
- RetryTemplate retryTemplate, RestTemplate restTemplate, AppFhirConfig appFhirConfig) {
+ public GicsConsentService(
+ GIcsConfigProperties gIcsConfigProperties,
+ RetryTemplate retryTemplate,
+ RestTemplate restTemplate,
+ AppFhirConfig appFhirConfig
+ ) {
this.retryTemplate = retryTemplate;
this.restTemplate = restTemplate;
this.fhirContext = appFhirConfig.fhirContext();
- httpHeader = buildHeader(gIcsConfigProperties.getUsername(),
- gIcsConfigProperties.getPassword());
this.gIcsConfigProperties = gIcsConfigProperties;
log.info("GicsConsentService initialized...");
}
- public String getGicsUri(String endpoint) {
- if (url == null) {
- final String gIcsBaseUri = gIcsConfigProperties.getUri();
- if (StringUtils.isBlank(gIcsBaseUri)) {
- throw new IllegalArgumentException(
- "gICS base URL is empty - should call gICS with false configuration.");
- }
- url = UriComponentsBuilder.fromUriString(gIcsBaseUri).path(endpoint)
- .toUriString();
- }
- return url;
- }
-
- @NotNull
- private static HttpHeaders buildHeader(String gPasUserName, String gPasPassword) {
- var headers = new HttpHeaders();
- headers.setContentType(MediaType.APPLICATION_XML);
-
- if (StringUtils.isBlank(gPasUserName) || StringUtils.isBlank(gPasPassword)) {
- return headers;
- }
-
- headers.setBasicAuth(gPasUserName, gPasPassword);
- return headers;
- }
-
- protected static Parameters getIsConsentedRequestParam(GIcsConfigProperties configProperties,
- String personIdentifierValue) {
+ protected Parameters getFhirRequestParameters(
+ String personIdentifierValue
+ ) {
var result = new Parameters();
- result.addParameter(new ParametersParameterComponent().setName("personIdentifier").setValue(
- new Identifier().setValue(personIdentifierValue)
- .setSystem(configProperties.getPersonIdentifierSystem())));
- result.addParameter(new ParametersParameterComponent().setName("domain")
- .setValue(new StringType().setValue(configProperties.getBroadConsentDomainName())));
- result.addParameter(new ParametersParameterComponent().setName("policy").setValue(
- new Coding().setCode(configProperties.getBroadConsentPolicyCode())
- .setSystem(configProperties.getBroadConsentPolicySystem())));
+ result.addParameter(
+ 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())
+ )
+ );
+ result.addParameter(
+ new ParametersParameterComponent()
+ .setName("policy")
+ .setValue(
+ new Coding()
+ .setCode(this.gIcsConfigProperties.getBroadConsentPolicyCode())
+ .setSystem(this.gIcsConfigProperties.getBroadConsentPolicySystem())
+ )
+ );
/*
* is mandatory parameter, but we ignore it via additional configuration parameter
* 'ignoreVersionNumber'.
*/
- result.addParameter(new ParametersParameterComponent().setName("version")
- .setValue(new StringType().setValue("1.1")));
+ result.addParameter(
+ new ParametersParameterComponent()
+ .setName("version")
+ .setValue(new StringType().setValue("1.1")
+ )
+ );
/* add config parameter with:
* ignoreVersionNumber -> true ->> Reason is we cannot know which policy version each patient
* has possibly signed or not, therefore we are happy with any version found.
* 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)));
+ 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))
+ );
+
result.addParameter(config);
return result;
}
+ private URI endpointUri(String endpoint) {
+ assert this.gIcsConfigProperties.getUri() != null;
+ return UriComponentsBuilder.fromUriString(this.gIcsConfigProperties.getUri()).path(endpoint).build().toUri();
+ }
+
+ private HttpHeaders headersWithHttpBasicAuth() {
+ assert this.gIcsConfigProperties.getUri() != null;
+
+ var headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_XML);
+
+ if (
+ StringUtils.isBlank(this.gIcsConfigProperties.getUsername())
+ || StringUtils.isBlank(this.gIcsConfigProperties.getPassword())
+ ) {
+ return headers;
+ }
+
+ headers.setBasicAuth(this.gIcsConfigProperties.getUsername(), this.gIcsConfigProperties.getPassword());
+ return headers;
+ }
+
protected String callGicsApi(Parameters parameter, String endpoint) {
var parameterAsXml = fhirContext.newXmlParser().encodeResourceToString(parameter);
-
- HttpEntity<String> requestEntity = new HttpEntity<>(parameterAsXml, this.httpHeader);
- ResponseEntity<String> responseEntity;
+ HttpEntity<String> requestEntity = new HttpEntity<>(parameterAsXml, this.headersWithHttpBasicAuth());
try {
- var url = getGicsUri(endpoint);
-
- responseEntity = retryTemplate.execute(
- ctx -> restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class));
+ var responseEntity = retryTemplate.execute(
+ 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());
+ 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;
@@ -137,39 +169,32 @@ public class GicsConsentService implements IGetConsent {
terminatedRetryException.getMessage());
log.error(msg);
return null;
-
- }
- 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());
- log.error(msg);
- return null;
}
}
@Override
public TtpConsentStatus getTtpBroadConsentStatus(String personIdentifierValue) {
- var parameter = GicsConsentService.getIsConsentedRequestParam(gIcsConfigProperties,
- personIdentifierValue);
-
- var consentStatusResponse = callGicsApi(parameter,
- GicsConsentService.IS_CONSENTED_ENDPOINT);
+ var consentStatusResponse = callGicsApi(
+ getFhirRequestParameters(personIdentifierValue),
+ GicsConsentService.IS_CONSENTED_ENDPOINT
+ );
return evaluateConsentResponse(consentStatusResponse);
}
- protected Bundle currentConsentForPersonAndTemplate(String personIdentifierValue,
- ConsentDomain targetConsentDomain, Date requestDate) {
-
- String consentDomain = getConsentDomain(targetConsentDomain);
+ protected Bundle currentConsentForPersonAndTemplate(
+ String personIdentifierValue,
+ ConsentDomain consentDomain,
+ Date requestDate
+ ) {
- var requestParameter = GicsConsentService.buildRequestParameterCurrentPolicyStatesForPerson(
- gIcsConfigProperties, personIdentifierValue, requestDate, consentDomain);
+ var requestParameter = buildRequestParameterCurrentPolicyStatesForPerson(
+ 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!
@@ -177,15 +202,15 @@ public class GicsConsentService implements IGetConsent {
"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;
log.error(errorMessage);
throw new IllegalStateException(errorMessage);
- } else if (iBaseResource instanceof Bundle) {
- return (Bundle) iBaseResource;
+ } else if (iBaseResource instanceof Bundle bundle) {
+ return bundle;
} else {
String errorMessage = "Consent request failed! Unexpected response received! -> "
+ consentDataSerialized;
@@ -195,40 +220,52 @@ public class GicsConsentService implements IGetConsent {
}
@NotNull
- private String getConsentDomain(ConsentDomain targetConsentDomain) {
- String consentDomain;
- switch (targetConsentDomain) {
- case BroadConsent -> consentDomain = gIcsConfigProperties.getBroadConsentDomainName();
- case Modelvorhaben64e ->
- consentDomain = gIcsConfigProperties.getGenomDeConsentDomainName();
- default -> throw new IllegalArgumentException(
- "target ConsentDomain is missing but must be provided!");
- }
- return consentDomain;
+ private String getConsentDomainName(ConsentDomain targetConsentDomain) {
+ return switch (targetConsentDomain) {
+ case BROAD_CONSENT -> gIcsConfigProperties.getBroadConsentDomainName();
+ case MODELLVORHABEN_64E -> gIcsConfigProperties.getGenomDeConsentDomainName();
+ };
}
- protected static Parameters buildRequestParameterCurrentPolicyStatesForPerson(
- GIcsConfigProperties gIcsConfigProperties, String personIdentifierValue, Date requestDate,
- String targetDomain) {
+ protected Parameters buildRequestParameterCurrentPolicyStatesForPerson(
+ String personIdentifierValue,
+ Date requestDate,
+ ConsentDomain consentDomain
+ ) {
var requestParameter = new Parameters();
- requestParameter.addParameter(new ParametersParameterComponent().setName("personIdentifier")
- .setValue(new Identifier().setValue(personIdentifierValue)
- .setSystem(gIcsConfigProperties.getPersonIdentifierSystem())));
-
- requestParameter.addParameter(new ParametersParameterComponent().setName("domain")
- .setValue(new StringType().setValue(targetDomain)));
+ requestParameter.addParameter(
+ 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)))
+ );
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)
+ 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));
- requestParameter.addParameter(new ParametersParameterComponent().setName("config").addPart()
- .setResource(nestedConfigParameters));
+ requestParameter.addParameter(
+ new ParametersParameterComponent().setName("config").addPart().setResource(nestedConfigParameters)
+ );
return requestParameter;
}
@@ -254,7 +291,7 @@ public class GicsConsentService implements IGetConsent {
}
} 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) {
@@ -265,17 +302,6 @@ public class GicsConsentService implements IGetConsent {
@Override
public Bundle getConsent(String patientId, Date requestDate, ConsentDomain consentDomain) {
- switch (consentDomain) {
- case BroadConsent -> {
- return currentConsentForPersonAndTemplate(patientId, ConsentDomain.BroadConsent,
- requestDate);
- }
- case Modelvorhaben64e -> {
- return currentConsentForPersonAndTemplate(patientId,
- ConsentDomain.Modelvorhaben64e, requestDate);
- }
- }
-
- return new Bundle();
+ return currentConsentForPersonAndTemplate(patientId, consentDomain, requestDate);
}
}
diff --git a/src/main/java/dev/dnpm/etl/processor/consent/IGetConsent.java b/src/main/java/dev/dnpm/etl/processor/consent/IConsentService.java
index 3482b9a..ded3515 100644
--- a/src/main/java/dev/dnpm/etl/processor/consent/IGetConsent.java
+++ b/src/main/java/dev/dnpm/etl/processor/consent/IConsentService.java
@@ -3,7 +3,7 @@ package dev.dnpm.etl.processor.consent;
import java.util.Date;
import org.hl7.fhir.r4.model.Bundle;
-public interface IGetConsent {
+public interface IConsentService {
/**
* Get broad consent status for a patient identifier
diff --git a/src/main/java/dev/dnpm/etl/processor/consent/ConsentByMtbFile.java b/src/main/java/dev/dnpm/etl/processor/consent/MtbFileConsentService.java
index f7ce39e..24cb8f7 100644
--- a/src/main/java/dev/dnpm/etl/processor/consent/ConsentByMtbFile.java
+++ b/src/main/java/dev/dnpm/etl/processor/consent/MtbFileConsentService.java
@@ -5,11 +5,11 @@ import org.hl7.fhir.r4.model.Bundle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class ConsentByMtbFile implements IGetConsent {
+public class MtbFileConsentService implements IConsentService {
- private static final Logger log = LoggerFactory.getLogger(ConsentByMtbFile.class);
+ private static final Logger log = LoggerFactory.getLogger(MtbFileConsentService.class);
- public ConsentByMtbFile() {
+ public MtbFileConsentService() {
log.info("ConsentCheckFileBased initialized...");
}