Merge branch 'master' into rel_5_7

This commit is contained in:
Tadgh
2022-02-17 17:52:36 -08:00
17 changed files with 214 additions and 128 deletions

View File

@@ -354,10 +354,14 @@ elasticsearch.schema_management_strategy=CREATE
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. 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.
## Enabling Resource to be stored in Lucene Index
Set `hapi.fhir.store_resource_in_lucene_index_enabled` in the [application.yaml](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/application.yaml) file to enable storing of resource json along with Lucene/Elasticsearch index mappings.
## Changing cached search results time ## Changing cached search results time
It is possible to change the cached search results time. The option `reuse_cached_search_results_millis` in the [application.yaml] is 6000 miliseconds by default. It is possible to change the cached search results time. The option `reuse_cached_search_results_millis` in the [application.yaml](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/application.yaml) is 6000 miliseconds by default.
Set `reuse_cached_search_results_millis: -1` in the [application.yaml] file to ignore the cache time every search. Set `reuse_cached_search_results_millis: -1` in the [application.yaml](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/application.yaml) file to ignore the cache time every search.
## Build the distroless variant of the image (for lower footprint and improved security) ## Build the distroless variant of the image (for lower footprint and improved security)

33
pom.xml
View File

@@ -21,11 +21,10 @@
<properties> <properties>
<java.version>8</java.version> <java.version>8</java.version>
<spring_boot_version>2.5.6</spring_boot_version>
</properties> </properties>
<prerequisites> <prerequisites>
<maven>3.6.3</maven> <maven>3.8.3</maven>
</prerequisites> </prerequisites>
<packaging>war</packaging> <packaging>war</packaging>
@@ -57,30 +56,22 @@
<dependency> <dependency>
<groupId>mysql</groupId> <groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId> <artifactId>mysql-connector-java</artifactId>
<version>8.0.25</version>
</dependency> </dependency>
<!-- https://mvnrepository.com/artifact/org.postgresql/postgresql -->
<dependency> <dependency>
<groupId>org.postgresql</groupId> <groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId> <artifactId>postgresql</artifactId>
<version>42.2.23</version>
</dependency> </dependency>
<!-- Needed for Email subscriptions --> <!-- Needed for Email subscriptions -->
<!-- <dependency>-->
<!-- <groupId>com.sun.mail</groupId>-->
<!-- <artifactId>javax.mail</artifactId>-->
<!-- <version>1.6.2</version>-->
<!-- <exclusions>-->
<!-- <exclusion>-->
<!-- <groupId>javax.activation</groupId>-->
<!-- <artifactId>activation</artifactId>-->
<!-- </exclusion>-->
<!-- </exclusions>-->
<!-- </dependency>-->
<dependency> <dependency>
<groupId>org.simplejavamail</groupId> <groupId>org.simplejavamail</groupId>
<artifactId>simple-java-mail</artifactId> <artifactId>simple-java-mail</artifactId>
<exclusions>
<exclusion>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
</exclusion>
</exclusions>
</dependency> </dependency>
<!-- This dependency includes the core HAPI-FHIR classes --> <!-- This dependency includes the core HAPI-FHIR classes -->
@@ -89,6 +80,12 @@
<artifactId>hapi-fhir-base</artifactId> <artifactId>hapi-fhir-base</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<!-- This dependency includes the EmailSenderImpl we will be using instead of standard javamail.-->
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-jpaserver-subscription</artifactId>
<version>${project.version}</version>
</dependency>
<!-- This dependency includes the JPA server itself, which is packaged separately from the rest of HAPI FHIR --> <!-- This dependency includes the JPA server itself, which is packaged separately from the rest of HAPI FHIR -->
<dependency> <dependency>
@@ -163,7 +160,7 @@
<dependency> <dependency>
<groupId>org.yaml</groupId> <groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId> <artifactId>snakeyaml</artifactId>
<version>1.29</version> <version>1.30</version>
</dependency> </dependency>
<!-- Used for CORS support --> <!-- Used for CORS support -->
@@ -196,7 +193,7 @@
<dependency> <dependency>
<groupId>org.webjars</groupId> <groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId> <artifactId>bootstrap</artifactId>
<version>3.4.1</version> <version>5.1.3</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.webjars</groupId> <groupId>org.webjars</groupId>

View File

