Merge branch 'master' into patch-3

This commit is contained in:
Jens Kristian Villadsen
2020-12-06 16:15:50 +01:00
committed by GitHub
27 changed files with 564 additions and 106 deletions

24
.github/workflows/maven.yml vendored Normal file
View File

@@ -0,0 +1,24 @@
# This workflow will build a Java project with Maven
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
name: Java CI with Maven
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 11
uses: actions/setup-java@v1
with:
java-version: 11
- name: Build with Maven
run: mvn -B package --file pom.xml

View File

@@ -42,15 +42,23 @@ docker run -p 8080:8080 -e hapi.fhir.default_encoding=xml hapiproject/hapi:lates
HAPI looks in the environment variables for properties in the [application.yaml](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/application.yaml) file for defaults. HAPI looks in the environment variables for properties in the [application.yaml](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/application.yaml) file for defaults.
### Configuration via overridden application.yaml file ### Configuration via overridden application.yaml file and using Docker
You can customize HAPI by telling HAPI to look for the configuration file in a different location, eg.: You can customize HAPI by telling HAPI to look for the configuration file in a different location, eg.:
```
docker run -p 8090:8080 -v $(pwd)/yourLocalFolder:/configs -e "--spring.config.location=file:///configs/another.application.yaml" hapiproject/hapi:latest
```
Here, the configuration file (*another.application.yaml*) is placed locally in the folder *yourLocalFolder*.
``` ```
docker run -p 8090:8080 -e "--spring.config.location=classpath:/another.application.yaml" hapiproject/hapi:latest docker run -p 8090:8080 -e "--spring.config.location=classpath:/another.application.yaml" hapiproject/hapi:latest
``` ```
Here, the configuration file (*another.application.yaml*) is part of the compiled set of resources.
### Example docker-compose.yml ### Example using docker-compose.yml for docker-compose
``` ```
version: '3.7' version: '3.7'
@@ -314,6 +322,10 @@ elasticsearch.required_index_status=YELLOW
elasticsearch.schema_management_strategy=CREATE elasticsearch.schema_management_strategy=CREATE
``` ```
## Enabling LastN
Set `hapi.fhir.lastn_enabled=true` in the [application.yaml](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/application.yaml) file to enable the $lastn operation on this server. Note that the $lastn operation relies on Elasticsearch, so for $lastn to work, indexing must be enabled using Elasticsearch.
## Example of a Dockerfile based on distroless images (for lower footprint and improved security) ## Example of a Dockerfile based on distroless images (for lower footprint and improved security)
```code ```code

View File

@@ -14,7 +14,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>5.1.0</version> <version>5.2.0</version>
</parent> </parent>
<artifactId>hapi-fhir-jpaserver-starter</artifactId> <artifactId>hapi-fhir-jpaserver-starter</artifactId>
@@ -177,37 +177,30 @@
<dependency> <dependency>
<groupId>org.webjars</groupId> <groupId>org.webjars</groupId>
<artifactId>Eonasdan-bootstrap-datetimepicker</artifactId> <artifactId>Eonasdan-bootstrap-datetimepicker</artifactId>
<version>4.17.43</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.webjars</groupId> <groupId>org.webjars</groupId>
<artifactId>font-awesome</artifactId> <artifactId>font-awesome</artifactId>
<version>5.8.2</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.webjars.bower</groupId> <groupId>org.webjars.bower</groupId>
<artifactId>awesome-bootstrap-checkbox</artifactId> <artifactId>awesome-bootstrap-checkbox</artifactId>
<version>1.0.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.webjars</groupId> <groupId>org.webjars</groupId>
<artifactId>jstimezonedetect</artifactId> <artifactId>jstimezonedetect</artifactId>
<version>1.0.6</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.webjars</groupId> <groupId>org.webjars</groupId>
<artifactId>select2</artifactId> <artifactId>select2</artifactId>
<version>4.0.3</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.webjars.bower</groupId> <groupId>org.webjars.bower</groupId>
<artifactId>jquery</artifactId> <artifactId>jquery</artifactId>
<version>3.3.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.webjars.bower</groupId> <groupId>org.webjars.bower</groupId>
<artifactId>moment</artifactId> <artifactId>moment</artifactId>
<version>2.15.1</version>
</dependency> </dependency>
<!-- The following dependencies are only needed for automated unit tests, you do not neccesarily need them to run the example. --> <!-- The following dependencies are only needed for automated unit tests, you do not neccesarily need them to run the example. -->

View File

