From bea5b0947f40a72f87812bb7bd2e91e92e6d152c Mon Sep 17 00:00:00 2001 From: James Agnew Date: Sun, 30 Jun 2019 13:16:15 -0400 Subject: [PATCH 1/8] Start branch for 4.0.0 release --- pom.xml | 2 +- .../java/ca/uhn/fhir/jpa/starter/HapiProperties.java | 5 +++++ .../ca/uhn/fhir/jpa/starter/JpaRestfulServer.java | 11 +++++++++++ src/main/resources/hapi.properties | 1 + 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0e272c5..c9c0fe2 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ ca.uhn.hapi.fhir hapi-fhir - 3.8.0 + 4.0.0-SNAPSHOT ca.uhn.hapi.fhir.demo diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/HapiProperties.java b/src/main/java/ca/uhn/fhir/jpa/starter/HapiProperties.java index d15c840..3cb3ebb 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/HapiProperties.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/HapiProperties.java @@ -25,6 +25,7 @@ public class HapiProperties { static final String DEFAULT_PRETTY_PRINT = "default_pretty_print"; static final String ETAG_SUPPORT = "etag_support"; static final String FHIR_VERSION = "fhir_version"; + static final String ALLOW_CASCADING_DELETES = "allow_cascading_deletes"; static final String HAPI_PROPERTIES = "hapi.properties"; static final String LOGGER_ERROR_FORMAT = "logger.error_format"; static final String LOGGER_FORMAT = "logger.format"; @@ -239,6 +240,10 @@ public class HapiProperties { return HapiProperties.getBooleanProperty(ALLOW_MULTIPLE_DELETE, false); } + public static Boolean getAllowCascadingDeletes() { + return HapiProperties.getBooleanProperty(ALLOW_CASCADING_DELETES, false); + } + public static Boolean getAllowExternalReferences() { return HapiProperties.getBooleanProperty(ALLOW_EXTERNAL_REFERENCES, false); } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/JpaRestfulServer.java b/src/main/java/ca/uhn/fhir/jpa/starter/JpaRestfulServer.java index 4a8a835..512b184 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/JpaRestfulServer.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/JpaRestfulServer.java @@ -2,9 +2,12 @@ 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.dao.DaoConfig; +import ca.uhn.fhir.jpa.dao.DaoRegistry; import ca.uhn.fhir.jpa.dao.IFhirSystemDao; +import ca.uhn.fhir.jpa.interceptor.CascadingDeleteInterceptor; import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu2; import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2; import ca.uhn.fhir.jpa.provider.SubscriptionTriggeringProvider; @@ -218,6 +221,14 @@ public class JpaRestfulServer extends RestfulServer { 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(daoRegistry, interceptorBroadcaster); + getInterceptorService().registerInterceptor(cascadingDeleteInterceptor); + } + } } diff --git a/src/main/resources/hapi.properties b/src/main/resources/hapi.properties index 99e6a9a..f2e1888 100644 --- a/src/main/resources/hapi.properties +++ b/src/main/resources/hapi.properties @@ -23,6 +23,7 @@ allow_override_default_search_params=true allow_contains_searches=true allow_multiple_delete=true allow_external_references=true +allow_cascading_deletes=true allow_placeholder_references=true expunge_enabled=true persistence_unit_name=HAPI_PU From 3c6343e20da69c34ea0757deb8f8b81c14df3d2c Mon Sep 17 00:00:00 2001 From: James Agnew Date: Thu, 4 Jul 2019 09:48:40 -0400 Subject: [PATCH 2/8] Allow customizing the list of supported resources --- .../ca/uhn/fhir/jpa/starter/HapiProperties.java | 13 +++++++++++++ .../ca/uhn/fhir/jpa/starter/JpaRestfulServer.java | 8 ++++++++ src/main/resources/hapi.properties | 13 +++++++++++++ 3 files changed, 34 insertions(+) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/HapiProperties.java b/src/main/java/ca/uhn/fhir/jpa/starter/HapiProperties.java index 3cb3ebb..a33ad3c 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/HapiProperties.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/HapiProperties.java @@ -8,7 +8,12 @@ import com.google.common.annotations.VisibleForTesting; import java.io.InputStream; import java.io.FileInputStream; +import java.util.Arrays; import java.util.Properties; +import java.util.Set; +import java.util.stream.Collectors; + +import static org.apache.commons.lang3.StringUtils.*; public class HapiProperties { static final String ALLOW_EXTERNAL_REFERENCES = "allow_external_references"; @@ -268,6 +273,14 @@ public class HapiProperties { return HapiProperties.getProperty(CORS_ALLOWED_ORIGIN, "*"); } + public static Set getSupportedResourceTypes() { + String[] types = defaultString(getProperty("supported_resource_types")).split(","); + return Arrays.stream(types) + .map(t->trim(t)) + .filter(t->isNotBlank(t)) + .collect(Collectors.toSet()); + } + public static String getServerName() { return HapiProperties.getProperty(SERVER_NAME, "Local Tester"); } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/JpaRestfulServer.java b/src/main/java/ca/uhn/fhir/jpa/starter/JpaRestfulServer.java index 512b184..2e1b996 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/JpaRestfulServer.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/JpaRestfulServer.java @@ -35,6 +35,7 @@ import org.springframework.web.cors.CorsConfiguration; import javax.servlet.ServletException; import java.util.Arrays; +import java.util.Set; public class JpaRestfulServer extends RestfulServer { @@ -51,6 +52,13 @@ public class JpaRestfulServer extends RestfulServer { */ ApplicationContext appCtx = (ApplicationContext) getServletContext().getAttribute("org.springframework.web.context.WebApplicationContext.ROOT"); + // Customize supported resource types + Set supportedResourceTypes = HapiProperties.getSupportedResourceTypes(); + if (!supportedResourceTypes.isEmpty()) { + DaoRegistry daoRegistry = appCtx.getBean(DaoRegistry.class); + daoRegistry.setSupportedResourceTypes(supportedResourceTypes); + } + /* * ResourceProviders are fetched from the Spring context */ diff --git a/src/main/resources/hapi.properties b/src/main/resources/hapi.properties index f2e1888..537f034 100644 --- a/src/main/resources/hapi.properties +++ b/src/main/resources/hapi.properties @@ -38,6 +38,15 @@ datasource.password= server.name=Local Tester server.id=home test.port= + +# Enable the following property if you want to customize the +# list of resources that is supported by the server (i.e. to +# disable specific resources) +#supported_resource_types=Patient,Observation,Encounter + +################################################### +# Database Settings +################################################### hibernate.dialect=ca.uhn.fhir.jpa.util.DerbyTenSevenHapiFhirDialect hibernate.search.model_mapping=ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory hibernate.format_sql=false @@ -52,6 +61,10 @@ hibernate.search.default.directory_provider=filesystem hibernate.search.default.indexBase=target/lucenefiles hibernate.search.lucene_version=LUCENE_CURRENT tester.config.refuse_to_fetch_third_party_urls=false + +################################################## +# CORS Settings +################################################## cors.enabled=true cors.allowed_origin=* From 76aafe0fa73c77a18dbb531c68f9366d3d3df9f2 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Sun, 14 Jul 2019 12:09:37 -0400 Subject: [PATCH 3/8] Use H2 --- pom.xml | 6 +++--- src/main/resources/hapi.properties | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index c9c0fe2..b875f33 100644 --- a/pom.xml +++ b/pom.xml @@ -138,11 +138,11 @@ - - org.apache.derby - derby + com.h2database + h2 diff --git a/src/main/resources/hapi.properties b/src/main/resources/hapi.properties index 537f034..4499207 100644 --- a/src/main/resources/hapi.properties +++ b/src/main/resources/hapi.properties @@ -31,8 +31,8 @@ logger.name=fhirtest.access logger.format=Path[${servletPath}] Source[${requestHeader.x-forwarded-for}] Operation[${operationType} ${operationName} ${idOrResourceName}] UA[${requestHeader.user-agent}] Params[${requestParameters}] ResponseEncoding[${responseEncodingNoDefault}] logger.error_format=ERROR - ${requestVerb} ${requestUrl} logger.log_exceptions=true -datasource.driver=org.apache.derby.jdbc.EmbeddedDriver -datasource.url=jdbc:derby:directory:target/jpaserver_derby_files;create=true +datasource.driver=org.h2.Driver +datasource.url=jdbc:h2:file:target/jpaserver_derby_files datasource.username= datasource.password= server.name=Local Tester @@ -47,7 +47,7 @@ test.port= ################################################### # Database Settings ################################################### -hibernate.dialect=ca.uhn.fhir.jpa.util.DerbyTenSevenHapiFhirDialect +hibernate.dialect=org.hibernate.dialect.H2Dialect hibernate.search.model_mapping=ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory hibernate.format_sql=false hibernate.show_sql=false From ef49c11e8e419730bd5ed0cbc872b52d990544e7 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Wed, 24 Jul 2019 13:42:12 -0400 Subject: [PATCH 4/8] Add config for binary sotrage --- .../uhn/fhir/jpa/starter/FhirServerConfigCommon.java | 11 ++++++++++- .../java/ca/uhn/fhir/jpa/starter/HapiProperties.java | 5 +++++ .../ca/uhn/fhir/jpa/starter/JpaRestfulServer.java | 7 +++++++ src/main/resources/hapi.properties | 5 +++++ 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java index e44faac..7523a0b 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java @@ -1,15 +1,18 @@ package ca.uhn.fhir.jpa.starter; +import ca.uhn.fhir.jpa.binstore.DatabaseBlobBinaryStorageSvcImpl; +import ca.uhn.fhir.jpa.binstore.IBinaryStorageSvc; import ca.uhn.fhir.jpa.dao.DaoConfig; import ca.uhn.fhir.jpa.model.entity.ModelConfig; import ca.uhn.fhir.jpa.subscription.module.cache.SubscriptionDeliveryHandlerFactory; import ca.uhn.fhir.jpa.subscription.module.subscriber.email.IEmailSender; import ca.uhn.fhir.jpa.subscription.module.subscriber.email.JavaMailEmailSender; import org.apache.commons.dbcp2.BasicDataSource; -import org.hl7.fhir.instance.model.Subscription; +import org.hl7.fhir.dstu2.model.Subscription; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; import org.springframework.transaction.annotation.EnableTransactionManagement; import org.thymeleaf.util.Validate; @@ -149,6 +152,12 @@ public class FhirServerConfigCommon { return retVal; } + @Lazy + @Bean + public IBinaryStorageSvc binaryStorageSvc() { + return new DatabaseBlobBinaryStorageSvcImpl(); + } + @Bean() public IEmailSender emailSender() { if (this.emailEnabled) { diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/HapiProperties.java b/src/main/java/ca/uhn/fhir/jpa/starter/HapiProperties.java index a33ad3c..fe0b2bb 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/HapiProperties.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/HapiProperties.java @@ -51,6 +51,7 @@ public class HapiProperties { static final String ALLOW_CONTAINS_SEARCHES = "allow_contains_searches"; static final String ALLOW_OVERRIDE_DEFAULT_SEARCH_PARAMS = "allow_override_default_search_params"; static final String EMAIL_FROM = "email.from"; + public static final String BINARY_STORAGE_ENABLED = "binary_storage.enabled"; private static Properties properties; @@ -165,6 +166,10 @@ public class HapiProperties { return FhirVersionEnum.DSTU3; } + public static boolean isBinaryStorageEnabled() { + return HapiProperties.getBooleanProperty(BINARY_STORAGE_ENABLED, true); + } + public static ETagSupportEnum getEtagSupport() { String etagSupportString = HapiProperties.getProperty(ETAG_SUPPORT); diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/JpaRestfulServer.java b/src/main/java/ca/uhn/fhir/jpa/starter/JpaRestfulServer.java index 2e1b996..ec211bb 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/JpaRestfulServer.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/JpaRestfulServer.java @@ -4,6 +4,8 @@ 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.binstore.BinaryStorageInterceptor; +import ca.uhn.fhir.jpa.binstore.IBinaryStorageSvc; import ca.uhn.fhir.jpa.dao.DaoConfig; import ca.uhn.fhir.jpa.dao.DaoRegistry; import ca.uhn.fhir.jpa.dao.IFhirSystemDao; @@ -237,6 +239,11 @@ public class JpaRestfulServer extends RestfulServer { getInterceptorService().registerInterceptor(cascadingDeleteInterceptor); } + // Binary Storage + if (HapiProperties.isBinaryStorageEnabled()) { + BinaryStorageInterceptor binaryStorageInterceptor = appCtx.getBean(BinaryStorageInterceptor.class); + getInterceptorService().registerInterceptor(binaryStorageInterceptor); + } } } diff --git a/src/main/resources/hapi.properties b/src/main/resources/hapi.properties index 4499207..8b5a4be 100644 --- a/src/main/resources/hapi.properties +++ b/src/main/resources/hapi.properties @@ -62,6 +62,11 @@ hibernate.search.default.indexBase=target/lucenefiles hibernate.search.lucene_version=LUCENE_CURRENT tester.config.refuse_to_fetch_third_party_urls=false +################################################## +# Binary Storage Operations +################################################## +binary_storage.enabled=true + ################################################## # CORS Settings ################################################## From 7831250d9273d53e7fb876178b8d9e2082d67a4b Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Sun, 11 Aug 2019 18:35:43 -0400 Subject: [PATCH 5/8] Get R5 support working --- pom.xml | 7 + .../fhir/jpa/starter/ApplicationContext.java | 2 + .../fhir/jpa/starter/FhirServerConfigR5.java | 58 +++++++ .../uhn/fhir/jpa/starter/HapiProperties.java | 15 ++ .../fhir/jpa/starter/JpaRestfulServer.java | 59 +++++-- src/main/resources/hapi.properties | 12 ++ .../jpa/starter/ExampleServerDstu2IT.java | 13 +- .../jpa/starter/ExampleServerDstu3IT.java | 44 +++-- .../fhir/jpa/starter/ExampleServerR4IT.java | 18 +- .../fhir/jpa/starter/ExampleServerR5IT.java | 157 ++++++++++++++++++ 10 files changed, 332 insertions(+), 53 deletions(-) create mode 100644 src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java create mode 100644 src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR5IT.java diff --git a/pom.xml b/pom.xml index b875f33..9df45f1 100644 --- a/pom.xml +++ b/pom.xml @@ -198,6 +198,13 @@ provided + + ca.uhn.hapi.fhir + hapi-fhir-test-utilities + ${project.version} + test + + diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/ApplicationContext.java b/src/main/java/ca/uhn/fhir/jpa/starter/ApplicationContext.java index ddddbe0..24b4f79 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/ApplicationContext.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/ApplicationContext.java @@ -13,6 +13,8 @@ public class ApplicationContext extends AnnotationConfigWebApplicationContext { register(FhirServerConfigDstu3.class, FhirServerConfigCommon.class); } else if (fhirVersion == FhirVersionEnum.R4) { register(FhirServerConfigR4.class, FhirServerConfigCommon.class); + } else if (fhirVersion == FhirVersionEnum.R5) { + register(FhirServerConfigR5.class, FhirServerConfigCommon.class); } else { throw new IllegalStateException(); } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java new file mode 100644 index 0000000..e176080 --- /dev/null +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java @@ -0,0 +1,58 @@ +package ca.uhn.fhir.jpa.starter; + +import ca.uhn.fhir.context.ConfigurationException; +import ca.uhn.fhir.jpa.config.BaseJavaConfigR4; +import ca.uhn.fhir.jpa.config.BaseJavaConfigR5; +import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.orm.jpa.JpaTransactionManager; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; + +import javax.persistence.EntityManagerFactory; +import javax.sql.DataSource; + +@Configuration +public class FhirServerConfigR5 extends BaseJavaConfigR5 { + + @Autowired + private DataSource myDataSource; + + /** + * We override the paging provider definition so that we can 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. + */ + @Override + public DatabaseBackedPagingProvider databaseBackedPagingProvider() { + DatabaseBackedPagingProvider pagingProvider = super.databaseBackedPagingProvider(); + pagingProvider.setDefaultPageSize(HapiProperties.getDefaultPageSize()); + pagingProvider.setMaximumPageSize(HapiProperties.getMaximumPageSize()); + return pagingProvider; + } + + @Override + @Bean() + public LocalContainerEntityManagerFactoryBean entityManagerFactory() { + LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory(); + 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(HapiProperties.getProperties()); + return retVal; + } + + @Bean() + public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) { + JpaTransactionManager retVal = new JpaTransactionManager(); + retVal.setEntityManagerFactory(entityManagerFactory); + return retVal; + } + +} diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/HapiProperties.java b/src/main/java/ca/uhn/fhir/jpa/starter/HapiProperties.java index fe0b2bb..3ef8bcb 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/HapiProperties.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/HapiProperties.java @@ -52,6 +52,8 @@ public class HapiProperties { static final String ALLOW_OVERRIDE_DEFAULT_SEARCH_PARAMS = "allow_override_default_search_params"; static final String EMAIL_FROM = "email.from"; public static final String BINARY_STORAGE_ENABLED = "binary_storage.enabled"; + private static final String VALIDATE_REQUESTS_ENABLED = "validation.requests.enabled"; + private static final String VALIDATE_RESPONSES_ENABLED = "validation.responses.enabled"; private static Properties properties; @@ -146,6 +148,10 @@ public class HapiProperties { return Boolean.parseBoolean(value); } + private static boolean getBooleanProperty(String propertyName, boolean defaultValue) { + return getBooleanProperty(propertyName, Boolean.valueOf(defaultValue)); + } + private static Integer getIntegerProperty(String propertyName, Integer defaultValue) { String value = HapiProperties.getProperty(propertyName); @@ -346,4 +352,13 @@ public class HapiProperties { String value = HapiProperties.getProperty(REUSE_CACHED_SEARCH_RESULTS_MILLIS, "-1"); return Long.valueOf(value); } + + public static boolean getValidateRequestsEnabled() { + return HapiProperties.getBooleanProperty(VALIDATE_REQUESTS_ENABLED, false); + } + + public static boolean getValidateResponsesEnabled() { + return HapiProperties.getBooleanProperty(VALIDATE_REQUESTS_ENABLED, false); + } + } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/JpaRestfulServer.java b/src/main/java/ca/uhn/fhir/jpa/starter/JpaRestfulServer.java index ec211bb..c8fa268 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/JpaRestfulServer.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/JpaRestfulServer.java @@ -5,7 +5,6 @@ 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.binstore.BinaryStorageInterceptor; -import ca.uhn.fhir.jpa.binstore.IBinaryStorageSvc; import ca.uhn.fhir.jpa.dao.DaoConfig; import ca.uhn.fhir.jpa.dao.DaoRegistry; import ca.uhn.fhir.jpa.dao.IFhirSystemDao; @@ -13,12 +12,13 @@ import ca.uhn.fhir.jpa.interceptor.CascadingDeleteInterceptor; 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.dstu3.TerminologyUploaderProviderDstu3; import ca.uhn.fhir.jpa.provider.r4.JpaConformanceProviderR4; import ca.uhn.fhir.jpa.provider.r4.JpaSystemProviderR4; -import ca.uhn.fhir.jpa.provider.r4.TerminologyUploaderProviderR4; +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.subscription.SubscriptionInterceptorLoader; import ca.uhn.fhir.jpa.subscription.module.interceptor.SubscriptionDebugLogInterceptor; @@ -27,9 +27,9 @@ 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.LoggingInterceptor; -import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor; +import ca.uhn.fhir.rest.server.interceptor.*; +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.springframework.context.ApplicationContext; @@ -37,6 +37,7 @@ import org.springframework.web.cors.CorsConfiguration; import javax.servlet.ServletException; import java.util.Arrays; +import java.util.Collections; import java.util.Set; public class JpaRestfulServer extends RestfulServer { @@ -76,6 +77,9 @@ public class JpaRestfulServer extends RestfulServer { } 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(); } @@ -108,6 +112,11 @@ public class JpaRestfulServer extends RestfulServer { JpaConformanceProviderR4 confProvider = new JpaConformanceProviderR4(this, systemDao, appCtx.getBean(DaoConfig.class)); confProvider.setImplementationDescription("HAPI FHIR R4 Server"); setServerConformanceProvider(confProvider); + } else if (fhirVersion == FhirVersionEnum.R5) { + IFhirSystemDao systemDao = appCtx.getBean("mySystemDaoR5", IFhirSystemDao.class); + JpaConformanceProviderR5 confProvider = new JpaConformanceProviderR5(this, systemDao, appCtx.getBean(DaoConfig.class)); + confProvider.setImplementationDescription("HAPI FHIR R5 Server"); + setServerConformanceProvider(confProvider); } else { throw new IllegalStateException(); } @@ -179,11 +188,7 @@ public class JpaRestfulServer extends RestfulServer { * with this feature. */ if (false) { // <-- DISABLED RIGHT NOW - if (fhirVersion == FhirVersionEnum.DSTU3) { - registerProvider(appCtx.getBean(TerminologyUploaderProviderDstu3.class)); - } else if (fhirVersion == FhirVersionEnum.R4) { - registerProvider(appCtx.getBean(TerminologyUploaderProviderR4.class)); - } + registerProvider(appCtx.getBean(TerminologyUploaderProvider.class)); } // If you want to enable the $trigger-subscription operation to allow @@ -244,6 +249,38 @@ public class JpaRestfulServer extends RestfulServer { BinaryStorageInterceptor binaryStorageInterceptor = appCtx.getBean(BinaryStorageInterceptor.class); getInterceptorService().registerInterceptor(binaryStorageInterceptor); } + + // Validation + IValidatorModule validatorModule; + switch (fhirVersion) { + case DSTU3: + validatorModule = appCtx.getBean("myInstanceValidatorDstu3", IValidatorModule.class); + break; + case R4: + validatorModule = appCtx.getBean("myInstanceValidatorR4", IValidatorModule.class); + break; + case R5: + validatorModule = appCtx.getBean("myInstanceValidatorR5", IValidatorModule.class); + break; + default: + validatorModule = null; + break; + } + 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); + } + } + } } diff --git a/src/main/resources/hapi.properties b/src/main/resources/hapi.properties index 8b5a4be..6b23b23 100644 --- a/src/main/resources/hapi.properties +++ b/src/main/resources/hapi.properties @@ -39,6 +39,18 @@ server.name=Local Tester server.id=home test.port= +################################################### +# Validation +################################################### +# Should all incoming requests be validated +validation.requests.enabled=false +# Should outgoing responses be validated +validation.responses.enabled=false + + +################################################### +# Supported Resources +################################################### # Enable the following property if you want to customize the # list of resources that is supported by the server (i.e. to # disable specific resources) diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu2IT.java b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu2IT.java index 70b4e5f..ee6ebf8 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu2IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu2IT.java @@ -5,7 +5,7 @@ import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; -import ca.uhn.fhir.util.PortUtil; +import ca.uhn.fhir.test.utilities.JettyUtil; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.webapp.WebAppContext; import org.hl7.fhir.instance.model.api.IIdType; @@ -23,16 +23,13 @@ public class ExampleServerDstu2IT { private static IGenericClient ourClient; private static FhirContext ourCtx; private static int ourPort; - private static Server ourServer; - private static String ourServerBase; static { HapiProperties.forceReload(); HapiProperties.setProperty(HapiProperties.FHIR_VERSION, "DSTU2"); - HapiProperties.setProperty(HapiProperties.DATASOURCE_URL, "jdbc:derby:memory:dbr2;create=true"); + HapiProperties.setProperty(HapiProperties.DATASOURCE_URL, "jdbc:h2:mem:dbr2"); ourCtx = FhirContext.forDstu2(); - ourPort = PortUtil.findFreePort(); } @Test @@ -59,7 +56,7 @@ public class ExampleServerDstu2IT { ourLog.info("Project base path is: {}", path); - ourServer = new Server(ourPort); + ourServer = new Server(0); WebAppContext webAppContext = new WebAppContext(); webAppContext.setContextPath("/hapi-fhir-jpaserver"); @@ -70,9 +67,11 @@ public class ExampleServerDstu2IT { ourServer.setHandler(webAppContext); ourServer.start(); + ourPort = JettyUtil.getPortForStartedServer(ourServer); + ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER); ourCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000); - ourServerBase = "http://localhost:" + ourPort + "/hapi-fhir-jpaserver/fhir/"; + String ourServerBase = "http://localhost:" + ourPort + "/hapi-fhir-jpaserver/fhir/"; ourClient = ourCtx.newRestfulGenericClient(ourServerBase); ourClient.registerInterceptor(new LoggingInterceptor(true)); } diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu3IT.java b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu3IT.java index 4580f9c..7507bfb 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu3IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu3IT.java @@ -1,18 +1,13 @@ package ca.uhn.fhir.jpa.starter; -import static ca.uhn.fhir.util.TestUtil.waitForSize; -import static org.junit.Assert.assertEquals; - -import java.io.IOException; -import java.net.URI; -import java.nio.file.Paths; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; - +import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.api.CacheControlDirective; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; -import ca.uhn.fhir.util.PortUtil; +import ca.uhn.fhir.rest.client.api.IGenericClient; +import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; +import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; +import ca.uhn.fhir.test.utilities.JettyUtil; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.websocket.api.Session; @@ -23,12 +18,17 @@ import org.hl7.fhir.dstu3.model.Observation; import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.dstu3.model.Subscription; import org.hl7.fhir.instance.model.api.IIdType; -import org.junit.*; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.rest.client.api.IGenericClient; -import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; -import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; +import java.net.URI; +import java.nio.file.Paths; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import static ca.uhn.fhir.util.TestUtil.waitForSize; +import static org.junit.Assert.assertEquals; public class ExampleServerDstu3IT { @@ -36,17 +36,14 @@ public class ExampleServerDstu3IT { private static IGenericClient ourClient; private static FhirContext ourCtx; private static int ourPort; - private static Server ourServer; - private static String ourServerBase; static { HapiProperties.forceReload(); HapiProperties.setProperty(HapiProperties.FHIR_VERSION, "DSTU3"); - HapiProperties.setProperty(HapiProperties.DATASOURCE_URL, "jdbc:derby:memory:dbr3;create=true"); + HapiProperties.setProperty(HapiProperties.DATASOURCE_URL, "jdbc:h2:mem:dbr3"); HapiProperties.setProperty(HapiProperties.SUBSCRIPTION_WEBSOCKET_ENABLED, "true"); ourCtx = FhirContext.forDstu3(); - ourPort = PortUtil.findFreePort(); } @Test @@ -131,10 +128,7 @@ public class ExampleServerDstu3IT { ourLog.info("Project base path is: {}", path); - if (ourPort == 0) { - ourPort = RandomServerPortProvider.findFreePort(); - } - ourServer = new Server(ourPort); + ourServer = new Server(0); WebAppContext webAppContext = new WebAppContext(); webAppContext.setContextPath("/hapi-fhir-jpaserver"); @@ -145,9 +139,11 @@ public class ExampleServerDstu3IT { ourServer.setHandler(webAppContext); ourServer.start(); + ourPort = JettyUtil.getPortForStartedServer(ourServer); + ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER); ourCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000); - ourServerBase = "http://localhost:" + ourPort + "/hapi-fhir-jpaserver/fhir/"; + String ourServerBase = "http://localhost:" + ourPort + "/hapi-fhir-jpaserver/fhir/"; ourClient = ourCtx.newRestfulGenericClient(ourServerBase); ourClient.registerInterceptor(new LoggingInterceptor(true)); } diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java index bff12bb..1eedafd 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java @@ -7,7 +7,7 @@ import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; -import ca.uhn.fhir.util.PortUtil; +import ca.uhn.fhir.test.utilities.JettyUtil; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.websocket.api.Session; @@ -36,22 +36,19 @@ public class ExampleServerR4IT { private static IGenericClient ourClient; private static FhirContext ourCtx; private static int ourPort; - private static Server ourServer; - private static String ourServerBase; static { HapiProperties.forceReload(); - HapiProperties.setProperty(HapiProperties.DATASOURCE_URL, "jdbc:derby:memory:dbr4;create=true"); + HapiProperties.setProperty(HapiProperties.DATASOURCE_URL, "jdbc:h2:mem:dbr4"); HapiProperties.setProperty(HapiProperties.FHIR_VERSION, "R4"); HapiProperties.setProperty(HapiProperties.SUBSCRIPTION_WEBSOCKET_ENABLED, "true"); ourCtx = FhirContext.forR4(); - ourPort = PortUtil.findFreePort(); } @Test public void testCreateAndRead() { - ourLog.info("Base URL is: " + HapiProperties.getServerAddress()); + ourLog.info("Base URL is: " + HapiProperties.getServerAddress()); String methodName = "testCreateResourceConditional"; Patient pt = new Patient(); @@ -131,10 +128,7 @@ public class ExampleServerR4IT { ourLog.info("Project base path is: {}", path); - if (ourPort == 0) { - ourPort = RandomServerPortProvider.findFreePort(); - } - ourServer = new Server(ourPort); + ourServer = new Server(0); WebAppContext webAppContext = new WebAppContext(); webAppContext.setContextPath("/hapi-fhir-jpaserver"); @@ -146,9 +140,11 @@ public class ExampleServerR4IT { ourServer.setHandler(webAppContext); ourServer.start(); + ourPort = JettyUtil.getPortForStartedServer(ourServer); + ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER); ourCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000); - ourServerBase = HapiProperties.getServerAddress(); + String ourServerBase = HapiProperties.getServerAddress(); ourServerBase = "http://localhost:" + ourPort + "/hapi-fhir-jpaserver/fhir/"; ourClient = ourCtx.newRestfulGenericClient(ourServerBase); diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR5IT.java b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR5IT.java new file mode 100644 index 0000000..04aee05 --- /dev/null +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR5IT.java @@ -0,0 +1,157 @@ +package ca.uhn.fhir.jpa.starter; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.rest.api.CacheControlDirective; +import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.rest.client.api.IGenericClient; +import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; +import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; +import ca.uhn.fhir.test.utilities.JettyUtil; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.webapp.WebAppContext; +import org.eclipse.jetty.websocket.api.Session; +import org.eclipse.jetty.websocket.client.ClientUpgradeRequest; +import org.eclipse.jetty.websocket.client.WebSocketClient; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.Observation; +import org.hl7.fhir.r4.model.Patient; +import org.hl7.fhir.r4.model.Subscription; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.net.URI; +import java.nio.file.Paths; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import static ca.uhn.fhir.util.TestUtil.waitForSize; +import static org.junit.Assert.assertEquals; + +public class ExampleServerR5IT { + + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExampleServerR5IT.class); + private static IGenericClient ourClient; + private static FhirContext ourCtx; + private static int ourPort; + private static Server ourServer; + + static { + HapiProperties.forceReload(); + HapiProperties.setProperty(HapiProperties.DATASOURCE_URL, "jdbc:h2:mem:dbr5"); + HapiProperties.setProperty(HapiProperties.FHIR_VERSION, "R5"); + HapiProperties.setProperty(HapiProperties.SUBSCRIPTION_WEBSOCKET_ENABLED, "true"); + ourCtx = FhirContext.forR4(); + } + + @Test + public void testCreateAndRead() { + ourLog.info("Base URL is: " + HapiProperties.getServerAddress()); + String methodName = "testCreateResourceConditional"; + + Patient pt = new Patient(); + pt.addName().setFamily(methodName); + IIdType id = ourClient.create().resource(pt).execute().getId(); + + Patient pt2 = ourClient.read().resource(Patient.class).withId(id).execute(); + assertEquals(methodName, pt2.getName().get(0).getFamily()); + } + + @Test + public void testWebsocketSubscription() throws Exception { + /* + * Create subscription + */ + Subscription subscription = new Subscription(); + subscription.setReason("Monitor new neonatal function (note, age will be determined by the monitor)"); + subscription.setStatus(Subscription.SubscriptionStatus.REQUESTED); + subscription.setCriteria("Observation?status=final"); + + Subscription.SubscriptionChannelComponent channel = new Subscription.SubscriptionChannelComponent(); + channel.setType(Subscription.SubscriptionChannelType.WEBSOCKET); + channel.setPayload("application/json"); + subscription.setChannel(channel); + + MethodOutcome methodOutcome = ourClient.create().resource(subscription).execute(); + IIdType mySubscriptionId = methodOutcome.getId(); + + // Wait for the subscription to be activated + waitForSize(1, () -> ourClient.search().forResource(Subscription.class).where(Subscription.STATUS.exactly().code("active")).cacheControl(new CacheControlDirective().setNoCache(true)).returnBundle(Bundle.class).execute().getEntry().size()); + + /* + * Attach websocket + */ + + WebSocketClient myWebSocketClient = new WebSocketClient(); + SocketImplementation mySocketImplementation = new SocketImplementation(mySubscriptionId.getIdPart(), EncodingEnum.JSON); + + myWebSocketClient.start(); + URI echoUri = new URI("ws://localhost:" + ourPort + "/hapi-fhir-jpaserver/websocket"); + ClientUpgradeRequest request = new ClientUpgradeRequest(); + ourLog.info("Connecting to : {}", echoUri); + Future connection = myWebSocketClient.connect(mySocketImplementation, echoUri, request); + Session session = connection.get(2, TimeUnit.SECONDS); + + ourLog.info("Connected to WS: {}", session.isOpen()); + + /* + * Create a matching resource + */ + Observation obs = new Observation(); + obs.setStatus(Observation.ObservationStatus.FINAL); + ourClient.create().resource(obs).execute(); + + // Give some time for the subscription to deliver + Thread.sleep(2000); + + /* + * Ensure that we receive a ping on the websocket + */ + waitForSize(1, () -> mySocketImplementation.myPingCount); + + /* + * Clean up + */ + ourClient.delete().resourceById(mySubscriptionId).execute(); + } + + @AfterClass + public static void afterClass() throws Exception { + ourServer.stop(); + } + + @BeforeClass + public static void beforeClass() throws Exception { + String path = Paths.get("").toAbsolutePath().toString(); + + ourLog.info("Project base path is: {}", path); + + ourServer = new Server(0); + + WebAppContext webAppContext = new WebAppContext(); + webAppContext.setContextPath("/hapi-fhir-jpaserver"); + webAppContext.setDisplayName("HAPI FHIR"); + webAppContext.setDescriptor(path + "/src/main/webapp/WEB-INF/web.xml"); + webAppContext.setResourceBase(path + "/target/hapi-fhir-jpaserver-starter"); + webAppContext.setParentLoaderPriority(true); + + ourServer.setHandler(webAppContext); + ourServer.start(); + + ourPort = JettyUtil.getPortForStartedServer(ourServer); + + ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER); + ourCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000); + String ourServerBase = "http://localhost:" + ourPort + "/hapi-fhir-jpaserver/fhir/"; + + ourClient = ourCtx.newRestfulGenericClient(ourServerBase); + ourClient.registerInterceptor(new LoggingInterceptor(true)); + } + + public static void main(String[] theArgs) throws Exception { + ourPort = 8080; + beforeClass(); + } +} From 447c2531c508e620e4c0fccfeebe8442a8c8c8a1 Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Sun, 11 Aug 2019 18:42:19 -0400 Subject: [PATCH 6/8] Travis fix --- .travis.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 7cd8c41..54d387b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,4 +29,7 @@ before_script: - sudo chown -R travis:travis "$HOME/.m2/repository"; script: - - mvn install + - mvn -U install + + + From 657eaf77583ac4645f3aae0d3f11755c9f6ea8eb Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Tue, 13 Aug 2019 08:18:02 -0400 Subject: [PATCH 7/8] Add graphql and _filter support --- .../jpa/starter/FhirServerConfigCommon.java | 2 ++ .../uhn/fhir/jpa/starter/HapiProperties.java | 34 ++++++++++++------- .../fhir/jpa/starter/JpaRestfulServer.java | 13 ++++--- src/main/resources/hapi.properties | 5 +++ 4 files changed, 37 insertions(+), 17 deletions(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java index 7523a0b..a396ee4 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java @@ -111,6 +111,8 @@ public class FhirServerConfigCommon { retVal.addSupportedSubscriptionType(Subscription.SubscriptionChannelType.WEBSOCKET); } + retVal.setFilterParameterEnabled(HapiProperties.getFilterSearchEnabled()); + return retVal; } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/HapiProperties.java b/src/main/java/ca/uhn/fhir/jpa/starter/HapiProperties.java index 3ef8bcb..793edbd 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/HapiProperties.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/HapiProperties.java @@ -6,8 +6,8 @@ import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.server.ETagSupportEnum; import com.google.common.annotations.VisibleForTesting; -import java.io.InputStream; import java.io.FileInputStream; +import java.io.InputStream; import java.util.Arrays; import java.util.Properties; import java.util.Set; @@ -16,6 +16,7 @@ import java.util.stream.Collectors; import static org.apache.commons.lang3.StringUtils.*; public class HapiProperties { + public static final String BINARY_STORAGE_ENABLED = "binary_storage.enabled"; static final String ALLOW_EXTERNAL_REFERENCES = "allow_external_references"; static final String ALLOW_MULTIPLE_DELETE = "allow_multiple_delete"; static final String ALLOW_PLACEHOLDER_REFERENCES = "allow_placeholder_references"; @@ -51,10 +52,10 @@ public class HapiProperties { static final String ALLOW_CONTAINS_SEARCHES = "allow_contains_searches"; static final String ALLOW_OVERRIDE_DEFAULT_SEARCH_PARAMS = "allow_override_default_search_params"; static final String EMAIL_FROM = "email.from"; - public static final String BINARY_STORAGE_ENABLED = "binary_storage.enabled"; private static final String VALIDATE_REQUESTS_ENABLED = "validation.requests.enabled"; private static final String VALIDATE_RESPONSES_ENABLED = "validation.responses.enabled"; - + private static final String FILTER_SEARCH_ENABLED = "filter_search.enabled"; + private static final String GRAPHQL_ENABLED = "graphql.enabled"; private static Properties properties; /* @@ -77,7 +78,7 @@ public class HapiProperties { public static Properties getProperties() { if (properties == null) { // Load the configurable properties file - try (InputStream in = HapiProperties.class.getClassLoader().getResourceAsStream(HAPI_PROPERTIES)){ + try (InputStream in = HapiProperties.class.getClassLoader().getResourceAsStream(HAPI_PROPERTIES)) { HapiProperties.properties = new Properties(); HapiProperties.properties.load(in); } catch (Exception e) { @@ -85,8 +86,8 @@ public class HapiProperties { } Properties overrideProps = loadOverrideProperties(); - if(overrideProps != null) { - properties.putAll(overrideProps); + if (overrideProps != null) { + properties.putAll(overrideProps); } } @@ -96,17 +97,17 @@ public class HapiProperties { /** * If a configuration file path is explicitly specified via -Dhapi.properties=, the properties there will * be used to override the entries in the default hapi.properties file (currently under WEB-INF/classes) + * * @return properties loaded from the explicitly specified configuraiton file if there is one, or null otherwise. */ private static Properties loadOverrideProperties() { String confFile = System.getProperty(HAPI_PROPERTIES); - if(confFile != null) { + if (confFile != null) { try { Properties props = new Properties(); props.load(new FileInputStream(confFile)); return props; - } - catch (Exception e) { + } catch (Exception e) { throw new ConfigurationException("Could not load HAPI properties file: " + confFile, e); } } @@ -287,8 +288,8 @@ public class HapiProperties { public static Set getSupportedResourceTypes() { String[] types = defaultString(getProperty("supported_resource_types")).split(","); return Arrays.stream(types) - .map(t->trim(t)) - .filter(t->isNotBlank(t)) + .map(t -> trim(t)) + .filter(t -> isNotBlank(t)) .collect(Collectors.toSet()); } @@ -358,7 +359,16 @@ public class HapiProperties { } public static boolean getValidateResponsesEnabled() { - return HapiProperties.getBooleanProperty(VALIDATE_REQUESTS_ENABLED, false); + return HapiProperties.getBooleanProperty(VALIDATE_RESPONSES_ENABLED, false); + } + + public static boolean getFilterSearchEnabled() { + return HapiProperties.getBooleanProperty(FILTER_SEARCH_ENABLED, true); + } + + public static boolean getGraphqlEnabled() { + return HapiProperties.getBooleanProperty(GRAPHQL_ENABLED, true); } } + diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/JpaRestfulServer.java b/src/main/java/ca/uhn/fhir/jpa/starter/JpaRestfulServer.java index c8fa268..9b6a62c 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/JpaRestfulServer.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/JpaRestfulServer.java @@ -9,10 +9,7 @@ import ca.uhn.fhir.jpa.dao.DaoConfig; import ca.uhn.fhir.jpa.dao.DaoRegistry; import ca.uhn.fhir.jpa.dao.IFhirSystemDao; import ca.uhn.fhir.jpa.interceptor.CascadingDeleteInterceptor; -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.*; import ca.uhn.fhir.jpa.provider.dstu3.JpaConformanceProviderDstu3; import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3; import ca.uhn.fhir.jpa.provider.r4.JpaConformanceProviderR4; @@ -54,7 +51,6 @@ public class JpaRestfulServer extends RestfulServer { * specified in the properties file. */ ApplicationContext appCtx = (ApplicationContext) getServletContext().getAttribute("org.springframework.web.context.WebApplicationContext.ROOT"); - // Customize supported resource types Set supportedResourceTypes = HapiProperties.getSupportedResourceTypes(); if (!supportedResourceTypes.isEmpty()) { @@ -281,6 +277,13 @@ public class JpaRestfulServer extends RestfulServer { } } + // GraphQL + if (HapiProperties.getGraphqlEnabled()) { + if (fhirVersion.isEqualOrNewerThan(FhirVersionEnum.DSTU3)) { + registerProvider(appCtx.getBean(GraphQLProvider.class)); + } + } + } } diff --git a/src/main/resources/hapi.properties b/src/main/resources/hapi.properties index 6b23b23..2cc874a 100644 --- a/src/main/resources/hapi.properties +++ b/src/main/resources/hapi.properties @@ -47,6 +47,11 @@ validation.requests.enabled=false # Should outgoing responses be validated validation.responses.enabled=false +################################################### +# Search Features +################################################### +filter_search.enabled=true +graphql.enabled=true ################################################### # Supported Resources From 93a956f4a093e601611f9df8fc60ea872ea3098c Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Tue, 13 Aug 2019 13:52:09 -0400 Subject: [PATCH 8/8] Fix #47 - Correct H2 database url --- src/main/resources/hapi.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/hapi.properties b/src/main/resources/hapi.properties index 2cc874a..edf26f6 100644 --- a/src/main/resources/hapi.properties +++ b/src/main/resources/hapi.properties @@ -32,7 +32,7 @@ logger.format=Path[${servletPath}] Source[${requestHeader.x-forwarded-for}] Oper logger.error_format=ERROR - ${requestVerb} ${requestUrl} logger.log_exceptions=true datasource.driver=org.h2.Driver -datasource.url=jdbc:h2:file:target/jpaserver_derby_files +datasource.url=jdbc:h2:file:./target/database/h2 datasource.username= datasource.password= server.name=Local Tester