@@ -11,10 +11,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import java.util.ArrayList; import java.util.*;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@ConfigurationProperties(prefix = "hapi.fhir") @ConfigurationProperties(prefix = "hapi.fhir")
@Configuration @Configuration
@@ -24,6 +21,7 @@ public class AppProperties {
private Boolean cql_enabled = false; private Boolean cql_enabled = false;
private Boolean openapi_enabled = false; private Boolean openapi_enabled = false;
private Boolean mdm_enabled = false; private Boolean mdm_enabled = false;
private boolean advanced_lucene_indexing = false;
private Boolean allow_cascading_deletes = false; private Boolean allow_cascading_deletes = false;
private Boolean allow_contains_searches = true; private Boolean allow_contains_searches = true;
private Boolean allow_external_references = false; private Boolean allow_external_references = false;
@@ -68,6 +66,7 @@ public class AppProperties {
private Map<String, ImplementationGuide> implementationGuides = null; private Map<String, ImplementationGuide> implementationGuides = null;
private Boolean lastn_enabled = false; private Boolean lastn_enabled = false;
private boolean store_resource_in_lucene_index_enabled = false;
private NormalizedQuantitySearchLevel normalized_quantity_search_level = NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED; private NormalizedQuantitySearchLevel normalized_quantity_search_level = NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED;
private Integer search_coord_core_pool_size = 20; private Integer search_coord_core_pool_size = 20;
@@ -76,6 +75,10 @@ public class AppProperties {
private Boolean use_apache_address_strategy = false; private Boolean use_apache_address_strategy = false;
private Boolean use_apache_address_strategy_https = false; private Boolean use_apache_address_strategy_https = false;
private Integer bundle_batch_pool_size = 20;
private Integer bundle_batch_pool_max_size = 100;
private List<String> local_base_urls = new ArrayList<>();
public Boolean getOpenapi_enabled() { public Boolean getOpenapi_enabled() {
return openapi_enabled; return openapi_enabled;
} }
@@ -196,7 +199,11 @@ public class AppProperties {
this.supported_resource_types = supported_resource_types; this.supported_resource_types = supported_resource_types;
} }
public Logger getLogger() { public List<String> getSupported_resource_types(List<String> supported_resource_types) {
return this.supported_resource_types;
}
public Logger getLogger() {
return logger; return logger;
} }
@@ -213,7 +220,15 @@ public class AppProperties {
this.client_id_strategy = client_id_strategy; this.client_id_strategy = client_id_strategy;
} }
public Boolean getAllow_cascading_deletes() { public boolean getAdvanced_lucene_indexing() {
return this.advanced_lucene_indexing;
}
public void setAdvanced_lucene_indexing(boolean theAdvanced_lucene_indexing) {
advanced_lucene_indexing = theAdvanced_lucene_indexing;
}
public Boolean getAllow_cascading_deletes() {
return allow_cascading_deletes; return allow_cascading_deletes;
} }
@@ -455,7 +470,15 @@ public class AppProperties {
this.lastn_enabled = lastn_enabled; this.lastn_enabled = lastn_enabled;
} }
public NormalizedQuantitySearchLevel getNormalized_quantity_search_level() { public boolean getStore_resource_in_lucene_index_enabled() {
return store_resource_in_lucene_index_enabled;
}
public void setStore_resource_in_lucene_index_enabled(Boolean store_resource_in_lucene_index_enabled) {
this.store_resource_in_lucene_index_enabled = store_resource_in_lucene_index_enabled;
}
public NormalizedQuantitySearchLevel getNormalized_quantity_search_level() {
return this.normalized_quantity_search_level; return this.normalized_quantity_search_level;
} }
@@ -489,6 +512,26 @@ public class AppProperties {
this.install_transitive_ig_dependencies = install_transitive_ig_dependencies; this.install_transitive_ig_dependencies = install_transitive_ig_dependencies;
} }
public Integer getBundle_batch_pool_size() {
return this.bundle_batch_pool_size;
}
public void setBundle_batch_pool_size(Integer bundle_batch_pool_size) {
this.bundle_batch_pool_size = bundle_batch_pool_size;
}
public Integer getBundle_batch_pool_max_size() {
return bundle_batch_pool_max_size;
}
public void setBundle_batch_pool_max_size(Integer bundle_batch_pool_max_size) {
this.bundle_batch_pool_max_size = bundle_batch_pool_max_size;
}
public List<String> getLocal_base_urls() {
return local_base_urls;
}
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("*");

View File

