Skip to content

Commit

Permalink
Check Availability of gICS and gPAS During Startup
Browse files Browse the repository at this point in the history
  • Loading branch information
trobanga committed Feb 25, 2025
1 parent 1b3a234 commit 94b6b56
Show file tree
Hide file tree
Showing 9 changed files with 103 additions and 29 deletions.
2 changes: 1 addition & 1 deletion .github/test/tc-agent/compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ services:
depends_on:
keystore:
condition: service_healthy
gics: # CI_ONLY
gics: # CI_ONLY
condition: service_healthy # CI_ONLY
gpas: # CI_ONLY
condition: service_healthy # CI_ONLY
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package care.smith.fts.tca.consent;

import static care.smith.fts.tca.consent.GicsFhirUtil.filterOuterBundle;
import static care.smith.fts.util.FhirClientUtils.fetchCapabilityStatement;
import static care.smith.fts.util.MediaTypes.APPLICATION_FHIR_JSON;
import static care.smith.fts.util.RetryStrategies.defaultRetryStrategy;
import static org.springframework.http.MediaType.APPLICATION_JSON;
Expand All @@ -10,18 +11,20 @@
import care.smith.fts.util.tca.ConsentFetchRequest;
import care.smith.fts.util.tca.ConsentRequest;
import io.micrometer.core.instrument.MeterRegistry;
import java.net.ConnectException;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.hl7.fhir.r4.model.*;
import org.springframework.http.HttpStatus;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.Exceptions;
import reactor.core.publisher.Mono;

