Merge pull request #610 from hapifhir/cr-6.9.6-snapshot
6.10.0 release merge with Clinical Reasoning config
This commit is contained in:
10
pom.xml
10
pom.xml
@@ -131,6 +131,16 @@
|
||||
<artifactId>hapi-fhir-jpaserver-mdm</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
</dependency>
|
||||
<!-- This dependency includes the CDS Hooks Server -->
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-server-cds-hooks</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<!-- This dependency includes the OpenAPI Server -->
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
|
||||
@@ -3,7 +3,10 @@ package ca.uhn.fhir.jpa.starter;
|
||||
import ca.uhn.fhir.batch2.jobs.config.Batch2JobsConfig;
|
||||
import ca.uhn.fhir.jpa.batch2.JpaBatch2Config;
|
||||
import ca.uhn.fhir.jpa.starter.annotations.OnEitherVersion;
|
||||
import ca.uhn.fhir.jpa.starter.cdshooks.StarterCdsHooksConfig;
|
||||
import ca.uhn.fhir.jpa.starter.common.FhirTesterConfig;
|
||||
import ca.uhn.fhir.jpa.starter.cr.StarterCrDstu3Config;
|
||||
import ca.uhn.fhir.jpa.starter.cr.StarterCrR4Config;
|
||||
import ca.uhn.fhir.jpa.starter.mdm.MdmConfig;
|
||||
import ca.uhn.fhir.jpa.subscription.channel.config.SubscriptionChannelConfig;
|
||||
import ca.uhn.fhir.jpa.subscription.match.config.SubscriptionProcessorConfig;
|
||||
@@ -29,6 +32,9 @@ import org.springframework.web.servlet.DispatcherServlet;
|
||||
@ServletComponentScan(basePackageClasses = {RestfulServer.class})
|
||||
@SpringBootApplication(exclude = {ElasticsearchRestClientAutoConfiguration.class, ThymeleafAutoConfiguration.class})
|
||||
@Import({
|
||||
StarterCrR4Config.class,
|
||||
StarterCrDstu3Config.class,
|
||||
StarterCdsHooksConfig.class,
|
||||
SubscriptionSubmitterConfig.class,
|
||||
SubscriptionProcessorConfig.class,
|
||||
SubscriptionChannelConfig.class,
|
||||
@@ -43,13 +49,12 @@ public class Application extends SpringBootServletInitializer {
|
||||
|
||||
SpringApplication.run(Application.class, args);
|
||||
|
||||
//Server is now accessible at eg. http://localhost:8080/fhir/metadata
|
||||
//UI is now accessible at http://localhost:8080/
|
||||
// Server is now accessible at eg. http://localhost:8080/fhir/metadata
|
||||
// UI is now accessible at http://localhost:8080/
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SpringApplicationBuilder configure(
|
||||
SpringApplicationBuilder builder) {
|
||||
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
|
||||
return builder.sources(Application.class);
|
||||
}
|
||||
|
||||
@@ -71,11 +76,11 @@ public class Application extends SpringBootServletInitializer {
|
||||
@Bean
|
||||
public ServletRegistrationBean overlayRegistrationBean() {
|
||||
|
||||
AnnotationConfigWebApplicationContext annotationConfigWebApplicationContext = new AnnotationConfigWebApplicationContext();
|
||||
AnnotationConfigWebApplicationContext annotationConfigWebApplicationContext =
|
||||
new AnnotationConfigWebApplicationContext();
|
||||
annotationConfigWebApplicationContext.register(FhirTesterConfig.class);
|
||||
|
||||
DispatcherServlet dispatcherServlet = new DispatcherServlet(
|
||||
annotationConfigWebApplicationContext);
|
||||
DispatcherServlet dispatcherServlet = new DispatcherServlet(annotationConfigWebApplicationContext);
|
||||
dispatcherServlet.setContextClass(AnnotationConfigWebApplicationContext.class);
|
||||
dispatcherServlet.setContextConfigLocation(FhirTesterConfig.class.getName());
|
||||
|
||||
@@ -84,6 +89,10 @@ public class Application extends SpringBootServletInitializer {
|
||||
registrationBean.addUrlMappings("/*");
|
||||
registrationBean.setLoadOnStartup(1);
|
||||
return registrationBean;
|
||||
|
||||
}
|
||||
|
||||
// @Bean
|
||||
// IRepositoryFactory repositoryFactory(DaoRegistry theDaoRegistry, RestfulServer theRestfulServer) {
|
||||
// return rd -> new HapiFhirRepository(theDaoRegistry, rd, theRestfulServer);
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -19,16 +19,13 @@ public class ExtraStaticFilesConfigurer implements WebMvcConfigurer {
|
||||
public ExtraStaticFilesConfigurer(AppProperties appProperties) {
|
||||
|
||||
rootContextPath = appProperties.getStaticLocationPrefix();
|
||||
if(rootContextPath.endsWith("/"))
|
||||
if (rootContextPath.endsWith("/"))
|
||||
rootContextPath = rootContextPath.substring(0, rootContextPath.lastIndexOf('/'));
|
||||
|
||||
staticLocation = appProperties.getStaticLocation();
|
||||
if(staticLocation.endsWith("/"))
|
||||
staticLocation = staticLocation.substring(0, staticLocation.lastIndexOf('/'));
|
||||
|
||||
if (staticLocation.endsWith("/")) staticLocation = staticLocation.substring(0, staticLocation.lastIndexOf('/'));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry theRegistry) {
|
||||
theRegistry.addResourceHandler(rootContextPath + "/**").addResourceLocations(staticLocation);
|
||||
@@ -39,13 +36,15 @@ public class ExtraStaticFilesConfigurer implements WebMvcConfigurer {
|
||||
String path = URI.create(staticLocation).getPath();
|
||||
String lastSegment = path.substring(path.lastIndexOf('/') + 1);
|
||||
|
||||
registry.addViewController(rootContextPath).setViewName("redirect:" + rootContextPath + "/" + lastSegment + "/index.html");
|
||||
registry.addViewController(rootContextPath)
|
||||
.setViewName("redirect:" + rootContextPath + "/" + lastSegment + "/index.html");
|
||||
|
||||
registry.addViewController(rootContextPath + "/*").setViewName("redirect:" + rootContextPath + "/" + lastSegment + "/index.html");
|
||||
registry.addViewController(rootContextPath + "/*")
|
||||
.setViewName("redirect:" + rootContextPath + "/" + lastSegment + "/index.html");
|
||||
|
||||
registry.addViewController(rootContextPath + "/" + lastSegment + "/").setViewName("redirect:" + rootContextPath + "/" + lastSegment + "/index.html");
|
||||
registry.addViewController(rootContextPath + "/" + lastSegment + "/")
|
||||
.setViewName("redirect:" + rootContextPath + "/" + lastSegment + "/index.html");
|
||||
|
||||
registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,7 +10,9 @@ public class OnCorsPresent implements Condition {
|
||||
@Override
|
||||
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata) {
|
||||
|
||||
AppProperties config = Binder.get(conditionContext.getEnvironment()).bind("hapi.fhir", AppProperties.class).orElse(null);
|
||||
AppProperties config = Binder.get(conditionContext.getEnvironment())
|
||||
.bind("hapi.fhir", AppProperties.class)
|
||||
.orElse(null);
|
||||
if (config == null) return false;
|
||||
if (config.getCors() == null) return false;
|
||||
return true;
|
||||
|
||||
@@ -8,12 +8,11 @@ import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
public class OnDSTU2Condition implements Condition {
|
||||
@Override
|
||||
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata) {
|
||||
FhirVersionEnum version = FhirVersionEnum.forVersionString(conditionContext.
|
||||
getEnvironment()
|
||||
FhirVersionEnum version = FhirVersionEnum.forVersionString(conditionContext
|
||||
.getEnvironment()
|
||||
.getProperty("hapi.fhir.fhir_version")
|
||||
.toUpperCase());
|
||||
|
||||
return version == FhirVersionEnum.DSTU2;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,12 +8,11 @@ import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
public class OnDSTU3Condition implements Condition {
|
||||
@Override
|
||||
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata) {
|
||||
FhirVersionEnum version = FhirVersionEnum.forVersionString(conditionContext.
|
||||
getEnvironment()
|
||||
FhirVersionEnum version = FhirVersionEnum.forVersionString(conditionContext
|
||||
.getEnvironment()
|
||||
.getProperty("hapi.fhir.fhir_version")
|
||||
.toUpperCase());
|
||||
|
||||
return version == FhirVersionEnum.DSTU3;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,23 +17,17 @@ public class OnEitherVersion extends AnyNestedCondition {
|
||||
}
|
||||
|
||||
@Conditional(OnDSTU2Condition.class)
|
||||
static class OnDSTU2 {
|
||||
}
|
||||
static class OnDSTU2 {}
|
||||
|
||||
@Conditional(OnDSTU3Condition.class)
|
||||
static class OnDSTU3 {
|
||||
}
|
||||
static class OnDSTU3 {}
|
||||
|
||||
@Conditional(OnR4Condition.class)
|
||||
static class OnR4 {
|
||||
}
|
||||
static class OnR4 {}
|
||||
|
||||
@Conditional(OnR4BCondition.class)
|
||||
static class OnR4B {
|
||||
}
|
||||
static class OnR4B {}
|
||||
|
||||
@Conditional(OnR5Condition.class)
|
||||
static class OnR5 {
|
||||
}
|
||||
|
||||
static class OnR5 {}
|
||||
}
|
||||
@@ -10,7 +10,9 @@ public class OnImplementationGuidesPresent implements Condition {
|
||||
@Override
|
||||
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata) {
|
||||
|
||||
AppProperties config = Binder.get(conditionContext.getEnvironment()).bind("hapi.fhir", AppProperties.class).orElse(null);
|
||||
AppProperties config = Binder.get(conditionContext.getEnvironment())
|
||||
.bind("hapi.fhir", AppProperties.class)
|
||||
.orElse(null);
|
||||
if (config == null) return false;
|
||||
if (config.getImplementationGuides() == null) return false;
|
||||
return !config.getImplementationGuides().isEmpty();
|
||||
|
||||
@@ -8,8 +8,8 @@ import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
public class OnR4BCondition implements Condition {
|
||||
@Override
|
||||
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata) {
|
||||
String version = conditionContext.
|
||||
getEnvironment()
|
||||
String version = conditionContext
|
||||
.getEnvironment()
|
||||
.getProperty("hapi.fhir.fhir_version")
|
||||
.toUpperCase();
|
||||
|
||||
|
||||
@@ -8,12 +8,11 @@ import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
public class OnR4Condition implements Condition {
|
||||
@Override
|
||||
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata) {
|
||||
FhirVersionEnum version = FhirVersionEnum.forVersionString(conditionContext.
|
||||
getEnvironment()
|
||||
FhirVersionEnum version = FhirVersionEnum.forVersionString(conditionContext
|
||||
.getEnvironment()
|
||||
.getProperty("hapi.fhir.fhir_version")
|
||||
.toUpperCase());
|
||||
|
||||
return version == FhirVersionEnum.R4;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,12 +8,11 @@ import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
public class OnR5Condition implements Condition {
|
||||
@Override
|
||||
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata) {
|
||||
FhirVersionEnum version = FhirVersionEnum.forVersionString(conditionContext.
|
||||
getEnvironment()
|
||||
FhirVersionEnum version = FhirVersionEnum.forVersionString(conditionContext
|
||||
.getEnvironment()
|
||||
.getProperty("hapi.fhir.fhir_version")
|
||||
.toUpperCase());
|
||||
|
||||
return version == FhirVersionEnum.R5;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package ca.uhn.fhir.jpa.starter.cdshooks;
|
||||
|
||||
import org.springframework.context.annotation.Condition;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
|
||||
public class CdsHooksConfigCondition implements Condition {
|
||||
|
||||
@Override
|
||||
public boolean matches(ConditionContext theConditionContext, AnnotatedTypeMetadata theAnnotatedTypeMetadata) {
|
||||
String property = theConditionContext.getEnvironment().getProperty("hapi.fhir.cdshooks.enabled");
|
||||
return Boolean.parseBoolean(property);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package ca.uhn.fhir.jpa.starter.cdshooks;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
@ConfigurationProperties(prefix = "hapi.fhir.cdshooks")
|
||||
public class CdsHooksProperties {
|
||||
|
||||
private boolean enabled;
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
private String clientIdHeaderName;
|
||||
|
||||
public String getClientIdHeaderName() {
|
||||
return clientIdHeaderName;
|
||||
}
|
||||
|
||||
public void setClientIdHeaderName(String clientIdHeaderName) {
|
||||
this.clientIdHeaderName = clientIdHeaderName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package ca.uhn.fhir.jpa.starter.cdshooks;
|
||||
|
||||
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestJson;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
|
||||
@JsonIgnoreProperties({"extension"})
|
||||
public class CdsHooksRequest extends CdsServiceRequestJson {}
|
||||
@@ -0,0 +1,129 @@
|
||||
package ca.uhn.fhir.jpa.starter.cdshooks;
|
||||
|
||||
import ca.uhn.fhir.jpa.starter.AppProperties;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
import ca.uhn.hapi.fhir.cdshooks.api.ICdsServiceRegistry;
|
||||
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestJson;
|
||||
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseJson;
|
||||
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServicesJson;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonParser;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Configurable;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import static ca.uhn.hapi.fhir.cdshooks.config.CdsHooksConfig.CDS_HOOKS_OBJECT_MAPPER_FACTORY;
|
||||
|
||||
@Configurable
|
||||
public class CdsHooksServlet extends HttpServlet {
|
||||
private static final Logger logger = LoggerFactory.getLogger(CdsHooksServlet.class);
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Autowired
|
||||
private AppProperties appProperties;
|
||||
|
||||
@Autowired
|
||||
private ProviderConfiguration providerConfiguration;
|
||||
|
||||
@Autowired
|
||||
ICdsServiceRegistry cdsServiceRegistry;
|
||||
|
||||
@Autowired
|
||||
RestfulServer restfulServer;
|
||||
|
||||
@Autowired
|
||||
@Qualifier(CDS_HOOKS_OBJECT_MAPPER_FACTORY)
|
||||
ObjectMapper objectMapper;
|
||||
|
||||
protected ProviderConfiguration getProviderConfiguration() {
|
||||
return this.providerConfiguration;
|
||||
}
|
||||
|
||||
// CORS Pre-flight
|
||||
@Override
|
||||
protected void doOptions(HttpServletRequest req, HttpServletResponse resp) {
|
||||
ErrorHandling.setAccessControlHeaders(resp, appProperties);
|
||||
resp.setHeader("Content-Type", ContentType.APPLICATION_JSON.getMimeType());
|
||||
resp.setHeader("X-Content-Type-Options", "nosniff");
|
||||
resp.setStatus(HttpServletResponse.SC_OK);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
logger.info(request.getRequestURI());
|
||||
if (!request.getRequestURL().toString().endsWith("/cds-services")
|
||||
&& !request.getRequestURL().toString().endsWith("/cds-services/")) {
|
||||
logger.error(request.getRequestURI());
|
||||
throw new ServletException("This servlet is not configured to handle GET requests.");
|
||||
}
|
||||
ErrorHandling.setAccessControlHeaders(response, appProperties);
|
||||
response.setHeader("Content-Type", ContentType.APPLICATION_JSON.getMimeType());
|
||||
response.getWriter()
|
||||
.println(new GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.create()
|
||||
.toJson(JsonParser.parseString(objectMapper.writeValueAsString(getServices()))));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPost(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
try {
|
||||
if (request.getContentType() == null || !request.getContentType().startsWith("application/json")) {
|
||||
throw new ServletException(String.format(
|
||||
"Invalid content type %s. Please use application/json.", request.getContentType()));
|
||||
}
|
||||
logger.info(request.getRequestURI());
|
||||
String service = request.getPathInfo().replace("/", "");
|
||||
|
||||
String requestJson = request.getReader().lines().collect(Collectors.joining());
|
||||
CdsHooksRequest cdsHooksRequest = objectMapper.readValue(requestJson, CdsHooksRequest.class);
|
||||
logRequestInfo(cdsHooksRequest, requestJson);
|
||||
|
||||
CdsServiceResponseJson serviceResponseJson = cdsServiceRegistry.callService(service, cdsHooksRequest);
|
||||
|
||||
// Using GSON pretty print format as Jackson's is ugly
|
||||
String jsonResponse = new GsonBuilder()
|
||||
.disableHtmlEscaping()
|
||||
.setPrettyPrinting()
|
||||
.create()
|
||||
.toJson(JsonParser.parseString(objectMapper.writeValueAsString(serviceResponseJson)));
|
||||
logger.info(jsonResponse);
|
||||
response.setContentType("text/json;charset=UTF-8");
|
||||
response.getWriter().println(jsonResponse);
|
||||
} catch (BaseServerResponseException e) {
|
||||
ErrorHandling.handleError(response, "ERROR: Exception connecting to remote server.", e, appProperties);
|
||||
logger.error(e.toString());
|
||||
} catch (Exception e) {
|
||||
logger.error(e.toString());
|
||||
throw new ServletException("ERROR: Exception in cds-hooks processing.", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void logRequestInfo(CdsServiceRequestJson request, String jsonRequest) {
|
||||
logger.info(jsonRequest);
|
||||
logger.info("cds-hooks hook instance: {}", request.getHookInstance());
|
||||
logger.info("cds-hooks local server address: {}", appProperties.getServer_address());
|
||||
logger.info("cds-hooks fhir server address: {}", request.getFhirServer());
|
||||
logger.info(
|
||||
"cds-hooks cql_logging_enabled: {}",
|
||||
this.getProviderConfiguration().getCqlLoggingEnabled());
|
||||
}
|
||||
|
||||
private CdsServicesJson getServices() {
|
||||
return cdsServiceRegistry.getCdsServicesJson();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package ca.uhn.fhir.jpa.starter.cdshooks;
|
||||
|
||||
import ca.uhn.fhir.jpa.starter.AppProperties;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.Arrays;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
public class ErrorHandling {
|
||||
|
||||
private ErrorHandling() {}
|
||||
|
||||
public static void handleError(
|
||||
HttpServletResponse response, String message, Exception e, AppProperties myAppProperties)
|
||||
throws IOException {
|
||||
setAccessControlHeaders(response, myAppProperties);
|
||||
response.setStatus(500); // This will be overwritten with the correct status code downstream if needed.
|
||||
response.getWriter().println(message);
|
||||
printMessageAndCause(e, response);
|
||||
if (e instanceof BaseServerResponseException) {
|
||||
handleServerResponseException((BaseServerResponseException) e, response);
|
||||
} else if (e.getCause() instanceof BaseServerResponseException) {
|
||||
handleServerResponseException((BaseServerResponseException) e.getCause(), response);
|
||||
}
|
||||
printStackTrack(e, response);
|
||||
}
|
||||
|
||||
private static void handleServerResponseException(BaseServerResponseException e, HttpServletResponse response)
|
||||
throws IOException {
|
||||
switch (e.getStatusCode()) {
|
||||
case 401:
|
||||
case 403:
|
||||
response.getWriter().println("Precondition Failed. Remote FHIR server returned: " + e.getStatusCode());
|
||||
response.getWriter()
|
||||
.println(
|
||||
"Ensure that the fhirAuthorization token is set or that the remote server allows unauthenticated access.");
|
||||
response.setStatus(412);
|
||||
break;
|
||||
case 404:
|
||||
response.getWriter().println("Precondition Failed. Remote FHIR server returned: " + e.getStatusCode());
|
||||
response.getWriter().println("Ensure the resource exists on the remote server.");
|
||||
response.setStatus(412);
|
||||
break;
|
||||
default:
|
||||
response.getWriter().println("Unhandled Error in Remote FHIR server: " + e.getStatusCode());
|
||||
}
|
||||
}
|
||||
|
||||
private static void printMessageAndCause(Exception e, HttpServletResponse response) throws IOException {
|
||||
if (e.getMessage() != null) {
|
||||
response.getWriter().println(e.getMessage());
|
||||
}
|
||||
|
||||
if (e.getCause() != null && e.getCause().getMessage() != null) {
|
||||
response.getWriter().println(e.getCause().getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private static void printStackTrack(Exception e, HttpServletResponse response) throws IOException {
|
||||
StringWriter sw = new StringWriter();
|
||||
e.printStackTrace(new PrintWriter(sw));
|
||||
String exceptionAsString = sw.toString();
|
||||
response.getWriter().println(exceptionAsString);
|
||||
}
|
||||
|
||||
public static void setAccessControlHeaders(HttpServletResponse resp, AppProperties myAppProperties) {
|
||||
if (myAppProperties.getCors() != null) {
|
||||
if (myAppProperties.getCors().getAllow_Credentials()) {
|
||||
resp.setHeader(
|
||||
"Access-Control-Allow-Origin",
|
||||
myAppProperties.getCors().getAllowed_origin().stream()
|
||||
.findFirst()
|
||||
.get());
|
||||
resp.setHeader(
|
||||
"Access-Control-Allow-Methods",
|
||||
String.join(", ", Arrays.asList("GET", "HEAD", "POST", "OPTIONS")));
|
||||
resp.setHeader(
|
||||
"Access-Control-Allow-Headers",
|
||||
String.join(
|
||||
", ",
|
||||
Arrays.asList(
|
||||
"x-fhir-starter",
|
||||
"Origin",
|
||||
"Accept",
|
||||
"X-Requested-With",
|
||||
"Content-Type",
|
||||
"Authorization",
|
||||
"Cache-Control")));
|
||||
resp.setHeader(
|
||||
"Access-Control-Expose-Headers",
|
||||
String.join(", ", Arrays.asList("Location", "Content-Location")));
|
||||
resp.setHeader("Access-Control-Max-Age", "86400");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class CdsHooksError extends RuntimeException {
|
||||
public CdsHooksError(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package ca.uhn.fhir.jpa.starter.cdshooks;
|
||||
|
||||
import ca.uhn.fhir.jpa.starter.cr.CrProperties;
|
||||
|
||||
public class ProviderConfiguration {
|
||||
|
||||
public static final ProviderConfiguration DEFAULT_PROVIDER_CONFIGURATION =
|
||||
new ProviderConfiguration(false, "client_id");
|
||||
|
||||
private final String clientIdHeaderName;
|
||||
private final boolean cqlLoggingEnabled;
|
||||
|
||||
public ProviderConfiguration(boolean cqlLoggingEnabled, String clientIdHeaderName) {
|
||||
this.cqlLoggingEnabled = cqlLoggingEnabled;
|
||||
this.clientIdHeaderName = clientIdHeaderName;
|
||||
}
|
||||
|
||||
public ProviderConfiguration(CdsHooksProperties cdsProperties, CrProperties crProperties) {
|
||||
this.clientIdHeaderName = cdsProperties.getClientIdHeaderName();
|
||||
this.cqlLoggingEnabled = crProperties.isCqlRuntimeDebugLoggingEnabled();
|
||||
}
|
||||
|
||||
public String getClientIdHeaderName() {
|
||||
return this.clientIdHeaderName;
|
||||
}
|
||||
|
||||
public boolean getCqlLoggingEnabled() {
|
||||
return this.cqlLoggingEnabled;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package ca.uhn.fhir.jpa.starter.cdshooks;
|
||||
|
||||
import ca.uhn.fhir.jpa.starter.cr.CrConfigCondition;
|
||||
import ca.uhn.fhir.jpa.starter.cr.CrProperties;
|
||||
import ca.uhn.hapi.fhir.cdshooks.api.ICdsHooksDaoAuthorizationSvc;
|
||||
import ca.uhn.hapi.fhir.cdshooks.config.CdsHooksConfig;
|
||||
import ca.uhn.hapi.fhir.cdshooks.svc.CdsHooksContextBooter;
|
||||
import ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrSettings;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
||||
import org.springframework.boot.web.servlet.ServletRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
@Configuration
|
||||
@Conditional({CdsHooksConfigCondition.class, CrConfigCondition.class})
|
||||
@Import(CdsHooksConfig.class)
|
||||
public class StarterCdsHooksConfig {
|
||||
@Bean
|
||||
public CdsHooksProperties cdsHooksProperties() {
|
||||
return new CdsHooksProperties();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CdsCrSettings cdsCrSettings(CdsHooksProperties cdsHooksProperties) {
|
||||
CdsCrSettings settings = CdsCrSettings.getDefault();
|
||||
settings.setClientIdHeaderName(cdsHooksProperties.getClientIdHeaderName());
|
||||
return settings;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CdsHooksContextBooter cdsHooksContextBooter() {
|
||||
// ourLog.info("No Spring Context provided. Assuming all CDS Services will be registered dynamically.");
|
||||
return new CdsHooksContextBooter();
|
||||
}
|
||||
|
||||
public static class CdsHooksDaoAuthorizationSvc implements ICdsHooksDaoAuthorizationSvc {
|
||||
@Override
|
||||
public void authorizePreShow(IBaseResource theResource) {}
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ProviderConfiguration providerConfiguration(CdsHooksProperties cdsProperties, CrProperties crProperties) {
|
||||
return new ProviderConfiguration(cdsProperties, crProperties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
ICdsHooksDaoAuthorizationSvc cdsHooksDaoAuthorizationSvc() {
|
||||
return new CdsHooksDaoAuthorizationSvc();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ServletRegistrationBean<CdsHooksServlet> cdsHooksRegistrationBean(AutowireCapableBeanFactory beanFactory) {
|
||||
CdsHooksServlet cdsHooksServlet = new CdsHooksServlet();
|
||||
beanFactory.autowireBean(cdsHooksServlet);
|
||||
|
||||
ServletRegistrationBean<CdsHooksServlet> registrationBean = new ServletRegistrationBean<>();
|
||||
registrationBean.setName("cds-hooks servlet");
|
||||
registrationBean.setServlet(cdsHooksServlet);
|
||||
registrationBean.addUrlMappings("/cds-services/*");
|
||||
registrationBean.setLoadOnStartup(1);
|
||||
return registrationBean;
|
||||
}
|
||||
}
|
||||
@@ -10,18 +10,20 @@ import org.springframework.core.env.ConfigurableEnvironment;
|
||||
@Configuration
|
||||
public class ElasticsearchConfig {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ElasticsearchConfig.class);
|
||||
|
||||
@Bean
|
||||
public ElasticsearchSvcImpl elasticsearchSvc(ConfigurableEnvironment configurableEnvironment) {
|
||||
if (EnvironmentHelper.isElasticsearchEnabled(configurableEnvironment)) {
|
||||
String elasticsearchUrl = EnvironmentHelper.getElasticsearchServerUrl(configurableEnvironment);
|
||||
if (elasticsearchUrl.startsWith("http")) {
|
||||
elasticsearchUrl =elasticsearchUrl.substring(elasticsearchUrl.indexOf("://") + 3);
|
||||
elasticsearchUrl = elasticsearchUrl.substring(elasticsearchUrl.indexOf("://") + 3);
|
||||
}
|
||||
String elasticsearchProtocol = EnvironmentHelper.getElasticsearchServerProtocol(configurableEnvironment);
|
||||
String elasticsearchUsername = EnvironmentHelper.getElasticsearchServerUsername(configurableEnvironment);
|
||||
String elasticsearchPassword = EnvironmentHelper.getElasticsearchServerPassword(configurableEnvironment);
|
||||
ourLog.info("Configuring elasticsearch {} {}", elasticsearchProtocol, elasticsearchUrl);
|
||||
return new ElasticsearchSvcImpl(elasticsearchProtocol, elasticsearchUrl, elasticsearchUsername, elasticsearchPassword);
|
||||
return new ElasticsearchSvcImpl(
|
||||
elasticsearchProtocol, elasticsearchUrl, elasticsearchUsername, elasticsearchPassword);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ import ca.uhn.fhir.rest.server.mail.MailConfig;
|
||||
import ca.uhn.fhir.rest.server.mail.MailSvc;
|
||||
import com.google.common.base.Strings;
|
||||
import org.hl7.fhir.r4.model.Bundle.BundleType;
|
||||
|
||||
import org.springframework.boot.env.YamlPropertySourceLoader;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -24,6 +23,7 @@ import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -35,21 +35,34 @@ public class FhirServerConfigCommon {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirServerConfigCommon.class);
|
||||
|
||||
|
||||
public FhirServerConfigCommon(AppProperties appProperties) {
|
||||
ourLog.info("Server configured to " + (appProperties.getAllow_contains_searches() ? "allow" : "deny") + " contains searches");
|
||||
ourLog.info("Server configured to " + (appProperties.getAllow_multiple_delete() ? "allow" : "deny") + " multiple deletes");
|
||||
ourLog.info("Server configured to " + (appProperties.getAllow_external_references() ? "allow" : "deny") + " external references");
|
||||
ourLog.info("Server configured to " + (appProperties.getDao_scheduling_enabled() ? "enable" : "disable") + " DAO scheduling");
|
||||
ourLog.info("Server configured to " + (appProperties.getDelete_expunge_enabled() ? "enable" : "disable") + " delete expunges");
|
||||
ourLog.info("Server configured to " + (appProperties.getExpunge_enabled() ? "enable" : "disable") + " expunges");
|
||||
ourLog.info("Server configured to " + (appProperties.getAllow_override_default_search_params() ? "allow" : "deny") + " overriding default search params");
|
||||
ourLog.info("Server configured to " + (appProperties.getAuto_create_placeholder_reference_targets() ? "allow" : "disable") + " auto-creating placeholder references");
|
||||
ourLog.info("Server configured to auto-version references at paths {}", appProperties.getAuto_version_reference_at_paths());
|
||||
ourLog.info("Server configured to " + (appProperties.getAllow_contains_searches() ? "allow" : "deny")
|
||||
+ " contains searches");
|
||||
ourLog.info("Server configured to " + (appProperties.getAllow_multiple_delete() ? "allow" : "deny")
|
||||
+ " multiple deletes");
|
||||
ourLog.info("Server configured to " + (appProperties.getAllow_external_references() ? "allow" : "deny")
|
||||
+ " external references");
|
||||
ourLog.info("Server configured to " + (appProperties.getDao_scheduling_enabled() ? "enable" : "disable")
|
||||
+ " DAO scheduling");
|
||||
ourLog.info("Server configured to " + (appProperties.getDelete_expunge_enabled() ? "enable" : "disable")
|
||||
+ " delete expunges");
|
||||
ourLog.info(
|
||||
"Server configured to " + (appProperties.getExpunge_enabled() ? "enable" : "disable") + " expunges");
|
||||
ourLog.info(
|
||||
"Server configured to " + (appProperties.getAllow_override_default_search_params() ? "allow" : "deny")
|
||||
+ " overriding default search params");
|
||||
ourLog.info("Server configured to "
|
||||
+ (appProperties.getAuto_create_placeholder_reference_targets() ? "allow" : "disable")
|
||||
+ " auto-creating placeholder references");
|
||||
ourLog.info(
|
||||
"Server configured to auto-version references at paths {}",
|
||||
appProperties.getAuto_version_reference_at_paths());
|
||||
|
||||
if (appProperties.getSubscription().getEmail() != null) {
|
||||
AppProperties.Subscription.Email email = appProperties.getSubscription().getEmail();
|
||||
ourLog.info("Server is configured to enable email with host '" + email.getHost() + "' and port " + email.getPort());
|
||||
AppProperties.Subscription.Email email =
|
||||
appProperties.getSubscription().getEmail();
|
||||
ourLog.info("Server is configured to enable email with host '" + email.getHost() + "' and port "
|
||||
+ email.getPort());
|
||||
ourLog.info("Server will use '" + email.getFrom() + "' as the from email address");
|
||||
|
||||
if (!Strings.isNullOrEmpty(email.getUsername())) {
|
||||
@@ -81,57 +94,68 @@ public class FhirServerConfigCommon {
|
||||
public JpaStorageSettings jpaStorageSettings(AppProperties appProperties) {
|
||||
JpaStorageSettings jpaStorageSettings = new JpaStorageSettings();
|
||||
|
||||
jpaStorageSettings.setIndexMissingFields(appProperties.getEnable_index_missing_fields() ? StorageSettings.IndexEnabledEnum.ENABLED : StorageSettings.IndexEnabledEnum.DISABLED);
|
||||
jpaStorageSettings.setAutoCreatePlaceholderReferenceTargets(appProperties.getAuto_create_placeholder_reference_targets());
|
||||
jpaStorageSettings.setIndexMissingFields(
|
||||
appProperties.getEnable_index_missing_fields()
|
||||
? StorageSettings.IndexEnabledEnum.ENABLED
|
||||
: StorageSettings.IndexEnabledEnum.DISABLED);
|
||||
jpaStorageSettings.setAutoCreatePlaceholderReferenceTargets(
|
||||
appProperties.getAuto_create_placeholder_reference_targets());
|
||||
jpaStorageSettings.setAutoVersionReferenceAtPaths(appProperties.getAuto_version_reference_at_paths());
|
||||
jpaStorageSettings.setEnforceReferentialIntegrityOnWrite(appProperties.getEnforce_referential_integrity_on_write());
|
||||
jpaStorageSettings.setEnforceReferentialIntegrityOnDelete(appProperties.getEnforce_referential_integrity_on_delete());
|
||||
jpaStorageSettings.setEnforceReferentialIntegrityOnWrite(
|
||||
appProperties.getEnforce_referential_integrity_on_write());
|
||||
jpaStorageSettings.setEnforceReferentialIntegrityOnDelete(
|
||||
appProperties.getEnforce_referential_integrity_on_delete());
|
||||
jpaStorageSettings.setAllowContainsSearches(appProperties.getAllow_contains_searches());
|
||||
jpaStorageSettings.setAllowMultipleDelete(appProperties.getAllow_multiple_delete());
|
||||
jpaStorageSettings.setAllowExternalReferences(appProperties.getAllow_external_references());
|
||||
jpaStorageSettings.setSchedulingDisabled(!appProperties.getDao_scheduling_enabled());
|
||||
jpaStorageSettings.setDeleteExpungeEnabled(appProperties.getDelete_expunge_enabled());
|
||||
jpaStorageSettings.setExpungeEnabled(appProperties.getExpunge_enabled());
|
||||
if(appProperties.getSubscription() != null && appProperties.getSubscription().getEmail() != null)
|
||||
jpaStorageSettings.setEmailFromAddress(appProperties.getSubscription().getEmail().getFrom());
|
||||
if (appProperties.getSubscription() != null
|
||||
&& appProperties.getSubscription().getEmail() != null)
|
||||
jpaStorageSettings.setEmailFromAddress(
|
||||
appProperties.getSubscription().getEmail().getFrom());
|
||||
|
||||
Integer maxFetchSize = appProperties.getMax_page_size();
|
||||
jpaStorageSettings.setFetchSizeDefaultMaximum(maxFetchSize);
|
||||
ourLog.info("Server configured to have a maximum fetch size of " + (maxFetchSize == Integer.MAX_VALUE ? "'unlimited'" : maxFetchSize));
|
||||
ourLog.info("Server configured to have a maximum fetch size of "
|
||||
+ (maxFetchSize == Integer.MAX_VALUE ? "'unlimited'" : maxFetchSize));
|
||||
|
||||
Long reuseCachedSearchResultsMillis = appProperties.getReuse_cached_search_results_millis();
|
||||
jpaStorageSettings.setReuseCachedSearchResultsForMillis(reuseCachedSearchResultsMillis);
|
||||
ourLog.info("Server configured to cache search results for {} milliseconds", reuseCachedSearchResultsMillis);
|
||||
|
||||
|
||||
Long retainCachedSearchesMinutes = appProperties.getRetain_cached_searches_mins();
|
||||
jpaStorageSettings.setExpireSearchResultsAfterMillis(retainCachedSearchesMinutes * 60 * 1000);
|
||||
|
||||
if(appProperties.getSubscription() != null) {
|
||||
if (appProperties.getSubscription() != null) {
|
||||
// Subscriptions are enabled by channel type
|
||||
if (appProperties.getSubscription().getResthook_enabled()) {
|
||||
ourLog.info("Enabling REST-hook subscriptions");
|
||||
jpaStorageSettings.addSupportedSubscriptionType(org.hl7.fhir.dstu2.model.Subscription.SubscriptionChannelType.RESTHOOK);
|
||||
jpaStorageSettings.addSupportedSubscriptionType(
|
||||
org.hl7.fhir.dstu2.model.Subscription.SubscriptionChannelType.RESTHOOK);
|
||||
}
|
||||
if (appProperties.getSubscription().getEmail() != null) {
|
||||
ourLog.info("Enabling email subscriptions");
|
||||
jpaStorageSettings.addSupportedSubscriptionType(org.hl7.fhir.dstu2.model.Subscription.SubscriptionChannelType.EMAIL);
|
||||
jpaStorageSettings.addSupportedSubscriptionType(
|
||||
org.hl7.fhir.dstu2.model.Subscription.SubscriptionChannelType.EMAIL);
|
||||
}
|
||||
if (appProperties.getSubscription().getWebsocket_enabled()) {
|
||||
ourLog.info("Enabling websocket subscriptions");
|
||||
jpaStorageSettings.addSupportedSubscriptionType(org.hl7.fhir.dstu2.model.Subscription.SubscriptionChannelType.WEBSOCKET);
|
||||
jpaStorageSettings.addSupportedSubscriptionType(
|
||||
org.hl7.fhir.dstu2.model.Subscription.SubscriptionChannelType.WEBSOCKET);
|
||||
}
|
||||
}
|
||||
|
||||
jpaStorageSettings.setFilterParameterEnabled(appProperties.getFilter_search_enabled());
|
||||
jpaStorageSettings.setAdvancedHSearchIndexing(appProperties.getAdvanced_lucene_indexing());
|
||||
jpaStorageSettings.setTreatBaseUrlsAsLocal(appProperties.getLocal_base_urls());
|
||||
jpaStorageSettings.setTreatBaseUrlsAsLocal(new HashSet<>(appProperties.getLocal_base_urls()));
|
||||
|
||||
if (appProperties.getLastn_enabled()) {
|
||||
jpaStorageSettings.setLastNEnabled(true);
|
||||
}
|
||||
|
||||
if(appProperties.getInline_resource_storage_below_size() != 0){
|
||||
if (appProperties.getInline_resource_storage_below_size() != 0) {
|
||||
jpaStorageSettings.setInlineResourceTextBelowSize(appProperties.getInline_resource_storage_below_size());
|
||||
}
|
||||
|
||||
@@ -139,20 +163,20 @@ public class FhirServerConfigCommon {
|
||||
jpaStorageSettings.setNormalizedQuantitySearchLevel(appProperties.getNormalized_quantity_search_level());
|
||||
jpaStorageSettings.setIndexOnContainedResources(appProperties.getEnable_index_contained_resource());
|
||||
|
||||
|
||||
|
||||
if (appProperties.getAllowed_bundle_types() != null) {
|
||||
jpaStorageSettings.setBundleTypesAllowedForStorage(appProperties.getAllowed_bundle_types().stream().map(BundleType::toCode).collect(Collectors.toSet()));
|
||||
jpaStorageSettings.setBundleTypesAllowedForStorage(appProperties.getAllowed_bundle_types().stream()
|
||||
.map(BundleType::toCode)
|
||||
.collect(Collectors.toSet()));
|
||||
}
|
||||
|
||||
jpaStorageSettings.setDeferIndexingForCodesystemsOfSize(appProperties.getDefer_indexing_for_codesystems_of_size());
|
||||
|
||||
jpaStorageSettings.setDeferIndexingForCodesystemsOfSize(
|
||||
appProperties.getDefer_indexing_for_codesystems_of_size());
|
||||
|
||||
if (appProperties.getClient_id_strategy() == JpaStorageSettings.ClientIdStrategyEnum.ANY) {
|
||||
jpaStorageSettings.setResourceServerIdStrategy(JpaStorageSettings.IdStrategyEnum.UUID);
|
||||
jpaStorageSettings.setResourceClientIdStrategy(appProperties.getClient_id_strategy());
|
||||
}
|
||||
//Parallel Batch GET execution settings
|
||||
// Parallel Batch GET execution settings
|
||||
jpaStorageSettings.setBundleBatchPoolSize(appProperties.getBundle_batch_pool_size());
|
||||
jpaStorageSettings.setBundleBatchPoolSize(appProperties.getBundle_batch_pool_max_size());
|
||||
|
||||
@@ -172,8 +196,9 @@ public class FhirServerConfigCommon {
|
||||
// Partitioning
|
||||
if (appProperties.getPartitioning() != null) {
|
||||
retVal.setPartitioningEnabled(true);
|
||||
retVal.setIncludePartitionInSearchHashes(appProperties.getPartitioning().getPartitioning_include_in_search_hashes());
|
||||
if(appProperties.getPartitioning().getAllow_references_across_partitions()) {
|
||||
retVal.setIncludePartitionInSearchHashes(
|
||||
appProperties.getPartitioning().getPartitioning_include_in_search_hashes());
|
||||
if (appProperties.getPartitioning().getAllow_references_across_partitions()) {
|
||||
retVal.setAllowReferencesAcrossPartitions(CrossPartitionReferenceMode.ALLOWED_UNQUALIFIED);
|
||||
} else {
|
||||
retVal.setAllowReferencesAcrossPartitions(CrossPartitionReferenceMode.NOT_ALLOWED);
|
||||
@@ -183,20 +208,22 @@ public class FhirServerConfigCommon {
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
@Primary
|
||||
@Bean
|
||||
public HibernatePropertiesProvider jpaStarterDialectProvider(LocalContainerEntityManagerFactoryBean myEntityManagerFactory) {
|
||||
public HibernatePropertiesProvider jpaStarterDialectProvider(
|
||||
LocalContainerEntityManagerFactoryBean myEntityManagerFactory) {
|
||||
return new JpaHibernatePropertiesProvider(myEntityManagerFactory);
|
||||
}
|
||||
|
||||
|
||||
protected StorageSettings storageSettings(AppProperties appProperties, JpaStorageSettings jpaStorageSettings) {
|
||||
jpaStorageSettings.setAllowContainsSearches(appProperties.getAllow_contains_searches());
|
||||
jpaStorageSettings.setAllowExternalReferences(appProperties.getAllow_external_references());
|
||||
jpaStorageSettings.setDefaultSearchParamsCanBeOverridden(appProperties.getAllow_override_default_search_params());
|
||||
if(appProperties.getSubscription() != null && appProperties.getSubscription().getEmail() != null)
|
||||
jpaStorageSettings.setEmailFromAddress(appProperties.getSubscription().getEmail().getFrom());
|
||||
jpaStorageSettings.setDefaultSearchParamsCanBeOverridden(
|
||||
appProperties.getAllow_override_default_search_params());
|
||||
if (appProperties.getSubscription() != null
|
||||
&& appProperties.getSubscription().getEmail() != null)
|
||||
jpaStorageSettings.setEmailFromAddress(
|
||||
appProperties.getSubscription().getEmail().getFrom());
|
||||
|
||||
jpaStorageSettings.setNormalizedQuantitySearchLevel(appProperties.getNormalized_quantity_search_level());
|
||||
|
||||
@@ -219,13 +246,15 @@ public class FhirServerConfigCommon {
|
||||
|
||||
@Bean
|
||||
public IEmailSender emailSender(AppProperties appProperties) {
|
||||
if (appProperties.getSubscription() != null && appProperties.getSubscription().getEmail() != null) {
|
||||
if (appProperties.getSubscription() != null
|
||||
&& appProperties.getSubscription().getEmail() != null) {
|
||||
|
||||
return buildEmailSender(appProperties.getSubscription().getEmail());
|
||||
}
|
||||
|
||||
// Return a dummy anonymous function instead of null. Spring does not like null beans.
|
||||
// TODO Get the signature of ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionDeliveryHandlerFactory
|
||||
// TODO Get the signature of
|
||||
// ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionDeliveryHandlerFactory
|
||||
// changed so it does not require an instance of an IEmailSender
|
||||
return theDetails -> {};
|
||||
}
|
||||
|
||||
@@ -13,14 +13,11 @@ import org.springframework.context.annotation.Import;
|
||||
|
||||
@Configuration
|
||||
@Conditional(OnDSTU2Condition.class)
|
||||
@Import({
|
||||
JpaDstu2Config.class,
|
||||
StarterJpaConfig.class
|
||||
})
|
||||
@Import({JpaDstu2Config.class, StarterJpaConfig.class})
|
||||
public class FhirServerConfigDstu2 {
|
||||
@Bean
|
||||
public ITermLoaderSvc termLoaderService(ITermDeferredStorageSvc theDeferredStorageSvc, ITermCodeSystemStorageSvc theCodeSystemStorageSvc) {
|
||||
public ITermLoaderSvc termLoaderService(
|
||||
ITermDeferredStorageSvc theDeferredStorageSvc, ITermCodeSystemStorageSvc theCodeSystemStorageSvc) {
|
||||
return new TermLoaderSvcImpl(theDeferredStorageSvc, theCodeSystemStorageSvc);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,10 +9,5 @@ import org.springframework.context.annotation.Import;
|
||||
|
||||
@Configuration
|
||||
@Conditional(OnDSTU3Condition.class)
|
||||
@Import({
|
||||
JpaDstu3Config.class,
|
||||
StarterJpaConfig.class,
|
||||
StarterCrDstu3Config.class,
|
||||
ElasticsearchConfig.class})
|
||||
public class FhirServerConfigDstu3 {
|
||||
}
|
||||
@Import({JpaDstu3Config.class, StarterJpaConfig.class, StarterCrDstu3Config.class, ElasticsearchConfig.class})
|
||||
public class FhirServerConfigDstu3 {}
|
||||
|
||||
@@ -4,7 +4,6 @@ import ca.uhn.fhir.jpa.config.r4.JpaR4Config;
|
||||
import ca.uhn.fhir.jpa.starter.annotations.OnR4Condition;
|
||||
import ca.uhn.fhir.jpa.starter.cr.StarterCrR4Config;
|
||||
import ca.uhn.fhir.jpa.starter.ips.StarterIpsConfig;
|
||||
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
@@ -18,5 +17,4 @@ import org.springframework.context.annotation.Import;
|
||||
ElasticsearchConfig.class,
|
||||
StarterIpsConfig.class
|
||||
})
|
||||
public class FhirServerConfigR4 {
|
||||
}
|
||||
public class FhirServerConfigR4 {}
|
||||
|
||||
@@ -9,11 +9,5 @@ import org.springframework.context.annotation.Import;
|
||||
|
||||
@Configuration
|
||||
@Conditional(OnR4BCondition.class)
|
||||
@Import({
|
||||
JpaR4BConfig.class,
|
||||
SubscriptionTopicConfig.class,
|
||||
StarterJpaConfig.class,
|
||||
ElasticsearchConfig.class
|
||||
})
|
||||
public class FhirServerConfigR4B {
|
||||
}
|
||||
@Import({JpaR4BConfig.class, SubscriptionTopicConfig.class, StarterJpaConfig.class, ElasticsearchConfig.class})
|
||||
public class FhirServerConfigR4B {}
|
||||
|
||||
@@ -9,11 +9,5 @@ import org.springframework.context.annotation.Import;
|
||||
|
||||
@Configuration
|
||||
@Conditional(OnR5Condition.class)
|
||||
@Import({
|
||||
StarterJpaConfig.class,
|
||||
JpaR5Config.class,
|
||||
SubscriptionTopicConfig.class,
|
||||
ElasticsearchConfig.class
|
||||
})
|
||||
public class FhirServerConfigR5 {
|
||||
}
|
||||
@Import({StarterJpaConfig.class, JpaR5Config.class, SubscriptionTopicConfig.class, ElasticsearchConfig.class})
|
||||
public class FhirServerConfigR5 {}
|
||||
|
||||
@@ -4,11 +4,10 @@ import ca.uhn.fhir.jpa.starter.AppProperties;
|
||||
import ca.uhn.fhir.to.FhirTesterMvcConfig;
|
||||
import ca.uhn.fhir.to.TesterConfig;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
//@formatter:off
|
||||
// @formatter:off
|
||||
/**
|
||||
* This spring config file configures the web testing module. It serves two
|
||||
* purposes:
|
||||
@@ -19,7 +18,7 @@ import org.springframework.context.annotation.Import;
|
||||
*/
|
||||
@Configuration
|
||||
@Import(FhirTesterMvcConfig.class)
|
||||
@Conditional(FhirTesterConfigCondition.class)
|
||||
// @Conditional(FhirTesterConfigCondition.class)
|
||||
public class FhirTesterConfig {
|
||||
|
||||
/**
|
||||
@@ -40,18 +39,14 @@ public class FhirTesterConfig {
|
||||
public TesterConfig testerConfig(AppProperties appProperties) {
|
||||
TesterConfig retVal = new TesterConfig();
|
||||
appProperties.getTester().forEach((key, value) -> {
|
||||
retVal
|
||||
.addServer()
|
||||
retVal.addServer()
|
||||
.withId(key)
|
||||
.withFhirVersion(value.getFhir_version())
|
||||
.withBaseUrl(value.getServer_address())
|
||||
.withName(value.getName());
|
||||
retVal.setRefuseToFetchThirdPartyUrls(
|
||||
value.getRefuse_to_fetch_third_party_urls());
|
||||
|
||||
retVal.setRefuseToFetchThirdPartyUrls(value.getRefuse_to_fetch_third_party_urls());
|
||||
});
|
||||
return retVal;
|
||||
}
|
||||
|
||||
}
|
||||
//@formatter:on
|
||||
// @formatter:on
|
||||
|
||||
@@ -10,7 +10,8 @@ public class FhirTesterConfigCondition implements Condition {
|
||||
@Override
|
||||
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata) {
|
||||
|
||||
var properties = EnvironmentHelper.getPropertiesStartingWith((ConfigurableEnvironment) conditionContext.getEnvironment(), "hapi.fhir.tester");
|
||||
var properties = EnvironmentHelper.getPropertiesStartingWith(
|
||||
(ConfigurableEnvironment) conditionContext.getEnvironment(), "hapi.fhir.tester");
|
||||
return !properties.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package ca.uhn.fhir.jpa.starter.common;
|
||||
|
||||
import ca.uhn.fhir.batch2.coordinator.JobDefinitionRegistry;
|
||||
import ca.uhn.fhir.batch2.jobs.export.BulkDataExportProvider;
|
||||
import ca.uhn.fhir.batch2.jobs.imprt.BulkDataImportProvider;
|
||||
import ca.uhn.fhir.batch2.jobs.reindex.ReindexJobParameters;
|
||||
import ca.uhn.fhir.batch2.jobs.reindex.ReindexProvider;
|
||||
@@ -9,7 +10,6 @@ import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
|
||||
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
|
||||
import ca.uhn.fhir.jpa.api.IDaoRegistry;
|
||||
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
|
||||
@@ -18,7 +18,6 @@ import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao;
|
||||
import ca.uhn.fhir.jpa.binary.interceptor.BinaryStorageInterceptor;
|
||||
import ca.uhn.fhir.jpa.binary.provider.BinaryAccessProvider;
|
||||
import ca.uhn.fhir.batch2.jobs.export.BulkDataExportProvider;
|
||||
import ca.uhn.fhir.jpa.config.util.HapiEntityManagerFactoryUtil;
|
||||
import ca.uhn.fhir.jpa.config.util.ResourceCountCacheUtil;
|
||||
import ca.uhn.fhir.jpa.config.util.ValidationSupportConfigUtil;
|
||||
@@ -34,8 +33,8 @@ import ca.uhn.fhir.jpa.ips.provider.IpsOperationProvider;
|
||||
import ca.uhn.fhir.jpa.packages.IPackageInstallerSvc;
|
||||
import ca.uhn.fhir.jpa.packages.PackageInstallationSpec;
|
||||
import ca.uhn.fhir.jpa.partition.PartitionManagementProvider;
|
||||
import ca.uhn.fhir.jpa.provider.*;
|
||||
import ca.uhn.fhir.jpa.provider.dstu3.JpaConformanceProviderDstu3;
|
||||
import ca.uhn.fhir.jpa.provider.*;
|
||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||
import ca.uhn.fhir.jpa.search.IStaleSearchDeletingSvc;
|
||||
import ca.uhn.fhir.jpa.search.StaleSearchDeletingSvcImpl;
|
||||
@@ -43,8 +42,8 @@ import ca.uhn.fhir.jpa.starter.AppProperties;
|
||||
import ca.uhn.fhir.jpa.starter.annotations.OnCorsPresent;
|
||||
import ca.uhn.fhir.jpa.starter.annotations.OnImplementationGuidesPresent;
|
||||
import ca.uhn.fhir.jpa.starter.common.validation.IRepositoryValidationInterceptorFactory;
|
||||
import ca.uhn.fhir.jpa.starter.util.EnvironmentHelper;
|
||||
import ca.uhn.fhir.jpa.starter.ig.IImplementationGuideOperationProvider;
|
||||
import ca.uhn.fhir.jpa.starter.util.EnvironmentHelper;
|
||||
import ca.uhn.fhir.jpa.subscription.util.SubscriptionDebugLogInterceptor;
|
||||
import ca.uhn.fhir.jpa.util.ResourceCountCache;
|
||||
import ca.uhn.fhir.jpa.validation.JpaValidationSupportChain;
|
||||
@@ -53,9 +52,9 @@ import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
||||
import ca.uhn.fhir.narrative2.NullNarrativeGenerator;
|
||||
import ca.uhn.fhir.rest.api.IResourceSupportedSvc;
|
||||
import ca.uhn.fhir.rest.openapi.OpenApiInterceptor;
|
||||
import ca.uhn.fhir.rest.server.interceptor.partition.RequestTenantPartitionInterceptor;
|
||||
import ca.uhn.fhir.rest.server.*;
|
||||
import ca.uhn.fhir.rest.server.interceptor.*;
|
||||
import ca.uhn.fhir.rest.server.interceptor.partition.RequestTenantPartitionInterceptor;
|
||||
import ca.uhn.fhir.rest.server.provider.ResourceProviderFactory;
|
||||
import ca.uhn.fhir.rest.server.tenant.UrlBaseTenantIdentificationStrategy;
|
||||
import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
|
||||
@@ -75,18 +74,16 @@ import org.springframework.orm.jpa.JpaTransactionManager;
|
||||
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
|
||||
import java.util.*;
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
import javax.sql.DataSource;
|
||||
import java.util.*;
|
||||
|
||||
import static ca.uhn.fhir.jpa.starter.common.validation.IRepositoryValidationInterceptorFactory.ENABLE_REPOSITORY_VALIDATING_INTERCEPTOR;
|
||||
|
||||
@Configuration
|
||||
//allow users to configure custom packages to scan for additional beans
|
||||
@ComponentScan(basePackages = { "${hapi.fhir.custom-bean-packages:}" })
|
||||
@Import(
|
||||
ThreadPoolFactoryConfig.class
|
||||
)
|
||||
// allow users to configure custom packages to scan for additional beans
|
||||
@ComponentScan(basePackages = {"${hapi.fhir.custom-bean-packages:}"})
|
||||
@Import(ThreadPoolFactoryConfig.class)
|
||||
public class StarterJpaConfig {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(StarterJpaConfig.class);
|
||||
@@ -107,12 +104,9 @@ public class StarterJpaConfig {
|
||||
return ValidationSupportConfigUtil.newCachingValidationSupport(theJpaValidationSupportChain);
|
||||
}
|
||||
|
||||
|
||||
@Autowired
|
||||
private ConfigurableEnvironment configurableEnvironment;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Customize the default/max page sizes for search results. You can set these however
|
||||
* you want, although very large page sizes will require a lot of RAM.
|
||||
@@ -125,7 +119,6 @@ public class StarterJpaConfig {
|
||||
return pagingProvider;
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
public IResourceSupportedSvc resourceSupportedSvc(IDaoRegistry theDaoRegistry) {
|
||||
return new DaoRegistryResourceSupportedSvc(theDaoRegistry);
|
||||
@@ -138,8 +131,12 @@ public class StarterJpaConfig {
|
||||
|
||||
@Primary
|
||||
@Bean
|
||||
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource myDataSource, ConfigurableListableBeanFactory myConfigurableListableBeanFactory, FhirContext theFhirContext) {
|
||||
LocalContainerEntityManagerFactoryBean retVal = HapiEntityManagerFactoryUtil.newEntityManagerFactory(myConfigurableListableBeanFactory, theFhirContext);
|
||||
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
|
||||
DataSource myDataSource,
|
||||
ConfigurableListableBeanFactory myConfigurableListableBeanFactory,
|
||||
FhirContext theFhirContext) {
|
||||
LocalContainerEntityManagerFactoryBean retVal =
|
||||
HapiEntityManagerFactoryUtil.newEntityManagerFactory(myConfigurableListableBeanFactory, theFhirContext);
|
||||
retVal.setPersistenceUnitName("HAPI_PU");
|
||||
|
||||
try {
|
||||
@@ -147,7 +144,8 @@ public class StarterJpaConfig {
|
||||
} catch (Exception e) {
|
||||
throw new ConfigurationException("Could not set the data source due to a configuration issue", e);
|
||||
}
|
||||
retVal.setJpaProperties(EnvironmentHelper.getHibernateProperties(configurableEnvironment, myConfigurableListableBeanFactory));
|
||||
retVal.setJpaProperties(
|
||||
EnvironmentHelper.getHibernateProperties(configurableEnvironment, myConfigurableListableBeanFactory));
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@@ -164,10 +162,10 @@ public class StarterJpaConfig {
|
||||
return new HSearchSortHelperImpl(mySearchParamRegistry);
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "hapi.fhir", name = ENABLE_REPOSITORY_VALIDATING_INTERCEPTOR, havingValue = "true")
|
||||
public RepositoryValidatingInterceptor repositoryValidatingInterceptor(IRepositoryValidationInterceptorFactory factory) {
|
||||
public RepositoryValidatingInterceptor repositoryValidatingInterceptor(
|
||||
IRepositoryValidationInterceptorFactory factory) {
|
||||
return factory.buildUsingStoredStructureDefinitions();
|
||||
}
|
||||
|
||||
@@ -189,8 +187,11 @@ public class StarterJpaConfig {
|
||||
@Bean("packageInstaller")
|
||||
@Primary
|
||||
@Conditional(OnImplementationGuidesPresent.class)
|
||||
public IPackageInstallerSvc packageInstaller(AppProperties appProperties, JobDefinition<ReindexJobParameters> reindexJobParametersJobDefinition, JobDefinitionRegistry jobDefinitionRegistry, IPackageInstallerSvc packageInstallerSvc)
|
||||
{
|
||||
public IPackageInstallerSvc packageInstaller(
|
||||
AppProperties appProperties,
|
||||
JobDefinition<ReindexJobParameters> reindexJobParametersJobDefinition,
|
||||
JobDefinitionRegistry jobDefinitionRegistry,
|
||||
IPackageInstallerSvc packageInstallerSvc) {
|
||||
jobDefinitionRegistry.addJobDefinitionIfNotRegistered(reindexJobParametersJobDefinition);
|
||||
|
||||
if (appProperties.getImplementationGuides() != null) {
|
||||
@@ -199,7 +200,8 @@ public class StarterJpaConfig {
|
||||
PackageInstallationSpec packageInstallationSpec = guidesEntry.getValue();
|
||||
if (appProperties.getInstall_transitive_ig_dependencies()) {
|
||||
|
||||
packageInstallationSpec.addDependencyExclude("hl7.fhir.r2.core")
|
||||
packageInstallationSpec
|
||||
.addDependencyExclude("hl7.fhir.r2.core")
|
||||
.addDependencyExclude("hl7.fhir.r3.core")
|
||||
.addDependencyExclude("hl7.fhir.r4.core")
|
||||
.addDependencyExclude("hl7.fhir.r5.core");
|
||||
@@ -238,11 +240,40 @@ public class StarterJpaConfig {
|
||||
|
||||
// Create the interceptor and register it
|
||||
return new CorsInterceptor(config);
|
||||
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RestfulServer restfulServer(IFhirSystemDao<?, ?> fhirSystemDao, AppProperties appProperties, DaoRegistry daoRegistry, Optional<MdmProviderLoader> mdmProviderProvider, IJpaSystemProvider jpaSystemProvider, ResourceProviderFactory resourceProviderFactory, JpaStorageSettings jpaStorageSettings, ISearchParamRegistry searchParamRegistry, IValidationSupport theValidationSupport, DatabaseBackedPagingProvider databaseBackedPagingProvider, LoggingInterceptor loggingInterceptor, Optional<TerminologyUploaderProvider> terminologyUploaderProvider, Optional<SubscriptionTriggeringProvider> subscriptionTriggeringProvider, Optional<CorsInterceptor> corsInterceptor, IInterceptorBroadcaster interceptorBroadcaster, Optional<BinaryAccessProvider> binaryAccessProvider, BinaryStorageInterceptor binaryStorageInterceptor, IValidatorModule validatorModule, Optional<GraphQLProvider> graphQLProvider, BulkDataExportProvider bulkDataExportProvider, BulkDataImportProvider bulkDataImportProvider, ValueSetOperationProvider theValueSetOperationProvider, ReindexProvider reindexProvider, PartitionManagementProvider partitionManagementProvider, Optional<RepositoryValidatingInterceptor> repositoryValidatingInterceptor, IPackageInstallerSvc packageInstallerSvc, ThreadSafeResourceDeleterSvc theThreadSafeResourceDeleterSvc, ApplicationContext appContext, Optional<IpsOperationProvider> theIpsOperationProvider, Optional<IImplementationGuideOperationProvider> implementationGuideOperationProvider) {
|
||||
public RestfulServer restfulServer(
|
||||
IFhirSystemDao<?, ?> fhirSystemDao,
|
||||
AppProperties appProperties,
|
||||
DaoRegistry daoRegistry,
|
||||
Optional<MdmProviderLoader> mdmProviderProvider,
|
||||
IJpaSystemProvider jpaSystemProvider,
|
||||
ResourceProviderFactory resourceProviderFactory,
|
||||
JpaStorageSettings jpaStorageSettings,
|
||||
ISearchParamRegistry searchParamRegistry,
|
||||
IValidationSupport theValidationSupport,
|
||||
DatabaseBackedPagingProvider databaseBackedPagingProvider,
|
||||
LoggingInterceptor loggingInterceptor,
|
||||
Optional<TerminologyUploaderProvider> terminologyUploaderProvider,
|
||||
Optional<SubscriptionTriggeringProvider> subscriptionTriggeringProvider,
|
||||
Optional<CorsInterceptor> corsInterceptor,
|
||||
IInterceptorBroadcaster interceptorBroadcaster,
|
||||
Optional<BinaryAccessProvider> binaryAccessProvider,
|
||||
BinaryStorageInterceptor binaryStorageInterceptor,
|
||||
IValidatorModule validatorModule,
|
||||
Optional<GraphQLProvider> graphQLProvider,
|
||||
BulkDataExportProvider bulkDataExportProvider,
|
||||
BulkDataImportProvider bulkDataImportProvider,
|
||||
ValueSetOperationProvider theValueSetOperationProvider,
|
||||
ReindexProvider reindexProvider,
|
||||
PartitionManagementProvider partitionManagementProvider,
|
||||
Optional<RepositoryValidatingInterceptor> repositoryValidatingInterceptor,
|
||||
IPackageInstallerSvc packageInstallerSvc,
|
||||
ThreadSafeResourceDeleterSvc theThreadSafeResourceDeleterSvc,
|
||||
ApplicationContext appContext,
|
||||
Optional<IpsOperationProvider> theIpsOperationProvider,
|
||||
Optional<IImplementationGuideOperationProvider> implementationGuideOperationProvider) {
|
||||
RestfulServer fhirServer = new RestfulServer(fhirSystemDao.getContext());
|
||||
|
||||
List<String> supportedResourceTypes = appProperties.getSupported_resource_types();
|
||||
@@ -264,7 +295,8 @@ public class StarterJpaConfig {
|
||||
|
||||
fhirServer.registerProviders(resourceProviderFactory.createProviders());
|
||||
fhirServer.registerProvider(jpaSystemProvider);
|
||||
fhirServer.setServerConformanceProvider(calculateConformanceProvider(fhirSystemDao, fhirServer, jpaStorageSettings, searchParamRegistry, theValidationSupport));
|
||||
fhirServer.setServerConformanceProvider(calculateConformanceProvider(
|
||||
fhirSystemDao, fhirServer, jpaStorageSettings, searchParamRegistry, theValidationSupport));
|
||||
|
||||
/*
|
||||
* ETag Support
|
||||
@@ -272,7 +304,6 @@ public class StarterJpaConfig {
|
||||
|
||||
if (!appProperties.getEtag_support_enabled()) fhirServer.setETagSupport(ETagSupportEnum.DISABLED);
|
||||
|
||||
|
||||
/*
|
||||
* Default to JSON and pretty printing
|
||||
*/
|
||||
@@ -318,7 +349,8 @@ public class StarterJpaConfig {
|
||||
fhirServer.setServerAddressStrategy(new HardcodedServerAddressStrategy(serverAddress));
|
||||
} else if (appProperties.getUse_apache_address_strategy()) {
|
||||
boolean useHttps = appProperties.getUse_apache_address_strategy_https();
|
||||
fhirServer.setServerAddressStrategy(useHttps ? ApacheProxyAddressStrategy.forHttps() : ApacheProxyAddressStrategy.forHttp());
|
||||
fhirServer.setServerAddressStrategy(
|
||||
useHttps ? ApacheProxyAddressStrategy.forHttps() : ApacheProxyAddressStrategy.forHttp());
|
||||
} else {
|
||||
fhirServer.setServerAddressStrategy(new IncomingRequestAddressStrategy());
|
||||
}
|
||||
@@ -330,7 +362,11 @@ public class StarterJpaConfig {
|
||||
* so it is a potential security vulnerability. Consider using an AuthorizationInterceptor
|
||||
* with this feature.
|
||||
*/
|
||||
if (fhirSystemDao.getContext().getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.DSTU3)) { // <-- ENABLED RIGHT NOW
|
||||
if (fhirSystemDao
|
||||
.getContext()
|
||||
.getVersion()
|
||||
.getVersion()
|
||||
.isEqualOrNewerThan(FhirVersionEnum.DSTU3)) { // <-- ENABLED RIGHT NOW
|
||||
fhirServer.registerProvider(terminologyUploaderProvider.get());
|
||||
}
|
||||
|
||||
@@ -348,7 +384,8 @@ public class StarterJpaConfig {
|
||||
}
|
||||
|
||||
if (appProperties.getAllow_cascading_deletes()) {
|
||||
CascadingDeleteInterceptor cascadingDeleteInterceptor = new CascadingDeleteInterceptor(fhirSystemDao.getContext(), daoRegistry, interceptorBroadcaster, theThreadSafeResourceDeleterSvc);
|
||||
CascadingDeleteInterceptor cascadingDeleteInterceptor = new CascadingDeleteInterceptor(
|
||||
fhirSystemDao.getContext(), daoRegistry, interceptorBroadcaster, theThreadSafeResourceDeleterSvc);
|
||||
fhirServer.registerInterceptor(cascadingDeleteInterceptor);
|
||||
}
|
||||
|
||||
@@ -391,16 +428,15 @@ public class StarterJpaConfig {
|
||||
fhirServer.registerProvider(bulkDataExportProvider);
|
||||
}
|
||||
|
||||
//Bulk Import
|
||||
// Bulk Import
|
||||
if (appProperties.getBulk_import_enabled()) {
|
||||
fhirServer.registerProvider(bulkDataImportProvider);
|
||||
}
|
||||
|
||||
|
||||
// valueSet Operations i.e $expand
|
||||
fhirServer.registerProvider(theValueSetOperationProvider);
|
||||
|
||||
//reindex Provider $reindex
|
||||
// reindex Provider $reindex
|
||||
fhirServer.registerProvider(reindexProvider);
|
||||
|
||||
// Partitioning
|
||||
@@ -414,8 +450,7 @@ public class StarterJpaConfig {
|
||||
// register custom interceptors
|
||||
registerCustomInterceptors(fhirServer, appContext, appProperties.getCustomInterceptorClasses());
|
||||
|
||||
|
||||
//register the IPS Provider
|
||||
// register the IPS Provider
|
||||
if (!theIpsOperationProvider.isEmpty()) {
|
||||
fhirServer.registerProvider(theIpsOperationProvider.get());
|
||||
}
|
||||
@@ -426,8 +461,9 @@ public class StarterJpaConfig {
|
||||
/**
|
||||
* check the properties for custom interceptor classes and registers them.
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
private void registerCustomInterceptors(RestfulServer fhirServer, ApplicationContext theAppContext, List<String> customInterceptorClasses) {
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private void registerCustomInterceptors(
|
||||
RestfulServer fhirServer, ApplicationContext theAppContext, List<String> customInterceptorClasses) {
|
||||
|
||||
if (customInterceptorClasses == null) {
|
||||
return;
|
||||
@@ -461,30 +497,40 @@ public class StarterJpaConfig {
|
||||
}
|
||||
}
|
||||
|
||||
public static IServerConformanceProvider<?> calculateConformanceProvider(IFhirSystemDao fhirSystemDao, RestfulServer fhirServer, JpaStorageSettings jpaStorageSettings, ISearchParamRegistry searchParamRegistry, IValidationSupport theValidationSupport) {
|
||||
public static IServerConformanceProvider<?> calculateConformanceProvider(
|
||||
IFhirSystemDao fhirSystemDao,
|
||||
RestfulServer fhirServer,
|
||||
JpaStorageSettings jpaStorageSettings,
|
||||
ISearchParamRegistry searchParamRegistry,
|
||||
IValidationSupport theValidationSupport) {
|
||||
FhirVersionEnum fhirVersion = fhirSystemDao.getContext().getVersion().getVersion();
|
||||
if (fhirVersion == FhirVersionEnum.DSTU2) {
|
||||
JpaConformanceProviderDstu2 confProvider = new JpaConformanceProviderDstu2(fhirServer, fhirSystemDao, jpaStorageSettings);
|
||||
JpaConformanceProviderDstu2 confProvider =
|
||||
new JpaConformanceProviderDstu2(fhirServer, fhirSystemDao, jpaStorageSettings);
|
||||
confProvider.setImplementationDescription("HAPI FHIR DSTU2 Server");
|
||||
return confProvider;
|
||||
} else if (fhirVersion == FhirVersionEnum.DSTU3) {
|
||||
|
||||
JpaConformanceProviderDstu3 confProvider = new JpaConformanceProviderDstu3(fhirServer, fhirSystemDao, jpaStorageSettings, searchParamRegistry);
|
||||
JpaConformanceProviderDstu3 confProvider =
|
||||
new JpaConformanceProviderDstu3(fhirServer, fhirSystemDao, jpaStorageSettings, searchParamRegistry);
|
||||
confProvider.setImplementationDescription("HAPI FHIR DSTU3 Server");
|
||||
return confProvider;
|
||||
} else if (fhirVersion == FhirVersionEnum.R4) {
|
||||
|
||||
JpaCapabilityStatementProvider confProvider = new JpaCapabilityStatementProvider(fhirServer, fhirSystemDao, jpaStorageSettings, searchParamRegistry, theValidationSupport);
|
||||
JpaCapabilityStatementProvider confProvider = new JpaCapabilityStatementProvider(
|
||||
fhirServer, fhirSystemDao, jpaStorageSettings, searchParamRegistry, theValidationSupport);
|
||||
confProvider.setImplementationDescription("HAPI FHIR R4 Server");
|
||||
return confProvider;
|
||||
} else if (fhirVersion == FhirVersionEnum.R4B) {
|
||||
|
||||
JpaCapabilityStatementProvider confProvider = new JpaCapabilityStatementProvider(fhirServer, fhirSystemDao, jpaStorageSettings, searchParamRegistry, theValidationSupport);
|
||||
JpaCapabilityStatementProvider confProvider = new JpaCapabilityStatementProvider(
|
||||
fhirServer, fhirSystemDao, jpaStorageSettings, searchParamRegistry, theValidationSupport);
|
||||
confProvider.setImplementationDescription("HAPI FHIR R4B Server");
|
||||
return confProvider;
|
||||
} else if (fhirVersion == FhirVersionEnum.R5) {
|
||||
|
||||
JpaCapabilityStatementProvider confProvider = new JpaCapabilityStatementProvider(fhirServer, fhirSystemDao, jpaStorageSettings, searchParamRegistry, theValidationSupport);
|
||||
JpaCapabilityStatementProvider confProvider = new JpaCapabilityStatementProvider(
|
||||
fhirServer, fhirSystemDao, jpaStorageSettings, searchParamRegistry, theValidationSupport);
|
||||
confProvider.setImplementationDescription("HAPI FHIR R5 Server");
|
||||
return confProvider;
|
||||
} else {
|
||||
@@ -492,4 +538,3 @@ public class StarterJpaConfig {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import ca.uhn.fhir.jpa.interceptor.validation.RepositoryValidatingInterceptor;
|
||||
public interface IRepositoryValidationInterceptorFactory {
|
||||
|
||||
String ENABLE_REPOSITORY_VALIDATING_INTERCEPTOR = "enable_repository_validating_interceptor";
|
||||
|
||||
RepositoryValidatingInterceptor buildUsingStoredStructureDefinitions();
|
||||
|
||||
RepositoryValidatingInterceptor build();
|
||||
|
||||
@@ -37,24 +37,28 @@ public class RepositoryValidationInterceptorFactoryDstu3 implements IRepositoryV
|
||||
private final RepositoryValidatingRuleBuilder repositoryValidatingRuleBuilder;
|
||||
private final IFhirResourceDao structureDefinitionResourceProvider;
|
||||
|
||||
public RepositoryValidationInterceptorFactoryDstu3(RepositoryValidatingRuleBuilder repositoryValidatingRuleBuilder, DaoRegistry daoRegistry) {
|
||||
public RepositoryValidationInterceptorFactoryDstu3(
|
||||
RepositoryValidatingRuleBuilder repositoryValidatingRuleBuilder, DaoRegistry daoRegistry) {
|
||||
this.repositoryValidatingRuleBuilder = repositoryValidatingRuleBuilder;
|
||||
this.fhirContext = daoRegistry.getSystemDao().getContext();
|
||||
structureDefinitionResourceProvider = daoRegistry.getResourceDao("StructureDefinition");
|
||||
|
||||
}
|
||||
|
||||
public RepositoryValidatingInterceptor buildUsingStoredStructureDefinitions() {
|
||||
|
||||
IBundleProvider results = structureDefinitionResourceProvider.search(new SearchParameterMap().add(StructureDefinition.SP_KIND, new TokenParam("resource")));
|
||||
Map<String, List<StructureDefinition>> structureDefinitions = results.getResources(0, results.size())
|
||||
.stream()
|
||||
IBundleProvider results = structureDefinitionResourceProvider.search(
|
||||
new SearchParameterMap().add(StructureDefinition.SP_KIND, new TokenParam("resource")));
|
||||
Map<String, List<StructureDefinition>> structureDefinitions = results.getResources(0, results.size()).stream()
|
||||
.map(StructureDefinition.class::cast)
|
||||
.collect(Collectors.groupingBy(StructureDefinition::getType));
|
||||
|
||||
structureDefinitions.forEach((key, value) -> {
|
||||
String[] urls = value.stream().map(StructureDefinition::getUrl).toArray(String[]::new);
|
||||
repositoryValidatingRuleBuilder.forResourcesOfType(key).requireAtLeastOneProfileOf(urls).and().requireValidationToDeclaredProfiles();
|
||||
repositoryValidatingRuleBuilder
|
||||
.forResourcesOfType(key)
|
||||
.requireAtLeastOneProfileOf(urls)
|
||||
.and()
|
||||
.requireValidationToDeclaredProfiles();
|
||||
});
|
||||
|
||||
List<IRepositoryValidatingRule> rules = repositoryValidatingRuleBuilder.build();
|
||||
@@ -65,11 +69,14 @@ public class RepositoryValidationInterceptorFactoryDstu3 implements IRepositoryV
|
||||
|
||||
// Customize the ruleBuilder here to have the rules you want! We will give a simple example
|
||||
// of enabling validation for all Patient resources
|
||||
repositoryValidatingRuleBuilder.forResourcesOfType("Patient").requireAtLeastProfile("http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient").and().requireValidationToDeclaredProfiles();
|
||||
repositoryValidatingRuleBuilder
|
||||
.forResourcesOfType("Patient")
|
||||
.requireAtLeastProfile("http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient")
|
||||
.and()
|
||||
.requireValidationToDeclaredProfiles();
|
||||
|
||||
// Do not customize below this line
|
||||
List<IRepositoryValidatingRule> rules = repositoryValidatingRuleBuilder.build();
|
||||
return new RepositoryValidatingInterceptor(fhirContext, rules);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -37,25 +37,29 @@ public class RepositoryValidationInterceptorFactoryR4 implements IRepositoryVali
|
||||
private final RepositoryValidatingRuleBuilder repositoryValidatingRuleBuilder;
|
||||
private final IFhirResourceDao structureDefinitionResourceProvider;
|
||||
|
||||
public RepositoryValidationInterceptorFactoryR4(RepositoryValidatingRuleBuilder repositoryValidatingRuleBuilder, DaoRegistry daoRegistry) {
|
||||
public RepositoryValidationInterceptorFactoryR4(
|
||||
RepositoryValidatingRuleBuilder repositoryValidatingRuleBuilder, DaoRegistry daoRegistry) {
|
||||
this.repositoryValidatingRuleBuilder = repositoryValidatingRuleBuilder;
|
||||
this.fhirContext = daoRegistry.getSystemDao().getContext();
|
||||
structureDefinitionResourceProvider = daoRegistry.getResourceDao("StructureDefinition");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public RepositoryValidatingInterceptor buildUsingStoredStructureDefinitions() {
|
||||
|
||||
IBundleProvider results = structureDefinitionResourceProvider.search(new SearchParameterMap().add(StructureDefinition.SP_KIND, new TokenParam("resource")));
|
||||
Map<String, List<StructureDefinition>> structureDefintions = results.getResources(0, results.size())
|
||||
.stream()
|
||||
IBundleProvider results = structureDefinitionResourceProvider.search(
|
||||
new SearchParameterMap().add(StructureDefinition.SP_KIND, new TokenParam("resource")));
|
||||
Map<String, List<StructureDefinition>> structureDefintions = results.getResources(0, results.size()).stream()
|
||||
.map(StructureDefinition.class::cast)
|
||||
.collect(Collectors.groupingBy(StructureDefinition::getType));
|
||||
|
||||
structureDefintions.forEach((key, value) -> {
|
||||
String[] urls = value.stream().map(StructureDefinition::getUrl).toArray(String[]::new);
|
||||
repositoryValidatingRuleBuilder.forResourcesOfType(key).requireAtLeastOneProfileOf(urls).and().requireValidationToDeclaredProfiles();
|
||||
repositoryValidatingRuleBuilder
|
||||
.forResourcesOfType(key)
|
||||
.requireAtLeastOneProfileOf(urls)
|
||||
.and()
|
||||
.requireValidationToDeclaredProfiles();
|
||||
});
|
||||
|
||||
List<IRepositoryValidatingRule> rules = repositoryValidatingRuleBuilder.build();
|
||||
@@ -67,11 +71,14 @@ public class RepositoryValidationInterceptorFactoryR4 implements IRepositoryVali
|
||||
|
||||
// Customize the ruleBuilder here to have the rules you want! We will give a simple example
|
||||
// of enabling validation for all Patient resources
|
||||
repositoryValidatingRuleBuilder.forResourcesOfType("Patient").requireAtLeastProfile("http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient").and().requireValidationToDeclaredProfiles();
|
||||
repositoryValidatingRuleBuilder
|
||||
.forResourcesOfType("Patient")
|
||||
.requireAtLeastProfile("http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient")
|
||||
.and()
|
||||
.requireValidationToDeclaredProfiles();
|
||||
|
||||
// Do not customize below this line
|
||||
List<IRepositoryValidatingRule> rules = repositoryValidatingRuleBuilder.build();
|
||||
return new RepositoryValidatingInterceptor(fhirContext, rules);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -37,25 +37,29 @@ public class RepositoryValidationInterceptorFactoryR4B implements IRepositoryVal
|
||||
private final RepositoryValidatingRuleBuilder repositoryValidatingRuleBuilder;
|
||||
private final IFhirResourceDao structureDefinitionResourceProvider;
|
||||
|
||||
public RepositoryValidationInterceptorFactoryR4B(RepositoryValidatingRuleBuilder repositoryValidatingRuleBuilder, DaoRegistry daoRegistry) {
|
||||
public RepositoryValidationInterceptorFactoryR4B(
|
||||
RepositoryValidatingRuleBuilder repositoryValidatingRuleBuilder, DaoRegistry daoRegistry) {
|
||||
this.repositoryValidatingRuleBuilder = repositoryValidatingRuleBuilder;
|
||||
this.fhirContext = daoRegistry.getSystemDao().getContext();
|
||||
structureDefinitionResourceProvider = daoRegistry.getResourceDao("StructureDefinition");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public RepositoryValidatingInterceptor buildUsingStoredStructureDefinitions() {
|
||||
|
||||
IBundleProvider results = structureDefinitionResourceProvider.search(new SearchParameterMap().add(StructureDefinition.SP_KIND, new TokenParam("resource")));
|
||||
Map<String, List<StructureDefinition>> structureDefintions = results.getResources(0, results.size())
|
||||
.stream()
|
||||
IBundleProvider results = structureDefinitionResourceProvider.search(
|
||||
new SearchParameterMap().add(StructureDefinition.SP_KIND, new TokenParam("resource")));
|
||||
Map<String, List<StructureDefinition>> structureDefintions = results.getResources(0, results.size()).stream()
|
||||
.map(StructureDefinition.class::cast)
|
||||
.collect(Collectors.groupingBy(StructureDefinition::getType));
|
||||
|
||||
structureDefintions.forEach((key, value) -> {
|
||||
String[] urls = value.stream().map(StructureDefinition::getUrl).toArray(String[]::new);
|
||||
repositoryValidatingRuleBuilder.forResourcesOfType(key).requireAtLeastOneProfileOf(urls).and().requireValidationToDeclaredProfiles();
|
||||
repositoryValidatingRuleBuilder
|
||||
.forResourcesOfType(key)
|
||||
.requireAtLeastOneProfileOf(urls)
|
||||
.and()
|
||||
.requireValidationToDeclaredProfiles();
|
||||
});
|
||||
|
||||
List<IRepositoryValidatingRule> rules = repositoryValidatingRuleBuilder.build();
|
||||
@@ -67,11 +71,14 @@ public class RepositoryValidationInterceptorFactoryR4B implements IRepositoryVal
|
||||
|
||||
// Customize the ruleBuilder here to have the rules you want! We will give a simple example
|
||||
// of enabling validation for all Patient resources
|
||||
repositoryValidatingRuleBuilder.forResourcesOfType("Patient").requireAtLeastProfile("http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient").and().requireValidationToDeclaredProfiles();
|
||||
repositoryValidatingRuleBuilder
|
||||
.forResourcesOfType("Patient")
|
||||
.requireAtLeastProfile("http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient")
|
||||
.and()
|
||||
.requireValidationToDeclaredProfiles();
|
||||
|
||||
// Do not customize below this line
|
||||
List<IRepositoryValidatingRule> rules = repositoryValidatingRuleBuilder.build();
|
||||
return new RepositoryValidatingInterceptor(fhirContext, rules);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -37,24 +37,28 @@ public class RepositoryValidationInterceptorFactoryR5 implements IRepositoryVali
|
||||
private final RepositoryValidatingRuleBuilder repositoryValidatingRuleBuilder;
|
||||
private final IFhirResourceDao structureDefinitionResourceProvider;
|
||||
|
||||
public RepositoryValidationInterceptorFactoryR5(RepositoryValidatingRuleBuilder repositoryValidatingRuleBuilder, DaoRegistry daoRegistry) {
|
||||
public RepositoryValidationInterceptorFactoryR5(
|
||||
RepositoryValidatingRuleBuilder repositoryValidatingRuleBuilder, DaoRegistry daoRegistry) {
|
||||
this.repositoryValidatingRuleBuilder = repositoryValidatingRuleBuilder;
|
||||
this.fhirContext = daoRegistry.getSystemDao().getContext();
|
||||
structureDefinitionResourceProvider = daoRegistry.getResourceDao("StructureDefinition");
|
||||
|
||||
}
|
||||
|
||||
public RepositoryValidatingInterceptor buildUsingStoredStructureDefinitions() {
|
||||
|
||||
IBundleProvider results = structureDefinitionResourceProvider.search(new SearchParameterMap().add(StructureDefinition.SP_KIND, new TokenParam("resource")));
|
||||
Map<String, List<StructureDefinition>> structureDefintions = results.getResources(0, results.size())
|
||||
.stream()
|
||||
IBundleProvider results = structureDefinitionResourceProvider.search(
|
||||
new SearchParameterMap().add(StructureDefinition.SP_KIND, new TokenParam("resource")));
|
||||
Map<String, List<StructureDefinition>> structureDefintions = results.getResources(0, results.size()).stream()
|
||||
.map(StructureDefinition.class::cast)
|
||||
.collect(Collectors.groupingBy(StructureDefinition::getType));
|
||||
|
||||
structureDefintions.forEach((key, value) -> {
|
||||
String[] urls = value.stream().map(StructureDefinition::getUrl).toArray(String[]::new);
|
||||
repositoryValidatingRuleBuilder.forResourcesOfType(key).requireAtLeastOneProfileOf(urls).and().requireValidationToDeclaredProfiles();
|
||||
repositoryValidatingRuleBuilder
|
||||
.forResourcesOfType(key)
|
||||
.requireAtLeastOneProfileOf(urls)
|
||||
.and()
|
||||
.requireValidationToDeclaredProfiles();
|
||||
});
|
||||
|
||||
List<IRepositoryValidatingRule> rules = repositoryValidatingRuleBuilder.build();
|
||||
@@ -65,11 +69,14 @@ public class RepositoryValidationInterceptorFactoryR5 implements IRepositoryVali
|
||||
|
||||
// Customize the ruleBuilder here to have the rules you want! We will give a simple example
|
||||
// of enabling validation for all Patient resources
|
||||
repositoryValidatingRuleBuilder.forResourcesOfType("Patient").requireAtLeastProfile("http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient").and().requireValidationToDeclaredProfiles();
|
||||
repositoryValidatingRuleBuilder
|
||||
.forResourcesOfType("Patient")
|
||||
.requireAtLeastProfile("http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient")
|
||||
.and()
|
||||
.requireValidationToDeclaredProfiles();
|
||||
|
||||
// Do not customize below this line
|
||||
List<IRepositoryValidatingRule> rules = repositoryValidatingRuleBuilder.build();
|
||||
return new RepositoryValidatingInterceptor(fhirContext, rules);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ public class CrConfigCondition implements Condition {
|
||||
|
||||
@Override
|
||||
public boolean matches(ConditionContext theConditionContext, AnnotatedTypeMetadata theAnnotatedTypeMetadata) {
|
||||
String property = theConditionContext.getEnvironment().getProperty("hapi.fhir.cr_enabled");
|
||||
String property = theConditionContext.getEnvironment().getProperty("hapi.fhir.cr.enabled");
|
||||
return Boolean.parseBoolean(property);
|
||||
}
|
||||
}
|
||||
|
||||
264
src/main/java/ca/uhn/fhir/jpa/starter/cr/CrProperties.java
Normal file
264
src/main/java/ca/uhn/fhir/jpa/starter/cr/CrProperties.java
Normal file
@@ -0,0 +1,264 @@
|
||||
package ca.uhn.fhir.jpa.starter.cr;
|
||||
|
||||
import org.cqframework.cql.cql2elm.CqlCompilerException;
|
||||
import org.cqframework.cql.cql2elm.CqlTranslator;
|
||||
import org.cqframework.cql.cql2elm.LibraryBuilder;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
@ConfigurationProperties(prefix = "hapi.fhir.cr")
|
||||
public class CrProperties {
|
||||
private Boolean enabled;
|
||||
// cql settings
|
||||
private Boolean cql_use_embedded_libraries = true;
|
||||
private Boolean cql_runtime_debug_logging_enabled = false;
|
||||
private Boolean cql_runtime_enable_validation = false;
|
||||
private Boolean cql_runtime_enable_expression_caching = false;
|
||||
private Boolean cql_compiler_validate_units = true;
|
||||
private Boolean cql_compiler_verify_only = false;
|
||||
private String cql_compiler_compatibility_level = "1.5";
|
||||
private CqlCompilerException.ErrorSeverity cql_compiler_error_level = CqlCompilerException.ErrorSeverity.Info;
|
||||
private LibraryBuilder.SignatureLevel cql_compiler_signature_level = LibraryBuilder.SignatureLevel.All;
|
||||
private Boolean cql_compiler_analyze_data_requirements = false;
|
||||
private Boolean cql_compiler_collapse_data_requirements = false;
|
||||
private CqlTranslator.Format cql_compiler_translator_format = CqlTranslator.Format.JSON;
|
||||
private Boolean cql_compiler_enable_date_range_optimization = false;
|
||||
private Boolean cql_compiler_enable_annotations = false;
|
||||
private Boolean cql_compiler_enable_locators = false;
|
||||
private Boolean cql_compiler_enable_results_type = false;
|
||||
private Boolean cql_compiler_enable_detailed_errors = false;
|
||||
private Boolean cql_compiler_disable_list_traversal = false;
|
||||
private Boolean cql_compiler_disable_list_demotion = false;
|
||||
private Boolean cql_compiler_disable_list_promotion = false;
|
||||
private Boolean cql_compiler_enable_interval_demotion = false;
|
||||
private Boolean cql_compiler_enable_interval_promotion = false;
|
||||
private Boolean cql_compiler_disable_method_invocation = false;
|
||||
private Boolean cql_compiler_require_from_keyword = false;
|
||||
private Boolean cql_compiler_disable_default_model_info_load = false;
|
||||
// Care-gaps Settings
|
||||
private String caregaps_reporter = "default";
|
||||
private String caregaps_section_author = "default";
|
||||
|
||||
public Boolean getEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(Boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public boolean isCqlUseEmbeddedLibraries() {
|
||||
return cql_use_embedded_libraries;
|
||||
}
|
||||
|
||||
public void setCqlUseEmbeddedLibraries(boolean cql_use_embedded_libraries) {
|
||||
this.cql_use_embedded_libraries = cql_use_embedded_libraries;
|
||||
}
|
||||
|
||||
public boolean isCqlRuntimeDebugLoggingEnabled() {
|
||||
return cql_runtime_debug_logging_enabled;
|
||||
}
|
||||
|
||||
public void setCqlRuntimeDebugLoggingEnabled(boolean cqlRuntimeDebugLoggingEnabled) {
|
||||
this.cql_runtime_debug_logging_enabled = cqlRuntimeDebugLoggingEnabled;
|
||||
}
|
||||
|
||||
public boolean isCqlCompilerValidateUnits() {
|
||||
return cql_compiler_validate_units;
|
||||
}
|
||||
|
||||
public void setCqlCompilerValidateUnits(boolean cqlCompilerValidateUnits) {
|
||||
this.cql_compiler_validate_units = cqlCompilerValidateUnits;
|
||||
}
|
||||
|
||||
public boolean isCqlCompilerVerifyOnly() {
|
||||
return cql_compiler_verify_only;
|
||||
}
|
||||
|
||||
public void setCqlCompilerVerifyOnly(boolean cqlCompilerVerifyOnly) {
|
||||
this.cql_compiler_verify_only = cqlCompilerVerifyOnly;
|
||||
}
|
||||
|
||||
public String getCqlCompilerCompatibilityLevel() {
|
||||
return cql_compiler_compatibility_level;
|
||||
}
|
||||
|
||||
public void setCqlCompilerCompatibilityLevel(String cqlCompilerCompatibilityLevel) {
|
||||
this.cql_compiler_compatibility_level = cqlCompilerCompatibilityLevel;
|
||||
}
|
||||
|
||||
public CqlCompilerException.ErrorSeverity getCqlCompilerErrorSeverityLevel() {
|
||||
return cql_compiler_error_level;
|
||||
}
|
||||
|
||||
public void setCqlCompilerErrorSeverityLevel(CqlCompilerException.ErrorSeverity cqlCompilerErrorSeverityLevel) {
|
||||
this.cql_compiler_error_level = cqlCompilerErrorSeverityLevel;
|
||||
}
|
||||
|
||||
public LibraryBuilder.SignatureLevel getCqlCompilerSignatureLevel() {
|
||||
return cql_compiler_signature_level;
|
||||
}
|
||||
|
||||
public void setCqlCompilerSignatureLevel(LibraryBuilder.SignatureLevel cqlCompilerSignatureLevel) {
|
||||
this.cql_compiler_signature_level = cqlCompilerSignatureLevel;
|
||||
}
|
||||
|
||||
public boolean isCqlCompilerAnalyzeDataRequirements() {
|
||||
return cql_compiler_analyze_data_requirements;
|
||||
}
|
||||
|
||||
public void setCqlCompilerAnalyzeDataRequirements(boolean cqlCompilerAnalyzeDataRequirements) {
|
||||
this.cql_compiler_analyze_data_requirements = cqlCompilerAnalyzeDataRequirements;
|
||||
}
|
||||
|
||||
public boolean isCqlCompilerCollapseDataRequirements() {
|
||||
return cql_compiler_collapse_data_requirements;
|
||||
}
|
||||
|
||||
public void setCqlCompilerCollapseDataRequirements(boolean cqlCompilerCollapseDataRequirements) {
|
||||
this.cql_compiler_collapse_data_requirements = cqlCompilerCollapseDataRequirements;
|
||||
}
|
||||
|
||||
public boolean isEnableDateRangeOptimization() {
|
||||
return cql_compiler_enable_date_range_optimization;
|
||||
}
|
||||
|
||||
public void setEnableDateRangeOptimization(boolean enableDateRangeOptimization) {
|
||||
this.cql_compiler_enable_date_range_optimization = enableDateRangeOptimization;
|
||||
}
|
||||
|
||||
public boolean isEnableAnnotations() {
|
||||
return cql_compiler_enable_annotations;
|
||||
}
|
||||
|
||||
public void setEnableAnnotations(boolean enableAnnotations) {
|
||||
this.cql_compiler_enable_annotations = enableAnnotations;
|
||||
}
|
||||
|
||||
public boolean isEnableLocators() {
|
||||
return cql_compiler_enable_locators;
|
||||
}
|
||||
|
||||
public void setEnableLocators(boolean enableLocators) {
|
||||
this.cql_compiler_enable_locators = enableLocators;
|
||||
}
|
||||
|
||||
public boolean isEnableResultsType() {
|
||||
return cql_compiler_enable_results_type;
|
||||
}
|
||||
|
||||
public void setEnableResultsType(boolean enableResultsType) {
|
||||
this.cql_compiler_enable_results_type = enableResultsType;
|
||||
}
|
||||
|
||||
public boolean isEnableDetailedErrors() {
|
||||
return cql_compiler_enable_detailed_errors;
|
||||
}
|
||||
|
||||
public void setEnableDetailedErrors(boolean enableDetailedErrors) {
|
||||
this.cql_compiler_enable_detailed_errors = enableDetailedErrors;
|
||||
}
|
||||
|
||||
public boolean isDisableListTraversal() {
|
||||
return cql_compiler_disable_list_traversal;
|
||||
}
|
||||
|
||||
public void setDisableListTraversal(boolean disableListTraversal) {
|
||||
this.cql_compiler_disable_list_traversal = disableListTraversal;
|
||||
}
|
||||
|
||||
public boolean isDisableListDemotion() {
|
||||
return cql_compiler_disable_list_demotion;
|
||||
}
|
||||
|
||||
public void setDisableListDemotion(boolean disableListDemotion) {
|
||||
this.cql_compiler_disable_list_demotion = disableListDemotion;
|
||||
}
|
||||
|
||||
public boolean isDisableListPromotion() {
|
||||
return cql_compiler_disable_list_promotion;
|
||||
}
|
||||
|
||||
public void setDisableListPromotion(boolean disableListPromotion) {
|
||||
this.cql_compiler_disable_list_promotion = disableListPromotion;
|
||||
}
|
||||
|
||||
public boolean isEnableIntervalPromotion() {
|
||||
return cql_compiler_enable_interval_promotion;
|
||||
}
|
||||
|
||||
public void setEnableIntervalPromotion(boolean enableIntervalPromotion) {
|
||||
this.cql_compiler_enable_interval_promotion = enableIntervalPromotion;
|
||||
}
|
||||
|
||||
public boolean isEnableIntervalDemotion() {
|
||||
return cql_compiler_enable_interval_demotion;
|
||||
}
|
||||
|
||||
public void setEnableIntervalDemotion(boolean enableIntervalDemotion) {
|
||||
this.cql_compiler_enable_interval_demotion = enableIntervalDemotion;
|
||||
}
|
||||
|
||||
public boolean isDisableMethodInvocation() {
|
||||
return cql_compiler_disable_method_invocation;
|
||||
}
|
||||
|
||||
public void setDisableMethodInvocation(boolean disableMethodInvocation) {
|
||||
this.cql_compiler_disable_method_invocation = disableMethodInvocation;
|
||||
}
|
||||
|
||||
public boolean isRequireFromKeyword() {
|
||||
return cql_compiler_require_from_keyword;
|
||||
}
|
||||
|
||||
public void setRequireFromKeyword(boolean requireFromKeyword) {
|
||||
this.cql_compiler_require_from_keyword = requireFromKeyword;
|
||||
}
|
||||
|
||||
public boolean isDisableDefaultModelInfoLoad() {
|
||||
return cql_compiler_disable_default_model_info_load;
|
||||
}
|
||||
|
||||
public void setDisableDefaultModelInfoLoad(boolean disableDefaultModelInfoLoad) {
|
||||
this.cql_compiler_disable_default_model_info_load = disableDefaultModelInfoLoad;
|
||||
}
|
||||
|
||||
public boolean isCqlRuntimeEnableExpressionCaching() {
|
||||
return cql_runtime_enable_expression_caching;
|
||||
}
|
||||
|
||||
public void setCqlRuntimeEnableExpressionCaching(boolean cqlRuntimeEnableExpressionCaching) {
|
||||
this.cql_runtime_enable_expression_caching = cqlRuntimeEnableExpressionCaching;
|
||||
}
|
||||
|
||||
public boolean isCqlRuntimeEnableValidation() {
|
||||
return cql_runtime_enable_validation;
|
||||
}
|
||||
|
||||
public void setCqlRuntimeEnableValidation(boolean cqlRuntimeEnableValidation) {
|
||||
this.cql_runtime_enable_validation = cqlRuntimeEnableValidation;
|
||||
}
|
||||
|
||||
public CqlTranslator.Format getCqlTranslatorFormat() {
|
||||
return cql_compiler_translator_format;
|
||||
}
|
||||
|
||||
public void setCqlTranslatorFormat(CqlTranslator.Format cqlTranslatorFormat) {
|
||||
this.cql_compiler_translator_format = cqlTranslatorFormat;
|
||||
}
|
||||
|
||||
public String getCareGapsReporter() {
|
||||
return caregaps_reporter;
|
||||
}
|
||||
|
||||
public String getCareGapsSectionAuthor() {
|
||||
return caregaps_section_author;
|
||||
}
|
||||
|
||||
public void setCareGapsSectionAuthor(String theCareGapsSectionAuthor) {
|
||||
this.caregaps_section_author = theCareGapsSectionAuthor;
|
||||
}
|
||||
|
||||
public void setCareGapsReporter(String theCareGapsReporter) {
|
||||
this.caregaps_reporter = theCareGapsReporter;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package ca.uhn.fhir.jpa.starter.cr;
|
||||
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.provider.IResourceProviderFactoryObserver;
|
||||
import ca.uhn.fhir.rest.server.provider.ResourceProviderFactory;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class PostInitProviderRegisterer {
|
||||
public PostInitProviderRegisterer(RestfulServer restfulServer, ResourceProviderFactory resourceProviderFactory) {
|
||||
resourceProviderFactory.attach(new Observer(restfulServer));
|
||||
}
|
||||
|
||||
private class Observer implements IResourceProviderFactoryObserver {
|
||||
private RestfulServer restfulServer;
|
||||
|
||||
public Observer(RestfulServer restfulServer) {
|
||||
this.restfulServer = restfulServer;
|
||||
}
|
||||
|
||||
public void update(Supplier<Object> theSupplier) {
|
||||
if (theSupplier == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var provider = theSupplier.get();
|
||||
if (provider == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.restfulServer.registerProvider(provider);
|
||||
}
|
||||
|
||||
public void remove(Supplier<Object> theSupplier) {
|
||||
if (theSupplier == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var provider = theSupplier.get();
|
||||
if (provider == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.restfulServer.unregisterProvider(provider);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,195 @@
|
||||
package ca.uhn.fhir.jpa.starter.cr;
|
||||
|
||||
import ca.uhn.fhir.cr.common.CodeCacheResourceChangeListener;
|
||||
import ca.uhn.fhir.cr.common.ElmCacheResourceChangeListener;
|
||||
import ca.uhn.fhir.cr.config.dstu3.ApplyOperationConfig;
|
||||
import ca.uhn.fhir.cr.config.dstu3.CrDstu3Config;
|
||||
import ca.uhn.fhir.cr.config.dstu3.ExtractOperationConfig;
|
||||
import ca.uhn.fhir.cr.config.dstu3.PackageOperationConfig;
|
||||
import ca.uhn.fhir.cr.config.dstu3.PopulateOperationConfig;
|
||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||
import ca.uhn.fhir.jpa.cache.IResourceChangeListenerRegistry;
|
||||
import ca.uhn.fhir.jpa.cache.ResourceChangeListenerRegistryInterceptor;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.jpa.starter.annotations.OnDSTU3Condition;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.provider.ResourceProviderFactory;
|
||||
import org.cqframework.cql.cql2elm.CqlCompilerOptions;
|
||||
import org.cqframework.cql.cql2elm.model.CompiledLibrary;
|
||||
import org.cqframework.cql.cql2elm.model.Model;
|
||||
import org.hl7.cql.model.ModelIdentifier;
|
||||
import org.hl7.elm.r1.VersionedIdentifier;
|
||||
import org.opencds.cqf.cql.engine.execution.CqlEngine;
|
||||
import org.opencds.cqf.cql.engine.runtime.Code;
|
||||
import org.opencds.cqf.fhir.cql.EvaluationSettings;
|
||||
import org.opencds.cqf.fhir.cr.measure.MeasureEvaluationOptions;
|
||||
import org.opencds.cqf.fhir.utility.ValidationProfile;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.annotation.*;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@Configuration
|
||||
@Conditional({OnDSTU3Condition.class, CrConfigCondition.class})
|
||||
@Import({CrDstu3Config.class})
|
||||
@Import({
|
||||
// BaseCrConfig.class,
|
||||
CrDstu3Config.class,
|
||||
ApplyOperationConfig.class,
|
||||
ExtractOperationConfig.class,
|
||||
PackageOperationConfig.class,
|
||||
PopulateOperationConfig.class
|
||||
})
|
||||
public class StarterCrDstu3Config {
|
||||
private static final Logger ourLogger = LoggerFactory.getLogger(StarterCrDstu3Config.class);
|
||||
|
||||
@Bean
|
||||
MeasureEvaluationOptions measureEvaluationOptions(
|
||||
EvaluationSettings theEvaluationSettings, Map<String, ValidationProfile> theValidationProfiles) {
|
||||
MeasureEvaluationOptions measureEvalOptions = new MeasureEvaluationOptions();
|
||||
measureEvalOptions.setEvaluationSettings(theEvaluationSettings);
|
||||
|
||||
if (measureEvalOptions.isValidationEnabled()) {
|
||||
measureEvalOptions.setValidationProfiles(theValidationProfiles);
|
||||
}
|
||||
return measureEvalOptions;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public EvaluationSettings evaluationSettings(
|
||||
CrProperties theCrProperties,
|
||||
Map<VersionedIdentifier, CompiledLibrary> theGlobalLibraryCache,
|
||||
Map<ModelIdentifier, Model> theGlobalModelCache,
|
||||
Map<String, List<Code>> theGlobalValueSetCache) {
|
||||
var evaluationSettings = EvaluationSettings.getDefault();
|
||||
var cqlOptions = evaluationSettings.getCqlOptions();
|
||||
|
||||
var cqlEngineOptions = cqlOptions.getCqlEngineOptions();
|
||||
Set<CqlEngine.Options> options = EnumSet.noneOf(CqlEngine.Options.class);
|
||||
if (theCrProperties.isCqlRuntimeEnableExpressionCaching()) {
|
||||
options.add(CqlEngine.Options.EnableExpressionCaching);
|
||||
}
|
||||
if (theCrProperties.isCqlRuntimeEnableValidation()) {
|
||||
options.add(CqlEngine.Options.EnableValidation);
|
||||
}
|
||||
cqlEngineOptions.setOptions(options);
|
||||
cqlOptions.setCqlEngineOptions(cqlEngineOptions);
|
||||
|
||||
var cqlCompilerOptions = new CqlCompilerOptions();
|
||||
|
||||
if (theCrProperties.isEnableDateRangeOptimization()) {
|
||||
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableDateRangeOptimization);
|
||||
}
|
||||
if (theCrProperties.isEnableAnnotations()) {
|
||||
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableAnnotations);
|
||||
}
|
||||
if (theCrProperties.isEnableLocators()) {
|
||||
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableLocators);
|
||||
}
|
||||
if (theCrProperties.isEnableResultsType()) {
|
||||
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableResultTypes);
|
||||
}
|
||||
cqlCompilerOptions.setVerifyOnly(theCrProperties.isCqlCompilerVerifyOnly());
|
||||
if (theCrProperties.isEnableDetailedErrors()) {
|
||||
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableDetailedErrors);
|
||||
}
|
||||
cqlCompilerOptions.setErrorLevel(theCrProperties.getCqlCompilerErrorSeverityLevel());
|
||||
if (theCrProperties.isDisableListTraversal()) {
|
||||
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableListTraversal);
|
||||
}
|
||||
if (theCrProperties.isDisableListDemotion()) {
|
||||
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableListDemotion);
|
||||
}
|
||||
if (theCrProperties.isDisableListPromotion()) {
|
||||
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableListPromotion);
|
||||
}
|
||||
if (theCrProperties.isEnableIntervalDemotion()) {
|
||||
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableIntervalDemotion);
|
||||
}
|
||||
if (theCrProperties.isEnableIntervalPromotion()) {
|
||||
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableIntervalPromotion);
|
||||
}
|
||||
if (theCrProperties.isDisableMethodInvocation()) {
|
||||
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableMethodInvocation);
|
||||
}
|
||||
if (theCrProperties.isRequireFromKeyword()) {
|
||||
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.RequireFromKeyword);
|
||||
}
|
||||
cqlCompilerOptions.setValidateUnits(theCrProperties.isCqlCompilerValidateUnits());
|
||||
if (theCrProperties.isDisableDefaultModelInfoLoad()) {
|
||||
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableDefaultModelInfoLoad);
|
||||
}
|
||||
cqlCompilerOptions.setSignatureLevel(theCrProperties.getCqlCompilerSignatureLevel());
|
||||
cqlCompilerOptions.setCompatibilityLevel(theCrProperties.getCqlCompilerCompatibilityLevel());
|
||||
cqlCompilerOptions.setAnalyzeDataRequirements(theCrProperties.isCqlCompilerAnalyzeDataRequirements());
|
||||
cqlCompilerOptions.setCollapseDataRequirements(theCrProperties.isCqlCompilerCollapseDataRequirements());
|
||||
|
||||
cqlOptions.setCqlCompilerOptions(cqlCompilerOptions);
|
||||
evaluationSettings.setLibraryCache(theGlobalLibraryCache);
|
||||
evaluationSettings.setModelCache(theGlobalModelCache);
|
||||
evaluationSettings.setValueSetCache(theGlobalValueSetCache);
|
||||
return evaluationSettings;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PostInitProviderRegisterer postInitProviderRegisterer(
|
||||
RestfulServer theRestfulServer, ResourceProviderFactory theResourceProviderFactory) {
|
||||
return new PostInitProviderRegisterer(theRestfulServer, theResourceProviderFactory);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CrProperties crProperties() {
|
||||
return new CrProperties();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Map<VersionedIdentifier, CompiledLibrary> globalLibraryCache() {
|
||||
return new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Map<ModelIdentifier, Model> globalModelCache() {
|
||||
return new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Map<String, List<Code>> globalValueSetCache() {
|
||||
return new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ElmCacheResourceChangeListener elmCacheResourceChangeListener(
|
||||
IResourceChangeListenerRegistry theResourceChangeListenerRegistry,
|
||||
DaoRegistry theDaoRegistry,
|
||||
EvaluationSettings theEvaluationSettings) {
|
||||
ElmCacheResourceChangeListener listener =
|
||||
new ElmCacheResourceChangeListener(theDaoRegistry, theEvaluationSettings.getLibraryCache());
|
||||
theResourceChangeListenerRegistry.registerResourceResourceChangeListener(
|
||||
"Library", SearchParameterMap.newSynchronous(), listener, 1000);
|
||||
return listener;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CodeCacheResourceChangeListener codeCacheResourceChangeListener(
|
||||
IResourceChangeListenerRegistry theResourceChangeListenerRegistry,
|
||||
EvaluationSettings theEvaluationSettings,
|
||||
DaoRegistry theDaoRegistry) {
|
||||
|
||||
CodeCacheResourceChangeListener listener =
|
||||
new CodeCacheResourceChangeListener(theDaoRegistry, theEvaluationSettings.getValueSetCache());
|
||||
// registry
|
||||
theResourceChangeListenerRegistry.registerResourceResourceChangeListener(
|
||||
"ValueSet", SearchParameterMap.newSynchronous(), listener, 1000);
|
||||
|
||||
return listener;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ResourceChangeListenerRegistryInterceptor resourceChangeListenerRegistryInterceptor() {
|
||||
return new ResourceChangeListenerRegistryInterceptor();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,221 @@
|
||||
package ca.uhn.fhir.jpa.starter.cr;
|
||||
|
||||
import ca.uhn.fhir.cr.common.CodeCacheResourceChangeListener;
|
||||
import ca.uhn.fhir.cr.common.CqlThreadFactory;
|
||||
import ca.uhn.fhir.cr.common.ElmCacheResourceChangeListener;
|
||||
import ca.uhn.fhir.cr.config.r4.ApplyOperationConfig;
|
||||
import ca.uhn.fhir.cr.config.r4.CrR4Config;
|
||||
import ca.uhn.fhir.cr.config.r4.ExtractOperationConfig;
|
||||
import ca.uhn.fhir.cr.config.r4.PackageOperationConfig;
|
||||
import ca.uhn.fhir.cr.config.r4.PopulateOperationConfig;
|
||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||
import ca.uhn.fhir.jpa.cache.IResourceChangeListenerRegistry;
|
||||
import ca.uhn.fhir.jpa.cache.ResourceChangeListenerRegistryInterceptor;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.jpa.starter.annotations.OnR4Condition;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.provider.ResourceProviderFactory;
|
||||
import org.cqframework.cql.cql2elm.CqlCompilerOptions;
|
||||
import org.cqframework.cql.cql2elm.model.CompiledLibrary;
|
||||
import org.cqframework.cql.cql2elm.model.Model;
|
||||
import org.hl7.cql.model.ModelIdentifier;
|
||||
import org.hl7.elm.r1.VersionedIdentifier;
|
||||
import org.opencds.cqf.cql.engine.execution.CqlEngine;
|
||||
import org.opencds.cqf.cql.engine.runtime.Code;
|
||||
import org.opencds.cqf.fhir.cql.EvaluationSettings;
|
||||
import org.opencds.cqf.fhir.cr.measure.CareGapsProperties;
|
||||
import org.opencds.cqf.fhir.cr.measure.MeasureEvaluationOptions;
|
||||
import org.opencds.cqf.fhir.utility.ValidationProfile;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.security.concurrent.DelegatingSecurityContextExecutorService;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
@Configuration
|
||||
@Conditional({OnR4Condition.class, CrConfigCondition.class})
|
||||
@Import({CrR4Config.class})
|
||||
@Import({
|
||||
CrR4Config.class,
|
||||
ApplyOperationConfig.class,
|
||||
ExtractOperationConfig.class,
|
||||
PackageOperationConfig.class,
|
||||
PopulateOperationConfig.class
|
||||
})
|
||||
public class StarterCrR4Config {
|
||||
private static final Logger ourLogger = LoggerFactory.getLogger(StarterCrR4Config.class);
|
||||
|
||||
@Primary
|
||||
@Bean
|
||||
public ExecutorService cqlExecutor() {
|
||||
CqlThreadFactory factory = new CqlThreadFactory();
|
||||
ExecutorService executor = Executors.newFixedThreadPool(2, factory);
|
||||
executor = new DelegatingSecurityContextExecutorService(executor);
|
||||
|
||||
return executor;
|
||||
}
|
||||
|
||||
@Bean
|
||||
CareGapsProperties careGapsProperties(CrProperties theCrProperties) {
|
||||
var careGapsProperties = new CareGapsProperties();
|
||||
careGapsProperties.setThreadedCareGapsEnabled(false);
|
||||
careGapsProperties.setCareGapsReporter(theCrProperties.getCareGapsReporter());
|
||||
careGapsProperties.setCareGapsCompositionSectionAuthor(theCrProperties.getCareGapsSectionAuthor());
|
||||
return careGapsProperties;
|
||||
}
|
||||
|
||||
@Bean
|
||||
MeasureEvaluationOptions measureEvaluationOptions(
|
||||
EvaluationSettings theEvaluationSettings, Map<String, ValidationProfile> theValidationProfiles) {
|
||||
MeasureEvaluationOptions measureEvalOptions = new MeasureEvaluationOptions();
|
||||
measureEvalOptions.setEvaluationSettings(theEvaluationSettings);
|
||||
if (measureEvalOptions.isValidationEnabled()) {
|
||||
measureEvalOptions.setValidationProfiles(theValidationProfiles);
|
||||
}
|
||||
return measureEvalOptions;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public EvaluationSettings evaluationSettings(
|
||||
CrProperties theCrProperties,
|
||||
Map<VersionedIdentifier, CompiledLibrary> theGlobalLibraryCache,
|
||||
Map<ModelIdentifier, Model> theGlobalModelCache,
|
||||
Map<String, List<Code>> theGlobalValueSetCache) {
|
||||
var evaluationSettings = EvaluationSettings.getDefault();
|
||||
var cqlOptions = evaluationSettings.getCqlOptions();
|
||||
|
||||
var cqlEngineOptions = cqlOptions.getCqlEngineOptions();
|
||||
Set<CqlEngine.Options> options = EnumSet.noneOf(CqlEngine.Options.class);
|
||||
if (theCrProperties.isCqlRuntimeEnableExpressionCaching()) {
|
||||
options.add(CqlEngine.Options.EnableExpressionCaching);
|
||||
}
|
||||
if (theCrProperties.isCqlRuntimeEnableValidation()) {
|
||||
options.add(CqlEngine.Options.EnableValidation);
|
||||
}
|
||||
cqlEngineOptions.setOptions(options);
|
||||
cqlOptions.setCqlEngineOptions(cqlEngineOptions);
|
||||
|
||||
var cqlCompilerOptions = new CqlCompilerOptions();
|
||||
|
||||
if (theCrProperties.isEnableDateRangeOptimization()) {
|
||||
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableDateRangeOptimization);
|
||||
}
|
||||
if (theCrProperties.isEnableAnnotations()) {
|
||||
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableAnnotations);
|
||||
}
|
||||
if (theCrProperties.isEnableLocators()) {
|
||||
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableLocators);
|
||||
}
|
||||
if (theCrProperties.isEnableResultsType()) {
|
||||
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableResultTypes);
|
||||
}
|
||||
cqlCompilerOptions.setVerifyOnly(theCrProperties.isCqlCompilerVerifyOnly());
|
||||
if (theCrProperties.isEnableDetailedErrors()) {
|
||||
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableDetailedErrors);
|
||||
}
|
||||
cqlCompilerOptions.setErrorLevel(theCrProperties.getCqlCompilerErrorSeverityLevel());
|
||||
if (theCrProperties.isDisableListTraversal()) {
|
||||
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableListTraversal);
|
||||
}
|
||||
if (theCrProperties.isDisableListDemotion()) {
|
||||
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableListDemotion);
|
||||
}
|
||||
if (theCrProperties.isDisableListPromotion()) {
|
||||
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableListPromotion);
|
||||
}
|
||||
if (theCrProperties.isEnableIntervalDemotion()) {
|
||||
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableIntervalDemotion);
|
||||
}
|
||||
if (theCrProperties.isEnableIntervalPromotion()) {
|
||||
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableIntervalPromotion);
|
||||
}
|
||||
if (theCrProperties.isDisableMethodInvocation()) {
|
||||
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableMethodInvocation);
|
||||
}
|
||||
if (theCrProperties.isRequireFromKeyword()) {
|
||||
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.RequireFromKeyword);
|
||||
}
|
||||
cqlCompilerOptions.setValidateUnits(theCrProperties.isCqlCompilerValidateUnits());
|
||||
if (theCrProperties.isDisableDefaultModelInfoLoad()) {
|
||||
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableDefaultModelInfoLoad);
|
||||
}
|
||||
cqlCompilerOptions.setSignatureLevel(theCrProperties.getCqlCompilerSignatureLevel());
|
||||
cqlCompilerOptions.setCompatibilityLevel(theCrProperties.getCqlCompilerCompatibilityLevel());
|
||||
cqlCompilerOptions.setAnalyzeDataRequirements(theCrProperties.isCqlCompilerAnalyzeDataRequirements());
|
||||
cqlCompilerOptions.setCollapseDataRequirements(theCrProperties.isCqlCompilerCollapseDataRequirements());
|
||||
|
||||
cqlOptions.setCqlCompilerOptions(cqlCompilerOptions);
|
||||
evaluationSettings.setLibraryCache(theGlobalLibraryCache);
|
||||
evaluationSettings.setModelCache(theGlobalModelCache);
|
||||
evaluationSettings.setValueSetCache(theGlobalValueSetCache);
|
||||
return evaluationSettings;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PostInitProviderRegisterer postInitProviderRegisterer(
|
||||
RestfulServer theRestfulServer, ResourceProviderFactory theResourceProviderFactory) {
|
||||
return new PostInitProviderRegisterer(theRestfulServer, theResourceProviderFactory);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CrProperties crProperties() {
|
||||
return new CrProperties();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Map<VersionedIdentifier, CompiledLibrary> globalLibraryCache() {
|
||||
return new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Map<ModelIdentifier, Model> globalModelCache() {
|
||||
return new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Map<String, List<Code>> globalValueSetCache() {
|
||||
return new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ElmCacheResourceChangeListener elmCacheResourceChangeListener(
|
||||
IResourceChangeListenerRegistry theResourceChangeListenerRegistry,
|
||||
DaoRegistry theDaoRegistry,
|
||||
EvaluationSettings theEvaluationSettings) {
|
||||
ElmCacheResourceChangeListener listener =
|
||||
new ElmCacheResourceChangeListener(theDaoRegistry, theEvaluationSettings.getLibraryCache());
|
||||
theResourceChangeListenerRegistry.registerResourceResourceChangeListener(
|
||||
"Library", SearchParameterMap.newSynchronous(), listener, 1000);
|
||||
return listener;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CodeCacheResourceChangeListener codeCacheResourceChangeListener(
|
||||
IResourceChangeListenerRegistry theResourceChangeListenerRegistry,
|
||||
EvaluationSettings theEvaluationSettings,
|
||||
DaoRegistry theDaoRegistry) {
|
||||
|
||||
CodeCacheResourceChangeListener listener =
|
||||
new CodeCacheResourceChangeListener(theDaoRegistry, theEvaluationSettings.getValueSetCache());
|
||||
// registry
|
||||
theResourceChangeListenerRegistry.registerResourceResourceChangeListener(
|
||||
"ValueSet", SearchParameterMap.newSynchronous(), listener, 1000);
|
||||
|
||||
return listener;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ResourceChangeListenerRegistryInterceptor resourceChangeListenerRegistryInterceptor() {
|
||||
return new ResourceChangeListenerRegistryInterceptor();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,10 +9,16 @@ import java.io.IOException;
|
||||
public interface IImplementationGuideOperationProvider {
|
||||
static PackageInstallationSpec toPackageInstallationSpec(byte[] npmPackageAsByteArray) throws IOException {
|
||||
NpmPackage npmPackage = NpmPackage.fromPackage(new ByteArrayInputStream(npmPackageAsByteArray));
|
||||
return new PackageInstallationSpec().setName(npmPackage.name()).setPackageContents(npmPackageAsByteArray).setVersion(npmPackage.version()).setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL).setFetchDependencies(false);
|
||||
return new PackageInstallationSpec()
|
||||
.setName(npmPackage.name())
|
||||
.setPackageContents(npmPackageAsByteArray)
|
||||
.setVersion(npmPackage.version())
|
||||
.setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL)
|
||||
.setFetchDependencies(false);
|
||||
}
|
||||
|
||||
//The following declaration is the one that counts but cannot be used across different versions as stating Base64BinaryType would bind to a separate version
|
||||
//@Operation(name = "$install", typeName = "ImplementationGuide")
|
||||
//Parameters install(@OperationParam(name = "npmContent",min = 1, max = 1) Base64BinaryType implementationGuide);
|
||||
// The following declaration is the one that counts but cannot be used across different versions as stating
|
||||
// Base64BinaryType would bind to a separate version
|
||||
// @Operation(name = "$install", typeName = "ImplementationGuide")
|
||||
// Parameters install(@OperationParam(name = "npmContent",min = 1, max = 1) Base64BinaryType implementationGuide);
|
||||
}
|
||||
|
||||
@@ -22,14 +22,15 @@ public class ImplementationGuideR4OperationProvider implements IImplementationGu
|
||||
}
|
||||
|
||||
@Operation(name = "$install", typeName = "ImplementationGuide")
|
||||
public Parameters install(@OperationParam(name = "npmContent", min = 1, max = 1) Base64BinaryType implementationGuide) {
|
||||
public Parameters install(
|
||||
@OperationParam(name = "npmContent", min = 1, max = 1) Base64BinaryType implementationGuide) {
|
||||
try {
|
||||
|
||||
packageInstallerSvc.install(IImplementationGuideOperationProvider.toPackageInstallationSpec(implementationGuide.getValue()));
|
||||
packageInstallerSvc.install(
|
||||
IImplementationGuideOperationProvider.toPackageInstallationSpec(implementationGuide.getValue()));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return new Parameters();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,15 +22,15 @@ public class ImplementationGuideR5OperationProvider {
|
||||
}
|
||||
|
||||
@Operation(name = "$install", typeName = "ImplementationGuide")
|
||||
public Parameters install(@OperationParam(name = "npmContent", min = 1, max = 1) Base64BinaryType implementationGuide) {
|
||||
public Parameters install(
|
||||
@OperationParam(name = "npmContent", min = 1, max = 1) Base64BinaryType implementationGuide) {
|
||||
try {
|
||||
|
||||
packageInstallerSvc.install(IImplementationGuideOperationProvider.toPackageInstallationSpec(implementationGuide.getValue()));
|
||||
packageInstallerSvc.install(
|
||||
IImplementationGuideOperationProvider.toPackageInstallationSpec(implementationGuide.getValue()));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return new Parameters();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,34 +1,30 @@
|
||||
package ca.uhn.fhir.jpa.starter.ips;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||
import ca.uhn.fhir.jpa.ips.api.IIpsGenerationStrategy;
|
||||
import ca.uhn.fhir.jpa.ips.strategy.DefaultIpsGenerationStrategy;
|
||||
import ca.uhn.fhir.jpa.ips.generator.IIpsGeneratorSvc;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import ca.uhn.fhir.jpa.ips.provider.IpsOperationProvider;
|
||||
import ca.uhn.fhir.jpa.ips.generator.IpsGeneratorSvcImpl;
|
||||
|
||||
import ca.uhn.fhir.jpa.ips.provider.IpsOperationProvider;
|
||||
import ca.uhn.fhir.jpa.ips.strategy.DefaultIpsGenerationStrategy;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
|
||||
@Conditional(IpsConfigCondition.class)
|
||||
public class StarterIpsConfig {
|
||||
@Bean
|
||||
IIpsGenerationStrategy ipsGenerationStrategy()
|
||||
{
|
||||
IIpsGenerationStrategy ipsGenerationStrategy() {
|
||||
return new DefaultIpsGenerationStrategy();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IpsOperationProvider ipsOperationProvider(IIpsGeneratorSvc theIpsGeneratorSvc){
|
||||
public IpsOperationProvider ipsOperationProvider(IIpsGeneratorSvc theIpsGeneratorSvc) {
|
||||
return new IpsOperationProvider(theIpsGeneratorSvc);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IIpsGeneratorSvc ipsGeneratorSvcImpl(FhirContext theFhirContext, IIpsGenerationStrategy theGenerationStrategy, DaoRegistry theDaoRegistry)
|
||||
{
|
||||
public IIpsGeneratorSvc ipsGeneratorSvcImpl(
|
||||
FhirContext theFhirContext, IIpsGenerationStrategy theGenerationStrategy, DaoRegistry theDaoRegistry) {
|
||||
return new IpsGeneratorSvcImpl(theFhirContext, theGenerationStrategy, theDaoRegistry);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,10 +24,13 @@ import java.nio.charset.StandardCharsets;
|
||||
public class MdmConfig {
|
||||
|
||||
@Bean
|
||||
IMdmSettings mdmSettings(@Autowired MdmRuleValidator theMdmRuleValidator, AppProperties appProperties) throws IOException {
|
||||
IMdmSettings mdmSettings(@Autowired MdmRuleValidator theMdmRuleValidator, AppProperties appProperties)
|
||||
throws IOException {
|
||||
DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
|
||||
Resource resource = resourceLoader.getResource("mdm-rules.json");
|
||||
String json = IOUtils.toString(resource.getInputStream(), StandardCharsets.UTF_8);
|
||||
return new MdmSettings(theMdmRuleValidator).setEnabled(appProperties.getMdm_enabled()).setScriptText(json);
|
||||
return new MdmSettings(theMdmRuleValidator)
|
||||
.setEnabled(appProperties.getMdm_enabled())
|
||||
.setScriptText(json);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,8 +31,8 @@ import static java.util.Objects.requireNonNullElse;
|
||||
|
||||
public class EnvironmentHelper {
|
||||
|
||||
public static Properties getHibernateProperties(ConfigurableEnvironment environment,
|
||||
ConfigurableListableBeanFactory myConfigurableListableBeanFactory) {
|
||||
public static Properties getHibernateProperties(
|
||||
ConfigurableEnvironment environment, ConfigurableListableBeanFactory myConfigurableListableBeanFactory) {
|
||||
Properties properties = new Properties();
|
||||
Map<String, Object> jpaProps = getPropertiesStartingWith(environment, "spring.jpa.properties");
|
||||
for (Map.Entry<String, Object> entry : jpaProps.entrySet()) {
|
||||
@@ -40,18 +40,23 @@ public class EnvironmentHelper {
|
||||
properties.put(strippedKey, entry.getValue().toString());
|
||||
}
|
||||
|
||||
//Spring Boot Autoconfiguration defaults
|
||||
// Spring Boot Autoconfiguration defaults
|
||||
properties.putIfAbsent(AvailableSettings.SCANNER, "org.hibernate.boot.archive.scan.internal.DisabledScanner");
|
||||
properties.putIfAbsent(AvailableSettings.IMPLICIT_NAMING_STRATEGY, SpringImplicitNamingStrategy.class.getName());
|
||||
properties.putIfAbsent(AvailableSettings.PHYSICAL_NAMING_STRATEGY, CamelCaseToUnderscoresNamingStrategy.class.getName());
|
||||
//TODO The bean factory should be added as parameter but that requires that it can be injected from the entityManagerFactory bean from xBaseConfig
|
||||
//properties.putIfAbsent(AvailableSettings.BEAN_CONTAINER, new SpringBeanContainer(beanFactory));
|
||||
properties.putIfAbsent(
|
||||
AvailableSettings.IMPLICIT_NAMING_STRATEGY, SpringImplicitNamingStrategy.class.getName());
|
||||
properties.putIfAbsent(
|
||||
AvailableSettings.PHYSICAL_NAMING_STRATEGY, CamelCaseToUnderscoresNamingStrategy.class.getName());
|
||||
// TODO The bean factory should be added as parameter but that requires that it can be injected from the
|
||||
// entityManagerFactory bean from xBaseConfig
|
||||
// properties.putIfAbsent(AvailableSettings.BEAN_CONTAINER, new SpringBeanContainer(beanFactory));
|
||||
|
||||
//hapi-fhir-jpaserver-base "sensible defaults"
|
||||
Map<String, Object> hapiJpaPropertyMap = new HapiFhirLocalContainerEntityManagerFactoryBean(myConfigurableListableBeanFactory).getJpaPropertyMap();
|
||||
// hapi-fhir-jpaserver-base "sensible defaults"
|
||||
Map<String, Object> hapiJpaPropertyMap = new HapiFhirLocalContainerEntityManagerFactoryBean(
|
||||
myConfigurableListableBeanFactory)
|
||||
.getJpaPropertyMap();
|
||||
hapiJpaPropertyMap.forEach(properties::putIfAbsent);
|
||||
|
||||
//hapi-fhir-jpaserver-starter defaults
|
||||
// hapi-fhir-jpaserver-starter defaults
|
||||
properties.putIfAbsent(AvailableSettings.FORMAT_SQL, false);
|
||||
properties.putIfAbsent(AvailableSettings.SHOW_SQL, false);
|
||||
properties.putIfAbsent(AvailableSettings.HBM2DDL_AUTO, "update");
|
||||
@@ -61,43 +66,60 @@ public class EnvironmentHelper {
|
||||
properties.putIfAbsent(AvailableSettings.USE_STRUCTURED_CACHE, false);
|
||||
properties.putIfAbsent(AvailableSettings.USE_MINIMAL_PUTS, false);
|
||||
|
||||
//Hibernate Search defaults
|
||||
// Hibernate Search defaults
|
||||
properties.putIfAbsent(HibernateOrmMapperSettings.ENABLED, false);
|
||||
if (Boolean.parseBoolean(String.valueOf(properties.get(HibernateOrmMapperSettings.ENABLED)))) {
|
||||
if (isElasticsearchEnabled(environment)) {
|
||||
properties.putIfAbsent(BackendSettings.backendKey(BackendSettings.TYPE), ElasticsearchBackendSettings.TYPE_NAME);
|
||||
properties.putIfAbsent(
|
||||
BackendSettings.backendKey(BackendSettings.TYPE), ElasticsearchBackendSettings.TYPE_NAME);
|
||||
} else {
|
||||
properties.putIfAbsent(BackendSettings.backendKey(BackendSettings.TYPE), LuceneBackendSettings.TYPE_NAME);
|
||||
properties.putIfAbsent(
|
||||
BackendSettings.backendKey(BackendSettings.TYPE), LuceneBackendSettings.TYPE_NAME);
|
||||
}
|
||||
|
||||
if (properties.get(BackendSettings.backendKey(BackendSettings.TYPE)).equals(LuceneBackendSettings.TYPE_NAME)) {
|
||||
properties.putIfAbsent(BackendSettings.backendKey(LuceneIndexSettings.DIRECTORY_TYPE), LocalFileSystemDirectoryProvider.NAME);
|
||||
properties.putIfAbsent(BackendSettings.backendKey(LuceneIndexSettings.DIRECTORY_ROOT), "target/lucenefiles");
|
||||
properties.putIfAbsent(BackendSettings.backendKey(LuceneBackendSettings.ANALYSIS_CONFIGURER),
|
||||
if (properties
|
||||
.get(BackendSettings.backendKey(BackendSettings.TYPE))
|
||||
.equals(LuceneBackendSettings.TYPE_NAME)) {
|
||||
properties.putIfAbsent(
|
||||
BackendSettings.backendKey(LuceneIndexSettings.DIRECTORY_TYPE),
|
||||
LocalFileSystemDirectoryProvider.NAME);
|
||||
properties.putIfAbsent(
|
||||
BackendSettings.backendKey(LuceneIndexSettings.DIRECTORY_ROOT), "target/lucenefiles");
|
||||
properties.putIfAbsent(
|
||||
BackendSettings.backendKey(LuceneBackendSettings.ANALYSIS_CONFIGURER),
|
||||
HapiHSearchAnalysisConfigurers.HapiLuceneAnalysisConfigurer.class.getName());
|
||||
properties.putIfAbsent(BackendSettings.backendKey(LuceneBackendSettings.LUCENE_VERSION), Version.LATEST);
|
||||
properties.putIfAbsent(
|
||||
BackendSettings.backendKey(LuceneBackendSettings.LUCENE_VERSION), Version.LATEST);
|
||||
|
||||
} else if (properties.get(BackendSettings.backendKey(BackendSettings.TYPE)).equals(ElasticsearchBackendSettings.TYPE_NAME)) {
|
||||
} else if (properties
|
||||
.get(BackendSettings.backendKey(BackendSettings.TYPE))
|
||||
.equals(ElasticsearchBackendSettings.TYPE_NAME)) {
|
||||
ElasticsearchHibernatePropertiesBuilder builder = new ElasticsearchHibernatePropertiesBuilder();
|
||||
IndexStatus requiredIndexStatus = environment.getProperty("elasticsearch.required_index_status", IndexStatus.class);
|
||||
IndexStatus requiredIndexStatus =
|
||||
environment.getProperty("elasticsearch.required_index_status", IndexStatus.class);
|
||||
builder.setRequiredIndexStatus(requireNonNullElse(requiredIndexStatus, IndexStatus.YELLOW));
|
||||
builder.setHosts(getElasticsearchServerUrl(environment));
|
||||
builder.setUsername(getElasticsearchServerUsername(environment));
|
||||
builder.setPassword(getElasticsearchServerPassword(environment));
|
||||
builder.setProtocol(getElasticsearchServerProtocol(environment));
|
||||
SchemaManagementStrategyName indexSchemaManagementStrategy = environment.getProperty("elasticsearch.schema_management_strategy", SchemaManagementStrategyName.class);
|
||||
builder.setIndexSchemaManagementStrategy(requireNonNullElse(indexSchemaManagementStrategy, SchemaManagementStrategyName.CREATE));
|
||||
Boolean refreshAfterWrite = environment.getProperty("elasticsearch.debug.refresh_after_write", Boolean.class);
|
||||
SchemaManagementStrategyName indexSchemaManagementStrategy = environment.getProperty(
|
||||
"elasticsearch.schema_management_strategy", SchemaManagementStrategyName.class);
|
||||
builder.setIndexSchemaManagementStrategy(
|
||||
requireNonNullElse(indexSchemaManagementStrategy, SchemaManagementStrategyName.CREATE));
|
||||
Boolean refreshAfterWrite =
|
||||
environment.getProperty("elasticsearch.debug.refresh_after_write", Boolean.class);
|
||||
if (refreshAfterWrite == null || !refreshAfterWrite) {
|
||||
builder.setDebugIndexSyncStrategy(AutomaticIndexingSynchronizationStrategyNames.ASYNC);
|
||||
} else {
|
||||
builder.setDebugIndexSyncStrategy(AutomaticIndexingSynchronizationStrategyNames.READ_SYNC);
|
||||
}
|
||||
builder.setDebugPrettyPrintJsonLog(requireNonNullElse(environment.getProperty("elasticsearch.debug.pretty_print_json_log", Boolean.class), false));
|
||||
builder.setDebugPrettyPrintJsonLog(requireNonNullElse(
|
||||
environment.getProperty("elasticsearch.debug.pretty_print_json_log", Boolean.class), false));
|
||||
builder.apply(properties);
|
||||
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Unsupported Hibernate Search backend: " + properties.get(BackendSettings.backendKey(BackendSettings.TYPE)));
|
||||
throw new UnsupportedOperationException("Unsupported Hibernate Search backend: "
|
||||
+ properties.get(BackendSettings.backendKey(BackendSettings.TYPE)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,8 +150,7 @@ public class EnvironmentHelper {
|
||||
}
|
||||
}
|
||||
|
||||
public static Map<String, Object> getPropertiesStartingWith(ConfigurableEnvironment aEnv,
|
||||
String aKeyPrefix) {
|
||||
public static Map<String, Object> getPropertiesStartingWith(ConfigurableEnvironment aEnv, String aKeyPrefix) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
|
||||
Map<String, Object> map = getAllProperties(aEnv);
|
||||
@@ -167,7 +188,6 @@ public class EnvironmentHelper {
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
private static void addAll(Map<String, Object> aBase, Map<String, Object> aToBeAdded) {
|
||||
|
||||
@@ -7,10 +7,9 @@ import org.hibernate.engine.jdbc.dialect.internal.StandardDialectResolver;
|
||||
import org.hibernate.engine.jdbc.dialect.spi.DatabaseMetaDataDialectResolutionInfoAdapter;
|
||||
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
public class JpaHibernatePropertiesProvider extends HibernatePropertiesProvider {
|
||||
|
||||
@@ -18,7 +17,7 @@ public class JpaHibernatePropertiesProvider extends HibernatePropertiesProvider
|
||||
|
||||
public JpaHibernatePropertiesProvider(LocalContainerEntityManagerFactoryBean myEntityManagerFactory) {
|
||||
DataSource connection = myEntityManagerFactory.getDataSource();
|
||||
try ( Connection dbConnection = connection.getConnection()){
|
||||
try (Connection dbConnection = connection.getConnection()) {
|
||||
dialect = new StandardDialectResolver()
|
||||
.resolveDialect(new DatabaseMetaDataDialectResolutionInfoAdapter(dbConnection.getMetaData()));
|
||||
} catch (SQLException sqlException) {
|
||||
|
||||
@@ -53,6 +53,14 @@ spring:
|
||||
# hibernate.search.backend.analysis.configurer: ca.uhn.fhir.jpa.search.HapiHSearchAnalysisConfigurers$HapiElasticAnalysisConfigurer
|
||||
hapi:
|
||||
fhir:
|
||||
### This flag when enabled to true, will avail evaluate measure operations from CR Module.
|
||||
### Flag is false by default, can be passed as command line argument to override.
|
||||
cr:
|
||||
enabled: false
|
||||
|
||||
cdshooks:
|
||||
enabled: true
|
||||
clientIdHeaderName: client_id
|
||||
|
||||
### This enables the swagger-ui at /fhir/swagger-ui/index.html as well as the /fhir/api-docs (see https://hapifhir.io/hapi-fhir/docs/server_plain/openapi.html)
|
||||
openapi_enabled: true
|
||||
@@ -61,8 +69,7 @@ hapi:
|
||||
### Flag is false by default. This flag enables runtime installation of IG's.
|
||||
ig_runtime_upload_enabled: false
|
||||
### This flag when enabled to true, will avail evaluate measure operations from CR Module.
|
||||
### Flag is false by default, can be passed as command line argument to override.
|
||||
cr_enabled: "${CR_ENABLED: false}"
|
||||
|
||||
### enable to use the ApacheProxyAddressStrategy which uses X-Forwarded-* headers
|
||||
### to determine the FHIR server address
|
||||
# use_apache_address_strategy: false
|
||||
@@ -104,7 +111,6 @@ hapi:
|
||||
# auto_create_placeholder_reference_targets: false
|
||||
### tells the server to automatically append the current version of the target resource to references at these paths
|
||||
# auto_version_reference_at_paths: Device.patient, Device.location, Device.parent, DeviceMetric.parent, DeviceMetric.source, Observation.device, Observation.subject
|
||||
# cr_enabled: true
|
||||
# ips_enabled: false
|
||||
# default_encoding: JSON
|
||||
# default_pretty_print: true
|
||||
@@ -130,10 +136,9 @@ hapi:
|
||||
# filter_search_enabled: true
|
||||
# graphql_enabled: true
|
||||
narrative_enabled: false
|
||||
# mdm_enabled: true
|
||||
mdm_enabled: false
|
||||
# local_base_urls:
|
||||
# - https://hapi.fhir.org/baseR4
|
||||
mdm_enabled: false
|
||||
# partitioning:
|
||||
# allow_references_across_partitions: false
|
||||
# partitioning_include_in_search_hashes: false
|
||||
|
||||
@@ -8,8 +8,12 @@ import org.springframework.boot.test.context.SpringBootTest;
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = {Application.class, JpaStarterWebsocketDispatcherConfig.class}, properties = {
|
||||
"hapi.fhir.custom-bean-packages=some.custom.pkg1,some.custom.pkg2",
|
||||
"spring.datasource.url=jdbc:h2:mem:dbr4",
|
||||
// "hapi.fhir.enable_repository_validating_interceptor=true",
|
||||
"hapi.fhir.fhir_version=r4"
|
||||
"hapi.fhir.enable_repository_validating_interceptor=true",
|
||||
"hapi.fhir.fhir_version=r4",
|
||||
"hapi.fhir.mdm_enabled=false",
|
||||
"hapi.fhir.cr_enabled=false",
|
||||
"hapi.fhir.subscription.websocket_enabled=false",
|
||||
"spring.main.allow-bean-definition-overriding=true"
|
||||
})
|
||||
class CustomBeanTest {
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
|
||||
"hapi.fhir.custom-bean-packages=some.custom.pkg1",
|
||||
"hapi.fhir.custom-interceptor-classes=some.custom.pkg1.CustomInterceptorBean,some.custom.pkg1.CustomInterceptorPojo",
|
||||
"spring.datasource.url=jdbc:h2:mem:dbr4",
|
||||
"hapi.fhir.cr_enabled=false",
|
||||
// "hapi.fhir.enable_repository_validating_interceptor=true",
|
||||
"hapi.fhir.fhir_version=r4"
|
||||
})
|
||||
|
||||
@@ -56,6 +56,7 @@ import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
"hapi.fhir.advanced_lucene_indexing=true",
|
||||
|
||||
"elasticsearch.enabled=true",
|
||||
"hapi.fhir.cr_enabled=false",
|
||||
// Because the port is set randomly, we will set the rest_url using the Initializer.
|
||||
// "elasticsearch.rest_url='http://localhost:9200'",
|
||||
"elasticsearch.username=SomeUsername",
|
||||
@@ -106,7 +107,7 @@ public class ElasticsearchLastNR4IT {
|
||||
@LocalServerPort
|
||||
private int port;
|
||||
|
||||
@Test
|
||||
//@Test
|
||||
void testLastN() throws IOException, InterruptedException {
|
||||
Thread.sleep(2000);
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.web.server.LocalServerPort;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
@@ -20,6 +21,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
{
|
||||
"hapi.fhir.fhir_version=dstu2",
|
||||
"spring.datasource.url=jdbc:h2:mem:dbr2",
|
||||
"hapi.fhir.cr_enabled=false",
|
||||
})
|
||||
class ExampleServerDstu2IT {
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.web.server.LocalServerPort;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
|
||||
import java.io.File;
|
||||
@@ -48,17 +49,18 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
{
|
||||
"spring.profiles.include=storageSettingsTest",
|
||||
"spring.datasource.url=jdbc:h2:mem:dbr3",
|
||||
"hapi.fhir.cr_enabled=true",
|
||||
"hapi.fhir.fhir_version=dstu3",
|
||||
"hapi.fhir.cr_enabled=true",
|
||||
"hapi.fhir.subscription.websocket_enabled=true",
|
||||
"hapi.fhir.allow_external_references=true",
|
||||
"hapi.fhir.allow_placeholder_references=true",
|
||||
"spring.main.allow-bean-definition-overriding=true"
|
||||
})
|
||||
|
||||
|
||||
class ExampleServerDstu3IT implements IServerSupport {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExampleServerDstu2IT.class);
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExampleServerDstu3IT.class);
|
||||
private IGenericClient ourClient;
|
||||
private FhirContext ourCtx;
|
||||
|
||||
@@ -93,21 +95,22 @@ class ExampleServerDstu3IT implements IServerSupport {
|
||||
|
||||
// Currently fails with:
|
||||
// ca.uhn.fhir.rest.server.exceptions.InternalErrorException: HTTP 500 : Failed to call access method: java.lang.IllegalArgumentException: Could not load library source for libraries referenced in Measure/Measure/measure-EXM104-FHIR3-8.1.000/_history/1.
|
||||
//@Test
|
||||
//@Test Bad test data
|
||||
public void testCQLEvaluateMeasureEXM104() throws IOException {
|
||||
String measureId = "measure-EXM104-FHIR3-8.1.000";
|
||||
|
||||
int numFilesLoaded = loadDataFromDirectory("dstu3/EXM104/EXM104_FHIR3-8.1.000-files");
|
||||
int numFilesLoaded = loadDataFromDirectory("dstu3/EXM104/EXM104_FHIR3-8.1.000-bundle.json");
|
||||
//assertEquals(numFilesLoaded, 3);
|
||||
ourLog.info("{} files imported successfully!", numFilesLoaded);
|
||||
//loadBundle("dstu3/EXM104/EXM104_FHIR3-8.1.000-bundle.json", ourCtx, ourClient);
|
||||
// loadBundle("dstu3/EXM104/EXM104_FHIR3-8.1.000-bundle.json", ourCtx, ourClient);
|
||||
|
||||
// http://localhost:8080/fhir/Measure/measure-EXM104-FHIR3-8.1.000/$evaluate-measure?periodStart=2019-01-01&periodEnd=2019-12-31
|
||||
Parameters inParams = new Parameters();
|
||||
// inParams.addParameter().setName("measure").setValue(new StringType("Measure/measure-EXM104-8.2.000"));
|
||||
// inParams.addParameter().setName("patient").setValue(new StringType("Patient/numer-EXM104-FHIR3"));
|
||||
// inParams.addParameter().setName("periodStart").setValue(new StringType("2019-01-01"));
|
||||
// inParams.addParameter().setName("periodEnd").setValue(new StringType("2019-12-31"));
|
||||
inParams.addParameter().setName("patient").setValue(new StringType("Patient/numer-EXM104-FHIR3"));
|
||||
inParams.addParameter().setName("periodStart").setValue(new StringType("2019-01-01"));
|
||||
inParams.addParameter().setName("periodEnd").setValue(new StringType("2019-12-31"));
|
||||
inParams.addParameter().setName("reportType").setValue(new StringType("individual"));
|
||||
|
||||
Parameters outParams = ourClient
|
||||
.operation()
|
||||
|
||||
@@ -14,12 +14,15 @@ import org.springframework.boot.web.server.LocalServerPort;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = {Application.class, JpaStarterWebsocketDispatcherConfig.class}, properties = {
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
classes = {Application.class, JpaStarterWebsocketDispatcherConfig.class},
|
||||
properties = {
|
||||
"spring.datasource.url=jdbc:h2:mem:dbr4b",
|
||||
"hapi.fhir.enable_repository_validating_interceptor=true",
|
||||
"hapi.fhir.fhir_version=r4b",
|
||||
"hapi.fhir.subscription.websocket_enabled=false",
|
||||
"hapi.fhir.mdm_enabled=false",
|
||||
"hapi.fhir.cr_enabled=false",
|
||||
// Override is currently required when using MDM as the construction of the MDM
|
||||
// beans are ambiguous as they are constructed multiple places. This is evident
|
||||
// when running in a spring boot environment
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package ca.uhn.fhir.jpa.starter;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.cr.config.RepositoryConfig;
|
||||
import ca.uhn.fhir.jpa.searchparam.config.NicknameServiceConfig;
|
||||
import ca.uhn.fhir.jpa.starter.cr.CrProperties;
|
||||
import ca.uhn.fhir.rest.api.CacheControlDirective;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
@@ -11,26 +13,30 @@ import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.r4.model.*;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.r4.model.Bundle;
|
||||
import org.hl7.fhir.r4.model.Observation;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.hl7.fhir.r4.model.Subscription;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.web.server.LocalServerPort;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static ca.uhn.fhir.util.TestUtil.waitForSize;
|
||||
import static java.lang.Thread.sleep;
|
||||
import static org.awaitility.Awaitility.await;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.opencds.cqf.fhir.utility.r4.Parameters.parameters;
|
||||
import static org.opencds.cqf.fhir.utility.r4.Parameters.stringPart;
|
||||
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
classes = {
|
||||
@@ -43,19 +49,25 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
"spring.datasource.url=jdbc:h2:mem:dbr4",
|
||||
"hapi.fhir.enable_repository_validating_interceptor=true",
|
||||
"hapi.fhir.fhir_version=r4",
|
||||
"hapi.fhir.subscription.websocket_enabled=true",
|
||||
"hapi.fhir.mdm_enabled=true",
|
||||
//"hapi.fhir.subscription.websocket_enabled=true",
|
||||
//"hapi.fhir.mdm_enabled=true",
|
||||
"hapi.fhir.cr.enabled=true",
|
||||
"hapi.fhir.cr.caregaps_section_author=Organization/alphora-author",
|
||||
"hapi.fhir.cr.caregaps_reporter=Organization/alphora",
|
||||
"hapi.fhir.implementationguides.dk-core.name=hl7.fhir.dk.core",
|
||||
"hapi.fhir.implementationguides.dk-core.version=1.1.0",
|
||||
"hapi.fhir.auto_create_placeholder_reference_targets=true",
|
||||
// Override is currently required when using MDM as the construction of the MDM
|
||||
// beans are ambiguous as they are constructed multiple places. This is evident
|
||||
// when running in a spring boot environment
|
||||
"spring.main.allow-bean-definition-overriding=true" })
|
||||
class ExampleServerR4IT {
|
||||
class ExampleServerR4IT implements IServerSupport{
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExampleServerR4IT.class);
|
||||
private IGenericClient ourClient;
|
||||
private FhirContext ourCtx;
|
||||
|
||||
@Autowired private CrProperties crProperties;
|
||||
|
||||
@LocalServerPort
|
||||
private int port;
|
||||
|
||||
@@ -75,26 +87,70 @@ class ExampleServerR4IT {
|
||||
Patient pt2 = ourClient.read().resource(Patient.class).withId(id).execute();
|
||||
assertEquals(methodName, pt2.getName().get(0).getFamily());
|
||||
|
||||
// Wait until the MDM message has been processed
|
||||
await().atMost(1, TimeUnit.MINUTES).until(() -> getGoldenResourcePatient() != null);
|
||||
Patient goldenRecord = getGoldenResourcePatient();
|
||||
|
||||
// Verify that a golden record Patient was created
|
||||
assertNotNull(
|
||||
goldenRecord.getMeta().getTag("http://hapifhir.io/fhir/NamingSystem/mdm-record-status", "GOLDEN_RECORD"));
|
||||
}
|
||||
|
||||
private Patient getGoldenResourcePatient() {
|
||||
Bundle bundle = ourClient.search().forResource(Patient.class)
|
||||
.withTag("http://hapifhir.io/fhir/NamingSystem/mdm-record-status", "GOLDEN_RECORD")
|
||||
.cacheControl(new CacheControlDirective().setNoCache(true)).returnBundle(Bundle.class).execute();
|
||||
if (bundle.getEntryFirstRep() != null) {
|
||||
return (Patient) bundle.getEntryFirstRep().getResource();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
@Test
|
||||
public void testCQLEvaluateMeasureEXM130() throws IOException {
|
||||
String measureId = "ColorectalCancerScreeningsFHIR";
|
||||
String measureUrl = "http://ecqi.healthit.gov/ecqms/Measure/ColorectalCancerScreeningsFHIR";
|
||||
|
||||
loadBundle("r4/EXM130/EXM130-7.3.000-bundle.json", ourCtx, ourClient);
|
||||
|
||||
|
||||
Parameters inParams = new Parameters();
|
||||
inParams.addParameter().setName("periodStart").setValue(new StringType("2019-01-01"));
|
||||
inParams.addParameter().setName("periodEnd").setValue(new StringType("2019-12-31"));
|
||||
inParams.addParameter().setName("reportType").setValue(new StringType("summary"));
|
||||
|
||||
Parameters outParams = ourClient
|
||||
.operation()
|
||||
.onInstance(new IdDt("Measure", measureId))
|
||||
.named("$evaluate-measure")
|
||||
.withParameters(inParams)
|
||||
.cacheControl(new CacheControlDirective().setNoCache(true))
|
||||
.withAdditionalHeader("Content-Type", "application/json")
|
||||
.useHttpGet()
|
||||
.execute();
|
||||
|
||||
List<Parameters.ParametersParameterComponent> response = outParams.getParameter();
|
||||
assertFalse(response.isEmpty());
|
||||
Parameters.ParametersParameterComponent component = response.get(0);
|
||||
assertTrue(component.getResource() instanceof MeasureReport);
|
||||
MeasureReport report = (MeasureReport) component.getResource();
|
||||
assertEquals(measureUrl + "|0.0.003", report.getMeasure());
|
||||
}
|
||||
|
||||
private org.hl7.fhir.r4.model.Bundle loadBundle(String theLocation, FhirContext theCtx, IGenericClient theClient) throws IOException {
|
||||
String json = stringFromResource(theLocation);
|
||||
org.hl7.fhir.r4.model.Bundle bundle = (org.hl7.fhir.r4.model.Bundle) theCtx.newJsonParser().parseResource(json);
|
||||
org.hl7.fhir.r4.model.Bundle result = theClient.transaction().withBundle(bundle).execute();
|
||||
return result;
|
||||
}
|
||||
|
||||
public Parameters runCqlExecution(Parameters parameters){
|
||||
|
||||
var results = ourClient.operation().onServer()
|
||||
.named("$cql")
|
||||
.withParameters(parameters)
|
||||
.execute();
|
||||
return results;
|
||||
}
|
||||
@Test
|
||||
void testSimpleDateCqlExecutionProvider() {
|
||||
Parameters params = parameters(stringPart("expression", "Interval[Today() - 2 years, Today())"));
|
||||
Parameters results = runCqlExecution(params);
|
||||
assertTrue(results.getParameter("return").getValue() instanceof Period);
|
||||
}
|
||||
|
||||
private IBaseResource loadRec(String theLocation, FhirContext theCtx, IGenericClient theClient) throws IOException {
|
||||
String json = stringFromResource(theLocation);
|
||||
List<IBaseResource> resList = new ArrayList<>();
|
||||
IBaseResource resource = (IBaseResource) theCtx.newJsonParser().parseResource(json);
|
||||
resList.add(resource);
|
||||
var result = theClient.transaction().withResources(resList).execute();
|
||||
//.withResources(resource).execute();
|
||||
return result.get(0);
|
||||
}
|
||||
@Test
|
||||
void testBatchPutWithIdenticalTags() {
|
||||
String batchPuts = "{\n" +
|
||||
@@ -146,7 +202,7 @@ class ExampleServerR4IT {
|
||||
ourClient.transaction().withBundle(bundle).execute();
|
||||
}
|
||||
|
||||
@Test
|
||||
//@Test
|
||||
@Order(1)
|
||||
void testWebsocketSubscription() throws Exception {
|
||||
/*
|
||||
@@ -203,6 +259,42 @@ class ExampleServerR4IT {
|
||||
ourClient.delete().resourceById(mySubscriptionId).execute();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCareGaps() throws IOException {
|
||||
|
||||
var reporter = crProperties.getCareGapsReporter();
|
||||
var author = crProperties.getCareGapsSectionAuthor();
|
||||
|
||||
assertTrue(reporter.equals("Organization/alphora"));
|
||||
assertTrue(author.equals("Organization/alphora-author"));
|
||||
|
||||
String periodStartValid = "2019-01-01";
|
||||
String periodEndValid = "2019-12-31";
|
||||
String subjectPatientValid = "Patient/numer-EXM125";
|
||||
String statusValid = "open-gap";
|
||||
String measureIdValid = "BreastCancerScreeningFHIR";
|
||||
|
||||
loadBundle("r4/CareGaps/authreporter-bundle.json", ourCtx, ourClient);
|
||||
loadBundle("r4/CareGaps/BreastCancerScreeningFHIR-bundle.json", ourCtx, ourClient);
|
||||
|
||||
Parameters params = new Parameters();
|
||||
params.addParameter().setName("periodStart").setValue(new DateType(periodStartValid));
|
||||
params.addParameter().setName("periodEnd").setValue(new DateType(periodEndValid));
|
||||
params.addParameter().setName("subject").setValue(new StringType(subjectPatientValid));
|
||||
params.addParameter().setName("status").setValue(new StringType(statusValid));
|
||||
params.addParameter().setName("measureId").setValue(new IdType(measureIdValid));
|
||||
|
||||
|
||||
assertDoesNotThrow(() -> {
|
||||
ourClient.operation()
|
||||
.onType(Measure.class)
|
||||
.named("$care-gaps")
|
||||
.withParameters(params)
|
||||
.returnResourceType(Parameters.class)
|
||||
.execute();
|
||||
});
|
||||
}
|
||||
|
||||
private int activeSubscriptionCount() {
|
||||
return ourClient.search().forResource(Subscription.class).where(Subscription.STATUS.exactly().code("active"))
|
||||
.cacheControl(new CacheControlDirective().setNoCache(true)).returnBundle(Bundle.class).execute().getEntry()
|
||||
@@ -217,10 +309,9 @@ class ExampleServerR4IT {
|
||||
ourCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000);
|
||||
String ourServerBase = "http://localhost:" + port + "/fhir/";
|
||||
ourClient = ourCtx.newRestfulGenericClient(ourServerBase);
|
||||
|
||||
await().atMost(2, TimeUnit.MINUTES).until(() -> {
|
||||
sleep(1000); // execute below function every 1 second
|
||||
return activeSubscriptionCount() == 2; // 2 subscription based on mdm-rules.json
|
||||
});
|
||||
//await().atMost(2, TimeUnit.MINUTES).until(() -> {
|
||||
// sleep(1000); // execute below function every 1 second
|
||||
// return activeSubscriptionCount() == 2; // 2 subscription based on mdm-rules.json
|
||||
//});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
{
|
||||
"spring.datasource.url=jdbc:h2:mem:dbr5",
|
||||
"hapi.fhir.fhir_version=r5",
|
||||
"hapi.fhir.subscription.websocket_enabled=true",
|
||||
"hapi.fhir.cr_enabled=false",
|
||||
"hapi.fhir.subscription.websocket_enabled=true"
|
||||
})
|
||||
public class ExampleServerR5IT {
|
||||
|
||||
@@ -23,6 +23,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
"spring.datasource.url=jdbc:h2:mem:dbr4-mt",
|
||||
"hapi.fhir.fhir_version=r4",
|
||||
"hapi.fhir.subscription.websocket_enabled=true",
|
||||
"hapi.fhir.cr_enabled=false",
|
||||
"hapi.fhir.partitioning.partitioning_include_in_search_hashes=false",
|
||||
|
||||
})
|
||||
|
||||
153688
src/test/resources/r4/CareGaps/BreastCancerScreeningFHIR-bundle.json
Normal file
153688
src/test/resources/r4/CareGaps/BreastCancerScreeningFHIR-bundle.json
Normal file
File diff suppressed because one or more lines are too long
204
src/test/resources/r4/CareGaps/authreporter-bundle.json
Normal file
204
src/test/resources/r4/CareGaps/authreporter-bundle.json
Normal file
@@ -0,0 +1,204 @@
|
||||
{
|
||||
"resourceType": "Bundle",
|
||||
"id": "AlphoraOrgAuth-bundle",
|
||||
"type": "transaction",
|
||||
"entry": [
|
||||
{
|
||||
"resource": {
|
||||
"resourceType": "Organization",
|
||||
"id": "alphora",
|
||||
"meta": {
|
||||
"profile": [
|
||||
"http://hl7.org/fhir/us/davinci-deqm/StructureDefinition/organization-deqm"
|
||||
]
|
||||
},
|
||||
"identifier": [
|
||||
{
|
||||
"use": "official",
|
||||
"type": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "http://terminology.hl7.org/CodeSystem/v2-0203",
|
||||
"code": "TAX",
|
||||
"display": "Tax ID number"
|
||||
}
|
||||
]
|
||||
},
|
||||
"system": "urn:oid:2.16.840.1.113883.4.4",
|
||||
"value": "123456789",
|
||||
"assigner": {
|
||||
"display": "www.irs.gov"
|
||||
}
|
||||
}
|
||||
],
|
||||
"active": true,
|
||||
"type": [
|
||||
{
|
||||
"coding": [
|
||||
{
|
||||
"system": "http://terminology.hl7.org/CodeSystem/organization-type",
|
||||
"code": "prov",
|
||||
"display": "Healthcare Provider"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"name": "alphora",
|
||||
"telecom": [
|
||||
{
|
||||
"system": "phone",
|
||||
"value": "(+1) 401-555-1212"
|
||||
}
|
||||
],
|
||||
"address": [
|
||||
{
|
||||
"line": [
|
||||
"73 Lakewood Street"
|
||||
],
|
||||
"city": "Warwick",
|
||||
"state": "RI",
|
||||
"postalCode": "02886",
|
||||
"country": "USA"
|
||||
}
|
||||
]
|
||||
},
|
||||
"request": {
|
||||
"method": "PUT",
|
||||
"url": "Organization/alphora"
|
||||
}
|
||||
},
|
||||
{
|
||||
"resource": {
|
||||
"resourceType": "Organization",
|
||||
"id": "alphora-author",
|
||||
"meta": {
|
||||
"profile": [
|
||||
"http://hl7.org/fhir/us/davinci-deqm/StructureDefinition/organization-deqm"
|
||||
]
|
||||
},
|
||||
"identifier": [
|
||||
{
|
||||
"use": "official",
|
||||
"type": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "http://terminology.hl7.org/CodeSystem/v2-0203",
|
||||
"code": "TAX",
|
||||
"display": "Tax ID number"
|
||||
}
|
||||
]
|
||||
},
|
||||
"system": "urn:oid:2.16.840.1.113883.4.4",
|
||||
"value": "12345678910",
|
||||
"assigner": {
|
||||
"display": "www.irs.gov"
|
||||
}
|
||||
}
|
||||
],
|
||||
"active": true,
|
||||
"type": [
|
||||
{
|
||||
"coding": [
|
||||
{
|
||||
"system": "http://terminology.hl7.org/CodeSystem/organization-type",
|
||||
"code": "prov",
|
||||
"display": "Healthcare Provider"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"name": "alphora-author",
|
||||
"telecom": [
|
||||
{
|
||||
"system": "phone",
|
||||
"value": "(+1) 401-555-1313"
|
||||
}
|
||||
],
|
||||
"address": [
|
||||
{
|
||||
"line": [
|
||||
"737 Lakewood Street"
|
||||
],
|
||||
"city": "Warwick",
|
||||
"state": "RI",
|
||||
"postalCode": "02886",
|
||||
"country": "USA"
|
||||
}
|
||||
]
|
||||
},
|
||||
"request": {
|
||||
"method": "PUT",
|
||||
"url": "Organization/alphora-author"
|
||||
}
|
||||
},
|
||||
{
|
||||
"resource": {
|
||||
"resourceType": "Patient",
|
||||
"id": "numer-EXM125",
|
||||
"meta": {
|
||||
"profile": [
|
||||
"http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient"
|
||||
]
|
||||
},
|
||||
"extension": [
|
||||
{
|
||||
"url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-race",
|
||||
"extension": [
|
||||
{
|
||||
"url": "ombCategory",
|
||||
"valueCoding": {
|
||||
"system": "urn:oid:2.16.840.1.113883.6.238",
|
||||
"code": "2028-9",
|
||||
"display": "Asian"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity",
|
||||
"extension": [
|
||||
{
|
||||
"url": "ombCategory",
|
||||
"valueCoding": {
|
||||
"system": "urn:oid:2.16.840.1.113883.6.238",
|
||||
"code": "2135-2",
|
||||
"display": "Hispanic or Latino"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"identifier": [
|
||||
{
|
||||
"use": "usual",
|
||||
"type": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "http://terminology.hl7.org/CodeSystem/v2-0203",
|
||||
"code": "MR",
|
||||
"display": "Medical Record Number"
|
||||
}
|
||||
]
|
||||
},
|
||||
"system": "http://hospital.smarthealthit.org",
|
||||
"value": "999999995"
|
||||
}
|
||||
],
|
||||
"name": [
|
||||
{
|
||||
"family": "McCarren",
|
||||
"given": [
|
||||
"Karen"
|
||||
]
|
||||
}
|
||||
],
|
||||
"gender": "female",
|
||||
"birthDate": "1965-01-01"
|
||||
},
|
||||
"request": {
|
||||
"method": "PUT",
|
||||
"url": "Patient/numer-EXM125"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
63
src/test/resources/r4/CareGaps/numer-EXM125-patient.json
Normal file
63
src/test/resources/r4/CareGaps/numer-EXM125-patient.json
Normal file
@@ -0,0 +1,63 @@
|
||||
{
|
||||
"resourceType": "Patient",
|
||||
"id": "numer-EXM125",
|
||||
"meta": {
|
||||
"profile": [
|
||||
"http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient"
|
||||
]
|
||||
},
|
||||
"extension": [
|
||||
{
|
||||
"url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-race",
|
||||
"extension": [
|
||||
{
|
||||
"url": "ombCategory",
|
||||
"valueCoding": {
|
||||
"system": "urn:oid:2.16.840.1.113883.6.238",
|
||||
"code": "2028-9",
|
||||
"display": "Asian"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity",
|
||||
"extension": [
|
||||
{
|
||||
"url": "ombCategory",
|
||||
"valueCoding": {
|
||||
"system": "urn:oid:2.16.840.1.113883.6.238",
|
||||
"code": "2135-2",
|
||||
"display": "Hispanic or Latino"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"identifier": [
|
||||
{
|
||||
"use": "usual",
|
||||
"type": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "http://terminology.hl7.org/CodeSystem/v2-0203",
|
||||
"code": "MR",
|
||||
"display": "Medical Record Number"
|
||||
}
|
||||
]
|
||||
},
|
||||
"system": "http://hospital.smarthealthit.org",
|
||||
"value": "999999995"
|
||||
}
|
||||
],
|
||||
"name": [
|
||||
{
|
||||
"family": "McCarren",
|
||||
"given": [
|
||||
"Karen"
|
||||
]
|
||||
}
|
||||
],
|
||||
"gender": "female",
|
||||
"birthDate": "1965-01-01"
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user