@@ -384,6 +384,9 @@ public class BaseJpaRestfulServer extends RestfulServer {
daoConfig.setResourceServerIdStrategy(DaoConfig.IdStrategyEnum.UUID); daoConfig.setResourceServerIdStrategy(DaoConfig.IdStrategyEnum.UUID);
daoConfig.setResourceClientIdStrategy(appProperties.getClient_id_strategy()); daoConfig.setResourceClientIdStrategy(appProperties.getClient_id_strategy());
} }
//Parallel Batch GET execution settings
daoConfig.setBundleBatchPoolSize(appProperties.getBundle_batch_pool_size());
daoConfig.setBundleBatchPoolSize(appProperties.getBundle_batch_pool_max_size());
if (appProperties.getImplementationGuides() != null) { if (appProperties.getImplementationGuides() != null) {
Map<String, AppProperties.ImplementationGuide> guides = appProperties.getImplementationGuides(); Map<String, AppProperties.ImplementationGuide> guides = appProperties.getImplementationGuides();
@@ -410,8 +413,8 @@ public class BaseJpaRestfulServer extends RestfulServer {
daoConfig.setLastNEnabled(true); daoConfig.setLastNEnabled(true);
} }
daoConfig.setStoreResourceInLuceneIndex(appProperties.getStore_resource_in_lucene_index_enabled());
daoConfig.getModelConfig().setNormalizedQuantitySearchLevel(appProperties.getNormalized_quantity_search_level()); daoConfig.getModelConfig().setNormalizedQuantitySearchLevel(appProperties.getNormalized_quantity_search_level());
daoConfig.getModelConfig().setIndexOnContainedResources(appProperties.getEnable_index_contained_resource());
daoConfig.getModelConfig().setIndexOnContainedResources(appProperties.getEnable_index_contained_resource());
} }
} }

View File

@@ -0,0 +1,33 @@
package ca.uhn.fhir.jpa.starter;
import ca.uhn.fhir.jpa.search.lastn.ElasticsearchSvcImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
/** Shared configuration for Elasticsearch */
@Configuration
public class ElasticsearchConfig {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ElasticsearchConfig.class);
@Autowired
private ConfigurableEnvironment configurableEnvironment;
@Bean()
public ElasticsearchSvcImpl elasticsearchSvc() {
if (EnvironmentHelper.isElasticsearchEnabled(configurableEnvironment)) {
String elasticsearchUrl = EnvironmentHelper.getElasticsearchServerUrl(configurableEnvironment);
if (elasticsearchUrl.startsWith("http")) {
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);
} else {
return null;
}
}
}

View File

