Merge pull request #102 from hapifhir/rel_5_1_0
Start Release branch for 5.1.0
This commit is contained in:
@@ -209,6 +209,10 @@ The server may be configured with subscription support by enabling properties in
|
|||||||
|
|
||||||
- `subscription.websocket.enabled` - Enables websocket subscriptions. With this enabled, your server will accept incoming websocket connections on the following URL (this example uses the default context path and port, you may need to tweak depending on your deployment environment): [ws://localhost:8080/hapi-fhir-jpaserver/websocket](ws://localhost:8080/hapi-fhir-jpaserver/websocket)
|
- `subscription.websocket.enabled` - Enables websocket subscriptions. With this enabled, your server will accept incoming websocket connections on the following URL (this example uses the default context path and port, you may need to tweak depending on your deployment environment): [ws://localhost:8080/hapi-fhir-jpaserver/websocket](ws://localhost:8080/hapi-fhir-jpaserver/websocket)
|
||||||
|
|
||||||
|
## Enabling EMPI
|
||||||
|
|
||||||
|
Set `empi.enabled=true` in the [hapi.properties](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/hapi.properties) file to enable EMPI on this server. The EMPI matching rules are configured in [empi-rules.json](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/empi-rules.json). The rules in this example file should be replaced with actual matching rules appropriate to your data. Note that EMPI relies on subscriptions, so for EMPI to work, subscriptions must be enabled.
|
||||||
|
|
||||||
## Using Elasticsearch
|
## Using Elasticsearch
|
||||||
|
|
||||||
By default, the server will use embedded lucene indexes for terminology and fulltext indexing purposes. You can switch this to using lucene by editing the properties in [hapi.properties](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/hapi.properties)
|
By default, the server will use embedded lucene indexes for terminology and fulltext indexing purposes. You can switch this to using lucene by editing the properties in [hapi.properties](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/hapi.properties)
|
||||||
|
|||||||
52
pom.xml
52
pom.xml
@@ -11,7 +11,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.0.2</version>
|
<version>5.1.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hapi-fhir-jpaserver-starter</artifactId>
|
<artifactId>hapi-fhir-jpaserver-starter</artifactId>
|
||||||
@@ -20,6 +20,20 @@
|
|||||||
<maven>3.5.0</maven>
|
<maven>3.5.0</maven>
|
||||||
</prerequisites>
|
</prerequisites>
|
||||||
|
|
||||||
|
<packaging>war</packaging>
|
||||||
|
|
||||||
|
<name>HAPI FHIR JPA Server - Starter Project</name>
|
||||||
|
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>oss-snapshots</id>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>true</enabled>
|
||||||
|
</snapshots>
|
||||||
|
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.eclipse.jetty.websocket</groupId>
|
<groupId>org.eclipse.jetty.websocket</groupId>
|
||||||
@@ -84,7 +98,12 @@
|
|||||||
</exclusion>
|
</exclusion>
|
||||||
</exclusions>
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- This dependency includes the JPA EMPI Server -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
|
<artifactId>hapi-fhir-jpaserver-empi</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
<!-- This dependency is used for the "FHIR Tester" web app overlay -->
|
<!-- This dependency is used for the "FHIR Tester" web app overlay -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
@@ -249,23 +268,23 @@
|
|||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.awaitility</groupId>
|
||||||
|
<artifactId>awaitility</artifactId>
|
||||||
|
<version>4.0.0-rc1</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- TODO: remove this and migrate to JU5 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<version>4.12</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<packaging>war</packaging>
|
|
||||||
|
|
||||||
<name>HAPI FHIR JPA Server - Starter Project</name>
|
|
||||||
|
|
||||||
<repositories>
|
|
||||||
<repository>
|
|
||||||
<id>oss-snapshots</id>
|
|
||||||
<snapshots>
|
|
||||||
<enabled>false</enabled>
|
|
||||||
</snapshots>
|
|
||||||
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
|
|
||||||
</repository>
|
|
||||||
</repositories>
|
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|
||||||
<!-- Tells Maven to name the generated WAR file as hapi-fhir-jpaserver.war -->
|
<!-- Tells Maven to name the generated WAR file as hapi-fhir-jpaserver.war -->
|
||||||
@@ -421,6 +440,7 @@
|
|||||||
<ignoredResourcePatterns>
|
<ignoredResourcePatterns>
|
||||||
<ignoredResourcePattern>.*\.txt$</ignoredResourcePattern>
|
<ignoredResourcePattern>.*\.txt$</ignoredResourcePattern>
|
||||||
<ignoredResourcePattern>.*\.html$</ignoredResourcePattern>
|
<ignoredResourcePattern>.*\.html$</ignoredResourcePattern>
|
||||||
|
<ignoredResourcePattern>config/favicon.ico</ignoredResourcePattern>
|
||||||
</ignoredResourcePatterns>
|
</ignoredResourcePatterns>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package ca.uhn.fhir.jpa.starter;
|
package ca.uhn.fhir.jpa.starter;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||||
|
import ca.uhn.fhir.jpa.empi.config.EmpiConsumerConfig;
|
||||||
|
import ca.uhn.fhir.jpa.empi.config.EmpiSubmitterConfig;
|
||||||
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;
|
||||||
@@ -35,6 +37,12 @@ public class ApplicationContext extends AnnotationConfigWebApplicationContext {
|
|||||||
register(SubscriptionChannelConfig.class);
|
register(SubscriptionChannelConfig.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (HapiProperties.getEmpiEnabled()) {
|
||||||
|
register(EmpiSubmitterConfig.class);
|
||||||
|
register(EmpiConsumerConfig.class);
|
||||||
|
register(EmpiConfig.class);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,8 @@ import ca.uhn.fhir.interceptor.api.IInterceptorService;
|
|||||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||||
import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao;
|
import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao;
|
||||||
import ca.uhn.fhir.jpa.api.rp.ResourceProviderFactory;
|
|
||||||
import ca.uhn.fhir.jpa.binstore.BinaryStorageInterceptor;
|
import ca.uhn.fhir.jpa.binstore.BinaryStorageInterceptor;
|
||||||
import ca.uhn.fhir.jpa.bulk.BulkDataExportProvider;
|
import ca.uhn.fhir.jpa.bulk.provider.BulkDataExportProvider;
|
||||||
import ca.uhn.fhir.jpa.interceptor.CascadingDeleteInterceptor;
|
import ca.uhn.fhir.jpa.interceptor.CascadingDeleteInterceptor;
|
||||||
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.GraphQLProvider;
|
||||||
@@ -37,6 +36,7 @@ import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
|
|||||||
import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor;
|
import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.ResponseValidatingInterceptor;
|
import ca.uhn.fhir.rest.server.interceptor.ResponseValidatingInterceptor;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.partition.RequestTenantPartitionInterceptor;
|
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.tenant.UrlBaseTenantIdentificationStrategy;
|
||||||
import ca.uhn.fhir.validation.IValidatorModule;
|
import ca.uhn.fhir.validation.IValidatorModule;
|
||||||
import ca.uhn.fhir.validation.ResultSeverityEnum;
|
import ca.uhn.fhir.validation.ResultSeverityEnum;
|
||||||
|
|||||||
31
src/main/java/ca/uhn/fhir/jpa/starter/EmpiConfig.java
Normal file
31
src/main/java/ca/uhn/fhir/jpa/starter/EmpiConfig.java
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package ca.uhn.fhir.jpa.starter;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.empi.api.IEmpiSettings;
|
||||||
|
import ca.uhn.fhir.empi.rules.config.EmpiRuleValidator;
|
||||||
|
import ca.uhn.fhir.empi.rules.config.EmpiSettings;
|
||||||
|
import ca.uhn.fhir.jpa.empi.svc.EmpiSearchParamSvc;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.channel.subscription.IChannelNamer;
|
||||||
|
import ca.uhn.fhir.rest.server.util.ISearchParamRetriever;
|
||||||
|
import com.google.common.base.Charsets;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.core.io.DefaultResourceLoader;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class EmpiConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
IEmpiSettings empiSettings(EmpiRuleValidator theEmpiRuleValidator) throws IOException {
|
||||||
|
DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
|
||||||
|
Resource resource = resourceLoader.getResource("empi-rules.json");
|
||||||
|
String json = IOUtils.toString(resource.getInputStream(), Charsets.UTF_8);
|
||||||
|
return new EmpiSettings(theEmpiRuleValidator).setEnabled(HapiProperties.getEmpiEnabled()).setScriptText(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -10,7 +10,6 @@ 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 org.apache.commons.dbcp2.BasicDataSource;
|
import org.apache.commons.dbcp2.BasicDataSource;
|
||||||
import org.hl7.fhir.dstu2.model.Subscription;
|
import org.hl7.fhir.dstu2.model.Subscription;
|
||||||
import org.springframework.beans.factory.BeanFactory;
|
|
||||||
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.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
@@ -25,7 +24,7 @@ import java.sql.Driver;
|
|||||||
* This is the primary configuration file for the example server
|
* This is the primary configuration file for the example server
|
||||||
*/
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableTransactionManagement()
|
@EnableTransactionManagement
|
||||||
public class FhirServerConfigCommon {
|
public class FhirServerConfigCommon {
|
||||||
|
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirServerConfigCommon.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirServerConfigCommon.class);
|
||||||
@@ -205,11 +204,10 @@ public class FhirServerConfigCommon {
|
|||||||
retVal.setSmtpServerPort(this.emailPort);
|
retVal.setSmtpServerPort(this.emailPort);
|
||||||
retVal.setSmtpServerUsername(this.emailUsername);
|
retVal.setSmtpServerUsername(this.emailUsername);
|
||||||
retVal.setSmtpServerPassword(this.emailPassword);
|
retVal.setSmtpServerPassword(this.emailPassword);
|
||||||
// TODO KHS add these when HAPI 4.2.0 is released
|
retVal.setAuth(this.emailAuth);
|
||||||
// retVal.setAuth(this.emailAuth);
|
retVal.setStartTlsEnable(this.emailStartTlsEnable);
|
||||||
// retVal.setStartTlsEnable(this.emailStartTlsEnable);
|
retVal.setStartTlsRequired(this.emailStartTlsRequired);
|
||||||
// retVal.setStartTlsRequired(this.emailStartTlsRequired);
|
retVal.setQuitWait(this.emailQuitWait);
|
||||||
// retVal.setQuitWait(this.emailQuitWait);
|
|
||||||
|
|
||||||
SubscriptionDeliveryHandlerFactory subscriptionDeliveryHandlerFactory = myAppCtx.getBean(SubscriptionDeliveryHandlerFactory.class);
|
SubscriptionDeliveryHandlerFactory subscriptionDeliveryHandlerFactory = myAppCtx.getBean(SubscriptionDeliveryHandlerFactory.class);
|
||||||
Validate.notNull(subscriptionDeliveryHandlerFactory, "No subscription delivery handler");
|
Validate.notNull(subscriptionDeliveryHandlerFactory, "No subscription delivery handler");
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
|||||||
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.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Primary;
|
||||||
import org.springframework.orm.jpa.JpaTransactionManager;
|
import org.springframework.orm.jpa.JpaTransactionManager;
|
||||||
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
||||||
|
|
||||||
@@ -47,8 +48,9 @@ public class FhirServerConfigDstu2 extends BaseJavaConfigDstu2 {
|
|||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean()
|
@Bean
|
||||||
public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
|
@Primary
|
||||||
|
public JpaTransactionManager hapiTransactionManager(EntityManagerFactory entityManagerFactory) {
|
||||||
JpaTransactionManager retVal = new JpaTransactionManager();
|
JpaTransactionManager retVal = new JpaTransactionManager();
|
||||||
retVal.setEntityManagerFactory(entityManagerFactory);
|
retVal.setEntityManagerFactory(entityManagerFactory);
|
||||||
return retVal;
|
return retVal;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
|||||||
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.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Primary;
|
||||||
import org.springframework.orm.jpa.JpaTransactionManager;
|
import org.springframework.orm.jpa.JpaTransactionManager;
|
||||||
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
||||||
|
|
||||||
@@ -32,7 +33,7 @@ public class FhirServerConfigDstu3 extends BaseJavaConfigDstu3 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Bean()
|
@Bean
|
||||||
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
||||||
LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory();
|
LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory();
|
||||||
retVal.setPersistenceUnitName("HAPI_PU");
|
retVal.setPersistenceUnitName("HAPI_PU");
|
||||||
@@ -47,8 +48,9 @@ public class FhirServerConfigDstu3 extends BaseJavaConfigDstu3 {
|
|||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean()
|
@Bean
|
||||||
public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
|
@Primary
|
||||||
|
public JpaTransactionManager hapiTransactionManager(EntityManagerFactory entityManagerFactory) {
|
||||||
JpaTransactionManager retVal = new JpaTransactionManager();
|
JpaTransactionManager retVal = new JpaTransactionManager();
|
||||||
retVal.setEntityManagerFactory(entityManagerFactory);
|
retVal.setEntityManagerFactory(entityManagerFactory);
|
||||||
return retVal;
|
return retVal;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
|||||||
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.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Primary;
|
||||||
import org.springframework.orm.jpa.JpaTransactionManager;
|
import org.springframework.orm.jpa.JpaTransactionManager;
|
||||||
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
||||||
|
|
||||||
@@ -47,8 +48,9 @@ public class FhirServerConfigR4 extends BaseJavaConfigR4 {
|
|||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean()
|
@Bean
|
||||||
public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
|
@Primary
|
||||||
|
public JpaTransactionManager hapiTransactionManager(EntityManagerFactory entityManagerFactory) {
|
||||||
JpaTransactionManager retVal = new JpaTransactionManager();
|
JpaTransactionManager retVal = new JpaTransactionManager();
|
||||||
retVal.setEntityManagerFactory(entityManagerFactory);
|
retVal.setEntityManagerFactory(entityManagerFactory);
|
||||||
return retVal;
|
return retVal;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
|||||||
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.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Primary;
|
||||||
import org.springframework.orm.jpa.JpaTransactionManager;
|
import org.springframework.orm.jpa.JpaTransactionManager;
|
||||||
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
||||||
|
|
||||||
@@ -47,8 +48,9 @@ public class FhirServerConfigR5 extends BaseJavaConfigR5 {
|
|||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean()
|
@Bean
|
||||||
public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
|
@Primary
|
||||||
|
public JpaTransactionManager hapiTransactionManager(EntityManagerFactory entityManagerFactory) {
|
||||||
JpaTransactionManager retVal = new JpaTransactionManager();
|
JpaTransactionManager retVal = new JpaTransactionManager();
|
||||||
retVal.setEntityManagerFactory(entityManagerFactory);
|
retVal.setEntityManagerFactory(entityManagerFactory);
|
||||||
return retVal;
|
return retVal;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import ca.uhn.fhir.jpa.search.elastic.ElasticsearchHibernatePropertiesBuilder;
|
|||||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||||
import ca.uhn.fhir.rest.server.ETagSupportEnum;
|
import ca.uhn.fhir.rest.server.ETagSupportEnum;
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.hibernate.search.elasticsearch.cfg.ElasticsearchIndexStatus;
|
import org.hibernate.search.elasticsearch.cfg.ElasticsearchIndexStatus;
|
||||||
import org.hibernate.search.elasticsearch.cfg.IndexSchemaManagementStrategy;
|
import org.hibernate.search.elasticsearch.cfg.IndexSchemaManagementStrategy;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@@ -59,6 +60,9 @@ public class HapiProperties {
|
|||||||
static final String SUBSCRIPTION_EMAIL_ENABLED = "subscription.email.enabled";
|
static final String SUBSCRIPTION_EMAIL_ENABLED = "subscription.email.enabled";
|
||||||
static final String SUBSCRIPTION_RESTHOOK_ENABLED = "subscription.resthook.enabled";
|
static final String SUBSCRIPTION_RESTHOOK_ENABLED = "subscription.resthook.enabled";
|
||||||
static final String SUBSCRIPTION_WEBSOCKET_ENABLED = "subscription.websocket.enabled";
|
static final String SUBSCRIPTION_WEBSOCKET_ENABLED = "subscription.websocket.enabled";
|
||||||
|
static final String EMPI_ENABLED = "empi.enabled";
|
||||||
|
static final String PARTITIONING_ENABLED = "partitioning.enabled";
|
||||||
|
static final String PARTITIONING_CROSS_PARTITION_REFERENCE_MODE = "partitioning.cross_partition_reference_mode";
|
||||||
static final String ALLOWED_BUNDLE_TYPES = "allowed_bundle_types";
|
static final String ALLOWED_BUNDLE_TYPES = "allowed_bundle_types";
|
||||||
static final String TEST_PORT = "test.port";
|
static final String TEST_PORT = "test.port";
|
||||||
static final String TESTER_CONFIG_REFUSE_TO_FETCH_THIRD_PARTY_URLS = "tester.config.refuse_to_fetch_third_party_urls";
|
static final String TESTER_CONFIG_REFUSE_TO_FETCH_THIRD_PARTY_URLS = "tester.config.refuse_to_fetch_third_party_urls";
|
||||||
@@ -76,8 +80,8 @@ public class HapiProperties {
|
|||||||
static final String EXPIRE_SEARCH_RESULTS_AFTER_MINS = "retain_cached_searches_mins";
|
static final String EXPIRE_SEARCH_RESULTS_AFTER_MINS = "retain_cached_searches_mins";
|
||||||
static final String MAX_BINARY_SIZE = "max_binary_size";
|
static final String MAX_BINARY_SIZE = "max_binary_size";
|
||||||
static final String PARTITIONING_MULTITENANCY_ENABLED = "partitioning.multitenancy.enabled";
|
static final String PARTITIONING_MULTITENANCY_ENABLED = "partitioning.multitenancy.enabled";
|
||||||
|
private static final String PARTITIONING_INCLUDE_PARTITION_IN_SEARCH_HASHES = "partitioning.partitioning_include_in_search_hashes";
|
||||||
static final String CLIENT_ID_STRATEGY = "daoconfig.client_id_strategy";
|
static final String CLIENT_ID_STRATEGY = "daoconfig.client_id_strategy";
|
||||||
|
|
||||||
private static Properties ourProperties;
|
private static Properties ourProperties;
|
||||||
|
|
||||||
public static boolean isElasticSearchEnabled() {
|
public static boolean isElasticSearchEnabled() {
|
||||||
@@ -339,10 +343,6 @@ public class HapiProperties {
|
|||||||
return HapiProperties.getBooleanProperty("expunge_enabled", true);
|
return HapiProperties.getBooleanProperty("expunge_enabled", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Integer getTestPort() {
|
|
||||||
return HapiProperties.getIntegerProperty(TEST_PORT, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Boolean getTesterConfigRefustToFetchThirdPartyUrls() {
|
public static Boolean getTesterConfigRefustToFetchThirdPartyUrls() {
|
||||||
return HapiProperties.getBooleanProperty(TESTER_CONFIG_REFUSE_TO_FETCH_THIRD_PARTY_URLS, false);
|
return HapiProperties.getBooleanProperty(TESTER_CONFIG_REFUSE_TO_FETCH_THIRD_PARTY_URLS, false);
|
||||||
}
|
}
|
||||||
@@ -363,8 +363,8 @@ public class HapiProperties {
|
|||||||
public static Set<String> getSupportedResourceTypes() {
|
public static Set<String> getSupportedResourceTypes() {
|
||||||
String[] types = defaultString(getProperty("supported_resource_types")).split(",");
|
String[] types = defaultString(getProperty("supported_resource_types")).split(",");
|
||||||
return Arrays.stream(types)
|
return Arrays.stream(types)
|
||||||
.map(t -> trim(t))
|
.map(StringUtils::trim)
|
||||||
.filter(t -> isNotBlank(t))
|
.filter(StringUtils::isNotBlank)
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -392,6 +392,23 @@ public class HapiProperties {
|
|||||||
return HapiProperties.getBooleanProperty(SUBSCRIPTION_WEBSOCKET_ENABLED, false);
|
return HapiProperties.getBooleanProperty(SUBSCRIPTION_WEBSOCKET_ENABLED, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Boolean getEmpiEnabled() {
|
||||||
|
return HapiProperties.getBooleanProperty(EMPI_ENABLED, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean getPartitioningEnabled() {
|
||||||
|
return HapiProperties.getBooleanProperty(PARTITIONING_ENABLED, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String getPartitioningCrossPartitionReferenceMode() {
|
||||||
|
return HapiProperties.getProperty(PARTITIONING_CROSS_PARTITION_REFERENCE_MODE, "NOT_ALLOWED");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean getIncludePartitionInSearchHashes() {
|
||||||
|
return HapiProperties.getBooleanProperty(PARTITIONING_INCLUDE_PARTITION_IN_SEARCH_HASHES, true);
|
||||||
|
}
|
||||||
|
|
||||||
public static Boolean getAllowContainsSearches() {
|
public static Boolean getAllowContainsSearches() {
|
||||||
return HapiProperties.getBooleanProperty(ALLOW_CONTAINS_SEARCHES, true);
|
return HapiProperties.getBooleanProperty(ALLOW_CONTAINS_SEARCHES, true);
|
||||||
}
|
}
|
||||||
@@ -512,5 +529,7 @@ public class HapiProperties {
|
|||||||
public static boolean getPartitioningMultitenancyEnabled() {
|
public static boolean getPartitioningMultitenancyEnabled() {
|
||||||
return HapiProperties.getBooleanProperty(PARTITIONING_MULTITENANCY_ENABLED, false);
|
return HapiProperties.getBooleanProperty(PARTITIONING_MULTITENANCY_ENABLED, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
46
src/main/resources/empi-rules.json
Normal file
46
src/main/resources/empi-rules.json
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"candidateSearchParams": [
|
||||||
|
{
|
||||||
|
"resourceType": "Patient",
|
||||||
|
"searchParams": ["birthdate"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceType": "*",
|
||||||
|
"searchParams": ["identifier"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resourceType": "Patient",
|
||||||
|
"searchParams": ["general-practitioner"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"candidateFilterSearchParams": [
|
||||||
|
{
|
||||||
|
"resourceType": "*",
|
||||||
|
"searchParam": "active",
|
||||||
|
"fixedValue": "true"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"matchFields": [
|
||||||
|
{
|
||||||
|
"name": "cosine-given-name",
|
||||||
|
"resourceType": "*",
|
||||||
|
"resourcePath": "name.given",
|
||||||
|
"metric": "COSINE",
|
||||||
|
"matchThreshold": 0.8,
|
||||||
|
"exact": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "jaro-last-name",
|
||||||
|
"resourceType": "*",
|
||||||
|
"resourcePath": "name.family",
|
||||||
|
"metric": "JARO_WINKLER",
|
||||||
|
"matchThreshold": 0.8,
|
||||||
|
"exact": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"matchResultMap": {
|
||||||
|
"cosine-given-name" : "POSSIBLE_MATCH",
|
||||||
|
"cosine-given-name,jaro-last-name" : "MATCH"
|
||||||
|
},
|
||||||
|
"eidSystem": "http://company.io/fhir/NamingSystem/custom-eid-system"
|
||||||
|
}
|
||||||
@@ -150,8 +150,18 @@ email.password=
|
|||||||
# Enable Websocket Subscription Channel
|
# Enable Websocket Subscription Channel
|
||||||
subscription.websocket.enabled=false
|
subscription.websocket.enabled=false
|
||||||
|
|
||||||
|
###################################################
|
||||||
|
# EMPI
|
||||||
|
###################################################
|
||||||
|
empi.enabled=false
|
||||||
|
|
||||||
###################################################
|
###################################################
|
||||||
# Partitioning And Multitenancy
|
# Partitioning And Multitenancy
|
||||||
###################################################
|
###################################################
|
||||||
|
partitioning.enabled=false
|
||||||
|
partitioning.cross_partition_reference_mode=NOT_ALLOWED
|
||||||
|
partitioning.partitioning_include_in_search_hashes=true
|
||||||
partitioning.multitenancy.enabled=false
|
partitioning.multitenancy.enabled=false
|
||||||
|
|
||||||
#daoconfig.client_id_strategy=ANY
|
#daoconfig.client_id_strategy=ANY
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,25 @@
|
|||||||
</encoder>
|
</encoder>
|
||||||
</appender>
|
</appender>
|
||||||
|
|
||||||
|
<appender name="EMPI_TROUBLESHOOTING" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
|
<filter class="ch.qos.logback.classic.filter.ThresholdFilter"><level>DEBUG</level></filter>
|
||||||
|
<file>${smile.basedir}/log/empi-troubleshooting.log</file>
|
||||||
|
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
|
||||||
|
<fileNamePattern>${smile.basedir}/log/empi-troubleshooting.log.%i.gz</fileNamePattern>
|
||||||
|
<minIndex>1</minIndex>
|
||||||
|
<maxIndex>9</maxIndex>
|
||||||
|
</rollingPolicy>
|
||||||
|
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
|
||||||
|
<maxFileSize>5MB</maxFileSize>
|
||||||
|
</triggeringPolicy>
|
||||||
|
<encoder>
|
||||||
|
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n${log.stackfilter.pattern}</pattern>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
<logger name="ca.uhn.fhir.log.empi_troubleshooting" level="TRACE">
|
||||||
|
<appender-ref ref="EMPI_TROUBLESHOOTING"/>
|
||||||
|
</logger>
|
||||||
|
|
||||||
<root level="INFO">
|
<root level="INFO">
|
||||||
<appender-ref ref="STDOUT" />
|
<appender-ref ref="STDOUT" />
|
||||||
</root>
|
</root>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import ca.uhn.fhir.rest.client.api.IGenericClient;
|
|||||||
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
|
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
|
||||||
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
||||||
import ca.uhn.fhir.test.utilities.JettyUtil;
|
import ca.uhn.fhir.test.utilities.JettyUtil;
|
||||||
|
import ca.uhn.fhir.util.BundleUtil;
|
||||||
import org.eclipse.jetty.server.Server;
|
import org.eclipse.jetty.server.Server;
|
||||||
import org.eclipse.jetty.webapp.WebAppContext;
|
import org.eclipse.jetty.webapp.WebAppContext;
|
||||||
import org.eclipse.jetty.websocket.api.Session;
|
import org.eclipse.jetty.websocket.api.Session;
|
||||||
@@ -17,6 +18,8 @@ import org.hl7.fhir.instance.model.api.IIdType;
|
|||||||
import org.hl7.fhir.r4.model.Bundle;
|
import org.hl7.fhir.r4.model.Bundle;
|
||||||
import org.hl7.fhir.r4.model.Observation;
|
import org.hl7.fhir.r4.model.Observation;
|
||||||
import org.hl7.fhir.r4.model.Patient;
|
import org.hl7.fhir.r4.model.Patient;
|
||||||
|
import org.hl7.fhir.r4.model.Person;
|
||||||
|
import org.hl7.fhir.r4.model.Reference;
|
||||||
import org.hl7.fhir.r4.model.Subscription;
|
import org.hl7.fhir.r4.model.Subscription;
|
||||||
import org.junit.AfterClass;
|
import org.junit.AfterClass;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
@@ -24,11 +27,16 @@ import org.junit.Test;
|
|||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import static ca.uhn.fhir.util.TestUtil.waitForSize;
|
import static ca.uhn.fhir.util.TestUtil.waitForSize;
|
||||||
|
import static org.awaitility.Awaitility.await;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
public class ExampleServerR4IT {
|
public class ExampleServerR4IT {
|
||||||
|
|
||||||
@@ -43,6 +51,7 @@ public class ExampleServerR4IT {
|
|||||||
HapiProperties.setProperty(HapiProperties.DATASOURCE_URL, "jdbc:h2:mem:dbr4");
|
HapiProperties.setProperty(HapiProperties.DATASOURCE_URL, "jdbc:h2:mem:dbr4");
|
||||||
HapiProperties.setProperty(HapiProperties.FHIR_VERSION, "R4");
|
HapiProperties.setProperty(HapiProperties.FHIR_VERSION, "R4");
|
||||||
HapiProperties.setProperty(HapiProperties.SUBSCRIPTION_WEBSOCKET_ENABLED, "true");
|
HapiProperties.setProperty(HapiProperties.SUBSCRIPTION_WEBSOCKET_ENABLED, "true");
|
||||||
|
HapiProperties.setProperty(HapiProperties.EMPI_ENABLED, "true");
|
||||||
ourCtx = FhirContext.forR4();
|
ourCtx = FhirContext.forR4();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,6 +66,27 @@ public class ExampleServerR4IT {
|
|||||||
|
|
||||||
Patient pt2 = ourClient.read().resource(Patient.class).withId(id).execute();
|
Patient pt2 = ourClient.read().resource(Patient.class).withId(id).execute();
|
||||||
assertEquals(methodName, pt2.getName().get(0).getFamily());
|
assertEquals(methodName, pt2.getName().get(0).getFamily());
|
||||||
|
|
||||||
|
// Test EMPI
|
||||||
|
|
||||||
|
// Wait until the EMPI message has been processed
|
||||||
|
await().until(() -> getPeople().size() > 0);
|
||||||
|
List<Person> persons = getPeople();
|
||||||
|
|
||||||
|
// Verify a Person was created that links to our Patient
|
||||||
|
Optional<String> personLinkToCreatedPatient = persons.stream()
|
||||||
|
.map(Person::getLink)
|
||||||
|
.flatMap(Collection::stream)
|
||||||
|
.map(Person.PersonLinkComponent::getTarget)
|
||||||
|
.map(Reference::getReference)
|
||||||
|
.filter(pid -> id.toUnqualifiedVersionless().getValue().equals(pid))
|
||||||
|
.findAny();
|
||||||
|
assertTrue(personLinkToCreatedPatient.isPresent());
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Person> getPeople() {
|
||||||
|
Bundle bundle = ourClient.search().forResource(Person.class).cacheControl(new CacheControlDirective().setNoCache(true)).returnBundle(Bundle.class).execute();
|
||||||
|
return BundleUtil.toListOfResourcesOfType(ourCtx, bundle, Person.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -78,7 +108,7 @@ public class ExampleServerR4IT {
|
|||||||
IIdType mySubscriptionId = methodOutcome.getId();
|
IIdType mySubscriptionId = methodOutcome.getId();
|
||||||
|
|
||||||
// Wait for the subscription to be activated
|
// Wait for the subscription to be activated
|
||||||
waitForSize(1, () -> ourClient.search().forResource(Subscription.class).where(Subscription.STATUS.exactly().code("active")).cacheControl(new CacheControlDirective().setNoCache(true)).returnBundle(Bundle.class).execute().getEntry().size());
|
await().until(() -> activeSubscriptionCount() == 3);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Attach websocket
|
* Attach websocket
|
||||||
@@ -117,6 +147,10 @@ public class ExampleServerR4IT {
|
|||||||
ourClient.delete().resourceById(mySubscriptionId).execute();
|
ourClient.delete().resourceById(mySubscriptionId).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().size();
|
||||||
|
}
|
||||||
|
|
||||||
@AfterClass
|
@AfterClass
|
||||||
public static void afterClass() throws Exception {
|
public static void afterClass() throws Exception {
|
||||||
ourServer.stop();
|
ourServer.stop();
|
||||||
|
|||||||
@@ -1,12 +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.model.util.ProviderConstants;
|
|
||||||
import ca.uhn.fhir.rest.api.CacheControlDirective;
|
import ca.uhn.fhir.rest.api.CacheControlDirective;
|
||||||
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;
|
||||||
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
||||||
import ca.uhn.fhir.rest.client.interceptor.UrlTenantSelectionInterceptor;
|
import ca.uhn.fhir.rest.client.interceptor.UrlTenantSelectionInterceptor;
|
||||||
|
import ca.uhn.fhir.rest.server.provider.ProviderConstants;
|
||||||
import ca.uhn.fhir.test.utilities.JettyUtil;
|
import ca.uhn.fhir.test.utilities.JettyUtil;
|
||||||
import org.eclipse.jetty.server.Server;
|
import org.eclipse.jetty.server.Server;
|
||||||
import org.eclipse.jetty.webapp.WebAppContext;
|
import org.eclipse.jetty.webapp.WebAppContext;
|
||||||
@@ -37,6 +37,7 @@ public class MultitenantServerR4IT {
|
|||||||
HapiProperties.setProperty(HapiProperties.DATASOURCE_URL, "jdbc:h2:mem:dbr4-mt");
|
HapiProperties.setProperty(HapiProperties.DATASOURCE_URL, "jdbc:h2:mem:dbr4-mt");
|
||||||
HapiProperties.setProperty(HapiProperties.FHIR_VERSION, "R4");
|
HapiProperties.setProperty(HapiProperties.FHIR_VERSION, "R4");
|
||||||
HapiProperties.setProperty(HapiProperties.SUBSCRIPTION_WEBSOCKET_ENABLED, "true");
|
HapiProperties.setProperty(HapiProperties.SUBSCRIPTION_WEBSOCKET_ENABLED, "true");
|
||||||
|
HapiProperties.setProperty(HapiProperties.PARTITIONING_ENABLED, "true");
|
||||||
HapiProperties.setProperty(HapiProperties.PARTITIONING_MULTITENANCY_ENABLED, "true");
|
HapiProperties.setProperty(HapiProperties.PARTITIONING_MULTITENANCY_ENABLED, "true");
|
||||||
ourCtx = FhirContext.forR4();
|
ourCtx = FhirContext.forR4();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user