@@ -7,8 +7,10 @@ import ca.uhn.fhir.rest.api.EncodingEnum;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.Bundle;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
@@ -52,14 +54,17 @@ public class AppProperties {
private ClientIdStrategyEnum client_id_strategy = ClientIdStrategyEnum.ALPHANUMERIC; private ClientIdStrategyEnum client_id_strategy = ClientIdStrategyEnum.ALPHANUMERIC;
private List<String> supported_resource_types = new ArrayList<>(); private List<String> supported_resource_types = new ArrayList<>();
private List<Bundle.BundleType> allowed_bundle_types = null; private List<Bundle.BundleType> allowed_bundle_types = null;
private Boolean narrative_enabled = true;
private Validation validation = new Validation(); private Validation validation = new Validation();
private List<Tester> tester = ImmutableList.of(new Tester()); private Map<String, Tester> tester = ImmutableMap.of("home", new Tester());
private Logger logger = new Logger(); private Logger logger = new Logger();
private Subscription subscription = new Subscription(); private Subscription subscription = new Subscription();
private Cors cors = null; private Cors cors = null;
private Partitioning partitioning = null; private Partitioning partitioning = null;
private List<ImplementationGuide> implementationGuides = null; private Map<String, ImplementationGuide> implementationGuides = null;
private Boolean lastn_enabled = false;
public Integer getDefer_indexing_for_codesystems_of_size() { public Integer getDefer_indexing_for_codesystems_of_size() {
return defer_indexing_for_codesystems_of_size; return defer_indexing_for_codesystems_of_size;
@@ -69,11 +74,11 @@ public class AppProperties {
this.defer_indexing_for_codesystems_of_size = defer_indexing_for_codesystems_of_size; this.defer_indexing_for_codesystems_of_size = defer_indexing_for_codesystems_of_size;
} }
public List<ImplementationGuide> getImplementationGuides() { public Map<String, ImplementationGuide> getImplementationGuides() {
return implementationGuides; return implementationGuides;
} }
public void setImplementationGuides(List<ImplementationGuide> implementationGuides) { public void setImplementationGuides(Map<String, ImplementationGuide> implementationGuides) {
this.implementationGuides = implementationGuides; this.implementationGuides = implementationGuides;
} }
@@ -363,14 +368,32 @@ public class AppProperties {
this.reuse_cached_search_results_millis = reuse_cached_search_results_millis; this.reuse_cached_search_results_millis = reuse_cached_search_results_millis;
} }
public List<Tester> getTester() { public Map<String, Tester> getTester() {
return tester; return tester;
} }
public void setTester(List<Tester> tester) { public void setTester(Map<String, Tester> tester) {
this.tester = tester; this.tester = tester;
} }
public Boolean getNarrative_enabled()
{
return narrative_enabled;
}
public void setNarrative_enabled(Boolean narrative_enabled)
{
this.narrative_enabled = narrative_enabled;
}
public Boolean getLastn_enabled() {
return lastn_enabled;
}
public void setLastn_enabled(Boolean lastn_enabled) {
this.lastn_enabled = lastn_enabled;
}
public static class Cors { public static class Cors {
private Boolean allow_Credentials = true; private Boolean allow_Credentials = true;
private List<String> allowed_origin = ImmutableList.of("*"); private List<String> allowed_origin = ImmutableList.of("*");
@@ -437,7 +460,6 @@ public class AppProperties {
public static class Tester { public static class Tester {
private String id = "home";
private String name = "Local Tester"; private String name = "Local Tester";
private String server_address = "http://localhost:8080/fhir"; private String server_address = "http://localhost:8080/fhir";
private Boolean refuse_to_fetch_third_party_urls = true; private Boolean refuse_to_fetch_third_party_urls = true;
@@ -451,14 +473,6 @@ public class AppProperties {
this.fhir_version = fhir_version; this.fhir_version = fhir_version;
} }
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() { public String getName() {
return name; return name;
} }

View File

@@ -1,6 +1,7 @@
package ca.uhn.fhir.jpa.starter; package ca.uhn.fhir.jpa.starter;
import ca.uhn.fhir.jpa.empi.EmpiConfig; import ca.uhn.fhir.jpa.empi.EmpiConfig;
import ca.uhn.fhir.jpa.starter.annotations.OnEitherVersion;
import ca.uhn.fhir.jpa.subscription.channel.config.SubscriptionChannelConfig; import ca.uhn.fhir.jpa.subscription.channel.config.SubscriptionChannelConfig;
import ca.uhn.fhir.jpa.subscription.match.config.SubscriptionProcessorConfig; import ca.uhn.fhir.jpa.subscription.match.config.SubscriptionProcessorConfig;
import ca.uhn.fhir.jpa.subscription.match.config.WebsocketDispatcherConfig; import ca.uhn.fhir.jpa.subscription.match.config.WebsocketDispatcherConfig;
@@ -15,6 +16,7 @@ import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.DispatcherServlet;
@@ -44,6 +46,7 @@ public class Application extends SpringBootServletInitializer {
AutowireCapableBeanFactory beanFactory; AutowireCapableBeanFactory beanFactory;
@Bean @Bean
@Conditional(OnEitherVersion.class)
public ServletRegistrationBean hapiServletRegistration() { public ServletRegistrationBean hapiServletRegistration() {
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(); ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean();
JpaRestfulServer jpaRestfulServer = new JpaRestfulServer(); JpaRestfulServer jpaRestfulServer = new JpaRestfulServer();

View File

@@ -13,11 +13,7 @@ import ca.uhn.fhir.jpa.interceptor.CascadingDeleteInterceptor;
import ca.uhn.fhir.jpa.packages.IPackageInstallerSvc; import ca.uhn.fhir.jpa.packages.IPackageInstallerSvc;
import ca.uhn.fhir.jpa.packages.PackageInstallationSpec; import ca.uhn.fhir.jpa.packages.PackageInstallationSpec;
import ca.uhn.fhir.jpa.partition.PartitionManagementProvider; import ca.uhn.fhir.jpa.partition.PartitionManagementProvider;
import ca.uhn.fhir.jpa.provider.GraphQLProvider; import ca.uhn.fhir.jpa.provider.*;
import ca.uhn.fhir.jpa.provider.IJpaSystemProvider;
import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu2;
import ca.uhn.fhir.jpa.provider.SubscriptionTriggeringProvider;
import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider;
import ca.uhn.fhir.jpa.provider.dstu3.JpaConformanceProviderDstu3; import ca.uhn.fhir.jpa.provider.dstu3.JpaConformanceProviderDstu3;
import ca.uhn.fhir.jpa.provider.r4.JpaConformanceProviderR4; import ca.uhn.fhir.jpa.provider.r4.JpaConformanceProviderR4;
import ca.uhn.fhir.jpa.provider.r5.JpaConformanceProviderR5; import ca.uhn.fhir.jpa.provider.r5.JpaConformanceProviderR5;
@@ -25,6 +21,8 @@ import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry; import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.jpa.subscription.util.SubscriptionDebugLogInterceptor; import ca.uhn.fhir.jpa.subscription.util.SubscriptionDebugLogInterceptor;
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator; import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
import ca.uhn.fhir.narrative.INarrativeGenerator;
import ca.uhn.fhir.narrative2.NullNarrativeGenerator;
import ca.uhn.fhir.rest.server.ETagSupportEnum; import ca.uhn.fhir.rest.server.ETagSupportEnum;
import ca.uhn.fhir.rest.server.HardcodedServerAddressStrategy; import ca.uhn.fhir.rest.server.HardcodedServerAddressStrategy;
import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.RestfulServer;
@@ -42,10 +40,7 @@ import org.springframework.http.HttpHeaders;
import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfiguration;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import java.util.Arrays; import java.util.*;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class BaseJpaRestfulServer extends RestfulServer { public class BaseJpaRestfulServer extends RestfulServer {
@@ -181,7 +176,11 @@ public class BaseJpaRestfulServer extends RestfulServer {
* This server tries to dynamically generate narratives * This server tries to dynamically generate narratives
*/ */
FhirContext ctx = getFhirContext(); FhirContext ctx = getFhirContext();
ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); INarrativeGenerator theNarrativeGenerator =
appProperties.getNarrative_enabled() ?
new DefaultThymeleafNarrativeGenerator() :
new NullNarrativeGenerator();
ctx.setNarrativeGenerator(theNarrativeGenerator);
/* /*
* Default to JSON and pretty printing * Default to JSON and pretty printing
@@ -349,15 +348,21 @@ public class BaseJpaRestfulServer extends RestfulServer {
} }
if (appProperties.getImplementationGuides() != null) { if (appProperties.getImplementationGuides() != null) {
List<AppProperties.ImplementationGuide> guides = appProperties.getImplementationGuides(); Map<String, AppProperties.ImplementationGuide> guides = appProperties.getImplementationGuides();
for (AppProperties.ImplementationGuide guide : guides) { for (Map.Entry<String, AppProperties.ImplementationGuide> guide : guides.entrySet()) {
packageInstallerSvc.install(new PackageInstallationSpec() packageInstallerSvc.install(new PackageInstallationSpec()
.setPackageUrl(guide.getUrl()) .setPackageUrl(guide.getValue().getUrl())
.setName(guide.getName()) .setName(guide.getValue().getName())
.setVersion(guide.getVersion()) .setVersion(guide.getValue().getVersion())
.setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL)); .setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL));
} }
} }
if (appProperties.getLastn_enabled()) {
daoConfig.setLastNEnabled(true);
}
} }

View File

@@ -1,5 +1,8 @@
package ca.uhn.fhir.jpa.starter; package ca.uhn.fhir.jpa.starter;
import ca.uhn.fhir.jpa.search.elastic.ElasticsearchHibernatePropertiesBuilder;
import org.hibernate.search.elasticsearch.cfg.ElasticsearchIndexStatus;
import org.hibernate.search.elasticsearch.cfg.IndexSchemaManagementStrategy;
import org.springframework.core.env.CompositePropertySource; import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.EnumerablePropertySource; import org.springframework.core.env.EnumerablePropertySource;
@@ -35,9 +38,66 @@ public class EnvironmentHelper {
properties.put(values[0], values[1]); properties.put(values[0], values[1]);
}); });
} }
if (environment.getProperty("elasticsearch.enabled", Boolean.class) != null
&& environment.getProperty("elasticsearch.enabled", Boolean.class) == true ){
ElasticsearchHibernatePropertiesBuilder builder = new ElasticsearchHibernatePropertiesBuilder();
ElasticsearchIndexStatus requiredIndexStatus = environment.getProperty("elasticsearch.required_index_status", ElasticsearchIndexStatus.class);
if (requiredIndexStatus == null) {
builder.setRequiredIndexStatus(ElasticsearchIndexStatus.YELLOW);
} else {
builder.setRequiredIndexStatus(requiredIndexStatus);
}
builder.setRestUrl(getElasticsearchServerUrl(environment));
builder.setUsername(getElasticsearchServerUsername(environment));
builder.setPassword(getElasticsearchServerPassword(environment));
IndexSchemaManagementStrategy indexSchemaManagementStrategy = environment.getProperty("elasticsearch.schema_management_strategy", IndexSchemaManagementStrategy.class);
if (indexSchemaManagementStrategy == null) {
builder.setIndexSchemaManagementStrategy(IndexSchemaManagementStrategy.CREATE);
} else {
builder.setIndexSchemaManagementStrategy(indexSchemaManagementStrategy);
}
// pretty_print_json_log: false
Boolean refreshAfterWrite = environment.getProperty("elasticsearch.debug.refresh_after_write", Boolean.class);
if (refreshAfterWrite == null) {
builder.setDebugRefreshAfterWrite(false);
} else {
builder.setDebugRefreshAfterWrite(refreshAfterWrite);
}
// pretty_print_json_log: false
Boolean prettyPrintJsonLog = environment.getProperty("elasticsearch.debug.pretty_print_json_log", Boolean.class);
if (prettyPrintJsonLog == null) {
builder.setDebugPrettyPrintJsonLog(false);
} else {
builder.setDebugPrettyPrintJsonLog(prettyPrintJsonLog);
}
builder.apply(properties);
}
return properties; return properties;
} }
public static String getElasticsearchServerUrl(ConfigurableEnvironment environment) {
return environment.getProperty("elasticsearch.rest_url", String.class);
}
public static String getElasticsearchServerUsername(ConfigurableEnvironment environment) {
return environment.getProperty("elasticsearch.username");
}
public static String getElasticsearchServerPassword(ConfigurableEnvironment environment) {
return environment.getProperty("elasticsearch.password");
}
public static Boolean isElasticsearchEnabled(ConfigurableEnvironment environment) {
if (environment.getProperty("elasticsearch.enabled", Boolean.class) != null) {
return environment.getProperty("elasticsearch.enabled", Boolean.class);
} else {
return false;
}
}
public static Map<String, Object> getPropertiesStartingWith(ConfigurableEnvironment aEnv, public static Map<String, Object> getPropertiesStartingWith(ConfigurableEnvironment aEnv,
String aKeyPrefix) { String aKeyPrefix) {
Map<String, Object> result = new HashMap<>(); Map<String, Object> result = new HashMap<>();

View File

@@ -3,6 +3,7 @@ package ca.uhn.fhir.jpa.starter;
import ca.uhn.fhir.jpa.api.config.DaoConfig; import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.binstore.DatabaseBlobBinaryStorageSvcImpl; import ca.uhn.fhir.jpa.binstore.DatabaseBlobBinaryStorageSvcImpl;
import ca.uhn.fhir.jpa.binstore.IBinaryStorageSvc; import ca.uhn.fhir.jpa.binstore.IBinaryStorageSvc;
import ca.uhn.fhir.jpa.config.HibernateDialectProvider;
import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.entity.ModelConfig; import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionDeliveryHandlerFactory; import ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionDeliveryHandlerFactory;
@@ -10,9 +11,12 @@ import ca.uhn.fhir.jpa.subscription.match.deliver.email.IEmailSender;
import ca.uhn.fhir.jpa.subscription.match.deliver.email.JavaMailEmailSender; import ca.uhn.fhir.jpa.subscription.match.deliver.email.JavaMailEmailSender;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import org.hl7.fhir.dstu2.model.Subscription; import org.hl7.fhir.dstu2.model.Subscription;
import org.springframework.boot.env.YamlPropertySourceLoader;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Primary;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.annotation.EnableTransactionManagement;
import java.util.Optional; import java.util.Optional;
@@ -110,6 +114,11 @@ public class FhirServerConfigCommon {
return retVal; return retVal;
} }
@Bean
public YamlPropertySourceLoader yamlPropertySourceLoader() {
return new YamlPropertySourceLoader();
}
@Bean @Bean
public PartitionSettings partitionSettings(AppProperties appProperties) { public PartitionSettings partitionSettings(AppProperties appProperties) {
PartitionSettings retVal = new PartitionSettings(); PartitionSettings retVal = new PartitionSettings();
@@ -123,6 +132,12 @@ public class FhirServerConfigCommon {
} }
@Primary
@Bean
public HibernateDialectProvider jpaStarterDialectProvider(LocalContainerEntityManagerFactoryBean myEntityManagerFactory) {
return new JpaHibernateDialectProvider(myEntityManagerFactory);
}
@Bean @Bean
public ModelConfig modelConfig(AppProperties appProperties) { public ModelConfig modelConfig(AppProperties appProperties) {
ModelConfig modelConfig = new ModelConfig(); ModelConfig modelConfig = new ModelConfig();

View File

@@ -3,24 +3,21 @@ package ca.uhn.fhir.jpa.starter;
import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.jpa.config.BaseJavaConfigDstu2; import ca.uhn.fhir.jpa.config.BaseJavaConfigDstu2;
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
import ca.uhn.fhir.jpa.starter.annotations.OnDSTU2Condition;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Profile;
import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import javax.persistence.EntityManagerFactory; import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource; import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
@Configuration @Configuration
@Profile("dstu2") @Conditional(OnDSTU2Condition.class)
public class FhirServerConfigDstu2 extends BaseJavaConfigDstu2 { public class FhirServerConfigDstu2 extends BaseJavaConfigDstu2 {
@Autowired @Autowired

View File

@@ -3,11 +3,10 @@ package ca.uhn.fhir.jpa.starter;
import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.jpa.config.BaseJavaConfigDstu3; import ca.uhn.fhir.jpa.config.BaseJavaConfigDstu3;
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
import ca.uhn.fhir.jpa.search.lastn.ElasticsearchSvcImpl;
import ca.uhn.fhir.jpa.starter.annotations.OnDSTU3Condition;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.*;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Profile;
import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
@@ -16,7 +15,7 @@ import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource; import javax.sql.DataSource;
@Configuration @Configuration
@Profile("dstu3") @Conditional(OnDSTU3Condition.class)
public class FhirServerConfigDstu3 extends BaseJavaConfigDstu3 { public class FhirServerConfigDstu3 extends BaseJavaConfigDstu3 {
@Autowired @Autowired
@@ -65,4 +64,18 @@ public class FhirServerConfigDstu3 extends BaseJavaConfigDstu3 {
return retVal; return retVal;
} }
@Bean()
public ElasticsearchSvcImpl elasticsearchSvc() {
if (EnvironmentHelper.isElasticsearchEnabled(configurableEnvironment)) {
String elasticsearchUrl = EnvironmentHelper.getElasticsearchServerUrl(configurableEnvironment);
String elasticsearchHost = elasticsearchUrl.substring(elasticsearchUrl.indexOf("://")+3, elasticsearchUrl.lastIndexOf(":"));
String elasticsearchUsername = EnvironmentHelper.getElasticsearchServerUsername(configurableEnvironment);
String elasticsearchPassword = EnvironmentHelper.getElasticsearchServerPassword(configurableEnvironment);
int elasticsearchPort = Integer.parseInt(elasticsearchUrl.substring(elasticsearchUrl.lastIndexOf(":")+1));
return new ElasticsearchSvcImpl(elasticsearchHost, elasticsearchPort, elasticsearchUsername, elasticsearchPassword);
} else {
return null;
}
}
} }

View File

@@ -3,23 +3,22 @@ package ca.uhn.fhir.jpa.starter;
import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.jpa.config.BaseJavaConfigR4; import ca.uhn.fhir.jpa.config.BaseJavaConfigR4;
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
import ca.uhn.fhir.jpa.search.lastn.ElasticsearchSvcImpl;
import ca.uhn.fhir.jpa.starter.annotations.OnR4Condition;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Profile;
import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import javax.persistence.EntityManagerFactory; import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource; import javax.sql.DataSource;
import java.util.Arrays;
import java.util.Properties;
@Configuration @Configuration
@Profile("r4") @Conditional(OnR4Condition.class)
public class FhirServerConfigR4 extends BaseJavaConfigR4 { public class FhirServerConfigR4 extends BaseJavaConfigR4 {
@Autowired @Autowired
@@ -68,4 +67,18 @@ public class FhirServerConfigR4 extends BaseJavaConfigR4 {
return retVal; return retVal;
} }
@Bean()
public ElasticsearchSvcImpl elasticsearchSvc() {
if (EnvironmentHelper.isElasticsearchEnabled(configurableEnvironment)) {
String elasticsearchUrl = EnvironmentHelper.getElasticsearchServerUrl(configurableEnvironment);
String elasticsearchHost = elasticsearchUrl.substring(elasticsearchUrl.indexOf("://")+3, elasticsearchUrl.lastIndexOf(":"));
String elasticsearchUsername = EnvironmentHelper.getElasticsearchServerUsername(configurableEnvironment);
String elasticsearchPassword = EnvironmentHelper.getElasticsearchServerPassword(configurableEnvironment);
int elasticsearchPort = Integer.parseInt(elasticsearchUrl.substring(elasticsearchUrl.lastIndexOf(":")+1));
return new ElasticsearchSvcImpl(elasticsearchHost, elasticsearchPort, elasticsearchUsername, elasticsearchPassword);
} else {
return null;
}
}
} }

View File

@@ -3,11 +3,13 @@ package ca.uhn.fhir.jpa.starter;
import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.jpa.config.BaseJavaConfigR5; import ca.uhn.fhir.jpa.config.BaseJavaConfigR5;
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
import ca.uhn.fhir.jpa.search.lastn.ElasticsearchSvcImpl;
import ca.uhn.fhir.jpa.starter.annotations.OnR5Condition;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Profile;
import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
@@ -16,7 +18,7 @@ import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource; import javax.sql.DataSource;
@Configuration @Configuration
@Profile("r5") @Conditional(OnR5Condition.class)
public class FhirServerConfigR5 extends BaseJavaConfigR5 { public class FhirServerConfigR5 extends BaseJavaConfigR5 {
@Autowired @Autowired
@@ -65,4 +67,19 @@ public class FhirServerConfigR5 extends BaseJavaConfigR5 {
return retVal; return retVal;
} }
@Bean()
public ElasticsearchSvcImpl elasticsearchSvc() {
if (EnvironmentHelper.isElasticsearchEnabled(configurableEnvironment)) {
String elasticsearchUrl = EnvironmentHelper.getElasticsearchServerUrl(configurableEnvironment);
String elasticsearchHost = elasticsearchUrl.substring(elasticsearchUrl.indexOf("://")+3, elasticsearchUrl.lastIndexOf(":"));
String elasticsearchUsername = EnvironmentHelper.getElasticsearchServerUsername(configurableEnvironment);
String elasticsearchPassword = EnvironmentHelper.getElasticsearchServerPassword(configurableEnvironment);
int elasticsearchPort = Integer.parseInt(elasticsearchUrl.substring(elasticsearchUrl.lastIndexOf(":")+1));
return new ElasticsearchSvcImpl(elasticsearchHost, elasticsearchPort, elasticsearchUsername, elasticsearchPassword);
} else {
return null;
}
}
} }

View File

@@ -36,15 +36,15 @@ public class FhirTesterConfig {
@Bean @Bean
public TesterConfig testerConfig(AppProperties appProperties) { public TesterConfig testerConfig(AppProperties appProperties) {
TesterConfig retVal = new TesterConfig(); TesterConfig retVal = new TesterConfig();
appProperties.getTester().stream().forEach(t -> { appProperties.getTester().entrySet().stream().forEach(t -> {
retVal retVal
.addServer() .addServer()
.withId(t.getId()) .withId(t.getKey())
.withFhirVersion(t.getFhir_version()) .withFhirVersion(t.getValue().getFhir_version())
.withBaseUrl(t.getServer_address()) .withBaseUrl(t.getValue().getServer_address())
.withName(t.getName()); .withName(t.getValue().getName());
retVal.setRefuseToFetchThirdPartyUrls( retVal.setRefuseToFetchThirdPartyUrls(
t.getRefuse_to_fetch_third_party_urls()); t.getValue().getRefuse_to_fetch_third_party_urls());
}); });
return retVal; return retVal;

View File

@@ -0,0 +1,31 @@
package ca.uhn.fhir.jpa.starter;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.jpa.config.HibernateDialectProvider;
import org.hibernate.dialect.Dialect;
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.SQLException;
public class JpaHibernateDialectProvider extends HibernateDialectProvider {
private final Dialect dialect;
public JpaHibernateDialectProvider(LocalContainerEntityManagerFactoryBean myEntityManagerFactory) {
DataSource connection = myEntityManagerFactory.getDataSource();
try {
dialect = new StandardDialectResolver()
.resolveDialect(new DatabaseMetaDataDialectResolutionInfoAdapter(connection.getConnection().getMetaData()));
} catch (SQLException sqlException) {
throw new ConfigurationException(sqlException.getMessage(), sqlException);
}
}
@Override
public Dialect getDialect() {
return dialect;
}
}

View File

@@ -0,0 +1,19 @@
package ca.uhn.fhir.jpa.starter.annotations;
import ca.uhn.fhir.context.FhirVersionEnum;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
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()
.getProperty("hapi.fhir.fhir_version")
.toUpperCase());
return version == FhirVersionEnum.DSTU2;
}
}

View File

@@ -0,0 +1,19 @@
package ca.uhn.fhir.jpa.starter.annotations;
import ca.uhn.fhir.context.FhirVersionEnum;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
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()
.getProperty("hapi.fhir.fhir_version")
.toUpperCase());
return version == FhirVersionEnum.DSTU3;
}
}

View File

@@ -0,0 +1,35 @@
package ca.uhn.fhir.jpa.starter.annotations;
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.context.annotation.Conditional;
public class OnEitherVersion extends AnyNestedCondition {
OnEitherVersion() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@Override
protected ConditionOutcome getFinalMatchOutcome(MemberMatchOutcomes memberOutcomes) {
ConditionOutcome result = super.getFinalMatchOutcome(memberOutcomes);
return result;
}
@Conditional(OnDSTU2Condition.class)
static class OnDSTU2 {
}
@Conditional(OnDSTU3Condition.class)
static class OnDSTU3 {
}
@Conditional(OnR4Condition.class)
static class OnR4 {
}
@Conditional(OnR5Condition.class)
static class OnR5 {
}
}

View File

@@ -0,0 +1,19 @@
package ca.uhn.fhir.jpa.starter.annotations;
import ca.uhn.fhir.context.FhirVersionEnum;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
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()
.getProperty("hapi.fhir.fhir_version")
.toUpperCase());
return version == FhirVersionEnum.R4;
}
}

View File

@@ -0,0 +1,19 @@
package ca.uhn.fhir.jpa.starter.annotations;
import ca.uhn.fhir.context.FhirVersionEnum;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
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()
.getProperty("hapi.fhir.fhir_version")
.toUpperCase());
return version == FhirVersionEnum.R5;
}
}

View File

@@ -1,28 +1,26 @@
spring: spring:
datasource: datasource:
url: 'jdbc:h2:file:./target/database/h2' url: 'jdbc:h2:file:./target/database/h2'
#url: jdbc:h2:mem:test_mem
username: sa username: sa
password: null password: null
driverClassName: org.h2.Driver driverClassName: org.h2.Driver
max-active: 15 max-active: 15
profiles: # jpa:
### This is the FHIR version. Choose between, dstu2, dstu3, r4 or r5 # properties:
active: r4 # hibernate.dialect: org.hibernate.dialect.h2dialect
jpa: # hibernate.search.model_mapping: ca.uhn.fhir.jpa.search.lucenesearchmappingfactory
properties: # hibernate.format_sql: false
hibernate.dialect: org.hibernate.dialect.H2Dialect # hibernate.show_sql: false
hibernate.search.model_mapping: ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory # hibernate.hbm2ddl.auto: update
hibernate.format_sql: false # hibernate.jdbc.batch_size: 20
hibernate.show_sql: false # hibernate.cache.use_query_cache: false
hibernate.hbm2ddl.auto: update # hibernate.cache.use_second_level_cache: false
hibernate.jdbc.batch_size: 20 # hibernate.cache.use_structured_entries: false
hibernate.cache.use_query_cache: false # hibernate.cache.use_minimal_puts: false
hibernate.cache.use_second_level_cache: false # hibernate.search.default.directory_provider: filesystem
hibernate.cache.use_structured_entries: false # hibernate.search.default.indexbase: target/lucenefiles
hibernate.cache.use_minimal_puts: false # hibernate.search.lucene_version: lucene_current
hibernate.search.default.directory_provider: filesystem
hibernate.search.default.indexBase: target/lucenefiles
hibernate.search.lucene_version: LUCENE_CURRENT
batch: batch:
job: job:
@@ -30,15 +28,21 @@ spring:
hapi: hapi:
fhir: fhir:
defer_indexing_for_codesystems_of_size: 101 ### enable to set the Server URL
# server_address: http://hapi.fhir.org/baseR4
### This is the FHIR version. Choose between, DSTU2, DSTU3, R4 or R5
fhir_version: R4
# defer_indexing_for_codesystems_of_size: 101
#implementationguides: #implementationguides:
# - #example from registry (packages.fhir.org)
# url: https://build.fhir.org/ig/hl7dk/dk-medcom/branches/corrections/package.tgz #swiss:
# name: dk.fhir.ig.medcom-core #name: swiss.mednet.fhir
#version: 0.8.0 #version: 0.8.0
# - #example not from registry
#ips_1_0_0:
#url: https://build.fhir.org/ig/HL7/fhir-ips/package.tgz
#name: hl7.fhir.uv.ips #name: hl7.fhir.uv.ips
# version: 0.3.0 #version: 1.0.0
#supported_resource_types: #supported_resource_types:
# - Patient # - Patient
@@ -62,6 +66,7 @@ hapi:
# fhirpath_interceptor_enabled: false # fhirpath_interceptor_enabled: false
# filter_search_enabled: true # filter_search_enabled: true
# graphql_enabled: true # graphql_enabled: true
# narrative_enabled: true
#partitioning: #partitioning:
# cross_partition_reference_mode: true # cross_partition_reference_mode: true
# multitenancy_enabled: true # multitenancy_enabled: true
@@ -87,14 +92,14 @@ hapi:
# retain_cached_searches_mins: 60 # retain_cached_searches_mins: 60
# reuse_cached_search_results_millis: 60000 # reuse_cached_search_results_millis: 60000
tester: tester:
-
id: home home:
name: Local Tester name: Local Tester
server_address: 'http://localhost:8080/fhir' server_address: 'http://localhost:8080/fhir'
refuse_to_fetch_third_party_urls: false refuse_to_fetch_third_party_urls: false
fhir_version: R4 fhir_version: R4
-
id: global global:
name: Global Tester name: Global Tester
server_address: "http://hapi.fhir.org/baseR4" server_address: "http://hapi.fhir.org/baseR4"
refuse_to_fetch_third_party_urls: false refuse_to_fetch_third_party_urls: false
@@ -118,6 +123,7 @@ hapi:
# startTlsEnable: # startTlsEnable:
# startTlsRequired: # startTlsRequired:
# quitWait: # quitWait:
# lastn_enabled: true
# #

View File

@@ -26,17 +26,17 @@
"name": "cosine-given-name", "name": "cosine-given-name",
"resourceType": "*", "resourceType": "*",
"resourcePath": "name.given", "resourcePath": "name.given",
"metric": "COSINE", "matcher": {
"matchThreshold": 0.8, "algorithm": "COLOGNE"
"exact": true }
}, },
{ {
"name": "jaro-last-name", "name": "jaro-last-name",
"resourceType": "*", "resourceType": "*",
"resourcePath": "name.family", "resourcePath": "name.family",
"metric": "JARO_WINKLER", "matcher": {
"matchThreshold": 0.8, "algorithm": "SOUNDEX"
"exact": true }
} }
], ],
"matchResultMap": { "matchResultMap": {

View File

@@ -0,0 +1,144 @@
package ca.uhn.fhir.jpa.starter;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.search.lastn.ElasticsearchSvcImpl;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.DateTimeType;
import org.hl7.fhir.r4.model.IntegerType;
import org.hl7.fhir.r4.model.Observation;
import org.hl7.fhir.r4.model.Parameters;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.StringType;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import pl.allegro.tech.embeddedelasticsearch.EmbeddedElastic;
import pl.allegro.tech.embeddedelasticsearch.PopularProperties;
import javax.annotation.PreDestroy;
import java.io.IOException;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import static org.junit.jupiter.api.Assertions.assertEquals;
@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class, properties =
{
"spring.batch.job.enabled=false",
"spring.datasource.url=jdbc:h2:mem:dbr4",
"hapi.fhir.fhir_version=r4",
"hapi.fhir.lastn_enabled=true",
"elasticsearch.enabled=true",
// Because the port is set randomly, we will set the rest_url using the Initializer.
// "elasticsearch.rest_url='http://localhost:9200'",
"elasticsearch.username=SomeUsername",
"elasticsearch.password=SomePassword"
})
@ContextConfiguration(initializers = ElasticsearchLastNR4IT.Initializer.class)
public class ElasticsearchLastNR4IT {
private IGenericClient ourClient;
private FhirContext ourCtx;
private static final String ELASTIC_VERSION = "6.5.4";
private static EmbeddedElastic embeddedElastic;
@Autowired
private ElasticsearchSvcImpl myElasticsearchSvc;
@BeforeAll
public static void beforeClass() {
embeddedElastic = null;
try {
embeddedElastic = EmbeddedElastic.builder()
.withElasticVersion(ELASTIC_VERSION)
.withSetting(PopularProperties.TRANSPORT_TCP_PORT, 0)
.withSetting(PopularProperties.HTTP_PORT, 0)
.withSetting(PopularProperties.CLUSTER_NAME, UUID.randomUUID())
.withStartTimeout(60, TimeUnit.SECONDS)
.build()
.start();
} catch (IOException | InterruptedException e) {
throw new ConfigurationException(e);
}
}
@PreDestroy
public void stop() {
embeddedElastic.stop();
}
@LocalServerPort
private int port;
@Test
void testLastN() throws IOException {
Patient pt = new Patient();
pt.addName().setFamily("Lastn").addGiven("Arthur");
IIdType id = ourClient.create().resource(pt).execute().getId().toUnqualifiedVersionless();
Observation obs = new Observation();
obs.getSubject().setReferenceElement(id);
String observationCode = "testobservationcode";
String codeSystem = "http://testobservationcodesystem";
obs.getCode().addCoding().setCode(observationCode).setSystem(codeSystem);
obs.setValue(new StringType(observationCode));
Date effectiveDtm = new GregorianCalendar().getTime();
obs.setEffective(new DateTimeType(effectiveDtm));
obs.getCategoryFirstRep().addCoding().setCode("testcategorycode").setSystem("http://testcategorycodesystem");
IIdType obsId = ourClient.create().resource(obs).execute().getId().toUnqualifiedVersionless();
myElasticsearchSvc.refreshIndex(ElasticsearchSvcImpl.OBSERVATION_INDEX);
Parameters output = ourClient.operation().onType(Observation.class).named("lastn")
.withParameter(Parameters.class, "max", new IntegerType(1))
.andParameter("subject", new StringType("Patient/" + id.getIdPart()))
.execute();
Bundle b = (Bundle) output.getParameter().get(0).getResource();
assertEquals(1, b.getTotal());
assertEquals(obsId, b.getEntry().get(0).getResource().getIdElement().toUnqualifiedVersionless());
}
@BeforeEach
void beforeEach() {
ourCtx = FhirContext.forR4();
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
ourCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000);
String ourServerBase = "http://localhost:" + port + "/fhir/";
ourClient = ourCtx.newRestfulGenericClient(ourServerBase);
ourClient.registerInterceptor(new LoggingInterceptor(true));
}
static class Initializer
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(
ConfigurableApplicationContext configurableApplicationContext) {
// Since the port is dynamically generated, replace the URL with one that has the correct port
TestPropertyValues.of("elasticsearch.rest_url=http://localhost:" + embeddedElastic.getHttpPort())
.applyTo(configurableApplicationContext.getEnvironment());
}
}
}

View File

@@ -19,7 +19,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class, properties = @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class, properties =
{ {
"spring.batch.job.enabled=false", "spring.batch.job.enabled=false",
"spring.profiles.active=dstu2", "hapi.fhir.fhir_version=dstu2",
"spring.datasource.url=jdbc:h2:mem:dbr2" "spring.datasource.url=jdbc:h2:mem:dbr2"
}) })
public class ExampleServerDstu2IT { public class ExampleServerDstu2IT {

View File

@@ -33,8 +33,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class, properties = @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class, properties =
{ {
"spring.batch.job.enabled=false", "spring.batch.job.enabled=false",
"spring.profiles.active=dstu3",
"spring.datasource.url=jdbc:h2:mem:dbr3", "spring.datasource.url=jdbc:h2:mem:dbr3",
"hapi.fhir.fhir_version=dstu3",
"hapi.fhir.subscription.websocket_enabled=true", "hapi.fhir.subscription.websocket_enabled=true",
"hapi.fhir.allow_external_references=true", "hapi.fhir.allow_external_references=true",
"hapi.fhir.allow_placeholder_references=true", "hapi.fhir.allow_placeholder_references=true",

View File

@@ -36,8 +36,8 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class, properties = @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class, properties =
{ {
"spring.batch.job.enabled=false", "spring.batch.job.enabled=false",
"spring.profiles.active=r4",
"spring.datasource.url=jdbc:h2:mem:dbr4", "spring.datasource.url=jdbc:h2:mem:dbr4",
"hapi.fhir.fhir_version=r4",
"hapi.fhir.subscription.websocket_enabled=true", "hapi.fhir.subscription.websocket_enabled=true",
"hapi.fhir.empi_enabled=true", "hapi.fhir.empi_enabled=true",
//Override is currently required when using Empi as the construction of the Empi beans are ambiguous as they are constructed multiple places. This is evident when running in a spring boot environment //Override is currently required when using Empi as the construction of the Empi beans are ambiguous as they are constructed multiple places. This is evident when running in a spring boot environment

View File

@@ -35,8 +35,8 @@ import org.springframework.test.context.junit.jupiter.SpringExtension;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class, properties = @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class, properties =
{ {
"spring.batch.job.enabled=false", "spring.batch.job.enabled=false",
"spring.profiles.active=r5",
"spring.datasource.url=jdbc:h2:mem:dbr5", "spring.datasource.url=jdbc:h2:mem:dbr5",
"hapi.fhir.fhir_version=r5",
"hapi.fhir.subscription.websocket_enabled=true" "hapi.fhir.subscription.websocket_enabled=true"
}) })
public class ExampleServerR5IT { public class ExampleServerR5IT {

View File

@@ -21,8 +21,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class, properties = @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class, properties =
{ {
"spring.batch.job.enabled=false", "spring.batch.job.enabled=false",
"spring.profiles.active=r4",
"spring.datasource.url=jdbc:h2:mem:dbr4-mt", "spring.datasource.url=jdbc:h2:mem:dbr4-mt",
"hapi.fhir.fhir_version=r4",
"hapi.fhir.subscription.websocket_enabled=true", "hapi.fhir.subscription.websocket_enabled=true",
"hapi.fhir.partitioning.partitioning_include_in_search_hashes=false" "hapi.fhir.partitioning.partitioning_include_in_search_hashes=false"