@@ -14,6 +14,8 @@ import ca.uhn.fhir.rest.server.mail.IMailSvc;
import ca.uhn.fhir.rest.server.mail.MailConfig; import ca.uhn.fhir.rest.server.mail.MailConfig;
import ca.uhn.fhir.rest.server.mail.MailSvc; import ca.uhn.fhir.rest.server.mail.MailSvc;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import java.util.HashSet;
import java.util.Optional;
import org.hl7.fhir.dstu2.model.Subscription; import org.hl7.fhir.dstu2.model.Subscription;
import org.springframework.boot.env.YamlPropertySourceLoader; import org.springframework.boot.env.YamlPropertySourceLoader;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@@ -23,8 +25,6 @@ import org.springframework.context.annotation.Primary;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.annotation.EnableTransactionManagement;
import java.util.Optional;
/** /**
* This is the primary configuration file for the example server * This is the primary configuration file for the example server
*/ */
@@ -119,6 +119,8 @@ public class FhirServerConfigCommon {
} }
retVal.setFilterParameterEnabled(appProperties.getFilter_search_enabled()); retVal.setFilterParameterEnabled(appProperties.getFilter_search_enabled());
retVal.setAdvancedLuceneIndexing(appProperties.getAdvanced_lucene_indexing());
retVal.setTreatBaseUrlsAsLocal(new HashSet<>(appProperties.getLocal_base_urls()));
return retVal; return retVal;
} }
@@ -154,8 +156,8 @@ public class FhirServerConfigCommon {
} }
@Bean @Bean
public ModelConfig modelConfig(AppProperties appProperties) { public ModelConfig modelConfig(AppProperties appProperties, DaoConfig daoConfig) {
ModelConfig modelConfig = new ModelConfig(); ModelConfig modelConfig = daoConfig.getModelConfig();
modelConfig.setAllowContainsSearches(appProperties.getAllow_contains_searches()); modelConfig.setAllowContainsSearches(appProperties.getAllow_contains_searches());
modelConfig.setAllowExternalReferences(appProperties.getAllow_external_references()); modelConfig.setAllowExternalReferences(appProperties.getAllow_external_references());
modelConfig.setDefaultSearchParamsCanBeOverridden(appProperties.getAllow_override_default_search_params()); modelConfig.setDefaultSearchParamsCanBeOverridden(appProperties.getAllow_override_default_search_params());
@@ -222,7 +224,7 @@ public class FhirServerConfigCommon {
IMailSvc mailSvc = new MailSvc(mailConfig); IMailSvc mailSvc = new MailSvc(mailConfig);
IEmailSender emailSender = new EmailSenderImpl(mailSvc); IEmailSender emailSender = new EmailSenderImpl(mailSvc);
subscriptionDeliveryHandlerFactory.get().setEmailSender(emailSender); subscriptionDeliveryHandlerFactory.ifPresent(theSubscriptionDeliveryHandlerFactory -> theSubscriptionDeliveryHandlerFactory.setEmailSender(emailSender));
return emailSender; return emailSender;
} }

View File

@@ -2,9 +2,7 @@ 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.model.config.PartitionSettings;
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 ca.uhn.fhir.jpa.starter.annotations.OnDSTU3Condition;
import ca.uhn.fhir.jpa.starter.cql.StarterCqlDstu3Config; import ca.uhn.fhir.jpa.starter.cql.StarterCqlDstu3Config;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
@@ -23,7 +21,7 @@ import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
@Configuration @Configuration
@Conditional(OnDSTU3Condition.class) @Conditional(OnDSTU3Condition.class)
@Import(StarterCqlDstu3Config.class) @Import({StarterCqlDstu3Config.class, ElasticsearchConfig.class})
public class FhirServerConfigDstu3 extends BaseJavaConfigDstu3 { public class FhirServerConfigDstu3 extends BaseJavaConfigDstu3 {
@Autowired @Autowired
@@ -77,6 +75,7 @@ public class FhirServerConfigDstu3 extends BaseJavaConfigDstu3 {
retVal.setJpaProperties(EnvironmentHelper.getHibernateProperties(configurableEnvironment, retVal.setJpaProperties(EnvironmentHelper.getHibernateProperties(configurableEnvironment,
myConfigurableListableBeanFactory)); myConfigurableListableBeanFactory));
return retVal; return retVal;
} }
@@ -87,26 +86,4 @@ public class FhirServerConfigDstu3 extends BaseJavaConfigDstu3 {
retVal.setEntityManagerFactory(entityManagerFactory); retVal.setEntityManagerFactory(entityManagerFactory);
return retVal; return retVal;
} }
@Bean()
public ElasticsearchSvcImpl elasticsearchSvc(PartitionSettings thePartitionSetings) {
if (Boolean.TRUE.equals(EnvironmentHelper.isElasticsearchEnabled(configurableEnvironment))) {
String elasticsearchUrl = EnvironmentHelper.getElasticsearchServerUrl(configurableEnvironment);
String elasticsearchHost = elasticsearchUrl;
String elasticsearchProtocol = EnvironmentHelper.getElasticsearchServerProtocol(configurableEnvironment);
if (elasticsearchUrl.startsWith("http")) {
elasticsearchProtocol = elasticsearchUrl.split("://")[0];
elasticsearchHost = elasticsearchUrl.split("://")[1];
}else {
elasticsearchProtocol = "http";
elasticsearchHost = elasticsearchUrl;
}
String elasticsearchUsername = EnvironmentHelper.getElasticsearchServerUsername(configurableEnvironment);
String elasticsearchPassword = EnvironmentHelper.getElasticsearchServerPassword(configurableEnvironment);
return new ElasticsearchSvcImpl(thePartitionSetings, elasticsearchProtocol, elasticsearchHost, elasticsearchUsername, elasticsearchPassword);
} else {
return null;
}
}
} }

View File

