Merge branch 'master' into rel_5_1_0
This commit is contained in:
3
.dockerignore
Normal file
3
.dockerignore
Normal file
@@ -0,0 +1,3 @@
|
||||
target
|
||||
.idea
|
||||
.git
|
||||
@@ -2,18 +2,16 @@ FROM hapiproject/hapi:base as build-hapi
|
||||
|
||||
ARG HAPI_FHIR_URL=https://github.com/jamesagnew/hapi-fhir/
|
||||
ARG HAPI_FHIR_BRANCH=master
|
||||
ARG HAPI_FHIR_STARTER_URL=https://github.com/hapifhir/hapi-fhir-jpaserver-starter/
|
||||
ARG HAPI_FHIR_STARTER_BRANCH=master
|
||||
|
||||
RUN git clone --branch ${HAPI_FHIR_BRANCH} ${HAPI_FHIR_URL}
|
||||
WORKDIR /tmp/hapi-fhir/
|
||||
RUN /tmp/apache-maven-3.6.2/bin/mvn dependency:resolve
|
||||
RUN /tmp/apache-maven-3.6.2/bin/mvn install -DskipTests
|
||||
|
||||
WORKDIR /tmp
|
||||
RUN git clone --branch ${HAPI_FHIR_STARTER_BRANCH} ${HAPI_FHIR_STARTER_URL}
|
||||
|
||||
WORKDIR /tmp/hapi-fhir-jpaserver-starter
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN /tmp/apache-maven-3.6.2/bin/mvn clean install -DskipTests
|
||||
|
||||
FROM tomcat:9-jre11
|
||||
|
||||
51
README.md
51
README.md
@@ -12,6 +12,57 @@ In order to use this sample, you should have:
|
||||
- Oracle Java (JDK) installed: Minimum JDK8 or newer.
|
||||
- Apache Maven build tool (newest version)
|
||||
|
||||
## Running via [Docker Hub](https://hub.docker.com/repository/docker/hapiproject/hapi)
|
||||
|
||||
Each tagged/released version of `hapi-fhir-jpaserver` is built as a Docker image and published to Docker hub. To run the published Docker image from DockerHub:
|
||||
|
||||
```
|
||||
docker pull hapiproject/hapi:latest
|
||||
docker run -p 8080:8080 hapiproject/hapi:tagname
|
||||
```
|
||||
|
||||
This will run the docker image with the default configuration, mapping port 8080 from the container to port 8080 in the host. Once running, you can access `http://localhost:8080/hapi-fhir-jpaserver/fhir` in the browser to access the HAPI FHIR server's UI.
|
||||
|
||||
If you change the mapped port, you need to change the configuration used by HAPI to have the correct `server_address` property/value.
|
||||
|
||||
### Configuration via environment variables
|
||||
|
||||
You can customize HAPI directly from the `run` command using environment variables. For example:
|
||||
|
||||
`docker run -p 8090:8080 -e server_address=http://localhost:8090/hapi-fhir-jpaserver/fhir hapiproject/hapi:tagname`
|
||||
|
||||
HAPI looks in the environment variables for properties in the [hapi.properties](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/hapi.properties) file.
|
||||
|
||||
### Configuration via overridden hapi.properties file
|
||||
|
||||
You can customize HAPI by telling HAPI to look for the `hapi.properties` file in a different location:
|
||||
|
||||
`docker run -p 8090:8080 -e hapi.properties=/some/directory/with/hapi.properties hapiproject/hapi:tagname`
|
||||
|
||||
### Example docker-compose.yml
|
||||
|
||||
```
|
||||
version: '3.7'
|
||||
services:
|
||||
web:
|
||||
image: "hapiproject/hapi:tagname"
|
||||
ports:
|
||||
- "8090:8080"
|
||||
configs:
|
||||
- source: hapi
|
||||
target: /data/hapi/hapi.properties
|
||||
volumes:
|
||||
- hapi-data:/data/hapi
|
||||
environment:
|
||||
JAVA_OPTS: '-Dhapi.properties=/data/hapi/hapi.properties'
|
||||
configs:
|
||||
hapi:
|
||||
external: true
|
||||
volumes:
|
||||
hapi-data:
|
||||
external: true
|
||||
```
|
||||
|
||||
## Running locally
|
||||
|
||||
The easiest way to run this server is to run it directly in Maven using a built-in Jetty server. To do this, change `src/main/resources/hapi.properties` `server_address` and `server.base` with the values commented out as _For Jetty, use this_ and then execute the following command:
|
||||
|
||||
2
pom.xml
2
pom.xml
@@ -48,7 +48,7 @@
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>8.0.11</version>
|
||||
<version>8.0.16</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/org.postgresql/postgresql -->
|
||||
<dependency>
|
||||
|
||||
337
src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java
Normal file
337
src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java
Normal file
@@ -0,0 +1,337 @@
|
||||
package ca.uhn.fhir.jpa.starter;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
|
||||
import ca.uhn.fhir.interceptor.api.IInterceptorService;
|
||||
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.api.rp.ResourceProviderFactory;
|
||||
import ca.uhn.fhir.jpa.binstore.BinaryStorageInterceptor;
|
||||
import ca.uhn.fhir.jpa.bulk.BulkDataExportProvider;
|
||||
import ca.uhn.fhir.jpa.interceptor.CascadingDeleteInterceptor;
|
||||
import ca.uhn.fhir.jpa.partition.PartitionManagementProvider;
|
||||
import ca.uhn.fhir.jpa.provider.GraphQLProvider;
|
||||
import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu2;
|
||||
import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2;
|
||||
import ca.uhn.fhir.jpa.provider.SubscriptionTriggeringProvider;
|
||||
import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider;
|
||||
import ca.uhn.fhir.jpa.provider.dstu3.JpaConformanceProviderDstu3;
|
||||
import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3;
|
||||
import ca.uhn.fhir.jpa.provider.r4.JpaConformanceProviderR4;
|
||||
import ca.uhn.fhir.jpa.provider.r4.JpaSystemProviderR4;
|
||||
import ca.uhn.fhir.jpa.provider.r5.JpaConformanceProviderR5;
|
||||
import ca.uhn.fhir.jpa.provider.r5.JpaSystemProviderR5;
|
||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
||||
import ca.uhn.fhir.jpa.subscription.util.SubscriptionDebugLogInterceptor;
|
||||
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
|
||||
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
||||
import ca.uhn.fhir.rest.server.HardcodedServerAddressStrategy;
|
||||
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.interceptor.partition.RequestTenantPartitionInterceptor;
|
||||
import ca.uhn.fhir.rest.server.tenant.UrlBaseTenantIdentificationStrategy;
|
||||
import ca.uhn.fhir.validation.IValidatorModule;
|
||||
import ca.uhn.fhir.validation.ResultSeverityEnum;
|
||||
import org.hl7.fhir.dstu3.model.Bundle;
|
||||
import org.hl7.fhir.dstu3.model.Meta;
|
||||
import org.hl7.fhir.r4.model.Bundle.BundleType;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
public class BaseJpaRestfulServer extends RestfulServer {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected void initialize() throws ServletException {
|
||||
super.initialize();
|
||||
|
||||
/*
|
||||
* Create a FhirContext object that uses the version of FHIR
|
||||
* specified in the properties file.
|
||||
*/
|
||||
ApplicationContext appCtx = (ApplicationContext) getServletContext()
|
||||
.getAttribute("org.springframework.web.context.WebApplicationContext.ROOT");
|
||||
// Customize supported resource types
|
||||
Set<String> supportedResourceTypes = HapiProperties.getSupportedResourceTypes();
|
||||
|
||||
if (!supportedResourceTypes.isEmpty() && !supportedResourceTypes.contains("SearchParameter")) {
|
||||
supportedResourceTypes.add("SearchParameter");
|
||||
}
|
||||
|
||||
if (!supportedResourceTypes.isEmpty()) {
|
||||
DaoRegistry daoRegistry = appCtx.getBean(DaoRegistry.class);
|
||||
daoRegistry.setSupportedResourceTypes(supportedResourceTypes);
|
||||
}
|
||||
|
||||
/*
|
||||
* ResourceProviders are fetched from the Spring context
|
||||
*/
|
||||
FhirVersionEnum fhirVersion = HapiProperties.getFhirVersion();
|
||||
ResourceProviderFactory resourceProviders;
|
||||
Object systemProvider;
|
||||
if (fhirVersion == FhirVersionEnum.DSTU2) {
|
||||
resourceProviders = appCtx.getBean("myResourceProvidersDstu2", ResourceProviderFactory.class);
|
||||
systemProvider = appCtx.getBean("mySystemProviderDstu2", JpaSystemProviderDstu2.class);
|
||||
} else if (fhirVersion == FhirVersionEnum.DSTU3) {
|
||||
resourceProviders = appCtx.getBean("myResourceProvidersDstu3", ResourceProviderFactory.class);
|
||||
systemProvider = appCtx.getBean("mySystemProviderDstu3", JpaSystemProviderDstu3.class);
|
||||
} else if (fhirVersion == FhirVersionEnum.R4) {
|
||||
resourceProviders = appCtx.getBean("myResourceProvidersR4", ResourceProviderFactory.class);
|
||||
systemProvider = appCtx.getBean("mySystemProviderR4", JpaSystemProviderR4.class);
|
||||
} else if (fhirVersion == FhirVersionEnum.R5) {
|
||||
resourceProviders = appCtx.getBean("myResourceProvidersR5", ResourceProviderFactory.class);
|
||||
systemProvider = appCtx.getBean("mySystemProviderR5", JpaSystemProviderR5.class);
|
||||
} else {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
setFhirContext(appCtx.getBean(FhirContext.class));
|
||||
|
||||
registerProviders(resourceProviders.createProviders());
|
||||
registerProvider(systemProvider);
|
||||
|
||||
/*
|
||||
* The conformance provider exports the supported resources, search parameters, etc for
|
||||
* this server. The JPA version adds resourceProviders counts to the exported statement, so it
|
||||
* is a nice addition.
|
||||
*
|
||||
* You can also create your own subclass of the conformance provider if you need to
|
||||
* provide further customization of your server's CapabilityStatement
|
||||
*/
|
||||
DaoConfig daoConfig = appCtx.getBean(DaoConfig.class);
|
||||
ISearchParamRegistry searchParamRegistry = appCtx.getBean(ISearchParamRegistry.class);
|
||||
if (fhirVersion == FhirVersionEnum.DSTU2) {
|
||||
IFhirSystemDao<ca.uhn.fhir.model.dstu2.resource.Bundle, MetaDt> systemDao = appCtx.getBean("mySystemDaoDstu2", IFhirSystemDao.class);
|
||||
JpaConformanceProviderDstu2 confProvider = new JpaConformanceProviderDstu2(this, systemDao, daoConfig);
|
||||
confProvider.setImplementationDescription("HAPI FHIR DSTU2 Server");
|
||||
setServerConformanceProvider(confProvider);
|
||||
} else {
|
||||
if (fhirVersion == FhirVersionEnum.DSTU3) {
|
||||
IFhirSystemDao<Bundle, Meta> systemDao = appCtx.getBean("mySystemDaoDstu3", IFhirSystemDao.class);
|
||||
JpaConformanceProviderDstu3 confProvider = new JpaConformanceProviderDstu3(this, systemDao, daoConfig, searchParamRegistry);
|
||||
confProvider.setImplementationDescription("HAPI FHIR DSTU3 Server");
|
||||
setServerConformanceProvider(confProvider);
|
||||
} else if (fhirVersion == FhirVersionEnum.R4) {
|
||||
IFhirSystemDao<org.hl7.fhir.r4.model.Bundle, org.hl7.fhir.r4.model.Meta> systemDao = appCtx.getBean("mySystemDaoR4", IFhirSystemDao.class);
|
||||
JpaConformanceProviderR4 confProvider = new JpaConformanceProviderR4(this, systemDao, daoConfig, searchParamRegistry);
|
||||
confProvider.setImplementationDescription("HAPI FHIR R4 Server");
|
||||
setServerConformanceProvider(confProvider);
|
||||
} else if (fhirVersion == FhirVersionEnum.R5) {
|
||||
IFhirSystemDao<org.hl7.fhir.r5.model.Bundle, org.hl7.fhir.r5.model.Meta> systemDao = appCtx.getBean("mySystemDaoR5", IFhirSystemDao.class);
|
||||
JpaConformanceProviderR5 confProvider = new JpaConformanceProviderR5(this, systemDao, daoConfig, searchParamRegistry);
|
||||
confProvider.setImplementationDescription("HAPI FHIR R5 Server");
|
||||
setServerConformanceProvider(confProvider);
|
||||
} else {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ETag Support
|
||||
*/
|
||||
setETagSupport(HapiProperties.getEtagSupport());
|
||||
|
||||
/*
|
||||
* This server tries to dynamically generate narratives
|
||||
*/
|
||||
FhirContext ctx = getFhirContext();
|
||||
ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator());
|
||||
|
||||
/*
|
||||
* Default to JSON and pretty printing
|
||||
*/
|
||||
setDefaultPrettyPrint(HapiProperties.getDefaultPrettyPrint());
|
||||
|
||||
/*
|
||||
* Default encoding
|
||||
*/
|
||||
setDefaultResponseEncoding(HapiProperties.getDefaultEncoding());
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
setPagingProvider(appCtx.getBean(DatabaseBackedPagingProvider.class));
|
||||
|
||||
/*
|
||||
* This interceptor formats the output using nice colourful
|
||||
* HTML output when the request is detected to come from a
|
||||
* browser.
|
||||
*/
|
||||
ResponseHighlighterInterceptor responseHighlighterInterceptor = new ResponseHighlighterInterceptor();
|
||||
this.registerInterceptor(responseHighlighterInterceptor);
|
||||
|
||||
if (HapiProperties.isFhirPathFilterInterceptorEnabled()) {
|
||||
registerInterceptor(new FhirPathFilterInterceptor());
|
||||
}
|
||||
|
||||
/*
|
||||
* Add some logging for each request
|
||||
*/
|
||||
LoggingInterceptor loggingInterceptor = new LoggingInterceptor();
|
||||
loggingInterceptor.setLoggerName(HapiProperties.getLoggerName());
|
||||
loggingInterceptor.setMessageFormat(HapiProperties.getLoggerFormat());
|
||||
loggingInterceptor.setErrorMessageFormat(HapiProperties.getLoggerErrorFormat());
|
||||
loggingInterceptor.setLogExceptions(HapiProperties.getLoggerLogExceptions());
|
||||
this.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 = HapiProperties.getServerAddress();
|
||||
if (serverAddress != null && serverAddress.length() > 0) {
|
||||
setServerAddressStrategy(new HardcodedServerAddressStrategy(serverAddress));
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 (false) { // <-- DISABLED RIGHT NOW
|
||||
registerProvider(appCtx.getBean(TerminologyUploaderProvider.class));
|
||||
}
|
||||
|
||||
// If you want to enable the $trigger-subscription operation to allow
|
||||
// manual triggering of a subscription delivery, enable this provider
|
||||
if (false) { // <-- DISABLED RIGHT NOW
|
||||
SubscriptionTriggeringProvider retriggeringProvider = appCtx
|
||||
.getBean(SubscriptionTriggeringProvider.class);
|
||||
registerProvider(retriggeringProvider);
|
||||
}
|
||||
|
||||
// Define your CORS configuration. This is an example
|
||||
// showing a typical setup. You should customize this
|
||||
// to your specific needs
|
||||
if (HapiProperties.getCorsEnabled()) {
|
||||
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");
|
||||
String allAllowedCORSOrigins = HapiProperties.getCorsAllowedOrigin();
|
||||
Arrays.stream(allAllowedCORSOrigins.split(",")).forEach(o -> {
|
||||
config.addAllowedOrigin(o);
|
||||
});
|
||||
config.addAllowedOrigin(HapiProperties.getCorsAllowedOrigin());
|
||||
|
||||
config.addExposedHeader("Location");
|
||||
config.addExposedHeader("Content-Location");
|
||||
config.setAllowedMethods(
|
||||
Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH", "HEAD"));
|
||||
config.setAllowCredentials(HapiProperties.getCorsAllowedCredentials());
|
||||
|
||||
// Create the interceptor and register it
|
||||
CorsInterceptor interceptor = new CorsInterceptor(config);
|
||||
registerInterceptor(interceptor);
|
||||
}
|
||||
|
||||
// If subscriptions are enabled, we want to register the interceptor that
|
||||
// will activate them and match results against them
|
||||
if (HapiProperties.getSubscriptionWebsocketEnabled() ||
|
||||
HapiProperties.getSubscriptionEmailEnabled() ||
|
||||
HapiProperties.getSubscriptionRestHookEnabled()) {
|
||||
// Subscription debug logging
|
||||
IInterceptorService interceptorService = appCtx.getBean(IInterceptorService.class);
|
||||
interceptorService.registerInterceptor(new SubscriptionDebugLogInterceptor());
|
||||
}
|
||||
|
||||
// Cascading deletes
|
||||
DaoRegistry daoRegistry = appCtx.getBean(DaoRegistry.class);
|
||||
IInterceptorBroadcaster interceptorBroadcaster = appCtx.getBean(IInterceptorBroadcaster.class);
|
||||
if (HapiProperties.getAllowCascadingDeletes()) {
|
||||
CascadingDeleteInterceptor cascadingDeleteInterceptor = new CascadingDeleteInterceptor(ctx, daoRegistry, interceptorBroadcaster);
|
||||
getInterceptorService().registerInterceptor(cascadingDeleteInterceptor);
|
||||
}
|
||||
|
||||
// Binary Storage
|
||||
if (HapiProperties.isBinaryStorageEnabled()) {
|
||||
BinaryStorageInterceptor binaryStorageInterceptor = appCtx
|
||||
.getBean(BinaryStorageInterceptor.class);
|
||||
getInterceptorService().registerInterceptor(binaryStorageInterceptor);
|
||||
}
|
||||
|
||||
// Validation
|
||||
IValidatorModule validatorModule = appCtx.getBean(IValidatorModule.class);
|
||||
if (validatorModule != null) {
|
||||
if (HapiProperties.getValidateRequestsEnabled()) {
|
||||
RequestValidatingInterceptor interceptor = new RequestValidatingInterceptor();
|
||||
interceptor.setFailOnSeverity(ResultSeverityEnum.ERROR);
|
||||
interceptor.setValidatorModules(Collections.singletonList(validatorModule));
|
||||
registerInterceptor(interceptor);
|
||||
}
|
||||
if (HapiProperties.getValidateResponsesEnabled()) {
|
||||
ResponseValidatingInterceptor interceptor = new ResponseValidatingInterceptor();
|
||||
interceptor.setFailOnSeverity(ResultSeverityEnum.ERROR);
|
||||
interceptor.setValidatorModules(Collections.singletonList(validatorModule));
|
||||
registerInterceptor(interceptor);
|
||||
}
|
||||
}
|
||||
|
||||
// GraphQL
|
||||
if (HapiProperties.getGraphqlEnabled()) {
|
||||
if (fhirVersion.isEqualOrNewerThan(FhirVersionEnum.DSTU3)) {
|
||||
registerProvider(appCtx.getBean(GraphQLProvider.class));
|
||||
}
|
||||
}
|
||||
|
||||
if (!HapiProperties.getAllowedBundleTypes().isEmpty()) {
|
||||
String allowedBundleTypesString = HapiProperties.getAllowedBundleTypes();
|
||||
Set<String> allowedBundleTypes = new HashSet<>();
|
||||
Arrays.stream(allowedBundleTypesString.split(",")).forEach(o -> {
|
||||
BundleType type = BundleType.valueOf(o);
|
||||
allowedBundleTypes.add(type.toCode());
|
||||
});
|
||||
DaoConfig config = daoConfig;
|
||||
config.setBundleTypesAllowedForStorage(
|
||||
Collections.unmodifiableSet(new TreeSet<>(allowedBundleTypes)));
|
||||
}
|
||||
|
||||
// Bulk Export
|
||||
if (HapiProperties.getBulkExportEnabled()) {
|
||||
registerProvider(appCtx.getBean(BulkDataExportProvider.class));
|
||||
}
|
||||
|
||||
// Partitioning
|
||||
if (HapiProperties.getPartitioningMultitenancyEnabled()) {
|
||||
registerInterceptor(new RequestTenantPartitionInterceptor());
|
||||
setTenantIdentificationStrategy(new UrlBaseTenantIdentificationStrategy());
|
||||
registerProviders(appCtx.getBean(PartitionManagementProvider.class));
|
||||
}
|
||||
|
||||
if (HapiProperties.getClientIdStrategy() == DaoConfig.ClientIdStrategyEnum.ANY) {
|
||||
daoConfig.setResourceServerIdStrategy(DaoConfig.IdStrategyEnum.UUID);
|
||||
daoConfig.setResourceClientIdStrategy(HapiProperties.getClientIdStrategy());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package ca.uhn.fhir.jpa.starter;
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.search.elastic.ElasticsearchHibernatePropertiesBuilder;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.ETagSupportEnum;
|
||||
@@ -17,6 +18,7 @@ import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||
@@ -78,6 +80,7 @@ public class HapiProperties {
|
||||
static final String MAX_BINARY_SIZE = "max_binary_size";
|
||||
static final String PARTITIONING_MULTITENANCY_ENABLED = "partitioning.multitenancy.enabled";
|
||||
private static final String PARTITIONING_INCLUDE_PARTITION_IN_SEARCH_HASHES = "partitioning.partitioning_include_in_search_hashes";
|
||||
static final String CLIENT_ID_STRATEGY = "daoconfig.client_id_strategy";
|
||||
private static Properties ourProperties;
|
||||
|
||||
public static boolean isElasticSearchEnabled() {
|
||||
@@ -143,6 +146,10 @@ public class HapiProperties {
|
||||
if (overrideProps != null) {
|
||||
properties.putAll(overrideProps);
|
||||
}
|
||||
properties.putAll(System.getenv().entrySet()
|
||||
.stream()
|
||||
.filter(e -> e.getValue() != null && properties.containsKey(e.getKey()))
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)));
|
||||
return properties;
|
||||
}
|
||||
|
||||
@@ -243,6 +250,16 @@ public class HapiProperties {
|
||||
return ETagSupportEnum.ENABLED;
|
||||
}
|
||||
|
||||
public static DaoConfig.ClientIdStrategyEnum getClientIdStrategy() {
|
||||
String idStrategy = HapiProperties.getProperty(CLIENT_ID_STRATEGY);
|
||||
|
||||
if (idStrategy != null && idStrategy.length() > 0) {
|
||||
return DaoConfig.ClientIdStrategyEnum.valueOf(idStrategy);
|
||||
}
|
||||
|
||||
return DaoConfig.ClientIdStrategyEnum.ALPHANUMERIC;
|
||||
}
|
||||
|
||||
public static EncodingEnum getDefaultEncoding() {
|
||||
String defaultEncodingString = HapiProperties.getProperty(DEFAULT_ENCODING);
|
||||
|
||||
@@ -515,5 +532,8 @@ public class HapiProperties {
|
||||
return HapiProperties.getBooleanProperty("fhirpath_interceptor.enabled", false);
|
||||
}
|
||||
|
||||
public static boolean getPartitioningMultitenancyEnabled() {
|
||||
return HapiProperties.getBooleanProperty(PARTITIONING_MULTITENANCY_ENABLED, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,341 +1,16 @@
|
||||
package ca.uhn.fhir.jpa.starter;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
|
||||
import ca.uhn.fhir.interceptor.api.IInterceptorService;
|
||||
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.binstore.BinaryStorageInterceptor;
|
||||
import ca.uhn.fhir.jpa.bulk.provider.BulkDataExportProvider;
|
||||
import ca.uhn.fhir.jpa.interceptor.CascadingDeleteInterceptor;
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||
import ca.uhn.fhir.jpa.partition.PartitionManagementProvider;
|
||||
import ca.uhn.fhir.jpa.provider.GraphQLProvider;
|
||||
import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu2;
|
||||
import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2;
|
||||
import ca.uhn.fhir.jpa.provider.SubscriptionTriggeringProvider;
|
||||
import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider;
|
||||
import ca.uhn.fhir.jpa.provider.dstu3.JpaConformanceProviderDstu3;
|
||||
import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3;
|
||||
import ca.uhn.fhir.jpa.provider.r4.JpaConformanceProviderR4;
|
||||
import ca.uhn.fhir.jpa.provider.r4.JpaSystemProviderR4;
|
||||
import ca.uhn.fhir.jpa.provider.r5.JpaConformanceProviderR5;
|
||||
import ca.uhn.fhir.jpa.provider.r5.JpaSystemProviderR5;
|
||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
||||
import ca.uhn.fhir.jpa.subscription.util.SubscriptionDebugLogInterceptor;
|
||||
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
|
||||
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
||||
import ca.uhn.fhir.rest.server.HardcodedServerAddressStrategy;
|
||||
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.interceptor.partition.RequestTenantPartitionInterceptor;
|
||||
import ca.uhn.fhir.rest.server.provider.ResourceProviderFactory;
|
||||
import ca.uhn.fhir.rest.server.tenant.UrlBaseTenantIdentificationStrategy;
|
||||
import ca.uhn.fhir.validation.IValidatorModule;
|
||||
import ca.uhn.fhir.validation.ResultSeverityEnum;
|
||||
import org.hl7.fhir.dstu3.model.Bundle;
|
||||
import org.hl7.fhir.dstu3.model.Meta;
|
||||
import org.hl7.fhir.r4.model.Bundle.BundleType;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
public class JpaRestfulServer extends RestfulServer {
|
||||
public class JpaRestfulServer extends BaseJpaRestfulServer {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected void initialize() throws ServletException {
|
||||
super.initialize();
|
||||
|
||||
/*
|
||||
* Create a FhirContext object that uses the version of FHIR
|
||||
* specified in the properties file.
|
||||
*/
|
||||
ApplicationContext appCtx = (ApplicationContext) getServletContext()
|
||||
.getAttribute("org.springframework.web.context.WebApplicationContext.ROOT");
|
||||
// Customize supported resource types
|
||||
Set<String> supportedResourceTypes = HapiProperties.getSupportedResourceTypes();
|
||||
|
||||
if (!supportedResourceTypes.isEmpty() && !supportedResourceTypes.contains("SearchParameter")) {
|
||||
supportedResourceTypes.add("SearchParameter");
|
||||
}
|
||||
|
||||
if (!supportedResourceTypes.isEmpty()) {
|
||||
DaoRegistry daoRegistry = appCtx.getBean(DaoRegistry.class);
|
||||
daoRegistry.setSupportedResourceTypes(supportedResourceTypes);
|
||||
}
|
||||
|
||||
/*
|
||||
* ResourceProviders are fetched from the Spring context
|
||||
*/
|
||||
FhirVersionEnum fhirVersion = HapiProperties.getFhirVersion();
|
||||
ResourceProviderFactory resourceProviders;
|
||||
Object systemProvider;
|
||||
if (fhirVersion == FhirVersionEnum.DSTU2) {
|
||||
resourceProviders = appCtx.getBean("myResourceProvidersDstu2", ResourceProviderFactory.class);
|
||||
systemProvider = appCtx.getBean("mySystemProviderDstu2", JpaSystemProviderDstu2.class);
|
||||
} else if (fhirVersion == FhirVersionEnum.DSTU3) {
|
||||
resourceProviders = appCtx.getBean("myResourceProvidersDstu3", ResourceProviderFactory.class);
|
||||
systemProvider = appCtx.getBean("mySystemProviderDstu3", JpaSystemProviderDstu3.class);
|
||||
} else if (fhirVersion == FhirVersionEnum.R4) {
|
||||
resourceProviders = appCtx.getBean("myResourceProvidersR4", ResourceProviderFactory.class);
|
||||
systemProvider = appCtx.getBean("mySystemProviderR4", JpaSystemProviderR4.class);
|
||||
} else if (fhirVersion == FhirVersionEnum.R5) {
|
||||
resourceProviders = appCtx.getBean("myResourceProvidersR5", ResourceProviderFactory.class);
|
||||
systemProvider = appCtx.getBean("mySystemProviderR5", JpaSystemProviderR5.class);
|
||||
} else {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
setFhirContext(appCtx.getBean(FhirContext.class));
|
||||
|
||||
registerProviders(resourceProviders.createProviders());
|
||||
registerProvider(systemProvider);
|
||||
|
||||
/*
|
||||
* The conformance provider exports the supported resources, search parameters, etc for
|
||||
* this server. The JPA version adds resourceProviders counts to the exported statement, so it
|
||||
* is a nice addition.
|
||||
*
|
||||
* You can also create your own subclass of the conformance provider if you need to
|
||||
* provide further customization of your server's CapabilityStatement
|
||||
*/
|
||||
DaoConfig daoConfig = appCtx.getBean(DaoConfig.class);
|
||||
ISearchParamRegistry searchParamRegistry = appCtx.getBean(ISearchParamRegistry.class);
|
||||
if (fhirVersion == FhirVersionEnum.DSTU2) {
|
||||
IFhirSystemDao<ca.uhn.fhir.model.dstu2.resource.Bundle, MetaDt> systemDao = appCtx.getBean("mySystemDaoDstu2", IFhirSystemDao.class);
|
||||
JpaConformanceProviderDstu2 confProvider = new JpaConformanceProviderDstu2(this, systemDao, daoConfig);
|
||||
confProvider.setImplementationDescription("HAPI FHIR DSTU2 Server");
|
||||
setServerConformanceProvider(confProvider);
|
||||
} else {
|
||||
if (fhirVersion == FhirVersionEnum.DSTU3) {
|
||||
IFhirSystemDao<Bundle, Meta> systemDao = appCtx.getBean("mySystemDaoDstu3", IFhirSystemDao.class);
|
||||
JpaConformanceProviderDstu3 confProvider = new JpaConformanceProviderDstu3(this, systemDao, daoConfig, searchParamRegistry);
|
||||
confProvider.setImplementationDescription("HAPI FHIR DSTU3 Server");
|
||||
setServerConformanceProvider(confProvider);
|
||||
} else if (fhirVersion == FhirVersionEnum.R4) {
|
||||
IFhirSystemDao<org.hl7.fhir.r4.model.Bundle, org.hl7.fhir.r4.model.Meta> systemDao = appCtx.getBean("mySystemDaoR4", IFhirSystemDao.class);
|
||||
JpaConformanceProviderR4 confProvider = new JpaConformanceProviderR4(this, systemDao, daoConfig, searchParamRegistry);
|
||||
confProvider.setImplementationDescription("HAPI FHIR R4 Server");
|
||||
setServerConformanceProvider(confProvider);
|
||||
} else if (fhirVersion == FhirVersionEnum.R5) {
|
||||
IFhirSystemDao<org.hl7.fhir.r5.model.Bundle, org.hl7.fhir.r5.model.Meta> systemDao = appCtx.getBean("mySystemDaoR5", IFhirSystemDao.class);
|
||||
JpaConformanceProviderR5 confProvider = new JpaConformanceProviderR5(this, systemDao, daoConfig, searchParamRegistry);
|
||||
confProvider.setImplementationDescription("HAPI FHIR R5 Server");
|
||||
setServerConformanceProvider(confProvider);
|
||||
} else {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ETag Support
|
||||
*/
|
||||
setETagSupport(HapiProperties.getEtagSupport());
|
||||
|
||||
/*
|
||||
* This server tries to dynamically generate narratives
|
||||
*/
|
||||
FhirContext ctx = getFhirContext();
|
||||
ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator());
|
||||
|
||||
/*
|
||||
* Default to JSON and pretty printing
|
||||
*/
|
||||
setDefaultPrettyPrint(HapiProperties.getDefaultPrettyPrint());
|
||||
|
||||
/*
|
||||
* Default encoding
|
||||
*/
|
||||
setDefaultResponseEncoding(HapiProperties.getDefaultEncoding());
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
setPagingProvider(appCtx.getBean(DatabaseBackedPagingProvider.class));
|
||||
|
||||
/*
|
||||
* This interceptor formats the output using nice colourful
|
||||
* HTML output when the request is detected to come from a
|
||||
* browser.
|
||||
*/
|
||||
ResponseHighlighterInterceptor responseHighlighterInterceptor = new ResponseHighlighterInterceptor();
|
||||
this.registerInterceptor(responseHighlighterInterceptor);
|
||||
|
||||
if (HapiProperties.isFhirPathFilterInterceptorEnabled()) {
|
||||
registerInterceptor(new FhirPathFilterInterceptor());
|
||||
}
|
||||
|
||||
/*
|
||||
* Add some logging for each request
|
||||
*/
|
||||
LoggingInterceptor loggingInterceptor = new LoggingInterceptor();
|
||||
loggingInterceptor.setLoggerName(HapiProperties.getLoggerName());
|
||||
loggingInterceptor.setMessageFormat(HapiProperties.getLoggerFormat());
|
||||
loggingInterceptor.setErrorMessageFormat(HapiProperties.getLoggerErrorFormat());
|
||||
loggingInterceptor.setLogExceptions(HapiProperties.getLoggerLogExceptions());
|
||||
this.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 = HapiProperties.getServerAddress();
|
||||
if (serverAddress != null && serverAddress.length() > 0) {
|
||||
setServerAddressStrategy(new HardcodedServerAddressStrategy(serverAddress));
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 (false) { // <-- DISABLED RIGHT NOW
|
||||
registerProvider(appCtx.getBean(TerminologyUploaderProvider.class));
|
||||
}
|
||||
|
||||
// If you want to enable the $trigger-subscription operation to allow
|
||||
// manual triggering of a subscription delivery, enable this provider
|
||||
if (false) { // <-- DISABLED RIGHT NOW
|
||||
SubscriptionTriggeringProvider retriggeringProvider = appCtx
|
||||
.getBean(SubscriptionTriggeringProvider.class);
|
||||
registerProvider(retriggeringProvider);
|
||||
}
|
||||
|
||||
// Define your CORS configuration. This is an example
|
||||
// showing a typical setup. You should customize this
|
||||
// to your specific needs
|
||||
if (HapiProperties.getCorsEnabled()) {
|
||||
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");
|
||||
String allAllowedCORSOrigins = HapiProperties.getCorsAllowedOrigin();
|
||||
Arrays.stream(allAllowedCORSOrigins.split(",")).forEach(o -> {
|
||||
config.addAllowedOrigin(o);
|
||||
});
|
||||
config.addAllowedOrigin(HapiProperties.getCorsAllowedOrigin());
|
||||
|
||||
config.addExposedHeader("Location");
|
||||
config.addExposedHeader("Content-Location");
|
||||
config.setAllowedMethods(
|
||||
Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH", "HEAD"));
|
||||
config.setAllowCredentials(HapiProperties.getCorsAllowedCredentials());
|
||||
|
||||
// Create the interceptor and register it
|
||||
CorsInterceptor interceptor = new CorsInterceptor(config);
|
||||
registerInterceptor(interceptor);
|
||||
}
|
||||
|
||||
// If subscriptions are enabled, we want to register the interceptor that
|
||||
// will activate them and match results against them
|
||||
if (HapiProperties.getSubscriptionWebsocketEnabled() ||
|
||||
HapiProperties.getSubscriptionEmailEnabled() ||
|
||||
HapiProperties.getSubscriptionRestHookEnabled()) {
|
||||
// Subscription debug logging
|
||||
IInterceptorService interceptorService = appCtx.getBean(IInterceptorService.class);
|
||||
interceptorService.registerInterceptor(new SubscriptionDebugLogInterceptor());
|
||||
}
|
||||
|
||||
// Cascading deletes
|
||||
DaoRegistry daoRegistry = appCtx.getBean(DaoRegistry.class);
|
||||
IInterceptorBroadcaster interceptorBroadcaster = appCtx.getBean(IInterceptorBroadcaster.class);
|
||||
if (HapiProperties.getAllowCascadingDeletes()) {
|
||||
CascadingDeleteInterceptor cascadingDeleteInterceptor = new CascadingDeleteInterceptor(ctx, daoRegistry, interceptorBroadcaster);
|
||||
getInterceptorService().registerInterceptor(cascadingDeleteInterceptor);
|
||||
}
|
||||
|
||||
// Binary Storage
|
||||
if (HapiProperties.isBinaryStorageEnabled()) {
|
||||
BinaryStorageInterceptor binaryStorageInterceptor = appCtx
|
||||
.getBean(BinaryStorageInterceptor.class);
|
||||
getInterceptorService().registerInterceptor(binaryStorageInterceptor);
|
||||
}
|
||||
|
||||
// Validation
|
||||
IValidatorModule validatorModule = appCtx.getBean(IValidatorModule.class);
|
||||
if (validatorModule != null) {
|
||||
if (HapiProperties.getValidateRequestsEnabled()) {
|
||||
RequestValidatingInterceptor interceptor = new RequestValidatingInterceptor();
|
||||
interceptor.setFailOnSeverity(ResultSeverityEnum.ERROR);
|
||||
interceptor.setValidatorModules(Collections.singletonList(validatorModule));
|
||||
registerInterceptor(interceptor);
|
||||
}
|
||||
if (HapiProperties.getValidateResponsesEnabled()) {
|
||||
ResponseValidatingInterceptor interceptor = new ResponseValidatingInterceptor();
|
||||
interceptor.setFailOnSeverity(ResultSeverityEnum.ERROR);
|
||||
interceptor.setValidatorModules(Collections.singletonList(validatorModule));
|
||||
registerInterceptor(interceptor);
|
||||
}
|
||||
}
|
||||
|
||||
// GraphQL
|
||||
if (HapiProperties.getGraphqlEnabled()) {
|
||||
if (fhirVersion.isEqualOrNewerThan(FhirVersionEnum.DSTU3)) {
|
||||
registerProvider(appCtx.getBean(GraphQLProvider.class));
|
||||
}
|
||||
}
|
||||
|
||||
if (!HapiProperties.getAllowedBundleTypes().isEmpty()) {
|
||||
String allowedBundleTypesString = HapiProperties.getAllowedBundleTypes();
|
||||
Set<String> allowedBundleTypes = new HashSet<>();
|
||||
Arrays.stream(allowedBundleTypesString.split(",")).forEach(o -> {
|
||||
BundleType type = BundleType.valueOf(o);
|
||||
allowedBundleTypes.add(type.toCode());
|
||||
});
|
||||
DaoConfig config = daoConfig;
|
||||
config.setBundleTypesAllowedForStorage(
|
||||
Collections.unmodifiableSet(new TreeSet<>(allowedBundleTypes)));
|
||||
}
|
||||
|
||||
// Bulk Export
|
||||
if (HapiProperties.getBulkExportEnabled()) {
|
||||
registerProvider(appCtx.getBean(BulkDataExportProvider.class));
|
||||
}
|
||||
|
||||
if (HapiProperties.getPartitioningEnabled()) {
|
||||
PartitionSettings partitionSettings = appCtx.getBean(PartitionSettings.class);
|
||||
partitionSettings.setPartitioningEnabled(true);
|
||||
PartitionSettings.CrossPartitionReferenceMode mode = PartitionSettings.CrossPartitionReferenceMode.valueOf(HapiProperties.getPartitioningCrossPartitionReferenceMode());
|
||||
partitionSettings.setAllowReferencesAcrossPartitions(mode);
|
||||
partitionSettings.setIncludePartitionInSearchHashes(HapiProperties.getIncludePartitionInSearchHashes());
|
||||
registerProvider(appCtx.getBean(PartitionManagementProvider.class));
|
||||
|
||||
if (HapiProperties.getPartitioningMultitenancyEnabled()) {
|
||||
registerInterceptor(new RequestTenantPartitionInterceptor());
|
||||
setTenantIdentificationStrategy(new UrlBaseTenantIdentificationStrategy());
|
||||
}
|
||||
|
||||
}
|
||||
// Add your own customization here
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@ cors.enabled=true
|
||||
cors.allowCredentials=true
|
||||
# Supports multiple, comma separated allowed origin entries
|
||||
# cors.allowed_origin=http://localhost:8080,https://localhost:8080,https://fhirtest.uhn.ca
|
||||
cors.allow_origin=*
|
||||
cors.allowed_origin=*
|
||||
|
||||
##################################################
|
||||
# Allowed Bundle Types for persistence (defaults are: COLLECTION,DOCUMENT,MESSAGE)
|
||||
@@ -163,3 +163,5 @@ partitioning.cross_partition_reference_mode=NOT_ALLOWED
|
||||
partitioning.partitioning_include_in_search_hashes=true
|
||||
partitioning.multitenancy.enabled=false
|
||||
|
||||
#daoconfig.client_id_strategy=ANY
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ import org.hl7.fhir.r4.model.Parameters;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.nio.file.Paths;
|
||||
|
||||
Reference in New Issue
Block a user