diff options
Diffstat (limited to 'src/main/java')
| -rw-r--r-- | src/main/java/dev/dnpm/etl/processor/consent/GicsConsentService.java | 226 |
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))); } } |