@@ -2,25 +2,26 @@ 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.model.config.PartitionSettings;
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 ca.uhn.fhir.jpa.starter.annotations.OnR4Condition;
import ca.uhn.fhir.jpa.starter.cql.StarterCqlR4Config; import ca.uhn.fhir.jpa.starter.cql.StarterCqlR4Config;
import javax.annotation.PostConstruct;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.annotation.*; 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.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;
import javax.annotation.PostConstruct;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
@Configuration @Configuration
@Conditional(OnR4Condition.class) @Conditional(OnR4Condition.class)
@Import(StarterCqlR4Config.class) @Import({StarterCqlR4Config.class, ElasticsearchConfig.class})
public class FhirServerConfigR4 extends BaseJavaConfigR4 { public class FhirServerConfigR4 extends BaseJavaConfigR4 {
@Autowired @Autowired
@@ -84,25 +85,4 @@ public class FhirServerConfigR4 extends BaseJavaConfigR4 {
return retVal; return retVal;
} }
@Bean()
public ElasticsearchSvcImpl elasticsearchSvc(PartitionSettings thePartitionSetings) {
if (Boolean.TRUE.equals(EnvironmentHelper.isElasticsearchEnabled(configurableEnvironment))) {
String elasticsearchUrl = EnvironmentHelper.getElasticsearchServerUrl(configurableEnvironment);
String elasticsearchHost = elasticsearchUrl;
String elasticsearchProtocol = EnvironmentHelper.getElasticsearchServerProtocol(configurableEnvironment);
if (elasticsearchUrl.startsWith("http")) {
elasticsearchProtocol = elasticsearchUrl.split("://")[0];
elasticsearchHost = elasticsearchUrl.split("://")[1];
}else {
elasticsearchProtocol = "http";
elasticsearchHost = elasticsearchUrl;
}
String elasticsearchUsername = EnvironmentHelper.getElasticsearchServerUsername(configurableEnvironment);
String elasticsearchPassword = EnvironmentHelper.getElasticsearchServerPassword(configurableEnvironment);
return new ElasticsearchSvcImpl(thePartitionSetings, elasticsearchProtocol, elasticsearchHost, elasticsearchUsername, elasticsearchPassword);
} else {
return null;
}
}
} }

View File

@@ -2,26 +2,25 @@ 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.model.config.PartitionSettings;
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 ca.uhn.fhir.jpa.starter.annotations.OnR5Condition;
import javax.annotation.PostConstruct;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.Primary;
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;
import javax.annotation.PostConstruct;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
@Configuration @Configuration
@Conditional(OnR5Condition.class) @Conditional(OnR5Condition.class)
@Import({ElasticsearchConfig.class})
public class FhirServerConfigR5 extends BaseJavaConfigR5 { public class FhirServerConfigR5 extends BaseJavaConfigR5 {
@Autowired @Autowired
@@ -85,26 +84,4 @@ public class FhirServerConfigR5 extends BaseJavaConfigR5 {
return retVal; return retVal;
} }
@Bean()
public ElasticsearchSvcImpl elasticsearchSvc(PartitionSettings thePartitionSetings) {
if (Boolean.TRUE.equals(EnvironmentHelper.isElasticsearchEnabled(configurableEnvironment))) {
String elasticsearchUrl = EnvironmentHelper.getElasticsearchServerUrl(configurableEnvironment);
String elasticsearchHost;
String elasticsearchProtocol = EnvironmentHelper.getElasticsearchServerProtocol(configurableEnvironment);
if (elasticsearchUrl.startsWith("http")) {
elasticsearchProtocol = elasticsearchUrl.split("://")[0];
elasticsearchHost = elasticsearchUrl.split("://")[1];
} else {
elasticsearchProtocol = "http";
elasticsearchHost = elasticsearchUrl;
}
String elasticsearchUsername = EnvironmentHelper.getElasticsearchServerUsername(configurableEnvironment);
String elasticsearchPassword = EnvironmentHelper.getElasticsearchServerPassword(configurableEnvironment);
return new ElasticsearchSvcImpl(thePartitionSetings, elasticsearchProtocol, elasticsearchHost, elasticsearchUsername, elasticsearchPassword);
} else {
return null;
}
}
} }

View File