/** This class provides functionalities for handling FHIR consents using an HTTP client. */
@Slf4j
public class FhirConsentedPatientsProvider implements ConsentedPatientsProvider {
public class GicsFhirConsentedPatientsProvider implements ConsentedPatientsProvider {
private final WebClient gicsClient;
private final MeterRegistry meterRegistry;

Expand All @@ -30,7 +33,7 @@ public class FhirConsentedPatientsProvider implements ConsentedPatientsProvider
*
* @param gicsClient the WebClient used for HTTP requests
*/
public FhirConsentedPatientsProvider(WebClient gicsClient, MeterRegistry meterRegistry) {
public GicsFhirConsentedPatientsProvider(WebClient gicsClient, MeterRegistry meterRegistry) {
this.gicsClient = gicsClient;
this.meterRegistry = meterRegistry;
}
Expand Down Expand Up @@ -76,8 +79,7 @@ private <C extends ConsentRequest> Mono<Bundle> doFetch(
.headers(h -> h.setContentType(APPLICATION_FHIR_JSON))
.headers(h -> h.setAccept(List.of(APPLICATION_FHIR_JSON, APPLICATION_JSON)))
.retrieve()
.onStatus(
r -> r.equals(HttpStatus.NOT_FOUND), FhirConsentedPatientsProvider::handleGicsNotFound)
.onStatus(r -> r.equals(HttpStatus.NOT_FOUND), this::handleGicsNotFound)
.bodyToMono(Bundle.class)
.doOnNext(b -> log.trace("body(n: {})", b.getEntry().size()))
.retryWhen(defaultRetryStrategy(meterRegistry, helper.requestName()))
Expand All @@ -86,10 +88,31 @@ private <C extends ConsentRequest> Mono<Bundle> doFetch(
.map(bundle -> helper.processResponse(bundle, req, requestUrl, paging));
}

private static Mono<Throwable> handleGicsNotFound(ClientResponse r) {
private Mono<Throwable> handleGicsNotFound(ClientResponse r) {
log.trace("response headers: {}", r.headers().asHttpHeaders());
return r.bodyToMono(OperationOutcome.class)
.doOnNext(re -> log.info("{}", re))
.onErrorResume(
serializationError ->
fetchCapabilityStatement(gicsClient)
.flatMap(capabilityStatement -> Mono.just(new OperationOutcome()))
.onErrorResume(

Check warning on line 98 in trust-center-agent/src/main/java/care/smith/fts/tca/consent/GicsFhirConsentedPatientsProvider.java

View check run for this annotation

Codecov / codecov/patch

trust-center-agent/src/main/java/care/smith/fts/tca/consent/GicsFhirConsentedPatientsProvider.java#L96-L98

Added lines #L96 - L98 were not covered by tests
e -> {
OperationOutcome outcome = new OperationOutcome();

Check warning on line 100 in trust-center-agent/src/main/java/care/smith/fts/tca/consent/GicsFhirConsentedPatientsProvider.java

View check run for this annotation

Codecov / codecov/patch

trust-center-agent/src/main/java/care/smith/fts/tca/consent/GicsFhirConsentedPatientsProvider.java#L100

Added line #L100 was not covered by tests
if (Exceptions.unwrap(e) instanceof ConnectException) {
log.error("Unable to connect to gICS", e);
outcome
.addIssue()
.setSeverity(OperationOutcome.IssueSeverity.ERROR)
.setDiagnostics("No connection to gICS");

Check warning on line 106 in trust-center-agent/src/main/java/care/smith/fts/tca/consent/GicsFhirConsentedPatientsProvider.java

View check run for this annotation

Codecov / codecov/patch

trust-center-agent/src/main/java/care/smith/fts/tca/consent/GicsFhirConsentedPatientsProvider.java#L102-L106

Added lines #L102 - L106 were not covered by tests
} else {
log.error("Unexpected error while connecting to gICS", e);
outcome
.addIssue()
.setSeverity(OperationOutcome.IssueSeverity.ERROR)
.setDiagnostics("Unexpected error while connecting to gICS");

Check warning on line 112 in trust-center-agent/src/main/java/care/smith/fts/tca/consent/GicsFhirConsentedPatientsProvider.java

View check run for this annotation

Codecov / codecov/patch

trust-center-agent/src/main/java/care/smith/fts/tca/consent/GicsFhirConsentedPatientsProvider.java#L108-L112

Added lines #L108 - L112 were not covered by tests
}
return Mono.just(outcome);

Check warning on line 114 in trust-center-agent/src/main/java/care/smith/fts/tca/consent/GicsFhirConsentedPatientsProvider.java

View check run for this annotation

Codecov / codecov/patch

trust-center-agent/src/main/java/care/smith/fts/tca/consent/GicsFhirConsentedPatientsProvider.java#L114

Added line #L114 was not covered by tests
}))
.flatMap(
b -> {
log.info("issue: {}", b.getIssueFirstRep());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
package care.smith.fts.tca.consent.configuration;

import care.smith.fts.tca.consent.FhirConsentedPatientsProvider;
import static care.smith.fts.util.FhirClientUtils.fetchCapabilityStatement;

import care.smith.fts.tca.consent.GicsFhirConsentedPatientsProvider;
import care.smith.fts.util.FhirClientUtils;
import care.smith.fts.util.HttpClientConfig;
import care.smith.fts.util.WebClientFactory;
import care.smith.fts.util.auth.HttpClientAuth;
import io.micrometer.core.instrument.MeterRegistry;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.client.WebClient;

@Slf4j
@Configuration
@ConfigurationProperties(prefix = "consent.gics.fhir")
@Data
Expand All @@ -31,11 +38,22 @@ public WebClient gicsClient(WebClientFactory clientFactory) {
return clientFactory.create(config);
}

@Bean("gicsApplicationRunner")
ApplicationRunner runner(@Qualifier("gicsFhirHttpClient") WebClient gicsClient) {
return args -> {
fetchCapabilityStatement(gicsClient)
.doOnNext(i -> log.info("gCIS available"))
.doOnError(e -> log.warn("No connection to gCIS", e))
.onErrorComplete()
.block();
};
}

@Bean
FhirConsentedPatientsProvider fhirConsentedPatientsProvider(
GicsFhirConsentedPatientsProvider fhirConsentedPatientsProvider(
WebClientFactory clientFactory, MeterRegistry meterRegistry) {
var config = new HttpClientConfig(baseUrl, auth);
var client = clientFactory.create(config);
return new FhirConsentedPatientsProvider(client, meterRegistry);
return new GicsFhirConsentedPatientsProvider(client, meterRegistry);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package care.smith.fts.tca.deidentification.configuration;

import static care.smith.fts.util.FhirClientUtils.fetchCapabilityStatement;

import care.smith.fts.util.FhirClientUtils;
import care.smith.fts.util.HttpClientConfig;
import care.smith.fts.util.WebClientFactory;
import care.smith.fts.util.auth.HttpClientAuth;
Expand All @@ -8,11 +11,15 @@
import java.security.SecureRandom;
import java.util.random.RandomGenerator;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.client.WebClient;

@Slf4j
@Configuration
@ConfigurationProperties(prefix = "de-identification.gpas.fhir")
@Data
Expand All @@ -30,6 +37,17 @@ public WebClient gpasClient(WebClientFactory clientFactory) {
return clientFactory.create(new HttpClientConfig(baseUrl, auth));
}

@Bean("gpasApplicationRunner")
ApplicationRunner runner(@Qualifier("gpasFhirHttpClient") WebClient gpasClient) {
return args -> {
fetchCapabilityStatement(gpasClient)
.doOnNext(i -> log.info("gPAS available"))
.doOnError(e -> log.warn("No connection to gPAS", e))
.onErrorComplete()
.block();
};
}

@Bean
public RandomGenerator secureRandom() {
return new SecureRandom();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
@SpringBootTest
@WireMockTest
@Import(TestWebClientFactory.class)
class FhirConsentedPatientsProviderFetchAllIT {
class GicsFhirConsentedPatientsProviderFetchAllIT {

@Autowired WebClient.Builder httpClientBuilder;
@Autowired MeterRegistry meterRegistry;
Expand Down Expand Up @@ -98,7 +98,7 @@ void tearDown() {
void paging() {
int totalEntries = 2 * defaultPageSize;
var fhirConsentProvider =
new FhirConsentedPatientsProvider(
new GicsFhirConsentedPatientsProvider(
httpClientBuilder.baseUrl(address).build(), meterRegistry);

Bundle bundle =
Expand Down Expand Up @@ -155,7 +155,7 @@ void noNextLinkOnLastPage() {
int pageSize = 1;

var fhirConsentProvider =
new FhirConsentedPatientsProvider(
new GicsFhirConsentedPatientsProvider(
httpClientBuilder.baseUrl(address).build(), meterRegistry);

Bundle bundle =
Expand Down Expand Up @@ -188,7 +188,7 @@ void noConsents() {
int pageSize = 1;

var fhirConsentProvider =
new FhirConsentedPatientsProvider(
new GicsFhirConsentedPatientsProvider(
httpClientBuilder.baseUrl(address).build(), meterRegistry);
Bundle bundle =
Stream.generate(gicsConsentGenerator::generateString)
Expand Down Expand Up @@ -223,7 +223,7 @@ void unknownDomainCausesGicsNotFound() {
int pageSize = 2;

var fhirConsentProvider =
new FhirConsentedPatientsProvider(
new GicsFhirConsentedPatientsProvider(
httpClientBuilder.baseUrl(address).build(), meterRegistry);

var operationOutcome = new OperationOutcome();
Expand Down Expand Up @@ -252,7 +252,7 @@ void somethingElseCausesGicsNotFound() {
int pageSize = 2;

var fhirConsentProvider =
new FhirConsentedPatientsProvider(
new GicsFhirConsentedPatientsProvider(
httpClientBuilder.baseUrl(address).build(), meterRegistry);

var operationOutcome = new OperationOutcome();
Expand Down Expand Up @@ -281,7 +281,7 @@ void diagnosticsIsNullInHandleGicsNotFound() {
int pageSize = 2;

var fhirConsentProvider =
new FhirConsentedPatientsProvider(
new GicsFhirConsentedPatientsProvider(
httpClientBuilder.baseUrl(address).build(), meterRegistry);

var operationOutcome = new OperationOutcome();
Expand Down Expand Up @@ -311,7 +311,7 @@ void emptyPoliciesYieldEmptyBundle() {

var consentRequest = new ConsentFetchAllRequest("MII", Set.of(), POLICY_SYSTEM);
var fhirConsentProvider =
new FhirConsentedPatientsProvider(
new GicsFhirConsentedPatientsProvider(
httpClientBuilder.baseUrl(address).build(), meterRegistry);
Bundle bundle =
Stream.generate(gicsConsentGenerator::generateString)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
@SpringBootTest
@WireMockTest
@Import(TestWebClientFactory.class)
class FhirConsentedPatientsProviderFetchIT {
class GicsFhirConsentedPatientsProviderFetchIT {

@Autowired WebClient.Builder httpClientBuilder;
@Autowired MeterRegistry meterRegistry;
Expand All @@ -56,7 +56,7 @@ class FhirConsentedPatientsProviderFetchIT {
"https://ths-greifswald.de/fhir/CodeSystem/gics/Policy";
private static final String PATIENT_IDENTIFIER_SYSTEM =
"https://ths-greifswald.de/fhir/gics/identifiers/Pseudonym";
private FhirConsentedPatientsProvider fhirConsentProvider;
private GicsFhirConsentedPatientsProvider fhirConsentProvider;

private static final Set<String> POLICIES =
Set.of(
Expand Down Expand Up @@ -106,7 +106,7 @@ void paging() {
int totalEntries = 2 * pageSize;

fhirConsentProvider =
new FhirConsentedPatientsProvider(
new GicsFhirConsentedPatientsProvider(
httpClientBuilder.baseUrl(address).build(), meterRegistry);

var bundle1 =
Expand Down Expand Up @@ -172,7 +172,7 @@ void noConsents() {
int pageSize = 2;

fhirConsentProvider =
new FhirConsentedPatientsProvider(
new GicsFhirConsentedPatientsProvider(
httpClientBuilder.baseUrl(address).build(), meterRegistry);
Bundle bundle =
Stream.generate(gicsConsentGenerator::generateString)
Expand Down Expand Up @@ -207,7 +207,7 @@ void unknownDomainCausesGicsNotFound() {
int pageSize = 2;

fhirConsentProvider =
new FhirConsentedPatientsProvider(
new GicsFhirConsentedPatientsProvider(
httpClientBuilder.baseUrl(address).build(), meterRegistry);

var operationOutcome = new OperationOutcome();
Expand All @@ -233,7 +233,7 @@ void somethingElseCausesGicsNotFound() {
int pageSize = 2;

fhirConsentProvider =
new FhirConsentedPatientsProvider(
new GicsFhirConsentedPatientsProvider(
httpClientBuilder.baseUrl(address).build(), meterRegistry);

var operationOutcome = new OperationOutcome();
Expand All @@ -259,7 +259,7 @@ void diagnosticsIsNullInHandleGicsNotFound() {
int pageSize = 2;

fhirConsentProvider =
new FhirConsentedPatientsProvider(
new GicsFhirConsentedPatientsProvider(
httpClientBuilder.baseUrl(address).build(), meterRegistry);

var operationOutcome = new OperationOutcome();
Expand Down Expand Up @@ -289,7 +289,7 @@ void emptyPoliciesYieldEmptyBundle() {
PATIENT_IDENTIFIER_SYSTEM,
List.of("id1", "id2", "id3", "id4"));
fhirConsentProvider =
new FhirConsentedPatientsProvider(
new GicsFhirConsentedPatientsProvider(
httpClientBuilder.baseUrl(address).build(), meterRegistry);

create(
Expand All @@ -310,7 +310,7 @@ void emptyPidsYieldEmptyBundle() {
new ConsentFetchRequest(
"MII", POLICIES, POLICY_SYSTEM, PATIENT_IDENTIFIER_SYSTEM, List.of());
fhirConsentProvider =
new FhirConsentedPatientsProvider(
new GicsFhirConsentedPatientsProvider(
httpClientBuilder.baseUrl(address).build(), meterRegistry);
create(
fhirConsentProvider.fetch(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import static org.assertj.core.api.Assertions.assertThat;

import care.smith.fts.tca.consent.FhirConsentedPatientsProvider;
import care.smith.fts.tca.consent.GicsFhirConsentedPatientsProvider;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
Expand All @@ -16,7 +16,7 @@
class GicsFhirConfigurationIT {

@Autowired private GicsFhirConfiguration gicsFhirConfiguration;
@Autowired private FhirConsentedPatientsProvider fhirConsentProvider;
@Autowired private GicsFhirConsentedPatientsProvider fhirConsentProvider;

@MockitoBean
RedissonClient redisClient; // We need to mock the redisClient otherwise the tests won't start
Expand Down
12 changes: 12 additions & 0 deletions util/src/main/java/care/smith/fts/util/FhirClientUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package care.smith.fts.util;

import org.hl7.fhir.r4.model.CapabilityStatement;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

public interface FhirClientUtils {

static Mono<CapabilityStatement> fetchCapabilityStatement(WebClient client) {
return client.get().uri("/metadata").retrieve().bodyToMono(CapabilityStatement.class);
}
}
3 changes: 3 additions & 0 deletions util/src/main/java/care/smith/fts/util/FhirUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
import java.util.stream.Stream;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.CapabilityStatement;
import org.hl7.fhir.r4.model.Resource;
import org.springframework.web.reactive.function.client.WebClient;

public interface FhirUtils {
FhirContext fctx = FhirContext.forR4();
Expand Down Expand Up @@ -76,4 +78,5 @@ private static Bundle toBundle(List<Resource> l) {
l.stream().map(r -> new Bundle.BundleEntryComponent().setResource(r)).toList();
return new Bundle().setTotal(l.size()).setEntry(list);
}

}

0 comments on commit 94b6b56

Please sign in to comment.