Feat/restructuring (#422)
* Did restructuring and made repo validation interceptor an optional bean instead as it makes it more clean * Moved construction of FHIR servlet into a bean for better reuse of others that would like to depend directly on this library * Disabled default validation enabled
This commit is contained in:
committed by
GitHub
parent
c5e460dab0
commit
d660d5f76d
@@ -0,0 +1,29 @@
|
||||
package ca.uhn.fhir.jpa.starter.common;
|
||||
|
||||
import ca.uhn.fhir.jpa.search.lastn.ElasticsearchSvcImpl;
|
||||
import ca.uhn.fhir.jpa.starter.util.EnvironmentHelper;
|
||||
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);
|
||||
@Bean
|
||||
public ElasticsearchSvcImpl elasticsearchSvc(ConfigurableEnvironment configurableEnvironment) {
|
||||
if (EnvironmentHelper.isElasticsearchEnabled(configurableEnvironment)) {
|
||||
String elasticsearchUrl = EnvironmentHelper.getElasticsearchServerUrl(configurableEnvironment);
|
||||
if (elasticsearchUrl.startsWith("http")) {
|
||||
elasticsearchUrl =elasticsearchUrl.substring(elasticsearchUrl.indexOf("://") + 3);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
package ca.uhn.fhir.jpa.starter.common;
|
||||
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.binary.api.IBinaryStorageSvc;
|
||||
import ca.uhn.fhir.jpa.binstore.DatabaseBlobBinaryStorageSvcImpl;
|
||||
import ca.uhn.fhir.jpa.config.HibernatePropertiesProvider;
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionSettings.CrossPartitionReferenceMode;
|
||||
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
|
||||
import ca.uhn.fhir.jpa.starter.AppProperties;
|
||||
import ca.uhn.fhir.jpa.starter.util.JpaHibernatePropertiesProvider;
|
||||
import ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionDeliveryHandlerFactory;
|
||||
import ca.uhn.fhir.jpa.subscription.match.deliver.email.EmailSenderImpl;
|
||||
import ca.uhn.fhir.jpa.subscription.match.deliver.email.IEmailSender;
|
||||
import ca.uhn.fhir.rest.server.mail.IMailSvc;
|
||||
import ca.uhn.fhir.rest.server.mail.MailConfig;
|
||||
import ca.uhn.fhir.rest.server.mail.MailSvc;
|
||||
import com.google.common.base.Strings;
|
||||
import org.hl7.fhir.r4.model.Bundle.BundleType;
|
||||
import org.hl7.fhir.dstu2.model.Subscription;
|
||||
import org.springframework.boot.env.YamlPropertySourceLoader;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* This is the primary configuration file for the example server
|
||||
*/
|
||||
@Configuration
|
||||
@EnableTransactionManagement
|
||||
public class FhirServerConfigCommon {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirServerConfigCommon.class);
|
||||
|
||||
|
||||
public FhirServerConfigCommon(AppProperties appProperties) {
|
||||
ourLog.info("Server configured to " + (appProperties.getAllow_contains_searches() ? "allow" : "deny") + " contains searches");
|
||||
ourLog.info("Server configured to " + (appProperties.getAllow_multiple_delete() ? "allow" : "deny") + " multiple deletes");
|
||||
ourLog.info("Server configured to " + (appProperties.getAllow_external_references() ? "allow" : "deny") + " external references");
|
||||
ourLog.info("Server configured to " + (appProperties.getDao_scheduling_enabled() ? "enable" : "disable") + " DAO scheduling");
|
||||
ourLog.info("Server configured to " + (appProperties.getDelete_expunge_enabled() ? "enable" : "disable") + " delete expunges");
|
||||
ourLog.info("Server configured to " + (appProperties.getExpunge_enabled() ? "enable" : "disable") + " expunges");
|
||||
ourLog.info("Server configured to " + (appProperties.getAllow_override_default_search_params() ? "allow" : "deny") + " overriding default search params");
|
||||
ourLog.info("Server configured to " + (appProperties.getAuto_create_placeholder_reference_targets() ? "allow" : "disable") + " auto-creating placeholder references");
|
||||
|
||||
if (appProperties.getSubscription().getEmail() != null) {
|
||||
AppProperties.Subscription.Email email = appProperties.getSubscription().getEmail();
|
||||
ourLog.info("Server is configured to enable email with host '" + email.getHost() + "' and port " + email.getPort());
|
||||
ourLog.info("Server will use '" + email.getFrom() + "' as the from email address");
|
||||
|
||||
if (!Strings.isNullOrEmpty(email.getUsername())) {
|
||||
ourLog.info("Server is configured to use username '" + email.getUsername() + "' for email");
|
||||
}
|
||||
|
||||
if (!Strings.isNullOrEmpty(email.getPassword())) {
|
||||
ourLog.info("Server is configured to use a password for email");
|
||||
}
|
||||
}
|
||||
|
||||
if (appProperties.getSubscription().getResthook_enabled()) {
|
||||
ourLog.info("REST-hook subscriptions enabled");
|
||||
}
|
||||
|
||||
if (appProperties.getSubscription().getEmail() != null) {
|
||||
ourLog.info("Email subscriptions enabled");
|
||||
}
|
||||
|
||||
if (appProperties.getEnable_index_contained_resource() == Boolean.TRUE) {
|
||||
ourLog.info("Indexed on contained resource enabled");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure FHIR properties around the the JPA server via this bean
|
||||
*/
|
||||
@Bean
|
||||
public DaoConfig daoConfig(AppProperties appProperties) {
|
||||
DaoConfig daoConfig = new DaoConfig();
|
||||
|
||||
daoConfig.setIndexMissingFields(appProperties.getEnable_index_missing_fields() ? DaoConfig.IndexEnabledEnum.ENABLED : DaoConfig.IndexEnabledEnum.DISABLED);
|
||||
daoConfig.setAutoCreatePlaceholderReferenceTargets(appProperties.getAuto_create_placeholder_reference_targets());
|
||||
daoConfig.setEnforceReferentialIntegrityOnWrite(appProperties.getEnforce_referential_integrity_on_write());
|
||||
daoConfig.setEnforceReferentialIntegrityOnDelete(appProperties.getEnforce_referential_integrity_on_delete());
|
||||
daoConfig.setAllowContainsSearches(appProperties.getAllow_contains_searches());
|
||||
daoConfig.setAllowMultipleDelete(appProperties.getAllow_multiple_delete());
|
||||
daoConfig.setAllowExternalReferences(appProperties.getAllow_external_references());
|
||||
daoConfig.setSchedulingDisabled(!appProperties.getDao_scheduling_enabled());
|
||||
daoConfig.setDeleteExpungeEnabled(appProperties.getDelete_expunge_enabled());
|
||||
daoConfig.setExpungeEnabled(appProperties.getExpunge_enabled());
|
||||
if(appProperties.getSubscription() != null && appProperties.getSubscription().getEmail() != null)
|
||||
daoConfig.setEmailFromAddress(appProperties.getSubscription().getEmail().getFrom());
|
||||
|
||||
Integer maxFetchSize = appProperties.getMax_page_size();
|
||||
daoConfig.setFetchSizeDefaultMaximum(maxFetchSize);
|
||||
ourLog.info("Server configured to have a maximum fetch size of " + (maxFetchSize == Integer.MAX_VALUE ? "'unlimited'" : maxFetchSize));
|
||||
|
||||
Long reuseCachedSearchResultsMillis = appProperties.getReuse_cached_search_results_millis();
|
||||
daoConfig.setReuseCachedSearchResultsForMillis(reuseCachedSearchResultsMillis);
|
||||
ourLog.info("Server configured to cache search results for {} milliseconds", reuseCachedSearchResultsMillis);
|
||||
|
||||
|
||||
Long retainCachedSearchesMinutes = appProperties.getRetain_cached_searches_mins();
|
||||
daoConfig.setExpireSearchResultsAfterMillis(retainCachedSearchesMinutes * 60 * 1000);
|
||||
|
||||
if(appProperties.getSubscription() != null) {
|
||||
// Subscriptions are enabled by channel type
|
||||
if (appProperties.getSubscription().getResthook_enabled()) {
|
||||
ourLog.info("Enabling REST-hook subscriptions");
|
||||
daoConfig.addSupportedSubscriptionType(org.hl7.fhir.dstu2.model.Subscription.SubscriptionChannelType.RESTHOOK);
|
||||
}
|
||||
if (appProperties.getSubscription().getEmail() != null) {
|
||||
ourLog.info("Enabling email subscriptions");
|
||||
daoConfig.addSupportedSubscriptionType(org.hl7.fhir.dstu2.model.Subscription.SubscriptionChannelType.EMAIL);
|
||||
}
|
||||
if (appProperties.getSubscription().getWebsocket_enabled()) {
|
||||
ourLog.info("Enabling websocket subscriptions");
|
||||
daoConfig.addSupportedSubscriptionType(org.hl7.fhir.dstu2.model.Subscription.SubscriptionChannelType.WEBSOCKET);
|
||||
}
|
||||
}
|
||||
|
||||
daoConfig.setFilterParameterEnabled(appProperties.getFilter_search_enabled());
|
||||
daoConfig.setAdvancedHSearchIndexing(appProperties.getAdvanced_lucene_indexing());
|
||||
daoConfig.setTreatBaseUrlsAsLocal(new HashSet<>(appProperties.getLocal_base_urls()));
|
||||
|
||||
if (appProperties.getLastn_enabled()) {
|
||||
daoConfig.setLastNEnabled(true);
|
||||
}
|
||||
|
||||
if(appProperties.getInline_resource_storage_below_size() != 0){
|
||||
daoConfig.setInlineResourceTextBelowSize(appProperties.getInline_resource_storage_below_size());
|
||||
}
|
||||
|
||||
|
||||
daoConfig.setStoreResourceInHSearchIndex(appProperties.getStore_resource_in_lucene_index_enabled());
|
||||
daoConfig.getModelConfig().setNormalizedQuantitySearchLevel(appProperties.getNormalized_quantity_search_level());
|
||||
daoConfig.getModelConfig().setIndexOnContainedResources(appProperties.getEnable_index_contained_resource());
|
||||
|
||||
|
||||
|
||||
if (appProperties.getAllowed_bundle_types() != null) {
|
||||
daoConfig.setBundleTypesAllowedForStorage(appProperties.getAllowed_bundle_types().stream().map(BundleType::toCode).collect(Collectors.toSet()));
|
||||
}
|
||||
|
||||
daoConfig.setDeferIndexingForCodesystemsOfSize(appProperties.getDefer_indexing_for_codesystems_of_size());
|
||||
|
||||
|
||||
if (appProperties.getClient_id_strategy() == DaoConfig.ClientIdStrategyEnum.ANY) {
|
||||
daoConfig.setResourceServerIdStrategy(DaoConfig.IdStrategyEnum.UUID);
|
||||
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());
|
||||
|
||||
|
||||
return daoConfig;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public YamlPropertySourceLoader yamlPropertySourceLoader() {
|
||||
return new YamlPropertySourceLoader();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PartitionSettings partitionSettings(AppProperties appProperties) {
|
||||
PartitionSettings retVal = new PartitionSettings();
|
||||
|
||||
// Partitioning
|
||||
if (appProperties.getPartitioning() != null) {
|
||||
retVal.setPartitioningEnabled(true);
|
||||
retVal.setIncludePartitionInSearchHashes(appProperties.getPartitioning().getPartitioning_include_in_search_hashes());
|
||||
if(appProperties.getPartitioning().getAllow_references_across_partitions()) {
|
||||
retVal.setAllowReferencesAcrossPartitions(CrossPartitionReferenceMode.ALLOWED_UNQUALIFIED);
|
||||
} else {
|
||||
retVal.setAllowReferencesAcrossPartitions(CrossPartitionReferenceMode.NOT_ALLOWED);
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
@Primary
|
||||
@Bean
|
||||
public HibernatePropertiesProvider jpaStarterDialectProvider(LocalContainerEntityManagerFactoryBean myEntityManagerFactory) {
|
||||
return new JpaHibernatePropertiesProvider(myEntityManagerFactory);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ModelConfig modelConfig(AppProperties appProperties, DaoConfig daoConfig) {
|
||||
ModelConfig modelConfig = daoConfig.getModelConfig();
|
||||
modelConfig.setAllowContainsSearches(appProperties.getAllow_contains_searches());
|
||||
modelConfig.setAllowExternalReferences(appProperties.getAllow_external_references());
|
||||
modelConfig.setDefaultSearchParamsCanBeOverridden(appProperties.getAllow_override_default_search_params());
|
||||
if(appProperties.getSubscription() != null && appProperties.getSubscription().getEmail() != null)
|
||||
modelConfig.setEmailFromAddress(appProperties.getSubscription().getEmail().getFrom());
|
||||
|
||||
// You can enable these if you want to support Subscriptions from your server
|
||||
if (appProperties.getSubscription() != null && appProperties.getSubscription().getResthook_enabled() != null) {
|
||||
modelConfig.addSupportedSubscriptionType(Subscription.SubscriptionChannelType.RESTHOOK);
|
||||
}
|
||||
|
||||
if (appProperties.getSubscription() != null && appProperties.getSubscription().getEmail() != null) {
|
||||
modelConfig.addSupportedSubscriptionType(Subscription.SubscriptionChannelType.EMAIL);
|
||||
}
|
||||
|
||||
modelConfig.setNormalizedQuantitySearchLevel(appProperties.getNormalized_quantity_search_level());
|
||||
|
||||
modelConfig.setIndexOnContainedResources(appProperties.getEnable_index_contained_resource());
|
||||
modelConfig.setIndexIdentifierOfType(appProperties.getEnable_index_of_type());
|
||||
return modelConfig;
|
||||
}
|
||||
|
||||
@Lazy
|
||||
@Bean
|
||||
public IBinaryStorageSvc binaryStorageSvc(AppProperties appProperties) {
|
||||
DatabaseBlobBinaryStorageSvcImpl binaryStorageSvc = new DatabaseBlobBinaryStorageSvcImpl();
|
||||
|
||||
if (appProperties.getMax_binary_size() != null) {
|
||||
binaryStorageSvc.setMaximumBinarySize(appProperties.getMax_binary_size());
|
||||
}
|
||||
|
||||
return binaryStorageSvc;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IEmailSender emailSender(AppProperties appProperties, Optional<SubscriptionDeliveryHandlerFactory> subscriptionDeliveryHandlerFactory) {
|
||||
if (appProperties.getSubscription() != null && appProperties.getSubscription().getEmail() != null) {
|
||||
MailConfig mailConfig = new MailConfig();
|
||||
|
||||
AppProperties.Subscription.Email email = appProperties.getSubscription().getEmail();
|
||||
mailConfig.setSmtpHostname(email.getHost());
|
||||
mailConfig.setSmtpPort(email.getPort());
|
||||
mailConfig.setSmtpUsername(email.getUsername());
|
||||
mailConfig.setSmtpPassword(email.getPassword());
|
||||
mailConfig.setSmtpUseStartTLS(email.getStartTlsEnable());
|
||||
|
||||
IMailSvc mailSvc = new MailSvc(mailConfig);
|
||||
IEmailSender emailSender = new EmailSenderImpl(mailSvc);
|
||||
|
||||
subscriptionDeliveryHandlerFactory.ifPresent(theSubscriptionDeliveryHandlerFactory -> theSubscriptionDeliveryHandlerFactory.setEmailSender(emailSender));
|
||||
|
||||
return emailSender;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package ca.uhn.fhir.jpa.starter.common;
|
||||
|
||||
import ca.uhn.fhir.jpa.config.JpaDstu2Config;
|
||||
import ca.uhn.fhir.jpa.starter.annotations.OnDSTU2Condition;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
@Configuration
|
||||
@Conditional(OnDSTU2Condition.class)
|
||||
@Import({
|
||||
StarterJpaConfig.class,
|
||||
JpaDstu2Config.class
|
||||
})
|
||||
public class FhirServerConfigDstu2 {
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package ca.uhn.fhir.jpa.starter.common;
|
||||
|
||||
import ca.uhn.fhir.jpa.config.dstu3.JpaDstu3Config;
|
||||
import ca.uhn.fhir.jpa.starter.annotations.OnDSTU3Condition;
|
||||
import ca.uhn.fhir.jpa.starter.cql.StarterCqlDstu3Config;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
@Configuration
|
||||
@Conditional(OnDSTU3Condition.class)
|
||||
@Import({
|
||||
StarterJpaConfig.class,
|
||||
JpaDstu3Config.class,
|
||||
StarterCqlDstu3Config.class,
|
||||
ElasticsearchConfig.class})
|
||||
public class FhirServerConfigDstu3 {
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package ca.uhn.fhir.jpa.starter.common;
|
||||
|
||||
import ca.uhn.fhir.jpa.config.r4.JpaR4Config;
|
||||
import ca.uhn.fhir.jpa.starter.annotations.OnR4Condition;
|
||||
import ca.uhn.fhir.jpa.starter.cql.StarterCqlR4Config;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
@Configuration
|
||||
@Conditional(OnR4Condition.class)
|
||||
@Import({
|
||||
StarterJpaConfig.class,
|
||||
JpaR4Config.class,
|
||||
StarterCqlR4Config.class,
|
||||
ElasticsearchConfig.class
|
||||
})
|
||||
public class FhirServerConfigR4 {
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package ca.uhn.fhir.jpa.starter.common;
|
||||
|
||||
import ca.uhn.fhir.jpa.config.r5.JpaR5Config;
|
||||
import ca.uhn.fhir.jpa.starter.annotations.OnR5Condition;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
@Configuration
|
||||
@Conditional(OnR5Condition.class)
|
||||
@Import({
|
||||
StarterJpaConfig.class,
|
||||
JpaR5Config.class,
|
||||
ElasticsearchConfig.class
|
||||
})
|
||||
public class FhirServerConfigR5 {
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package ca.uhn.fhir.jpa.starter.common;
|
||||
|
||||
import ca.uhn.fhir.jpa.starter.AppProperties;
|
||||
import ca.uhn.fhir.to.FhirTesterMvcConfig;
|
||||
import ca.uhn.fhir.to.TesterConfig;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
//@formatter:off
|
||||
/**
|
||||
* This spring config file configures the web testing module. It serves two
|
||||
* purposes:
|
||||
* 1. It imports FhirTesterMvcConfig, which is the spring config for the
|
||||
* tester itself
|
||||
* 2. It tells the tester which server(s) to talk to, via the testerConfig()
|
||||
* method below
|
||||
*/
|
||||
@Configuration
|
||||
@Import(FhirTesterMvcConfig.class)
|
||||
public class FhirTesterConfig {
|
||||
|
||||
/**
|
||||
* This bean tells the testing webpage which servers it should configure itself
|
||||
* to communicate with. In this example we configure it to talk to the local
|
||||
* server, as well as one public server. If you are creating a project to
|
||||
* deploy somewhere else, you might choose to only put your own server's
|
||||
* address here.
|
||||
*
|
||||
* Note the use of the ${serverBase} variable below. This will be replaced with
|
||||
* the base URL as reported by the server itself. Often for a simple Tomcat
|
||||
* (or other container) installation, this will end up being something
|
||||
* like "http://localhost:8080/hapi-fhir-jpaserver-starter". If you are
|
||||
* deploying your server to a place with a fully qualified domain name,
|
||||
* you might want to use that instead of using the variable.
|
||||
*/
|
||||
@Bean
|
||||
public TesterConfig testerConfig(AppProperties appProperties) {
|
||||
TesterConfig retVal = new TesterConfig();
|
||||
appProperties.getTester().forEach((key, value) -> {
|
||||
retVal
|
||||
.addServer()
|
||||
.withId(key)
|
||||
.withFhirVersion(value.getFhir_version())
|
||||
.withBaseUrl(value.getServer_address())
|
||||
.withName(value.getName());
|
||||
retVal.setRefuseToFetchThirdPartyUrls(
|
||||
value.getRefuse_to_fetch_third_party_urls());
|
||||
|
||||
});
|
||||
return retVal;
|
||||
}
|
||||
|
||||
}
|
||||
//@formatter:on
|
||||
@@ -0,0 +1,433 @@
|
||||
package ca.uhn.fhir.jpa.starter.common;
|
||||
|
||||
import ca.uhn.fhir.batch2.jobs.imprt.BulkDataImportProvider;
|
||||
import ca.uhn.fhir.batch2.jobs.reindex.ReindexProvider;
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
|
||||
import ca.uhn.fhir.jpa.api.IDaoRegistry;
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao;
|
||||
import ca.uhn.fhir.jpa.batch.config.NonPersistedBatchConfigurer;
|
||||
import ca.uhn.fhir.jpa.binary.interceptor.BinaryStorageInterceptor;
|
||||
import ca.uhn.fhir.jpa.binary.provider.BinaryAccessProvider;
|
||||
import ca.uhn.fhir.jpa.bulk.export.provider.BulkDataExportProvider;
|
||||
import ca.uhn.fhir.jpa.config.util.HapiEntityManagerFactoryUtil;
|
||||
import ca.uhn.fhir.jpa.config.util.ResourceCountCacheUtil;
|
||||
import ca.uhn.fhir.jpa.config.util.ValidationSupportConfigUtil;
|
||||
import ca.uhn.fhir.jpa.dao.FulltextSearchSvcImpl;
|
||||
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
|
||||
import ca.uhn.fhir.jpa.dao.mdm.MdmLinkDaoJpaImpl;
|
||||
import ca.uhn.fhir.jpa.dao.search.HSearchSortHelperImpl;
|
||||
import ca.uhn.fhir.jpa.dao.search.IHSearchSortHelper;
|
||||
import ca.uhn.fhir.jpa.graphql.GraphQLProvider;
|
||||
import ca.uhn.fhir.jpa.interceptor.CascadingDeleteInterceptor;
|
||||
import ca.uhn.fhir.jpa.interceptor.validation.RepositoryValidatingInterceptor;
|
||||
import ca.uhn.fhir.jpa.packages.IPackageInstallerSvc;
|
||||
import ca.uhn.fhir.jpa.packages.PackageInstallationSpec;
|
||||
import ca.uhn.fhir.jpa.partition.PartitionManagementProvider;
|
||||
import ca.uhn.fhir.jpa.provider.*;
|
||||
import ca.uhn.fhir.jpa.provider.dstu3.JpaConformanceProviderDstu3;
|
||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||
import ca.uhn.fhir.jpa.search.IStaleSearchDeletingSvc;
|
||||
import ca.uhn.fhir.jpa.search.StaleSearchDeletingSvcImpl;
|
||||
import ca.uhn.fhir.jpa.starter.AppProperties;
|
||||
import ca.uhn.fhir.jpa.starter.common.validation.IRepositoryValidationInterceptorFactory;
|
||||
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.dao.IMdmLinkDao;
|
||||
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.*;
|
||||
import ca.uhn.fhir.rest.server.interceptor.*;
|
||||
import ca.uhn.fhir.rest.server.interceptor.partition.RequestTenantPartitionInterceptor;
|
||||
import ca.uhn.fhir.rest.server.provider.ResourceProviderFactory;
|
||||
import ca.uhn.fhir.rest.server.tenant.UrlBaseTenantIdentificationStrategy;
|
||||
import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
|
||||
import ca.uhn.fhir.validation.IValidatorModule;
|
||||
import ca.uhn.fhir.validation.ResultSeverityEnum;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import org.hl7.fhir.common.hapi.validation.support.CachingValidationSupport;
|
||||
import org.springframework.batch.core.configuration.annotation.BatchConfigurer;
|
||||
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.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
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 javax.persistence.EntityManagerFactory;
|
||||
import javax.sql.DataSource;
|
||||
import java.util.*;
|
||||
|
||||
import static ca.uhn.fhir.jpa.starter.common.validation.IRepositoryValidationInterceptorFactory.ENABLE_REPOSITORY_VALIDATING_INTERCEPTOR;
|
||||
|
||||
@Configuration
|
||||
public class StarterJpaConfig {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(StarterJpaConfig.class);
|
||||
|
||||
@Bean
|
||||
public IFulltextSearchSvc fullTextSearchSvc() {
|
||||
return new FulltextSearchSvcImpl();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IStaleSearchDeletingSvc staleSearchDeletingSvc() {
|
||||
return new StaleSearchDeletingSvcImpl();
|
||||
}
|
||||
|
||||
@Primary
|
||||
@Bean
|
||||
public CachingValidationSupport validationSupportChain(JpaValidationSupportChain theJpaValidationSupportChain) {
|
||||
return ValidationSupportConfigUtil.newCachingValidationSupport(theJpaValidationSupportChain);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BatchConfigurer batchConfigurer() {
|
||||
return new NonPersistedBatchConfigurer();
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private ConfigurableEnvironment configurableEnvironment;
|
||||
|
||||
/**
|
||||
* Customize the default/max page sizes for search results. You can set these however
|
||||
* you want, although very large page sizes will require a lot of RAM.
|
||||
*/
|
||||
@Bean
|
||||
public DatabaseBackedPagingProvider databaseBackedPagingProvider(AppProperties appProperties) {
|
||||
DatabaseBackedPagingProvider pagingProvider = new DatabaseBackedPagingProvider();
|
||||
pagingProvider.setDefaultPageSize(appProperties.getDefault_page_size());
|
||||
pagingProvider.setMaximumPageSize(appProperties.getMax_page_size());
|
||||
return pagingProvider;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IResourceSupportedSvc resourceSupportedSvc(IDaoRegistry theDaoRegistry) {
|
||||
return new DaoRegistryResourceSupportedSvc(theDaoRegistry);
|
||||
}
|
||||
|
||||
@Bean(name = "myResourceCountsCache")
|
||||
public ResourceCountCache resourceCountsCache(IFhirSystemDao<?, ?> theSystemDao) {
|
||||
return ResourceCountCacheUtil.newResourceCountCache(theSystemDao);
|
||||
}
|
||||
|
||||
@Primary
|
||||
@Bean
|
||||
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource myDataSource, ConfigurableListableBeanFactory myConfigurableListableBeanFactory, FhirContext theFhirContext) {
|
||||
LocalContainerEntityManagerFactoryBean retVal = HapiEntityManagerFactoryUtil.newEntityManagerFactory(myConfigurableListableBeanFactory, theFhirContext);
|
||||
retVal.setPersistenceUnitName("HAPI_PU");
|
||||
|
||||
try {
|
||||
retVal.setDataSource(myDataSource);
|
||||
} catch (Exception e) {
|
||||
throw new ConfigurationException("Could not set the data source due to a configuration issue", e);
|
||||
}
|
||||
retVal.setJpaProperties(EnvironmentHelper.getHibernateProperties(configurableEnvironment, myConfigurableListableBeanFactory));
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
public JpaTransactionManager hapiTransactionManager(EntityManagerFactory entityManagerFactory) {
|
||||
JpaTransactionManager retVal = new JpaTransactionManager();
|
||||
retVal.setEntityManagerFactory(entityManagerFactory);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IHSearchSortHelper hSearchSortHelper(ISearchParamRegistry mySearchParamRegistry) {
|
||||
return new HSearchSortHelperImpl(mySearchParamRegistry);
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "hapi.fhir", name = ENABLE_REPOSITORY_VALIDATING_INTERCEPTOR, havingValue = "true")
|
||||
public RepositoryValidatingInterceptor repositoryValidatingInterceptor(IRepositoryValidationInterceptorFactory factory) {
|
||||
return factory.buildUsingStoredStructureDefinitions();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public LoggingInterceptor loggingInterceptor(AppProperties appProperties) {
|
||||
|
||||
/*
|
||||
* Add some logging for each request
|
||||
*/
|
||||
|
||||
LoggingInterceptor loggingInterceptor = new LoggingInterceptor();
|
||||
loggingInterceptor.setLoggerName(appProperties.getLogger().getName());
|
||||
loggingInterceptor.setMessageFormat(appProperties.getLogger().getFormat());
|
||||
loggingInterceptor.setErrorMessageFormat(appProperties.getLogger().getError_format());
|
||||
loggingInterceptor.setLogExceptions(appProperties.getLogger().getLog_exceptions());
|
||||
return loggingInterceptor;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
/*
|
||||
This bean is currently necessary to override from MDM settings
|
||||
*/
|
||||
IMdmLinkDao mdmLinkDao() {
|
||||
return new MdmLinkDaoJpaImpl();
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "hapi.fhir", name = "cors")
|
||||
public CorsInterceptor corsInterceptor(AppProperties appProperties) {
|
||||
// Define your CORS configuration. This is an example
|
||||
// showing a typical setup. You should customize this
|
||||
// to your specific needs
|
||||
ourLog.info("CORS is enabled on this server");
|
||||
CorsConfiguration config = new CorsConfiguration();
|
||||
config.addAllowedHeader(HttpHeaders.ORIGIN);
|
||||
config.addAllowedHeader(HttpHeaders.ACCEPT);
|
||||
config.addAllowedHeader(HttpHeaders.CONTENT_TYPE);
|
||||
config.addAllowedHeader(HttpHeaders.AUTHORIZATION);
|
||||
config.addAllowedHeader(HttpHeaders.CACHE_CONTROL);
|
||||
config.addAllowedHeader("x-fhir-starter");
|
||||
config.addAllowedHeader("X-Requested-With");
|
||||
config.addAllowedHeader("Prefer");
|
||||
|
||||
List<String> allAllowedCORSOrigins = appProperties.getCors().getAllowed_origin();
|
||||
allAllowedCORSOrigins.forEach(config::addAllowedOriginPattern);
|
||||
ourLog.info("CORS allows the following origins: " + String.join(", ", allAllowedCORSOrigins));
|
||||
|
||||
config.addExposedHeader("Location");
|
||||
config.addExposedHeader("Content-Location");
|
||||
config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH", "HEAD"));
|
||||
config.setAllowCredentials(appProperties.getCors().getAllow_Credentials());
|
||||
|
||||
// Create the interceptor and register it
|
||||
return new CorsInterceptor(config);
|
||||
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RestfulServer restfulServer(IFhirSystemDao<?, ?> fhirSystemDao, AppProperties appProperties, DaoRegistry daoRegistry, Optional<MdmProviderLoader> mdmProviderProvider, IJpaSystemProvider jpaSystemProvider, ResourceProviderFactory resourceProviderFactory, DaoConfig daoConfig, ISearchParamRegistry searchParamRegistry, IValidationSupport theValidationSupport, DatabaseBackedPagingProvider databaseBackedPagingProvider, LoggingInterceptor loggingInterceptor, Optional<TerminologyUploaderProvider> terminologyUploaderProvider, Optional<SubscriptionTriggeringProvider> subscriptionTriggeringProvider, Optional<CorsInterceptor> corsInterceptor, IInterceptorBroadcaster interceptorBroadcaster, Optional<BinaryAccessProvider> binaryAccessProvider, BinaryStorageInterceptor binaryStorageInterceptor, IValidatorModule validatorModule, Optional<GraphQLProvider> graphQLProvider, BulkDataExportProvider bulkDataExportProvider, BulkDataImportProvider bulkDataImportProvider, ValueSetOperationProvider theValueSetOperationProvider, ReindexProvider reindexProvider, PartitionManagementProvider partitionManagementProvider, Optional<RepositoryValidatingInterceptor> repositoryValidatingInterceptor, IPackageInstallerSvc packageInstallerSvc) {
|
||||
RestfulServer fhirServer = new RestfulServer(fhirSystemDao.getContext());
|
||||
|
||||
List<String> supportedResourceTypes = appProperties.getSupported_resource_types();
|
||||
|
||||
if (!supportedResourceTypes.isEmpty()) {
|
||||
if (!supportedResourceTypes.contains("SearchParameter")) {
|
||||
supportedResourceTypes.add("SearchParameter");
|
||||
}
|
||||
daoRegistry.setSupportedResourceTypes(supportedResourceTypes);
|
||||
}
|
||||
|
||||
if (appProperties.getNarrative_enabled()) {
|
||||
fhirSystemDao.getContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator());
|
||||
} else {
|
||||
fhirSystemDao.getContext().setNarrativeGenerator(new NullNarrativeGenerator());
|
||||
}
|
||||
|
||||
if (appProperties.getMdm_enabled()) mdmProviderProvider.get().loadProvider();
|
||||
|
||||
fhirServer.registerProviders(resourceProviderFactory.createProviders());
|
||||
fhirServer.registerProvider(jpaSystemProvider);
|
||||
fhirServer.setServerConformanceProvider(calculateConformanceProvider(fhirSystemDao, fhirServer, daoConfig, searchParamRegistry, theValidationSupport));
|
||||
|
||||
/*
|
||||
* ETag Support
|
||||
*/
|
||||
|
||||
if (!appProperties.getEtag_support_enabled()) fhirServer.setETagSupport(ETagSupportEnum.DISABLED);
|
||||
|
||||
|
||||
/*
|
||||
* Default to JSON and pretty printing
|
||||
*/
|
||||
fhirServer.setDefaultPrettyPrint(appProperties.getDefault_pretty_print());
|
||||
|
||||
/*
|
||||
* Default encoding
|
||||
*/
|
||||
fhirServer.setDefaultResponseEncoding(appProperties.getDefault_encoding());
|
||||
|
||||
/*
|
||||
* This configures the server to page search results to and from
|
||||
* the database, instead of only paging them to memory. This may mean
|
||||
* a performance hit when performing searches that return lots of results,
|
||||
* but makes the server much more scalable.
|
||||
*/
|
||||
|
||||
fhirServer.setPagingProvider(databaseBackedPagingProvider);
|
||||
|
||||
/*
|
||||
* This interceptor formats the output using nice colourful
|
||||
* HTML output when the request is detected to come from a
|
||||
* browser.
|
||||
*/
|
||||
fhirServer.registerInterceptor(new ResponseHighlighterInterceptor());
|
||||
|
||||
if (appProperties.getFhirpath_interceptor_enabled()) {
|
||||
fhirServer.registerInterceptor(new FhirPathFilterInterceptor());
|
||||
}
|
||||
|
||||
fhirServer.registerInterceptor(loggingInterceptor);
|
||||
|
||||
/*
|
||||
* If you are hosting this server at a specific DNS name, the server will try to
|
||||
* figure out the FHIR base URL based on what the web container tells it, but
|
||||
* this doesn't always work. If you are setting links in your search bundles that
|
||||
* just refer to "localhost", you might want to use a server address strategy:
|
||||
*/
|
||||
String serverAddress = appProperties.getServer_address();
|
||||
if (!Strings.isNullOrEmpty(serverAddress)) {
|
||||
fhirServer.setServerAddressStrategy(new HardcodedServerAddressStrategy(serverAddress));
|
||||
} else if (appProperties.getUse_apache_address_strategy()) {
|
||||
boolean useHttps = appProperties.getUse_apache_address_strategy_https();
|
||||
fhirServer.setServerAddressStrategy(useHttps ? ApacheProxyAddressStrategy.forHttps() : ApacheProxyAddressStrategy.forHttp());
|
||||
} else {
|
||||
fhirServer.setServerAddressStrategy(new IncomingRequestAddressStrategy());
|
||||
}
|
||||
|
||||
/*
|
||||
* If you are using DSTU3+, you may want to add a terminology uploader, which allows
|
||||
* uploading of external terminologies such as Snomed CT. Note that this uploader
|
||||
* does not have any security attached (any anonymous user may use it by default)
|
||||
* so it is a potential security vulnerability. Consider using an AuthorizationInterceptor
|
||||
* with this feature.
|
||||
*/
|
||||
if (fhirSystemDao.getContext().getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.DSTU3)) { // <-- ENABLED RIGHT NOW
|
||||
fhirServer.registerProvider(terminologyUploaderProvider.get());
|
||||
}
|
||||
|
||||
// If you want to enable the $trigger-subscription operation to allow
|
||||
// manual triggering of a subscription delivery, enable this provider
|
||||
if (true) { // <-- ENABLED RIGHT NOW
|
||||
fhirServer.registerProvider(subscriptionTriggeringProvider.get());
|
||||
}
|
||||
|
||||
corsInterceptor.ifPresent(fhirServer::registerInterceptor);
|
||||
|
||||
if (appProperties.getSubscription() != null) {
|
||||
// Subscription debug logging
|
||||
fhirServer.registerInterceptor(new SubscriptionDebugLogInterceptor());
|
||||
}
|
||||
|
||||
if (appProperties.getAllow_cascading_deletes()) {
|
||||
CascadingDeleteInterceptor cascadingDeleteInterceptor = new CascadingDeleteInterceptor(fhirSystemDao.getContext(), daoRegistry, interceptorBroadcaster);
|
||||
fhirServer.registerInterceptor(cascadingDeleteInterceptor);
|
||||
}
|
||||
|
||||
// Binary Storage
|
||||
if (appProperties.getBinary_storage_enabled() && binaryAccessProvider.isPresent()) {
|
||||
fhirServer.registerProvider(binaryAccessProvider.get());
|
||||
fhirServer.registerInterceptor(binaryStorageInterceptor);
|
||||
}
|
||||
|
||||
// Validation
|
||||
|
||||
if (validatorModule != null) {
|
||||
if (appProperties.getValidation().getRequests_enabled()) {
|
||||
RequestValidatingInterceptor interceptor = new RequestValidatingInterceptor();
|
||||
interceptor.setFailOnSeverity(ResultSeverityEnum.ERROR);
|
||||
interceptor.setValidatorModules(Collections.singletonList(validatorModule));
|
||||
fhirServer.registerInterceptor(interceptor);
|
||||
}
|
||||
if (appProperties.getValidation().getResponses_enabled()) {
|
||||
ResponseValidatingInterceptor interceptor = new ResponseValidatingInterceptor();
|
||||
interceptor.setFailOnSeverity(ResultSeverityEnum.ERROR);
|
||||
interceptor.setValidatorModules(Collections.singletonList(validatorModule));
|
||||
fhirServer.registerInterceptor(interceptor);
|
||||
}
|
||||
}
|
||||
|
||||
// GraphQL
|
||||
if (appProperties.getGraphql_enabled()) {
|
||||
if (fhirSystemDao.getContext().getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.DSTU3)) {
|
||||
fhirServer.registerProvider(graphQLProvider.get());
|
||||
}
|
||||
}
|
||||
|
||||
if (appProperties.getOpenapi_enabled()) {
|
||||
fhirServer.registerInterceptor(new OpenApiInterceptor());
|
||||
}
|
||||
|
||||
// Bulk Export
|
||||
if (appProperties.getBulk_export_enabled()) {
|
||||
fhirServer.registerProvider(bulkDataExportProvider);
|
||||
}
|
||||
|
||||
//Bulk Import
|
||||
if (appProperties.getBulk_import_enabled()) {
|
||||
fhirServer.registerProvider(bulkDataImportProvider);
|
||||
}
|
||||
|
||||
|
||||
// valueSet Operations i.e $expand
|
||||
fhirServer.registerProvider(theValueSetOperationProvider);
|
||||
|
||||
//reindex Provider $reindex
|
||||
fhirServer.registerProvider(reindexProvider);
|
||||
|
||||
// Partitioning
|
||||
if (appProperties.getPartitioning() != null) {
|
||||
fhirServer.registerInterceptor(new RequestTenantPartitionInterceptor());
|
||||
fhirServer.setTenantIdentificationStrategy(new UrlBaseTenantIdentificationStrategy());
|
||||
fhirServer.registerProviders(partitionManagementProvider);
|
||||
}
|
||||
|
||||
|
||||
repositoryValidatingInterceptor.ifPresent(fhirServer::registerInterceptor);
|
||||
|
||||
if (appProperties.getImplementationGuides() != null) {
|
||||
Map<String, AppProperties.ImplementationGuide> guides = appProperties.getImplementationGuides();
|
||||
for (Map.Entry<String, AppProperties.ImplementationGuide> guide : guides.entrySet()) {
|
||||
PackageInstallationSpec packageInstallationSpec = new PackageInstallationSpec().setPackageUrl(guide.getValue().getUrl()).setName(guide.getValue().getName()).setVersion(guide.getValue().getVersion()).setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL);
|
||||
if (appProperties.getInstall_transitive_ig_dependencies()) {
|
||||
packageInstallationSpec.setFetchDependencies(true);
|
||||
packageInstallationSpec.setDependencyExcludes(ImmutableList.of("hl7.fhir.r2.core", "hl7.fhir.r3.core", "hl7.fhir.r4.core", "hl7.fhir.r5.core"));
|
||||
}
|
||||
packageInstallerSvc.install(packageInstallationSpec);
|
||||
}
|
||||
}
|
||||
|
||||
return fhirServer;
|
||||
}
|
||||
|
||||
public static IServerConformanceProvider<?> calculateConformanceProvider(IFhirSystemDao fhirSystemDao, RestfulServer fhirServer, DaoConfig daoConfig, ISearchParamRegistry searchParamRegistry, IValidationSupport theValidationSupport) {
|
||||
FhirVersionEnum fhirVersion = fhirSystemDao.getContext().getVersion().getVersion();
|
||||
if (fhirVersion == FhirVersionEnum.DSTU2) {
|
||||
|
||||
JpaConformanceProviderDstu2 confProvider = new JpaConformanceProviderDstu2(fhirServer, fhirSystemDao, daoConfig);
|
||||
confProvider.setImplementationDescription("HAPI FHIR DSTU2 Server");
|
||||
return confProvider;
|
||||
} else if (fhirVersion == FhirVersionEnum.DSTU3) {
|
||||
|
||||
JpaConformanceProviderDstu3 confProvider = new JpaConformanceProviderDstu3(fhirServer, fhirSystemDao, daoConfig, searchParamRegistry);
|
||||
confProvider.setImplementationDescription("HAPI FHIR DSTU3 Server");
|
||||
return confProvider;
|
||||
} else if (fhirVersion == FhirVersionEnum.R4) {
|
||||
|
||||
JpaCapabilityStatementProvider confProvider = new JpaCapabilityStatementProvider(fhirServer, fhirSystemDao, daoConfig, searchParamRegistry, theValidationSupport);
|
||||
confProvider.setImplementationDescription("HAPI FHIR R4 Server");
|
||||
return confProvider;
|
||||
} else if (fhirVersion == FhirVersionEnum.R5) {
|
||||
|
||||
JpaCapabilityStatementProvider confProvider = new JpaCapabilityStatementProvider(fhirServer, fhirSystemDao, daoConfig, searchParamRegistry, theValidationSupport);
|
||||
confProvider.setImplementationDescription("HAPI FHIR R5 Server");
|
||||
return confProvider;
|
||||
} else {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package ca.uhn.fhir.jpa.starter.common.validation;
|
||||
|
||||
import ca.uhn.fhir.jpa.interceptor.validation.RepositoryValidatingInterceptor;
|
||||
|
||||
public interface IRepositoryValidationInterceptorFactory {
|
||||
|
||||
String ENABLE_REPOSITORY_VALIDATING_INTERCEPTOR = "enable_repository_validating_interceptor";
|
||||
RepositoryValidatingInterceptor buildUsingStoredStructureDefinitions();
|
||||
|
||||
RepositoryValidatingInterceptor build();
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package ca.uhn.fhir.jpa.starter.common.validation;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.interceptor.validation.IRepositoryValidatingRule;
|
||||
import ca.uhn.fhir.jpa.interceptor.validation.RepositoryValidatingInterceptor;
|
||||
import ca.uhn.fhir.jpa.interceptor.validation.RepositoryValidatingRuleBuilder;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.jpa.starter.annotations.OnDSTU3Condition;
|
||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.param.TokenParam;
|
||||
import org.hl7.fhir.dstu3.model.StructureDefinition;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static ca.uhn.fhir.jpa.starter.common.validation.IRepositoryValidationInterceptorFactory.ENABLE_REPOSITORY_VALIDATING_INTERCEPTOR;
|
||||
|
||||
/**
|
||||
* This class can be customized to enable the {@link RepositoryValidatingInterceptor}
|
||||
* on this server.
|
||||
* <p>
|
||||
* The <code>enable_repository_validating_interceptor</code> property must be enabled in <code>application.yaml</code>
|
||||
* in order to use this class.
|
||||
*/
|
||||
@ConditionalOnProperty(prefix = "hapi.fhir", name = ENABLE_REPOSITORY_VALIDATING_INTERCEPTOR, havingValue = "true")
|
||||
@Configuration
|
||||
@Conditional(OnDSTU3Condition.class)
|
||||
public class RepositoryValidationInterceptorFactoryDstu3 implements IRepositoryValidationInterceptorFactory {
|
||||
|
||||
private final FhirContext fhirContext;
|
||||
private final RepositoryValidatingRuleBuilder repositoryValidatingRuleBuilder;
|
||||
private final IFhirResourceDao structureDefinitionResourceProvider;
|
||||
|
||||
public RepositoryValidationInterceptorFactoryDstu3(RepositoryValidatingRuleBuilder repositoryValidatingRuleBuilder, DaoRegistry daoRegistry) {
|
||||
this.repositoryValidatingRuleBuilder = repositoryValidatingRuleBuilder;
|
||||
this.fhirContext = daoRegistry.getSystemDao().getContext();
|
||||
structureDefinitionResourceProvider = daoRegistry.getResourceDao("StructureDefinition");
|
||||
|
||||
}
|
||||
|
||||
public RepositoryValidatingInterceptor buildUsingStoredStructureDefinitions() {
|
||||
|
||||
IBundleProvider results = structureDefinitionResourceProvider.search(new SearchParameterMap().add(StructureDefinition.SP_KIND, new TokenParam("resource")));
|
||||
Map<String, List<StructureDefinition>> structureDefintions = results.getResources(0, results.size())
|
||||
.stream()
|
||||
.map(StructureDefinition.class::cast)
|
||||
.collect(Collectors.groupingBy(StructureDefinition::getType));
|
||||
|
||||
structureDefintions.forEach((key, value) -> {
|
||||
String[] urls = value.stream().map(StructureDefinition::getUrl).toArray(String[]::new);
|
||||
repositoryValidatingRuleBuilder.forResourcesOfType(key).requireAtLeastOneProfileOf(urls).and().requireValidationToDeclaredProfiles();
|
||||
});
|
||||
|
||||
List<IRepositoryValidatingRule> rules = repositoryValidatingRuleBuilder.build();
|
||||
return new RepositoryValidatingInterceptor(fhirContext, rules);
|
||||
}
|
||||
|
||||
public RepositoryValidatingInterceptor build() {
|
||||
|
||||
// Customize the ruleBuilder here to have the rules you want! We will give a simple example
|
||||
// of enabling validation for all Patient resources
|
||||
repositoryValidatingRuleBuilder.forResourcesOfType("Patient").requireAtLeastProfile("http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient").and().requireValidationToDeclaredProfiles();
|
||||
|
||||
// Do not customize below this line
|
||||
List<IRepositoryValidatingRule> rules = repositoryValidatingRuleBuilder.build();
|
||||
return new RepositoryValidatingInterceptor(fhirContext, rules);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package ca.uhn.fhir.jpa.starter.common.validation;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.interceptor.validation.IRepositoryValidatingRule;
|
||||
import ca.uhn.fhir.jpa.interceptor.validation.RepositoryValidatingInterceptor;
|
||||
import ca.uhn.fhir.jpa.interceptor.validation.RepositoryValidatingRuleBuilder;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.jpa.starter.annotations.OnR4Condition;
|
||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.param.TokenParam;
|
||||
import org.hl7.fhir.r4.model.StructureDefinition;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static ca.uhn.fhir.jpa.starter.common.validation.IRepositoryValidationInterceptorFactory.ENABLE_REPOSITORY_VALIDATING_INTERCEPTOR;
|
||||
|
||||
/**
|
||||
* This class can be customized to enable the {@link ca.uhn.fhir.jpa.interceptor.validation.RepositoryValidatingInterceptor}
|
||||
* on this server.
|
||||
* <p>
|
||||
* The <code>enable_repository_validating_interceptor</code> property must be enabled in <code>application.yaml</code>
|
||||
* in order to use this class.
|
||||
*/
|
||||
@ConditionalOnProperty(prefix = "hapi.fhir", name = ENABLE_REPOSITORY_VALIDATING_INTERCEPTOR, havingValue = "true")
|
||||
@Configuration
|
||||
@Conditional(OnR4Condition.class)
|
||||
public class RepositoryValidationInterceptorFactoryR4 implements IRepositoryValidationInterceptorFactory {
|
||||
|
||||
private final FhirContext fhirContext;
|
||||
private final RepositoryValidatingRuleBuilder repositoryValidatingRuleBuilder;
|
||||
private final IFhirResourceDao structureDefinitionResourceProvider;
|
||||
|
||||
public RepositoryValidationInterceptorFactoryR4(RepositoryValidatingRuleBuilder repositoryValidatingRuleBuilder, DaoRegistry daoRegistry) {
|
||||
this.repositoryValidatingRuleBuilder = repositoryValidatingRuleBuilder;
|
||||
this.fhirContext = daoRegistry.getSystemDao().getContext();
|
||||
structureDefinitionResourceProvider = daoRegistry.getResourceDao("StructureDefinition");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public RepositoryValidatingInterceptor buildUsingStoredStructureDefinitions() {
|
||||
|
||||
IBundleProvider results = structureDefinitionResourceProvider.search(new SearchParameterMap().add(StructureDefinition.SP_KIND, new TokenParam("resource")));
|
||||
Map<String, List<StructureDefinition>> structureDefintions = results.getResources(0, results.size())
|
||||
.stream()
|
||||
.map(StructureDefinition.class::cast)
|
||||
.collect(Collectors.groupingBy(StructureDefinition::getType));
|
||||
|
||||
structureDefintions.forEach((key, value) -> {
|
||||
String[] urls = value.stream().map(StructureDefinition::getUrl).toArray(String[]::new);
|
||||
repositoryValidatingRuleBuilder.forResourcesOfType(key).requireAtLeastOneProfileOf(urls).and().requireValidationToDeclaredProfiles();
|
||||
});
|
||||
|
||||
List<IRepositoryValidatingRule> rules = repositoryValidatingRuleBuilder.build();
|
||||
return new RepositoryValidatingInterceptor(fhirContext, rules);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RepositoryValidatingInterceptor build() {
|
||||
|
||||
// Customize the ruleBuilder here to have the rules you want! We will give a simple example
|
||||
// of enabling validation for all Patient resources
|
||||
repositoryValidatingRuleBuilder.forResourcesOfType("Patient").requireAtLeastProfile("http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient").and().requireValidationToDeclaredProfiles();
|
||||
|
||||
// Do not customize below this line
|
||||
List<IRepositoryValidatingRule> rules = repositoryValidatingRuleBuilder.build();
|
||||
return new RepositoryValidatingInterceptor(fhirContext, rules);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package ca.uhn.fhir.jpa.starter.common.validation;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.interceptor.validation.IRepositoryValidatingRule;
|
||||
import ca.uhn.fhir.jpa.interceptor.validation.RepositoryValidatingInterceptor;
|
||||
import ca.uhn.fhir.jpa.interceptor.validation.RepositoryValidatingRuleBuilder;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.jpa.starter.annotations.OnR5Condition;
|
||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.param.TokenParam;
|
||||
import org.hl7.fhir.r5.model.StructureDefinition;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static ca.uhn.fhir.jpa.starter.common.validation.IRepositoryValidationInterceptorFactory.ENABLE_REPOSITORY_VALIDATING_INTERCEPTOR;
|
||||
|
||||
/**
|
||||
* This class can be customized to enable the {@link RepositoryValidatingInterceptor}
|
||||
* on this server.
|
||||
* <p>
|
||||
* The <code>enable_repository_validating_interceptor</code> property must be enabled in <code>application.yaml</code>
|
||||
* in order to use this class.
|
||||
*/
|
||||
@ConditionalOnProperty(prefix = "hapi.fhir", name = ENABLE_REPOSITORY_VALIDATING_INTERCEPTOR, havingValue = "true")
|
||||
@Configuration
|
||||
@Conditional(OnR5Condition.class)
|
||||
public class RepositoryValidationInterceptorFactoryR5 implements IRepositoryValidationInterceptorFactory {
|
||||
|
||||
private final FhirContext fhirContext;
|
||||
private final RepositoryValidatingRuleBuilder repositoryValidatingRuleBuilder;
|
||||
private final IFhirResourceDao structureDefinitionResourceProvider;
|
||||
|
||||
public RepositoryValidationInterceptorFactoryR5(RepositoryValidatingRuleBuilder repositoryValidatingRuleBuilder, DaoRegistry daoRegistry) {
|
||||
this.repositoryValidatingRuleBuilder = repositoryValidatingRuleBuilder;
|
||||
this.fhirContext = daoRegistry.getSystemDao().getContext();
|
||||
structureDefinitionResourceProvider = daoRegistry.getResourceDao("StructureDefinition");
|
||||
|
||||
}
|
||||
|
||||
public RepositoryValidatingInterceptor buildUsingStoredStructureDefinitions() {
|
||||
|
||||
IBundleProvider results = structureDefinitionResourceProvider.search(new SearchParameterMap().add(StructureDefinition.SP_KIND, new TokenParam("resource")));
|
||||
Map<String, List<StructureDefinition>> structureDefintions = results.getResources(0, results.size())
|
||||
.stream()
|
||||
.map(StructureDefinition.class::cast)
|
||||
.collect(Collectors.groupingBy(StructureDefinition::getType));
|
||||
|
||||
structureDefintions.forEach((key, value) -> {
|
||||
String[] urls = value.stream().map(StructureDefinition::getUrl).toArray(String[]::new);
|
||||
repositoryValidatingRuleBuilder.forResourcesOfType(key).requireAtLeastOneProfileOf(urls).and().requireValidationToDeclaredProfiles();
|
||||
});
|
||||
|
||||
List<IRepositoryValidatingRule> rules = repositoryValidatingRuleBuilder.build();
|
||||
return new RepositoryValidatingInterceptor(fhirContext, rules);
|
||||
}
|
||||
|
||||
public RepositoryValidatingInterceptor build() {
|
||||
|
||||
// Customize the ruleBuilder here to have the rules you want! We will give a simple example
|
||||
// of enabling validation for all Patient resources
|
||||
repositoryValidatingRuleBuilder.forResourcesOfType("Patient").requireAtLeastProfile("http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient").and().requireValidationToDeclaredProfiles();
|
||||
|
||||
// Do not customize below this line
|
||||
List<IRepositoryValidatingRule> rules = repositoryValidatingRuleBuilder.build();
|
||||
return new RepositoryValidatingInterceptor(fhirContext, rules);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user