@@ -1,4 +1,10 @@
spring: spring:
main:
allow-circular-references: true
flyway:
enabled: false
check-location: false
baselineOnMigrate: true
datasource: datasource:
url: 'jdbc:h2:file:./target/database/h2' url: 'jdbc:h2:file:./target/database/h2'
#url: jdbc:h2:mem:test_mem #url: jdbc:h2:mem:test_mem
@@ -10,13 +16,14 @@ spring:
# database connection pool size # database connection pool size
hikari: hikari:
maximum-pool-size: 10 maximum-pool-size: 10
flyway:
check-location: false
baselineOnMigrate: true
jpa: jpa:
properties: properties:
hibernate.format_sql: false hibernate.format_sql: false
hibernate.show_sql: false hibernate.show_sql: false
#Hibernate dialect is automatically detected except Postgres and H2.
#If using H2, then supply the value of ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect
#If using postgres, then supply the value of ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgres94Dialect
hibernate.dialect: ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect hibernate.dialect: ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect
# hibernate.hbm2ddl.auto: update # hibernate.hbm2ddl.auto: update
# hibernate.jdbc.batch_size: 20 # hibernate.jdbc.batch_size: 20
@@ -77,7 +84,9 @@ hapi:
# enable_repository_validating_interceptor: false # enable_repository_validating_interceptor: false
# enable_index_missing_fields: false # enable_index_missing_fields: false
# enable_index_contained_resource: false # enable_index_contained_resource: false
# enforce_referential_integrity_on_delete: false # advanced_lucene_indexing: false
advanced_lucene_indexing: true
# enforce_referential_integrity_on_delete: false
# enforce_referential_integrity_on_write: false # enforce_referential_integrity_on_write: false
# etag_support_enabled: true # etag_support_enabled: true
# expunge_enabled: true # expunge_enabled: true
@@ -87,6 +96,9 @@ hapi:
# filter_search_enabled: true # filter_search_enabled: true
# graphql_enabled: true # graphql_enabled: true
# narrative_enabled: true # narrative_enabled: true
# mdm_enabled: true
# local_base_urls:
# - https://hapi.fhir.org/baseR4
mdm_enabled: true mdm_enabled: true
# partitioning: # partitioning:
# allow_references_across_partitions: false # allow_references_across_partitions: false
@@ -102,7 +114,11 @@ hapi:
search-coord-max-pool-size: 100 search-coord-max-pool-size: 100
search-coord-queue-capacity: 200 search-coord-queue-capacity: 200
# logger: # Threadpool size for BATCH'ed GETs in a bundle.
# bundle_batch_pool_size: 10
# bundle_batch_pool_max_size: 50
# logger:
# error_format: 'ERROR - ${requestVerb} ${requestUrl}' # error_format: 'ERROR - ${requestVerb} ${requestUrl}'
# format: >- # format: >-
# Path[${servletPath}] Source[${requestHeader.x-forwarded-for}] # Path[${servletPath}] Source[${requestHeader.x-forwarded-for}]
@@ -145,6 +161,7 @@ hapi:
# startTlsRequired: # startTlsRequired:
# quitWait: # quitWait:
# lastn_enabled: true # lastn_enabled: true
# store_resource_in_lucene_index_enabled: true
### This is configuration for normalized quantity serach level default is 0 ### This is configuration for normalized quantity serach level default is 0
### 0: NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED - default ### 0: NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED - default
### 1: NORMALIZED_QUANTITY_STORAGE_SUPPORTED ### 1: NORMALIZED_QUANTITY_STORAGE_SUPPORTED

View File

@@ -42,6 +42,7 @@ import org.testcontainers.elasticsearch.ElasticsearchContainer;
"spring.datasource.url=jdbc:h2:mem:dbr4", "spring.datasource.url=jdbc:h2:mem:dbr4",
"hapi.fhir.fhir_version=r4", "hapi.fhir.fhir_version=r4",
"hapi.fhir.lastn_enabled=true", "hapi.fhir.lastn_enabled=true",
"hapi.fhir.store_resource_in_lucene_index_enabled=true",
"elasticsearch.enabled=true", "elasticsearch.enabled=true",
// Because the port is set randomly, we will set the rest_url using the Initializer. // Because the port is set randomly, we will set the rest_url using the Initializer.
// "elasticsearch.rest_url='http://localhost:9200'", // "elasticsearch.rest_url='http://localhost:9200'",

View File

@@ -21,7 +21,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
"spring.batch.job.enabled=false", "spring.batch.job.enabled=false",
"hapi.fhir.fhir_version=dstu2", "hapi.fhir.fhir_version=dstu2",
"spring.datasource.url=jdbc:h2:mem:dbr2", "spring.datasource.url=jdbc:h2:mem:dbr2",
"spring.main.allow-bean-definition-overriding=true"
}) })
public class ExampleServerDstu2IT { public class ExampleServerDstu2IT {

View File

@@ -46,7 +46,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
"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",
"spring.main.allow-bean-definition-overriding=true"
}) })

