diff --git a/pom.xml b/pom.xml
index d52cc93..771de2a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -14,7 +14,7 @@
ca.uhn.hapi.fhir
hapi-fhir
- 7.6.0
+ 8.0.0
hapi-fhir-jpaserver-starter
diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java b/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java
index f4aa66d..58c5bac 100644
--- a/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java
+++ b/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java
@@ -23,6 +23,11 @@ import java.util.Set;
@EnableConfigurationProperties
public class AppProperties {
+ private final Set auto_version_reference_at_paths = new HashSet<>();
+ private final Set local_base_urls = new HashSet<>();
+ private final Set logical_urls = new HashSet<>();
+ private final List custom_interceptor_classes = new ArrayList<>();
+ private final List custom_provider_classes = new ArrayList<>();
private Boolean cr_enabled = false;
private Boolean ips_enabled = false;
private Boolean openapi_enabled = false;
@@ -37,7 +42,6 @@ public class AppProperties {
private Boolean allow_override_default_search_params = true;
private Boolean auto_create_placeholder_reference_targets = false;
private Boolean mass_ingestion_mode_enabled = false;
- private final Set auto_version_reference_at_paths = new HashSet<>();
private Boolean language_search_parameter_enabled = false;
private Boolean dao_scheduling_enabled = true;
private Boolean delete_expunge_enabled = false;
@@ -70,9 +74,7 @@ public class AppProperties {
private List supported_resource_types = new ArrayList<>();
private List allowed_bundle_types = null;
private Boolean narrative_enabled = true;
-
private Boolean ig_runtime_upload_enabled = false;
-
private Validation validation = new Validation();
private Map tester = null;
private Logger logger = new Logger();
@@ -82,28 +84,17 @@ public class AppProperties {
private Boolean validate_resource_status_for_package_upload = true;
private Boolean install_transitive_ig_dependencies = true;
private Map implementationGuides = null;
-
private String custom_content_path = null;
private String app_content_path = null;
-
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 Boolean use_apache_address_strategy = 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 final Set local_base_urls = new HashSet<>();
- private final Set logical_urls = new HashSet<>();
-
private Boolean resource_dbhistory_enabled = true;
-
- private final List custom_interceptor_classes = new ArrayList<>();
-
- private final List custom_provider_classes = new ArrayList<>();
private Boolean upliftedRefchains_enabled = false;
private boolean userRequestRetryVersionConflictsInterceptorEnabled = false;
@@ -226,6 +217,10 @@ public class AppProperties {
return subscription;
}
+ public void setSubscription(Subscription subscription) {
+ this.subscription = subscription;
+ }
+
public Boolean getDefault_pretty_print() {
return default_pretty_print;
}
@@ -234,10 +229,6 @@ public class AppProperties {
this.default_pretty_print = default_pretty_print;
}
- public void setSubscription(Subscription subscription) {
- this.subscription = subscription;
- }
-
public Validation getValidation() {
return validation;
}
@@ -671,6 +662,22 @@ public class AppProperties {
this.userRequestRetryVersionConflictsInterceptorEnabled = userRequestRetryVersionConflictsInterceptorEnabled;
}
+ public boolean getEnable_index_of_type() {
+ return enable_index_of_type;
+ }
+
+ public void setEnable_index_of_type(boolean enable_index_of_type) {
+ this.enable_index_of_type = enable_index_of_type;
+ }
+
+ public Boolean getResource_dbhistory_enabled() {
+ return resource_dbhistory_enabled;
+ }
+
+ public void setResource_dbhistory_enabled(Boolean resource_dbhistory_enabled) {
+ this.resource_dbhistory_enabled = resource_dbhistory_enabled;
+ }
+
public static class Cors {
private Boolean allow_Credentials = true;
private List allowed_origin = List.of("*");
@@ -800,6 +807,38 @@ public class AppProperties {
private Boolean partitioning_include_in_search_hashes = false;
private Boolean allow_references_across_partitions = false;
private Boolean conditional_create_duplicate_identifiers_enabled = false;
+ private Boolean database_partition_mode_enabled = false;
+ private Boolean patient_id_partitioning_mode = false;
+ private Integer default_partition_id = 0;
+ private boolean request_tenant_partitioning_mode;
+
+ public boolean isRequest_tenant_partitioning_mode() {
+ return request_tenant_partitioning_mode;
+ }
+
+ public Integer getDefault_partition_id() {
+ return default_partition_id;
+ }
+
+ public void setDefault_partition_id(Integer theDefault_partition_id) {
+ default_partition_id = theDefault_partition_id;
+ }
+
+ public Boolean getDatabase_partition_mode_enabled() {
+ return database_partition_mode_enabled;
+ }
+
+ public void setDatabase_partition_mode_enabled(Boolean theDatabase_partition_mode_enabled) {
+ database_partition_mode_enabled = theDatabase_partition_mode_enabled;
+ }
+
+ public Boolean getPatient_id_partitioning_mode() {
+ return patient_id_partitioning_mode;
+ }
+
+ public void setPatient_id_partitioning_mode(Boolean thePatient_id_partitioning_mode) {
+ patient_id_partitioning_mode = thePatient_id_partitioning_mode;
+ }
public Boolean getPartitioning_include_in_search_hashes() {
return partitioning_include_in_search_hashes;
@@ -825,10 +864,22 @@ public class AppProperties {
Boolean conditional_create_duplicate_identifiers_enabled) {
this.conditional_create_duplicate_identifiers_enabled = conditional_create_duplicate_identifiers_enabled;
}
+
+ public boolean getRequest_tenant_partitioning_mode() {
+ return request_tenant_partitioning_mode;
+ }
+
+ public void setRequest_tenant_partitioning_mode(boolean theRequest_tenant_partitioning_mode) {
+ request_tenant_partitioning_mode = theRequest_tenant_partitioning_mode;
+ }
}
public static class Subscription {
+ private Boolean resthook_enabled = false;
+ private Boolean websocket_enabled = false;
+ private Email email = null;
+
public Boolean getResthook_enabled() {
return resthook_enabled;
}
@@ -845,10 +896,6 @@ public class AppProperties {
this.websocket_enabled = websocket_enabled;
}
- private Boolean resthook_enabled = false;
- private Boolean websocket_enabled = false;
- private Email email = null;
-
public Email getEmail() {
return email;
}
@@ -858,6 +905,16 @@ public class AppProperties {
}
public static class Email {
+ private String from;
+ private String host;
+ private Integer port = 25;
+ private String username;
+ private String password;
+ private Boolean auth = false;
+ private Boolean startTlsEnable = false;
+ private Boolean startTlsRequired = false;
+ private Boolean quitWait = false;
+
public String getFrom() {
return from;
}
@@ -929,32 +986,6 @@ public class AppProperties {
public void setQuitWait(Boolean quitWait) {
this.quitWait = quitWait;
}
-
- private String from;
- private String host;
- private Integer port = 25;
- private String username;
- private String password;
- private Boolean auth = false;
- private Boolean startTlsEnable = false;
- private Boolean startTlsRequired = false;
- private Boolean quitWait = false;
}
}
-
- public boolean getEnable_index_of_type() {
- return enable_index_of_type;
- }
-
- public void setEnable_index_of_type(boolean enable_index_of_type) {
- this.enable_index_of_type = enable_index_of_type;
- }
-
- public Boolean getResource_dbhistory_enabled() {
- return resource_dbhistory_enabled;
- }
-
- public void setResource_dbhistory_enabled(Boolean resource_dbhistory_enabled) {
- this.resource_dbhistory_enabled = resource_dbhistory_enabled;
- }
}
diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/cdshooks/CdsHooksRequest.java b/src/main/java/ca/uhn/fhir/jpa/starter/cdshooks/CdsHooksRequest.java
index 088eab2..b50ae43 100644
--- a/src/main/java/ca/uhn/fhir/jpa/starter/cdshooks/CdsHooksRequest.java
+++ b/src/main/java/ca/uhn/fhir/jpa/starter/cdshooks/CdsHooksRequest.java
@@ -1,6 +1,6 @@
package ca.uhn.fhir.jpa.starter.cdshooks;
-import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestJson;
+import ca.uhn.fhir.rest.api.server.cdshooks.CdsServiceRequestJson;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@JsonIgnoreProperties({"extension"})
diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/cdshooks/CdsHooksServlet.java b/src/main/java/ca/uhn/fhir/jpa/starter/cdshooks/CdsHooksServlet.java
index 2d72e69..62a6b65 100644
--- a/src/main/java/ca/uhn/fhir/jpa/starter/cdshooks/CdsHooksServlet.java
+++ b/src/main/java/ca/uhn/fhir/jpa/starter/cdshooks/CdsHooksServlet.java
@@ -1,10 +1,10 @@
package ca.uhn.fhir.jpa.starter.cdshooks;
import ca.uhn.fhir.jpa.starter.AppProperties;
+import ca.uhn.fhir.rest.api.server.cdshooks.CdsServiceRequestJson;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.hapi.fhir.cdshooks.api.ICdsServiceRegistry;
-import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestJson;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseJson;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServicesJson;
import com.fasterxml.jackson.databind.ObjectMapper;
diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/cdshooks/ModuleConfigurationPrefetchSvc.java b/src/main/java/ca/uhn/fhir/jpa/starter/cdshooks/ModuleConfigurationPrefetchSvc.java
index d2f4f2f..12e5594 100644
--- a/src/main/java/ca/uhn/fhir/jpa/starter/cdshooks/ModuleConfigurationPrefetchSvc.java
+++ b/src/main/java/ca/uhn/fhir/jpa/starter/cdshooks/ModuleConfigurationPrefetchSvc.java
@@ -2,6 +2,9 @@ package ca.uhn.fhir.jpa.starter.cdshooks;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.i18n.Msg;
+import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
+import ca.uhn.fhir.rest.api.server.cdshooks.CdsServiceRequestAuthorizationJson;
+import ca.uhn.fhir.rest.api.server.cdshooks.CdsServiceRequestJson;
import ca.uhn.fhir.rest.client.api.IClientInterceptor;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.interceptor.BearerTokenAuthInterceptor;
@@ -11,8 +14,6 @@ import ca.uhn.fhir.util.UrlUtil;
import ca.uhn.hapi.fhir.cdshooks.api.ICdsHooksDaoAuthorizationSvc;
import ca.uhn.hapi.fhir.cdshooks.api.ICdsServiceMethod;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceJson;
-import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestAuthorizationJson;
-import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestJson;
import ca.uhn.hapi.fhir.cdshooks.svc.prefetch.CdsPrefetchDaoSvc;
import ca.uhn.hapi.fhir.cdshooks.svc.prefetch.CdsPrefetchFhirClientSvc;
import ca.uhn.hapi.fhir.cdshooks.svc.prefetch.CdsPrefetchSvc;
@@ -72,12 +73,14 @@ public class ModuleConfigurationPrefetchSvc extends CdsPrefetchSvc {
CdsResolutionStrategySvc theCdsResolutionStrategySvc,
CdsPrefetchDaoSvc theResourcePrefetchDao,
CdsPrefetchFhirClientSvc theResourcePrefetchFhirClient,
- ICdsHooksDaoAuthorizationSvc theCdsHooksDaoAuthorizationSvc) {
+ ICdsHooksDaoAuthorizationSvc theCdsHooksDaoAuthorizationSvc,
+ IInterceptorBroadcaster theInterceptorBroadcaster) {
super(
theCdsResolutionStrategySvc,
theResourcePrefetchDao,
theResourcePrefetchFhirClient,
- theCdsHooksDaoAuthorizationSvc);
+ theCdsHooksDaoAuthorizationSvc,
+ theInterceptorBroadcaster);
myResourcePrefetchFhirClient = theResourcePrefetchFhirClient;
fhirContext = theResourcePrefetchDao.getFhirContext();
}
diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/cdshooks/UpdatedCdsCrServiceR4.java b/src/main/java/ca/uhn/fhir/jpa/starter/cdshooks/UpdatedCdsCrServiceR4.java
index 51b31dc..249d477 100644
--- a/src/main/java/ca/uhn/fhir/jpa/starter/cdshooks/UpdatedCdsCrServiceR4.java
+++ b/src/main/java/ca/uhn/fhir/jpa/starter/cdshooks/UpdatedCdsCrServiceR4.java
@@ -1,8 +1,8 @@
package ca.uhn.fhir.jpa.starter.cdshooks;
import ca.uhn.fhir.rest.api.server.RequestDetails;
+import ca.uhn.fhir.rest.api.server.cdshooks.CdsServiceRequestJson;
import ca.uhn.hapi.fhir.cdshooks.api.ICdsConfigService;
-import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestJson;
import ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrServiceR4;
import org.hl7.fhir.r4.model.BooleanType;
import org.hl7.fhir.r4.model.Parameters;
diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigCommon.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigCommon.java
index e27517d..f77d3b2 100644
--- a/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigCommon.java
+++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigCommon.java
@@ -27,6 +27,8 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
import java.util.HashSet;
import java.util.stream.Collectors;
+import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
+
/**
* This is the primary configuration file for the example server
*/
@@ -168,7 +170,7 @@ public class FhirServerConfigCommon {
jpaStorageSettings.setExpireSearchResultsAfterMillis(retainCachedSearchesMinutes * 60 * 1000);
jpaStorageSettings.setFilterParameterEnabled(appProperties.getFilter_search_enabled());
- jpaStorageSettings.setAdvancedHSearchIndexing(appProperties.getAdvanced_lucene_indexing());
+ jpaStorageSettings.setHibernateSearchIndexSearchParams(appProperties.getAdvanced_lucene_indexing());
jpaStorageSettings.setTreatBaseUrlsAsLocal(new HashSet<>(appProperties.getLocal_base_urls()));
jpaStorageSettings.setTreatReferencesAsLogical(new HashSet<>(appProperties.getLogical_urls()));
@@ -237,6 +239,14 @@ public class FhirServerConfigCommon {
// Partitioning
if (appProperties.getPartitioning() != null) {
retVal.setPartitioningEnabled(true);
+ boolean databasePartitionModeEnabled =
+ defaultIfNull(appProperties.getPartitioning().getDatabase_partition_mode_enabled(), Boolean.FALSE);
+ Integer defaultPartitionId = appProperties.getPartitioning().getDefault_partition_id();
+ if (databasePartitionModeEnabled) {
+ retVal.setDatabasePartitionMode(true);
+ defaultPartitionId = defaultIfNull(defaultPartitionId, 0);
+ }
+ retVal.setDefaultPartitionId(defaultPartitionId);
retVal.setIncludePartitionInSearchHashes(
appProperties.getPartitioning().getPartitioning_include_in_search_hashes());
if (appProperties.getPartitioning().getAllow_references_across_partitions()) {
@@ -251,6 +261,11 @@ public class FhirServerConfigCommon {
return retVal;
}
+ @Bean
+ public PartitionModeConfigurer partitionModeConfigurer() {
+ return new PartitionModeConfigurer();
+ }
+
@Primary
@Bean
public HibernatePropertiesProvider jpaStarterDialectProvider(
diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/common/PartitionModeConfigurer.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/PartitionModeConfigurer.java
new file mode 100644
index 0000000..c83e8fc
--- /dev/null
+++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/PartitionModeConfigurer.java
@@ -0,0 +1,56 @@
+package ca.uhn.fhir.jpa.starter.common;
+
+import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.jpa.interceptor.PatientIdPartitionInterceptor;
+import ca.uhn.fhir.jpa.model.config.PartitionSettings;
+import ca.uhn.fhir.jpa.partition.PartitionManagementProvider;
+import ca.uhn.fhir.jpa.searchparam.extractor.ISearchParamExtractor;
+import ca.uhn.fhir.jpa.starter.AppProperties;
+import ca.uhn.fhir.rest.server.RestfulServer;
+import ca.uhn.fhir.rest.server.interceptor.partition.RequestTenantPartitionInterceptor;
+import ca.uhn.fhir.rest.server.tenant.UrlBaseTenantIdentificationStrategy;
+import jakarta.annotation.PostConstruct;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+
+public class PartitionModeConfigurer {
+ private static final Logger ourLog = LoggerFactory.getLogger(PartitionModeConfigurer.class);
+
+ @Autowired
+ private AppProperties myAppProperties;
+
+ @Autowired
+ private FhirContext myFhirContext;
+
+ @Autowired
+ private ISearchParamExtractor mySearchParamExtractor;
+
+ @Autowired
+ private PartitionSettings myPartitionSettings;
+
+ @Autowired
+ private RestfulServer myRestfulServer;
+
+ @Autowired
+ private PartitionManagementProvider myPartitionManagementProvider;
+
+ @PostConstruct
+ public void start() {
+ if (myAppProperties.getPartitioning() != null) {
+ if (myAppProperties.getPartitioning().getPatient_id_partitioning_mode() == Boolean.TRUE) {
+ ourLog.info("Partitioning mode enabled in: Patient ID partitioning mode");
+ PatientIdPartitionInterceptor patientIdInterceptor =
+ new PatientIdPartitionInterceptor(myFhirContext, mySearchParamExtractor, myPartitionSettings);
+ myRestfulServer.registerInterceptor(patientIdInterceptor);
+ myPartitionSettings.setUnnamedPartitionMode(true);
+ } else if (myAppProperties.getPartitioning().getRequest_tenant_partitioning_mode() == Boolean.TRUE) {
+ ourLog.info("Partitioning mode enabled in: Request tenant partitioning mode");
+ myRestfulServer.registerInterceptor(new RequestTenantPartitionInterceptor());
+ myRestfulServer.setTenantIdentificationStrategy(new UrlBaseTenantIdentificationStrategy());
+ }
+
+ myRestfulServer.registerProviders(myPartitionManagementProvider);
+ }
+ }
+}
diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/common/StarterJpaConfig.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/StarterJpaConfig.java
index 8824c77..167dc77 100644
--- a/src/main/java/ca/uhn/fhir/jpa/starter/common/StarterJpaConfig.java
+++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/StarterJpaConfig.java
@@ -18,7 +18,6 @@ import ca.uhn.fhir.jpa.binary.interceptor.BinaryStorageInterceptor;
import ca.uhn.fhir.jpa.binary.provider.BinaryAccessProvider;
import ca.uhn.fhir.jpa.config.util.HapiEntityManagerFactoryUtil;
import ca.uhn.fhir.jpa.config.util.ResourceCountCacheUtil;
-import ca.uhn.fhir.jpa.config.util.ValidationSupportConfigUtil;
import ca.uhn.fhir.jpa.dao.FulltextSearchSvcImpl;
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
import ca.uhn.fhir.jpa.dao.search.HSearchSortHelperImpl;
@@ -32,9 +31,14 @@ import ca.uhn.fhir.jpa.ips.provider.IpsOperationProvider;
import ca.uhn.fhir.jpa.model.config.SubscriptionSettings;
import ca.uhn.fhir.jpa.packages.IPackageInstallerSvc;
import ca.uhn.fhir.jpa.packages.PackageInstallationSpec;
-import ca.uhn.fhir.jpa.partition.PartitionManagementProvider;
+import ca.uhn.fhir.jpa.provider.DaoRegistryResourceSupportedSvc;
+import ca.uhn.fhir.jpa.provider.IJpaSystemProvider;
+import ca.uhn.fhir.jpa.provider.JpaCapabilityStatementProvider;
+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.ValueSetOperationProvider;
import ca.uhn.fhir.jpa.provider.dstu3.JpaConformanceProviderDstu3;
-import ca.uhn.fhir.jpa.provider.*;
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
import ca.uhn.fhir.jpa.search.IStaleSearchDeletingSvc;
import ca.uhn.fhir.jpa.search.StaleSearchDeletingSvcImpl;
@@ -46,23 +50,29 @@ import ca.uhn.fhir.jpa.starter.ig.IImplementationGuideOperationProvider;
import ca.uhn.fhir.jpa.starter.util.EnvironmentHelper;
import ca.uhn.fhir.jpa.subscription.util.SubscriptionDebugLogInterceptor;
import ca.uhn.fhir.jpa.util.ResourceCountCache;
-import ca.uhn.fhir.jpa.validation.JpaValidationSupportChain;
import ca.uhn.fhir.mdm.provider.MdmProviderLoader;
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
import ca.uhn.fhir.narrative2.NullNarrativeGenerator;
import ca.uhn.fhir.rest.api.IResourceSupportedSvc;
import ca.uhn.fhir.rest.openapi.OpenApiInterceptor;
-import ca.uhn.fhir.rest.server.interceptor.partition.RequestTenantPartitionInterceptor;
-import ca.uhn.fhir.rest.server.*;
-import ca.uhn.fhir.rest.server.interceptor.*;
+import ca.uhn.fhir.rest.server.ApacheProxyAddressStrategy;
+import ca.uhn.fhir.rest.server.ETagSupportEnum;
+import ca.uhn.fhir.rest.server.HardcodedServerAddressStrategy;
+import ca.uhn.fhir.rest.server.IServerConformanceProvider;
+import ca.uhn.fhir.rest.server.IncomingRequestAddressStrategy;
+import ca.uhn.fhir.rest.server.RestfulServer;
+import ca.uhn.fhir.rest.server.interceptor.CorsInterceptor;
+import ca.uhn.fhir.rest.server.interceptor.FhirPathFilterInterceptor;
+import ca.uhn.fhir.rest.server.interceptor.LoggingInterceptor;
+import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
+import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor;
+import ca.uhn.fhir.rest.server.interceptor.ResponseValidatingInterceptor;
import ca.uhn.fhir.rest.server.provider.ResourceProviderFactory;
-import ca.uhn.fhir.rest.server.tenant.UrlBaseTenantIdentificationStrategy;
import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
import ca.uhn.fhir.validation.IValidatorModule;
import ca.uhn.fhir.validation.ResultSeverityEnum;
import com.google.common.base.Strings;
import jakarta.persistence.EntityManagerFactory;
-import org.hl7.fhir.common.hapi.validation.support.CachingValidationSupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
@@ -70,14 +80,23 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.ApplicationContext;
-import org.springframework.context.annotation.*;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+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.http.HttpHeaders;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.web.cors.CorsConfiguration;
-import java.util.*;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
import javax.sql.DataSource;
import static ca.uhn.fhir.jpa.starter.common.validation.IRepositoryValidationInterceptorFactory.ENABLE_REPOSITORY_VALIDATING_INTERCEPTOR;
@@ -100,12 +119,6 @@ public class StarterJpaConfig {
return new StaleSearchDeletingSvcImpl();
}
- @Primary
- @Bean
- public CachingValidationSupport validationSupportChain(JpaValidationSupportChain theJpaValidationSupportChain) {
- return ValidationSupportConfigUtil.newCachingValidationSupport(theJpaValidationSupportChain);
- }
-
@Autowired
private ConfigurableEnvironment configurableEnvironment;
@@ -271,7 +284,6 @@ public class StarterJpaConfig {
BulkDataImportProvider bulkDataImportProvider,
ValueSetOperationProvider theValueSetOperationProvider,
ReindexProvider reindexProvider,
- PartitionManagementProvider partitionManagementProvider,
Optional repositoryValidatingInterceptor,
IPackageInstallerSvc packageInstallerSvc,
ThreadSafeResourceDeleterSvc theThreadSafeResourceDeleterSvc,
@@ -443,12 +455,7 @@ public class StarterJpaConfig {
// reindex Provider $reindex
fhirServer.registerProvider(reindexProvider);
- // Partitioning
- if (appProperties.getPartitioning() != null) {
- fhirServer.registerInterceptor(new RequestTenantPartitionInterceptor());
- fhirServer.setTenantIdentificationStrategy(new UrlBaseTenantIdentificationStrategy());
- fhirServer.registerProviders(partitionManagementProvider);
- }
+ // Validation
repositoryValidatingInterceptor.ifPresent(fhirServer::registerInterceptor);
// register custom interceptors
diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDbpmR5IT.java b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDbpmR5IT.java
new file mode 100644
index 0000000..2f6afa2
--- /dev/null
+++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDbpmR5IT.java
@@ -0,0 +1,84 @@
+package ca.uhn.fhir.jpa.starter;
+
+import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.jpa.migrate.DriverTypeEnum;
+import ca.uhn.fhir.jpa.migrate.JdbcUtils;
+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.r5.model.Patient;
+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.web.server.LocalServerPort;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+import org.springframework.transaction.PlatformTransactionManager;
+import org.springframework.transaction.support.TransactionTemplate;
+
+import javax.sql.DataSource;
+import java.sql.SQLException;
+import java.util.Set;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+@ExtendWith(SpringExtension.class)
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = {Application.class}, properties =
+ {
+ "spring.datasource.url=jdbc:h2:mem:dbr5_dbpm",
+ "hapi.fhir.fhir_version=r5",
+ "hapi.fhir.partitioning.database_partition_mode_enabled=true",
+ "hapi.fhir.partitioning.patient_id_partitioning_mode=true"
+ })
+public class ExampleServerDbpmR5IT {
+
+ private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExampleServerDstu2IT.class);
+ private IGenericClient ourClient;
+ private FhirContext ourCtx;
+
+
+ @LocalServerPort
+ private int port;
+
+ @Autowired
+ private DataSource myDataSource;
+
+ @Autowired
+ private PlatformTransactionManager myTxManager;
+
+
+ @Test
+ void testCreateAndRead() {
+ Patient pt = new Patient();
+ pt.setId("A");
+ pt.setActive(true);
+ IIdType id = ourClient.update().resource(pt).execute().getId();
+
+ Patient pt2 = ourClient.read().resource(Patient.class).withId("Patient/A").execute();
+ assertTrue(pt2.getActive());
+ }
+
+
+ @Test
+ public void testValidateSchema() throws SQLException {
+ TransactionTemplate tt = new TransactionTemplate(myTxManager);
+ DriverTypeEnum.ConnectionProperties cp = new DriverTypeEnum.ConnectionProperties(myDataSource, tt, DriverTypeEnum.H2_EMBEDDED);
+ Set columns = JdbcUtils.getPrimaryKeyColumns(cp, "HFJ_RESOURCE");
+ assertThat(columns).containsExactlyInAnyOrder("RES_ID", "PARTITION_ID");
+ }
+
+
+ @BeforeEach
+ void beforeEach() {
+
+ ourCtx = FhirContext.forR5();
+ 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));
+ }
+}
diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java
index 7c48fdf..b33dc74 100644
--- a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java
+++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java
@@ -118,7 +118,7 @@ class ExampleServerR4IT implements IServerSupport {
Parameters inParams = new Parameters();
inParams.addParameter().setName("periodStart").setValue(new StringType("2019-01-01"));
inParams.addParameter().setName("periodEnd").setValue(new StringType("2019-12-31"));
- inParams.addParameter().setName("reportType").setValue(new StringType("summary"));
+ inParams.addParameter().setName("reportType").setValue(new StringType("population"));
Parameters outParams = ourClient
.operation()
diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/MultitenantServerR4IT.java b/src/test/java/ca/uhn/fhir/jpa/starter/MultitenantServerR4IT.java
index bbe1348..9233961 100644
--- a/src/test/java/ca/uhn/fhir/jpa/starter/MultitenantServerR4IT.java
+++ b/src/test/java/ca/uhn/fhir/jpa/starter/MultitenantServerR4IT.java
@@ -7,7 +7,11 @@ import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.rest.client.interceptor.UrlTenantSelectionInterceptor;
import ca.uhn.fhir.rest.server.provider.ProviderConstants;
-import org.hl7.fhir.r4.model.*;
+import org.hl7.fhir.r4.model.Bundle;
+import org.hl7.fhir.r4.model.CodeType;
+import org.hl7.fhir.r4.model.IntegerType;
+import org.hl7.fhir.r4.model.Parameters;
+import org.hl7.fhir.r4.model.Patient;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -19,89 +23,86 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = {Application.class}, properties =
- {
- "spring.datasource.url=jdbc:h2:mem:dbr4-mt",
- "hapi.fhir.fhir_version=r4",
- "hapi.fhir.subscription.websocket_enabled=true",
- "hapi.fhir.cr_enabled=false",
- "hapi.fhir.partitioning.partitioning_include_in_search_hashes=false",
-
- })
+ {
+ "spring.datasource.url=jdbc:h2:mem:dbr4-mt",
+ "hapi.fhir.fhir_version=r4",
+ "hapi.fhir.subscription.websocket_enabled=true",
+ "hapi.fhir.cr_enabled=false",
+ "hapi.fhir.partitioning.partitioning_include_in_search_hashes=false",
+ "hapi.fhir.partitioning.request_tenant_partitioning_mode=true",
+ })
class MultitenantServerR4IT {
- private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExampleServerDstu2IT.class);
- private IGenericClient ourClient;
- private FhirContext ourCtx;
+ private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExampleServerDstu2IT.class);
+ private static UrlTenantSelectionInterceptor ourClientTenantInterceptor;
+ private IGenericClient ourClient;
+ private FhirContext ourCtx;
+ @LocalServerPort
+ private int port;
- @LocalServerPort
- private int port;
-
- private static UrlTenantSelectionInterceptor ourClientTenantInterceptor;
+ @Test
+ void testCreateAndReadInTenantA() {
- @Test
- void testCreateAndReadInTenantA() {
+ // Create tenant A
+ ourClientTenantInterceptor.setTenantId("DEFAULT");
+ ourClient
+ .operation()
+ .onServer()
+ .named(ProviderConstants.PARTITION_MANAGEMENT_CREATE_PARTITION)
+ .withParameter(Parameters.class, ProviderConstants.PARTITION_MANAGEMENT_PARTITION_ID, new IntegerType(1))
+ .andParameter(ProviderConstants.PARTITION_MANAGEMENT_PARTITION_NAME, new CodeType("TENANT-A"))
+ .execute();
- // Create tenant A
- ourClientTenantInterceptor.setTenantId("DEFAULT");
- ourClient
- .operation()
- .onServer()
- .named(ProviderConstants.PARTITION_MANAGEMENT_CREATE_PARTITION)
- .withParameter(Parameters.class, ProviderConstants.PARTITION_MANAGEMENT_PARTITION_ID, new IntegerType(1))
- .andParameter(ProviderConstants.PARTITION_MANAGEMENT_PARTITION_NAME, new CodeType("TENANT-A"))
- .execute();
+ ourClientTenantInterceptor.setTenantId("TENANT-A");
+ Patient pt = new Patient();
+ pt.addName().setFamily("Family A");
+ ourClient.create().resource(pt).execute().getId();
+
+ Bundle searchResult = ourClient.search().forResource(Patient.class).returnBundle(Bundle.class).cacheControl(new CacheControlDirective().setNoCache(true)).execute();
+ assertEquals(1, searchResult.getEntry().size());
+ Patient pt2 = (Patient) searchResult.getEntry().get(0).getResource();
+ assertEquals("Family A", pt2.getName().get(0).getFamily());
+ }
+
+ @Test
+ void testCreateAndReadInTenantB() {
- ourClientTenantInterceptor.setTenantId("TENANT-A");
- Patient pt = new Patient();
- pt.addName().setFamily("Family A");
- ourClient.create().resource(pt).execute().getId();
-
- Bundle searchResult = ourClient.search().forResource(Patient.class).returnBundle(Bundle.class).cacheControl(new CacheControlDirective().setNoCache(true)).execute();
- assertEquals(1, searchResult.getEntry().size());
- Patient pt2 = (Patient) searchResult.getEntry().get(0).getResource();
- assertEquals("Family A", pt2.getName().get(0).getFamily());
- }
-
- @Test
- void testCreateAndReadInTenantB() {
+ // Create tenant A
+ ourClientTenantInterceptor.setTenantId("DEFAULT");
+ ourClient
+ .operation()
+ .onServer()
+ .named(ProviderConstants.PARTITION_MANAGEMENT_CREATE_PARTITION)
+ .withParameter(Parameters.class, ProviderConstants.PARTITION_MANAGEMENT_PARTITION_ID, new IntegerType(2))
+ .andParameter(ProviderConstants.PARTITION_MANAGEMENT_PARTITION_NAME, new CodeType("TENANT-B"))
+ .execute();
- // Create tenant A
- ourClientTenantInterceptor.setTenantId("DEFAULT");
- ourClient
- .operation()
- .onServer()
- .named(ProviderConstants.PARTITION_MANAGEMENT_CREATE_PARTITION)
- .withParameter(Parameters.class, ProviderConstants.PARTITION_MANAGEMENT_PARTITION_ID, new IntegerType(2))
- .andParameter(ProviderConstants.PARTITION_MANAGEMENT_PARTITION_NAME, new CodeType("TENANT-B"))
- .execute();
+ ourClientTenantInterceptor.setTenantId("TENANT-B");
+ Patient pt = new Patient();
+ pt.addName().setFamily("Family B");
+ ourClient.create().resource(pt).execute().getId();
+ Bundle searchResult = ourClient.search().forResource(Patient.class).returnBundle(Bundle.class).cacheControl(new CacheControlDirective().setNoCache(true)).execute();
+ assertEquals(1, searchResult.getEntry().size());
+ Patient pt2 = (Patient) searchResult.getEntry().get(0).getResource();
+ assertEquals("Family B", pt2.getName().get(0).getFamily());
+ }
- ourClientTenantInterceptor.setTenantId("TENANT-B");
- Patient pt = new Patient();
- pt.addName().setFamily("Family B");
- ourClient.create().resource(pt).execute().getId();
+ @BeforeEach
+ void beforeEach() {
- Bundle searchResult = ourClient.search().forResource(Patient.class).returnBundle(Bundle.class).cacheControl(new CacheControlDirective().setNoCache(true)).execute();
- assertEquals(1, searchResult.getEntry().size());
- Patient pt2 = (Patient) searchResult.getEntry().get(0).getResource();
- assertEquals("Family B", pt2.getName().get(0).getFamily());
- }
-
- @BeforeEach
- void beforeEach() {
-
- ourClientTenantInterceptor = new UrlTenantSelectionInterceptor();
- 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));
- ourClient.registerInterceptor(ourClientTenantInterceptor);
- }
+ ourClientTenantInterceptor = new UrlTenantSelectionInterceptor();
+ 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));
+ ourClient.registerInterceptor(ourClientTenantInterceptor);
+ }
}
diff --git a/src/test/resources/application.yaml b/src/test/resources/application.yaml
index a494359..f57c1e2 100644
--- a/src/test/resources/application.yaml
+++ b/src/test/resources/application.yaml
@@ -145,10 +145,27 @@ hapi:
# local_base_urls:
# - https://hapi.fhir.org/baseR4
mdm_enabled: false
+
+ ### Uncomment the following section, and any sub-properties you need in order to enable
+ ### partitioning support on this server.
# partitioning:
# allow_references_across_partitions: false
# partitioning_include_in_search_hashes: false
- # partitioning_include_in_search_hashes
+ # default_partition_id: 0
+ # ### Enable the following setting to enable Database Partitioning Mode
+ # ### See: https://hapifhir.io/hapi-fhir/docs/server_jpa_partitioning/db_partition_mode.html
+ # database_partition_mode_enabled: false
+ # ### Partition Style: Partitioning requires a partition interceptor which helps the server
+ # ### select which partition(s) should be accessed for a given request. You can supply your
+ # ### own interceptor (see https://hapifhir.io/hapi-fhir/docs/server_jpa_partitioning/partitioning.html#partition-interceptors )
+ # ### but the following setting can also be used to use a built-in form.
+ # ### Patient ID Partitioning Mode uses the patient/subject ID to determine the partition
+ # patient_id_partitioning_mode: false
+ # ### Request tenant mode can be used for a multi-tenancy setup where the request path is
+ # ### expected to have an additional path element, e.g. GET http://example.com/fhir/TENANT-ID/Patient/A
+ # request_tenant_partitioning_mode: false
+
+
#cors:
# allow_Credentials: true
# These are allowed_origin patterns, see: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/cors/CorsConfiguration.html#setAllowedOriginPatterns-java.util.List-