From db89d843536f7d05876b5e93835ac0e02921fb15 Mon Sep 17 00:00:00 2001 From: Jakub Lidke Date: Fri, 5 Sep 2025 14:55:58 +0200 Subject: [PATCH] fix: Setze in der Broad Consent Resource das MII Broad Consent Profil und die Consent Policy, falls diese fehlen sollten. Die von gICS einzeln gelieferte Consent Provisions werden in einer Ressource zusammengefasst. --- .../processor/consent/GicsConsentService.java | 207 ++++++++++-------- 1 file changed, 119 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..7857ede 100644 --- a/src/main/java/dev/dnpm/etl/processor/consent/GicsConsentService.java +++ b/src/main/java/dev/dnpm/etl/processor/consent/GicsConsentService.java @@ -39,12 +39,15 @@ public class GicsConsentService implements IConsentService { private final RestTemplate restTemplate; private final FhirContext fhirContext; private final GIcsConfigProperties gIcsConfigProperties; + private final String BROAD_CONSENT_PROFILE_URI = "https://www.medizininformatik-initiative.de/fhir/modul-consent/StructureDefinition/mii-pr-consent-einwilligung"; + ; + private final String BROAD_CONSENT_POLICY = "urn:oid:2.16.840.1.113883.3.1937.777.24.2.1791"; 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 +57,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 +92,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 +104,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 +133,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 +148,28 @@ public class GicsConsentService implements IConsentService { HttpEntity 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 +178,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 +231,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 +294,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 +305,34 @@ 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 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); + } + + gIcsResultBundle.getEntry().stream().skip(1).forEach(c -> consentAsOne.getProvision().addProvision(((Consent) c.getResource()).getProvision().getProvisionFirstRep())); + + gIcsResultBundle.getEntry().clear(); + gIcsResultBundle.addEntry(bundleEntryComponent); + return gIcsResultBundle; } }