mirror of
https://github.com/pcvolkmer/mv64e-etl-processor
synced 2025-09-13 17:02:52 +00:00
Compare commits
8 Commits
0.11.x
...
gics-provi
Author | SHA1 | Date | |
---|---|---|---|
|
4bd6117ba8 | ||
|
ace5637ed8 | ||
|
88857cf201 | ||
|
db89d84353 | ||
3d9d84438d | |||
10b5bedac3 | |||
96f22a6744 | |||
c38c0c6197 |
@@ -7,22 +7,22 @@ plugins {
|
||||
war
|
||||
id("org.springframework.boot") version "3.5.5"
|
||||
id("io.spring.dependency-management") version "1.1.7"
|
||||
kotlin("jvm") version "1.9.25"
|
||||
kotlin("plugin.spring") version "1.9.25"
|
||||
kotlin("jvm") version "2.2.10"
|
||||
kotlin("plugin.spring") version "2.2.10"
|
||||
jacoco
|
||||
}
|
||||
|
||||
group = "dev.dnpm"
|
||||
version = "0.11.1"
|
||||
version = "0.12.0-SNAPSHOT"
|
||||
|
||||
var versions = mapOf(
|
||||
"mtb-dto" to "0.1.0-SNAPSHOT",
|
||||
"hapi-fhir" to "7.6.1",
|
||||
"mockito-kotlin" to "5.4.0",
|
||||
"archunit" to "1.3.0",
|
||||
"hapi-fhir" to "8.4.0",
|
||||
"mockito-kotlin" to "6.0.0",
|
||||
"archunit" to "1.4.1",
|
||||
// Webjars
|
||||
"webjars-locator" to "0.52",
|
||||
"echarts" to "5.4.3",
|
||||
"echarts" to "6.0.0",
|
||||
"htmx.org" to "1.9.12"
|
||||
)
|
||||
|
||||
|
@@ -39,12 +39,14 @@ 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 +56,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 +91,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 +103,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 +132,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 +147,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 +177,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 +230,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 +293,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 +304,36 @@ 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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@@ -23,8 +23,6 @@ import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import dev.dnpm.etl.processor.config.AppFhirConfig;
|
||||
import dev.dnpm.etl.processor.config.GPasConfigProperties;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import org.apache.commons.lang3.NotImplementedException;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.hc.core5.net.URIBuilder;
|
||||
@@ -39,9 +37,11 @@ import org.springframework.http.*;
|
||||
import org.springframework.retry.support.RetryTemplate;
|
||||
import org.springframework.web.client.HttpClientErrorException.BadRequest;
|
||||
import org.springframework.web.client.HttpClientErrorException.Unauthorized;
|
||||
import org.springframework.web.client.RestClientException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
public class GpasPseudonymGenerator implements Generator {
|
||||
|
||||
private final FhirContext r4Context;
|
||||
@@ -52,10 +52,10 @@ public class GpasPseudonymGenerator implements Generator {
|
||||
private final RestTemplate restTemplate;
|
||||
private final @NotNull String genomDeTanDomain;
|
||||
private final @NotNull String pidPsnDomain;
|
||||
protected final static String createOrGetPsn = "$pseudonymizeAllowCreate";
|
||||
protected final static String createMultiDomainPsn = "$pseudonymize-secondary";
|
||||
private final static String SINGLE_PSN_PART_NAME = "pseudonym";
|
||||
private final static String MULTI_PSN_PART_NAME = "value";
|
||||
protected static final String CREATE_OR_GET_PSN = "$pseudonymizeAllowCreate";
|
||||
protected static final String CREATE_MULTI_DOMAIN_PSN = "$pseudonymize-secondary";
|
||||
private static final String SINGLE_PSN_PART_NAME = "pseudonym";
|
||||
private static final String MULTI_PSN_PART_NAME = "value";
|
||||
|
||||
public GpasPseudonymGenerator(GPasConfigProperties gpasCfg, RetryTemplate retryTemplate,
|
||||
RestTemplate restTemplate, AppFhirConfig appFhirConfig) {
|
||||
@@ -85,7 +85,7 @@ public class GpasPseudonymGenerator implements Generator {
|
||||
switch (domainType) {
|
||||
case SINGLE_PSN_DOMAIN -> {
|
||||
final var requestBody = createSinglePsnRequestBody(id, pidPsnDomain);
|
||||
final var responseEntity = getGpasPseudonym(requestBody, createOrGetPsn);
|
||||
final var responseEntity = getGpasPseudonym(requestBody, CREATE_OR_GET_PSN);
|
||||
final var gPasPseudonymResult = (Parameters) r4Context.newJsonParser()
|
||||
.parseResource(responseEntity.getBody());
|
||||
|
||||
@@ -93,7 +93,7 @@ public class GpasPseudonymGenerator implements Generator {
|
||||
}
|
||||
case MULTI_PSN_DOMAIN -> {
|
||||
final var requestBody = createMultiPsnRequestBody(id, genomDeTanDomain);
|
||||
final var responseEntity = getGpasPseudonym(requestBody, createMultiDomainPsn);
|
||||
final var responseEntity = getGpasPseudonym(requestBody, CREATE_MULTI_DOMAIN_PSN);
|
||||
final var gPasPseudonymResult = (Parameters) r4Context.newJsonParser()
|
||||
.parseResource(responseEntity.getBody());
|
||||
|
||||
@@ -150,23 +150,22 @@ public class GpasPseudonymGenerator implements Generator {
|
||||
log.debug("API request succeeded. Response: {}", responseEntity.getStatusCode());
|
||||
return responseEntity;
|
||||
}
|
||||
} catch (RestClientException rce) {
|
||||
if (rce instanceof BadRequest) {
|
||||
String msg = "gPas or request configuration is incorrect. Please check both."
|
||||
+ rce.getMessage();
|
||||
log.debug(
|
||||
msg);
|
||||
throw new PseudonymRequestFailed(msg, rce);
|
||||
}
|
||||
if (rce instanceof Unauthorized) {
|
||||
var msg = "gPas access credentials are invalid check your configuration. msg: '%s".formatted(
|
||||
rce.getMessage());
|
||||
log.error(msg);
|
||||
throw new PseudonymRequestFailed(msg, rce);
|
||||
}
|
||||
} catch (Exception unexpected) {
|
||||
} catch (BadRequest e) {
|
||||
String msg = "gPas or request configuration is incorrect. Please check both."
|
||||
+ e.getMessage();
|
||||
log.error(msg);
|
||||
throw new PseudonymRequestFailed(msg, e);
|
||||
} catch (Unauthorized e) {
|
||||
var msg = "gPas access credentials are invalid check your configuration. msg: '%s"
|
||||
.formatted(e.getMessage());
|
||||
log.error(msg);
|
||||
throw new PseudonymRequestFailed(msg, e);
|
||||
}
|
||||
catch (Exception unexpected) {
|
||||
throw new PseudonymRequestFailed(
|
||||
"API request due unexpected error unsuccessful gPas unsuccessful.", unexpected);
|
||||
"API request due unexpected error unsuccessful gPas unsuccessful.",
|
||||
unexpected
|
||||
);
|
||||
}
|
||||
throw new PseudonymRequestFailed(
|
||||
"API request due unexpected error unsuccessful gPas unsuccessful.");
|
||||
|
@@ -30,6 +30,7 @@ import org.springframework.data.relational.core.mapping.Table
|
||||
import org.springframework.data.repository.CrudRepository
|
||||
import org.springframework.data.repository.PagingAndSortingRepository
|
||||
import java.time.Instant
|
||||
import java.time.temporal.ChronoUnit
|
||||
import java.util.*
|
||||
|
||||
@Table("request")
|
||||
@@ -65,6 +66,12 @@ data class Request(
|
||||
processedAt: Instant
|
||||
) :
|
||||
this(null, uuid, patientPseudonym, pid, fingerprint, type, status, processedAt)
|
||||
|
||||
fun isPendingUnknown(): Boolean {
|
||||
return this.status == RequestStatus.UNKNOWN && this.processedAt.isBefore(
|
||||
Instant.now().minus(10, ChronoUnit.MINUTES)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@JvmRecord
|
||||
@@ -90,19 +97,23 @@ interface RequestRepository : CrudRepository<Request, Long>, PagingAndSortingRep
|
||||
@Query("SELECT count(*) AS count, status FROM request WHERE type = 'MTB_FILE' GROUP BY status ORDER BY status, count DESC;")
|
||||
fun countStates(): List<CountedState>
|
||||
|
||||
@Query("SELECT count(*) AS count, status FROM (" +
|
||||
"SELECT status, rank() OVER (PARTITION BY patient_pseudonym ORDER BY processed_at DESC) AS rank FROM request " +
|
||||
"WHERE type = 'MTB_FILE' AND status NOT IN ('DUPLICATION') " +
|
||||
") rank WHERE rank = 1 GROUP BY status ORDER BY status, count DESC;")
|
||||
@Query(
|
||||
"SELECT count(*) AS count, status FROM (" +
|
||||
"SELECT status, rank() OVER (PARTITION BY patient_pseudonym ORDER BY processed_at DESC) AS rank FROM request " +
|
||||
"WHERE type = 'MTB_FILE' AND status NOT IN ('DUPLICATION') " +
|
||||
") rank WHERE rank = 1 GROUP BY status ORDER BY status, count DESC;"
|
||||
)
|
||||
fun findPatientUniqueStates(): List<CountedState>
|
||||
|
||||
@Query("SELECT count(*) AS count, status FROM request WHERE type = 'DELETE' GROUP BY status ORDER BY status, count DESC;")
|
||||
fun countDeleteStates(): List<CountedState>
|
||||
|
||||
@Query("SELECT count(*) AS count, status FROM (" +
|
||||
"SELECT status, rank() OVER (PARTITION BY patient_pseudonym ORDER BY processed_at DESC) AS rank FROM request " +
|
||||
"WHERE type = 'DELETE'" +
|
||||
") rank WHERE rank = 1 GROUP BY status ORDER BY status, count DESC;")
|
||||
@Query(
|
||||
"SELECT count(*) AS count, status FROM (" +
|
||||
"SELECT status, rank() OVER (PARTITION BY patient_pseudonym ORDER BY processed_at DESC) AS rank FROM request " +
|
||||
"WHERE type = 'DELETE'" +
|
||||
") rank WHERE rank = 1 GROUP BY status ORDER BY status, count DESC;"
|
||||
)
|
||||
fun findPatientUniqueDeleteStates(): List<CountedState>
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -137,15 +137,7 @@ class ConsentProcessor(
|
||||
}
|
||||
|
||||
val provisionComponent: ProvisionComponent = provisions.first()
|
||||
|
||||
var provisionCode: String? = null
|
||||
if (provisionComponent.code != null && provisionComponent.code.isNotEmpty()) {
|
||||
val codableConcept: CodeableConcept = provisionComponent.code.first()
|
||||
if (codableConcept.coding != null && codableConcept.coding.isNotEmpty()) {
|
||||
provisionCode = codableConcept.coding.first().code
|
||||
}
|
||||
}
|
||||
|
||||
val provisionCode = getProvisionCode(provisionComponent)
|
||||
if (provisionCode != null) {
|
||||
try {
|
||||
val modelProjectConsentPurpose =
|
||||
@@ -177,6 +169,17 @@ class ConsentProcessor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun getProvisionCode(provisionComponent: ProvisionComponent): String? {
|
||||
var provisionCode: String? = null
|
||||
if (provisionComponent.code != null && provisionComponent.code.isNotEmpty()) {
|
||||
val codableConcept: CodeableConcept = provisionComponent.code.first()
|
||||
if (codableConcept.coding != null && codableConcept.coding.isNotEmpty()) {
|
||||
provisionCode = codableConcept.coding.first().code
|
||||
}
|
||||
}
|
||||
return provisionCode
|
||||
}
|
||||
|
||||
private fun setGenomDeSubmissionType(mtbFile: Mtb) {
|
||||
if (appConfigProperties.genomDeTestSubmission) {
|
||||
mtbFile.metadata.type = MvhSubmissionType.TEST
|
||||
@@ -238,9 +241,9 @@ class ConsentProcessor(
|
||||
consent.provision.provision.filter { subProvision ->
|
||||
isRequestDateInRange(requestDate, subProvision.period)
|
||||
// search coding entries of current provision for code and system
|
||||
subProvision.code.map { c -> c.coding }.flatten().firstOrNull { code ->
|
||||
subProvision.code.map { c -> c.coding }.flatten().any { code ->
|
||||
targetCode.equals(code.code) && targetSystem.equals(code.system)
|
||||
} != null
|
||||
}
|
||||
}.map { subProvision ->
|
||||
subProvision
|
||||
}
|
||||
@@ -257,11 +260,11 @@ class ConsentProcessor(
|
||||
researchAllowedPolicySystem: String?,
|
||||
policyRules: Collection<Coding>
|
||||
): Boolean {
|
||||
return policyRules.find { code ->
|
||||
return policyRules.any { code ->
|
||||
researchAllowedPolicySystem.equals(code.getSystem()) && (researchAllowedPolicyOid.equals(
|
||||
code.getCode()
|
||||
))
|
||||
} != null
|
||||
}
|
||||
}
|
||||
|
||||
fun isRequestDateInRange(requestDate: Date?, provPeriod: Period): Boolean {
|
||||
|
@@ -52,7 +52,8 @@
|
||||
<td th:if="${request.status.value.contains('success')}" class="bg-green"><small>[[ ${request.status} ]]</small></td>
|
||||
<td th:if="${request.status.value.contains('warning')}" class="bg-yellow"><small>[[ ${request.status} ]]</small></td>
|
||||
<td th:if="${request.status.value.contains('error')}" class="bg-red"><small>[[ ${request.status} ]]</small></td>
|
||||
<td th:if="${request.status.value == 'unknown'}" class="bg-gray"><small>[[ ${request.status} ]]</small></td>
|
||||
<td th:if="${request.status.value == 'unknown' and not request.isPendingUnknown()}" class="bg-gray"><small>[[ ${request.status} ]]</small></td>
|
||||
<td th:if="${request.status.value == 'unknown' and request.isPendingUnknown()}" class="bg-yellow"><small>⏰ [[ ${request.status} ]] ⏰</small></td>
|
||||
<td th:if="${request.status.value == 'duplication'}" class="bg-gray"><small>[[ ${request.status} ]]</small></td>
|
||||
<td th:if="${request.status.value == 'no-consent'}" class="bg-blue"><small>[[ ${request.status} ]]</small></td>
|
||||
<td th:style="${request.type.value == 'delete'} ? 'color: red;'"><small>[[ ${request.type} ]]</small></td>
|
||||
|
@@ -24,14 +24,15 @@ import java.time.Instant;
|
||||
import java.util.Date;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.fail;
|
||||
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
|
||||
import static org.springframework.test.web.client.response.MockRestResponseCreators.withServerError;
|
||||
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
|
||||
|
||||
@ContextConfiguration(classes = {AppConfiguration.class, ObjectMapper.class})
|
||||
@TestPropertySource(properties = {
|
||||
"app.consent.service=gics",
|
||||
"app.consent.gics.uri=http://localhost:8090/ttp-fhir/fhir/gics"
|
||||
"app.consent.service=gics",
|
||||
"app.consent.gics.uri=http://localhost:8090/ttp-fhir/fhir/gics"
|
||||
})
|
||||
@RestClientTest
|
||||
class GicsConsentServiceTest {
|
||||
@@ -46,8 +47,8 @@ class GicsConsentServiceTest {
|
||||
|
||||
@BeforeEach
|
||||
void setUp(
|
||||
@Autowired AppFhirConfig appFhirConfig,
|
||||
@Autowired GIcsConfigProperties gIcsConfigProperties
|
||||
@Autowired AppFhirConfig appFhirConfig,
|
||||
@Autowired GIcsConfigProperties gIcsConfigProperties
|
||||
) {
|
||||
this.appFhirConfig = appFhirConfig;
|
||||
this.gIcsConfigProperties = gIcsConfigProperties;
|
||||
@@ -56,33 +57,33 @@ class GicsConsentServiceTest {
|
||||
|
||||
this.mockRestServiceServer = MockRestServiceServer.createServer(restTemplate);
|
||||
this.gicsConsentService = new GicsConsentService(
|
||||
this.gIcsConfigProperties,
|
||||
RetryTemplate.builder().maxAttempts(1).build(),
|
||||
restTemplate,
|
||||
this.appFhirConfig
|
||||
this.gIcsConfigProperties,
|
||||
RetryTemplate.builder().maxAttempts(1).build(),
|
||||
restTemplate,
|
||||
this.appFhirConfig
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnTtpBroadConsentStatus() {
|
||||
final Parameters consentedResponse = new Parameters()
|
||||
.addParameter(
|
||||
new ParametersParameterComponent()
|
||||
.setName("consented")
|
||||
.setValue(new BooleanType().setValue(true))
|
||||
);
|
||||
.addParameter(
|
||||
new ParametersParameterComponent()
|
||||
.setName("consented")
|
||||
.setValue(new BooleanType().setValue(true))
|
||||
);
|
||||
|
||||
mockRestServiceServer
|
||||
.expect(
|
||||
requestTo(
|
||||
"http://localhost:8090/ttp-fhir/fhir/gics" + GicsConsentService.IS_CONSENTED_ENDPOINT)
|
||||
)
|
||||
.andRespond(
|
||||
withSuccess(
|
||||
appFhirConfig.fhirContext().newJsonParser().encodeResourceToString(consentedResponse),
|
||||
MediaType.APPLICATION_JSON
|
||||
.expect(
|
||||
requestTo(
|
||||
"http://localhost:8090/ttp-fhir/fhir/gics" + GicsConsentService.IS_CONSENTED_ENDPOINT)
|
||||
)
|
||||
);
|
||||
.andRespond(
|
||||
withSuccess(
|
||||
appFhirConfig.fhirContext().newJsonParser().encodeResourceToString(consentedResponse),
|
||||
MediaType.APPLICATION_JSON
|
||||
)
|
||||
);
|
||||
|
||||
var consentStatus = gicsConsentService.getTtpBroadConsentStatus("123456");
|
||||
assertThat(consentStatus).isEqualTo(TtpConsentStatus.BROAD_CONSENT_GIVEN);
|
||||
@@ -91,22 +92,22 @@ class GicsConsentServiceTest {
|
||||
@Test
|
||||
void shouldReturnRevokedConsent() {
|
||||
final Parameters revokedResponse = new Parameters()
|
||||
.addParameter(
|
||||
new ParametersParameterComponent()
|
||||
.setName("consented")
|
||||
.setValue(new BooleanType().setValue(false))
|
||||
);
|
||||
.addParameter(
|
||||
new ParametersParameterComponent()
|
||||
.setName("consented")
|
||||
.setValue(new BooleanType().setValue(false))
|
||||
);
|
||||
|
||||
mockRestServiceServer
|
||||
.expect(
|
||||
requestTo(
|
||||
"http://localhost:8090/ttp-fhir/fhir/gics" + GicsConsentService.IS_CONSENTED_ENDPOINT)
|
||||
)
|
||||
.andRespond(
|
||||
withSuccess(
|
||||
appFhirConfig.fhirContext().newJsonParser().encodeResourceToString(revokedResponse),
|
||||
MediaType.APPLICATION_JSON)
|
||||
);
|
||||
.expect(
|
||||
requestTo(
|
||||
"http://localhost:8090/ttp-fhir/fhir/gics" + GicsConsentService.IS_CONSENTED_ENDPOINT)
|
||||
)
|
||||
.andRespond(
|
||||
withSuccess(
|
||||
appFhirConfig.fhirContext().newJsonParser().encodeResourceToString(revokedResponse),
|
||||
MediaType.APPLICATION_JSON)
|
||||
);
|
||||
|
||||
var consentStatus = gicsConsentService.getTtpBroadConsentStatus("123456");
|
||||
assertThat(consentStatus).isEqualTo(TtpConsentStatus.BROAD_CONSENT_MISSING_OR_REJECTED);
|
||||
@@ -116,23 +117,23 @@ class GicsConsentServiceTest {
|
||||
@Test
|
||||
void shouldReturnInvalidParameterResponse() {
|
||||
final OperationOutcome responseWithErrorOutcome = new OperationOutcome()
|
||||
.addIssue(
|
||||
new OperationOutcomeIssueComponent()
|
||||
.setSeverity(IssueSeverity.ERROR)
|
||||
.setCode(IssueType.PROCESSING)
|
||||
.setDiagnostics("Invalid policy parameter...")
|
||||
);
|
||||
.addIssue(
|
||||
new OperationOutcomeIssueComponent()
|
||||
.setSeverity(IssueSeverity.ERROR)
|
||||
.setCode(IssueType.PROCESSING)
|
||||
.setDiagnostics("Invalid policy parameter...")
|
||||
);
|
||||
|
||||
mockRestServiceServer
|
||||
.expect(
|
||||
requestTo(GICS_BASE_URI + GicsConsentService.IS_CONSENTED_ENDPOINT)
|
||||
)
|
||||
.andRespond(
|
||||
withSuccess(
|
||||
appFhirConfig.fhirContext().newJsonParser().encodeResourceToString(responseWithErrorOutcome),
|
||||
MediaType.APPLICATION_JSON
|
||||
.expect(
|
||||
requestTo(GICS_BASE_URI + GicsConsentService.IS_CONSENTED_ENDPOINT)
|
||||
)
|
||||
);
|
||||
.andRespond(
|
||||
withSuccess(
|
||||
appFhirConfig.fhirContext().newJsonParser().encodeResourceToString(responseWithErrorOutcome),
|
||||
MediaType.APPLICATION_JSON
|
||||
)
|
||||
);
|
||||
|
||||
var consentStatus = gicsConsentService.getTtpBroadConsentStatus("123456");
|
||||
assertThat(consentStatus).isEqualTo(TtpConsentStatus.FAILED_TO_ASK);
|
||||
@@ -141,12 +142,12 @@ class GicsConsentServiceTest {
|
||||
@Test
|
||||
void shouldReturnRequestError() {
|
||||
mockRestServiceServer
|
||||
.expect(
|
||||
requestTo(GICS_BASE_URI + GicsConsentService.IS_CONSENTED_ENDPOINT)
|
||||
)
|
||||
.andRespond(
|
||||
withServerError()
|
||||
);
|
||||
.expect(
|
||||
requestTo(GICS_BASE_URI + GicsConsentService.IS_CONSENTED_ENDPOINT)
|
||||
)
|
||||
.andRespond(
|
||||
withServerError()
|
||||
);
|
||||
|
||||
var consentStatus = gicsConsentService.getTtpBroadConsentStatus("123456");
|
||||
assertThat(consentStatus).isEqualTo(TtpConsentStatus.FAILED_TO_ASK);
|
||||
@@ -156,26 +157,29 @@ class GicsConsentServiceTest {
|
||||
void buildRequestParameterCurrentPolicyStatesForPersonTest() {
|
||||
String pid = "12345678";
|
||||
var result = gicsConsentService
|
||||
.buildRequestParameterCurrentPolicyStatesForPerson(
|
||||
pid,
|
||||
Date.from(Instant.now()),
|
||||
ConsentDomain.MODELLVORHABEN_64E
|
||||
);
|
||||
.buildRequestParameterCurrentPolicyStatesForPerson(
|
||||
pid,
|
||||
Date.from(Instant.now()),
|
||||
ConsentDomain.MODELLVORHABEN_64E
|
||||
);
|
||||
|
||||
assertThat(result.getParameter())
|
||||
.as("should contain 3 parameter resources")
|
||||
.hasSize(3);
|
||||
.as("should contain 3 parameter resources")
|
||||
.hasSize(3);
|
||||
|
||||
assertThat(((StringType) result.getParameter("domain").getValue()).getValue())
|
||||
.isEqualTo(
|
||||
gIcsConfigProperties.getGenomDeConsentDomainName()
|
||||
);
|
||||
.isEqualTo(
|
||||
gIcsConfigProperties.getGenomDeConsentDomainName()
|
||||
);
|
||||
|
||||
assertThat(((Identifier) result.getParameter("personIdentifier").getValue()).getValue())
|
||||
.isEqualTo(
|
||||
pid
|
||||
);
|
||||
.isEqualTo(
|
||||
pid
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void convertGicsResultToMiiBroadConsent() {
|
||||
fail("todo: implement Test gicsConsentService.convertGicsResultToMiiBroadConsent");
|
||||
}
|
||||
}
|
||||
|
@@ -80,7 +80,7 @@ class ConsentProcessorTest {
|
||||
val checkResult = consentProcessor.consentGatedCheckAndTryEmbedding(inputMtb)
|
||||
|
||||
assertThat(checkResult).isTrue
|
||||
assertThat(inputMtb.metadata.researchConsents).hasSize(26)
|
||||
assertThat(inputMtb.metadata.researchConsents).isNotEmpty
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
Reference in New Issue
Block a user