View File

@@ -1,9 +1,12 @@
package ca.uhn.fhir.jpa.starter; package ca.uhn.fhir.jpa.starter;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.rest.api.CacheControlDirective; import ca.uhn.fhir.rest.api.CacheControlDirective;
import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
@@ -86,6 +89,57 @@ class ExampleServerR4IT {
} }
} }
@Test
public void testBatchPutWithIdenticalTags() {
String batchPuts = "{\n" +
"\t\"resourceType\": \"Bundle\",\n" +
"\t\"id\": \"patients\",\n" +
"\t\"type\": \"batch\",\n" +
"\t\"entry\": [\n" +
"\t\t{\n" +
"\t\t\t\"request\": {\n" +
"\t\t\t\t\"method\": \"PUT\",\n" +
"\t\t\t\t\"url\": \"Patient/pat-1\"\n" +
"\t\t\t},\n" +
"\t\t\t\"resource\": {\n" +
"\t\t\t\t\"resourceType\": \"Patient\",\n" +
"\t\t\t\t\"id\": \"pat-1\",\n" +
"\t\t\t\t\"meta\": {\n" +
"\t\t\t\t\t\"tag\": [\n" +
"\t\t\t\t\t\t{\n" +
"\t\t\t\t\t\t\t\"system\": \"http://mysystem.org\",\n" +
"\t\t\t\t\t\t\t\"code\": \"value2\"\n" +
"\t\t\t\t\t\t}\n" +
"\t\t\t\t\t]\n" +
"\t\t\t\t}\n" +
"\t\t\t},\n" +
"\t\t\t\"fullUrl\": \"/Patient/pat-1\"\n" +
"\t\t},\n" +
"\t\t{\n" +
"\t\t\t\"request\": {\n" +
"\t\t\t\t\"method\": \"PUT\",\n" +
"\t\t\t\t\"url\": \"Patient/pat-2\"\n" +
"\t\t\t},\n" +
"\t\t\t\"resource\": {\n" +
"\t\t\t\t\"resourceType\": \"Patient\",\n" +
"\t\t\t\t\"id\": \"pat-2\",\n" +
"\t\t\t\t\"meta\": {\n" +
"\t\t\t\t\t\"tag\": [\n" +
"\t\t\t\t\t\t{\n" +
"\t\t\t\t\t\t\t\"system\": \"http://mysystem.org\",\n" +
"\t\t\t\t\t\t\t\"code\": \"value2\"\n" +
"\t\t\t\t\t\t}\n" +
"\t\t\t\t\t]\n" +
"\t\t\t\t}\n" +
"\t\t\t},\n" +
"\t\t\t\"fullUrl\": \"/Patient/pat-2\"\n" +
"\t\t}\n" +
"\t]\n" +
"}";
Bundle bundle = FhirContext.forR4().newJsonParser().parseResource(Bundle.class, batchPuts);
ourClient.transaction().withBundle(bundle).execute();
}
@Test @Test
@Order(1) @Order(1)
void testWebsocketSubscription() throws Exception { void testWebsocketSubscription() throws Exception {

View File

@@ -39,7 +39,6 @@ import org.springframework.test.context.junit.jupiter.SpringExtension;
"hapi.fhir.fhir_version=r5", "hapi.fhir.fhir_version=r5",
"hapi.fhir.subscription.websocket_enabled=true", "hapi.fhir.subscription.websocket_enabled=true",
"hapi.fhir.subscription.websocket_enabled=true", "hapi.fhir.subscription.websocket_enabled=true",
"spring.main.allow-bean-definition-overriding=true"
}) })
public class ExampleServerR5IT { public class ExampleServerR5IT {

View File

@@ -25,7 +25,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
"hapi.fhir.fhir_version=r4", "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",
"spring.main.allow-bean-definition-overriding=true"
}) })
public class MultitenantServerR4IT { public class MultitenantServerR4IT {

View File

@@ -35,6 +35,8 @@ hapi:
# fhirpath_interceptor_enabled: false # fhirpath_interceptor_enabled: false
# filter_search_enabled: true # filter_search_enabled: true
# graphql_enabled: true # graphql_enabled: true
# local_base_urls:
# - http://hapi.fhir.org/baseR4
#partitioning: #partitioning:
# cross_partition_reference_mode: true # cross_partition_reference_mode: true
# multitenancy_enabled: true # multitenancy_enabled: true