From 395a87ee84ba22e541b60aafd1496f2a96796274 Mon Sep 17 00:00:00 2001 From: Tadgh Date: Fri, 27 Aug 2021 13:34:47 -0400 Subject: [PATCH 001/200] Bump dep version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 80dc78f3e30..79d8fe73d74 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.5.0 + 5.6.0-PRE2-SNAPSHOT hapi-fhir-jpaserver-starter From 5038b5bd2c746a2d9c3407ade42a968cd191c743 Mon Sep 17 00:00:00 2001 From: Tadgh Date: Fri, 27 Aug 2021 13:50:42 -0400 Subject: [PATCH 002/200] Remove javamail and replace with simple-java-mail --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 79d8fe73d74..bee8ba77b6a 100644 --- a/pom.xml +++ b/pom.xml @@ -67,12 +67,12 @@ - com.sun.mail - javax.mail + org.simplejavamail + simple-java-mail - javax.activation - activation + jakarta.annotation + jakarta.annotation-api From 6e876c15029334c615514721a0968fbad198cc1b Mon Sep 17 00:00:00 2001 From: Tadgh Date: Fri, 27 Aug 2021 16:29:06 -0400 Subject: [PATCH 003/200] WIP --- pom.xml | 6 +++ .../uhn/fhir/jpa/starter/AppProperties.java | 19 +++++++++ .../jpa/starter/BaseJpaRestfulServer.java | 5 +++ .../jpa/starter/FhirServerConfigCommon.java | 39 ++++++++++--------- src/main/resources/application.yaml | 4 ++ 5 files changed, 55 insertions(+), 18 deletions(-) diff --git a/pom.xml b/pom.xml index bee8ba77b6a..1e434d68d39 100644 --- a/pom.xml +++ b/pom.xml @@ -83,6 +83,12 @@ hapi-fhir-base ${project.version} + + + ca.uhn.hapi.fhir + hapi-fhir-jpaserver-subscription + ${project.version} + diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java b/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java index 4c69373a25f..98113b2a425 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java @@ -74,6 +74,9 @@ public class AppProperties { private Boolean use_apache_address_strategy = false; private Boolean use_apache_address_strategy_https = false; + private Integer bundle_batch_pool_size = 20; + private Integer bundle_batch_pool_max_size = 100; + public Boolean getUse_apache_address_strategy() { return use_apache_address_strategy; } @@ -471,6 +474,22 @@ public void setInstall_transitive_ig_dependencies(boolean install_transitive_ig_ this.install_transitive_ig_dependencies = install_transitive_ig_dependencies; } + public Integer getBundle_batch_pool_size() { + return this.bundle_batch_pool_size; + } + + public void setBundle_batch_pool_size(Integer bundle_batch_pool_size) { + this.bundle_batch_pool_size = bundle_batch_pool_size; + } + + public Integer getBundle_batch_pool_max_size() { + return bundle_batch_pool_max_size; + } + + public void setBundle_batch_pool_max_size(Integer bundle_batch_pool_max_size) { + this.bundle_batch_pool_max_size = bundle_batch_pool_max_size; + } + public static class Cors { private Boolean allow_Credentials = true; private List allowed_origin = ImmutableList.of("*"); diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java index dc16873fff1..2ff07f16787 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java @@ -373,6 +373,11 @@ protected void initialize() throws ServletException { 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()); + System.out.println("BATCH SIZE IS " + appProperties.getBundle_batch_pool_size()); + System.out.println("BATCH MAX SIZE IS " + appProperties.getBundle_batch_pool_max_size()); if (appProperties.getImplementationGuides() != null) { Map guides = appProperties.getImplementationGuides(); 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 8777e0ab1f7..74d46bc8709 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java @@ -8,10 +8,16 @@ import ca.uhn.fhir.jpa.model.config.PartitionSettings.CrossPartitionReferenceMode; import ca.uhn.fhir.jpa.model.entity.ModelConfig; 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.jpa.subscription.match.deliver.email.JavaMailEmailSender; +import ca.uhn.fhir.rest.server.mail.MailConfig; import com.google.common.base.Strings; +import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.dstu2.model.Subscription; +import org.simplejavamail.api.email.Email; +import org.simplejavamail.api.mailer.Mailer; +import org.simplejavamail.api.mailer.config.TransportStrategy; +import org.simplejavamail.mailer.MailerBuilder; import org.springframework.boot.env.YamlPropertySourceLoader; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -205,23 +211,20 @@ public IBinaryStorageSvc binaryStorageSvc(AppProperties appProperties) { @Bean() public IEmailSender emailSender(AppProperties appProperties, Optional subscriptionDeliveryHandlerFactory) { if (appProperties.getSubscription() != null && appProperties.getSubscription().getEmail() != null) { - JavaMailEmailSender retVal = new JavaMailEmailSender(); - - AppProperties.Subscription.Email email = appProperties.getSubscription().getEmail(); - retVal.setSmtpServerHostname(email.getHost()); - retVal.setSmtpServerPort(email.getPort()); - retVal.setSmtpServerUsername(email.getUsername()); - retVal.setSmtpServerPassword(email.getPassword()); - retVal.setAuth(email.getAuth()); - retVal.setStartTlsEnable(email.getStartTlsEnable()); - retVal.setStartTlsRequired(email.getStartTlsRequired()); - retVal.setQuitWait(email.getQuitWait()); - - if(subscriptionDeliveryHandlerFactory.isPresent()) - subscriptionDeliveryHandlerFactory.get().setEmailSender(retVal); - - return retVal; - } + AppProperties.Subscription.Email email = appProperties.getSubscription().getEmail(); + MailConfig mailConfig = new MailConfig(); + mailConfig.setSmtpHostname(email.getHost()); + mailConfig.setSmtpUseStartTLS(email.getStartTlsRequired()); + mailConfig.setSmtpPort(email.getPort()); + mailConfig.setSmtpUsername(email.getUsername()); + mailConfig.setSmtpPassword(email.getPassword()); + EmailSenderImpl emailSender = new EmailSenderImpl(mailConfig); + + if(subscriptionDeliveryHandlerFactory.isPresent()) { + subscriptionDeliveryHandlerFactory.get().setEmailSender(emailSender); + } + + } return null; } diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 7be442375ad..02cbd8027af 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -99,6 +99,10 @@ hapi: search-coord-max-pool-size: 100 search-coord-queue-capacity: 200 + # Threadpool size for BATCH'ed GETs in a bundle. +# bundle_batch_pool_size: 10 +# bundle_batch_pool_max_size: 50 + # logger: # error_format: 'ERROR - ${requestVerb} ${requestUrl}' # format: >- From a8a98c311cadb9793c0ec423ccf7b6b2aafeae4e Mon Sep 17 00:00:00 2001 From: Tadgh Date: Sat, 28 Aug 2021 12:52:56 -0400 Subject: [PATCH 004/200] Remove print --- src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java index 2ff07f16787..3d287c09ff5 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java @@ -376,8 +376,6 @@ protected void initialize() throws ServletException { //Parallel Batch GET execution settings daoConfig.setBundleBatchPoolSize(appProperties.getBundle_batch_pool_size()); daoConfig.setBundleBatchPoolSize(appProperties.getBundle_batch_pool_max_size()); - System.out.println("BATCH SIZE IS " + appProperties.getBundle_batch_pool_size()); - System.out.println("BATCH MAX SIZE IS " + appProperties.getBundle_batch_pool_max_size()); if (appProperties.getImplementationGuides() != null) { Map guides = appProperties.getImplementationGuides(); From e26364670d4d497e93a0baff74824710508d3841 Mon Sep 17 00:00:00 2001 From: Tadgh Date: Sat, 28 Aug 2021 18:52:41 -0400 Subject: [PATCH 005/200] Remove bean override --- src/main/java/ca/uhn/fhir/jpa/starter/Application.java | 8 -------- src/main/resources/application.yaml | 3 --- .../ca/uhn/fhir/jpa/starter/ExampleServerDstu2IT.java | 1 - .../ca/uhn/fhir/jpa/starter/ExampleServerDstu3IT.java | 1 - .../java/ca/uhn/fhir/jpa/starter/ExampleServerR5IT.java | 1 - .../ca/uhn/fhir/jpa/starter/MultitenantServerR4IT.java | 1 - 6 files changed, 15 deletions(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/Application.java b/src/main/java/ca/uhn/fhir/jpa/starter/Application.java index e823d5f3ddb..383d03a9aa0 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/Application.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/Application.java @@ -29,14 +29,6 @@ public class Application extends SpringBootServletInitializer { public static void main(String[] args) { - /* - * https://github.com/hapifhir/hapi-fhir-jpaserver-starter/issues/246 - * This will be allowed for a short period until we know how MDM should be configured - * or don't have multiple equal bean instantiations. - * - * This will require changes in the main project as stated in the Github comment - * */ - System.setProperty("spring.main.allow-bean-definition-overriding","true"); System.setProperty("spring.batch.job.enabled", "false"); SpringApplication.run(Application.class, args); diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 02cbd8027af..8861c3e7301 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -31,9 +31,6 @@ spring: batch: job: enabled: false - main: -# TODO 5.6.0 -> Prevent duplicate bean definitions in the Spring batch config in HAPI: see: - allow-bean-definition-overriding: true hapi: fhir: ### This is the FHIR version. Choose between, DSTU2, DSTU3, R4 or R5 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 0b408deb2c7..964fae24a6f 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu2IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu2IT.java @@ -21,7 +21,6 @@ "spring.batch.job.enabled=false", "hapi.fhir.fhir_version=dstu2", "spring.datasource.url=jdbc:h2:mem:dbr2", - "spring.main.allow-bean-definition-overriding=true" }) public class ExampleServerDstu2IT { 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 91cb4baeb30..b4ca6c3cec3 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu3IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu3IT.java @@ -46,7 +46,6 @@ "hapi.fhir.subscription.websocket_enabled=true", "hapi.fhir.allow_external_references=true", "hapi.fhir.allow_placeholder_references=true", - "spring.main.allow-bean-definition-overriding=true" }) diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR5IT.java b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR5IT.java index 6a7fc52df23..055d1ab25d7 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR5IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR5IT.java @@ -39,7 +39,6 @@ "hapi.fhir.fhir_version=r5", "hapi.fhir.subscription.websocket_enabled=true", "hapi.fhir.subscription.websocket_enabled=true", - "spring.main.allow-bean-definition-overriding=true" }) public class ExampleServerR5IT { diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/MultitenantServerR4IT.java b/src/test/java/ca/uhn/fhir/jpa/starter/MultitenantServerR4IT.java index d8ae7cfa1a1..29af5e7482a 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/MultitenantServerR4IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/MultitenantServerR4IT.java @@ -25,7 +25,6 @@ "hapi.fhir.fhir_version=r4", "hapi.fhir.subscription.websocket_enabled=true", "hapi.fhir.partitioning.partitioning_include_in_search_hashes=false", - "spring.main.allow-bean-definition-overriding=true" }) public class MultitenantServerR4IT { From 6e2836b91dafb1230269ddb837af1150d1739e8d Mon Sep 17 00:00:00 2001 From: Tadgh Date: Tue, 7 Sep 2021 18:36:58 -0400 Subject: [PATCH 006/200] Bump version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1e434d68d39..20f740eb260 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.6.0-PRE2-SNAPSHOT + 5.6.0-PRE1_NIH-SNAPSHOT hapi-fhir-jpaserver-starter From f3ea6803da25d610847cf04a051854cda48a8357 Mon Sep 17 00:00:00 2001 From: Tadgh Date: Thu, 9 Sep 2021 14:41:38 -0400 Subject: [PATCH 007/200] Add broken test --- .../fhir/jpa/starter/ExampleServerR4IT.java | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) 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 6d995238411..7b0548747a7 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java @@ -1,9 +1,12 @@ package ca.uhn.fhir.jpa.starter; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jpa.partition.SystemRequestDetails; +import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; 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.api.server.IBundleProvider; import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; @@ -86,6 +89,57 @@ private Patient getGoldenResourcePatient() { } } + @Test + public void testBatchPutWithIdenticalTags() { + String batchPuts = "{\n" + + "\t\"resourceType\": \"Bundle\",\n" + + "\t\"id\": \"patients\",\n" + + "\t\"type\": \"batch\",\n" + + "\t\"entry\": [\n" + + "\t\t{\n" + + "\t\t\t\"request\": {\n" + + "\t\t\t\t\"method\": \"PUT\",\n" + + "\t\t\t\t\"url\": \"Patient/pat-1\"\n" + + "\t\t\t},\n" + + "\t\t\t\"resource\": {\n" + + "\t\t\t\t\"resourceType\": \"Patient\",\n" + + "\t\t\t\t\"id\": \"pat-1\",\n" + + "\t\t\t\t\"meta\": {\n" + + "\t\t\t\t\t\"tag\": [\n" + + "\t\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\t\"system\": \"http://mysystem.org\",\n" + + "\t\t\t\t\t\t\t\"code\": \"value2\"\n" + + "\t\t\t\t\t\t}\n" + + "\t\t\t\t\t]\n" + + "\t\t\t\t}\n" + + "\t\t\t},\n" + + "\t\t\t\"fullUrl\": \"/Patient/pat-1\"\n" + + "\t\t},\n" + + "\t\t{\n" + + "\t\t\t\"request\": {\n" + + "\t\t\t\t\"method\": \"PUT\",\n" + + "\t\t\t\t\"url\": \"Patient/pat-2\"\n" + + "\t\t\t},\n" + + "\t\t\t\"resource\": {\n" + + "\t\t\t\t\"resourceType\": \"Patient\",\n" + + "\t\t\t\t\"id\": \"pat-2\",\n" + + "\t\t\t\t\"meta\": {\n" + + "\t\t\t\t\t\"tag\": [\n" + + "\t\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\t\"system\": \"http://mysystem.org\",\n" + + "\t\t\t\t\t\t\t\"code\": \"value2\"\n" + + "\t\t\t\t\t\t}\n" + + "\t\t\t\t\t]\n" + + "\t\t\t\t}\n" + + "\t\t\t},\n" + + "\t\t\t\"fullUrl\": \"/Patient/pat-2\"\n" + + "\t\t}\n" + + "\t]\n" + + "}"; + Bundle bundle = FhirContext.forR4().newJsonParser().parseResource(Bundle.class, batchPuts); + ourClient.transaction().withBundle(bundle).execute(); + } + @Test @Order(1) void testWebsocketSubscription() throws Exception { From e41c186bddfe67411423b5dcd76280ea761cece3 Mon Sep 17 00:00:00 2001 From: Tadgh Date: Thu, 9 Sep 2021 14:53:54 -0400 Subject: [PATCH 008/200] Update for new style of container bean --- .../java/ca/uhn/fhir/jpa/starter/EnvironmentHelper.java | 5 +++-- .../ca/uhn/fhir/jpa/starter/FhirServerConfigDstu2.java | 7 ++++--- .../ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java | 7 ++++--- .../java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java | 7 ++++--- .../java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java | 7 ++++--- 5 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/EnvironmentHelper.java b/src/main/java/ca/uhn/fhir/jpa/starter/EnvironmentHelper.java index 75db0f8c957..cc6aaba7581 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/EnvironmentHelper.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/EnvironmentHelper.java @@ -14,6 +14,7 @@ import org.hibernate.search.mapper.orm.automaticindexing.session.AutomaticIndexingSynchronizationStrategyNames; import org.hibernate.search.mapper.orm.cfg.HibernateOrmMapperSettings; import org.hibernate.search.mapper.orm.schema.management.SchemaManagementStrategyName; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy; import org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy; import org.springframework.core.env.CompositePropertySource; @@ -25,7 +26,7 @@ public class EnvironmentHelper { - public static Properties getHibernateProperties(ConfigurableEnvironment environment) { + public static Properties getHibernateProperties(ConfigurableEnvironment environment, ConfigurableListableBeanFactory theBeanFactory) { Properties properties = new Properties(); Map jpaProps = getPropertiesStartingWith(environment, "spring.jpa.properties"); for (Map.Entry entry : jpaProps.entrySet()) { @@ -41,7 +42,7 @@ public static Properties getHibernateProperties(ConfigurableEnvironment environm //properties.putIfAbsent(AvailableSettings.BEAN_CONTAINER, new SpringBeanContainer(beanFactory)); //hapi-fhir-jpaserver-base "sensible defaults" - Map hapiJpaPropertyMap = new HapiFhirLocalContainerEntityManagerFactoryBean().getJpaPropertyMap(); + Map hapiJpaPropertyMap = new HapiFhirLocalContainerEntityManagerFactoryBean(theBeanFactory).getJpaPropertyMap(); hapiJpaPropertyMap.forEach(properties::putIfAbsent); //hapi-fhir-jpaserver-starter defaults diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu2.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu2.java index 6e26d41fafe..67bcd40b782 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu2.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu2.java @@ -5,6 +5,7 @@ import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; import ca.uhn.fhir.jpa.starter.annotations.OnDSTU2Condition; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; @@ -58,8 +59,8 @@ public DatabaseBackedPagingProvider databaseBackedPagingProvider() { @Override @Bean() - public LocalContainerEntityManagerFactoryBean entityManagerFactory() { - LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory(); + public LocalContainerEntityManagerFactoryBean entityManagerFactory(ConfigurableListableBeanFactory theBeanFactory) { + LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory(theBeanFactory); retVal.setPersistenceUnitName("HAPI_PU"); try { @@ -67,7 +68,7 @@ public LocalContainerEntityManagerFactoryBean entityManagerFactory() { } catch (Exception e) { throw new ConfigurationException("Could not set the data source due to a configuration issue", e); } - retVal.setJpaProperties(EnvironmentHelper.getHibernateProperties(configurableEnvironment)); + retVal.setJpaProperties(EnvironmentHelper.getHibernateProperties(configurableEnvironment, theBeanFactory)); return retVal; } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java index 367c0ec6809..4fbde9c5317 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java @@ -10,6 +10,7 @@ import javax.persistence.EntityManagerFactory; import javax.sql.DataSource; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; @@ -62,8 +63,8 @@ public DatabaseBackedPagingProvider databaseBackedPagingProvider() { @Override @Bean - public LocalContainerEntityManagerFactoryBean entityManagerFactory() { - LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory(); + public LocalContainerEntityManagerFactoryBean entityManagerFactory(ConfigurableListableBeanFactory theBeanFactory) { + LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory(theBeanFactory); retVal.setPersistenceUnitName("HAPI_PU"); try { @@ -72,7 +73,7 @@ public LocalContainerEntityManagerFactoryBean entityManagerFactory() { throw new ConfigurationException("Could not set the data source due to a configuration issue", e); } - retVal.setJpaProperties(EnvironmentHelper.getHibernateProperties(configurableEnvironment)); + retVal.setJpaProperties(EnvironmentHelper.getHibernateProperties(configurableEnvironment, theBeanFactory)); return retVal; } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java index a53b0578711..c2f228788b4 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java @@ -7,6 +7,7 @@ import ca.uhn.fhir.jpa.starter.annotations.OnR4Condition; import ca.uhn.fhir.jpa.starter.cql.StarterCqlR4Config; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.context.annotation.*; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.orm.jpa.JpaTransactionManager; @@ -58,8 +59,8 @@ public DatabaseBackedPagingProvider databaseBackedPagingProvider() { @Override @Bean() - public LocalContainerEntityManagerFactoryBean entityManagerFactory() { - LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory(); + public LocalContainerEntityManagerFactoryBean entityManagerFactory(ConfigurableListableBeanFactory theBeanFactory) { + LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory(theBeanFactory); retVal.setPersistenceUnitName("HAPI_PU"); try { @@ -68,7 +69,7 @@ public LocalContainerEntityManagerFactoryBean entityManagerFactory() { throw new ConfigurationException("Could not set the data source due to a configuration issue", e); } - retVal.setJpaProperties(EnvironmentHelper.getHibernateProperties(configurableEnvironment)); + retVal.setJpaProperties(EnvironmentHelper.getHibernateProperties(configurableEnvironment, theBeanFactory)); return retVal; } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java index 2c6d72eaa46..0a9bd72d4dd 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java @@ -6,6 +6,7 @@ import ca.uhn.fhir.jpa.search.lastn.ElasticsearchSvcImpl; import ca.uhn.fhir.jpa.starter.annotations.OnR5Condition; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; @@ -59,8 +60,8 @@ public DatabaseBackedPagingProvider databaseBackedPagingProvider() { @Override @Bean() - public LocalContainerEntityManagerFactoryBean entityManagerFactory() { - LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory(); + public LocalContainerEntityManagerFactoryBean entityManagerFactory(ConfigurableListableBeanFactory theBeanFactory) { + LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory(theBeanFactory); retVal.setPersistenceUnitName("HAPI_PU"); try { @@ -69,7 +70,7 @@ public LocalContainerEntityManagerFactoryBean entityManagerFactory() { throw new ConfigurationException("Could not set the data source due to a configuration issue", e); } - retVal.setJpaProperties(EnvironmentHelper.getHibernateProperties(configurableEnvironment)); + retVal.setJpaProperties(EnvironmentHelper.getHibernateProperties(configurableEnvironment, theBeanFactory)); return retVal; } From 370d68a55cf366c53e40934d28fed689925f4567 Mon Sep 17 00:00:00 2001 From: Tadgh Date: Thu, 16 Sep 2021 01:00:35 -0400 Subject: [PATCH 009/200] Bump for new version --- pom.xml | 2 +- .../ca/uhn/fhir/jpa/starter/EnvironmentHelper.java | 2 +- .../uhn/fhir/jpa/starter/FhirServerConfigDstu3.java | 12 ++++-------- .../ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java | 8 ++------ .../ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java | 12 ++++-------- 5 files changed, 12 insertions(+), 24 deletions(-) diff --git a/pom.xml b/pom.xml index 20f740eb260..bc2f2a2bf0b 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.6.0-PRE1_NIH-SNAPSHOT + 5.6.0-PRE5_NIH hapi-fhir-jpaserver-starter diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/EnvironmentHelper.java b/src/main/java/ca/uhn/fhir/jpa/starter/EnvironmentHelper.java index cc6aaba7581..88e372ca83d 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/EnvironmentHelper.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/EnvironmentHelper.java @@ -74,7 +74,7 @@ public static Properties getHibernateProperties(ConfigurableEnvironment environm ElasticsearchHibernatePropertiesBuilder builder = new ElasticsearchHibernatePropertiesBuilder(); IndexStatus requiredIndexStatus = environment.getProperty("elasticsearch.required_index_status", IndexStatus.class); builder.setRequiredIndexStatus(requireNonNullElse(requiredIndexStatus, IndexStatus.YELLOW)); - builder.setRestUrl(getElasticsearchServerUrl(environment)); + builder.setHosts(getElasticsearchServerUrl(environment)); builder.setUsername(getElasticsearchServerUsername(environment)); builder.setPassword(getElasticsearchServerPassword(environment)); builder.setProtocol(getElasticsearchServerProtocol(environment)); diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java index 4fbde9c5317..3c7029362f2 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java @@ -89,16 +89,12 @@ public JpaTransactionManager hapiTransactionManager(EntityManagerFactory entityM public ElasticsearchSvcImpl elasticsearchSvc() { if (EnvironmentHelper.isElasticsearchEnabled(configurableEnvironment)) { String elasticsearchUrl = EnvironmentHelper.getElasticsearchServerUrl(configurableEnvironment); - String elasticsearchHost; - if (elasticsearchUrl.startsWith("http")) { - elasticsearchHost = elasticsearchUrl.substring(elasticsearchUrl.indexOf("://") + 3, elasticsearchUrl.lastIndexOf(":")); - } else { - elasticsearchHost = elasticsearchUrl.substring(0, elasticsearchUrl.indexOf(":")); - } + if (elasticsearchUrl.startsWith("http")) { + elasticsearchUrl =elasticsearchUrl.substring(elasticsearchUrl.indexOf("://") + 3); + } String elasticsearchUsername = EnvironmentHelper.getElasticsearchServerUsername(configurableEnvironment); String elasticsearchPassword = EnvironmentHelper.getElasticsearchServerPassword(configurableEnvironment); - int elasticsearchPort = Integer.parseInt(elasticsearchUrl.substring(elasticsearchUrl.lastIndexOf(":")+1)); - return new ElasticsearchSvcImpl(elasticsearchHost, elasticsearchPort, elasticsearchUsername, elasticsearchPassword); + return new ElasticsearchSvcImpl(elasticsearchUrl, elasticsearchUsername, elasticsearchPassword); } else { return null; } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java index c2f228788b4..8e4892aba77 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java @@ -85,17 +85,13 @@ public JpaTransactionManager hapiTransactionManager(EntityManagerFactory entityM public ElasticsearchSvcImpl elasticsearchSvc() { if (EnvironmentHelper.isElasticsearchEnabled(configurableEnvironment)) { String elasticsearchUrl = EnvironmentHelper.getElasticsearchServerUrl(configurableEnvironment); - String elasticsearchHost; if (elasticsearchUrl.startsWith("http")) { - elasticsearchHost = elasticsearchUrl.substring(elasticsearchUrl.indexOf("://") + 3, elasticsearchUrl.lastIndexOf(":")); - } else { - elasticsearchHost = elasticsearchUrl.substring(0, elasticsearchUrl.indexOf(":")); + elasticsearchUrl =elasticsearchUrl.substring(elasticsearchUrl.indexOf("://") + 3); } String elasticsearchUsername = EnvironmentHelper.getElasticsearchServerUsername(configurableEnvironment); String elasticsearchPassword = EnvironmentHelper.getElasticsearchServerPassword(configurableEnvironment); - int elasticsearchPort = Integer.parseInt(elasticsearchUrl.substring(elasticsearchUrl.lastIndexOf(":")+1)); - return new ElasticsearchSvcImpl(elasticsearchHost, elasticsearchPort, elasticsearchUsername, elasticsearchPassword); + return new ElasticsearchSvcImpl(elasticsearchUrl, elasticsearchUsername, elasticsearchPassword); } else { return null; } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java index 0a9bd72d4dd..9ad27d63baf 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java @@ -86,16 +86,12 @@ public JpaTransactionManager hapiTransactionManager(EntityManagerFactory entityM public ElasticsearchSvcImpl elasticsearchSvc() { if (EnvironmentHelper.isElasticsearchEnabled(configurableEnvironment)) { String elasticsearchUrl = EnvironmentHelper.getElasticsearchServerUrl(configurableEnvironment); - String elasticsearchHost; - if (elasticsearchUrl.startsWith("http")) { - elasticsearchHost = elasticsearchUrl.substring(elasticsearchUrl.indexOf("://") + 3, elasticsearchUrl.lastIndexOf(":")); - } else { - elasticsearchHost = elasticsearchUrl.substring(0, elasticsearchUrl.indexOf(":")); - } + if (elasticsearchUrl.startsWith("http")) { + elasticsearchUrl =elasticsearchUrl.substring(elasticsearchUrl.indexOf("://") + 3); + } String elasticsearchUsername = EnvironmentHelper.getElasticsearchServerUsername(configurableEnvironment); String elasticsearchPassword = EnvironmentHelper.getElasticsearchServerPassword(configurableEnvironment); - int elasticsearchPort = Integer.parseInt(elasticsearchUrl.substring(elasticsearchUrl.lastIndexOf(":")+1)); - return new ElasticsearchSvcImpl(elasticsearchHost, elasticsearchPort, elasticsearchUsername, elasticsearchPassword); + return new ElasticsearchSvcImpl(elasticsearchUrl, elasticsearchUsername, elasticsearchPassword); } else { return null; } From ac75421a7b869ce7b16fe5cb89882eacc539a374 Mon Sep 17 00:00:00 2001 From: Michael Buckley Date: Tue, 5 Oct 2021 15:07:08 -0400 Subject: [PATCH 010/200] Update to 5.6.0-PRE7_NIH-SNAPSHOT and activate advanced index --- pom.xml | 2 +- .../java/ca/uhn/fhir/jpa/starter/AppProperties.java | 11 ++++++++++- .../uhn/fhir/jpa/starter/FhirServerConfigCommon.java | 1 + src/main/resources/application.yaml | 2 ++ 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index bc2f2a2bf0b..73a4ce00d00 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.6.0-PRE5_NIH + 5.6.0-PRE7_NIH-SNAPSHOT hapi-fhir-jpaserver-starter diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java b/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java index 98113b2a425..aa140154d7d 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java @@ -23,6 +23,7 @@ public class AppProperties { private Boolean cql_enabled = false; private Boolean mdm_enabled = false; + private boolean advanced_lucene_indexing = false; private Boolean allow_cascading_deletes = false; private Boolean allow_contains_searches = true; private Boolean allow_external_references = false; @@ -206,7 +207,15 @@ public void setClient_id_strategy( this.client_id_strategy = client_id_strategy; } - public Boolean getAllow_cascading_deletes() { + public boolean getAdvanced_lucene_indexing() { + return this.advanced_lucene_indexing; + } + + public void setAdvanced_lucene_indexing(boolean theAdvanced_lucene_indexing) { + advanced_lucene_indexing = theAdvanced_lucene_indexing; + } + + public Boolean getAllow_cascading_deletes() { return allow_cascading_deletes; } 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 74d46bc8709..63f675ff6d8 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java @@ -120,6 +120,7 @@ public DaoConfig daoConfig(AppProperties appProperties) { } retVal.setFilterParameterEnabled(appProperties.getFilter_search_enabled()); + retVal.setAdvancedLuceneIndexing(appProperties.getAdvanced_lucene_indexing()); return retVal; } diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 8861c3e7301..e775be29d71 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -71,6 +71,8 @@ hapi: # enable_repository_validating_interceptor: false # enable_index_missing_fields: false # enable_index_contained_resource: false +# advanced_lucene_indexing: false + advanced_lucene_indexing: true # enforce_referential_integrity_on_delete: false # enforce_referential_integrity_on_write: false # etag_support_enabled: true From 3272b8c0d22bc532dbe276afecb2e72a2b563431 Mon Sep 17 00:00:00 2001 From: Michael Buckley Date: Tue, 5 Oct 2021 16:06:19 -0400 Subject: [PATCH 011/200] Disable default flyway processing --- src/main/resources/application.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index e775be29d71..4f509d6a417 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -1,4 +1,6 @@ spring: + flyway: + enabled: false datasource: url: 'jdbc:h2:file:./target/database/h2' #url: jdbc:h2:mem:test_mem From 097db1642f14baa4817b82bdde529885bb35dfee Mon Sep 17 00:00:00 2001 From: Michael Buckley Date: Thu, 7 Oct 2021 15:14:06 -0400 Subject: [PATCH 012/200] Add local_base_urls configuration to feed DaoConfig.setTreatBaseUrlsAsLocal() --- .../ca/uhn/fhir/jpa/starter/AppProperties.java | 16 +++++++++++----- .../fhir/jpa/starter/FhirServerConfigCommon.java | 2 ++ .../resources/application-integrationtest.yaml | 2 ++ 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java b/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java index aa140154d7d..78d666060cb 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java @@ -11,10 +11,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Configuration; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Objects; +import java.util.*; @ConfigurationProperties(prefix = "hapi.fhir") @Configuration @@ -77,6 +74,7 @@ public class AppProperties { private Integer bundle_batch_pool_size = 20; private Integer bundle_batch_pool_max_size = 100; + private List local_base_urls = new ArrayList<>(); public Boolean getUse_apache_address_strategy() { return use_apache_address_strategy; @@ -190,7 +188,11 @@ public void setSupported_resource_types(List supported_resource_types) { this.supported_resource_types = supported_resource_types; } - public Logger getLogger() { + public List getSupported_resource_types(List supported_resource_types) { + return this.supported_resource_types; + } + + public Logger getLogger() { return logger; } @@ -499,6 +501,10 @@ public void setBundle_batch_pool_max_size(Integer bundle_batch_pool_max_size) { this.bundle_batch_pool_max_size = bundle_batch_pool_max_size; } + public List getLocal_base_urls() { + return local_base_urls; + } + public static class Cors { private Boolean allow_Credentials = true; private List allowed_origin = ImmutableList.of("*"); 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 63f675ff6d8..7310fe09208 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java @@ -26,6 +26,7 @@ import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.transaction.annotation.EnableTransactionManagement; +import java.util.HashSet; import java.util.Optional; /** @@ -121,6 +122,7 @@ public DaoConfig daoConfig(AppProperties appProperties) { retVal.setFilterParameterEnabled(appProperties.getFilter_search_enabled()); retVal.setAdvancedLuceneIndexing(appProperties.getAdvanced_lucene_indexing()); + retVal.setTreatBaseUrlsAsLocal(new HashSet<>(appProperties.getLocal_base_urls())); return retVal; } diff --git a/src/test/resources/application-integrationtest.yaml b/src/test/resources/application-integrationtest.yaml index b19a14bbb50..6afbcbf1c66 100644 --- a/src/test/resources/application-integrationtest.yaml +++ b/src/test/resources/application-integrationtest.yaml @@ -34,6 +34,8 @@ hapi: # fhirpath_interceptor_enabled: false # filter_search_enabled: true # graphql_enabled: true +# local_base_urls: +# - http://hapi.fhir.org/baseR4 #partitioning: # cross_partition_reference_mode: true # multitenancy_enabled: true From ea3f10ec86774abfaedf220f46a7b515f9751d26 Mon Sep 17 00:00:00 2001 From: jkv Date: Wed, 10 Nov 2021 20:57:31 +0100 Subject: [PATCH 013/200] Added OpenAPI / Swagger option --- pom.xml | 6 ++++++ src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java | 9 +++++++++ .../ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java | 5 +++++ src/main/resources/application.yaml | 2 ++ 4 files changed, 22 insertions(+) diff --git a/pom.xml b/pom.xml index 94f096c6af1..a7d67e56f53 100644 --- a/pom.xml +++ b/pom.xml @@ -112,6 +112,12 @@ hapi-fhir-jpaserver-mdm ${project.version} + + + ca.uhn.hapi.fhir + hapi-fhir-server-openapi + ${project.version} + ca.uhn.hapi.fhir diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java b/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java index fd92001cca2..87b4181af6f 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java @@ -22,6 +22,7 @@ public class AppProperties { private Boolean cql_enabled = false; + private Boolean openapi_enabled = false; private Boolean mdm_enabled = false; private Boolean allow_cascading_deletes = false; private Boolean allow_contains_searches = true; @@ -75,6 +76,14 @@ public class AppProperties { private Boolean use_apache_address_strategy = false; private Boolean use_apache_address_strategy_https = false; + public Boolean getOpenapi_enabled() { + return openapi_enabled; + } + + public void setOpenapi_enabled(Boolean openapi_enabled) { + this.openapi_enabled = openapi_enabled; + } + public Boolean getUse_apache_address_strategy() { return use_apache_address_strategy; } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java index dc16873fff1..b58c41cb20d 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java @@ -28,6 +28,7 @@ import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator; import ca.uhn.fhir.narrative.INarrativeGenerator; import ca.uhn.fhir.narrative2.NullNarrativeGenerator; +import ca.uhn.fhir.rest.openapi.OpenApiInterceptor; import ca.uhn.fhir.rest.server.ApacheProxyAddressStrategy; import ca.uhn.fhir.rest.server.ETagSupportEnum; import ca.uhn.fhir.rest.server.HardcodedServerAddressStrategy; @@ -357,6 +358,10 @@ protected void initialize() throws ServletException { daoConfig.setDeferIndexingForCodesystemsOfSize(appProperties.getDefer_indexing_for_codesystems_of_size()); + if (appProperties.getOpenapi_enabled()) { + registerInterceptor(new OpenApiInterceptor()); + } + // Bulk Export if (appProperties.getBulk_export_enabled()) { registerProvider(bulkDataExportProvider); diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index c4c393d6dfe..1c51699f1e8 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -36,6 +36,8 @@ spring: allow-bean-definition-overriding: true hapi: fhir: + ### This enables the swagger-ui at /fhir/swagger-ui/index.html as well as the /fhir/api-docs (see https://hapifhir.io/hapi-fhir/docs/server_plain/openapi.html) + openapi_enabled: true ### This is the FHIR version. Choose between, DSTU2, DSTU3, R4 or R5 fhir_version: R4 ### enable to use the ApacheProxyAddressStrategy which uses X-Forwarded-* headers From 16559ea17cf679812fabe8e2b2c2a05dc757e751 Mon Sep 17 00:00:00 2001 From: jkv Date: Thu, 18 Nov 2021 19:48:40 +0100 Subject: [PATCH 014/200] Upgraded to 5.6.0 Subscription tests fail ... --- pom.xml | 2 +- .../ca/uhn/fhir/jpa/starter/Application.java | 10 -------- .../fhir/jpa/starter/EnvironmentHelper.java | 8 ++++--- .../jpa/starter/FhirServerConfigCommon.java | 24 +++++++++---------- .../jpa/starter/FhirServerConfigDstu2.java | 8 ++++--- .../jpa/starter/FhirServerConfigDstu3.java | 12 ++++++---- .../fhir/jpa/starter/FhirServerConfigR4.java | 12 ++++++---- .../fhir/jpa/starter/FhirServerConfigR5.java | 12 ++++++---- src/main/resources/application.yaml | 5 ++-- 9 files changed, 46 insertions(+), 47 deletions(-) diff --git a/pom.xml b/pom.xml index a7d67e56f53..6b677e8ce46 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.5.1 + 5.6.0 hapi-fhir-jpaserver-starter diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/Application.java b/src/main/java/ca/uhn/fhir/jpa/starter/Application.java index e823d5f3ddb..2cf60f56344 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/Application.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/Application.java @@ -29,16 +29,6 @@ public class Application extends SpringBootServletInitializer { public static void main(String[] args) { - /* - * https://github.com/hapifhir/hapi-fhir-jpaserver-starter/issues/246 - * This will be allowed for a short period until we know how MDM should be configured - * or don't have multiple equal bean instantiations. - * - * This will require changes in the main project as stated in the Github comment - * */ - System.setProperty("spring.main.allow-bean-definition-overriding","true"); - - System.setProperty("spring.batch.job.enabled", "false"); SpringApplication.run(Application.class, args); //Server is now accessible at eg. http://localhost:8080/fhir/metadata diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/EnvironmentHelper.java b/src/main/java/ca/uhn/fhir/jpa/starter/EnvironmentHelper.java index 75db0f8c957..96a958f187f 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/EnvironmentHelper.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/EnvironmentHelper.java @@ -14,6 +14,7 @@ import org.hibernate.search.mapper.orm.automaticindexing.session.AutomaticIndexingSynchronizationStrategyNames; import org.hibernate.search.mapper.orm.cfg.HibernateOrmMapperSettings; import org.hibernate.search.mapper.orm.schema.management.SchemaManagementStrategyName; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy; import org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy; import org.springframework.core.env.CompositePropertySource; @@ -25,7 +26,8 @@ public class EnvironmentHelper { - public static Properties getHibernateProperties(ConfigurableEnvironment environment) { + public static Properties getHibernateProperties(ConfigurableEnvironment environment, + ConfigurableListableBeanFactory myConfigurableListableBeanFactory) { Properties properties = new Properties(); Map jpaProps = getPropertiesStartingWith(environment, "spring.jpa.properties"); for (Map.Entry entry : jpaProps.entrySet()) { @@ -41,7 +43,7 @@ public static Properties getHibernateProperties(ConfigurableEnvironment environm //properties.putIfAbsent(AvailableSettings.BEAN_CONTAINER, new SpringBeanContainer(beanFactory)); //hapi-fhir-jpaserver-base "sensible defaults" - Map hapiJpaPropertyMap = new HapiFhirLocalContainerEntityManagerFactoryBean().getJpaPropertyMap(); + Map hapiJpaPropertyMap = new HapiFhirLocalContainerEntityManagerFactoryBean(myConfigurableListableBeanFactory).getJpaPropertyMap(); hapiJpaPropertyMap.forEach(properties::putIfAbsent); //hapi-fhir-jpaserver-starter defaults @@ -73,7 +75,7 @@ public static Properties getHibernateProperties(ConfigurableEnvironment environm ElasticsearchHibernatePropertiesBuilder builder = new ElasticsearchHibernatePropertiesBuilder(); IndexStatus requiredIndexStatus = environment.getProperty("elasticsearch.required_index_status", IndexStatus.class); builder.setRequiredIndexStatus(requireNonNullElse(requiredIndexStatus, IndexStatus.YELLOW)); - builder.setRestUrl(getElasticsearchServerUrl(environment)); + builder.setHosts(getElasticsearchServerUrl(environment)); builder.setUsername(getElasticsearchServerUsername(environment)); builder.setPassword(getElasticsearchServerPassword(environment)); builder.setProtocol(getElasticsearchServerProtocol(environment)); 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 4ec59123ed0..1e296d9bd3b 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java @@ -8,8 +8,9 @@ import ca.uhn.fhir.jpa.model.config.PartitionSettings.CrossPartitionReferenceMode; import ca.uhn.fhir.jpa.model.entity.ModelConfig; 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.jpa.subscription.match.deliver.email.JavaMailEmailSender; +import ca.uhn.fhir.rest.server.mail.MailConfig; import com.google.common.base.Strings; import org.hl7.fhir.dstu2.model.Subscription; import org.springframework.boot.env.YamlPropertySourceLoader; @@ -207,22 +208,21 @@ public IBinaryStorageSvc binaryStorageSvc(AppProperties appProperties) { @Bean() public IEmailSender emailSender(AppProperties appProperties, Optional subscriptionDeliveryHandlerFactory) { if (appProperties.getSubscription() != null && appProperties.getSubscription().getEmail() != null) { - JavaMailEmailSender retVal = new JavaMailEmailSender(); + MailConfig mailConfig = new MailConfig(); AppProperties.Subscription.Email email = appProperties.getSubscription().getEmail(); - retVal.setSmtpServerHostname(email.getHost()); - retVal.setSmtpServerPort(email.getPort()); - retVal.setSmtpServerUsername(email.getUsername()); - retVal.setSmtpServerPassword(email.getPassword()); - retVal.setAuth(email.getAuth()); - retVal.setStartTlsEnable(email.getStartTlsEnable()); - retVal.setStartTlsRequired(email.getStartTlsRequired()); - retVal.setQuitWait(email.getQuitWait()); + mailConfig.setSmtpHostname(email.getHost()); + mailConfig.setSmtpPort(email.getPort()); + mailConfig.setSmtpUsername(email.getUsername()); + mailConfig.setSmtpPassword(email.getPassword()); + mailConfig.setSmtpUseStartTLS(email.getStartTlsEnable()); + + IEmailSender emailSender = new EmailSenderImpl(mailConfig); if(subscriptionDeliveryHandlerFactory.isPresent()) - subscriptionDeliveryHandlerFactory.get().setEmailSender(retVal); + subscriptionDeliveryHandlerFactory.get().setEmailSender(emailSender); - return retVal; + return emailSender; } return null; diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu2.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu2.java index 6e26d41fafe..25548bf3f51 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu2.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu2.java @@ -5,6 +5,7 @@ import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; import ca.uhn.fhir.jpa.starter.annotations.OnDSTU2Condition; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; @@ -58,8 +59,9 @@ public DatabaseBackedPagingProvider databaseBackedPagingProvider() { @Override @Bean() - public LocalContainerEntityManagerFactoryBean entityManagerFactory() { - LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory(); + public LocalContainerEntityManagerFactoryBean entityManagerFactory( + ConfigurableListableBeanFactory myConfigurableListableBeanFactory) { + LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory(myConfigurableListableBeanFactory); retVal.setPersistenceUnitName("HAPI_PU"); try { @@ -67,7 +69,7 @@ public LocalContainerEntityManagerFactoryBean entityManagerFactory() { } catch (Exception e) { throw new ConfigurationException("Could not set the data source due to a configuration issue", e); } - retVal.setJpaProperties(EnvironmentHelper.getHibernateProperties(configurableEnvironment)); + retVal.setJpaProperties(EnvironmentHelper.getHibernateProperties(configurableEnvironment, myConfigurableListableBeanFactory)); return retVal; } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java index 367c0ec6809..84822ddd3f4 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java @@ -10,6 +10,7 @@ import javax.persistence.EntityManagerFactory; import javax.sql.DataSource; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; @@ -62,8 +63,9 @@ public DatabaseBackedPagingProvider databaseBackedPagingProvider() { @Override @Bean - public LocalContainerEntityManagerFactoryBean entityManagerFactory() { - LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory(); + public LocalContainerEntityManagerFactoryBean entityManagerFactory( + ConfigurableListableBeanFactory myConfigurableListableBeanFactory) { + LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory(myConfigurableListableBeanFactory); retVal.setPersistenceUnitName("HAPI_PU"); try { @@ -72,7 +74,8 @@ public LocalContainerEntityManagerFactoryBean entityManagerFactory() { throw new ConfigurationException("Could not set the data source due to a configuration issue", e); } - retVal.setJpaProperties(EnvironmentHelper.getHibernateProperties(configurableEnvironment)); + retVal.setJpaProperties(EnvironmentHelper.getHibernateProperties(configurableEnvironment, + myConfigurableListableBeanFactory)); return retVal; } @@ -96,8 +99,7 @@ public ElasticsearchSvcImpl elasticsearchSvc() { } String elasticsearchUsername = EnvironmentHelper.getElasticsearchServerUsername(configurableEnvironment); String elasticsearchPassword = EnvironmentHelper.getElasticsearchServerPassword(configurableEnvironment); - int elasticsearchPort = Integer.parseInt(elasticsearchUrl.substring(elasticsearchUrl.lastIndexOf(":")+1)); - return new ElasticsearchSvcImpl(elasticsearchHost, elasticsearchPort, elasticsearchUsername, elasticsearchPassword); + return new ElasticsearchSvcImpl(elasticsearchHost, elasticsearchUsername, elasticsearchPassword); } else { return null; } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java index a53b0578711..842256a26fa 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java @@ -7,6 +7,7 @@ import ca.uhn.fhir.jpa.starter.annotations.OnR4Condition; import ca.uhn.fhir.jpa.starter.cql.StarterCqlR4Config; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.context.annotation.*; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.orm.jpa.JpaTransactionManager; @@ -58,8 +59,9 @@ public DatabaseBackedPagingProvider databaseBackedPagingProvider() { @Override @Bean() - public LocalContainerEntityManagerFactoryBean entityManagerFactory() { - LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory(); + public LocalContainerEntityManagerFactoryBean entityManagerFactory( + ConfigurableListableBeanFactory myConfigurableListableBeanFactory) { + LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory(myConfigurableListableBeanFactory); retVal.setPersistenceUnitName("HAPI_PU"); try { @@ -68,7 +70,8 @@ public LocalContainerEntityManagerFactoryBean entityManagerFactory() { throw new ConfigurationException("Could not set the data source due to a configuration issue", e); } - retVal.setJpaProperties(EnvironmentHelper.getHibernateProperties(configurableEnvironment)); + retVal.setJpaProperties(EnvironmentHelper.getHibernateProperties(configurableEnvironment, + myConfigurableListableBeanFactory)); return retVal; } @@ -93,8 +96,7 @@ public ElasticsearchSvcImpl elasticsearchSvc() { String elasticsearchUsername = EnvironmentHelper.getElasticsearchServerUsername(configurableEnvironment); String elasticsearchPassword = EnvironmentHelper.getElasticsearchServerPassword(configurableEnvironment); - int elasticsearchPort = Integer.parseInt(elasticsearchUrl.substring(elasticsearchUrl.lastIndexOf(":")+1)); - return new ElasticsearchSvcImpl(elasticsearchHost, elasticsearchPort, elasticsearchUsername, elasticsearchPassword); + return new ElasticsearchSvcImpl(elasticsearchHost, elasticsearchUsername, elasticsearchPassword); } else { return null; } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java index 2c6d72eaa46..3cb696833d9 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java @@ -6,6 +6,7 @@ import ca.uhn.fhir.jpa.search.lastn.ElasticsearchSvcImpl; import ca.uhn.fhir.jpa.starter.annotations.OnR5Condition; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; @@ -59,8 +60,9 @@ public DatabaseBackedPagingProvider databaseBackedPagingProvider() { @Override @Bean() - public LocalContainerEntityManagerFactoryBean entityManagerFactory() { - LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory(); + public LocalContainerEntityManagerFactoryBean entityManagerFactory( + ConfigurableListableBeanFactory myConfigurableListableBeanFactory) { + LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory(myConfigurableListableBeanFactory); retVal.setPersistenceUnitName("HAPI_PU"); try { @@ -69,7 +71,8 @@ public LocalContainerEntityManagerFactoryBean entityManagerFactory() { throw new ConfigurationException("Could not set the data source due to a configuration issue", e); } - retVal.setJpaProperties(EnvironmentHelper.getHibernateProperties(configurableEnvironment)); + retVal.setJpaProperties(EnvironmentHelper.getHibernateProperties(configurableEnvironment, + myConfigurableListableBeanFactory)); return retVal; } @@ -93,8 +96,7 @@ public ElasticsearchSvcImpl elasticsearchSvc() { } String elasticsearchUsername = EnvironmentHelper.getElasticsearchServerUsername(configurableEnvironment); String elasticsearchPassword = EnvironmentHelper.getElasticsearchServerPassword(configurableEnvironment); - int elasticsearchPort = Integer.parseInt(elasticsearchUrl.substring(elasticsearchUrl.lastIndexOf(":")+1)); - return new ElasticsearchSvcImpl(elasticsearchHost, elasticsearchPort, elasticsearchUsername, elasticsearchPassword); + return new ElasticsearchSvcImpl(elasticsearchHost, elasticsearchUsername, elasticsearchPassword); } else { return null; } diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 1c51699f1e8..7dd9ece39b1 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -10,6 +10,8 @@ spring: # database connection pool size hikari: maximum-pool-size: 10 + flyway: + check-location: false jpa: properties: hibernate.format_sql: false @@ -31,9 +33,6 @@ spring: batch: job: enabled: false - main: -# TODO 5.6.0 -> Prevent duplicate bean definitions in the Spring batch config in HAPI: see: - allow-bean-definition-overriding: true hapi: fhir: ### This enables the swagger-ui at /fhir/swagger-ui/index.html as well as the /fhir/api-docs (see https://hapifhir.io/hapi-fhir/docs/server_plain/openapi.html) From 96450874af60f9bbac1b00afb8da6ecb5fc39fdf Mon Sep 17 00:00:00 2001 From: jkv Date: Thu, 18 Nov 2021 20:17:30 +0100 Subject: [PATCH 015/200] Bumped version of Spring Boot in order to fix same issue as https://github.com/Haulmont/jmix-security/issues/90 --- pom.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pom.xml b/pom.xml index 6b677e8ce46..338e9c9fda2 100644 --- a/pom.xml +++ b/pom.xml @@ -21,6 +21,7 @@ 8 + 2.5.6 @@ -69,6 +70,7 @@ com.sun.mail javax.mail + 1.6.2 javax.activation From 0203a366685f1dea772148b1a8e2b8f9eea1823d Mon Sep 17 00:00:00 2001 From: Jens Kristian Villadsen Date: Sat, 20 Nov 2021 16:07:26 +0100 Subject: [PATCH 016/200] Update application.yaml See https://github.com/hapifhir/hapi-fhir-jpaserver-starter/issues/292 --- src/main/resources/application.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 7dd9ece39b1..bca18601146 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -12,6 +12,7 @@ spring: maximum-pool-size: 10 flyway: check-location: false + baselineOnMigrate: true jpa: properties: hibernate.format_sql: false From e39d84ed36439f3361d3601477e28bd6e6d0d504 Mon Sep 17 00:00:00 2001 From: chgl Date: Sat, 20 Nov 2021 13:55:29 +0100 Subject: [PATCH 017/200] documented use of Values.extraEnv --- charts/hapi-fhir-jpaserver/Chart.yaml | 6 ++--- charts/hapi-fhir-jpaserver/README.md | 28 ++++++++++++++++----- charts/hapi-fhir-jpaserver/README.md.gotmpl | 15 +++++++++++ charts/hapi-fhir-jpaserver/values.yaml | 11 ++++++++ 4 files changed, 51 insertions(+), 9 deletions(-) diff --git a/charts/hapi-fhir-jpaserver/Chart.yaml b/charts/hapi-fhir-jpaserver/Chart.yaml index 0b839154c3d..b4cb3ac9213 100644 --- a/charts/hapi-fhir-jpaserver/Chart.yaml +++ b/charts/hapi-fhir-jpaserver/Chart.yaml @@ -18,6 +18,6 @@ annotations: # added, changed, deprecated, removed, fixed, and security. - kind: changed description: | - updated HAPI FHIR starter image to 5.5.1 -appVersion: v5.5.1 -version: 0.6.0 + updated HAPI FHIR starter image to 5.6.0 +appVersion: v5.6.0 +version: 0.7.0 diff --git a/charts/hapi-fhir-jpaserver/README.md b/charts/hapi-fhir-jpaserver/README.md index e1e549606a8..efbf65d54be 100644 --- a/charts/hapi-fhir-jpaserver/README.md +++ b/charts/hapi-fhir-jpaserver/README.md @@ -1,6 +1,6 @@ # HAPI FHIR JPA Server Starter Helm Chart -![Version: 0.6.0](https://img.shields.io/badge/Version-0.6.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v5.5.1](https://img.shields.io/badge/AppVersion-v5.5.1-informational?style=flat-square) +![Version: 0.7.0](https://img.shields.io/badge/Version-0.7.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v5.6.0](https://img.shields.io/badge/AppVersion-v5.6.0-informational?style=flat-square) This helm chart will help you install the HAPI FHIR JPA Server in a Kubernetes environment. @@ -11,6 +11,9 @@ helm repo add hapifhir https://hapifhir.github.io/hapi-fhir-jpaserver-starter/ helm install --render-subchart-notes hapi-fhir-jpaserver hapifhir/hapi-fhir-jpaserver ``` +> ⚠ By default, the included [PostgreSQL Helm chart](https://github.com/bitnami/charts/tree/master/bitnami/postgresql#upgrading) +> auto-generates a random password for the database which may cause problems when upgrading the chart (see [here for details](https://github.com/bitnami/charts/tree/master/bitnami/postgresql#upgrading)). + ## Values | Key | Type | Default | Description | @@ -24,11 +27,12 @@ helm install --render-subchart-notes hapi-fhir-jpaserver hapifhir/hapi-fhir-jpas | externalDatabase.password | string | `""` | database password | | externalDatabase.port | int | `5432` | database port number | | externalDatabase.user | string | `"fhir"` | username for the external database | +| extraEnv | list | `[]` | extra environment variables to set on the server container | | fullnameOverride | string | `""` | override the chart fullname | | image.flavor | string | `"distroless"` | the flavor or variant of the image to use. appended to the image tag by `-`. | -| image.pullPolicy | string | `"IfNotPresent"` | | -| image.registry | string | `"docker.io"` | | -| image.repository | string | `"hapiproject/hapi"` | | +| image.pullPolicy | string | `"IfNotPresent"` | image pullPolicy to use | +| image.registry | string | `"docker.io"` | registry where the HAPI FHIR server image is hosted | +| image.repository | string | `"hapiproject/hapi"` | the path inside the repository | | image.tag | string | `""` | defaults to `Chart.appVersion` | | imagePullSecrets | list | `[]` | image pull secrets to use when pulling the image | | ingress.annotations | object | `{}` | provide any additional annotations which may be required. Evaluated as a template. | @@ -61,8 +65,8 @@ helm install --render-subchart-notes hapi-fhir-jpaserver hapifhir/hapi-fhir-jpas | securityContext.readOnlyRootFilesystem | bool | `true` | | | securityContext.runAsNonRoot | bool | `true` | | | securityContext.runAsUser | int | `65532` | | -| service.port | int | `8080` | | -| service.type | string | `"ClusterIP"` | | +| service.port | int | `8080` | port where the server will be exposed at | +| service.type | string | `"ClusterIP"` | service type | | startupProbe.failureThreshold | int | `10` | | | startupProbe.initialDelaySeconds | int | `60` | | | startupProbe.periodSeconds | int | `30` | | @@ -70,5 +74,17 @@ helm install --render-subchart-notes hapi-fhir-jpaserver hapifhir/hapi-fhir-jpas | startupProbe.timeoutSeconds | int | `30` | | | tolerations | list | `[]` | pod tolerations | +## Development + +To update the Helm chart when a new version of the `hapiproject/hapi` image is released, the [Chart.yaml](Chart.yaml)'s +`appVersion` and `version` fields need to be updated accordingly. Afterwards, re-generate the [README.md](README.md) +by running: + +```sh +$ helm-docs +INFO[2021-11-20T12:38:04Z] Found Chart directories [charts/hapi-fhir-jpaserver] +INFO[2021-11-20T12:38:04Z] Generating README Documentation for chart /usr/src/app/charts/hapi-fhir-jpaserver +``` + ---------------------------------------------- Autogenerated from chart metadata using [helm-docs v1.5.0](https://github.com/norwoodj/helm-docs/releases/v1.5.0) diff --git a/charts/hapi-fhir-jpaserver/README.md.gotmpl b/charts/hapi-fhir-jpaserver/README.md.gotmpl index c599d14392e..e345f8be8cb 100644 --- a/charts/hapi-fhir-jpaserver/README.md.gotmpl +++ b/charts/hapi-fhir-jpaserver/README.md.gotmpl @@ -11,6 +11,21 @@ helm repo add hapifhir https://hapifhir.github.io/hapi-fhir-jpaserver-starter/ helm install --render-subchart-notes hapi-fhir-jpaserver hapifhir/hapi-fhir-jpaserver ``` +> ⚠ By default, the included [PostgreSQL Helm chart](https://github.com/bitnami/charts/tree/master/bitnami/postgresql#upgrading) +> auto-generates a random password for the database which may cause problems when upgrading the chart (see [here for details](https://github.com/bitnami/charts/tree/master/bitnami/postgresql#upgrading)). + {{ template "chart.valuesSection" . }} +## Development + +To update the Helm chart when a new version of the `hapiproject/hapi` image is released, the [Chart.yaml](Chart.yaml)'s +`appVersion` and `version` fields need to be updated accordingly. Afterwards, re-generate the [README.md](README.md) +by running: + +```sh +$ helm-docs +INFO[2021-11-20T12:38:04Z] Found Chart directories [charts/hapi-fhir-jpaserver] +INFO[2021-11-20T12:38:04Z] Generating README Documentation for chart /usr/src/app/charts/hapi-fhir-jpaserver +``` + {{ template "helm-docs.versionFooter" . }} diff --git a/charts/hapi-fhir-jpaserver/values.yaml b/charts/hapi-fhir-jpaserver/values.yaml index 5e3c63bfb3c..13feab2de7e 100644 --- a/charts/hapi-fhir-jpaserver/values.yaml +++ b/charts/hapi-fhir-jpaserver/values.yaml @@ -2,13 +2,16 @@ replicaCount: 1 image: + # -- registry where the HAPI FHIR server image is hosted registry: docker.io + # -- the path inside the repository repository: hapiproject/hapi # -- defaults to `Chart.appVersion` tag: "" # -- the flavor or variant of the image to use. # appended to the image tag by `-`. flavor: "distroless" + # -- image pullPolicy to use pullPolicy: IfNotPresent # -- image pull secrets to use when pulling the image @@ -42,7 +45,9 @@ securityContext: # service to expose the server service: + # -- service type type: ClusterIP + # -- port where the server will be exposed at port: 8080 ingress: @@ -157,3 +162,9 @@ networkPolicy: # matchLabels: # app.kubernetes.io/name: {{ $.Release.Name }} allowedFrom: [] + +# -- extra environment variables to set on the server container +extraEnv: + [] + # - name: SPRING_FLYWAY_BASELINE_ON_MIGRATE + # value: "true" From 304f779a2bbccd98471b0e831ce912058ee07025 Mon Sep 17 00:00:00 2001 From: chgl Date: Tue, 23 Nov 2021 00:11:28 +0100 Subject: [PATCH 018/200] added options for specifying a PodDisruptionBudget --- charts/hapi-fhir-jpaserver/Chart.yaml | 3 +++ charts/hapi-fhir-jpaserver/README.md | 3 +++ .../templates/poddisruptionbudget.yaml | 19 +++++++++++++++++++ charts/hapi-fhir-jpaserver/values.yaml | 9 +++++++++ 4 files changed, 34 insertions(+) create mode 100644 charts/hapi-fhir-jpaserver/templates/poddisruptionbudget.yaml diff --git a/charts/hapi-fhir-jpaserver/Chart.yaml b/charts/hapi-fhir-jpaserver/Chart.yaml index b4cb3ac9213..dd2c479314a 100644 --- a/charts/hapi-fhir-jpaserver/Chart.yaml +++ b/charts/hapi-fhir-jpaserver/Chart.yaml @@ -19,5 +19,8 @@ annotations: - kind: changed description: | updated HAPI FHIR starter image to 5.6.0 + - kind: added + description: | + added support for configuring PodDisruptionBudget for the server pods appVersion: v5.6.0 version: 0.7.0 diff --git a/charts/hapi-fhir-jpaserver/README.md b/charts/hapi-fhir-jpaserver/README.md index efbf65d54be..9208bd6f967 100644 --- a/charts/hapi-fhir-jpaserver/README.md +++ b/charts/hapi-fhir-jpaserver/README.md @@ -47,6 +47,9 @@ helm install --render-subchart-notes hapi-fhir-jpaserver hapifhir/hapi-fhir-jpas | networkPolicy.explicitNamespacesSelector | object | `{}` | a Kubernetes LabelSelector to explicitly select namespaces from which ingress traffic could be allowed | | nodeSelector | object | `{}` | node selector for the pod | | podAnnotations | object | `{}` | annotations applied to the server pod | +| podDisruptionBudget.enabled | bool | `false` | Enable PodDisruptionBudget for the server pods. uses policy/v1/PodDisruptionBudget thus requiring k8s 1.21+ | +| podDisruptionBudget.maxUnavailable | string | `""` | maximum unavailable instances | +| podDisruptionBudget.minAvailable | int | `1` | minimum available instances | | podSecurityContext | object | `{}` | pod security context | | postgresql.containerSecurityContext.allowPrivilegeEscalation | bool | `false` | | | postgresql.containerSecurityContext.capabilities.drop[0] | string | `"ALL"` | | diff --git a/charts/hapi-fhir-jpaserver/templates/poddisruptionbudget.yaml b/charts/hapi-fhir-jpaserver/templates/poddisruptionbudget.yaml new file mode 100644 index 00000000000..8c35cdd9019 --- /dev/null +++ b/charts/hapi-fhir-jpaserver/templates/poddisruptionbudget.yaml @@ -0,0 +1,19 @@ + +{{- if .Values.podDisruptionBudget.enabled }} +kind: PodDisruptionBudget +apiVersion: policy/v1 +metadata: + name: {{ include "hapi-fhir-jpaserver.fullname" . }} + labels: + {{- include "hapi-fhir-jpaserver.labels" . | nindent 4 }} +spec: + {{- if .Values.podDisruptionBudget.minAvailable }} + minAvailable: {{ .Values.podDisruptionBudget.minAvailable }} + {{- end }} + {{- if .Values.podDisruptionBudget.maxUnavailable }} + maxUnavailable: {{ .Values.podDisruptionBudget.maxUnavailable }} + {{- end }} + selector: + matchLabels: + {{- include "hapi-fhir-jpaserver.selectorLabels" . | nindent 6 }} +{{- end }} diff --git a/charts/hapi-fhir-jpaserver/values.yaml b/charts/hapi-fhir-jpaserver/values.yaml index 13feab2de7e..5fb71ddb257 100644 --- a/charts/hapi-fhir-jpaserver/values.yaml +++ b/charts/hapi-fhir-jpaserver/values.yaml @@ -168,3 +168,12 @@ extraEnv: [] # - name: SPRING_FLYWAY_BASELINE_ON_MIGRATE # value: "true" + +podDisruptionBudget: + # -- Enable PodDisruptionBudget for the server pods. + # uses policy/v1/PodDisruptionBudget thus requiring k8s 1.21+ + enabled: false + # -- minimum available instances + minAvailable: 1 + # -- maximum unavailable instances + maxUnavailable: "" From 8ce44c2ae114cfcada91eacc5002c155fcffa9d0 Mon Sep 17 00:00:00 2001 From: chgl Date: Tue, 23 Nov 2021 00:12:26 +0100 Subject: [PATCH 019/200] simplified chart release workflow the Ubuntu runner base image already includes Helm 3.7.0 --- .github/workflows/chart-release.yaml | 5 ----- .github/workflows/chart-test.yaml | 5 ----- .../hapi-fhir-jpaserver/templates/poddisruptionbudget.yaml | 1 - 3 files changed, 11 deletions(-) diff --git a/.github/workflows/chart-release.yaml b/.github/workflows/chart-release.yaml index 5d30c2f47e7..ae2045ceb05 100644 --- a/.github/workflows/chart-release.yaml +++ b/.github/workflows/chart-release.yaml @@ -21,11 +21,6 @@ jobs: git config user.name "$GITHUB_ACTOR" git config user.email "$GITHUB_ACTOR@users.noreply.github.com" - - name: Install Helm - uses: azure/setup-helm@v1 - with: - version: v3.7.0 - - name: Add bitnami repo run: helm repo add bitnami https://charts.bitnami.com/bitnami diff --git a/.github/workflows/chart-test.yaml b/.github/workflows/chart-test.yaml index 90bb32a8208..b3eb576bf83 100644 --- a/.github/workflows/chart-test.yaml +++ b/.github/workflows/chart-test.yaml @@ -43,11 +43,6 @@ jobs: with: fetch-depth: 0 - - name: Set up Helm - uses: azure/setup-helm@v1 - with: - version: v3.7.0 - - name: Set up chart-testing uses: helm/chart-testing-action@v2.1.0 diff --git a/charts/hapi-fhir-jpaserver/templates/poddisruptionbudget.yaml b/charts/hapi-fhir-jpaserver/templates/poddisruptionbudget.yaml index 8c35cdd9019..bae8dad8a9a 100644 --- a/charts/hapi-fhir-jpaserver/templates/poddisruptionbudget.yaml +++ b/charts/hapi-fhir-jpaserver/templates/poddisruptionbudget.yaml @@ -1,4 +1,3 @@ - {{- if .Values.podDisruptionBudget.enabled }} kind: PodDisruptionBudget apiVersion: policy/v1 From 6092200db727123afdde4b4422b16f5d7f3624a5 Mon Sep 17 00:00:00 2001 From: ppalacin Date: Thu, 25 Nov 2021 12:00:23 +0100 Subject: [PATCH 020/200] Support HTTPS --- .../jpa/starter/FhirServerConfigDstu3.java | 28 +++++++++--------- .../fhir/jpa/starter/FhirServerConfigR4.java | 29 +++++++++---------- .../fhir/jpa/starter/FhirServerConfigR5.java | 12 ++++---- 3 files changed, 34 insertions(+), 35 deletions(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java index 84822ddd3f4..9f80a5ef7e7 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java @@ -89,20 +89,20 @@ public JpaTransactionManager hapiTransactionManager(EntityManagerFactory entityM @Bean() public ElasticsearchSvcImpl elasticsearchSvc() { - if (EnvironmentHelper.isElasticsearchEnabled(configurableEnvironment)) { - String elasticsearchUrl = EnvironmentHelper.getElasticsearchServerUrl(configurableEnvironment); - String elasticsearchHost; - if (elasticsearchUrl.startsWith("http")) { - elasticsearchHost = elasticsearchUrl.substring(elasticsearchUrl.indexOf("://") + 3, elasticsearchUrl.lastIndexOf(":")); - } else { - elasticsearchHost = elasticsearchUrl.substring(0, elasticsearchUrl.indexOf(":")); - } - String elasticsearchUsername = EnvironmentHelper.getElasticsearchServerUsername(configurableEnvironment); - String elasticsearchPassword = EnvironmentHelper.getElasticsearchServerPassword(configurableEnvironment); - return new ElasticsearchSvcImpl(elasticsearchHost, elasticsearchUsername, elasticsearchPassword); - } else { - return null; - } + if (Boolean.TRUE.equals(EnvironmentHelper.isElasticsearchEnabled(configurableEnvironment))) { + String elasticsearchUrl = EnvironmentHelper.getElasticsearchServerUrl(configurableEnvironment); + String elasticsearchHost = elasticsearchUrl; + String elasticsearchProtocol = EnvironmentHelper.getElasticsearchServerProtocol(configurableEnvironment); + if (elasticsearchUrl.startsWith("http")) { + elasticsearchProtocol = elasticsearchUrl.split("://")[0]; + elasticsearchHost = elasticsearchUrl.split("://")[1]; + } + String elasticsearchUsername = EnvironmentHelper.getElasticsearchServerUsername(configurableEnvironment); + String elasticsearchPassword = EnvironmentHelper.getElasticsearchServerPassword(configurableEnvironment); + return new ElasticsearchSvcImpl(elasticsearchProtocol, elasticsearchHost, elasticsearchUsername, elasticsearchPassword); + } else { + return null; + } } } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java index 842256a26fa..7959645f31e 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java @@ -85,21 +85,20 @@ public JpaTransactionManager hapiTransactionManager(EntityManagerFactory entityM @Bean() public ElasticsearchSvcImpl elasticsearchSvc() { - if (EnvironmentHelper.isElasticsearchEnabled(configurableEnvironment)) { - String elasticsearchUrl = EnvironmentHelper.getElasticsearchServerUrl(configurableEnvironment); - String elasticsearchHost; - if (elasticsearchUrl.startsWith("http")) { - elasticsearchHost = elasticsearchUrl.substring(elasticsearchUrl.indexOf("://") + 3, elasticsearchUrl.lastIndexOf(":")); - } else { - elasticsearchHost = elasticsearchUrl.substring(0, elasticsearchUrl.indexOf(":")); - } - - String elasticsearchUsername = EnvironmentHelper.getElasticsearchServerUsername(configurableEnvironment); - String elasticsearchPassword = EnvironmentHelper.getElasticsearchServerPassword(configurableEnvironment); - return new ElasticsearchSvcImpl(elasticsearchHost, elasticsearchUsername, elasticsearchPassword); - } else { - return null; - } + if (Boolean.TRUE.equals(EnvironmentHelper.isElasticsearchEnabled(configurableEnvironment))) { + String elasticsearchUrl = EnvironmentHelper.getElasticsearchServerUrl(configurableEnvironment); + String elasticsearchHost = elasticsearchUrl; + String elasticsearchProtocol = EnvironmentHelper.getElasticsearchServerProtocol(configurableEnvironment); + if (elasticsearchUrl.startsWith("http")) { + elasticsearchProtocol = elasticsearchUrl.split("://")[0]; + elasticsearchHost = elasticsearchUrl.split("://")[1]; + } + String elasticsearchUsername = EnvironmentHelper.getElasticsearchServerUsername(configurableEnvironment); + String elasticsearchPassword = EnvironmentHelper.getElasticsearchServerPassword(configurableEnvironment); + return new ElasticsearchSvcImpl(elasticsearchProtocol, elasticsearchHost, elasticsearchUsername, elasticsearchPassword); + } else { + return null; + } } } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java index 3cb696833d9..556bef1ff38 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java @@ -86,17 +86,17 @@ public JpaTransactionManager hapiTransactionManager(EntityManagerFactory entityM @Bean() public ElasticsearchSvcImpl elasticsearchSvc() { - if (EnvironmentHelper.isElasticsearchEnabled(configurableEnvironment)) { + if (Boolean.TRUE.equals(EnvironmentHelper.isElasticsearchEnabled(configurableEnvironment))) { String elasticsearchUrl = EnvironmentHelper.getElasticsearchServerUrl(configurableEnvironment); String elasticsearchHost; + String elasticsearchProtocol = EnvironmentHelper.getElasticsearchServerProtocol(configurableEnvironment); if (elasticsearchUrl.startsWith("http")) { - elasticsearchHost = elasticsearchUrl.substring(elasticsearchUrl.indexOf("://") + 3, elasticsearchUrl.lastIndexOf(":")); - } else { - elasticsearchHost = elasticsearchUrl.substring(0, elasticsearchUrl.indexOf(":")); - } + elasticsearchProtocol = elasticsearchUrl.split("://")[0]; + elasticsearchHost = elasticsearchUrl.split("://")[1]; + } String elasticsearchUsername = EnvironmentHelper.getElasticsearchServerUsername(configurableEnvironment); String elasticsearchPassword = EnvironmentHelper.getElasticsearchServerPassword(configurableEnvironment); - return new ElasticsearchSvcImpl(elasticsearchHost, elasticsearchUsername, elasticsearchPassword); + return new ElasticsearchSvcImpl(elasticsearchProtocol, elasticsearchHost, elasticsearchUsername, elasticsearchPassword); } else { return null; } From ff8302a04e09176777fae9e44cf62ca6fd224f43 Mon Sep 17 00:00:00 2001 From: ppalacin Date: Thu, 25 Nov 2021 12:20:04 +0100 Subject: [PATCH 021/200] Use default application.yaml --- src/main/resources/application.yaml | 176 ++++++++++++++-------------- 1 file changed, 88 insertions(+), 88 deletions(-) diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index bca18601146..b1ec01c0a97 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -17,20 +17,20 @@ spring: properties: hibernate.format_sql: false hibernate.show_sql: false -# hibernate.dialect: org.hibernate.dialect.h2dialect -# hibernate.hbm2ddl.auto: update -# hibernate.jdbc.batch_size: 20 -# hibernate.cache.use_query_cache: false -# hibernate.cache.use_second_level_cache: false -# hibernate.cache.use_structured_entries: false -# hibernate.cache.use_minimal_puts: false -### These settings will enable fulltext search with lucene -# hibernate.search.enabled: true -# hibernate.search.backend.type: lucene -# hibernate.search.backend.analysis.configurer: ca.uhn.fhir.jpa.search.HapiLuceneAnalysisConfigurer -# hibernate.search.backend.directory.type: local-filesystem -# hibernate.search.backend.directory.root: target/lucenefiles -# hibernate.search.backend.lucene_version: lucene_current + # hibernate.dialect: org.hibernate.dialect.h2dialect + # hibernate.hbm2ddl.auto: update + # hibernate.jdbc.batch_size: 20 + # hibernate.cache.use_query_cache: false + # hibernate.cache.use_second_level_cache: false + # hibernate.cache.use_structured_entries: false + # hibernate.cache.use_minimal_puts: false + ### These settings will enable fulltext search with lucene + # hibernate.search.enabled: true + # hibernate.search.backend.type: lucene + # hibernate.search.backend.analysis.configurer: ca.uhn.fhir.jpa.search.HapiLuceneAnalysisConfigurer + # hibernate.search.backend.directory.type: local-filesystem + # hibernate.search.backend.directory.root: target/lucenefiles + # hibernate.search.backend.lucene_version: lucene_current batch: job: enabled: false @@ -40,57 +40,57 @@ hapi: openapi_enabled: true ### This is the FHIR version. Choose between, DSTU2, DSTU3, R4 or R5 fhir_version: R4 -### enable to use the ApacheProxyAddressStrategy which uses X-Forwarded-* headers -### to determine the FHIR server address -# use_apache_address_strategy: false -### forces the use of the https:// protocol for the returned server address. -### alternatively, it may be set using the X-Forwarded-Proto header. -# use_apache_address_strategy_https: false -### enable to set the Server URL -# server_address: http://hapi.fhir.org/baseR4 -# defer_indexing_for_codesystems_of_size: 101 -# install_transitive_ig_dependencies: true -# implementationguides: -### example from registry (packages.fhir.org) -# swiss: -# name: swiss.mednet.fhir -# version: 0.8.0 -# example not from registry -# ips_1_0_0: -# url: https://build.fhir.org/ig/HL7/fhir-ips/package.tgz -# name: hl7.fhir.uv.ips -# version: 1.0.0 -# supported_resource_types: -# - Patient -# - Observation -# allow_cascading_deletes: true -# allow_contains_searches: true -# allow_external_references: true -# allow_multiple_delete: true -# allow_override_default_search_params: true -# auto_create_placeholder_reference_targets: false -# cql_enabled: true -# default_encoding: JSON -# default_pretty_print: true -# default_page_size: 20 -# delete_expunge_enabled: true -# enable_repository_validating_interceptor: false -# enable_index_missing_fields: false -# enable_index_contained_resource: false -# enforce_referential_integrity_on_delete: false -# enforce_referential_integrity_on_write: false -# etag_support_enabled: true -# expunge_enabled: true -# daoconfig_client_id_strategy: null -# client_id_strategy: ALPHANUMERIC -# fhirpath_interceptor_enabled: false -# filter_search_enabled: true -# graphql_enabled: true -# narrative_enabled: true -# mdm_enabled: true -# partitioning: -# allow_references_across_partitions: false -# partitioning_include_in_search_hashes: false + ### enable to use the ApacheProxyAddressStrategy which uses X-Forwarded-* headers + ### to determine the FHIR server address + # use_apache_address_strategy: false + ### forces the use of the https:// protocol for the returned server address. + ### alternatively, it may be set using the X-Forwarded-Proto header. + # use_apache_address_strategy_https: false + ### enable to set the Server URL + # server_address: http://hapi.fhir.org/baseR4 + # defer_indexing_for_codesystems_of_size: 101 + # install_transitive_ig_dependencies: true + # implementationguides: + ### example from registry (packages.fhir.org) + # swiss: + # name: swiss.mednet.fhir + # version: 0.8.0 + # example not from registry + # ips_1_0_0: + # url: https://build.fhir.org/ig/HL7/fhir-ips/package.tgz + # name: hl7.fhir.uv.ips + # version: 1.0.0 + # supported_resource_types: + # - Patient + # - Observation + # allow_cascading_deletes: true + # allow_contains_searches: true + # allow_external_references: true + # allow_multiple_delete: true + # allow_override_default_search_params: true + # auto_create_placeholder_reference_targets: false + # cql_enabled: true + # default_encoding: JSON + # default_pretty_print: true + # default_page_size: 20 + # delete_expunge_enabled: true + # enable_repository_validating_interceptor: false + # enable_index_missing_fields: false + # enable_index_contained_resource: false + # enforce_referential_integrity_on_delete: false + # enforce_referential_integrity_on_write: false + # etag_support_enabled: true + # expunge_enabled: true + # daoconfig_client_id_strategy: null + # client_id_strategy: ALPHANUMERIC + # fhirpath_interceptor_enabled: false + # filter_search_enabled: true + # graphql_enabled: true + # narrative_enabled: true + # mdm_enabled: true + # partitioning: + # allow_references_across_partitions: false + # partitioning_include_in_search_hashes: false cors: allow_Credentials: true # These are allowed_origin patterns, see: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/cors/CorsConfiguration.html#setAllowedOriginPatterns-java.util.List- @@ -102,30 +102,30 @@ hapi: search-coord-max-pool-size: 100 search-coord-queue-capacity: 200 -# logger: -# error_format: 'ERROR - ${requestVerb} ${requestUrl}' -# format: >- -# Path[${servletPath}] Source[${requestHeader.x-forwarded-for}] -# Operation[${operationType} ${operationName} ${idOrResourceName}] -# UA[${requestHeader.user-agent}] Params[${requestParameters}] -# ResponseEncoding[${responseEncodingNoDefault}] -# log_exceptions: true -# name: fhirtest.access -# max_binary_size: 104857600 -# max_page_size: 200 -# retain_cached_searches_mins: 60 -# reuse_cached_search_results_millis: 60000 + # logger: + # error_format: 'ERROR - ${requestVerb} ${requestUrl}' + # format: >- + # Path[${servletPath}] Source[${requestHeader.x-forwarded-for}] + # Operation[${operationType} ${operationName} ${idOrResourceName}] + # UA[${requestHeader.user-agent}] Params[${requestParameters}] + # ResponseEncoding[${responseEncodingNoDefault}] + # log_exceptions: true + # name: fhirtest.access + # max_binary_size: 104857600 + # max_page_size: 200 + # retain_cached_searches_mins: 60 + # reuse_cached_search_results_millis: 60000 tester: - home: - name: Local Tester - server_address: 'http://localhost:8080/fhir' - refuse_to_fetch_third_party_urls: false - fhir_version: R4 - global: - name: Global Tester - server_address: "http://hapi.fhir.org/baseR4" - refuse_to_fetch_third_party_urls: false - fhir_version: R4 + home: + name: Local Tester + server_address: 'http://localhost:8080/fhir' + refuse_to_fetch_third_party_urls: false + fhir_version: R4 + global: + name: Global Tester + server_address: "http://hapi.fhir.org/baseR4" + refuse_to_fetch_third_party_urls: false + fhir_version: R4 # validation: # requests_enabled: true # responses_enabled: true From e077091db42555d1e588d20fdbc34a0781ebd643 Mon Sep 17 00:00:00 2001 From: Ally Shaban Date: Wed, 1 Dec 2021 07:58:43 +0300 Subject: [PATCH 022/200] registering ValueSetOperationProvider --- .../java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java index b58c41cb20d..ff533059e24 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java @@ -22,6 +22,7 @@ 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.ValueSetOperationProvider; import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; import ca.uhn.fhir.jpa.subscription.util.SubscriptionDebugLogInterceptor; import ca.uhn.fhir.mdm.provider.MdmProviderLoader; @@ -92,6 +93,8 @@ public class BaseJpaRestfulServer extends RestfulServer { @Autowired PartitionManagementProvider partitionManagementProvider; @Autowired + ValueSetOperationProvider valueSetOperationProvider; + @Autowired BinaryStorageInterceptor binaryStorageInterceptor; @Autowired IPackageInstallerSvc packageInstallerSvc; @@ -367,6 +370,9 @@ protected void initialize() throws ServletException { registerProvider(bulkDataExportProvider); } + // valueSet Operations i.e $expand + registerProvider(valueSetOperationProvider); + // Partitioning if (appProperties.getPartitioning() != null) { registerInterceptor(new RequestTenantPartitionInterceptor()); From 8c6a1a713e85833ba180388c97c1c9e004fa76ac Mon Sep 17 00:00:00 2001 From: Michael Buckley Date: Thu, 9 Dec 2021 14:54:20 -0500 Subject: [PATCH 023/200] Upgrade to 5.7.0-PRE4-SNAPSHOT for testing --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bb5986d73c5..2c7ebb6c92f 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.6.0 + 5.7.0-PRE4-SNAPSHOT hapi-fhir-jpaserver-starter From bfa6ed1c1ec2348fe7cf853f7549c6aa565595ed Mon Sep 17 00:00:00 2001 From: Michael Buckley Date: Thu, 9 Dec 2021 16:14:13 -0500 Subject: [PATCH 024/200] Share elasticsearch configuration --- .../jpa/starter/BaseJpaRestfulServer.java | 2 +- .../fhir/jpa/starter/ElasticsearchConfig.java | 33 +++++++++++++++++++ .../jpa/starter/FhirServerConfigDstu3.java | 19 +---------- .../fhir/jpa/starter/FhirServerConfigR4.java | 19 +---------- .../fhir/jpa/starter/FhirServerConfigR5.java | 23 ++----------- 5 files changed, 39 insertions(+), 57 deletions(-) create mode 100644 src/main/java/ca/uhn/fhir/jpa/starter/ElasticsearchConfig.java diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java index 736c3daa80e..e272c981152 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java @@ -11,11 +11,11 @@ import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao; import ca.uhn.fhir.jpa.binstore.BinaryStorageInterceptor; import ca.uhn.fhir.jpa.bulk.export.provider.BulkDataExportProvider; +import ca.uhn.fhir.jpa.graphql.GraphQLProvider; import ca.uhn.fhir.jpa.interceptor.CascadingDeleteInterceptor; 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.GraphQLProvider; import ca.uhn.fhir.jpa.provider.IJpaSystemProvider; import ca.uhn.fhir.jpa.provider.JpaCapabilityStatementProvider; import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu2; diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/ElasticsearchConfig.java b/src/main/java/ca/uhn/fhir/jpa/starter/ElasticsearchConfig.java new file mode 100644 index 00000000000..fd27e84ee36 --- /dev/null +++ b/src/main/java/ca/uhn/fhir/jpa/starter/ElasticsearchConfig.java @@ -0,0 +1,33 @@ +package ca.uhn.fhir.jpa.starter; + +import ca.uhn.fhir.jpa.search.lastn.ElasticsearchSvcImpl; +import org.springframework.beans.factory.annotation.Autowired; +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); + + @Autowired + private ConfigurableEnvironment configurableEnvironment; + + @Bean() + public ElasticsearchSvcImpl elasticsearchSvc() { + 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; + } + } +} diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java index 0baa646bf85..074d5b74ee9 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java @@ -3,7 +3,6 @@ import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.jpa.config.BaseJavaConfigDstu3; import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; -import ca.uhn.fhir.jpa.search.lastn.ElasticsearchSvcImpl; import ca.uhn.fhir.jpa.starter.annotations.OnDSTU3Condition; import ca.uhn.fhir.jpa.starter.cql.StarterCqlDstu3Config; import javax.annotation.PostConstruct; @@ -22,7 +21,7 @@ @Configuration @Conditional(OnDSTU3Condition.class) -@Import(StarterCqlDstu3Config.class) +@Import({StarterCqlDstu3Config.class, ElasticsearchConfig.class}) public class FhirServerConfigDstu3 extends BaseJavaConfigDstu3 { @Autowired @@ -87,20 +86,4 @@ public JpaTransactionManager hapiTransactionManager(EntityManagerFactory entityM retVal.setEntityManagerFactory(entityManagerFactory); return retVal; } - - @Bean() - public ElasticsearchSvcImpl elasticsearchSvc() { - if (EnvironmentHelper.isElasticsearchEnabled(configurableEnvironment)) { - String elasticsearchUrl = EnvironmentHelper.getElasticsearchServerUrl(configurableEnvironment); - if (elasticsearchUrl.startsWith("http")) { - elasticsearchUrl =elasticsearchUrl.substring(elasticsearchUrl.indexOf("://") + 3); - } - String elasticsearchUsername = EnvironmentHelper.getElasticsearchServerUsername(configurableEnvironment); - String elasticsearchPassword = EnvironmentHelper.getElasticsearchServerPassword(configurableEnvironment); - return new ElasticsearchSvcImpl(elasticsearchUrl, elasticsearchUsername, elasticsearchPassword); - } else { - return null; - } - } - } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java index 4148bd46b73..c37d4468d7f 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java @@ -3,7 +3,6 @@ import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.jpa.config.BaseJavaConfigR4; import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; -import ca.uhn.fhir.jpa.search.lastn.ElasticsearchSvcImpl; import ca.uhn.fhir.jpa.starter.annotations.OnR4Condition; import ca.uhn.fhir.jpa.starter.cql.StarterCqlR4Config; import org.springframework.beans.factory.annotation.Autowired; @@ -19,7 +18,7 @@ @Configuration @Conditional(OnR4Condition.class) -@Import(StarterCqlR4Config.class) +@Import({StarterCqlR4Config.class, ElasticsearchConfig.class}) public class FhirServerConfigR4 extends BaseJavaConfigR4 { @Autowired @@ -83,20 +82,4 @@ public JpaTransactionManager hapiTransactionManager(EntityManagerFactory entityM return retVal; } - @Bean() - public ElasticsearchSvcImpl elasticsearchSvc() { - if (EnvironmentHelper.isElasticsearchEnabled(configurableEnvironment)) { - String elasticsearchUrl = EnvironmentHelper.getElasticsearchServerUrl(configurableEnvironment); - if (elasticsearchUrl.startsWith("http")) { - elasticsearchUrl =elasticsearchUrl.substring(elasticsearchUrl.indexOf("://") + 3); - } - - String elasticsearchUsername = EnvironmentHelper.getElasticsearchServerUsername(configurableEnvironment); - String elasticsearchPassword = EnvironmentHelper.getElasticsearchServerPassword(configurableEnvironment); - return new ElasticsearchSvcImpl(elasticsearchUrl, elasticsearchUsername, elasticsearchPassword); - } else { - return null; - } - } - } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java index ccd7074433b..a4305db8888 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java @@ -5,12 +5,10 @@ import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; import ca.uhn.fhir.jpa.search.lastn.ElasticsearchSvcImpl; import ca.uhn.fhir.jpa.starter.annotations.OnR5Condition; +import ca.uhn.fhir.jpa.starter.cql.StarterCqlR4Config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Conditional; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; +import org.springframework.context.annotation.*; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; @@ -21,6 +19,7 @@ @Configuration @Conditional(OnR5Condition.class) +@Import({ElasticsearchConfig.class}) public class FhirServerConfigR5 extends BaseJavaConfigR5 { @Autowired @@ -84,20 +83,4 @@ public JpaTransactionManager hapiTransactionManager(EntityManagerFactory entityM return retVal; } - @Bean() - public ElasticsearchSvcImpl elasticsearchSvc() { - if (EnvironmentHelper.isElasticsearchEnabled(configurableEnvironment)) { - String elasticsearchUrl = EnvironmentHelper.getElasticsearchServerUrl(configurableEnvironment); - if (elasticsearchUrl.startsWith("http")) { - elasticsearchUrl =elasticsearchUrl.substring(elasticsearchUrl.indexOf("://") + 3); - } - String elasticsearchUsername = EnvironmentHelper.getElasticsearchServerUsername(configurableEnvironment); - String elasticsearchPassword = EnvironmentHelper.getElasticsearchServerPassword(configurableEnvironment); - return new ElasticsearchSvcImpl(elasticsearchUrl, elasticsearchUsername, elasticsearchPassword); - } else { - return null; - } - } - - } From 8568132896cdfa60febe99d89ebcc77367b0bf93 Mon Sep 17 00:00:00 2001 From: Jens Kristian Villadsen Date: Sat, 11 Dec 2021 17:08:36 +0100 Subject: [PATCH 025/200] Fixed compile issues --- .../java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java | 5 +++-- .../java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java | 5 +++-- .../java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java | 5 +++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java index 9f80a5ef7e7..c795f971c65 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java @@ -2,6 +2,7 @@ import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.jpa.config.BaseJavaConfigDstu3; +import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; import ca.uhn.fhir.jpa.search.lastn.ElasticsearchSvcImpl; import ca.uhn.fhir.jpa.starter.annotations.OnDSTU3Condition; @@ -88,7 +89,7 @@ public JpaTransactionManager hapiTransactionManager(EntityManagerFactory entityM } @Bean() - public ElasticsearchSvcImpl elasticsearchSvc() { + public ElasticsearchSvcImpl elasticsearchSvc(PartitionSettings thePartitionSetings) { if (Boolean.TRUE.equals(EnvironmentHelper.isElasticsearchEnabled(configurableEnvironment))) { String elasticsearchUrl = EnvironmentHelper.getElasticsearchServerUrl(configurableEnvironment); String elasticsearchHost = elasticsearchUrl; @@ -99,7 +100,7 @@ public ElasticsearchSvcImpl elasticsearchSvc() { } String elasticsearchUsername = EnvironmentHelper.getElasticsearchServerUsername(configurableEnvironment); String elasticsearchPassword = EnvironmentHelper.getElasticsearchServerPassword(configurableEnvironment); - return new ElasticsearchSvcImpl(elasticsearchProtocol, elasticsearchHost, elasticsearchUsername, elasticsearchPassword); + return new ElasticsearchSvcImpl(thePartitionSetings, elasticsearchUrl, elasticsearchUsername, elasticsearchPassword); } else { return null; } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java index 7959645f31e..e49ca4af1e4 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java @@ -2,6 +2,7 @@ import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.jpa.config.BaseJavaConfigR4; +import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; import ca.uhn.fhir.jpa.search.lastn.ElasticsearchSvcImpl; import ca.uhn.fhir.jpa.starter.annotations.OnR4Condition; @@ -84,7 +85,7 @@ public JpaTransactionManager hapiTransactionManager(EntityManagerFactory entityM } @Bean() - public ElasticsearchSvcImpl elasticsearchSvc() { + public ElasticsearchSvcImpl elasticsearchSvc(PartitionSettings thePartitionSetings) { if (Boolean.TRUE.equals(EnvironmentHelper.isElasticsearchEnabled(configurableEnvironment))) { String elasticsearchUrl = EnvironmentHelper.getElasticsearchServerUrl(configurableEnvironment); String elasticsearchHost = elasticsearchUrl; @@ -95,7 +96,7 @@ public ElasticsearchSvcImpl elasticsearchSvc() { } String elasticsearchUsername = EnvironmentHelper.getElasticsearchServerUsername(configurableEnvironment); String elasticsearchPassword = EnvironmentHelper.getElasticsearchServerPassword(configurableEnvironment); - return new ElasticsearchSvcImpl(elasticsearchProtocol, elasticsearchHost, elasticsearchUsername, elasticsearchPassword); + return new ElasticsearchSvcImpl(thePartitionSetings, elasticsearchUrl, elasticsearchUsername, elasticsearchPassword); } else { return null; } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java index 556bef1ff38..2bb392c0315 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java @@ -2,6 +2,7 @@ import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.jpa.config.BaseJavaConfigR5; +import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; import ca.uhn.fhir.jpa.search.lastn.ElasticsearchSvcImpl; import ca.uhn.fhir.jpa.starter.annotations.OnR5Condition; @@ -85,7 +86,7 @@ public JpaTransactionManager hapiTransactionManager(EntityManagerFactory entityM } @Bean() - public ElasticsearchSvcImpl elasticsearchSvc() { + public ElasticsearchSvcImpl elasticsearchSvc(PartitionSettings thePartitionSetings) { if (Boolean.TRUE.equals(EnvironmentHelper.isElasticsearchEnabled(configurableEnvironment))) { String elasticsearchUrl = EnvironmentHelper.getElasticsearchServerUrl(configurableEnvironment); String elasticsearchHost; @@ -96,7 +97,7 @@ public ElasticsearchSvcImpl elasticsearchSvc() { } String elasticsearchUsername = EnvironmentHelper.getElasticsearchServerUsername(configurableEnvironment); String elasticsearchPassword = EnvironmentHelper.getElasticsearchServerPassword(configurableEnvironment); - return new ElasticsearchSvcImpl(elasticsearchProtocol, elasticsearchHost, elasticsearchUsername, elasticsearchPassword); + return new ElasticsearchSvcImpl(thePartitionSetings, elasticsearchUrl, elasticsearchUsername, elasticsearchPassword); } else { return null; } From 21f5d1dcbb74674294715802182199f8f39ec813 Mon Sep 17 00:00:00 2001 From: Michael Buckley Date: Thu, 16 Dec 2021 17:14:31 -0500 Subject: [PATCH 026/200] Fix misconfiguration modelConfig is part of DaoConfig and should not have a separate lifecycle. --- .../ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java | 4 ++-- src/main/resources/application.yaml | 7 ++++--- 2 files changed, 6 insertions(+), 5 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 47e06dd7f6e..80f75f5a719 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java @@ -160,8 +160,8 @@ public HibernatePropertiesProvider jpaStarterDialectProvider(LocalContainerEntit } @Bean - public ModelConfig modelConfig(AppProperties appProperties) { - ModelConfig modelConfig = new ModelConfig(); + 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()); diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 049d8012ee0..d7abaf2eb69 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -1,6 +1,8 @@ spring: flyway: enabled: false + check-location: false + baselineOnMigrate: true datasource: url: 'jdbc:h2:file:./target/database/h2' #url: jdbc:h2:mem:test_mem @@ -12,9 +14,6 @@ spring: # database connection pool size hikari: maximum-pool-size: 10 - flyway: - check-location: false - baselineOnMigrate: true jpa: properties: hibernate.format_sql: false @@ -92,6 +91,8 @@ hapi: # graphql_enabled: true # narrative_enabled: true # mdm_enabled: true +# local_base_urls: +# - https://hapi.fhir.org/baseR4 # partitioning: # allow_references_across_partitions: false # partitioning_include_in_search_hashes: false From 42e3e78bbcb922a3c6f468830c397ab7122adf49 Mon Sep 17 00:00:00 2001 From: Michael Buckley Date: Fri, 17 Dec 2021 18:05:49 -0500 Subject: [PATCH 027/200] Bump to 5.7.0-PRE8-SNAPSHOT --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2c7ebb6c92f..77319080b88 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.7.0-PRE4-SNAPSHOT + 5.7.0-PRE8-SNAPSHOT hapi-fhir-jpaserver-starter From 22e0e1e4bf44bfbb8a5384b6c1d236cb39cdce3f Mon Sep 17 00:00:00 2001 From: Vadim Peretokin Date: Mon, 3 Jan 2022 14:00:34 +0100 Subject: [PATCH 028/200] Typo fix in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bb2dcfb7a8e..57cd00ed017 100644 --- a/README.md +++ b/README.md @@ -368,7 +368,7 @@ using the `gcr.io/distroless/java-debian10:11` base image: docker build --target=release-distroless -t hapi-fhir:distroless . ``` -Note that distroless images are also automatically build and pushed to the container registry, +Note that distroless images are also automatically built and pushed to the container registry, see the `-distroless` suffix in the image tags. ## Adding custom operations From 528d2bc087ae2f5bf49b2fb38d17dba134feda63 Mon Sep 17 00:00:00 2001 From: Jaison B Date: Wed, 5 Jan 2022 15:02:57 -0700 Subject: [PATCH 029/200] Add configuration flag to enable storing of resources in lucene index (#304) * Add configuration flag to enable storing of resources in lucene index * Fix build issue * Fix code review suggestions Co-authored-by: Jaison B --- README.md | 8 ++++++-- .../java/ca/uhn/fhir/jpa/starter/AppProperties.java | 11 ++++++++++- .../uhn/fhir/jpa/starter/BaseJpaRestfulServer.java | 4 ++-- .../uhn/fhir/jpa/starter/FhirServerConfigCommon.java | 12 ++++++------ src/main/resources/application.yaml | 1 + .../uhn/fhir/jpa/starter/ElasticsearchLastNR4IT.java | 1 + 6 files changed, 26 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index bb2dcfb7a8e..8e6a36c3bbc 100644 --- a/README.md +++ b/README.md @@ -354,10 +354,14 @@ elasticsearch.schema_management_strategy=CREATE Set `hapi.fhir.lastn_enabled=true` in the [application.yaml](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/application.yaml) file to enable the $lastn operation on this server. Note that the $lastn operation relies on Elasticsearch, so for $lastn to work, indexing must be enabled using Elasticsearch. +## Enabling Resource to be stored in Lucene Index + +Set `hapi.fhir.store_resource_in_lucene_index_enabled` in the [application.yaml](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/application.yaml) file to enable storing of resource json along with Lucene/Elasticsearch index mappings. + ## Changing cached search results time -It is possible to change the cached search results time. The option `reuse_cached_search_results_millis` in the [application.yaml] is 6000 miliseconds by default. -Set `reuse_cached_search_results_millis: -1` in the [application.yaml] file to ignore the cache time every search. +It is possible to change the cached search results time. The option `reuse_cached_search_results_millis` in the [application.yaml](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/application.yaml) is 6000 miliseconds by default. +Set `reuse_cached_search_results_millis: -1` in the [application.yaml](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/application.yaml) file to ignore the cache time every search. ## Build the distroless variant of the image (for lower footprint and improved security) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java b/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java index 2a7df3811e0..1118df7fd55 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java @@ -66,6 +66,7 @@ public class AppProperties { private Map implementationGuides = null; private Boolean lastn_enabled = false; + private boolean store_resource_in_lucene_index_enabled = false; private NormalizedQuantitySearchLevel normalized_quantity_search_level = NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED; private Integer search_coord_core_pool_size = 20; @@ -469,7 +470,15 @@ public void setLastn_enabled(Boolean lastn_enabled) { this.lastn_enabled = lastn_enabled; } - public NormalizedQuantitySearchLevel getNormalized_quantity_search_level() { + public boolean getStore_resource_in_lucene_index_enabled() { + return store_resource_in_lucene_index_enabled; + } + + public void setStore_resource_in_lucene_index_enabled(Boolean store_resource_in_lucene_index_enabled) { + this.store_resource_in_lucene_index_enabled = store_resource_in_lucene_index_enabled; + } + + public NormalizedQuantitySearchLevel getNormalized_quantity_search_level() { return this.normalized_quantity_search_level; } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java index e272c981152..36ef8efd364 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java @@ -407,8 +407,8 @@ protected void initialize() throws ServletException { daoConfig.setLastNEnabled(true); } + daoConfig.setStoreResourceInLuceneIndex(appProperties.getStore_resource_in_lucene_index_enabled()); daoConfig.getModelConfig().setNormalizedQuantitySearchLevel(appProperties.getNormalized_quantity_search_level()); - - daoConfig.getModelConfig().setIndexOnContainedResources(appProperties.getEnable_index_contained_resource()); + daoConfig.getModelConfig().setIndexOnContainedResources(appProperties.getEnable_index_contained_resource()); } } 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 80f75f5a719..3dcc0b14664 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java @@ -11,6 +11,7 @@ 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.MailConfig; +import ca.uhn.fhir.rest.server.mail.MailSvc; import com.google.common.base.Strings; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.dstu2.model.Subscription; @@ -69,7 +70,7 @@ public FhirServerConfigCommon(AppProperties appProperties) { 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"); } @@ -145,7 +146,7 @@ public PartitionSettings partitionSettings(AppProperties appProperties) { if(appProperties.getPartitioning().getAllow_references_across_partitions()) { retVal.setAllowReferencesAcrossPartitions(CrossPartitionReferenceMode.ALLOWED_UNQUALIFIED); } else { - retVal.setAllowReferencesAcrossPartitions(CrossPartitionReferenceMode.NOT_ALLOWED); + retVal.setAllowReferencesAcrossPartitions(CrossPartitionReferenceMode.NOT_ALLOWED); } } @@ -178,7 +179,7 @@ public ModelConfig modelConfig(AppProperties appProperties, DaoConfig daoConfig) } modelConfig.setNormalizedQuantitySearchLevel(appProperties.getNormalized_quantity_search_level()); - + modelConfig.setIndexOnContainedResources(appProperties.getEnable_index_contained_resource()); return modelConfig; } @@ -225,10 +226,9 @@ public IEmailSender emailSender(AppProperties appProperties, Optional theSubscriptionDeliveryHandlerFactory.setEmailSender(emailSender)); return emailSender; } diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index d7abaf2eb69..17b1dfb4b58 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -154,6 +154,7 @@ hapi: # startTlsRequired: # quitWait: # lastn_enabled: true +# store_resource_in_lucene_index_enabled: true ### This is configuration for normalized quantity serach level default is 0 ### 0: NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED - default ### 1: NORMALIZED_QUANTITY_STORAGE_SUPPORTED diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/ElasticsearchLastNR4IT.java b/src/test/java/ca/uhn/fhir/jpa/starter/ElasticsearchLastNR4IT.java index 24dfada96cb..1387354f75e 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/ElasticsearchLastNR4IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ElasticsearchLastNR4IT.java @@ -42,6 +42,7 @@ "spring.datasource.url=jdbc:h2:mem:dbr4", "hapi.fhir.fhir_version=r4", "hapi.fhir.lastn_enabled=true", + "hapi.fhir.store_resource_in_lucene_index_enabled=true", "elasticsearch.enabled=true", // Because the port is set randomly, we will set the rest_url using the Initializer. // "elasticsearch.rest_url='http://localhost:9200'", From 5312f78b956d4ad4863580035972263586d616c3 Mon Sep 17 00:00:00 2001 From: Jaison B Date: Mon, 24 Jan 2022 16:21:50 -0700 Subject: [PATCH 030/200] Add ES native aggregation builder for lastN --- pom.xml | 2 +- src/main/resources/application.yaml | 35 ++++++++++++++++------------- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/pom.xml b/pom.xml index 77319080b88..e8e5d4847e2 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.7.0-PRE8-SNAPSHOT + 5.7.0-PRE9-SNAPSHOT hapi-fhir-jpaserver-starter diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 17b1dfb4b58..9313f4fe74d 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -5,7 +5,7 @@ spring: baselineOnMigrate: true datasource: url: 'jdbc:h2:file:./target/database/h2' - #url: jdbc:h2:mem:test_mem +# url: jdbc:h2:mem:test_mem username: sa password: null driverClassName: org.h2.Driver @@ -18,6 +18,7 @@ spring: properties: hibernate.format_sql: false hibernate.show_sql: false + hibernate.dialect: ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect # hibernate.dialect: org.hibernate.dialect.h2dialect # hibernate.hbm2ddl.auto: update # hibernate.jdbc.batch_size: 20 @@ -26,8 +27,12 @@ spring: # hibernate.cache.use_structured_entries: false # hibernate.cache.use_minimal_puts: false ### These settings will enable fulltext search with lucene -# hibernate.search.enabled: true + hibernate.search.enabled: true +# hibernate.search.automatic_indexing.strategy: session +# hibernate.search.automatic_indexing.synchronization.strategy: sync + hibernate.search.backend.type: elasticsearch # hibernate.search.backend.type: lucene + hibernate.search.backedn.analysis.configurer: ca.uhn.fhir.jpa.search.elastic.HapiElasticsearchAnalysisConfigurer # hibernate.search.backend.analysis.configurer: ca.uhn.fhir.jpa.search.HapiLuceneAnalysisConfigurer # hibernate.search.backend.directory.type: local-filesystem # hibernate.search.backend.directory.root: target/lucenefiles @@ -153,21 +158,21 @@ hapi: # startTlsEnable: # startTlsRequired: # quitWait: -# lastn_enabled: true -# store_resource_in_lucene_index_enabled: true + lastn_enabled: true + store_resource_in_lucene_index_enabled: true ### This is configuration for normalized quantity serach level default is 0 ### 0: NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED - default ### 1: NORMALIZED_QUANTITY_STORAGE_SUPPORTED ### 2: NORMALIZED_QUANTITY_SEARCH_SUPPORTED # normalized_quantity_search_level: 2 -#elasticsearch: -# debug: -# pretty_print_json_log: false -# refresh_after_write: false -# enabled: false -# password: SomePassword -# required_index_status: YELLOW -# rest_url: 'localhost:9200' -# protocol: 'http' -# schema_management_strategy: CREATE -# username: SomeUsername +elasticsearch: + debug: + pretty_print_json_log: true + refresh_after_write: false + enabled: true + password: smilecdr + required_index_status: YELLOW + rest_url: 'localhost:19200' + protocol: 'http' + schema_management_strategy: CREATE + username: elastic From d2984d2c0c1a0744624a60bc083035890040897b Mon Sep 17 00:00:00 2001 From: Jaison B Date: Mon, 24 Jan 2022 16:25:24 -0700 Subject: [PATCH 031/200] Revert "Add ES native aggregation builder for lastN" This reverts commit 5312f78b956d4ad4863580035972263586d616c3. --- pom.xml | 2 +- src/main/resources/application.yaml | 35 +++++++++++++---------------- 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/pom.xml b/pom.xml index e8e5d4847e2..77319080b88 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.7.0-PRE9-SNAPSHOT + 5.7.0-PRE8-SNAPSHOT hapi-fhir-jpaserver-starter diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 9313f4fe74d..17b1dfb4b58 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -5,7 +5,7 @@ spring: baselineOnMigrate: true datasource: url: 'jdbc:h2:file:./target/database/h2' -# url: jdbc:h2:mem:test_mem + #url: jdbc:h2:mem:test_mem username: sa password: null driverClassName: org.h2.Driver @@ -18,7 +18,6 @@ spring: properties: hibernate.format_sql: false hibernate.show_sql: false - hibernate.dialect: ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect # hibernate.dialect: org.hibernate.dialect.h2dialect # hibernate.hbm2ddl.auto: update # hibernate.jdbc.batch_size: 20 @@ -27,12 +26,8 @@ spring: # hibernate.cache.use_structured_entries: false # hibernate.cache.use_minimal_puts: false ### These settings will enable fulltext search with lucene - hibernate.search.enabled: true -# hibernate.search.automatic_indexing.strategy: session -# hibernate.search.automatic_indexing.synchronization.strategy: sync - hibernate.search.backend.type: elasticsearch +# hibernate.search.enabled: true # hibernate.search.backend.type: lucene - hibernate.search.backedn.analysis.configurer: ca.uhn.fhir.jpa.search.elastic.HapiElasticsearchAnalysisConfigurer # hibernate.search.backend.analysis.configurer: ca.uhn.fhir.jpa.search.HapiLuceneAnalysisConfigurer # hibernate.search.backend.directory.type: local-filesystem # hibernate.search.backend.directory.root: target/lucenefiles @@ -158,21 +153,21 @@ hapi: # startTlsEnable: # startTlsRequired: # quitWait: - lastn_enabled: true - store_resource_in_lucene_index_enabled: true +# lastn_enabled: true +# store_resource_in_lucene_index_enabled: true ### This is configuration for normalized quantity serach level default is 0 ### 0: NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED - default ### 1: NORMALIZED_QUANTITY_STORAGE_SUPPORTED ### 2: NORMALIZED_QUANTITY_SEARCH_SUPPORTED # normalized_quantity_search_level: 2 -elasticsearch: - debug: - pretty_print_json_log: true - refresh_after_write: false - enabled: true - password: smilecdr - required_index_status: YELLOW - rest_url: 'localhost:19200' - protocol: 'http' - schema_management_strategy: CREATE - username: elastic +#elasticsearch: +# debug: +# pretty_print_json_log: false +# refresh_after_write: false +# enabled: false +# password: SomePassword +# required_index_status: YELLOW +# rest_url: 'localhost:9200' +# protocol: 'http' +# schema_management_strategy: CREATE +# username: SomeUsername From 0f133274a1fd43e09abf7559962e6196ddc606a9 Mon Sep 17 00:00:00 2001 From: Tadgh Date: Wed, 26 Jan 2022 16:31:41 -0800 Subject: [PATCH 032/200] bump version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 338e9c9fda2..cd08e13c289 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.6.0 + 5.7.0-PRE9-SNAPSHOT hapi-fhir-jpaserver-starter From bbd9428d6e32d75ca55203532bf9565a5f23bd83 Mon Sep 17 00:00:00 2001 From: Tadgh Date: Wed, 26 Jan 2022 18:45:45 -0800 Subject: [PATCH 033/200] Update for 5.7.x changes --- .../java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java | 2 +- .../ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java | 6 ++++-- .../java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java | 5 ++++- .../java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java | 5 ++++- .../java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java | 5 ++++- 5 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java index ff533059e24..3e6174331a8 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java @@ -11,11 +11,11 @@ import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao; import ca.uhn.fhir.jpa.binstore.BinaryStorageInterceptor; import ca.uhn.fhir.jpa.bulk.export.provider.BulkDataExportProvider; +import ca.uhn.fhir.jpa.graphql.GraphQLProvider; import ca.uhn.fhir.jpa.interceptor.CascadingDeleteInterceptor; 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.GraphQLProvider; import ca.uhn.fhir.jpa.provider.IJpaSystemProvider; import ca.uhn.fhir.jpa.provider.JpaCapabilityStatementProvider; import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu2; 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 1e296d9bd3b..b055252ccbd 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java @@ -10,7 +10,9 @@ 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.dstu2.model.Subscription; import org.springframework.boot.env.YamlPropertySourceLoader; @@ -217,9 +219,9 @@ public IEmailSender emailSender(AppProperties appProperties, Optional Date: Wed, 26 Jan 2022 20:28:05 -0800 Subject: [PATCH 034/200] Fix h2 dialect, replace mail dep --- pom.xml | 24 +++++++++++-------- .../uhn/fhir/jpa/starter/mdm/MdmConfig.java | 6 ----- src/main/resources/application.yaml | 4 ++-- 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/pom.xml b/pom.xml index cd08e13c289..329070b8cbb 100644 --- a/pom.xml +++ b/pom.xml @@ -67,16 +67,20 @@ - - com.sun.mail - javax.mail - 1.6.2 - - - javax.activation - activation - - + + + + + + + + + + + + + org.simplejavamail + simple-java-mail diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/mdm/MdmConfig.java b/src/main/java/ca/uhn/fhir/jpa/starter/mdm/MdmConfig.java index 301c8833150..39718f6c9ad 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/mdm/MdmConfig.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/mdm/MdmConfig.java @@ -24,11 +24,6 @@ @Import({MdmConsumerConfig.class, MdmSubmitterConfig.class}) public class MdmConfig { - @Bean - MdmRuleValidator mdmRuleValidator(FhirContext theFhirContext, ISearchParamRegistry theSearchParamRegistry) { - return new MdmRuleValidator(theFhirContext, theSearchParamRegistry); - } - @Bean IMdmSettings mdmSettings(@Autowired MdmRuleValidator theMdmRuleValidator, AppProperties appProperties) throws IOException { DefaultResourceLoader resourceLoader = new DefaultResourceLoader(); @@ -36,5 +31,4 @@ IMdmSettings mdmSettings(@Autowired MdmRuleValidator theMdmRuleValidator, AppPro String json = IOUtils.toString(resource.getInputStream(), Charsets.UTF_8); return new MdmSettings(theMdmRuleValidator).setEnabled(appProperties.getMdm_enabled()).setScriptText(json); } - } diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index b1ec01c0a97..1ad52569f90 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -17,7 +17,7 @@ spring: properties: hibernate.format_sql: false hibernate.show_sql: false - # hibernate.dialect: org.hibernate.dialect.h2dialect + hibernate.dialect: ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect # hibernate.hbm2ddl.auto: update # hibernate.jdbc.batch_size: 20 # hibernate.cache.use_query_cache: false @@ -87,7 +87,7 @@ hapi: # filter_search_enabled: true # graphql_enabled: true # narrative_enabled: true - # mdm_enabled: true + mdm_enabled: true # partitioning: # allow_references_across_partitions: false # partitioning_include_in_search_hashes: false From 4952c00df7d878660d4bd64b4616ef1c140abd4a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Feb 2022 00:59:55 +0000 Subject: [PATCH 035/200] Bump postgresql from 42.2.23 to 42.2.25 Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.23 to 42.2.25. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.23...REL42.2.25) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e8e5d4847e2..d9a0387bc15 100644 --- a/pom.xml +++ b/pom.xml @@ -63,7 +63,7 @@ org.postgresql postgresql - 42.2.23 + 42.2.25 From eac87fca9b1864bfcbd315bc5b25d1fdd1f24972 Mon Sep 17 00:00:00 2001 From: Michael Buckley Date: Wed, 2 Feb 2022 10:35:41 -0500 Subject: [PATCH 036/200] bump Hapi PRE version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 77319080b88..62cb82356d6 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.7.0-PRE8-SNAPSHOT + 5.7.0-PRE10-SNAPSHOT hapi-fhir-jpaserver-starter From 56f43324fd816df95610c9ffb60b4cc2b52f1c0a Mon Sep 17 00:00:00 2001 From: Michael Buckley Date: Thu, 3 Feb 2022 13:25:22 -0500 Subject: [PATCH 037/200] Bump to Hapi 6.0-SNAPSHOT and register the ValueSet provider. --- pom.xml | 2 +- .../ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/pom.xml b/pom.xml index 62cb82356d6..7a12c7686ee 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.7.0-PRE10-SNAPSHOT + 6.0.0-PRE1-SNAPSHOT hapi-fhir-jpaserver-starter diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java index 36ef8efd364..ca6a3622f5f 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java @@ -16,11 +16,7 @@ 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.IJpaSystemProvider; -import ca.uhn.fhir.jpa.provider.JpaCapabilityStatementProvider; -import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu2; -import ca.uhn.fhir.jpa.provider.SubscriptionTriggeringProvider; -import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider; +import ca.uhn.fhir.jpa.provider.*; import ca.uhn.fhir.jpa.provider.dstu3.JpaConformanceProviderDstu3; import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; import ca.uhn.fhir.jpa.subscription.util.SubscriptionDebugLogInterceptor; @@ -78,6 +74,8 @@ public class BaseJpaRestfulServer extends RestfulServer { @Autowired IJpaSystemProvider jpaSystemProvider; @Autowired + ValueSetOperationProvider myValueSetOperationProvider; + @Autowired IInterceptorBroadcaster interceptorBroadcaster; @Autowired DatabaseBackedPagingProvider databaseBackedPagingProvider; @@ -141,7 +139,7 @@ protected void initialize() throws ServletException { registerProviders(resourceProviderFactory.createProviders()); registerProvider(jpaSystemProvider); - + registerProvider(myValueSetOperationProvider); /* * 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 From e45ba6ce5b1c56a7b5803c96e811d6eefe1b141d Mon Sep 17 00:00:00 2001 From: Jens Kristian Villadsen Date: Thu, 17 Feb 2022 23:12:10 +0100 Subject: [PATCH 038/200] Version bump to 5.7.0 and a few other components now draw the version from parent --- pom.xml | 12 ++++-------- src/main/resources/application.yaml | 6 +++++- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pom.xml b/pom.xml index d9a0387bc15..69aef165fbf 100644 --- a/pom.xml +++ b/pom.xml @@ -14,18 +14,17 @@ ca.uhn.hapi.fhir hapi-fhir - 5.7.0-PRE9-SNAPSHOT + 5.7.0 hapi-fhir-jpaserver-starter 8 - 2.5.6 - 3.6.3 + 3.8.3 war @@ -57,13 +56,10 @@ mysql mysql-connector-java - 8.0.25 - org.postgresql postgresql - 42.2.25 @@ -164,7 +160,7 @@ org.yaml snakeyaml - 1.29 + 1.30 @@ -197,7 +193,7 @@ org.webjars bootstrap - 3.4.1 + 5.1.3 org.webjars diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 03abf0bdcb7..cc298f4cc5d 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -18,7 +18,11 @@ spring: properties: hibernate.format_sql: false hibernate.show_sql: false - # hibernate.dialect: org.hibernate.dialect.h2dialect + #Hibernate dialect is automatically detected except Postgres and H2. + #If using H2, then supply the value of ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect + #If using postgres, then supply the value of ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgres94Dialect + + hibernate.dialect: ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect # hibernate.hbm2ddl.auto: update # hibernate.jdbc.batch_size: 20 # hibernate.cache.use_query_cache: false From 3fd880399c81c27094617877483ea6a6b9e09c0b Mon Sep 17 00:00:00 2001 From: Jens Kristian Villadsen Date: Fri, 18 Feb 2022 01:03:49 +0100 Subject: [PATCH 039/200] Update application.yaml Momentarily added `allow-circular-references: true` --- src/main/resources/application.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index cc298f4cc5d..73c5418ee32 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -1,4 +1,6 @@ spring: + main: + allow-circular-references: true flyway: enabled: false check-location: false From fa8999bd9b0c4a6e91e397838841d0cd9d180017 Mon Sep 17 00:00:00 2001 From: Tadgh Date: Thu, 17 Feb 2022 16:11:57 -0800 Subject: [PATCH 040/200] bump to real version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 329070b8cbb..f8e4aac8731 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.7.0-PRE9-SNAPSHOT + 5.7.0 hapi-fhir-jpaserver-starter From 94f47f4736df7293c44336462139ed17dee947d4 Mon Sep 17 00:00:00 2001 From: Tadgh Date: Mon, 21 Feb 2022 16:13:35 -0800 Subject: [PATCH 041/200] Bump version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 69aef165fbf..a08affeef25 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.7.0 + 6.0.0-PRE3-SNAPSHOT hapi-fhir-jpaserver-starter From 0e4926e59bd3aaef09a424fc709f1077cc05dd8c Mon Sep 17 00:00:00 2001 From: Tadgh Date: Mon, 21 Feb 2022 16:24:08 -0800 Subject: [PATCH 042/200] Remove search coord thread pool --- .../uhn/fhir/jpa/starter/AppProperties.java | 21 ------------------- .../jpa/starter/FhirServerConfigDstu2.java | 12 ----------- .../jpa/starter/FhirServerConfigDstu3.java | 13 ------------ .../fhir/jpa/starter/FhirServerConfigR4.java | 15 ++----------- .../fhir/jpa/starter/FhirServerConfigR5.java | 13 ------------ 5 files changed, 2 insertions(+), 72 deletions(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java b/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java index 1118df7fd55..b3534081bbd 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java @@ -69,9 +69,6 @@ public class AppProperties { private boolean store_resource_in_lucene_index_enabled = false; private NormalizedQuantitySearchLevel normalized_quantity_search_level = NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED; - private Integer search_coord_core_pool_size = 20; - private Integer search_coord_max_pool_size = 100; - private Integer search_coord_queue_capacity = 200; private Boolean use_apache_address_strategy = false; private Boolean use_apache_address_strategy_https = false; @@ -486,24 +483,6 @@ public void setNormalized_quantity_search_level(NormalizedQuantitySearchLevel no this.normalized_quantity_search_level = normalized_quantity_search_level; } - public Integer getSearch_coord_core_pool_size() { return search_coord_core_pool_size; } - - public void setSearch_coord_core_pool_size(Integer search_coord_core_pool_size) { - this.search_coord_core_pool_size = search_coord_core_pool_size; - } - - public Integer getSearch_coord_max_pool_size() { return search_coord_max_pool_size; } - - public void setSearch_coord_max_pool_size(Integer search_coord_max_pool_size) { - this.search_coord_max_pool_size = search_coord_max_pool_size; - } - - public Integer getSearch_coord_queue_capacity() { return search_coord_queue_capacity; } - - public void setSearch_coord_queue_capacity(Integer search_coord_queue_capacity) { - this.search_coord_queue_capacity = search_coord_queue_capacity; - } - public boolean getInstall_transitive_ig_dependencies() { return install_transitive_ig_dependencies; } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu2.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu2.java index 25548bf3f51..e01d6564113 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu2.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu2.java @@ -33,18 +33,6 @@ public class FhirServerConfigDstu2 extends BaseJavaConfigDstu2 { @Autowired AppProperties appProperties; - @PostConstruct - public void initSettings() { - if(appProperties.getSearch_coord_core_pool_size() != null) { - setSearchCoordCorePoolSize(appProperties.getSearch_coord_core_pool_size()); - } - if(appProperties.getSearch_coord_max_pool_size() != null) { - setSearchCoordMaxPoolSize(appProperties.getSearch_coord_max_pool_size()); - } - if(appProperties.getSearch_coord_queue_capacity() != null) { - setSearchCoordQueueCapacity(appProperties.getSearch_coord_queue_capacity()); - } - } @Override public DatabaseBackedPagingProvider databaseBackedPagingProvider() { diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java index 074d5b74ee9..b258d90bfea 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java @@ -35,19 +35,6 @@ public class FhirServerConfigDstu3 extends BaseJavaConfigDstu3 { @Autowired AppProperties appProperties; - @PostConstruct - public void initSettings() { - if(appProperties.getSearch_coord_core_pool_size() != null) { - setSearchCoordCorePoolSize(appProperties.getSearch_coord_core_pool_size()); - } - if(appProperties.getSearch_coord_max_pool_size() != null) { - setSearchCoordMaxPoolSize(appProperties.getSearch_coord_max_pool_size()); - } - if(appProperties.getSearch_coord_queue_capacity() != null) { - setSearchCoordQueueCapacity(appProperties.getSearch_coord_queue_capacity()); - } - } - @Override public DatabaseBackedPagingProvider databaseBackedPagingProvider() { diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java index 206ad80e5bc..3bb3c0e01d2 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java @@ -2,6 +2,7 @@ import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.jpa.config.BaseJavaConfigR4; +import ca.uhn.fhir.jpa.config.r4.JpaR4Config; import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; import ca.uhn.fhir.jpa.starter.annotations.OnR4Condition; import ca.uhn.fhir.jpa.starter.cql.StarterCqlR4Config; @@ -22,7 +23,7 @@ @Configuration @Conditional(OnR4Condition.class) @Import({StarterCqlR4Config.class, ElasticsearchConfig.class}) -public class FhirServerConfigR4 extends BaseJavaConfigR4 { +public class FhirServerConfigR4 extends JpaR4Config { @Autowired private DataSource myDataSource; @@ -35,18 +36,6 @@ public class FhirServerConfigR4 extends BaseJavaConfigR4 { @Autowired AppProperties appProperties; - @PostConstruct - public void initSettings() { - if(appProperties.getSearch_coord_core_pool_size() != null) { - setSearchCoordCorePoolSize(appProperties.getSearch_coord_core_pool_size()); - } - if(appProperties.getSearch_coord_max_pool_size() != null) { - setSearchCoordMaxPoolSize(appProperties.getSearch_coord_max_pool_size()); - } - if(appProperties.getSearch_coord_queue_capacity() != null) { - setSearchCoordQueueCapacity(appProperties.getSearch_coord_queue_capacity()); - } - } @Override public DatabaseBackedPagingProvider databaseBackedPagingProvider() { diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java index 6d6d64fc023..9c16871578a 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java @@ -34,19 +34,6 @@ public class FhirServerConfigR5 extends BaseJavaConfigR5 { @Autowired AppProperties appProperties; - @PostConstruct - public void initSettings() { - if(appProperties.getSearch_coord_core_pool_size() != null) { - setSearchCoordCorePoolSize(appProperties.getSearch_coord_core_pool_size()); - } - if(appProperties.getSearch_coord_max_pool_size() != null) { - setSearchCoordMaxPoolSize(appProperties.getSearch_coord_max_pool_size()); - } - if(appProperties.getSearch_coord_queue_capacity() != null) { - setSearchCoordQueueCapacity(appProperties.getSearch_coord_queue_capacity()); - } - } - @Override public DatabaseBackedPagingProvider databaseBackedPagingProvider() { DatabaseBackedPagingProvider pagingProvider = super.databaseBackedPagingProvider(); From 1a528978b87bc8c78420fac4281cc1e01d7a3b21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kan=20MacLean?= Date: Tue, 22 Feb 2022 09:36:41 +0100 Subject: [PATCH 043/200] Added instructions about removing Hibernate dialect To fix the problem raised in [this](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/issues/318) issue. --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index a377bf16b7b..2f2b01f620f 100644 --- a/README.md +++ b/README.md @@ -189,6 +189,13 @@ spring: password: admin driverClassName: com.mysql.jdbc.Driver ``` + +Also, make sure you are not setting the Hibernate dialect explicitly, in other words remove any lines similar to: + +``` +hibernate.dialect: {some none MySQL dialect} +``` + On some systems, it might be necessary to override hibernate's default naming strategy. The naming strategy must be set using spring.jpa.hibernate.physical_naming_strategy. ```yaml @@ -279,6 +286,8 @@ spring: driverClassName: com.mysql.jdbc.Driver ``` +Also, make sure you are not setting the Hibernate Dialect explicitly, see more details in the section about MySQL. + ## Running hapi-fhir-jpaserver directly from IntelliJ as Spring Boot Make sure you run with the maven profile called ```boot``` and NOT also ```jetty```. Then you are ready to press debug the project directly without any extra Application Servers. From 28e86bdb8ddb10fb0cb3d906a6b8a25cb8c12d47 Mon Sep 17 00:00:00 2001 From: Jaison Baskaran Date: Wed, 23 Feb 2022 09:38:42 -0700 Subject: [PATCH 044/200] Bump HAPI-FHIR version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7a12c7686ee..0ee8f08b7cb 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.0.0-PRE1-SNAPSHOT + 6.0.0-PRE2-SNAPSHOT hapi-fhir-jpaserver-starter From f2ba86d7db3780b3be0028c04cb0d0be8f7b0105 Mon Sep 17 00:00:00 2001 From: Tadgh Date: Wed, 23 Feb 2022 09:29:22 -0800 Subject: [PATCH 045/200] Move to newlines --- .../java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java index 3bb3c0e01d2..a7f6b0a8b4c 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java @@ -2,6 +2,7 @@ import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.jpa.config.BaseJavaConfigR4; +import ca.uhn.fhir.jpa.config.HapiJpaConfig; import ca.uhn.fhir.jpa.config.r4.JpaR4Config; import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; import ca.uhn.fhir.jpa.starter.annotations.OnR4Condition; @@ -22,7 +23,11 @@ @Configuration @Conditional(OnR4Condition.class) -@Import({StarterCqlR4Config.class, ElasticsearchConfig.class}) +@Import({ + StarterCqlR4Config.class, + ElasticsearchConfig.class + }) +@Import({JpaR4Config.class, HapiJpaConfig.class}) public class FhirServerConfigR4 extends JpaR4Config { @Autowired From 810b090846d6c2365d709ed119745cd3d293778d Mon Sep 17 00:00:00 2001 From: Ken Stevens Date: Wed, 23 Feb 2022 17:24:21 -0500 Subject: [PATCH 046/200] fix to work with latest hapi-fhir --- .../uhn/fhir/jpa/starter/AppProperties.java | 5 +- .../ca/uhn/fhir/jpa/starter/Application.java | 2 +- .../jpa/starter/BaseJpaRestfulServer.java | 31 ++--- .../fhir/jpa/starter/ElasticsearchConfig.java | 2 +- .../fhir/jpa/starter/EnvironmentHelper.java | 5 +- .../jpa/starter/FhirServerConfigCommon.java | 9 +- .../jpa/starter/FhirServerConfigDstu2.java | 71 ++---------- .../jpa/starter/FhirServerConfigDstu3.java | 73 ++---------- .../fhir/jpa/starter/FhirServerConfigR4.java | 76 ++---------- .../fhir/jpa/starter/FhirServerConfigR5.java | 71 ++---------- .../fhir/jpa/starter/StarterJpaConfig.java | 109 ++++++++++++++++++ .../uhn/fhir/jpa/starter/mdm/MdmConfig.java | 5 +- src/main/resources/application.yaml | 2 +- 13 files changed, 163 insertions(+), 298 deletions(-) create mode 100644 src/main/java/ca/uhn/fhir/jpa/starter/StarterJpaConfig.java diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java b/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java index b3534081bbd..5d5c1731e65 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java @@ -11,7 +11,10 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Configuration; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; @ConfigurationProperties(prefix = "hapi.fhir") @Configuration diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/Application.java b/src/main/java/ca/uhn/fhir/jpa/starter/Application.java index 2cf60f56344..0ba207207d5 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/Application.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/Application.java @@ -1,7 +1,7 @@ package ca.uhn.fhir.jpa.starter; -import ca.uhn.fhir.jpa.starter.mdm.MdmConfig; import ca.uhn.fhir.jpa.starter.annotations.OnEitherVersion; +import ca.uhn.fhir.jpa.starter.mdm.MdmConfig; import ca.uhn.fhir.jpa.subscription.channel.config.SubscriptionChannelConfig; import ca.uhn.fhir.jpa.subscription.match.config.SubscriptionProcessorConfig; import ca.uhn.fhir.jpa.subscription.match.config.WebsocketDispatcherConfig; diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java index a41e399b874..b7e15a30ad3 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java @@ -16,13 +16,8 @@ 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.IJpaSystemProvider; -import ca.uhn.fhir.jpa.provider.JpaCapabilityStatementProvider; -import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu2; -import ca.uhn.fhir.jpa.provider.SubscriptionTriggeringProvider; -import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider; +import ca.uhn.fhir.jpa.provider.*; import ca.uhn.fhir.jpa.provider.dstu3.JpaConformanceProviderDstu3; -import ca.uhn.fhir.jpa.provider.ValueSetOperationProvider; import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; import ca.uhn.fhir.jpa.subscription.util.SubscriptionDebugLogInterceptor; import ca.uhn.fhir.mdm.provider.MdmProviderLoader; @@ -30,17 +25,8 @@ import ca.uhn.fhir.narrative.INarrativeGenerator; import ca.uhn.fhir.narrative2.NullNarrativeGenerator; import ca.uhn.fhir.rest.openapi.OpenApiInterceptor; -import ca.uhn.fhir.rest.server.ApacheProxyAddressStrategy; -import ca.uhn.fhir.rest.server.ETagSupportEnum; -import ca.uhn.fhir.rest.server.HardcodedServerAddressStrategy; -import ca.uhn.fhir.rest.server.IncomingRequestAddressStrategy; -import ca.uhn.fhir.rest.server.RestfulServer; -import ca.uhn.fhir.rest.server.interceptor.CorsInterceptor; -import ca.uhn.fhir.rest.server.interceptor.FhirPathFilterInterceptor; -import ca.uhn.fhir.rest.server.interceptor.LoggingInterceptor; -import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor; -import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor; -import ca.uhn.fhir.rest.server.interceptor.ResponseValidatingInterceptor; +import ca.uhn.fhir.rest.server.*; +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; @@ -49,19 +35,16 @@ import ca.uhn.fhir.validation.ResultSeverityEnum; import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.stream.Collectors; -import javax.servlet.ServletException; import org.hl7.fhir.r4.model.Bundle.BundleType; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.http.HttpHeaders; import org.springframework.web.cors.CorsConfiguration; +import javax.servlet.ServletException; +import java.util.*; +import java.util.stream.Collectors; + public class BaseJpaRestfulServer extends RestfulServer { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseJpaRestfulServer.class); diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/ElasticsearchConfig.java b/src/main/java/ca/uhn/fhir/jpa/starter/ElasticsearchConfig.java index fd27e84ee36..21216e6dd59 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/ElasticsearchConfig.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/ElasticsearchConfig.java @@ -14,7 +14,7 @@ public class ElasticsearchConfig { @Autowired private ConfigurableEnvironment configurableEnvironment; - @Bean() + @Bean public ElasticsearchSvcImpl elasticsearchSvc() { if (EnvironmentHelper.isElasticsearchEnabled(configurableEnvironment)) { String elasticsearchUrl = EnvironmentHelper.getElasticsearchServerUrl(configurableEnvironment); diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/EnvironmentHelper.java b/src/main/java/ca/uhn/fhir/jpa/starter/EnvironmentHelper.java index 96a958f187f..11b612811c1 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/EnvironmentHelper.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/EnvironmentHelper.java @@ -22,7 +22,10 @@ import org.springframework.core.env.EnumerablePropertySource; import org.springframework.core.env.PropertySource; -import java.util.*; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; public class EnvironmentHelper { 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 fb99b7f0dc8..eda9ae4c2dc 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java @@ -14,8 +14,6 @@ import ca.uhn.fhir.rest.server.mail.MailConfig; import ca.uhn.fhir.rest.server.mail.MailSvc; import com.google.common.base.Strings; -import java.util.HashSet; -import java.util.Optional; import org.hl7.fhir.dstu2.model.Subscription; import org.springframework.boot.env.YamlPropertySourceLoader; import org.springframework.context.annotation.Bean; @@ -25,6 +23,9 @@ import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.transaction.annotation.EnableTransactionManagement; +import java.util.HashSet; +import java.util.Optional; + /** * This is the primary configuration file for the example server */ @@ -74,7 +75,7 @@ public FhirServerConfigCommon(AppProperties appProperties) { /** * Configure FHIR properties around the the JPA server via this bean */ - @Bean() + @Bean public DaoConfig daoConfig(AppProperties appProperties) { DaoConfig retVal = new DaoConfig(); @@ -209,7 +210,7 @@ public IBinaryStorageSvc binaryStorageSvc(AppProperties appProperties) { return binaryStorageSvc; } - @Bean() + @Bean public IEmailSender emailSender(AppProperties appProperties, Optional subscriptionDeliveryHandlerFactory) { if (appProperties.getSubscription() != null && appProperties.getSubscription().getEmail() != null) { MailConfig mailConfig = new MailConfig(); diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu2.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu2.java index e01d6564113..b8011389d20 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu2.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu2.java @@ -1,73 +1,16 @@ package ca.uhn.fhir.jpa.starter; -import ca.uhn.fhir.context.ConfigurationException; -import ca.uhn.fhir.jpa.config.BaseJavaConfigDstu2; -import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; +import ca.uhn.fhir.jpa.config.JpaDstu2Config; import ca.uhn.fhir.jpa.starter.annotations.OnDSTU2Condition; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; -import org.springframework.core.env.ConfigurableEnvironment; -import org.springframework.orm.jpa.JpaTransactionManager; -import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; - -import javax.annotation.PostConstruct; -import javax.persistence.EntityManagerFactory; -import javax.sql.DataSource; +import org.springframework.context.annotation.Import; @Configuration @Conditional(OnDSTU2Condition.class) -public class FhirServerConfigDstu2 extends BaseJavaConfigDstu2 { - - @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. - */ - @Autowired - AppProperties appProperties; - - - @Override - public DatabaseBackedPagingProvider databaseBackedPagingProvider() { - DatabaseBackedPagingProvider pagingProvider = super.databaseBackedPagingProvider(); - pagingProvider.setDefaultPageSize(appProperties.getDefault_page_size()); - pagingProvider.setMaximumPageSize(appProperties.getMax_page_size()); - return pagingProvider; - } - - @Autowired - private ConfigurableEnvironment configurableEnvironment; - - @Override - @Bean() - public LocalContainerEntityManagerFactoryBean entityManagerFactory( - ConfigurableListableBeanFactory myConfigurableListableBeanFactory) { - LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory(myConfigurableListableBeanFactory); - 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; - } - - +@Import({ + StarterJpaConfig.class, + JpaDstu2Config.class +}) +public class FhirServerConfigDstu2 { } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java index b258d90bfea..35d1dabb49c 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java @@ -1,76 +1,19 @@ package ca.uhn.fhir.jpa.starter; -import ca.uhn.fhir.context.ConfigurationException; -import ca.uhn.fhir.jpa.config.BaseJavaConfigDstu3; -import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; +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 javax.annotation.PostConstruct; -import javax.persistence.EntityManagerFactory; -import javax.sql.DataSource; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.context.annotation.Bean; +import ca.uhn.fhir.jpa.starter.mdm.MdmConfig; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; -import org.springframework.context.annotation.Primary; -import org.springframework.core.env.ConfigurableEnvironment; -import org.springframework.orm.jpa.JpaTransactionManager; -import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; @Configuration @Conditional(OnDSTU3Condition.class) -@Import({StarterCqlDstu3Config.class, ElasticsearchConfig.class}) -public class FhirServerConfigDstu3 extends BaseJavaConfigDstu3 { - - @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. - */ - @Autowired - AppProperties appProperties; - - - @Override - public DatabaseBackedPagingProvider databaseBackedPagingProvider() { - DatabaseBackedPagingProvider pagingProvider = super.databaseBackedPagingProvider(); - pagingProvider.setDefaultPageSize(appProperties.getDefault_page_size()); - pagingProvider.setMaximumPageSize(appProperties.getMax_page_size()); - return pagingProvider; - } - - @Autowired - private ConfigurableEnvironment configurableEnvironment; - - @Override - @Bean - public LocalContainerEntityManagerFactoryBean entityManagerFactory( - ConfigurableListableBeanFactory myConfigurableListableBeanFactory) { - LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory(myConfigurableListableBeanFactory); - 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; - } +@Import({ + StarterJpaConfig.class, + JpaDstu3Config.class, + StarterCqlDstu3Config.class, + ElasticsearchConfig.class}) +public class FhirServerConfigDstu3 { } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java index a7f6b0a8b4c..c31cf529edd 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java @@ -1,82 +1,20 @@ 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.HapiJpaConfig; import ca.uhn.fhir.jpa.config.r4.JpaR4Config; -import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; import ca.uhn.fhir.jpa.starter.annotations.OnR4Condition; import ca.uhn.fhir.jpa.starter.cql.StarterCqlR4Config; -import javax.annotation.PostConstruct; -import javax.persistence.EntityManagerFactory; -import javax.sql.DataSource; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.context.annotation.Bean; +import ca.uhn.fhir.jpa.starter.mdm.MdmConfig; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; -import org.springframework.context.annotation.Primary; -import org.springframework.core.env.ConfigurableEnvironment; -import org.springframework.orm.jpa.JpaTransactionManager; -import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; @Configuration @Conditional(OnR4Condition.class) @Import({ - StarterCqlR4Config.class, - ElasticsearchConfig.class - }) -@Import({JpaR4Config.class, HapiJpaConfig.class}) -public class FhirServerConfigR4 extends JpaR4Config { - - @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. - */ - @Autowired - AppProperties appProperties; - - - @Override - public DatabaseBackedPagingProvider databaseBackedPagingProvider() { - DatabaseBackedPagingProvider pagingProvider = super.databaseBackedPagingProvider(); - pagingProvider.setDefaultPageSize(appProperties.getDefault_page_size()); - pagingProvider.setMaximumPageSize(appProperties.getMax_page_size()); - return pagingProvider; - } - - @Autowired - private ConfigurableEnvironment configurableEnvironment; - - @Override - @Bean() - public LocalContainerEntityManagerFactoryBean entityManagerFactory( - ConfigurableListableBeanFactory myConfigurableListableBeanFactory) { - LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory(myConfigurableListableBeanFactory); - 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; - } - + StarterJpaConfig.class, + JpaR4Config.class, + StarterCqlR4Config.class, + ElasticsearchConfig.class +}) +public class FhirServerConfigR4 { } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java index 9c16871578a..8ee03df272d 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java @@ -1,74 +1,17 @@ package ca.uhn.fhir.jpa.starter; -import ca.uhn.fhir.context.ConfigurationException; -import ca.uhn.fhir.jpa.config.BaseJavaConfigR5; -import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; +import ca.uhn.fhir.jpa.config.r5.JpaR5Config; import ca.uhn.fhir.jpa.starter.annotations.OnR5Condition; -import javax.annotation.PostConstruct; -import javax.persistence.EntityManagerFactory; -import javax.sql.DataSource; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; -import org.springframework.context.annotation.Primary; -import org.springframework.core.env.ConfigurableEnvironment; -import org.springframework.orm.jpa.JpaTransactionManager; -import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; @Configuration @Conditional(OnR5Condition.class) -@Import({ElasticsearchConfig.class}) -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. - */ - @Autowired - AppProperties appProperties; - - @Override - public DatabaseBackedPagingProvider databaseBackedPagingProvider() { - DatabaseBackedPagingProvider pagingProvider = super.databaseBackedPagingProvider(); - pagingProvider.setDefaultPageSize(appProperties.getDefault_page_size()); - pagingProvider.setMaximumPageSize(appProperties.getMax_page_size()); - return pagingProvider; - } - - @Autowired - private ConfigurableEnvironment configurableEnvironment; - - @Override - @Bean() - public LocalContainerEntityManagerFactoryBean entityManagerFactory( - ConfigurableListableBeanFactory myConfigurableListableBeanFactory) { - LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory(myConfigurableListableBeanFactory); - 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; - } - +@Import({ + StarterJpaConfig.class, + JpaR5Config.class, + ElasticsearchConfig.class +}) +public class FhirServerConfigR5 { } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/StarterJpaConfig.java b/src/main/java/ca/uhn/fhir/jpa/starter/StarterJpaConfig.java new file mode 100644 index 00000000000..07fd0282cb5 --- /dev/null +++ b/src/main/java/ca/uhn/fhir/jpa/starter/StarterJpaConfig.java @@ -0,0 +1,109 @@ +package ca.uhn.fhir.jpa.starter; + +import ca.uhn.fhir.context.ConfigurationException; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jpa.api.IDaoRegistry; +import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao; +import ca.uhn.fhir.jpa.batch.config.NonPersistedBatchConfigurer; +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.provider.DaoRegistryResourceSupportedSvc; +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.util.ResourceCountCache; +import ca.uhn.fhir.jpa.validation.JpaValidationSupportChain; +import ca.uhn.fhir.rest.api.IResourceSupportedSvc; +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.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.orm.jpa.JpaTransactionManager; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; + +import javax.persistence.EntityManagerFactory; +import javax.sql.DataSource; + +@Configuration +public class StarterJpaConfig { + @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 + AppProperties appProperties; + @Autowired + private DataSource myDataSource; + @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() { + 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( + 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; + } +} diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/mdm/MdmConfig.java b/src/main/java/ca/uhn/fhir/jpa/starter/mdm/MdmConfig.java index 39718f6c9ad..50dfe9e0489 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/mdm/MdmConfig.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/mdm/MdmConfig.java @@ -1,15 +1,12 @@ package ca.uhn.fhir.jpa.starter.mdm; -import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jpa.mdm.config.MdmConsumerConfig; import ca.uhn.fhir.jpa.mdm.config.MdmSubmitterConfig; import ca.uhn.fhir.jpa.starter.AppProperties; import ca.uhn.fhir.mdm.api.IMdmSettings; import ca.uhn.fhir.mdm.rules.config.MdmRuleValidator; import ca.uhn.fhir.mdm.rules.config.MdmSettings; -import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; import com.google.common.base.Charsets; -import java.io.IOException; import org.apache.commons.io.IOUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; @@ -19,6 +16,8 @@ import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.Resource; +import java.io.IOException; + @Configuration @Conditional(MdmConfigCondition.class) @Import({MdmConsumerConfig.class, MdmSubmitterConfig.class}) diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 7ea0ccc7c7f..7083b7d3ecb 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -99,7 +99,7 @@ hapi: # mdm_enabled: true # local_base_urls: # - https://hapi.fhir.org/baseR4 - mdm_enabled: true + mdm_enabled: false # partitioning: # allow_references_across_partitions: false # partitioning_include_in_search_hashes: false From 24a20a7e973aef9674204bbce202cac860d9a472 Mon Sep 17 00:00:00 2001 From: Jens Kristian Villadsen Date: Wed, 2 Mar 2022 16:39:13 +0100 Subject: [PATCH 047/200] Feature/update docker support (#319) * Updated docker image according to discussion on https://github.com/hapifhir/hapi-fhir-jpaserver-starter/pull/305 * Added doc * Added corrections according to comments * Update Dockerfile * Update build-images.yaml Updated to default to distroless --- .github/workflows/build-images.yaml | 24 ++++++++--------- Dockerfile | 40 ++++++++++++++++++----------- 2 files changed, 37 insertions(+), 27 deletions(-) diff --git a/.github/workflows/build-images.yaml b/.github/workflows/build-images.yaml index dae02057a17..2a413b5e7dc 100644 --- a/.github/workflows/build-images.yaml +++ b/.github/workflows/build-images.yaml @@ -19,25 +19,24 @@ jobs: name: Build runs-on: ubuntu-20.04 steps: - - name: Docker meta + - name: Container meta for default (distroless) image id: docker_meta uses: docker/metadata-action@v3 with: images: ${{ env.IMAGES }} tags: | type=match,pattern=image-(.*),group=1,enable=${{github.event_name != 'pull_request'}} - type=sha + - - name: Docker distroless meta - id: docker_distroless_meta + - name: Container meta for tomcat image + id: docker_tomcat_meta uses: docker/metadata-action@v3 with: images: ${{ env.IMAGES }} tags: | type=match,pattern=image-(.*),group=1,enable=${{github.event_name != 'pull_request'}} - type=sha flavor: | - suffix=-distroless,onlatest=true + suffix=-tomcat,onlatest=true - name: Set up QEMU uses: docker/setup-qemu-action@v1 @@ -60,7 +59,7 @@ jobs: restore-keys: | ${{ runner.os }}-buildx- - - name: Build and push + - name: Build and push default (distroless) image id: docker_build uses: docker/build-push-action@v2 with: @@ -70,15 +69,16 @@ jobs: tags: ${{ steps.docker_meta.outputs.tags }} labels: ${{ steps.docker_meta.outputs.labels }} platforms: ${{ env.PLATFORMS }} + target: default - - name: Build and push distroless - id: docker_build_distroless + - name: Build and push tomcat image + id: docker_build_tomcat uses: docker/build-push-action@v2 with: cache-from: type=local,src=/tmp/.buildx-cache cache-to: type=local,dest=/tmp/.buildx-cache push: ${{ github.event_name != 'pull_request' }} - tags: ${{ steps.docker_distroless_meta.outputs.tags }} - labels: ${{ steps.docker_distroless_meta.outputs.labels }} + tags: ${{ steps.docker_tomcat_meta.outputs.tags }} + labels: ${{ steps.docker_tomcat_meta.outputs.labels }} platforms: ${{ env.PLATFORMS }} - target: release-distroless + target: tomcat diff --git a/Dockerfile b/Dockerfile index 5d3772e75e7..3a8ea7a44f1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM maven:3.8.2-jdk-11-slim as build-hapi +FROM maven:3.8-openjdk-17-slim as build-hapi WORKDIR /tmp/hapi-fhir-jpaserver-starter COPY pom.xml . @@ -6,14 +6,34 @@ COPY server.xml . RUN mvn -ntp dependency:go-offline COPY src/ /tmp/hapi-fhir-jpaserver-starter/src/ -RUN mvn clean install -DskipTests +RUN mvn clean install -DskipTests -Djdk.lang.Process.launchMechanism=vfork FROM build-hapi AS build-distroless RUN mvn package spring-boot:repackage -Pboot -RUN mkdir /app && \ - cp /tmp/hapi-fhir-jpaserver-starter/target/ROOT.war /app/main.war +RUN mkdir /app && cp /tmp/hapi-fhir-jpaserver-starter/target/ROOT.war /app/main.war -FROM gcr.io/distroless/java-debian11:11 AS release-distroless + +########### bitnami tomcat version is suitable for debugging and comes with a shell +########### it can be built using eg. `docker build --target tomcat .` +FROM bitnami/tomcat:9.0 as tomcat + +RUN rm -rf /opt/bitnami/tomcat/webapps/ROOT && \ + rm -rf /opt/bitnami/tomcat/webapps_default/ROOT && \ + mkdir -p /opt/bitnami/hapi/data/hapi/lucenefiles && \ + chmod 775 /opt/bitnami/hapi/data/hapi/lucenefiles + +USER root +RUN mkdir -p /target && chown -R 1001:1001 target +USER 1001 + +COPY --chown=1001:1001 catalina.properties /opt/bitnami/tomcat/conf/catalina.properties +COPY --chown=1001:1001 server.xml /opt/bitnami/tomcat/conf/server.xml +COPY --from=build-hapi --chown=1001:1001 /tmp/hapi-fhir-jpaserver-starter/target/ROOT.war /opt/bitnami/tomcat/webapps_default/ROOT.war + +ENV ALLOW_EMPTY_PASSWORD=yes + +########### distroless brings focus on security and runs on plain spring boot - this is the default image +FROM gcr.io/distroless/java17:nonroot as default COPY --chown=nonroot:nonroot --from=build-distroless /app /app # 65532 is the nonroot user's uid # used here instead of the name to allow Kubernetes to easily detect that the container @@ -21,13 +41,3 @@ COPY --chown=nonroot:nonroot --from=build-distroless /app /app USER 65532:65532 WORKDIR /app CMD ["/app/main.war"] - -FROM tomcat:9.0.53-jdk11-openjdk-slim-bullseye - -RUN mkdir -p /data/hapi/lucenefiles && chmod 775 /data/hapi/lucenefiles -COPY --from=build-hapi /tmp/hapi-fhir-jpaserver-starter/target/*.war /usr/local/tomcat/webapps/ - -COPY catalina.properties /usr/local/tomcat/conf/catalina.properties -COPY server.xml /usr/local/tomcat/conf/server.xml - -CMD ["catalina.sh", "run"] From 7db15103fe4790817588e74a099126e8008b620f Mon Sep 17 00:00:00 2001 From: "Joel Schneider (NMDP)" Date: Fri, 4 Mar 2022 04:38:16 -0600 Subject: [PATCH 048/200] add dao_scheduling_enabled configuration property (#324) --- src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java | 9 +++++++++ .../ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java | 2 ++ 2 files changed, 11 insertions(+) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java b/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java index 1118df7fd55..2ef74cfe3df 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java @@ -28,6 +28,7 @@ public class AppProperties { private Boolean allow_multiple_delete = false; private Boolean allow_override_default_search_params = true; private Boolean auto_create_placeholder_reference_targets = false; + private Boolean dao_scheduling_enabled = true; private Boolean delete_expunge_enabled = false; private Boolean enable_index_missing_fields = false; private Boolean enable_index_contained_resource = false; @@ -286,6 +287,14 @@ public void setDefault_page_size(Integer default_page_size) { this.default_page_size = default_page_size; } + public Boolean getDao_scheduling_enabled() { + return dao_scheduling_enabled; + } + + public void setDao_scheduling_enabled(Boolean dao_scheduling_enabled) { + this.dao_scheduling_enabled = dao_scheduling_enabled; + } + public Boolean getDelete_expunge_enabled() { return delete_expunge_enabled; } 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 fb99b7f0dc8..d9617c75b21 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java @@ -39,6 +39,7 @@ 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"); @@ -85,6 +86,7 @@ public DaoConfig daoConfig(AppProperties appProperties) { retVal.setAllowContainsSearches(appProperties.getAllow_contains_searches()); retVal.setAllowMultipleDelete(appProperties.getAllow_multiple_delete()); retVal.setAllowExternalReferences(appProperties.getAllow_external_references()); + retVal.setSchedulingDisabled(!appProperties.getDao_scheduling_enabled()); retVal.setDeleteExpungeEnabled(appProperties.getDelete_expunge_enabled()); retVal.setExpungeEnabled(appProperties.getExpunge_enabled()); if(appProperties.getSubscription() != null && appProperties.getSubscription().getEmail() != null) From 40d7b9ce27355fedb3fb70fcd79a09d7bd8290bb Mon Sep 17 00:00:00 2001 From: Patrick Werner Date: Mon, 7 Mar 2022 13:20:06 +0100 Subject: [PATCH 049/200] added reindexProvider to Config (#326) --- .../java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java index a41e399b874..3d94924ac21 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java @@ -42,6 +42,7 @@ 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.ReindexProvider; import ca.uhn.fhir.rest.server.provider.ResourceProviderFactory; import ca.uhn.fhir.rest.server.tenant.UrlBaseTenantIdentificationStrategy; import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; @@ -95,6 +96,8 @@ public class BaseJpaRestfulServer extends RestfulServer { @Autowired ValueSetOperationProvider valueSetOperationProvider; @Autowired + ReindexProvider reindexProvider; + @Autowired BinaryStorageInterceptor binaryStorageInterceptor; @Autowired IPackageInstallerSvc packageInstallerSvc; @@ -373,6 +376,9 @@ protected void initialize() throws ServletException { // valueSet Operations i.e $expand registerProvider(valueSetOperationProvider); + //reindex Provider $reindex + registerProvider(reindexProvider); + // Partitioning if (appProperties.getPartitioning() != null) { registerInterceptor(new RequestTenantPartitionInterceptor()); From 0483db9195057326a574b29af654f8da0b1cfee1 Mon Sep 17 00:00:00 2001 From: Jens Kristian Villadsen Date: Tue, 8 Mar 2022 10:42:52 +0100 Subject: [PATCH 050/200] Update application.yaml --- src/main/resources/application.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 7ea0ccc7c7f..3b7e97bf7f4 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -1,6 +1,7 @@ spring: main: allow-circular-references: true + allow-bean-definition-overriding: true flyway: enabled: false check-location: false From 929a3535fa93d33b24cef06ed4158ae2fcb9f6a2 Mon Sep 17 00:00:00 2001 From: Jens Kristian Villadsen Date: Tue, 8 Mar 2022 10:44:13 +0100 Subject: [PATCH 051/200] Update application.yaml Roll back - mistake from my side --- src/main/resources/application.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 3b7e97bf7f4..3131c7ed55a 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -1,7 +1,7 @@ spring: main: allow-circular-references: true - allow-bean-definition-overriding: true + #allow-bean-definition-overriding: true flyway: enabled: false check-location: false From 146b9f68ac92ae62da878de36450bd7619d016ff Mon Sep 17 00:00:00 2001 From: Jaison Baskaran Date: Wed, 9 Mar 2022 09:03:10 -0700 Subject: [PATCH 052/200] Bump hapi-fhir version to 'PRE5' (#329) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a08affeef25..83635dcaa45 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.0.0-PRE3-SNAPSHOT + 6.0.0-PRE5-SNAPSHOT hapi-fhir-jpaserver-starter From aeef4b176c0a48065c6190f6b56d27a45a62db33 Mon Sep 17 00:00:00 2001 From: jkv Date: Sun, 20 Mar 2022 21:22:36 +0100 Subject: [PATCH 053/200] Add actuator --- pom.xml | 19 +++++++++++++++++++ src/main/resources/application.yaml | 7 +++++++ 2 files changed, 26 insertions(+) diff --git a/pom.xml b/pom.xml index 69aef165fbf..3c511efb15a 100644 --- a/pom.xml +++ b/pom.xml @@ -85,6 +85,12 @@ ca.uhn.hapi.fhir hapi-fhir-jpaserver-subscription ${project.version} + + + com.zaxxer + HikariCP-java7 + + @@ -301,6 +307,19 @@ ${spring_boot_version} + + org.springframework.boot + spring-boot-starter-actuator + ${spring_boot_version} + + + + com.zaxxer + HikariCP + 5.0.1 + + + org.junit.jupiter junit-jupiter-api diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 3131c7ed55a..11da8d1cafd 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -1,3 +1,10 @@ +#Adds the option to go to eg. http://localhost:8080/actuator/env for seeing the running configuration +management: + endpoints: + web: + exposure: + include: "*" + exclude: "beans" spring: main: allow-circular-references: true From 403b87573d6381e85186f8d3c1f4226052fdae98 Mon Sep 17 00:00:00 2001 From: Michael Buckley Date: Wed, 23 Mar 2022 10:34:42 -0400 Subject: [PATCH 054/200] Bump to PRE8 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 83635dcaa45..056a508e32d 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.0.0-PRE5-SNAPSHOT + 6.0.0-PRE8-SNAPSHOT hapi-fhir-jpaserver-starter From da319e8761ed2ef017ed5e9dcbde489a99489123 Mon Sep 17 00:00:00 2001 From: craig mcclendon Date: Wed, 23 Mar 2022 14:12:48 -0500 Subject: [PATCH 055/200] disable springboot actuator endpoints other than 'health' for security reasons (#338) Co-authored-by: Craig McClendon --- src/main/resources/application.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 11da8d1cafd..b03dd76f559 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -1,10 +1,10 @@ -#Adds the option to go to eg. http://localhost:8080/actuator/env for seeing the running configuration +#Adds the option to go to eg. http://localhost:8080/actuator/health for seeing the running configuration +#see https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html#actuator.endpoints management: endpoints: web: exposure: - include: "*" - exclude: "beans" + include: "health" spring: main: allow-circular-references: true From 49401c0d3dbd52c9a5b8ac3c9c4407fcb973a367 Mon Sep 17 00:00:00 2001 From: Jaison Baskaran Date: Tue, 29 Mar 2022 15:16:08 -0600 Subject: [PATCH 056/200] Bump to PRE9 --- pom.xml | 2 +- src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java | 2 +- .../java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 056a508e32d..0c0f6280ed3 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.0.0-PRE8-SNAPSHOT + 6.0.0-PRE9-SNAPSHOT hapi-fhir-jpaserver-starter diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java index e147da6b6a8..48e159a3713 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java @@ -9,7 +9,7 @@ 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.binary.interceptor.BinaryStorageInterceptor; import ca.uhn.fhir.jpa.bulk.export.provider.BulkDataExportProvider; import ca.uhn.fhir.jpa.graphql.GraphQLProvider; import ca.uhn.fhir.jpa.interceptor.CascadingDeleteInterceptor; 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 eda9ae4c2dc..cc93f60d821 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java @@ -1,8 +1,8 @@ package ca.uhn.fhir.jpa.starter; 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.binstore.IBinaryStorageSvc; import ca.uhn.fhir.jpa.config.HibernatePropertiesProvider; import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.jpa.model.config.PartitionSettings.CrossPartitionReferenceMode; From 6b3f57cf12a1f264a9ef9ce9bcd885de0a67d269 Mon Sep 17 00:00:00 2001 From: Jens Kristian Villadsen Date: Thu, 7 Apr 2022 22:32:38 +0200 Subject: [PATCH 057/200] Update application.yaml (#345) --- src/main/resources/application.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 3131c7ed55a..df616721414 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -100,7 +100,6 @@ hapi: # mdm_enabled: true # local_base_urls: # - https://hapi.fhir.org/baseR4 - mdm_enabled: true # partitioning: # allow_references_across_partitions: false # partitioning_include_in_search_hashes: false From cdda71b25320afa83538c2c6d88e097bedbfaa70 Mon Sep 17 00:00:00 2001 From: craig mcclendon Date: Sat, 9 Apr 2022 12:19:44 -0500 Subject: [PATCH 058/200] add support for ms sql server (#347) --- README.md | 20 ++++++++++++++++++++ pom.xml | 4 ++++ 2 files changed, 24 insertions(+) diff --git a/README.md b/README.md index a377bf16b7b..bb67d6ac821 100644 --- a/README.md +++ b/README.md @@ -215,6 +215,26 @@ spring: Because the integration tests within the project rely on the default H2 database configuration, it is important to either explicity skip the integration tests during the build process, i.e., `mvn install -DskipTests`, or delete the tests altogether. Failure to skip or delete the tests once you've configured PostgreSQL for the datasource.driver, datasource.url, and hibernate.dialect as outlined above will result in build errors and compilation failure. +### Microsoft SQL Server configuration + +To configure the starter app to use MS SQL Server, instead of the default H2, update the application.yaml file to have the following: + +```yaml +spring: + datasource: + url: 'jdbc:sqlserver://:;databaseName=' + username: admin + password: admin + driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver +``` + + +Because the integration tests within the project rely on the default H2 database configuration, it is important to either explicity skip the integration tests during the build process, i.e., `mvn install -DskipTests`, or delete the tests altogether. Failure to skip or delete the tests once you've configured PostgreSQL for the datasource.driver, datasource.url, and hibernate.dialect as outlined above will result in build errors and compilation failure. + + +NOTE: MS SQL Server by default uses a case-insensitive codepage. This will cause errors with some operations - such as when expanding case-sensitive valuesets (UCUM) as there are unique indexes defined on the terminology tables for codes. +It is recommended to deploy a case-sensitive database prior to running HAPI FHIR when using MS SQL Server to avoid these and potentially other issues. + ## Customizing The Web Testpage UI The UI that comes with this server is an exact clone of the server available at [http://hapi.fhir.org](http://hapi.fhir.org). You may skin this UI if you'd like. For example, you might change the introductory text or replace the logo with your own. diff --git a/pom.xml b/pom.xml index 69aef165fbf..591664c24c3 100644 --- a/pom.xml +++ b/pom.xml @@ -61,6 +61,10 @@ org.postgresql postgresql + + com.microsoft.sqlserver + mssql-jdbc + From bb21ccfe90e5e40b86587bcf9b04cda08114b878 Mon Sep 17 00:00:00 2001 From: dotasek Date: Mon, 11 Apr 2022 11:47:09 -0400 Subject: [PATCH 059/200] Fix comments in Demo that lead to 404 (#348) Co-authored-by: dotasek --- src/test/java/ca/uhn/fhir/jpa/starter/Demo.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/Demo.java b/src/test/java/ca/uhn/fhir/jpa/starter/Demo.java index 5dadf14f8da..d46094360ee 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/Demo.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/Demo.java @@ -15,6 +15,6 @@ public static void main(String[] args) { System.setProperty("spring.batch.job.enabled", "false"); SpringApplication.run(Demo.class, args); - //Server is now accessible at eg. http://localhost:8080/metadata + //Server is now accessible at eg. http://localhost:8080/fhir/metadata } } From 4bed69fedfd1f3021c1ecdae388dfee9067073e2 Mon Sep 17 00:00:00 2001 From: chgl Date: Mon, 11 Apr 2022 17:56:32 +0200 Subject: [PATCH 060/200] updated helm chart to use v5.7.0 and latest PostgreSQL sub-chart (#346) --- charts/hapi-fhir-jpaserver/Chart.lock | 6 +-- charts/hapi-fhir-jpaserver/Chart.yaml | 17 ++++---- charts/hapi-fhir-jpaserver/README.md | 13 +++---- .../ci/enabled-ingress-values.yaml | 6 +++ .../templates/_helpers.tpl | 24 +++--------- .../templates/deployment.yaml | 8 ++-- .../templates/externaldb-secret.yaml | 4 +- ...st-connection.yaml => test-endpoints.yaml} | 29 +++++++++++++- charts/hapi-fhir-jpaserver/values.yaml | 39 +++++++++---------- 9 files changed, 82 insertions(+), 64 deletions(-) create mode 100644 charts/hapi-fhir-jpaserver/ci/enabled-ingress-values.yaml rename charts/hapi-fhir-jpaserver/templates/tests/{test-connection.yaml => test-endpoints.yaml} (53%) diff --git a/charts/hapi-fhir-jpaserver/Chart.lock b/charts/hapi-fhir-jpaserver/Chart.lock index 0db0f3a7b87..bfb87acb260 100644 --- a/charts/hapi-fhir-jpaserver/Chart.lock +++ b/charts/hapi-fhir-jpaserver/Chart.lock @@ -1,6 +1,6 @@ dependencies: - name: postgresql repository: https://charts.bitnami.com/bitnami - version: 10.12.2 -digest: sha256:38ee315eae1af3e3f6eb20e1dd8ffd60d4ab7ee0c51bf26941b56c8bcb376c11 -generated: "2021-10-07T00:19:18.9743522+02:00" + version: 11.1.19 +digest: sha256:5bb38230bfa62c63547851e6f46f66a61441a4a4f18e3689827546277e34d192 +generated: "2022-04-08T21:55:34.6868891+02:00" diff --git a/charts/hapi-fhir-jpaserver/Chart.yaml b/charts/hapi-fhir-jpaserver/Chart.yaml index dd2c479314a..3cb702b205b 100644 --- a/charts/hapi-fhir-jpaserver/Chart.yaml +++ b/charts/hapi-fhir-jpaserver/Chart.yaml @@ -7,20 +7,23 @@ sources: - https://github.com/hapifhir/hapi-fhir-jpaserver-starter dependencies: - name: postgresql - version: 10.12.2 + version: 11.1.19 repository: https://charts.bitnami.com/bitnami condition: postgresql.enabled annotations: artifacthub.io/license: Apache-2.0 - artifacthub.io/prerelease: "true" artifacthub.io/changes: | # When using the list of objects option the valid supported kinds are # added, changed, deprecated, removed, fixed, and security. - kind: changed description: | - updated HAPI FHIR starter image to 5.6.0 - - kind: added + updated HAPI FHIR starter image to 5.7.0 + - kind: changed + description: | + BREAKING CHANGE: updated included PostgreSQL-subchart to v11 + - kind: changed description: | - added support for configuring PodDisruptionBudget for the server pods -appVersion: v5.6.0 -version: 0.7.0 + BREAKING CHANGE: removed ability to override the image flavor. + The one based on distroless is now the new default. +appVersion: v5.7.0 +version: 0.8.0 diff --git a/charts/hapi-fhir-jpaserver/README.md b/charts/hapi-fhir-jpaserver/README.md index 9208bd6f967..288e2ce517c 100644 --- a/charts/hapi-fhir-jpaserver/README.md +++ b/charts/hapi-fhir-jpaserver/README.md @@ -1,6 +1,6 @@ # HAPI FHIR JPA Server Starter Helm Chart -![Version: 0.7.0](https://img.shields.io/badge/Version-0.7.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v5.6.0](https://img.shields.io/badge/AppVersion-v5.6.0-informational?style=flat-square) +![Version: 0.8.0](https://img.shields.io/badge/Version-0.8.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v5.7.0](https://img.shields.io/badge/AppVersion-v5.7.0-informational?style=flat-square) This helm chart will help you install the HAPI FHIR JPA Server in a Kubernetes environment. @@ -29,11 +29,10 @@ helm install --render-subchart-notes hapi-fhir-jpaserver hapifhir/hapi-fhir-jpas | externalDatabase.user | string | `"fhir"` | username for the external database | | extraEnv | list | `[]` | extra environment variables to set on the server container | | fullnameOverride | string | `""` | override the chart fullname | -| image.flavor | string | `"distroless"` | the flavor or variant of the image to use. appended to the image tag by `-`. | | image.pullPolicy | string | `"IfNotPresent"` | image pullPolicy to use | | image.registry | string | `"docker.io"` | registry where the HAPI FHIR server image is hosted | | image.repository | string | `"hapiproject/hapi"` | the path inside the repository | -| image.tag | string | `""` | defaults to `Chart.appVersion` | +| image.tag | string | `""` | defaults to `Chart.appVersion`. As of v5.7.0, this is the `distroless` flavor | | imagePullSecrets | list | `[]` | image pull secrets to use when pulling the image | | ingress.annotations | object | `{}` | provide any additional annotations which may be required. Evaluated as a template. | | ingress.enabled | bool | `false` | whether to create an Ingress to expose the FHIR server HTTP endpoint | @@ -51,11 +50,11 @@ helm install --render-subchart-notes hapi-fhir-jpaserver hapifhir/hapi-fhir-jpas | podDisruptionBudget.maxUnavailable | string | `""` | maximum unavailable instances | | podDisruptionBudget.minAvailable | int | `1` | minimum available instances | | podSecurityContext | object | `{}` | pod security context | -| postgresql.containerSecurityContext.allowPrivilegeEscalation | bool | `false` | | -| postgresql.containerSecurityContext.capabilities.drop[0] | string | `"ALL"` | | +| postgresql.auth.database | string | `"fhir"` | name for a custom database to create | +| postgresql.auth.existingSecret | string | `""` | Name of existing secret to use for PostgreSQL credentials `auth.postgresPassword`, `auth.password`, and `auth.replicationPassword` will be ignored and picked up from this secret The secret must contain the keys `postgres-password` (which is the password for "postgres" admin user), `password` (which is the password for the custom user to create when `auth.username` is set), and `replication-password` (which is the password for replication user). The secret might also contains the key `ldap-password` if LDAP is enabled. `ldap.bind_password` will be ignored and picked from this secret in this case. The value is evaluated as a template. | | postgresql.enabled | bool | `true` | enable an included PostgreSQL DB. see for details if set to `false`, the values under `externalDatabase` are used | -| postgresql.existingSecret | string | `""` | Name of existing secret to use for PostgreSQL passwords. The secret has to contain the keys `postgresql-password` which is the password for `postgresqlUsername` when it is different of `postgres`, `postgresql-postgres-password` which will override `postgresqlPassword`, `postgresql-replication-password` which will override `replication.password` and `postgresql-ldap-password` which will be sed to authenticate on LDAP. The value is evaluated as a template. | -| postgresql.postgresqlDatabase | string | `"fhir"` | name of the database to create see: | +| postgresql.primary.containerSecurityContext.allowPrivilegeEscalation | bool | `false` | | +| postgresql.primary.containerSecurityContext.capabilities.drop[0] | string | `"ALL"` | | | readinessProbe.failureThreshold | int | `5` | | | readinessProbe.initialDelaySeconds | int | `30` | | | readinessProbe.periodSeconds | int | `20` | | diff --git a/charts/hapi-fhir-jpaserver/ci/enabled-ingress-values.yaml b/charts/hapi-fhir-jpaserver/ci/enabled-ingress-values.yaml new file mode 100644 index 00000000000..f28063f19a0 --- /dev/null +++ b/charts/hapi-fhir-jpaserver/ci/enabled-ingress-values.yaml @@ -0,0 +1,6 @@ +ingress: + enabled: true + +postgresql: + auth: + postgresPassword: secretpassword diff --git a/charts/hapi-fhir-jpaserver/templates/_helpers.tpl b/charts/hapi-fhir-jpaserver/templates/_helpers.tpl index 178d84028bd..eee1ed59867 100644 --- a/charts/hapi-fhir-jpaserver/templates/_helpers.tpl +++ b/charts/hapi-fhir-jpaserver/templates/_helpers.tpl @@ -30,18 +30,6 @@ Create chart name and version as used by the chart label. {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} {{- end }} -{{/* -Create image tag -*/}} -{{- define "hapi-fhir-jpaserver.imageTag" -}} -{{- $version := default .Chart.AppVersion .Values.image.tag -}} -{{- if .Values.image.flavor }} -{{- printf "%s-%s" $version .Values.image.flavor }} -{{- else }} -{{- printf "%s" $version }} -{{- end }} -{{- end }} - {{/* Common labels */}} @@ -75,10 +63,10 @@ We truncate at 63 chars because some Kubernetes name fields are limited to this Get the Postgresql credentials secret name. */}} {{- define "hapi-fhir-jpaserver.postgresql.secretName" -}} -{{- if and (.Values.postgresql.enabled) (not .Values.postgresql.existingSecret) -}} +{{- if and (.Values.postgresql.enabled) (not .Values.postgresql.auth.existingSecret) -}} {{- printf "%s" (include "hapi-fhir-jpaserver.postgresql.fullname" .) -}} -{{- else if and (.Values.postgresql.enabled) (.Values.postgresql.existingSecret) -}} - {{- printf "%s" .Values.postgresql.existingSecret -}} +{{- else if and (.Values.postgresql.enabled) (.Values.postgresql.auth.existingSecret) -}} + {{- printf "%s" .Values.postgresql.auth.existingSecret -}} {{- else }} {{- if .Values.externalDatabase.existingSecret -}} {{- printf "%s" .Values.externalDatabase.existingSecret -}} @@ -95,7 +83,7 @@ Get the Postgresql credentials secret key. {{- if (.Values.externalDatabase.existingSecret) -}} {{- printf "%s" .Values.externalDatabase.existingSecretKey -}} {{- else }} - {{- printf "postgresql-password" -}} + {{- printf "postgres-password" -}} {{- end -}} {{- end -}} @@ -110,14 +98,14 @@ Add environment variables to configure database values Add environment variables to configure database values */}} {{- define "hapi-fhir-jpaserver.database.user" -}} -{{- ternary .Values.postgresql.postgresqlUsername .Values.externalDatabase.user .Values.postgresql.enabled -}} +{{- ternary "postgres" .Values.externalDatabase.user .Values.postgresql.enabled -}} {{- end -}} {{/* Add environment variables to configure database values */}} {{- define "hapi-fhir-jpaserver.database.name" -}} -{{- ternary .Values.postgresql.postgresqlDatabase .Values.externalDatabase.database .Values.postgresql.enabled -}} +{{- ternary .Values.postgresql.auth.database .Values.externalDatabase.database .Values.postgresql.enabled -}} {{- end -}} {{/* diff --git a/charts/hapi-fhir-jpaserver/templates/deployment.yaml b/charts/hapi-fhir-jpaserver/templates/deployment.yaml index a58024c82e3..187ee9d2816 100644 --- a/charts/hapi-fhir-jpaserver/templates/deployment.yaml +++ b/charts/hapi-fhir-jpaserver/templates/deployment.yaml @@ -60,7 +60,7 @@ spec: - name: {{ .Chart.Name }} securityContext: {{- toYaml .Values.securityContext | nindent 12 }} - image: {{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ include "hapi-fhir-jpaserver.imageTag" . }} + image: {{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ default .Chart.AppVersion .Values.image.tag }} imagePullPolicy: {{ .Values.image.pullPolicy }} ports: - name: http @@ -102,12 +102,10 @@ spec: key: {{ include "hapi-fhir-jpaserver.postgresql.secretKey" . }} - name: SPRING_DATASOURCE_DRIVERCLASSNAME value: org.postgresql.Driver - - name: SPRING_JPA_PROPERTIES_HIBERNATE_DIALECT - value: org.hibernate.dialect.PostgreSQL10Dialect + - name: spring.jpa.properties.hibernate.dialect + value: ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgres94Dialect - name: HAPI_FHIR_USE_APACHE_ADDRESS_STRATEGY value: "true" - - name: SPRING_JPA_DATABASE_PLATFORM - value: org.hibernate.dialect.PostgreSQLDialect {{- if .Values.extraEnv }} {{ toYaml .Values.extraEnv | nindent 12 }} {{- end }} diff --git a/charts/hapi-fhir-jpaserver/templates/externaldb-secret.yaml b/charts/hapi-fhir-jpaserver/templates/externaldb-secret.yaml index e3a35d80219..a487cb6b030 100644 --- a/charts/hapi-fhir-jpaserver/templates/externaldb-secret.yaml +++ b/charts/hapi-fhir-jpaserver/templates/externaldb-secret.yaml @@ -1,4 +1,4 @@ -{{- if and (not .Values.postgresql.enabled) (not .Values.externalDatabase.existingSecret) (not .Values.postgresql.existingSecret) }} +{{- if and (not .Values.postgresql.enabled) (not .Values.externalDatabase.existingSecret) (not .Values.postgresql.auth.existingSecret) }} apiVersion: v1 kind: Secret metadata: @@ -7,5 +7,5 @@ metadata: {{- include "hapi-fhir-jpaserver.labels" . | nindent 4 }} type: Opaque data: - postgresql-password: {{ .Values.externalDatabase.password | b64enc | quote }} + postgres-password: {{ .Values.externalDatabase.password | b64enc | quote }} {{- end }} diff --git a/charts/hapi-fhir-jpaserver/templates/tests/test-connection.yaml b/charts/hapi-fhir-jpaserver/templates/tests/test-endpoints.yaml similarity index 53% rename from charts/hapi-fhir-jpaserver/templates/tests/test-connection.yaml rename to charts/hapi-fhir-jpaserver/templates/tests/test-endpoints.yaml index eac503dfbf2..911f59d6ae3 100644 --- a/charts/hapi-fhir-jpaserver/templates/tests/test-connection.yaml +++ b/charts/hapi-fhir-jpaserver/templates/tests/test-endpoints.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: Pod metadata: - name: "{{ include "hapi-fhir-jpaserver.fullname" . }}-test-connection" + name: "{{ include "hapi-fhir-jpaserver.fullname" . }}-test-endpoints" labels: {{- include "hapi-fhir-jpaserver.labels" . | nindent 4 }} {{ include "hapi-fhir-jpaserver.fullname" . }}-client: "true" @@ -10,7 +10,32 @@ metadata: spec: restartPolicy: Never containers: - - name: wget + - name: test-metadata-endpoint + image: busybox:1 + command: ['wget', '-O', '-'] + args: ['http://{{ include "hapi-fhir-jpaserver.fullname" . }}:{{ .Values.service.port }}/fhir/metadata'] + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsUser: 22222 + runAsNonRoot: true + resources: + limits: + cpu: 100m + memory: 128Mi + requests: + cpu: 100m + memory: 128Mi + livenessProbe: + exec: + command: ["true"] + readinessProbe: + exec: + command: ["true"] + - name: test-patient-endpoint image: busybox:1 command: ['wget', '-O', '-'] args: ['http://{{ include "hapi-fhir-jpaserver.fullname" . }}:{{ .Values.service.port }}/fhir/Patient?_count=1'] diff --git a/charts/hapi-fhir-jpaserver/values.yaml b/charts/hapi-fhir-jpaserver/values.yaml index 5fb71ddb257..e89a5c4dd72 100644 --- a/charts/hapi-fhir-jpaserver/values.yaml +++ b/charts/hapi-fhir-jpaserver/values.yaml @@ -6,11 +6,8 @@ image: registry: docker.io # -- the path inside the repository repository: hapiproject/hapi - # -- defaults to `Chart.appVersion` + # -- defaults to `Chart.appVersion`. As of v5.7.0, this is the `distroless` flavor tag: "" - # -- the flavor or variant of the image to use. - # appended to the image tag by `-`. - flavor: "distroless" # -- image pullPolicy to use pullPolicy: IfNotPresent @@ -96,22 +93,24 @@ postgresql: # see for details # if set to `false`, the values under `externalDatabase` are used enabled: true - # -- name of the database to create - # see: - postgresqlDatabase: "fhir" - # -- Name of existing secret to use for PostgreSQL passwords. - # The secret has to contain the keys `postgresql-password` - # which is the password for `postgresqlUsername` when it is - # different of `postgres`, `postgresql-postgres-password` which - # will override `postgresqlPassword`, `postgresql-replication-password` - # which will override `replication.password` and `postgresql-ldap-password` - # which will be sed to authenticate on LDAP. The value is evaluated as a template. - existingSecret: "" - containerSecurityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL + auth: + # -- name for a custom database to create + database: "fhir" + # -- Name of existing secret to use for PostgreSQL credentials + # `auth.postgresPassword`, `auth.password`, and `auth.replicationPassword` will be ignored and picked up from this secret + # The secret must contain the keys `postgres-password` (which is the password for "postgres" admin user), + # `password` (which is the password for the custom user to create when `auth.username` is set), + # and `replication-password` (which is the password for replication user). + # The secret might also contains the key `ldap-password` if LDAP is enabled. `ldap.bind_password` will be ignored and + # picked from this secret in this case. + # The value is evaluated as a template. + existingSecret: "" + primary: + containerSecurityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL readinessProbe: failureThreshold: 5 From bf51c2263af48f1a5927363d45c136068c78bd21 Mon Sep 17 00:00:00 2001 From: Jens Kristian Villadsen Date: Mon, 11 Apr 2022 20:00:06 +0200 Subject: [PATCH 061/200] Update application.yaml Reverted to sane defaults --- src/main/resources/application.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index df616721414..891bf363894 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -86,8 +86,7 @@ hapi: # enable_index_missing_fields: false # enable_index_contained_resource: false # advanced_lucene_indexing: false - advanced_lucene_indexing: true -# enforce_referential_integrity_on_delete: false + # enforce_referential_integrity_on_delete: false # enforce_referential_integrity_on_write: false # etag_support_enabled: true # expunge_enabled: true From ae724f417224c41fb6ab6eb527b1e5f9d522a86e Mon Sep 17 00:00:00 2001 From: Jaison Baskaran Date: Wed, 13 Apr 2022 08:54:11 -0600 Subject: [PATCH 062/200] Bump to PRE10 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0c0f6280ed3..566ae629ef8 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT hapi-fhir-jpaserver-starter From c607a98728b2eb79bf73b266e3457bbab7f44e3c Mon Sep 17 00:00:00 2001 From: chgl Date: Fri, 15 Apr 2022 19:28:53 +0200 Subject: [PATCH 063/200] Updated to HAPI FHIR version 5.7.2 (#349) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 65d418fcee1..fc2346b7871 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.7.0 + 5.7.2 hapi-fhir-jpaserver-starter From f1e18d200a2656db37a81dd355909bf1fd9b587b Mon Sep 17 00:00:00 2001 From: Michael Buckley Date: Wed, 20 Apr 2022 12:23:10 -0400 Subject: [PATCH 064/200] Revert accidental default activation of experimental lucene indexing --- src/main/resources/application.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 7083b7d3ecb..deea04ff892 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -84,8 +84,8 @@ hapi: # enable_repository_validating_interceptor: false # enable_index_missing_fields: false # enable_index_contained_resource: false - # advanced_lucene_indexing: false - advanced_lucene_indexing: true + # This is an experimental feature, and does not fully support _total and other FHIR features. + advanced_lucene_indexing: false # enforce_referential_integrity_on_delete: false # enforce_referential_integrity_on_write: false # etag_support_enabled: true From c8da589636ca510445310efb36692806cb8b8a7b Mon Sep 17 00:00:00 2001 From: Patrick Werner Date: Wed, 20 Apr 2022 18:31:37 +0200 Subject: [PATCH 065/200] Add disclaimer for advanced_lucene_indexing added warning to advanced_lucene_indexing: false property --- src/main/resources/application.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 062008fa88d..95c7c5af06e 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -92,6 +92,8 @@ hapi: # enable_repository_validating_interceptor: false # enable_index_missing_fields: false # enable_index_contained_resource: false + ### !!Extended Lucene/Elasticsearch Indexing is still a experimental feature, expect some features (e.g. _total=accurate) to not work as expected!! + ### more information here: https://hapifhir.io/hapi-fhir/docs/server_jpa/elastic.html # advanced_lucene_indexing: false # enforce_referential_integrity_on_delete: false # enforce_referential_integrity_on_write: false From f736b6d8e63bd6bf802eeec57614fd768629a116 Mon Sep 17 00:00:00 2001 From: Michael Buckley Date: Fri, 29 Apr 2022 08:57:37 -0400 Subject: [PATCH 066/200] Bump to hapi PRE11 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 566ae629ef8..b8fb17f048c 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.0.0-PRE10-SNAPSHOT + 6.0.0-PRE11-SNAPSHOT hapi-fhir-jpaserver-starter From fdfa6fd711f411c58b9175223c05141cea0598fe Mon Sep 17 00:00:00 2001 From: chgl Date: Sun, 1 May 2022 21:48:18 +0200 Subject: [PATCH 067/200] Expose Prometheus metrics (#355) --- pom.xml | 7 +++++++ src/main/resources/application.yaml | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fc2346b7871..6a69b1b8932 100644 --- a/pom.xml +++ b/pom.xml @@ -317,6 +317,13 @@ ${spring_boot_version} + + + io.micrometer + micrometer-registry-prometheus + 1.8.5 + + com.zaxxer HikariCP diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 95c7c5af06e..45c3e13a60d 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -4,7 +4,7 @@ management: endpoints: web: exposure: - include: "health" + include: "health,prometheus" spring: main: allow-circular-references: true From 91e4105fd8c0c900e552633fbe02d436564b8675 Mon Sep 17 00:00:00 2001 From: Alejandro Medina Date: Fri, 6 May 2022 12:25:51 -0400 Subject: [PATCH 068/200] Add: of-type modifier option in application.yaml (#363) Co-authored-by: Alejandro Medina --- src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java | 9 +++++++++ .../ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java | 1 + src/main/resources/application.yaml | 1 + 3 files changed, 11 insertions(+) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java b/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java index 2ef74cfe3df..a4ab834f401 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java @@ -22,6 +22,7 @@ public class AppProperties { private Boolean openapi_enabled = false; private Boolean mdm_enabled = false; private boolean advanced_lucene_indexing = false; + private boolean enable_index_of_type = false; private Boolean allow_cascading_deletes = false; private Boolean allow_contains_searches = true; private Boolean allow_external_references = false; @@ -834,4 +835,12 @@ public void setQuitWait(Boolean quitWait) { private Boolean quitWait = false; } } + + public boolean getEnable_index_of_type() { + return enable_index_of_type; + } + + public void setEnable_index_of_type(boolean enable_index_of_type) { + this.enable_index_of_type = enable_index_of_type; + } } 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 d9617c75b21..3896167e556 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java @@ -178,6 +178,7 @@ public ModelConfig modelConfig(AppProperties appProperties, DaoConfig daoConfig) modelConfig.setNormalizedQuantitySearchLevel(appProperties.getNormalized_quantity_search_level()); modelConfig.setIndexOnContainedResources(appProperties.getEnable_index_contained_resource()); + modelConfig.setIndexIdentifierOfType(appProperties.getEnable_index_of_type()); return modelConfig; } diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 45c3e13a60d..67c86f43c86 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -91,6 +91,7 @@ hapi: # delete_expunge_enabled: true # enable_repository_validating_interceptor: false # enable_index_missing_fields: false + # enable_index_of_type: true # enable_index_contained_resource: false ### !!Extended Lucene/Elasticsearch Indexing is still a experimental feature, expect some features (e.g. _total=accurate) to not work as expected!! ### more information here: https://hapifhir.io/hapi-fhir/docs/server_jpa/elastic.html From 244113ba67d99b414f2c1bba6379145c16b922dd Mon Sep 17 00:00:00 2001 From: Dennis Verspuij <6680484+dennisverspuij@users.noreply.github.com> Date: Mon, 9 May 2022 21:39:56 +0200 Subject: [PATCH 069/200] Fix applying supported_resource_types option with list that already includes SearchParameter (#365) --- .../java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java index 3d94924ac21..fb48ffda537 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java @@ -131,8 +131,10 @@ protected void initialize() throws ServletException { // Customize supported resource types List supportedResourceTypes = appProperties.getSupported_resource_types(); - if (!supportedResourceTypes.isEmpty() && !supportedResourceTypes.contains("SearchParameter")) { - supportedResourceTypes.add("SearchParameter"); + if (!supportedResourceTypes.isEmpty()) { + if (!supportedResourceTypes.contains("SearchParameter")) { + supportedResourceTypes.add("SearchParameter"); + } daoRegistry.setSupportedResourceTypes(supportedResourceTypes); } From cd0b8d7392ceaef13a68aa9f56fb038489f0152f Mon Sep 17 00:00:00 2001 From: Tadgh Date: Mon, 16 May 2022 09:57:30 -0700 Subject: [PATCH 070/200] Bump pom and minimum java version --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index b8fb17f048c..a21f708461c 100644 --- a/pom.xml +++ b/pom.xml @@ -14,13 +14,13 @@ ca.uhn.hapi.fhir hapi-fhir - 6.0.0-PRE11-SNAPSHOT + 6.0.0 hapi-fhir-jpaserver-starter - 8 + 11 @@ -375,7 +375,7 @@ maven-compiler-plugin 3.8.1 - 8 + 11 From ffd0cb1a4f8a662f01058ce5bf7a5101c5298241 Mon Sep 17 00:00:00 2001 From: Tadgh Date: Mon, 16 May 2022 14:26:52 -0700 Subject: [PATCH 071/200] Bump ES version --- .../uhn/fhir/jpa/starter/ElasticsearchLastNR4IT.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/ElasticsearchLastNR4IT.java b/src/test/java/ca/uhn/fhir/jpa/starter/ElasticsearchLastNR4IT.java index 1387354f75e..366ed48d456 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/ElasticsearchLastNR4IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ElasticsearchLastNR4IT.java @@ -43,6 +43,7 @@ "hapi.fhir.fhir_version=r4", "hapi.fhir.lastn_enabled=true", "hapi.fhir.store_resource_in_lucene_index_enabled=true", + "hapi.fhir.advanced_lucene_indexing=true", "elasticsearch.enabled=true", // Because the port is set randomly, we will set the rest_url using the Initializer. // "elasticsearch.rest_url='http://localhost:9200'", @@ -57,10 +58,9 @@ public class ElasticsearchLastNR4IT { private IGenericClient ourClient; private FhirContext ourCtx; - private static final String ELASTIC_VERSION = "7.10.2"; - private static final String ELASTIC_IMAGE = "docker.elastic.co/elasticsearch/elasticsearch:" + ELASTIC_VERSION; - - private static ElasticsearchContainer embeddedElastic; + private static final String ELASTIC_VERSION = "7.16.3"; + private static final String ELASTIC_IMAGE = "docker.elastic.co/elasticsearch/elasticsearch:" + ELASTIC_VERSION; + private static ElasticsearchContainer embeddedElastic; @Autowired private ElasticsearchSvcImpl myElasticsearchSvc; @@ -90,8 +90,10 @@ void testLastN() throws IOException { obs.getSubject().setReferenceElement(id); String observationCode = "testobservationcode"; String codeSystem = "http://testobservationcodesystem"; + obs.getCode().addCoding().setCode(observationCode).setSystem(codeSystem); obs.setValue(new StringType(observationCode)); + Date effectiveDtm = new GregorianCalendar().getTime(); obs.setEffective(new DateTimeType(effectiveDtm)); obs.getCategoryFirstRep().addCoding().setCode("testcategorycode").setSystem("http://testcategorycodesystem"); @@ -103,6 +105,7 @@ void testLastN() throws IOException { .withParameter(Parameters.class, "max", new IntegerType(1)) .andParameter("subject", new StringType("Patient/" + id.getIdPart())) .execute(); + Bundle b = (Bundle) output.getParameter().get(0).getResource(); assertEquals(1, b.getTotal()); assertEquals(obsId, b.getEntry().get(0).getResource().getIdElement().toUnqualifiedVersionless()); From 87585ec7cc3d17c2bc4b51cc148f2537031244c2 Mon Sep 17 00:00:00 2001 From: Jaison Baskaran Date: Mon, 16 May 2022 16:18:30 -0600 Subject: [PATCH 072/200] hibernate search application properties updates. --- .../ca/uhn/fhir/jpa/starter/ElasticsearchLastNR4IT.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/ElasticsearchLastNR4IT.java b/src/test/java/ca/uhn/fhir/jpa/starter/ElasticsearchLastNR4IT.java index 366ed48d456..593faaea87e 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/ElasticsearchLastNR4IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ElasticsearchLastNR4IT.java @@ -50,7 +50,10 @@ "elasticsearch.username=SomeUsername", "elasticsearch.password=SomePassword", "elasticsearch.protocol=http", - "spring.main.allow-bean-definition-overriding=true" + "spring.main.allow-bean-definition-overriding=true", + "spring.jpa.properties.hibernate.search.enabled=true", + "spring.jpa.properties.hibernate.search.backend.type=elasticsearch", + "spring.jpa.properties.hibernate.search.backend.analysis.configurer=ca.uhn.fhir.jpa.search.elastic.HapiElasticsearchAnalysisConfigurer" }) @ContextConfiguration(initializers = ElasticsearchLastNR4IT.Initializer.class) public class ElasticsearchLastNR4IT { @@ -80,7 +83,8 @@ public void stop() { private int port; @Test - void testLastN() throws IOException { + void testLastN() throws IOException, InterruptedException { + Thread.sleep(2000); Patient pt = new Patient(); pt.addName().setFamily("Lastn").addGiven("Arthur"); @@ -105,7 +109,6 @@ void testLastN() throws IOException { .withParameter(Parameters.class, "max", new IntegerType(1)) .andParameter("subject", new StringType("Patient/" + id.getIdPart())) .execute(); - Bundle b = (Bundle) output.getParameter().get(0).getResource(); assertEquals(1, b.getTotal()); assertEquals(obsId, b.getEntry().get(0).getResource().getIdElement().toUnqualifiedVersionless()); From 12ea07b460bdf71dbf92e4d198a1869e6dbc75e5 Mon Sep 17 00:00:00 2001 From: Tadgh Date: Mon, 16 May 2022 15:45:49 -0700 Subject: [PATCH 073/200] make lastN test pass --- .../java/ca/uhn/fhir/jpa/starter/ElasticsearchLastNR4IT.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/ElasticsearchLastNR4IT.java b/src/test/java/ca/uhn/fhir/jpa/starter/ElasticsearchLastNR4IT.java index 593faaea87e..6ad49c61cf5 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/ElasticsearchLastNR4IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ElasticsearchLastNR4IT.java @@ -49,6 +49,7 @@ // "elasticsearch.rest_url='http://localhost:9200'", "elasticsearch.username=SomeUsername", "elasticsearch.password=SomePassword", + "elasticsearch.debug.refresh_after_write=true", "elasticsearch.protocol=http", "spring.main.allow-bean-definition-overriding=true", "spring.jpa.properties.hibernate.search.enabled=true", From 5942823f17f6cdf0e6c0888b16db0dcac9908db4 Mon Sep 17 00:00:00 2001 From: Tadgh Date: Thu, 19 May 2022 11:56:14 -0700 Subject: [PATCH 074/200] Remove value set provider as it causes a boot failure without lucene --- .../ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java index 0a714da84d8..616dbcaef40 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java @@ -78,8 +78,9 @@ public class BaseJpaRestfulServer extends RestfulServer { BulkDataExportProvider bulkDataExportProvider; @Autowired PartitionManagementProvider partitionManagementProvider; - @Autowired - ValueSetOperationProvider valueSetOperationProvider; + //TODO GGG RE-ADD ONCE FIXED IN HAPI-FHIR +// @Autowired +// ValueSetOperationProvider valueSetOperationProvider; @Autowired ReindexProvider reindexProvider; @Autowired @@ -134,7 +135,8 @@ protected void initialize() throws ServletException { registerProviders(resourceProviderFactory.createProviders()); registerProvider(jpaSystemProvider); - registerProvider(myValueSetOperationProvider); + //TODO GGG RE-ADD ONCE FIXED IN HAPI-FHIR +// registerProvider(myValueSetOperationProvider); /* * 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 @@ -361,7 +363,8 @@ protected void initialize() throws ServletException { } // valueSet Operations i.e $expand - registerProvider(valueSetOperationProvider); + //TODO GGG RE-ADD ONCE FIXED IN HAPI-FHIR +// registerProvider(myValueSetOperationProvider); //reindex Provider $reindex registerProvider(reindexProvider); From af842619b0f9de2684a633c49e9078416b231d5a Mon Sep 17 00:00:00 2001 From: Tadgh Date: Thu, 19 May 2022 12:59:22 -0700 Subject: [PATCH 075/200] Fix reindex provider --- .../java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java index 616dbcaef40..6d8a0f2c24d 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java @@ -1,5 +1,6 @@ package ca.uhn.fhir.jpa.starter; +import ca.uhn.fhir.batch2.jobs.reindex.ReindexProvider; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.context.support.IValidationSupport; @@ -28,7 +29,6 @@ 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.ReindexProvider; import ca.uhn.fhir.rest.server.provider.ResourceProviderFactory; import ca.uhn.fhir.rest.server.tenant.UrlBaseTenantIdentificationStrategy; import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; @@ -81,8 +81,8 @@ public class BaseJpaRestfulServer extends RestfulServer { //TODO GGG RE-ADD ONCE FIXED IN HAPI-FHIR // @Autowired // ValueSetOperationProvider valueSetOperationProvider; - @Autowired - ReindexProvider reindexProvider; + @Autowired + ReindexProvider reindexProvider; @Autowired BinaryStorageInterceptor binaryStorageInterceptor; @Autowired From d4bc6febb39d5fdd240bb3dee9c381d5003507bf Mon Sep 17 00:00:00 2001 From: Tadgh Date: Thu, 19 May 2022 13:34:29 -0700 Subject: [PATCH 076/200] Bump java version for test --- .github/workflows/maven.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 435d58b3dfd..9919452b887 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -17,9 +17,9 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v1 with: - java-version: 11 + java-version: 17 - name: Build with Maven run: mvn -B package --file pom.xml From 6ad499cecb82c3bc1c76b50aa58d46ea6b95a91b Mon Sep 17 00:00:00 2001 From: Tadgh Date: Thu, 19 May 2022 14:45:42 -0700 Subject: [PATCH 077/200] Bump to pre-01 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6019652990c..b2da79cfc32 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.0.0 + 6.1.0-PRE1-SNAPSHOT hapi-fhir-jpaserver-starter From e5b0fc721621eaece1291bbdacc72a4caabf67a2 Mon Sep 17 00:00:00 2001 From: Ken Stevens Date: Fri, 20 May 2022 15:24:17 -0400 Subject: [PATCH 078/200] fix build --- .../java/ca/uhn/fhir/jpa/starter/Application.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/Application.java b/src/main/java/ca/uhn/fhir/jpa/starter/Application.java index 0ba207207d5..2a26b5cb3ef 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/Application.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/Application.java @@ -1,5 +1,7 @@ package ca.uhn.fhir.jpa.starter; +import ca.uhn.fhir.batch2.jobs.config.Batch2JobsConfig; +import ca.uhn.fhir.jpa.batch2.JpaBatch2Config; import ca.uhn.fhir.jpa.starter.annotations.OnEitherVersion; import ca.uhn.fhir.jpa.starter.mdm.MdmConfig; import ca.uhn.fhir.jpa.subscription.channel.config.SubscriptionChannelConfig; @@ -24,7 +26,15 @@ @ServletComponentScan(basePackageClasses = { JpaRestfulServer.class}) @SpringBootApplication(exclude = {ElasticsearchRestClientAutoConfiguration.class}) -@Import({SubscriptionSubmitterConfig.class, SubscriptionProcessorConfig.class, SubscriptionChannelConfig.class, WebsocketDispatcherConfig.class, MdmConfig.class}) +@Import({ + SubscriptionSubmitterConfig.class, + SubscriptionProcessorConfig.class, + SubscriptionChannelConfig.class, + WebsocketDispatcherConfig.class, + MdmConfig.class, + JpaBatch2Config.class, + Batch2JobsConfig.class +}) public class Application extends SpringBootServletInitializer { public static void main(String[] args) { From 4dacf6ae904fa45a04c38a7bb9622cdff9115247 Mon Sep 17 00:00:00 2001 From: Tadgh Date: Sat, 21 May 2022 09:54:50 -0700 Subject: [PATCH 079/200] Re-add valuesetoperation provider --- .../ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java index 6d8a0f2c24d..7da8638f8e7 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java @@ -78,11 +78,11 @@ public class BaseJpaRestfulServer extends RestfulServer { BulkDataExportProvider bulkDataExportProvider; @Autowired PartitionManagementProvider partitionManagementProvider; - //TODO GGG RE-ADD ONCE FIXED IN HAPI-FHIR -// @Autowired -// ValueSetOperationProvider valueSetOperationProvider; - @Autowired - ReindexProvider reindexProvider; + + @Autowired + ValueSetOperationProvider valueSetOperationProvider; + @Autowired + ReindexProvider reindexProvider; @Autowired BinaryStorageInterceptor binaryStorageInterceptor; @Autowired From f39393ca976db25e0dda588bb67451683af074e6 Mon Sep 17 00:00:00 2001 From: Tadgh Date: Sat, 21 May 2022 10:10:20 -0700 Subject: [PATCH 080/200] Disable lucene by default --- src/main/resources/application.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 0174848fb7f..9492327dc52 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -40,7 +40,7 @@ spring: # hibernate.cache.use_structured_entries: false # hibernate.cache.use_minimal_puts: false ### These settings will enable fulltext search with lucene - # hibernate.search.enabled: true + hibernate.search.enabled: false # hibernate.search.backend.type: lucene # hibernate.search.backend.analysis.configurer: ca.uhn.fhir.jpa.search.HapiLuceneAnalysisConfigurer # hibernate.search.backend.directory.type: local-filesystem From d660dc137004d8bbe2c635fe6b7ae31701f286c0 Mon Sep 17 00:00:00 2001 From: Tadgh Date: Sat, 21 May 2022 10:31:49 -0700 Subject: [PATCH 081/200] Re-add valueset operation provider --- .../java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java index 7da8638f8e7..c7ec35b34a5 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java @@ -135,8 +135,6 @@ protected void initialize() throws ServletException { registerProviders(resourceProviderFactory.createProviders()); registerProvider(jpaSystemProvider); - //TODO GGG RE-ADD ONCE FIXED IN HAPI-FHIR -// registerProvider(myValueSetOperationProvider); /* * 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 @@ -363,8 +361,7 @@ protected void initialize() throws ServletException { } // valueSet Operations i.e $expand - //TODO GGG RE-ADD ONCE FIXED IN HAPI-FHIR -// registerProvider(myValueSetOperationProvider); + registerProvider(myValueSetOperationProvider); //reindex Provider $reindex registerProvider(reindexProvider); From 9882a1cd58065bf90f2d9ef011d99ffe7acbb779 Mon Sep 17 00:00:00 2001 From: markiantorno Date: Tue, 24 May 2022 17:37:45 -0400 Subject: [PATCH 082/200] adding smoke test files --- .../smoketestfiles/patient_batch_create.json | 85 +++++++ .../smoketestfiles/patient_create.json | 162 +++++++++++++ .../smoketestfiles/patient_patch.json | 7 + .../patient_process_message.json | 63 +++++ .../smoketestfiles/patient_update.json | 20 ++ src/test/smoketest/SMOKE_TEST.md | 10 + src/test/smoketest/http-client.env.json | 7 + src/test/smoketest/plain_server.rest | 223 ++++++++++++++++++ 8 files changed, 577 insertions(+) create mode 100644 src/test/resources/smoketestfiles/patient_batch_create.json create mode 100644 src/test/resources/smoketestfiles/patient_create.json create mode 100644 src/test/resources/smoketestfiles/patient_patch.json create mode 100644 src/test/resources/smoketestfiles/patient_process_message.json create mode 100644 src/test/resources/smoketestfiles/patient_update.json create mode 100644 src/test/smoketest/SMOKE_TEST.md create mode 100644 src/test/smoketest/http-client.env.json create mode 100644 src/test/smoketest/plain_server.rest diff --git a/src/test/resources/smoketestfiles/patient_batch_create.json b/src/test/resources/smoketestfiles/patient_batch_create.json new file mode 100644 index 00000000000..c476f72f931 --- /dev/null +++ b/src/test/resources/smoketestfiles/patient_batch_create.json @@ -0,0 +1,85 @@ +{ + "resourceType": "Bundle", + "id": "bundle-transaction", + "meta": { + "lastUpdated": "2014-08-18T01:43:30Z" + }, + "type": "transaction", + "entry": [ + { + "resource": { + "resourceType": "Patient", + "text": { + "status": "generated", + "div": "
Some narrative
" + }, + "active": true, + "name": [ + { + "use": "official", + "family": "Iantorno", + "given": [ + "Mark" + ] + } + ], + "gender": "male", + "birthDate": "1983-06-23" + }, + "request": { + "method": "POST", + "url": "Patient" + } + }, + { + "resource": { + "resourceType": "Patient", + "text": { + "status": "generated", + "div": "
Some narrative
" + }, + "active": true, + "name": [ + { + "use": "official", + "family": "Iantorno", + "given": [ + "Alexander" + ] + } + ], + "gender": "male", + "birthDate": "1993-08-16" + }, + "request": { + "method": "POST", + "url": "Patient" + } + }, + { + "resource": { + "resourceType": "Patient", + "text": { + "status": "generated", + "div": "
Some narrative
" + }, + "active": true, + "name": [ + { + "use": "official", + "family": "Cash", + "given": [ + "Johnny" + ] + } + ], + "gender": "male", + "birthDate": "1932-02-26" + }, + "request": { + "method": "POST", + "url": "Patient" + } + } + ] +} \ No newline at end of file diff --git a/src/test/resources/smoketestfiles/patient_create.json b/src/test/resources/smoketestfiles/patient_create.json new file mode 100644 index 00000000000..73f58999de6 --- /dev/null +++ b/src/test/resources/smoketestfiles/patient_create.json @@ -0,0 +1,162 @@ +{ + "resourceType": "Patient", + "id": "example", + "text": { + "status": "generated", + "div": "
\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t
NamePeter James \n Chalmers ("Jim")\n
Address534 Erewhon, Pleasantville, Vic, 3999
ContactsHome: unknown. Work: (03) 5555 6473
IdMRN: 12345 (Acme Healthcare)
\n\t\t
" + }, + "identifier": [ + { + "use": "usual", + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0203", + "code": "MR" + } + ] + }, + "system": "urn:oid:1.2.36.146.595.217.0.1", + "value": "12345", + "period": { + "start": "2001-05-06" + }, + "assigner": { + "display": "Acme Healthcare" + } + } + ], + "active": true, + "name": [ + { + "use": "official", + "family": "Chalmers", + "given": [ + "Peter", + "James" + ] + }, + { + "use": "usual", + "given": [ + "Jim" + ] + }, + { + "use": "maiden", + "family": "Windsor", + "given": [ + "Peter", + "James" + ], + "period": { + "end": "2002" + } + } + ], + "telecom": [ + { + "use": "home" + }, + { + "system": "phone", + "value": "(03) 5555 6473", + "use": "work", + "rank": 1 + }, + { + "system": "phone", + "value": "(03) 3410 5613", + "use": "mobile", + "rank": 2 + }, + { + "system": "phone", + "value": "(03) 5555 8834", + "use": "old", + "period": { + "end": "2014" + } + } + ], + "gender": "male", + "birthDate": "1974-12-25", + "_birthDate": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/patient-birthTime", + "valueDateTime": "1974-12-25T14:35:45-05:00" + } + ] + }, + "deceasedBoolean": false, + "address": [ + { + "use": "home", + "type": "both", + "text": "534 Erewhon St PeasantVille, Rainbow, Vic 3999", + "line": [ + "534 Erewhon St" + ], + "city": "PleasantVille", + "district": "Rainbow", + "state": "Vic", + "postalCode": "3999", + "period": { + "start": "1974-12-25" + } + } + ], + "contact": [ + { + "relationship": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0131", + "code": "N" + } + ] + } + ], + "name": { + "family": "du Marché", + "_family": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/humanname-own-prefix", + "valueString": "VV" + } + ] + }, + "given": [ + "Bénédicte" + ] + }, + "telecom": [ + { + "system": "phone", + "value": "+33 (237) 998327" + } + ], + "address": { + "use": "home", + "type": "both", + "line": [ + "534 Erewhon St" + ], + "city": "PleasantVille", + "district": "Rainbow", + "state": "Vic", + "postalCode": "3999", + "period": { + "start": "1974-12-25" + } + }, + "gender": "female", + "period": { + "start": "2012" + } + } + ] +} \ No newline at end of file diff --git a/src/test/resources/smoketestfiles/patient_patch.json b/src/test/resources/smoketestfiles/patient_patch.json new file mode 100644 index 00000000000..b1cfaaa25d6 --- /dev/null +++ b/src/test/resources/smoketestfiles/patient_patch.json @@ -0,0 +1,7 @@ +[ + { + "op": "add", + "path": "/active", + "value": false + } +] \ No newline at end of file diff --git a/src/test/resources/smoketestfiles/patient_process_message.json b/src/test/resources/smoketestfiles/patient_process_message.json new file mode 100644 index 00000000000..033f3cf4a86 --- /dev/null +++ b/src/test/resources/smoketestfiles/patient_process_message.json @@ -0,0 +1,63 @@ +{ + "resourceType": "MessageHeader", + "id": "{{batch_patient_id}}", + "text": { + "status": "generated", + "div": "
\n\t\t\t

Update Person resource for Peter James CHALMERS (Jim), MRN: 12345 (Acme Healthcare)

\n\t\t
" + }, + "eventCoding": { + "system": "http://example.org/fhir/message-events", + "code": "admin-notify" + }, + "destination": [ + { + "name": "Acme Message Gateway", + "target": { + "reference": "Device/example" + }, + "endpoint": "llp:10.11.12.14:5432", + "receiver": { + "reference": "http://acme.com/ehr/fhir/Practitioner/2323-33-4" + } + } + ], + "sender": { + "reference": "Organization/1" + }, + "enterer": { + "reference": "Practitioner/example" + }, + "author": { + "reference": "Practitioner/example" + }, + "source": { + "name": "Acme Central Patient Registry", + "software": "FooBar Patient Manager", + "version": "3.1.45.AABB", + "contact": { + "system": "phone", + "value": "+1 (555) 123 4567" + }, + "endpoint": "llp:10.11.12.13:5432" + }, + "reason": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/message-reasons-encounter", + "code": "admit" + } + ] + }, + "response": { + "identifier": { + "value": "5015fe84-8e76-4526-89d8-44b322e8d4fb" + }, + "code": "ok" + }, + "focus": [ + { + "reference": "Patient/example" + } + ], + "definition": "http:////acme.com/ehr/fhir/messagedefinition/patientrequest" +} \ No newline at end of file diff --git a/src/test/resources/smoketestfiles/patient_update.json b/src/test/resources/smoketestfiles/patient_update.json new file mode 100644 index 00000000000..b182b2fbf52 --- /dev/null +++ b/src/test/resources/smoketestfiles/patient_update.json @@ -0,0 +1,20 @@ +{ + "resourceType": "Patient", + "id": "{{batch_patient_id}}", + "text": { + "status": "generated", + "div": "
Some narrative
" + }, + "active": true, + "name": [ + { + "use": "official", + "family": "Iantoryes", + "given": [ + "Mark" + ] + } + ], + "gender": "male", + "birthDate": "1983-06-23" +} diff --git a/src/test/smoketest/SMOKE_TEST.md b/src/test/smoketest/SMOKE_TEST.md new file mode 100644 index 00000000000..3370613fa85 --- /dev/null +++ b/src/test/smoketest/SMOKE_TEST.md @@ -0,0 +1,10 @@ +# JPA Server Starter Smoke Tests + +--- + +### What they do... +When updating the HAPI-FHIR version, or making changes to the JPA server starter code itself, + +### Requirements... + +### How to run the smoke test... diff --git a/src/test/smoketest/http-client.env.json b/src/test/smoketest/http-client.env.json new file mode 100644 index 00000000000..85b8ef4a45d --- /dev/null +++ b/src/test/smoketest/http-client.env.json @@ -0,0 +1,7 @@ +{ + "default": { + "host": "localhost:8080", + "username": "username", + "password": "password" + } +} \ No newline at end of file diff --git a/src/test/smoketest/plain_server.rest b/src/test/smoketest/plain_server.rest new file mode 100644 index 00000000000..47e15154315 --- /dev/null +++ b/src/test/smoketest/plain_server.rest @@ -0,0 +1,223 @@ +### Create Single Patient +# https://hapifhir.io/hapi-fhir/docs/client/generic_client.html#create-type +POST http://{{host}}/fhir/Patient +Content-Type: application/json + +< ../resources/smoketestfiles/patient_create.json + +> {% + client.test("Patient created successfully", function() { + client.assert(response.status === 201, "Response status is not 201"); + }); + client.test("Response content-type is json", function() { + const type = response.contentType.mimeType; + client.assert(type === "application/fhir+json", "Expected 'application/fhir+json' but received '" + type + "'"); + }); + client.test("Response resourceType is Patient", function() { + const resourceType = response.body.resourceType; + client.assert(resourceType === "Patient", "Expected 'Patient' but received '" + resourceType + "'"); + }); + client.global.set("single_patient_id", response.body.id); + client.global.set("single_patient_family_name", response.body.name[0].family); +%} + +### Search Single Patient +# https://hapifhir.io/hapi-fhir/docs/client/generic_client.html#search +GET http://{{host}}/fhir/Patient?name={{single_patient_family_name}}&_id={{single_patient_id}} + +> {% + client.test("Patient created successfully", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); + client.test("Response content-type is json", function() { + const type = response.contentType.mimeType; + client.assert(type === "application/fhir+json", "Expected 'application/fhir+json' but received '" + type + "'"); + }); + client.test("Response resourceType is Bundle", function() { + const resourceType = response.body.resourceType; + client.assert(resourceType === "Bundle", "Expected 'Bundle' but received '" + resourceType + "'"); + }); + client.test("Total patients found is 1", function() { + const totalFound = response.body.total; + client.assert(totalFound === 1, "Expected '1' match but found '" + totalFound + "'"); + }); + %} + +### Delete Patient +# https://hapifhir.io/hapi-fhir/docs/client/generic_client.html#delete-instance +DELETE http://{{host}}/fhir/Patient/{{single_patient_id}} + +> {% + client.test("Patient deleted successfully", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); + client.test("Response content-type is json", function() { + const type = response.contentType.mimeType; + client.assert(type === "application/fhir+json", "Expected 'application/fhir+json' but received '" + type + "'"); + }); + client.test("Response resourceType is OperationOutcome", function() { + const resourceType = response.body.resourceType; + client.assert(resourceType === "OperationOutcome", "Expected 'OperationOutcome' but received '" + resourceType + "'"); + }); +%} + +### Batch Patient Create +# https://hapifhir.io/hapi-fhir/docs/client/generic_client.html#transaction-server +POST http://{{host}}/fhir/ +Content-Type: application/json + +< ../resources/smoketestfiles/patient_batch_create.json + +> {% + client.test("Bundle transaction completed successfully", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); + client.test("Response content-type is json", function() { + const type = response.contentType.mimeType; + client.assert(type === "application/fhir+json", "Expected 'application/fhir+json' but received '" + type + "'"); + }); + client.test("Response resourceType is Bundle", function() { + const resourceType = response.body.resourceType; + client.assert(resourceType === "Bundle", "Expected 'Bundle' but received '" + resourceType + "'"); + }); + client.test("All patient additions successful", function() { + for (var index = 0; index < response.body.entry.length; index++) { + client.assert(response.body.entry[index].response.status === "201 Created", "Expected '201 Created' for patient index " + index + " but received '" + response.body.entry[index].response.status + "'"); + } + }); + const batch_patient_location = response.body.entry[0].response.location; + const indexOfHistory = batch_patient_location.lastIndexOf("/_history"); + trimmed_location = batch_patient_location.substring(0, indexOfHistory); + trimmed_location = trimmed_location.replace("Patient/", "") + client.global.set("batch_patient_id", trimmed_location); +%} + +### Update - Instance +# https://hapifhir.io/hapi-fhir/docs/client/generic_client.html#update-instance +PUT http://{{host}}/fhir/Patient/{{batch_patient_id}} +Content-Type: application/json + +< ../resources/smoketestfiles/patient_update.json + +> {% + client.test("Bundle transaction completed successfully", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); + client.test("Response content-type is json", function() { + const type = response.contentType.mimeType; + client.assert(type === "application/fhir+json", "Expected 'application/fhir+json' but received '" + type + "'"); + }); + client.test("Response resourceType is Patient", function() { + const resourceType = response.body.resourceType; + client.assert(resourceType === "Patient", "Expected 'Patient' but received '" + resourceType + "'"); + }); + client.test("Test last name updated", function() { + const familyName = response.body.name[0].family; + client.assert(familyName === "Iantoryes", "Expected updated family name 'Iantoryes' but received '" + familyName + "'"); + }); + client.test("Test version number updated", function() { + const versionId = response.body.meta.versionId; + client.assert(versionId === "2", "Expected updated versionId name '2' but received '" + versionId + "'"); + }); +%} + +### Patch - Instance +# https://hapifhir.io/hapi-fhir/docs/client/generic_client.html#patch-instance +PATCH http://{{host}}/fhir/Patient/{{batch_patient_id}} +Content-Type: application/json-patch+json + +< ../resources/smoketestfiles/patient_patch.json + +> {% + client.test("Patch operation completed successfully", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); + client.test("Response content-type is json", function() { + const type = response.contentType.mimeType; + client.assert(type === "application/fhir+json", "Expected 'application/fhir+json' but received '" + type + "'"); + }); + client.test("Response resourceType is Patient", function() { + const resourceType = response.body.resourceType; + client.assert(resourceType === "Patient", "Expected 'Patient' but received '" + resourceType + "'"); + }); + client.test("Test active field patched", function() { + const active = response.body.active; + client.assert(active === false, "Expected updated active 'false' but received '" + active + "'"); + }); +%} + +### History - Server/Type/Instance +# https://hapifhir.io/hapi-fhir/docs/client/generic_client.html#history-servertypeinstance +GET http://{{host}}/fhir/Patient/{{batch_patient_id}}/_history + +> {% + client.test("History completed successfully", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); + client.test("Response content-type is json", function() { + const type = response.contentType.mimeType; + client.assert(type === "application/fhir+json", "Expected 'application/fhir+json' but received '" + type + "'"); + }); + client.test("Response resourceType is Bundle", function() { + const resourceType = response.body.resourceType; + client.assert(resourceType === "Bundle", "Expected 'Bundle' but received '" + resourceType + "'"); + }); + client.test("Test receive history type", function() { + const type = response.body.type; + client.assert(type === "history", "Expected type 'history' but received '" + type + "'"); + }); +%} + +### Capability Statement +# https://hapifhir.io/hapi-fhir/docs/client/generic_client.html#capability-statement-metadata-server +GET http://{{host}}/fhir/metadata + +> {% + client.test("CapabilityStatement fetched successfully", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); + client.test("Response content-type is json", function() { + const type = response.contentType.mimeType; + client.assert(type === "application/fhir+json", "Expected 'application/fhir+json' but received '" + type + "'"); + }); + client.test("Response resourceType is CapabilityStatement", function() { + const resourceType = response.body.resourceType; + client.assert(resourceType === "CapabilityStatement", "Expected 'CapabilityStatement' but received '" + resourceType + "'"); + }); +%} + +### Extended Operations - everything +# https://hapifhir.io/hapi-fhir/docs/client/generic_client.html#extended-operations +GET http://{{host}}/fhir/Patient/{{batch_patient_id}}/$everything + +> {% + client.test("$everything operation successful", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); + client.test("Response content-type is json", function() { + const type = response.contentType.mimeType; + client.assert(type === "application/fhir+json", "Expected 'application/fhir+json' but received '" + type + "'"); + }); + client.test("Response resourceType is Bundle", function() { + const resourceType = response.body.resourceType; + client.assert(resourceType === "Bundle", "Expected 'Bundle' but received '" + resourceType + "'"); + }); +%} + +### Extended Operations - validate +# https://hapifhir.io/hapi-fhir/docs/client/generic_client.html#extended-operations +POST http://{{host}}/fhir/Patient/{{batch_patient_id}}/$validate + +> {% + client.test("$validate operation successful", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); + client.test("Response content-type is json", function() { + const type = response.contentType.mimeType; + client.assert(type === "application/fhir+json", "Expected 'application/fhir+json' but received '" + type + "'"); + }); + client.test("Response resourceType is OperationOutcome", function() { + const resourceType = response.body.resourceType; + client.assert(resourceType === "OperationOutcome", "Expected 'OperationOutcome' but received '" + resourceType + "'"); + }); +%} \ No newline at end of file From 067c7f16c4f450599e088b7a30f4cb50475495ad Mon Sep 17 00:00:00 2001 From: markiantorno Date: Tue, 24 May 2022 19:18:07 -0400 Subject: [PATCH 083/200] adding base documentation --- src/test/smoketest/SMOKE_TEST.md | 52 +++++++++++++++++-- src/test/smoketest/plain_server.rest | 30 +++++------ .../smoketestfiles/patient_batch_create.json | 0 .../smoketestfiles/patient_create.json | 0 .../smoketestfiles/patient_patch.json | 0 .../patient_process_message.json | 0 .../smoketestfiles/patient_update.json | 0 7 files changed, 63 insertions(+), 19 deletions(-) rename src/test/{resources => smoketest}/smoketestfiles/patient_batch_create.json (100%) rename src/test/{resources => smoketest}/smoketestfiles/patient_create.json (100%) rename src/test/{resources => smoketest}/smoketestfiles/patient_patch.json (100%) rename src/test/{resources => smoketest}/smoketestfiles/patient_process_message.json (100%) rename src/test/{resources => smoketest}/smoketestfiles/patient_update.json (100%) diff --git a/src/test/smoketest/SMOKE_TEST.md b/src/test/smoketest/SMOKE_TEST.md index 3370613fa85..577e685cef1 100644 --- a/src/test/smoketest/SMOKE_TEST.md +++ b/src/test/smoketest/SMOKE_TEST.md @@ -2,9 +2,53 @@ --- -### What they do... -When updating the HAPI-FHIR version, or making changes to the JPA server starter code itself, +When updating the supporting HAPI-FHIR version, or making changes to the JPA server starter code itself, it is sometimes +useful to run basic smoke tests to ensure the basic functionality of the server is maintained. The goal of these tests is +to provide users a quick way to run groups of tests that correspond to various sections within the +[HAPI-FHIR documentation][Link-HAPI-FHIR-docs]. -### Requirements... +### Requirements +Tests are written in IntelliJ [HTTP Client Request files][Link-HTTP-Client-Req-Intro]. Ultimate edition of IntelliJ +is required to run these tests. -### How to run the smoke test... +For more details on integrated tooling and request syntax, there is [documentation available][Link-HTTP-Client-Req-Exploring] +on the jetbrains website, in addition to the [API reference][Link-HTTP-Client-Req-API]. + +### Formatting +As mentioned, each test file corresponds to a given section within the hapifhir documentation as close as possible. For +example, there is a `plain_server.rest` file, which attemps to smoke test all basic functionality outlined in the section +[within the docs][Link-HAPI-FHIR-docs-plain-server]. + +Individual tests are formatted as follows: +```javascript +### Test Title Here +# +REST-OPERATION ENDPOINT-URL +// Verification Steps +``` + +Users can setup custom environment variables for running tests locally against their own servers. This can be done by +adding environment information within the `http-client.env.json` file. By default, we provide the following: +```json +{ + "default": { + "host": "localhost:8080", + "username": "username", + "password": "password" + } +} +``` + +### Running the Tests +Within IntelliJ, right click the file you wish to run tests in and select the `Run All` option from the menu. + +**Important:** Tests may not work individually when run. Often times, tests need to be run sequentially, as they depend +on resources/references from previous tests to complete. _(An example of this would be adding a Patient, saving the id, +then using that idea to test if we can successfully PATCH that Patient resource.)_ + + +[Link-HAPI-FHIR-docs]: https://hapifhir.io/hapi-fhir/docs/ +[Link-HAPI-FHIR-docs-plain-server]: https://hapifhir.io/hapi-fhir/docs/server_plain/server_types.html +[Link-HTTP-Client-Req-Intro]: https://www.jetbrains.com/help/idea/http-client-in-product-code-editor.html +[Link-HTTP-Client-Req-Exploring]: https://www.jetbrains.com/help/idea/exploring-http-syntax.html +[Link-HTTP-Client-Req-API]: https://www.jetbrains.com/help/idea/http-response-handling-api-reference.html \ No newline at end of file diff --git a/src/test/smoketest/plain_server.rest b/src/test/smoketest/plain_server.rest index 47e15154315..c5856ffa8f9 100644 --- a/src/test/smoketest/plain_server.rest +++ b/src/test/smoketest/plain_server.rest @@ -1,9 +1,9 @@ ### Create Single Patient -# https://hapifhir.io/hapi-fhir/docs/client/generic_client.html#create-type +# https://hapifhir.io/hapi-fhir/docs/server_plain/rest_operations.html#type_create POST http://{{host}}/fhir/Patient Content-Type: application/json -< ../resources/smoketestfiles/patient_create.json +< smoketestfiles/patient_create.json > {% client.test("Patient created successfully", function() { @@ -22,11 +22,11 @@ Content-Type: application/json %} ### Search Single Patient -# https://hapifhir.io/hapi-fhir/docs/client/generic_client.html#search +# https://hapifhir.io/hapi-fhir/docs/server_plain/rest_operations.html#type_search GET http://{{host}}/fhir/Patient?name={{single_patient_family_name}}&_id={{single_patient_id}} > {% - client.test("Patient created successfully", function() { + client.test("Patient search successful", function() { client.assert(response.status === 200, "Response status is not 200"); }); client.test("Response content-type is json", function() { @@ -44,7 +44,7 @@ GET http://{{host}}/fhir/Patient?name={{single_patient_family_name}}&_id={{singl %} ### Delete Patient -# https://hapifhir.io/hapi-fhir/docs/client/generic_client.html#delete-instance +# https://hapifhir.io/hapi-fhir/docs/server_plain/rest_operations.html#instance_delete DELETE http://{{host}}/fhir/Patient/{{single_patient_id}} > {% @@ -62,11 +62,11 @@ DELETE http://{{host}}/fhir/Patient/{{single_patient_id}} %} ### Batch Patient Create -# https://hapifhir.io/hapi-fhir/docs/client/generic_client.html#transaction-server +# https://hapifhir.io/hapi-fhir/docs/server_plain/rest_operations.html#system_transaction POST http://{{host}}/fhir/ Content-Type: application/json -< ../resources/smoketestfiles/patient_batch_create.json +< smoketestfiles/patient_batch_create.json > {% client.test("Bundle transaction completed successfully", function() { @@ -93,11 +93,11 @@ Content-Type: application/json %} ### Update - Instance -# https://hapifhir.io/hapi-fhir/docs/client/generic_client.html#update-instance +# https://hapifhir.io/hapi-fhir/docs/server_plain/rest_operations.html#instance_update PUT http://{{host}}/fhir/Patient/{{batch_patient_id}} Content-Type: application/json -< ../resources/smoketestfiles/patient_update.json +< smoketestfiles/patient_update.json > {% client.test("Bundle transaction completed successfully", function() { @@ -122,11 +122,11 @@ Content-Type: application/json %} ### Patch - Instance -# https://hapifhir.io/hapi-fhir/docs/client/generic_client.html#patch-instance +# https://hapifhir.io/hapi-fhir/docs/server_plain/rest_operations.html#instance-level-patch PATCH http://{{host}}/fhir/Patient/{{batch_patient_id}} Content-Type: application/json-patch+json -< ../resources/smoketestfiles/patient_patch.json +< smoketestfiles/patient_patch.json > {% client.test("Patch operation completed successfully", function() { @@ -147,7 +147,7 @@ Content-Type: application/json-patch+json %} ### History - Server/Type/Instance -# https://hapifhir.io/hapi-fhir/docs/client/generic_client.html#history-servertypeinstance +# https://hapifhir.io/hapi-fhir/docs/server_plain/rest_operations.html#history GET http://{{host}}/fhir/Patient/{{batch_patient_id}}/_history > {% @@ -169,7 +169,7 @@ GET http://{{host}}/fhir/Patient/{{batch_patient_id}}/_history %} ### Capability Statement -# https://hapifhir.io/hapi-fhir/docs/client/generic_client.html#capability-statement-metadata-server +# https://hapifhir.io/hapi-fhir/docs/server_plain/rest_operations.html#system_capabilities GET http://{{host}}/fhir/metadata > {% @@ -187,7 +187,7 @@ GET http://{{host}}/fhir/metadata %} ### Extended Operations - everything -# https://hapifhir.io/hapi-fhir/docs/client/generic_client.html#extended-operations +# https://hapifhir.io/hapi-fhir/docs/server_plain/rest_operations_operations.html GET http://{{host}}/fhir/Patient/{{batch_patient_id}}/$everything > {% @@ -205,7 +205,7 @@ GET http://{{host}}/fhir/Patient/{{batch_patient_id}}/$everything %} ### Extended Operations - validate -# https://hapifhir.io/hapi-fhir/docs/client/generic_client.html#extended-operations +# https://hapifhir.io/hapi-fhir/docs/server_plain/rest_operations_operations.html POST http://{{host}}/fhir/Patient/{{batch_patient_id}}/$validate > {% diff --git a/src/test/resources/smoketestfiles/patient_batch_create.json b/src/test/smoketest/smoketestfiles/patient_batch_create.json similarity index 100% rename from src/test/resources/smoketestfiles/patient_batch_create.json rename to src/test/smoketest/smoketestfiles/patient_batch_create.json diff --git a/src/test/resources/smoketestfiles/patient_create.json b/src/test/smoketest/smoketestfiles/patient_create.json similarity index 100% rename from src/test/resources/smoketestfiles/patient_create.json rename to src/test/smoketest/smoketestfiles/patient_create.json diff --git a/src/test/resources/smoketestfiles/patient_patch.json b/src/test/smoketest/smoketestfiles/patient_patch.json similarity index 100% rename from src/test/resources/smoketestfiles/patient_patch.json rename to src/test/smoketest/smoketestfiles/patient_patch.json diff --git a/src/test/resources/smoketestfiles/patient_process_message.json b/src/test/smoketest/smoketestfiles/patient_process_message.json similarity index 100% rename from src/test/resources/smoketestfiles/patient_process_message.json rename to src/test/smoketest/smoketestfiles/patient_process_message.json diff --git a/src/test/resources/smoketestfiles/patient_update.json b/src/test/smoketest/smoketestfiles/patient_update.json similarity index 100% rename from src/test/resources/smoketestfiles/patient_update.json rename to src/test/smoketest/smoketestfiles/patient_update.json From 8d6247bfe8357f0769ff7478e3533ed765006bae Mon Sep 17 00:00:00 2001 From: markiantorno Date: Tue, 24 May 2022 19:20:13 -0400 Subject: [PATCH 084/200] wip --- src/test/smoketest/SMOKE_TEST.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/smoketest/SMOKE_TEST.md b/src/test/smoketest/SMOKE_TEST.md index 577e685cef1..3610e6f15d8 100644 --- a/src/test/smoketest/SMOKE_TEST.md +++ b/src/test/smoketest/SMOKE_TEST.md @@ -44,7 +44,8 @@ Within IntelliJ, right click the file you wish to run tests in and select the `R **Important:** Tests may not work individually when run. Often times, tests need to be run sequentially, as they depend on resources/references from previous tests to complete. _(An example of this would be adding a Patient, saving the id, -then using that idea to test if we can successfully PATCH that Patient resource.)_ +then using that saved id to test if we can successfully PATCH that Patient resource. Without that saved id from the +previous test creating that patient, the PATCH test will fail.)_ [Link-HAPI-FHIR-docs]: https://hapifhir.io/hapi-fhir/docs/ From b71880e39ea06392a0a9aefdf40bc2621831dbef Mon Sep 17 00:00:00 2001 From: Mark Iantorno Date: Wed, 25 May 2022 12:55:16 -0400 Subject: [PATCH 085/200] Update src/test/smoketest/SMOKE_TEST.md Co-authored-by: Ken Stevens --- src/test/smoketest/SMOKE_TEST.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/smoketest/SMOKE_TEST.md b/src/test/smoketest/SMOKE_TEST.md index 3610e6f15d8..5f798250bb4 100644 --- a/src/test/smoketest/SMOKE_TEST.md +++ b/src/test/smoketest/SMOKE_TEST.md @@ -15,7 +15,7 @@ For more details on integrated tooling and request syntax, there is [documentati on the jetbrains website, in addition to the [API reference][Link-HTTP-Client-Req-API]. ### Formatting -As mentioned, each test file corresponds to a given section within the hapifhir documentation as close as possible. For +Each test file corresponds to a given section within the hapifhir documentation as close as possible. For example, there is a `plain_server.rest` file, which attemps to smoke test all basic functionality outlined in the section [within the docs][Link-HAPI-FHIR-docs-plain-server]. From 788015f467b91c9fb5dfaf043a7a4edd21553f94 Mon Sep 17 00:00:00 2001 From: Mark Iantorno Date: Wed, 25 May 2022 12:55:22 -0400 Subject: [PATCH 086/200] Update src/test/smoketest/SMOKE_TEST.md Co-authored-by: Ken Stevens --- src/test/smoketest/SMOKE_TEST.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/test/smoketest/SMOKE_TEST.md b/src/test/smoketest/SMOKE_TEST.md index 5f798250bb4..7ced8ca084f 100644 --- a/src/test/smoketest/SMOKE_TEST.md +++ b/src/test/smoketest/SMOKE_TEST.md @@ -2,8 +2,7 @@ --- -When updating the supporting HAPI-FHIR version, or making changes to the JPA server starter code itself, it is sometimes -useful to run basic smoke tests to ensure the basic functionality of the server is maintained. The goal of these tests is +When updating the supporting HAPI-FHIR version, or making changes to the JPA server starter code itself, it is recommended to run basic smoke tests to ensure the basic functionality of the server is maintained. The goal of these tests is to provide users a quick way to run groups of tests that correspond to various sections within the [HAPI-FHIR documentation][Link-HAPI-FHIR-docs]. From e9ff226750d5ac335d46bbddad1bdaa6a3baa935 Mon Sep 17 00:00:00 2001 From: Mark Iantorno Date: Wed, 25 May 2022 12:55:41 -0400 Subject: [PATCH 087/200] Update src/test/smoketest/SMOKE_TEST.md Co-authored-by: Ken Stevens --- src/test/smoketest/SMOKE_TEST.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/test/smoketest/SMOKE_TEST.md b/src/test/smoketest/SMOKE_TEST.md index 7ced8ca084f..968d7fb2fd8 100644 --- a/src/test/smoketest/SMOKE_TEST.md +++ b/src/test/smoketest/SMOKE_TEST.md @@ -26,8 +26,7 @@ REST-OPERATION ENDPOINT-URL // Verification Steps ``` -Users can setup custom environment variables for running tests locally against their own servers. This can be done by -adding environment information within the `http-client.env.json` file. By default, we provide the following: +To run these tests against a specific server, configure the server details within the `http-client.env.json` file. By default, we provide the following: ```json { "default": { From 73d7ee185771a10e9156d7b2d97fe7e5af6ae20f Mon Sep 17 00:00:00 2001 From: Mark Iantorno Date: Thu, 26 May 2022 15:00:07 -0400 Subject: [PATCH 088/200] bumping to non-snapshot version (#377) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b2da79cfc32..9cbbfab03e8 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.1.0-PRE1-SNAPSHOT + 6.0.1 hapi-fhir-jpaserver-starter From 67caa88e06cf79df9f52db95ea9b2da03b00af6c Mon Sep 17 00:00:00 2001 From: chgl Date: Sat, 4 Jun 2022 14:24:28 +0200 Subject: [PATCH 089/200] updated helm chart to use latest v6.0.1 version of the image (#382) * updated helm chart to use latest v6.0.1 version of the image * updated workflow to run against multiple k8s versions --- .github/workflows/chart-test.yaml | 13 +++-- charts/hapi-fhir-jpaserver/Chart.lock | 6 +-- charts/hapi-fhir-jpaserver/Chart.yaml | 30 ++++++++--- charts/hapi-fhir-jpaserver/README.md | 16 ++++-- .../templates/deployment.yaml | 32 ++++++++++-- .../templates/networkpolicy.yaml | 27 ---------- .../templates/service.yaml | 4 ++ .../templates/servicemonitor.yaml | 30 +++++++++++ charts/hapi-fhir-jpaserver/values.yaml | 51 +++++++++++-------- 9 files changed, 139 insertions(+), 70 deletions(-) delete mode 100644 charts/hapi-fhir-jpaserver/templates/networkpolicy.yaml create mode 100644 charts/hapi-fhir-jpaserver/templates/servicemonitor.yaml diff --git a/.github/workflows/chart-test.yaml b/.github/workflows/chart-test.yaml index b3eb576bf83..1d32194682f 100644 --- a/.github/workflows/chart-test.yaml +++ b/.github/workflows/chart-test.yaml @@ -15,7 +15,7 @@ jobs: - name: Install helm-docs working-directory: /tmp env: - HELM_DOCS_URL: https://github.com/norwoodj/helm-docs/releases/download/v1.5.0/helm-docs_1.5.0_Linux_x86_64.tar.gz + HELM_DOCS_URL: https://github.com/norwoodj/helm-docs/releases/download/v1.9.1/helm-docs_1.9.1_Linux_x86_64.tar.gz run: | curl -LSs $HELM_DOCS_URL | tar xz && \ mv ./helm-docs /usr/local/bin/helm-docs && \ @@ -35,16 +35,19 @@ jobs: test: runs-on: ubuntu-20.04 + strategy: + matrix: + k8s-version: [1.22.9, 1.23.6, 1.24.1] needs: - lint steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 0 - name: Set up chart-testing - uses: helm/chart-testing-action@v2.1.0 + uses: helm/chart-testing-action@v2.2.1 - name: Run chart-testing (list-changed) id: list-changed @@ -57,6 +60,10 @@ jobs: - name: Create k8s Kind Cluster uses: helm/kind-action@v1.2.0 if: steps.list-changed.outputs.changed == 'true' + with: + version: v0.14.0 + cluster_name: kind-cluster-k8s-${{ matrix.k8s-version }} + node_image: kindest/node:v${{ matrix.k8s-version }} - name: Run chart-testing (install) run: ct install --config .github/ct/config.yaml diff --git a/charts/hapi-fhir-jpaserver/Chart.lock b/charts/hapi-fhir-jpaserver/Chart.lock index bfb87acb260..e8c97e8edcc 100644 --- a/charts/hapi-fhir-jpaserver/Chart.lock +++ b/charts/hapi-fhir-jpaserver/Chart.lock @@ -1,6 +1,6 @@ dependencies: - name: postgresql repository: https://charts.bitnami.com/bitnami - version: 11.1.19 -digest: sha256:5bb38230bfa62c63547851e6f46f66a61441a4a4f18e3689827546277e34d192 -generated: "2022-04-08T21:55:34.6868891+02:00" + version: 11.6.2 +digest: sha256:1b96efc47b5dbe28bf34bcb694697325f3d2755a39ce2f1c371b2c9de9fac9d3 +generated: "2022-06-03T11:48:19.1684784+02:00" diff --git a/charts/hapi-fhir-jpaserver/Chart.yaml b/charts/hapi-fhir-jpaserver/Chart.yaml index 3cb702b205b..9cebc38656e 100644 --- a/charts/hapi-fhir-jpaserver/Chart.yaml +++ b/charts/hapi-fhir-jpaserver/Chart.yaml @@ -7,9 +7,11 @@ sources: - https://github.com/hapifhir/hapi-fhir-jpaserver-starter dependencies: - name: postgresql - version: 11.1.19 + version: 11.6.2 repository: https://charts.bitnami.com/bitnami condition: postgresql.enabled +appVersion: v6.0.1 +version: 0.9.0 annotations: artifacthub.io/license: Apache-2.0 artifacthub.io/changes: | @@ -17,13 +19,27 @@ annotations: # added, changed, deprecated, removed, fixed, and security. - kind: changed description: | - updated HAPI FHIR starter image to 5.7.0 + BREAKING CHANGE: updated HAPI FHIR starter image to v6.0.1. + See for all application changes. - kind: changed description: | - BREAKING CHANGE: updated included PostgreSQL-subchart to v11 + updated included PostgreSQL-subchart to v11.6.2 + - kind: fixed + description: | + use a fixed image for the wait-for-database container (docker.io/bitnami/postgresql:14.3.0-debian-10-r20) + instead of relying on the PostgreSQL sub-chart values + - kind: changed + description: | + expose actuator/metrics endpoint on a separate port (8081) + - kind: added + description: | + support for monitoring metrics using ServiceMonitor CRDs - kind: changed description: | - BREAKING CHANGE: removed ability to override the image flavor. - The one based on distroless is now the new default. -appVersion: v5.7.0 -version: 0.8.0 + switched liveness and readiness probes to Spring Boot actuator endpoints + - kind: changed + description: | + BREAKING CHANGE: removed included `NetworkPolicy`, which is subject to more thorough rework + - kind: added + description: | + allow configuring `topologySpreadConstraints` for the deployment diff --git a/charts/hapi-fhir-jpaserver/README.md b/charts/hapi-fhir-jpaserver/README.md index 288e2ce517c..20d0d6f9410 100644 --- a/charts/hapi-fhir-jpaserver/README.md +++ b/charts/hapi-fhir-jpaserver/README.md @@ -1,6 +1,6 @@ # HAPI FHIR JPA Server Starter Helm Chart -![Version: 0.8.0](https://img.shields.io/badge/Version-0.8.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v5.7.0](https://img.shields.io/badge/AppVersion-v5.7.0-informational?style=flat-square) +![Version: 0.9.0](https://img.shields.io/badge/Version-0.9.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v6.0.1](https://img.shields.io/badge/AppVersion-v6.0.1-informational?style=flat-square) This helm chart will help you install the HAPI FHIR JPA Server in a Kubernetes environment. @@ -40,10 +40,15 @@ helm install --render-subchart-notes hapi-fhir-jpaserver hapifhir/hapi-fhir-jpas | ingress.hosts[0].pathType | string | `"ImplementationSpecific"` | | | ingress.hosts[0].paths[0] | string | `"/"` | | | ingress.tls | list | `[]` | ingress TLS config | +| livenessProbe.failureThreshold | int | `5` | | +| livenessProbe.initialDelaySeconds | int | `30` | | +| livenessProbe.periodSeconds | int | `20` | | +| livenessProbe.successThreshold | int | `1` | | +| livenessProbe.timeoutSeconds | int | `30` | | +| metrics.service.port | int | `8081` | | +| metrics.serviceMonitor.additionalLabels | object | `{}` | additional labels to apply to the ServiceMonitor object, e.g. `release: prometheus` | +| metrics.serviceMonitor.enabled | bool | `false` | if enabled, creates a ServiceMonitor instance for Prometheus Operator-based monitoring | | nameOverride | string | `""` | override the chart name | -| networkPolicy.allowedFrom | list | `[]` | Additional allowed NetworkPolicyPeer specs Evaluated as a template so you could do: Example: allowedFrom: - podSelector: matchLabels: app.kubernetes.io/name: {{ $.Release.Name }} | -| networkPolicy.enabled | bool | `false` | enable NetworkPolicy | -| networkPolicy.explicitNamespacesSelector | object | `{}` | a Kubernetes LabelSelector to explicitly select namespaces from which ingress traffic could be allowed | | nodeSelector | object | `{}` | node selector for the pod | | podAnnotations | object | `{}` | annotations applied to the server pod | | podDisruptionBudget.enabled | bool | `false` | Enable PodDisruptionBudget for the server pods. uses policy/v1/PodDisruptionBudget thus requiring k8s 1.21+ | @@ -75,6 +80,7 @@ helm install --render-subchart-notes hapi-fhir-jpaserver hapifhir/hapi-fhir-jpas | startupProbe.successThreshold | int | `1` | | | startupProbe.timeoutSeconds | int | `30` | | | tolerations | list | `[]` | pod tolerations | +| topologySpreadConstraints | list | `[]` | pod topology spread configuration see: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/#api | ## Development @@ -89,4 +95,4 @@ INFO[2021-11-20T12:38:04Z] Generating README Documentation for chart /usr/src/ap ``` ---------------------------------------------- -Autogenerated from chart metadata using [helm-docs v1.5.0](https://github.com/norwoodj/helm-docs/releases/v1.5.0) +Autogenerated from chart metadata using [helm-docs v1.9.1](https://github.com/norwoodj/helm-docs/releases/v1.9.1) diff --git a/charts/hapi-fhir-jpaserver/templates/deployment.yaml b/charts/hapi-fhir-jpaserver/templates/deployment.yaml index 187ee9d2816..741eb71add2 100644 --- a/charts/hapi-fhir-jpaserver/templates/deployment.yaml +++ b/charts/hapi-fhir-jpaserver/templates/deployment.yaml @@ -30,7 +30,7 @@ spec: {{- toYaml .Values.podSecurityContext | nindent 8 }} initContainers: - name: wait-for-db-to-be-ready - image: "{{ .Values.postgresql.image.registry }}/{{ .Values.postgresql.image.repository }}:{{ .Values.postgresql.image.tag }}" + image: docker.io/bitnami/postgresql:14.3.0-debian-10-r20 imagePullPolicy: IfNotPresent securityContext: allowPrivilegeEscalation: false @@ -66,9 +66,23 @@ spec: - name: http containerPort: 8080 protocol: TCP + - name: metrics + containerPort: 8081 + protocol: TCP + startupProbe: + httpGet: + path: /readyz + port: http + {{- with .Values.startupProbe }} + initialDelaySeconds: {{ .initialDelaySeconds }} + periodSeconds: {{ .periodSeconds }} + timeoutSeconds: {{ .timeoutSeconds }} + successThreshold: {{ .successThreshold }} + failureThreshold: {{ .failureThreshold }} + {{- end }} readinessProbe: httpGet: - path: / + path: /readyz port: http {{- with .Values.readinessProbe }} initialDelaySeconds: {{ .initialDelaySeconds }} @@ -77,11 +91,11 @@ spec: successThreshold: {{ .successThreshold }} failureThreshold: {{ .failureThreshold }} {{- end }} - startupProbe: + livenessProbe: httpGet: - path: /fhir/metadata + path: /livez port: http - {{- with .Values.startupProbe }} + {{- with .Values.livenessProbe }} initialDelaySeconds: {{ .initialDelaySeconds }} periodSeconds: {{ .periodSeconds }} timeoutSeconds: {{ .timeoutSeconds }} @@ -106,6 +120,10 @@ spec: value: ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgres94Dialect - name: HAPI_FHIR_USE_APACHE_ADDRESS_STRATEGY value: "true" + - name: MANAGEMENT_ENDPOINT_HEALTH_PROBES_ADD_ADDITIONAL_PATHS + value: "true" + - name: MANAGEMENT_SERVER_PORT + value: "8081" {{- if .Values.extraEnv }} {{ toYaml .Values.extraEnv | nindent 12 }} {{- end }} @@ -126,6 +144,10 @@ spec: tolerations: {{- toYaml . | nindent 8 }} {{- end }} + {{- with .Values.topologySpreadConstraints }} + topologySpreadConstraints: + {{- toYaml . | nindent 8 }} + {{- end }} volumes: - name: tmp-volume emptyDir: {} diff --git a/charts/hapi-fhir-jpaserver/templates/networkpolicy.yaml b/charts/hapi-fhir-jpaserver/templates/networkpolicy.yaml deleted file mode 100644 index d051950e0e1..00000000000 --- a/charts/hapi-fhir-jpaserver/templates/networkpolicy.yaml +++ /dev/null @@ -1,27 +0,0 @@ -{{- if .Values.networkPolicy.enabled }} -kind: NetworkPolicy -apiVersion: networking.k8s.io/v1 -metadata: - name: {{ include "hapi-fhir-jpaserver.fullname" . }} - labels: - {{- include "hapi-fhir-jpaserver.labels" . | nindent 4 }} -spec: - podSelector: - matchLabels: - {{- include "hapi-fhir-jpaserver.selectorLabels" . | nindent 6 }} - ingress: - # Allow inbound connections from pods with the "hapi-fhir-jpaserver-client: true" label - - ports: - - port: http - from: - - podSelector: - matchLabels: - {{ include "hapi-fhir-jpaserver.fullname" . }}-client: "true" - {{- with .Values.networkPolicy.explicitNamespacesSelector }} - namespaceSelector: - {{ toYaml . | nindent 12 }} - {{- end }} - {{- with .Values.networkPolicy.allowedFrom }} - {{ tpl (toYaml .) $ | nindent 8 }} - {{- end }} -{{- end }} diff --git a/charts/hapi-fhir-jpaserver/templates/service.yaml b/charts/hapi-fhir-jpaserver/templates/service.yaml index 90a05a291e8..d7ecaa5d25e 100644 --- a/charts/hapi-fhir-jpaserver/templates/service.yaml +++ b/charts/hapi-fhir-jpaserver/templates/service.yaml @@ -11,5 +11,9 @@ spec: targetPort: http protocol: TCP name: http + - port: {{ .Values.metrics.service.port }} + targetPort: metrics + protocol: TCP + name: metrics selector: {{- include "hapi-fhir-jpaserver.selectorLabels" . | nindent 4 }} diff --git a/charts/hapi-fhir-jpaserver/templates/servicemonitor.yaml b/charts/hapi-fhir-jpaserver/templates/servicemonitor.yaml new file mode 100644 index 00000000000..e161feeb5c9 --- /dev/null +++ b/charts/hapi-fhir-jpaserver/templates/servicemonitor.yaml @@ -0,0 +1,30 @@ +{{- if .Values.metrics.serviceMonitor.enabled }} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ include "hapi-fhir-jpaserver.fullname" . }} + {{- if .Values.metrics.serviceMonitor.namespace }} + namespace: {{ .Values.metrics.serviceMonitor.namespace }} + {{- end }} + labels: + {{- include "hapi-fhir-jpaserver.labels" . | nindent 4 }} + {{- if .Values.metrics.serviceMonitor.additionalLabels }} + {{- toYaml .Values.metrics.serviceMonitor.additionalLabels | nindent 4 }} + {{- end }} +spec: + endpoints: + - port: metrics + path: /actuator/prometheus + {{- if .Values.metrics.serviceMonitor.interval }} + interval: {{ .Values.metrics.serviceMonitor.interval }} + {{- end }} + {{- if .Values.metrics.serviceMonitor.scrapeTimeout }} + scrapeTimeout: {{ .Values.metrics.serviceMonitor.scrapeTimeout }} + {{- end }} + namespaceSelector: + matchNames: + - {{ .Release.Namespace }} + selector: + matchLabels: + {{- include "hapi-fhir-jpaserver.selectorLabels" . | nindent 6 }} +{{- end }} diff --git a/charts/hapi-fhir-jpaserver/values.yaml b/charts/hapi-fhir-jpaserver/values.yaml index e89a5c4dd72..55863c89d23 100644 --- a/charts/hapi-fhir-jpaserver/values.yaml +++ b/charts/hapi-fhir-jpaserver/values.yaml @@ -88,6 +88,18 @@ tolerations: [] # -- pod affinity affinity: {} +# -- pod topology spread configuration +# see: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/#api +topologySpreadConstraints: + [] + # - maxSkew: 1 + # topologyKey: topology.kubernetes.io/zone + # whenUnsatisfiable: ScheduleAnyway + # labelSelector: + # matchLabels: + # app.kubernetes.io/instance: hapi-fhir-jpaserver + # app.kubernetes.io/name: hapi-fhir-jpaserver + postgresql: # -- enable an included PostgreSQL DB. # see for details @@ -126,6 +138,13 @@ startupProbe: successThreshold: 1 timeoutSeconds: 30 +livenessProbe: + failureThreshold: 5 + initialDelaySeconds: 30 + periodSeconds: 20 + successThreshold: 1 + timeoutSeconds: 30 + externalDatabase: # -- external database host used with `postgresql.enabled=false` host: localhost @@ -142,26 +161,6 @@ externalDatabase: # -- database name database: fhir -networkPolicy: - # -- enable NetworkPolicy - enabled: false - # -- a Kubernetes LabelSelector to explicitly select namespaces from which ingress traffic could be allowed - explicitNamespacesSelector: - {} - # matchLabels: - # team: one - # test: foo - - # -- Additional allowed NetworkPolicyPeer specs - # Evaluated as a template so you could do: - # - # Example: - # allowedFrom: - # - podSelector: - # matchLabels: - # app.kubernetes.io/name: {{ $.Release.Name }} - allowedFrom: [] - # -- extra environment variables to set on the server container extraEnv: [] @@ -176,3 +175,15 @@ podDisruptionBudget: minAvailable: 1 # -- maximum unavailable instances maxUnavailable: "" + +metrics: + serviceMonitor: + # -- if enabled, creates a ServiceMonitor instance for Prometheus Operator-based monitoring + enabled: false + # -- additional labels to apply to the ServiceMonitor object, e.g. `release: prometheus` + additionalLabels: {} + # namespace: monitoring + # interval: 30s + # scrapeTimeout: 10s + service: + port: 8081 From 41ba07a5e5fcdbcee05b23f4d58c3c1d115b2052 Mon Sep 17 00:00:00 2001 From: Ibrohim Kholilul Islam Date: Fri, 17 Jun 2022 18:15:58 +0000 Subject: [PATCH 090/200] add BinaryAccessProvider to BaseJpaRestfulServer --- .../java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java index c7ec35b34a5..0043609ae68 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java @@ -11,6 +11,7 @@ import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao; 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.graphql.GraphQLProvider; import ca.uhn.fhir.jpa.interceptor.CascadingDeleteInterceptor; @@ -86,6 +87,8 @@ public class BaseJpaRestfulServer extends RestfulServer { @Autowired BinaryStorageInterceptor binaryStorageInterceptor; @Autowired + Optional binaryAccessProvider; + @Autowired IPackageInstallerSvc packageInstallerSvc; @Autowired AppProperties appProperties; @@ -318,6 +321,7 @@ protected void initialize() throws ServletException { // Binary Storage if (appProperties.getBinary_storage_enabled()) { + registerProvider(binaryAccessProvider.get()); getInterceptorService().registerInterceptor(binaryStorageInterceptor); } From a026b1f390ab5c4cefc00ccd22cd5928206924ff Mon Sep 17 00:00:00 2001 From: Ibrohim Kholilul Islam Date: Thu, 23 Jun 2022 12:00:55 +0700 Subject: [PATCH 091/200] Update src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java Co-authored-by: Kevin Dougan SmileCDR <72025369+KevinDougan-SmileCDR@users.noreply.github.com> --- src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java index 0043609ae68..6ec6bc1e5fd 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java @@ -320,7 +320,7 @@ protected void initialize() throws ServletException { } // Binary Storage - if (appProperties.getBinary_storage_enabled()) { + if (appProperties.getBinary_storage_enabled() && binaryAccessProvider.isPresent()) { registerProvider(binaryAccessProvider.get()); getInterceptorService().registerInterceptor(binaryStorageInterceptor); } From d6b5bc3cd22c018f283a2699ab77ec643cd1ccba Mon Sep 17 00:00:00 2001 From: Patrick Werner Date: Mon, 4 Jul 2022 14:46:15 +0200 Subject: [PATCH 092/200] Applying fix from upstream for h2 Binaries This issue is already fixed upstream. Can be removed with next hapi version upgrade. https://github.com/hapifhir/hapi-fhir/pull/3676 --- pom.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pom.xml b/pom.xml index 9cbbfab03e8..1f33108199b 100644 --- a/pom.xml +++ b/pom.xml @@ -197,6 +197,8 @@ com.h2database h2 + + 2.1.212 From bea4d471aaa1d4370ed79bb3615054c2be5b18c3 Mon Sep 17 00:00:00 2001 From: Patrick Werner Date: Mon, 4 Jul 2022 14:54:49 +0200 Subject: [PATCH 093/200] removed wrong and duplicated config entry `daoconfig_client_id_strategy`does not resolve and is a duplicate of `client_id_strategy` --- src/main/resources/application.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 9492327dc52..8a18d87bb98 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -102,7 +102,6 @@ hapi: # enforce_referential_integrity_on_write: false # etag_support_enabled: true # expunge_enabled: true - # daoconfig_client_id_strategy: null # client_id_strategy: ALPHANUMERIC # fhirpath_interceptor_enabled: false # filter_search_enabled: true From cd8b06b263a9d18ca820fb09860e51a6029e16eb Mon Sep 17 00:00:00 2001 From: chgl Date: Mon, 4 Jul 2022 19:44:20 +0200 Subject: [PATCH 094/200] Added OpenTelemetry Java Agent JAR to container image (#391) Closes #387 --- Dockerfile | 9 ++++++++- README.md | 25 +++++++++++++++++++++---- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index 3a8ea7a44f1..4eba966f8e3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,9 @@ FROM maven:3.8-openjdk-17-slim as build-hapi WORKDIR /tmp/hapi-fhir-jpaserver-starter +ARG OPENTELEMETRY_JAVA_AGENT_VERSION=1.15.0 +RUN curl -LSsO https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v${OPENTELEMETRY_JAVA_AGENT_VERSION}/opentelemetry-javaagent.jar + COPY pom.xml . COPY server.xml . RUN mvn -ntp dependency:go-offline @@ -29,15 +32,19 @@ USER 1001 COPY --chown=1001:1001 catalina.properties /opt/bitnami/tomcat/conf/catalina.properties COPY --chown=1001:1001 server.xml /opt/bitnami/tomcat/conf/server.xml COPY --from=build-hapi --chown=1001:1001 /tmp/hapi-fhir-jpaserver-starter/target/ROOT.war /opt/bitnami/tomcat/webapps_default/ROOT.war +COPY --from=build-hapi --chown=1001:1001 /tmp/hapi-fhir-jpaserver-starter/opentelemetry-javaagent.jar /app ENV ALLOW_EMPTY_PASSWORD=yes ########### distroless brings focus on security and runs on plain spring boot - this is the default image FROM gcr.io/distroless/java17:nonroot as default -COPY --chown=nonroot:nonroot --from=build-distroless /app /app # 65532 is the nonroot user's uid # used here instead of the name to allow Kubernetes to easily detect that the container # is running as a non-root (uid != 0) user. USER 65532:65532 WORKDIR /app + +COPY --chown=nonroot:nonroot --from=build-distroless /app /app +COPY --chown=nonroot:nonroot --from=build-hapi /tmp/hapi-fhir-jpaserver-starter/opentelemetry-javaagent.jar /app + CMD ["/app/main.war"] diff --git a/README.md b/README.md index 781429da4a1..da7d46efb34 100644 --- a/README.md +++ b/README.md @@ -196,7 +196,7 @@ Also, make sure you are not setting the Hibernate dialect explicitly, in other w hibernate.dialect: {some none MySQL dialect} ``` -On some systems, it might be necessary to override hibernate's default naming strategy. The naming strategy must be set using spring.jpa.hibernate.physical_naming_strategy. +On some systems, it might be necessary to override hibernate's default naming strategy. The naming strategy must be set using spring.jpa.hibernate.physical_naming_strategy. ```yaml spring: @@ -239,8 +239,8 @@ spring: Because the integration tests within the project rely on the default H2 database configuration, it is important to either explicity skip the integration tests during the build process, i.e., `mvn install -DskipTests`, or delete the tests altogether. Failure to skip or delete the tests once you've configured PostgreSQL for the datasource.driver, datasource.url, and hibernate.dialect as outlined above will result in build errors and compilation failure. -NOTE: MS SQL Server by default uses a case-insensitive codepage. This will cause errors with some operations - such as when expanding case-sensitive valuesets (UCUM) as there are unique indexes defined on the terminology tables for codes. -It is recommended to deploy a case-sensitive database prior to running HAPI FHIR when using MS SQL Server to avoid these and potentially other issues. +NOTE: MS SQL Server by default uses a case-insensitive codepage. This will cause errors with some operations - such as when expanding case-sensitive valuesets (UCUM) as there are unique indexes defined on the terminology tables for codes. +It is recommended to deploy a case-sensitive database prior to running HAPI FHIR when using MS SQL Server to avoid these and potentially other issues. ## Customizing The Web Testpage UI @@ -390,7 +390,7 @@ Set `hapi.fhir.store_resource_in_lucene_index_enabled` in the [application.yaml] ## Changing cached search results time It is possible to change the cached search results time. The option `reuse_cached_search_results_millis` in the [application.yaml](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/application.yaml) is 6000 miliseconds by default. -Set `reuse_cached_search_results_millis: -1` in the [application.yaml](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/application.yaml) file to ignore the cache time every search. +Set `reuse_cached_search_results_millis: -1` in the [application.yaml](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/application.yaml) file to ignore the cache time every search. ## Build the distroless variant of the image (for lower footprint and improved security) @@ -409,3 +409,20 @@ see the `-distroless` suffix in the image tags. To add a custom operation, refer to the documentation in the core hapi-fhir libraries [here](https://hapifhir.io/hapi-fhir/docs/server_plain/rest_operations_operations.html). Within `hapi-fhir-jpaserver-starter`, create a generic class (that does not extend or implement any classes or interfaces), add the `@Operation` as a method within the generic class, and then register the class as a provider using `RestfulServer.registerProvider()`. + +## Enable OpenTelemetry auto-instrumentation + +The container image includes the [OpenTelemetry Java auto-instrumentation](https://github.com/open-telemetry/opentelemetry-java-instrumentation) +Java agent JAR which can be used to export telemetry data for the HAPI FHIR JPA Server. You can enable it by specifying the `-javaagent` flag, +for example by overriding the `JAVA_TOOL_OPTIONS` environment variable: + +```sh +docker run --rm -it -p 8080:8080 \ + -e JAVA_TOOL_OPTIONS="-javaagent:/app/opentelemetry-javaagent.jar" \ + -e OTEL_TRACES_EXPORTER="jaeger" \ + -e OTEL_SERVICE_NAME="hapi-fhir-server" \ + -e OTEL_EXPORTER_JAEGER_ENDPOINT="http://jaeger:14250" \ + docker.io/hapiproject/hapi:latest +``` + +You can configure the agent using environment variables or Java system properties, see for details. From 6ad29890f0e85e240e207a6c7183c8109fd4fe87 Mon Sep 17 00:00:00 2001 From: markiantorno Date: Tue, 5 Jul 2022 14:27:05 -0400 Subject: [PATCH 095/200] changes to pom.xml to enable publishing --- pom.xml | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 1f33108199b..6bf149e74e7 100644 --- a/pom.xml +++ b/pom.xml @@ -18,6 +18,7 @@ hapi-fhir-jpaserver-starter + war 11 @@ -27,8 +28,6 @@ 3.8.3
- war - HAPI FHIR JPA Server - Starter Project @@ -353,7 +352,6 @@ test
- @@ -588,5 +586,61 @@
+ + ossrh-repo + + false + + deployToSonatype + + + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.13 + true + + ossrh + https://oss.sonatype.org/ + true + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.6 + + + sign-artifacts + verify + + sign + + + ${gpg.keyname} + ${gpg.keyname} + + --pinentry-mode + loopback + + + + + + + + From c9dd6054fa253d3769859942e3f3ac4e71463f20 Mon Sep 17 00:00:00 2001 From: chgl Date: Fri, 8 Jul 2022 00:18:49 +0200 Subject: [PATCH 096/200] fixed directory of the .war in tomcat-based image --- Dockerfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 4eba966f8e3..464281e8673 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,7 +21,6 @@ RUN mkdir /app && cp /tmp/hapi-fhir-jpaserver-starter/target/ROOT.war /app/main. FROM bitnami/tomcat:9.0 as tomcat RUN rm -rf /opt/bitnami/tomcat/webapps/ROOT && \ - rm -rf /opt/bitnami/tomcat/webapps_default/ROOT && \ mkdir -p /opt/bitnami/hapi/data/hapi/lucenefiles && \ chmod 775 /opt/bitnami/hapi/data/hapi/lucenefiles @@ -31,7 +30,7 @@ USER 1001 COPY --chown=1001:1001 catalina.properties /opt/bitnami/tomcat/conf/catalina.properties COPY --chown=1001:1001 server.xml /opt/bitnami/tomcat/conf/server.xml -COPY --from=build-hapi --chown=1001:1001 /tmp/hapi-fhir-jpaserver-starter/target/ROOT.war /opt/bitnami/tomcat/webapps_default/ROOT.war +COPY --from=build-hapi --chown=1001:1001 /tmp/hapi-fhir-jpaserver-starter/target/ROOT.war /opt/bitnami/tomcat/webapps/ROOT.war COPY --from=build-hapi --chown=1001:1001 /tmp/hapi-fhir-jpaserver-starter/opentelemetry-javaagent.jar /app ENV ALLOW_EMPTY_PASSWORD=yes From d148f458e0e07dad1ee2ce55c79b07bc3f612aad Mon Sep 17 00:00:00 2001 From: Arbaaz Muslim Date: Tue, 9 Aug 2022 16:31:49 -0700 Subject: [PATCH 097/200] bulk data instrumentation included --- src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java | 9 +++++++++ .../ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java | 8 ++++++++ src/main/resources/application.yaml | 2 ++ 3 files changed, 19 insertions(+) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java b/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java index af8be7c44c3..27bb715c116 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java @@ -46,6 +46,7 @@ public class AppProperties { private Boolean graphql_enabled = false; private Boolean binary_storage_enabled = false; private Boolean bulk_export_enabled = false; + private Boolean bulk_import_enabled = false; private Boolean default_pretty_print = true; private Integer default_page_size = 20; private Integer max_binary_size = null; @@ -402,6 +403,14 @@ public void setBulk_export_enabled(Boolean bulk_export_enabled) { this.bulk_export_enabled = bulk_export_enabled; } + public Boolean getBulk_import_enabled() { + return bulk_import_enabled; + } + + public void setBulk_import_enabled(Boolean bulk_import_enabled) { + this.bulk_import_enabled = bulk_import_enabled; + } + public EncodingEnum getDefault_encoding() { return default_encoding; } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java index 6ec6bc1e5fd..5a457026936 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java @@ -1,5 +1,6 @@ package ca.uhn.fhir.jpa.starter; +import ca.uhn.fhir.batch2.jobs.imprt.BulkDataImportProvider; import ca.uhn.fhir.batch2.jobs.reindex.ReindexProvider; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; @@ -78,6 +79,8 @@ public class BaseJpaRestfulServer extends RestfulServer { @Autowired BulkDataExportProvider bulkDataExportProvider; @Autowired + BulkDataImportProvider bulkDataImportProvider; + @Autowired PartitionManagementProvider partitionManagementProvider; @Autowired @@ -364,6 +367,11 @@ protected void initialize() throws ServletException { registerProvider(bulkDataExportProvider); } + //Bulk Import + if (appProperties.getBulk_import_enabled()) { + registerProvider(bulkDataImportProvider); + } + // valueSet Operations i.e $expand registerProvider(myValueSetOperationProvider); diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 8a18d87bb98..c8222637643 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -96,6 +96,8 @@ hapi: ### !!Extended Lucene/Elasticsearch Indexing is still a experimental feature, expect some features (e.g. _total=accurate) to not work as expected!! ### more information here: https://hapifhir.io/hapi-fhir/docs/server_jpa/elastic.html advanced_lucene_indexing: false + bulk_export_enabled: true + bulk_import_enabled: true # enforce_referential_integrity_on_delete: false # This is an experimental feature, and does not fully support _total and other FHIR features. # enforce_referential_integrity_on_delete: false From c617f4e395957b72c4eba9cbfe58843436705307 Mon Sep 17 00:00:00 2001 From: Arbaaz Muslim Date: Tue, 9 Aug 2022 16:48:22 -0700 Subject: [PATCH 098/200] bulk data instrumentation turned off by default --- src/main/resources/application.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index c8222637643..cb9c9a3bd8b 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -96,8 +96,8 @@ hapi: ### !!Extended Lucene/Elasticsearch Indexing is still a experimental feature, expect some features (e.g. _total=accurate) to not work as expected!! ### more information here: https://hapifhir.io/hapi-fhir/docs/server_jpa/elastic.html advanced_lucene_indexing: false - bulk_export_enabled: true - bulk_import_enabled: true + bulk_export_enabled: false + bulk_import_enabled: false # enforce_referential_integrity_on_delete: false # This is an experimental feature, and does not fully support _total and other FHIR features. # enforce_referential_integrity_on_delete: false From 1753272122665df6b2b25dc99944be44f275abdb Mon Sep 17 00:00:00 2001 From: chgl Date: Mon, 15 Aug 2022 01:20:49 +0200 Subject: [PATCH 099/200] updated opentelemetry-java-instrumentation JAR to 1.16.0 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 464281e8673..1eb0a2b3e0f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ FROM maven:3.8-openjdk-17-slim as build-hapi WORKDIR /tmp/hapi-fhir-jpaserver-starter -ARG OPENTELEMETRY_JAVA_AGENT_VERSION=1.15.0 +ARG OPENTELEMETRY_JAVA_AGENT_VERSION=1.16.0 RUN curl -LSsO https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v${OPENTELEMETRY_JAVA_AGENT_VERSION}/opentelemetry-javaagent.jar COPY pom.xml . From ee74116e6b4b73b84da374eca01892d30fb410ef Mon Sep 17 00:00:00 2001 From: Jens Kristian Villadsen Date: Thu, 18 Aug 2022 21:05:36 +0200 Subject: [PATCH 100/200] Better support for ARM java17 regular doesn't have ARM, java17-debian11 does --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 1eb0a2b3e0f..82af8df9a31 100644 --- a/Dockerfile +++ b/Dockerfile @@ -36,7 +36,7 @@ COPY --from=build-hapi --chown=1001:1001 /tmp/hapi-fhir-jpaserver-starter/opente ENV ALLOW_EMPTY_PASSWORD=yes ########### distroless brings focus on security and runs on plain spring boot - this is the default image -FROM gcr.io/distroless/java17:nonroot as default +FROM gcr.io/distroless/java17-debian11:nonroot as default # 65532 is the nonroot user's uid # used here instead of the name to allow Kubernetes to easily detect that the container # is running as a non-root (uid != 0) user. From 956cfb171dd8562d60ac49623e05338420d32264 Mon Sep 17 00:00:00 2001 From: chgl Date: Mon, 22 Aug 2022 18:15:34 +0200 Subject: [PATCH 101/200] Updated Otel Java agent to 1.17.0 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 1eb0a2b3e0f..af067ebd1be 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ FROM maven:3.8-openjdk-17-slim as build-hapi WORKDIR /tmp/hapi-fhir-jpaserver-starter -ARG OPENTELEMETRY_JAVA_AGENT_VERSION=1.16.0 +ARG OPENTELEMETRY_JAVA_AGENT_VERSION=1.17.0 RUN curl -LSsO https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v${OPENTELEMETRY_JAVA_AGENT_VERSION}/opentelemetry-javaagent.jar COPY pom.xml . From d059954c5f40d59bc0553539e678fdc892526ddb Mon Sep 17 00:00:00 2001 From: chgl Date: Mon, 22 Aug 2022 18:16:24 +0200 Subject: [PATCH 102/200] Updated hapi-fhir to 6.1.0 --- README.md | 4 ++-- pom.xml | 4 +--- .../jpa/starter/BaseJpaRestfulServer.java | 2 +- .../fhir/jpa/starter/EnvironmentHelper.java | 7 ++++--- .../jpa/starter/FhirServerConfigCommon.java | 2 +- .../fhir/jpa/starter/StarterJpaConfig.java | 19 +++++++++++++++++++ src/main/resources/application.yaml | 2 +- 7 files changed, 29 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index da7d46efb34..c104d2398ff 100644 --- a/README.md +++ b/README.md @@ -17,9 +17,9 @@ In order to use this sample, you should have: - Apache Maven build tool (newest version) ### or - - Docker, as the entire project can be built using multistage docker (with both JDK and maven wrapped in docker) or used directly from [Docker Hub](https://hub.docker.com/repository/docker/hapiproject/hapi) + - Docker, as the entire project can be built using multistage docker (with both JDK and maven wrapped in docker) or used directly from [Docker Hub](https://hub.docker.com/r/hapiproject/hapi) -## Running via [Docker Hub](https://hub.docker.com/repository/docker/hapiproject/hapi) +## Running via [Docker Hub](https://hub.docker.com/r/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: diff --git a/pom.xml b/pom.xml index 6bf149e74e7..3b2596f6f54 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.0.1 + 6.1.0 hapi-fhir-jpaserver-starter @@ -196,8 +196,6 @@ com.h2database h2 - - 2.1.212 diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java index 5a457026936..4cad6bfd726 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java @@ -418,7 +418,7 @@ protected void initialize() throws ServletException { daoConfig.setLastNEnabled(true); } - daoConfig.setStoreResourceInLuceneIndex(appProperties.getStore_resource_in_lucene_index_enabled()); + 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()); } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/EnvironmentHelper.java b/src/main/java/ca/uhn/fhir/jpa/starter/EnvironmentHelper.java index 11b612811c1..732705ac521 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/EnvironmentHelper.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/EnvironmentHelper.java @@ -1,7 +1,7 @@ package ca.uhn.fhir.jpa.starter; import ca.uhn.fhir.jpa.config.HapiFhirLocalContainerEntityManagerFactoryBean; -import ca.uhn.fhir.jpa.search.HapiLuceneAnalysisConfigurer; +import ca.uhn.fhir.jpa.search.HapiHSearchAnalysisConfigurers; import ca.uhn.fhir.jpa.search.elastic.ElasticsearchHibernatePropertiesBuilder; import org.apache.lucene.util.Version; import org.hibernate.cfg.AvailableSettings; @@ -71,7 +71,8 @@ public static Properties getHibernateProperties(ConfigurableEnvironment environm if (properties.get(BackendSettings.backendKey(BackendSettings.TYPE)).equals(LuceneBackendSettings.TYPE_NAME)) { properties.putIfAbsent(BackendSettings.backendKey(LuceneIndexSettings.DIRECTORY_TYPE), LocalFileSystemDirectoryProvider.NAME); properties.putIfAbsent(BackendSettings.backendKey(LuceneIndexSettings.DIRECTORY_ROOT), "target/lucenefiles"); - properties.putIfAbsent(BackendSettings.backendKey(LuceneBackendSettings.ANALYSIS_CONFIGURER), HapiLuceneAnalysisConfigurer.class.getName()); + properties.putIfAbsent(BackendSettings.backendKey(LuceneBackendSettings.ANALYSIS_CONFIGURER), + HapiHSearchAnalysisConfigurers.HapiLuceneAnalysisConfigurer.class.getName()); properties.putIfAbsent(BackendSettings.backendKey(LuceneBackendSettings.LUCENE_VERSION), Version.LATEST); } else if (properties.get(BackendSettings.backendKey(BackendSettings.TYPE)).equals(ElasticsearchBackendSettings.TYPE_NAME)) { @@ -188,4 +189,4 @@ private static void addAll(Map aBase, Map aToBeA aBase.put(entry.getKey(), entry.getValue()); } } -} \ No newline at end of file +} 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 15638f5336b..ac80f093b0e 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java @@ -122,7 +122,7 @@ public DaoConfig daoConfig(AppProperties appProperties) { } retVal.setFilterParameterEnabled(appProperties.getFilter_search_enabled()); - retVal.setAdvancedLuceneIndexing(appProperties.getAdvanced_lucene_indexing()); + retVal.setAdvancedHSearchIndexing(appProperties.getAdvanced_lucene_indexing()); retVal.setTreatBaseUrlsAsLocal(new HashSet<>(appProperties.getLocal_base_urls())); return retVal; diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/StarterJpaConfig.java b/src/main/java/ca/uhn/fhir/jpa/starter/StarterJpaConfig.java index 07fd0282cb5..2acfabd898f 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/StarterJpaConfig.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/StarterJpaConfig.java @@ -10,13 +10,19 @@ 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.provider.DaoRegistryResourceSupportedSvc; 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.util.ResourceCountCache; import ca.uhn.fhir.jpa.validation.JpaValidationSupportChain; +import ca.uhn.fhir.mdm.dao.IMdmLinkDao; import ca.uhn.fhir.rest.api.IResourceSupportedSvc; +import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; + import org.hl7.fhir.common.hapi.validation.support.CachingValidationSupport; import org.springframework.batch.core.configuration.annotation.BatchConfigurer; import org.springframework.beans.factory.annotation.Autowired; @@ -106,4 +112,17 @@ public JpaTransactionManager hapiTransactionManager(EntityManagerFactory entityM retVal.setEntityManagerFactory(entityManagerFactory); return retVal; } + + @Autowired + private ISearchParamRegistry mySearchParamRegistry; + + @Bean + public IHSearchSortHelper hSearchSortHelper() { + return new HSearchSortHelperImpl(mySearchParamRegistry); + } + + @Bean + public IMdmLinkDao mdmLinkDao(){ + return new MdmLinkDaoJpaImpl(); + } } diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index cb9c9a3bd8b..3c0be6675d4 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -42,7 +42,7 @@ spring: ### These settings will enable fulltext search with lucene hibernate.search.enabled: false # hibernate.search.backend.type: lucene - # hibernate.search.backend.analysis.configurer: ca.uhn.fhir.jpa.search.HapiLuceneAnalysisConfigurer + # hibernate.search.backend.analysis.configurer: ca.uhn.fhir.jpa.search.HapiHSearchAnalysisConfigurers$HapiLuceneAnalysisConfigurer # hibernate.search.backend.directory.type: local-filesystem # hibernate.search.backend.directory.root: target/lucenefiles # hibernate.search.backend.lucene_version: lucene_current From 68e64f2f33757a33cb0183be35730f303b90e1ad Mon Sep 17 00:00:00 2001 From: chgl Date: Thu, 25 Aug 2022 02:34:02 +0200 Subject: [PATCH 103/200] Minor Helm chart dependency updates and security improvements --- .github/workflows/chart-test.yaml | 2 +- charts/hapi-fhir-jpaserver/Chart.lock | 6 +-- charts/hapi-fhir-jpaserver/Chart.yaml | 30 +++++------ charts/hapi-fhir-jpaserver/README.md | 11 ++-- .../templates/deployment.yaml | 18 +++---- .../templates/service.yaml | 4 +- .../templates/servicemonitor.yaml | 2 +- .../templates/tests/test-endpoints.yaml | 53 ++++++++++++------- charts/hapi-fhir-jpaserver/values.yaml | 25 ++++++++- 9 files changed, 89 insertions(+), 62 deletions(-) diff --git a/.github/workflows/chart-test.yaml b/.github/workflows/chart-test.yaml index 1d32194682f..f4357fb35cc 100644 --- a/.github/workflows/chart-test.yaml +++ b/.github/workflows/chart-test.yaml @@ -15,7 +15,7 @@ jobs: - name: Install helm-docs working-directory: /tmp env: - HELM_DOCS_URL: https://github.com/norwoodj/helm-docs/releases/download/v1.9.1/helm-docs_1.9.1_Linux_x86_64.tar.gz + HELM_DOCS_URL: https://github.com/norwoodj/helm-docs/releases/download/v1.11.0/helm-docs_1.11.0_Linux_x86_64.tar.gz run: | curl -LSs $HELM_DOCS_URL | tar xz && \ mv ./helm-docs /usr/local/bin/helm-docs && \ diff --git a/charts/hapi-fhir-jpaserver/Chart.lock b/charts/hapi-fhir-jpaserver/Chart.lock index e8c97e8edcc..411bc270ef5 100644 --- a/charts/hapi-fhir-jpaserver/Chart.lock +++ b/charts/hapi-fhir-jpaserver/Chart.lock @@ -1,6 +1,6 @@ dependencies: - name: postgresql repository: https://charts.bitnami.com/bitnami - version: 11.6.2 -digest: sha256:1b96efc47b5dbe28bf34bcb694697325f3d2755a39ce2f1c371b2c9de9fac9d3 -generated: "2022-06-03T11:48:19.1684784+02:00" + version: 11.8.1 +digest: sha256:671325f8b3d0b85183fa241190e72705fb124a41254a5db6445bcc105e1ca7ec +generated: "2022-08-25T02:14:58.3432514+02:00" diff --git a/charts/hapi-fhir-jpaserver/Chart.yaml b/charts/hapi-fhir-jpaserver/Chart.yaml index 9cebc38656e..e172526f43d 100644 --- a/charts/hapi-fhir-jpaserver/Chart.yaml +++ b/charts/hapi-fhir-jpaserver/Chart.yaml @@ -7,11 +7,11 @@ sources: - https://github.com/hapifhir/hapi-fhir-jpaserver-starter dependencies: - name: postgresql - version: 11.6.2 + version: 11.8.1 repository: https://charts.bitnami.com/bitnami condition: postgresql.enabled -appVersion: v6.0.1 -version: 0.9.0 +appVersion: v6.x +version: 0.10.0 annotations: artifacthub.io/license: Apache-2.0 artifacthub.io/changes: | @@ -19,27 +19,21 @@ annotations: # added, changed, deprecated, removed, fixed, and security. - kind: changed description: | - BREAKING CHANGE: updated HAPI FHIR starter image to v6.0.1. - See for all application changes. + updated included PostgreSQL-subchart to v11.8.1. + Fixes `coalesce.go:220: warning: cannot overwrite table with non table for fhirserver.postgresql.primary.topologySpreadConstraints (map[])` warning - kind: changed description: | - updated included PostgreSQL-subchart to v11.6.2 - - kind: fixed + set `securityContext.seccompProfile.type=RuntimeDefault` for included PostgreSQL as well as all `initContainer` and Helm + test pods to comply with the "restricted" Pod Security Standard: + - kind: changed description: | - use a fixed image for the wait-for-database container (docker.io/bitnami/postgresql:14.3.0-debian-10-r20) - instead of relying on the PostgreSQL sub-chart values + use curl as the image for running Helm test pods - kind: changed description: | - expose actuator/metrics endpoint on a separate port (8081) + renamed `metrics` port to `http-metrics` for istio compliant naming - kind: added description: | - support for monitoring metrics using ServiceMonitor CRDs - - kind: changed - description: | - switched liveness and readiness probes to Spring Boot actuator endpoints + Helm test job to test metrics endpoint - kind: changed description: | - BREAKING CHANGE: removed included `NetworkPolicy`, which is subject to more thorough rework - - kind: added - description: | - allow configuring `topologySpreadConstraints` for the deployment + use full digest instead of just a tag for the server image reference diff --git a/charts/hapi-fhir-jpaserver/README.md b/charts/hapi-fhir-jpaserver/README.md index 20d0d6f9410..9b72dc5c7f5 100644 --- a/charts/hapi-fhir-jpaserver/README.md +++ b/charts/hapi-fhir-jpaserver/README.md @@ -1,6 +1,6 @@ # HAPI FHIR JPA Server Starter Helm Chart -![Version: 0.9.0](https://img.shields.io/badge/Version-0.9.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v6.0.1](https://img.shields.io/badge/AppVersion-v6.0.1-informational?style=flat-square) +![Version: 0.10.0](https://img.shields.io/badge/Version-0.10.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v6.x](https://img.shields.io/badge/AppVersion-v6.x-informational?style=flat-square) This helm chart will help you install the HAPI FHIR JPA Server in a Kubernetes environment. @@ -32,7 +32,7 @@ helm install --render-subchart-notes hapi-fhir-jpaserver hapifhir/hapi-fhir-jpas | image.pullPolicy | string | `"IfNotPresent"` | image pullPolicy to use | | image.registry | string | `"docker.io"` | registry where the HAPI FHIR server image is hosted | | image.repository | string | `"hapiproject/hapi"` | the path inside the repository | -| image.tag | string | `""` | defaults to `Chart.appVersion`. As of v5.7.0, this is the `distroless` flavor | +| image.tag | string | `"v6.0.1@sha256:63c98d8be3dadc77b47dca3115490f22bf99512f363f779f7bbcb42f569aeac3"` | the image tag. As of v5.7.0, this is the `distroless` flavor by default, add `-tomcat` to use the Tomcat-based image. | | imagePullSecrets | list | `[]` | image pull secrets to use when pulling the image | | ingress.annotations | object | `{}` | provide any additional annotations which may be required. Evaluated as a template. | | ingress.enabled | bool | `false` | whether to create an Ingress to expose the FHIR server HTTP endpoint | @@ -60,6 +60,8 @@ helm install --render-subchart-notes hapi-fhir-jpaserver hapifhir/hapi-fhir-jpas | postgresql.enabled | bool | `true` | enable an included PostgreSQL DB. see for details if set to `false`, the values under `externalDatabase` are used | | postgresql.primary.containerSecurityContext.allowPrivilegeEscalation | bool | `false` | | | postgresql.primary.containerSecurityContext.capabilities.drop[0] | string | `"ALL"` | | +| postgresql.primary.containerSecurityContext.runAsNonRoot | bool | `true` | | +| postgresql.primary.containerSecurityContext.seccompProfile.type | string | `"RuntimeDefault"` | | | readinessProbe.failureThreshold | int | `5` | | | readinessProbe.initialDelaySeconds | int | `30` | | | readinessProbe.periodSeconds | int | `20` | | @@ -69,9 +71,12 @@ helm install --render-subchart-notes hapi-fhir-jpaserver hapifhir/hapi-fhir-jpas | resources | object | `{}` | configure the FHIR server's resource requests and limits | | securityContext.allowPrivilegeEscalation | bool | `false` | | | securityContext.capabilities.drop[0] | string | `"ALL"` | | +| securityContext.privileged | bool | `false` | | | securityContext.readOnlyRootFilesystem | bool | `true` | | +| securityContext.runAsGroup | int | `65532` | | | securityContext.runAsNonRoot | bool | `true` | | | securityContext.runAsUser | int | `65532` | | +| securityContext.seccompProfile.type | string | `"RuntimeDefault"` | | | service.port | int | `8080` | port where the server will be exposed at | | service.type | string | `"ClusterIP"` | service type | | startupProbe.failureThreshold | int | `10` | | @@ -95,4 +100,4 @@ INFO[2021-11-20T12:38:04Z] Generating README Documentation for chart /usr/src/ap ``` ---------------------------------------------- -Autogenerated from chart metadata using [helm-docs v1.9.1](https://github.com/norwoodj/helm-docs/releases/v1.9.1) +Autogenerated from chart metadata using [helm-docs v1.11.0](https://github.com/norwoodj/helm-docs/releases/v1.11.0) diff --git a/charts/hapi-fhir-jpaserver/templates/deployment.yaml b/charts/hapi-fhir-jpaserver/templates/deployment.yaml index 741eb71add2..fa88745b016 100644 --- a/charts/hapi-fhir-jpaserver/templates/deployment.yaml +++ b/charts/hapi-fhir-jpaserver/templates/deployment.yaml @@ -30,18 +30,12 @@ spec: {{- toYaml .Values.podSecurityContext | nindent 8 }} initContainers: - name: wait-for-db-to-be-ready - image: docker.io/bitnami/postgresql:14.3.0-debian-10-r20 + image: docker.io/bitnami/postgresql:14.5.0@sha256:4355265e33e9c2a786aa145884d4b36ffd4c41c516b35d60df0b7495141ec738 imagePullPolicy: IfNotPresent + {{- with .Values.restrictedContainerSecurityContext }} securityContext: - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - privileged: false - capabilities: - drop: - - ALL - runAsNonRoot: true - runAsUser: 1001 - runAsGroup: 1001 + {{- toYaml . | nindent 12 }} + {{- end }} env: - name: PGHOST value: "{{ include "hapi-fhir-jpaserver.database.host" . }}" @@ -60,13 +54,13 @@ spec: - name: {{ .Chart.Name }} securityContext: {{- toYaml .Values.securityContext | nindent 12 }} - image: {{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ default .Chart.AppVersion .Values.image.tag }} + image: {{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag }} imagePullPolicy: {{ .Values.image.pullPolicy }} ports: - name: http containerPort: 8080 protocol: TCP - - name: metrics + - name: http-metrics containerPort: 8081 protocol: TCP startupProbe: diff --git a/charts/hapi-fhir-jpaserver/templates/service.yaml b/charts/hapi-fhir-jpaserver/templates/service.yaml index d7ecaa5d25e..b18b1e7f382 100644 --- a/charts/hapi-fhir-jpaserver/templates/service.yaml +++ b/charts/hapi-fhir-jpaserver/templates/service.yaml @@ -12,8 +12,8 @@ spec: protocol: TCP name: http - port: {{ .Values.metrics.service.port }} - targetPort: metrics + targetPort: http-metrics protocol: TCP - name: metrics + name: http-metrics selector: {{- include "hapi-fhir-jpaserver.selectorLabels" . | nindent 4 }} diff --git a/charts/hapi-fhir-jpaserver/templates/servicemonitor.yaml b/charts/hapi-fhir-jpaserver/templates/servicemonitor.yaml index e161feeb5c9..8bfad8b61e5 100644 --- a/charts/hapi-fhir-jpaserver/templates/servicemonitor.yaml +++ b/charts/hapi-fhir-jpaserver/templates/servicemonitor.yaml @@ -13,7 +13,7 @@ metadata: {{- end }} spec: endpoints: - - port: metrics + - port: http-metrics path: /actuator/prometheus {{- if .Values.metrics.serviceMonitor.interval }} interval: {{ .Values.metrics.serviceMonitor.interval }} diff --git a/charts/hapi-fhir-jpaserver/templates/tests/test-endpoints.yaml b/charts/hapi-fhir-jpaserver/templates/tests/test-endpoints.yaml index 911f59d6ae3..30aab5aba93 100644 --- a/charts/hapi-fhir-jpaserver/templates/tests/test-endpoints.yaml +++ b/charts/hapi-fhir-jpaserver/templates/tests/test-endpoints.yaml @@ -11,17 +11,13 @@ spec: restartPolicy: Never containers: - name: test-metadata-endpoint - image: busybox:1 - command: ['wget', '-O', '-'] - args: ['http://{{ include "hapi-fhir-jpaserver.fullname" . }}:{{ .Values.service.port }}/fhir/metadata'] + image: docker.io/curlimages/curl:7.84.0@sha256:5a2a25d96aa941ea2fc47acc50122f7c3d007399a075df61a82d6d2c3a567a2b + command: ["curl", "--fail-with-body"] + args: ["http://{{ include "hapi-fhir-jpaserver.fullname" . }}:{{ .Values.service.port }}/fhir/metadata?_summary=true"] + {{- with .Values.restrictedContainerSecurityContext }} securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - readOnlyRootFilesystem: true - runAsUser: 22222 - runAsNonRoot: true + {{- toYaml . | nindent 8 }} + {{- end }} resources: limits: cpu: 100m @@ -36,17 +32,34 @@ spec: exec: command: ["true"] - name: test-patient-endpoint - image: busybox:1 - command: ['wget', '-O', '-'] - args: ['http://{{ include "hapi-fhir-jpaserver.fullname" . }}:{{ .Values.service.port }}/fhir/Patient?_count=1'] + image: docker.io/curlimages/curl:7.84.0@sha256:5a2a25d96aa941ea2fc47acc50122f7c3d007399a075df61a82d6d2c3a567a2b + command: ["curl", "--fail-with-body"] + args: ["http://{{ include "hapi-fhir-jpaserver.fullname" . }}:{{ .Values.service.port }}/fhir/Patient?_count=1&_summary=true"] + {{- with .Values.restrictedContainerSecurityContext }} securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - readOnlyRootFilesystem: true - runAsUser: 22222 - runAsNonRoot: true + {{- toYaml . | nindent 8 }} + {{- end }} + resources: + limits: + cpu: 100m + memory: 128Mi + requests: + cpu: 100m + memory: 128Mi + livenessProbe: + exec: + command: ["true"] + readinessProbe: + exec: + command: ["true"] + - name: test-metrics-endpoint + image: docker.io/curlimages/curl:7.84.0@sha256:5a2a25d96aa941ea2fc47acc50122f7c3d007399a075df61a82d6d2c3a567a2b + command: ["curl", "--fail-with-body"] + args: ["http://{{ include "hapi-fhir-jpaserver.fullname" . }}:{{ .Values.metrics.service.port }}/actuator/prometheus"] + {{- with .Values.restrictedContainerSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} resources: limits: cpu: 100m diff --git a/charts/hapi-fhir-jpaserver/values.yaml b/charts/hapi-fhir-jpaserver/values.yaml index 55863c89d23..231b781821e 100644 --- a/charts/hapi-fhir-jpaserver/values.yaml +++ b/charts/hapi-fhir-jpaserver/values.yaml @@ -6,8 +6,8 @@ image: registry: docker.io # -- the path inside the repository repository: hapiproject/hapi - # -- defaults to `Chart.appVersion`. As of v5.7.0, this is the `distroless` flavor - tag: "" + # -- the image tag. As of v5.7.0, this is the `distroless` flavor by default, add `-tomcat` to use the Tomcat-based image. + tag: "v6.0.1@sha256:63c98d8be3dadc77b47dca3115490f22bf99512f363f779f7bbcb42f569aeac3" # -- image pullPolicy to use pullPolicy: IfNotPresent @@ -39,6 +39,10 @@ securityContext: readOnlyRootFilesystem: true runAsNonRoot: true runAsUser: 65532 + runAsGroup: 65532 + privileged: false + seccompProfile: + type: RuntimeDefault # service to expose the server service: @@ -123,6 +127,9 @@ postgresql: capabilities: drop: - ALL + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault readinessProbe: failureThreshold: 5 @@ -187,3 +194,17 @@ metrics: # scrapeTimeout: 10s service: port: 8081 + +# @ignore +restrictedContainerSecurityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + privileged: false + capabilities: + drop: + - ALL + runAsNonRoot: true + runAsUser: 65534 + runAsGroup: 65534 + seccompProfile: + type: RuntimeDefault From 4790c4372f28a21cd76e29169a1827095aa555ef Mon Sep 17 00:00:00 2001 From: chgl Date: Thu, 25 Aug 2022 02:46:13 +0200 Subject: [PATCH 104/200] Don't run maven CI on changes to the helm chart --- .github/workflows/maven.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 9919452b887..7fc93b8be73 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -5,10 +5,14 @@ name: Java CI with Maven on: push: - branches: - - '**' + branches: + - '**' + paths-ignore: + - "charts/**" pull_request: branches: [ master ] + paths-ignore: + - "charts/**" jobs: build: From bdc621e535a15eacca3891abd94f1bfc6a8fa333 Mon Sep 17 00:00:00 2001 From: chgl Date: Thu, 25 Aug 2022 23:21:19 +0200 Subject: [PATCH 105/200] updated helm chart to use version 6.1.0 of the image --- charts/hapi-fhir-jpaserver/Chart.yaml | 22 ++------ charts/hapi-fhir-jpaserver/README.md | 57 +++++++++++++++++++-- charts/hapi-fhir-jpaserver/README.md.gotmpl | 53 ++++++++++++++++++- charts/hapi-fhir-jpaserver/values.yaml | 2 +- 4 files changed, 108 insertions(+), 26 deletions(-) diff --git a/charts/hapi-fhir-jpaserver/Chart.yaml b/charts/hapi-fhir-jpaserver/Chart.yaml index e172526f43d..4632de77230 100644 --- a/charts/hapi-fhir-jpaserver/Chart.yaml +++ b/charts/hapi-fhir-jpaserver/Chart.yaml @@ -11,29 +11,13 @@ dependencies: repository: https://charts.bitnami.com/bitnami condition: postgresql.enabled appVersion: v6.x -version: 0.10.0 +version: 0.10.1 annotations: artifacthub.io/license: Apache-2.0 artifacthub.io/changes: | # When using the list of objects option the valid supported kinds are # added, changed, deprecated, removed, fixed, and security. - kind: changed - description: | - updated included PostgreSQL-subchart to v11.8.1. - Fixes `coalesce.go:220: warning: cannot overwrite table with non table for fhirserver.postgresql.primary.topologySpreadConstraints (map[])` warning + description: updated image version to v6.1.0 - kind: changed - description: | - set `securityContext.seccompProfile.type=RuntimeDefault` for included PostgreSQL as well as all `initContainer` and Helm - test pods to comply with the "restricted" Pod Security Standard: - - kind: changed - description: | - use curl as the image for running Helm test pods - - kind: changed - description: | - renamed `metrics` port to `http-metrics` for istio compliant naming - - kind: added - description: | - Helm test job to test metrics endpoint - - kind: changed - description: | - use full digest instead of just a tag for the server image reference + description: added section on configuring the chart for distributed tracing to the README.md diff --git a/charts/hapi-fhir-jpaserver/README.md b/charts/hapi-fhir-jpaserver/README.md index 9b72dc5c7f5..798e001bb0f 100644 --- a/charts/hapi-fhir-jpaserver/README.md +++ b/charts/hapi-fhir-jpaserver/README.md @@ -1,6 +1,6 @@ # HAPI FHIR JPA Server Starter Helm Chart -![Version: 0.10.0](https://img.shields.io/badge/Version-0.10.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v6.x](https://img.shields.io/badge/AppVersion-v6.x-informational?style=flat-square) +![Version: 0.10.1](https://img.shields.io/badge/Version-0.10.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v6.x](https://img.shields.io/badge/AppVersion-v6.x-informational?style=flat-square) This helm chart will help you install the HAPI FHIR JPA Server in a Kubernetes environment. @@ -32,7 +32,7 @@ helm install --render-subchart-notes hapi-fhir-jpaserver hapifhir/hapi-fhir-jpas | image.pullPolicy | string | `"IfNotPresent"` | image pullPolicy to use | | image.registry | string | `"docker.io"` | registry where the HAPI FHIR server image is hosted | | image.repository | string | `"hapiproject/hapi"` | the path inside the repository | -| image.tag | string | `"v6.0.1@sha256:63c98d8be3dadc77b47dca3115490f22bf99512f363f779f7bbcb42f569aeac3"` | the image tag. As of v5.7.0, this is the `distroless` flavor by default, add `-tomcat` to use the Tomcat-based image. | +| image.tag | string | `"v6.1.0@sha256:253f87bb1f5b7627f8e25f76a4b0aa7a83f31968c6e111ad74d3cc4ad9ae812e"` | the image tag. As of v5.7.0, this is the `distroless` flavor by default, add `-tomcat` to use the Tomcat-based image. | | imagePullSecrets | list | `[]` | image pull secrets to use when pulling the image | | ingress.annotations | object | `{}` | provide any additional annotations which may be required. Evaluated as a template. | | ingress.enabled | bool | `false` | whether to create an Ingress to expose the FHIR server HTTP endpoint | @@ -89,8 +89,8 @@ helm install --render-subchart-notes hapi-fhir-jpaserver hapifhir/hapi-fhir-jpas ## Development -To update the Helm chart when a new version of the `hapiproject/hapi` image is released, the [Chart.yaml](Chart.yaml)'s -`appVersion` and `version` fields need to be updated accordingly. Afterwards, re-generate the [README.md](README.md) +To update the Helm chart when a new version of the `hapiproject/hapi` image is released, [values.yaml](values.yaml) `image.tag` and the [Chart.yaml](Chart.yaml)'s +`version` and optionally the `appVersion` field on major releases need to be updated. Afterwards, re-generate the [README.md](README.md) by running: ```sh @@ -99,5 +99,54 @@ INFO[2021-11-20T12:38:04Z] Found Chart directories [charts/hapi-fhir-jpaserver] INFO[2021-11-20T12:38:04Z] Generating README Documentation for chart /usr/src/app/charts/hapi-fhir-jpaserver ``` +## Enable Distributed Tracing based on the OpenTelemtry Java Agent + +The container image includes the [OpenTelemetry Java agent JAR](https://github.com/open-telemetry/opentelemetry-java-instrumentation) +which can be used to enable distributed tracing. It can be configured entirely using environment variables, +see for details. + +Here's an example setup deploying [Jaeger](https://www.jaegertracing.io/) as a tracing backend: + +```sh +# required by the Jaeger Operator +kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.9.1/cert-manager.yaml +kubectl create namespace observability +kubectl create -f https://github.com/jaegertracing/jaeger-operator/releases/download/v1.37.0/jaeger-operator.yaml -n observability + +cat < in your browser. + ---------------------------------------------- Autogenerated from chart metadata using [helm-docs v1.11.0](https://github.com/norwoodj/helm-docs/releases/v1.11.0) diff --git a/charts/hapi-fhir-jpaserver/README.md.gotmpl b/charts/hapi-fhir-jpaserver/README.md.gotmpl index e345f8be8cb..bfea0325fe1 100644 --- a/charts/hapi-fhir-jpaserver/README.md.gotmpl +++ b/charts/hapi-fhir-jpaserver/README.md.gotmpl @@ -18,8 +18,8 @@ helm install --render-subchart-notes hapi-fhir-jpaserver hapifhir/hapi-fhir-jpas ## Development -To update the Helm chart when a new version of the `hapiproject/hapi` image is released, the [Chart.yaml](Chart.yaml)'s -`appVersion` and `version` fields need to be updated accordingly. Afterwards, re-generate the [README.md](README.md) +To update the Helm chart when a new version of the `hapiproject/hapi` image is released, [values.yaml](values.yaml) `image.tag` and the [Chart.yaml](Chart.yaml)'s +`version` and optionally the `appVersion` field on major releases need to be updated. Afterwards, re-generate the [README.md](README.md) by running: ```sh @@ -28,4 +28,53 @@ INFO[2021-11-20T12:38:04Z] Found Chart directories [charts/hapi-fhir-jpaserver] INFO[2021-11-20T12:38:04Z] Generating README Documentation for chart /usr/src/app/charts/hapi-fhir-jpaserver ``` +## Enable Distributed Tracing based on the OpenTelemtry Java Agent + +The container image includes the [OpenTelemetry Java agent JAR](https://github.com/open-telemetry/opentelemetry-java-instrumentation) +which can be used to enable distributed tracing. It can be configured entirely using environment variables, +see for details. + +Here's an example setup deploying [Jaeger](https://www.jaegertracing.io/) as a tracing backend: + +```sh +# required by the Jaeger Operator +kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.9.1/cert-manager.yaml +kubectl create namespace observability +kubectl create -f https://github.com/jaegertracing/jaeger-operator/releases/download/v1.37.0/jaeger-operator.yaml -n observability + +cat < in your browser. + {{ template "helm-docs.versionFooter" . }} diff --git a/charts/hapi-fhir-jpaserver/values.yaml b/charts/hapi-fhir-jpaserver/values.yaml index 231b781821e..8b05fbe1146 100644 --- a/charts/hapi-fhir-jpaserver/values.yaml +++ b/charts/hapi-fhir-jpaserver/values.yaml @@ -7,7 +7,7 @@ image: # -- the path inside the repository repository: hapiproject/hapi # -- the image tag. As of v5.7.0, this is the `distroless` flavor by default, add `-tomcat` to use the Tomcat-based image. - tag: "v6.0.1@sha256:63c98d8be3dadc77b47dca3115490f22bf99512f363f779f7bbcb42f569aeac3" + tag: "v6.1.0@sha256:253f87bb1f5b7627f8e25f76a4b0aa7a83f31968c6e111ad74d3cc4ad9ae812e" # -- image pullPolicy to use pullPolicy: IfNotPresent From c5e460dab02c32714fbe05f44b514ef8bdf273b8 Mon Sep 17 00:00:00 2001 From: Patrick Werner Date: Sat, 3 Sep 2022 20:04:59 +0200 Subject: [PATCH 106/200] added appProperties.getInline_resource_storage_below_size() (#420) * added appProperties.getInline_resource_storage_below_size() * indentations --- .../uhn/fhir/jpa/starter/AppProperties.java | 11 ++++++++- .../jpa/starter/BaseJpaRestfulServer.java | 4 ++++ src/main/resources/application.yaml | 23 ++++++++++--------- 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java b/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java index 27bb715c116..431431747f9 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java @@ -45,6 +45,7 @@ public class AppProperties { private Boolean filter_search_enabled = true; private Boolean graphql_enabled = false; private Boolean binary_storage_enabled = false; + private Integer inline_resource_storage_below_size = 0; private Boolean bulk_export_enabled = false; private Boolean bulk_import_enabled = false; private Boolean default_pretty_print = true; @@ -395,7 +396,15 @@ public void setBinary_storage_enabled(Boolean binary_storage_enabled) { this.binary_storage_enabled = binary_storage_enabled; } - public Boolean getBulk_export_enabled() { + public Integer getInline_resource_storage_below_size() { + return inline_resource_storage_below_size; + } + + public void setInline_resource_storage_below_size(Integer inline_resource_storage_below_size) { + this.inline_resource_storage_below_size = inline_resource_storage_below_size; + } + + public Boolean getBulk_export_enabled() { return bulk_export_enabled; } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java index 4cad6bfd726..81c0e0fb13b 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java @@ -418,6 +418,10 @@ protected void initialize() throws ServletException { 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()); diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 3c0be6675d4..b8c322ca8f6 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -100,7 +100,7 @@ hapi: bulk_import_enabled: false # enforce_referential_integrity_on_delete: false # This is an experimental feature, and does not fully support _total and other FHIR features. -# enforce_referential_integrity_on_delete: false + # enforce_referential_integrity_on_delete: false # enforce_referential_integrity_on_write: false # etag_support_enabled: true # expunge_enabled: true @@ -110,8 +110,8 @@ hapi: # graphql_enabled: true # narrative_enabled: true # mdm_enabled: true -# local_base_urls: -# - https://hapi.fhir.org/baseR4 + # local_base_urls: + # - https://hapi.fhir.org/baseR4 mdm_enabled: false # partitioning: # allow_references_across_partitions: false @@ -128,10 +128,10 @@ hapi: search-coord-queue-capacity: 200 # Threadpool size for BATCH'ed GETs in a bundle. -# bundle_batch_pool_size: 10 -# bundle_batch_pool_max_size: 50 + # bundle_batch_pool_size: 10 + # bundle_batch_pool_max_size: 50 -# logger: + # logger: # error_format: 'ERROR - ${requestVerb} ${requestUrl}' # format: >- # Path[${servletPath}] Source[${requestHeader.x-forwarded-for}] @@ -155,10 +155,11 @@ hapi: server_address: "http://hapi.fhir.org/baseR4" refuse_to_fetch_third_party_urls: false fhir_version: R4 -# validation: -# requests_enabled: true -# responses_enabled: true -# binary_storage_enabled: true + # validation: + # requests_enabled: true + # responses_enabled: true + # binary_storage_enabled: true + inline_resource_storage_below_size: 4000 # bulk_export_enabled: true # subscription: # resthook_enabled: true @@ -190,4 +191,4 @@ hapi: # rest_url: 'localhost:9200' # protocol: 'http' # schema_management_strategy: CREATE -# username: SomeUsername +# username: SomeUsername \ No newline at end of file From d660d5f76d63ad4697148f9d612aedd29640efe9 Mon Sep 17 00:00:00 2001 From: Jens Kristian Villadsen Date: Sat, 10 Sep 2022 21:14:01 +0200 Subject: [PATCH 107/200] 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 --- .../uhn/fhir/jpa/starter/AppProperties.java | 6 +- .../ca/uhn/fhir/jpa/starter/Application.java | 12 +- .../jpa/starter/BaseJpaRestfulServer.java | 429 ----------------- .../fhir/jpa/starter/JpaRestfulServer.java | 28 -- .../fhir/jpa/starter/StarterJpaConfig.java | 128 ------ .../{ => common}/ElasticsearchConfig.java | 10 +- .../{ => common}/FhirServerConfigCommon.java | 101 ++-- .../{ => common}/FhirServerConfigDstu2.java | 2 +- .../{ => common}/FhirServerConfigDstu3.java | 3 +- .../{ => common}/FhirServerConfigR4.java | 3 +- .../{ => common}/FhirServerConfigR5.java | 2 +- .../{ => common}/FhirTesterConfig.java | 23 +- .../jpa/starter/common/StarterJpaConfig.java | 433 ++++++++++++++++++ ...epositoryValidationInterceptorFactory.java | 4 +- ...toryValidationInterceptorFactoryDstu3.java | 13 +- ...ositoryValidationInterceptorFactoryR4.java | 13 +- ...ositoryValidationInterceptorFactoryR5.java | 13 +- .../jpa/starter/cql/CqlConfigCondition.java | 3 +- .../starter/{ => util}/EnvironmentHelper.java | 19 +- .../JpaHibernatePropertiesProvider.java | 2 +- src/main/resources/application.yaml | 2 +- .../java/ca/uhn/fhir/jpa/starter/Demo.java | 20 - .../jpa/starter/ExampleServerDstu3IT.java | 4 +- .../fhir/jpa/starter/ExampleServerR4IT.java | 4 - .../jpa/starter/SocketImplementation.java | 4 +- 25 files changed, 554 insertions(+), 727 deletions(-) delete mode 100644 src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java delete mode 100644 src/main/java/ca/uhn/fhir/jpa/starter/JpaRestfulServer.java delete mode 100644 src/main/java/ca/uhn/fhir/jpa/starter/StarterJpaConfig.java rename src/main/java/ca/uhn/fhir/jpa/starter/{ => common}/ElasticsearchConfig.java (85%) rename src/main/java/ca/uhn/fhir/jpa/starter/{ => common}/FhirServerConfigCommon.java (71%) rename src/main/java/ca/uhn/fhir/jpa/starter/{ => common}/FhirServerConfigDstu2.java (91%) rename src/main/java/ca/uhn/fhir/jpa/starter/{ => common}/FhirServerConfigDstu3.java (87%) rename src/main/java/ca/uhn/fhir/jpa/starter/{ => common}/FhirServerConfigR4.java (87%) rename src/main/java/ca/uhn/fhir/jpa/starter/{ => common}/FhirServerConfigR5.java (91%) rename src/main/java/ca/uhn/fhir/jpa/starter/{ => common}/FhirTesterConfig.java (78%) create mode 100644 src/main/java/ca/uhn/fhir/jpa/starter/common/StarterJpaConfig.java rename src/main/java/ca/uhn/fhir/jpa/starter/{ => common/validation}/IRepositoryValidationInterceptorFactory.java (63%) rename src/main/java/ca/uhn/fhir/jpa/starter/{ => common/validation}/RepositoryValidationInterceptorFactoryDstu3.java (84%) rename src/main/java/ca/uhn/fhir/jpa/starter/{ => common/validation}/RepositoryValidationInterceptorFactoryR4.java (84%) rename src/main/java/ca/uhn/fhir/jpa/starter/{ => common/validation}/RepositoryValidationInterceptorFactoryR5.java (84%) rename src/main/java/ca/uhn/fhir/jpa/starter/{ => util}/EnvironmentHelper.java (95%) rename src/main/java/ca/uhn/fhir/jpa/starter/{ => util}/JpaHibernatePropertiesProvider.java (96%) delete mode 100644 src/test/java/ca/uhn/fhir/jpa/starter/Demo.java diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java b/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java index 431431747f9..e7599a8b284 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java @@ -81,7 +81,7 @@ public class AppProperties { private Integer bundle_batch_pool_size = 20; private Integer bundle_batch_pool_max_size = 100; - private List local_base_urls = new ArrayList<>(); + private final List local_base_urls = new ArrayList<>(); public Boolean getOpenapi_enabled() { return openapi_enabled; @@ -203,10 +203,6 @@ public void setSupported_resource_types(List supported_resource_types) { this.supported_resource_types = supported_resource_types; } - public List getSupported_resource_types(List supported_resource_types) { - return this.supported_resource_types; - } - public Logger getLogger() { return logger; } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/Application.java b/src/main/java/ca/uhn/fhir/jpa/starter/Application.java index 2a26b5cb3ef..8031c598f7e 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/Application.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/Application.java @@ -3,11 +3,13 @@ import ca.uhn.fhir.batch2.jobs.config.Batch2JobsConfig; import ca.uhn.fhir.jpa.batch2.JpaBatch2Config; import ca.uhn.fhir.jpa.starter.annotations.OnEitherVersion; +import ca.uhn.fhir.jpa.starter.common.FhirTesterConfig; import ca.uhn.fhir.jpa.starter.mdm.MdmConfig; import ca.uhn.fhir.jpa.subscription.channel.config.SubscriptionChannelConfig; import ca.uhn.fhir.jpa.subscription.match.config.SubscriptionProcessorConfig; import ca.uhn.fhir.jpa.subscription.match.config.WebsocketDispatcherConfig; import ca.uhn.fhir.jpa.subscription.submit.config.SubscriptionSubmitterConfig; +import ca.uhn.fhir.rest.server.RestfulServer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.boot.SpringApplication; @@ -23,8 +25,7 @@ import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import org.springframework.web.servlet.DispatcherServlet; -@ServletComponentScan(basePackageClasses = { - JpaRestfulServer.class}) +@ServletComponentScan(basePackageClasses = {RestfulServer.class}) @SpringBootApplication(exclude = {ElasticsearchRestClientAutoConfiguration.class}) @Import({ SubscriptionSubmitterConfig.class, @@ -56,11 +57,10 @@ protected SpringApplicationBuilder configure( @Bean @Conditional(OnEitherVersion.class) - public ServletRegistrationBean hapiServletRegistration() { + public ServletRegistrationBean hapiServletRegistration(RestfulServer restfulServer) { ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(); - JpaRestfulServer jpaRestfulServer = new JpaRestfulServer(); - beanFactory.autowireBean(jpaRestfulServer); - servletRegistrationBean.setServlet(jpaRestfulServer); + beanFactory.autowireBean(restfulServer); + servletRegistrationBean.setServlet(restfulServer); servletRegistrationBean.addUrlMappings("/fhir/*"); servletRegistrationBean.setLoadOnStartup(1); diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java deleted file mode 100644 index 81c0e0fb13b..00000000000 --- a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java +++ /dev/null @@ -1,429 +0,0 @@ -package ca.uhn.fhir.jpa.starter; - -import ca.uhn.fhir.batch2.jobs.imprt.BulkDataImportProvider; -import ca.uhn.fhir.batch2.jobs.reindex.ReindexProvider; -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.context.FhirVersionEnum; -import ca.uhn.fhir.context.support.IValidationSupport; -import ca.uhn.fhir.cql.common.provider.CqlProviderLoader; -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.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.graphql.GraphQLProvider; -import ca.uhn.fhir.jpa.interceptor.CascadingDeleteInterceptor; -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.subscription.util.SubscriptionDebugLogInterceptor; -import ca.uhn.fhir.mdm.provider.MdmProviderLoader; -import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator; -import ca.uhn.fhir.narrative.INarrativeGenerator; -import ca.uhn.fhir.narrative2.NullNarrativeGenerator; -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.r4.model.Bundle.BundleType; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.http.HttpHeaders; -import org.springframework.web.cors.CorsConfiguration; - -import javax.servlet.ServletException; -import java.util.*; -import java.util.stream.Collectors; - -public class BaseJpaRestfulServer extends RestfulServer { - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseJpaRestfulServer.class); - - private static final long serialVersionUID = 1L; - @Autowired - DaoRegistry daoRegistry; - @Autowired - DaoConfig daoConfig; - @Autowired - ISearchParamRegistry searchParamRegistry; - @Autowired - IFhirSystemDao fhirSystemDao; - @Autowired - ResourceProviderFactory resourceProviderFactory; - @Autowired - IJpaSystemProvider jpaSystemProvider; - @Autowired - ValueSetOperationProvider myValueSetOperationProvider; - @Autowired - IInterceptorBroadcaster interceptorBroadcaster; - @Autowired - DatabaseBackedPagingProvider databaseBackedPagingProvider; - @Autowired - IInterceptorService interceptorService; - @Autowired - IValidatorModule validatorModule; - @Autowired - Optional graphQLProvider; - @Autowired - BulkDataExportProvider bulkDataExportProvider; - @Autowired - BulkDataImportProvider bulkDataImportProvider; - @Autowired - PartitionManagementProvider partitionManagementProvider; - - @Autowired - ValueSetOperationProvider valueSetOperationProvider; - @Autowired - ReindexProvider reindexProvider; - @Autowired - BinaryStorageInterceptor binaryStorageInterceptor; - @Autowired - Optional binaryAccessProvider; - @Autowired - IPackageInstallerSvc packageInstallerSvc; - @Autowired - AppProperties appProperties; - @Autowired - ApplicationContext myApplicationContext; - @Autowired(required = false) - IRepositoryValidationInterceptorFactory factory; - // These are set only if the features are enabled - @Autowired - Optional cqlProviderLoader; - @Autowired - Optional mdmProviderProvider; - - @Autowired - private IValidationSupport myValidationSupport; - - public BaseJpaRestfulServer() { - } - - @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. - */ - // Customize supported resource types - List supportedResourceTypes = appProperties.getSupported_resource_types(); - - if (!supportedResourceTypes.isEmpty()) { - if (!supportedResourceTypes.contains("SearchParameter")) { - supportedResourceTypes.add("SearchParameter"); - } - daoRegistry.setSupportedResourceTypes(supportedResourceTypes); - } - - setFhirContext(fhirSystemDao.getContext()); - - /* - * Order matters - the MDM provider registers itself on the resourceProviderFactory - hence the loading must be done - * ahead of provider registration - */ - if(appProperties.getMdm_enabled()) - mdmProviderProvider.get().loadProvider(); - - registerProviders(resourceProviderFactory.createProviders()); - registerProvider(jpaSystemProvider); - /* - * 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 - */ - - FhirVersionEnum fhirVersion = fhirSystemDao.getContext().getVersion().getVersion(); - if (fhirVersion == FhirVersionEnum.DSTU2) { - - JpaConformanceProviderDstu2 confProvider = new JpaConformanceProviderDstu2(this, fhirSystemDao, - daoConfig); - confProvider.setImplementationDescription("HAPI FHIR DSTU2 Server"); - setServerConformanceProvider(confProvider); - } else { - if (fhirVersion == FhirVersionEnum.DSTU3) { - - JpaConformanceProviderDstu3 confProvider = new JpaConformanceProviderDstu3(this, fhirSystemDao, - daoConfig, searchParamRegistry); - confProvider.setImplementationDescription("HAPI FHIR DSTU3 Server"); - setServerConformanceProvider(confProvider); - } else if (fhirVersion == FhirVersionEnum.R4) { - - JpaCapabilityStatementProvider confProvider = new JpaCapabilityStatementProvider(this, fhirSystemDao, - daoConfig, searchParamRegistry, myValidationSupport); - confProvider.setImplementationDescription("HAPI FHIR R4 Server"); - setServerConformanceProvider(confProvider); - } else if (fhirVersion == FhirVersionEnum.R5) { - - JpaCapabilityStatementProvider confProvider = new JpaCapabilityStatementProvider(this, fhirSystemDao, - daoConfig, searchParamRegistry, myValidationSupport); - confProvider.setImplementationDescription("HAPI FHIR R5 Server"); - setServerConformanceProvider(confProvider); - } else { - throw new IllegalStateException(); - } - } - - /* - * ETag Support - */ - - if (appProperties.getEtag_support_enabled() == false) - setETagSupport(ETagSupportEnum.DISABLED); - - - /* - * This server tries to dynamically generate narratives - */ - FhirContext ctx = getFhirContext(); - INarrativeGenerator theNarrativeGenerator = - appProperties.getNarrative_enabled() ? - new DefaultThymeleafNarrativeGenerator() : - new NullNarrativeGenerator(); - ctx.setNarrativeGenerator(theNarrativeGenerator); - - /* - * Default to JSON and pretty printing - */ - setDefaultPrettyPrint(appProperties.getDefault_pretty_print()); - - /* - * Default encoding - */ - 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. - */ - - setPagingProvider(databaseBackedPagingProvider); - - /* - * 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 (appProperties.getFhirpath_interceptor_enabled()) { - registerInterceptor(new FhirPathFilterInterceptor()); - } - - /* - * 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()); - 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 = appProperties.getServer_address(); - if (!Strings.isNullOrEmpty(serverAddress)) { - setServerAddressStrategy(new HardcodedServerAddressStrategy(serverAddress)); - } else if (appProperties.getUse_apache_address_strategy()) { - boolean useHttps = appProperties.getUse_apache_address_strategy_https(); - setServerAddressStrategy(useHttps ? ApacheProxyAddressStrategy.forHttps() : - ApacheProxyAddressStrategy.forHttp()); - } else { - 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 (ctx.getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.DSTU3)) { // <-- ENABLED RIGHT NOW - registerProvider(myApplicationContext.getBean(TerminologyUploaderProvider.class)); - } - - // 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 - registerProvider(myApplicationContext.getBean(SubscriptionTriggeringProvider.class)); - } - - // Define your CORS configuration. This is an example - // showing a typical setup. You should customize this - // to your specific needs - if (appProperties.getCors() != null) { - 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 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 - CorsInterceptor interceptor = new CorsInterceptor(config); - registerInterceptor(interceptor); - } else { - ourLog.info("CORS is disabled on this server"); - } - - // If subscriptions are enabled, we want to register the interceptor that - // will activate them and match results against them - if (appProperties.getSubscription() != null) { - // Subscription debug logging - interceptorService.registerInterceptor(new SubscriptionDebugLogInterceptor()); - } - - // Cascading deletes - - - if (appProperties.getAllow_cascading_deletes()) { - CascadingDeleteInterceptor cascadingDeleteInterceptor = new CascadingDeleteInterceptor(ctx, - daoRegistry, interceptorBroadcaster); - getInterceptorService().registerInterceptor(cascadingDeleteInterceptor); - } - - // Binary Storage - if (appProperties.getBinary_storage_enabled() && binaryAccessProvider.isPresent()) { - registerProvider(binaryAccessProvider.get()); - getInterceptorService().registerInterceptor(binaryStorageInterceptor); - } - - // Validation - - if (validatorModule != null) { - if (appProperties.getValidation().getRequests_enabled()) { - RequestValidatingInterceptor interceptor = new RequestValidatingInterceptor(); - interceptor.setFailOnSeverity(ResultSeverityEnum.ERROR); - interceptor.setValidatorModules(Collections.singletonList(validatorModule)); - registerInterceptor(interceptor); - } - if (appProperties.getValidation().getResponses_enabled()) { - ResponseValidatingInterceptor interceptor = new ResponseValidatingInterceptor(); - interceptor.setFailOnSeverity(ResultSeverityEnum.ERROR); - interceptor.setValidatorModules(Collections.singletonList(validatorModule)); - registerInterceptor(interceptor); - } - } - - // GraphQL - if (appProperties.getGraphql_enabled()) { - if (fhirVersion.isEqualOrNewerThan(FhirVersionEnum.DSTU3)) { - registerProvider(graphQLProvider.get()); - } - } - - 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.getOpenapi_enabled()) { - registerInterceptor(new OpenApiInterceptor()); - } - - // Bulk Export - if (appProperties.getBulk_export_enabled()) { - registerProvider(bulkDataExportProvider); - } - - //Bulk Import - if (appProperties.getBulk_import_enabled()) { - registerProvider(bulkDataImportProvider); - } - - // valueSet Operations i.e $expand - registerProvider(myValueSetOperationProvider); - - //reindex Provider $reindex - registerProvider(reindexProvider); - - // Partitioning - if (appProperties.getPartitioning() != null) { - registerInterceptor(new RequestTenantPartitionInterceptor()); - setTenantIdentificationStrategy(new UrlBaseTenantIdentificationStrategy()); - registerProviders(partitionManagementProvider); - } - - 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()); - - if (appProperties.getImplementationGuides() != null) { - Map guides = appProperties.getImplementationGuides(); - for (Map.Entry 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); - } - } - - if(factory != null) { - interceptorService.registerInterceptor(factory.buildUsingStoredStructureDefinitions()); - } - - - 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()); - } -} diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/JpaRestfulServer.java b/src/main/java/ca/uhn/fhir/jpa/starter/JpaRestfulServer.java deleted file mode 100644 index c895c2240cd..00000000000 --- a/src/main/java/ca/uhn/fhir/jpa/starter/JpaRestfulServer.java +++ /dev/null @@ -1,28 +0,0 @@ -package ca.uhn.fhir.jpa.starter; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Import; - -import javax.servlet.ServletException; - -@Import(AppProperties.class) -public class JpaRestfulServer extends BaseJpaRestfulServer { - - @Autowired - AppProperties appProperties; - - private static final long serialVersionUID = 1L; - - public JpaRestfulServer() { - super(); - } - - @Override - protected void initialize() throws ServletException { - super.initialize(); - - // Add your own customization here - - } - -} diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/StarterJpaConfig.java b/src/main/java/ca/uhn/fhir/jpa/starter/StarterJpaConfig.java deleted file mode 100644 index 2acfabd898f..00000000000 --- a/src/main/java/ca/uhn/fhir/jpa/starter/StarterJpaConfig.java +++ /dev/null @@ -1,128 +0,0 @@ -package ca.uhn.fhir.jpa.starter; - -import ca.uhn.fhir.context.ConfigurationException; -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.jpa.api.IDaoRegistry; -import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao; -import ca.uhn.fhir.jpa.batch.config.NonPersistedBatchConfigurer; -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.provider.DaoRegistryResourceSupportedSvc; -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.util.ResourceCountCache; -import ca.uhn.fhir.jpa.validation.JpaValidationSupportChain; -import ca.uhn.fhir.mdm.dao.IMdmLinkDao; -import ca.uhn.fhir.rest.api.IResourceSupportedSvc; -import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; - -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.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; -import org.springframework.core.env.ConfigurableEnvironment; -import org.springframework.orm.jpa.JpaTransactionManager; -import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; - -import javax.persistence.EntityManagerFactory; -import javax.sql.DataSource; - -@Configuration -public class StarterJpaConfig { - @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 - AppProperties appProperties; - @Autowired - private DataSource myDataSource; - @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() { - 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( - 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; - } - - @Autowired - private ISearchParamRegistry mySearchParamRegistry; - - @Bean - public IHSearchSortHelper hSearchSortHelper() { - return new HSearchSortHelperImpl(mySearchParamRegistry); - } - - @Bean - public IMdmLinkDao mdmLinkDao(){ - return new MdmLinkDaoJpaImpl(); - } -} diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/ElasticsearchConfig.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/ElasticsearchConfig.java similarity index 85% rename from src/main/java/ca/uhn/fhir/jpa/starter/ElasticsearchConfig.java rename to src/main/java/ca/uhn/fhir/jpa/starter/common/ElasticsearchConfig.java index 21216e6dd59..78de8907af4 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/ElasticsearchConfig.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/ElasticsearchConfig.java @@ -1,7 +1,7 @@ -package ca.uhn.fhir.jpa.starter; +package ca.uhn.fhir.jpa.starter.common; import ca.uhn.fhir.jpa.search.lastn.ElasticsearchSvcImpl; -import org.springframework.beans.factory.annotation.Autowired; +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; @@ -10,12 +10,8 @@ @Configuration public class ElasticsearchConfig { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ElasticsearchConfig.class); - - @Autowired - private ConfigurableEnvironment configurableEnvironment; - @Bean - public ElasticsearchSvcImpl elasticsearchSvc() { + public ElasticsearchSvcImpl elasticsearchSvc(ConfigurableEnvironment configurableEnvironment) { if (EnvironmentHelper.isElasticsearchEnabled(configurableEnvironment)) { String elasticsearchUrl = EnvironmentHelper.getElasticsearchServerUrl(configurableEnvironment); if (elasticsearchUrl.startsWith("http")) { diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigCommon.java similarity index 71% rename from src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java rename to src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigCommon.java index ac80f093b0e..4dd4412c77a 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigCommon.java @@ -1,4 +1,4 @@ -package ca.uhn.fhir.jpa.starter; +package ca.uhn.fhir.jpa.starter.common; import ca.uhn.fhir.jpa.api.config.DaoConfig; import ca.uhn.fhir.jpa.binary.api.IBinaryStorageSvc; @@ -7,6 +7,8 @@ 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; @@ -14,6 +16,7 @@ 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; @@ -25,6 +28,7 @@ import java.util.HashSet; import java.util.Optional; +import java.util.stream.Collectors; /** * This is the primary configuration file for the example server @@ -78,54 +82,85 @@ public FhirServerConfigCommon(AppProperties appProperties) { */ @Bean public DaoConfig daoConfig(AppProperties appProperties) { - DaoConfig retVal = new DaoConfig(); - - retVal.setIndexMissingFields(appProperties.getEnable_index_missing_fields() ? DaoConfig.IndexEnabledEnum.ENABLED : DaoConfig.IndexEnabledEnum.DISABLED); - retVal.setAutoCreatePlaceholderReferenceTargets(appProperties.getAuto_create_placeholder_reference_targets()); - retVal.setEnforceReferentialIntegrityOnWrite(appProperties.getEnforce_referential_integrity_on_write()); - retVal.setEnforceReferentialIntegrityOnDelete(appProperties.getEnforce_referential_integrity_on_delete()); - retVal.setAllowContainsSearches(appProperties.getAllow_contains_searches()); - retVal.setAllowMultipleDelete(appProperties.getAllow_multiple_delete()); - retVal.setAllowExternalReferences(appProperties.getAllow_external_references()); - retVal.setSchedulingDisabled(!appProperties.getDao_scheduling_enabled()); - retVal.setDeleteExpungeEnabled(appProperties.getDelete_expunge_enabled()); - retVal.setExpungeEnabled(appProperties.getExpunge_enabled()); + 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) - retVal.setEmailFromAddress(appProperties.getSubscription().getEmail().getFrom()); + daoConfig.setEmailFromAddress(appProperties.getSubscription().getEmail().getFrom()); Integer maxFetchSize = appProperties.getMax_page_size(); - retVal.setFetchSizeDefaultMaximum(maxFetchSize); + 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(); - retVal.setReuseCachedSearchResultsForMillis(reuseCachedSearchResultsMillis); + daoConfig.setReuseCachedSearchResultsForMillis(reuseCachedSearchResultsMillis); ourLog.info("Server configured to cache search results for {} milliseconds", reuseCachedSearchResultsMillis); Long retainCachedSearchesMinutes = appProperties.getRetain_cached_searches_mins(); - retVal.setExpireSearchResultsAfterMillis(retainCachedSearchesMinutes * 60 * 1000); + 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"); - retVal.addSupportedSubscriptionType(org.hl7.fhir.dstu2.model.Subscription.SubscriptionChannelType.RESTHOOK); + daoConfig.addSupportedSubscriptionType(org.hl7.fhir.dstu2.model.Subscription.SubscriptionChannelType.RESTHOOK); } if (appProperties.getSubscription().getEmail() != null) { ourLog.info("Enabling email subscriptions"); - retVal.addSupportedSubscriptionType(org.hl7.fhir.dstu2.model.Subscription.SubscriptionChannelType.EMAIL); + daoConfig.addSupportedSubscriptionType(org.hl7.fhir.dstu2.model.Subscription.SubscriptionChannelType.EMAIL); } if (appProperties.getSubscription().getWebsocket_enabled()) { ourLog.info("Enabling websocket subscriptions"); - retVal.addSupportedSubscriptionType(org.hl7.fhir.dstu2.model.Subscription.SubscriptionChannelType.WEBSOCKET); + daoConfig.addSupportedSubscriptionType(org.hl7.fhir.dstu2.model.Subscription.SubscriptionChannelType.WEBSOCKET); } } - retVal.setFilterParameterEnabled(appProperties.getFilter_search_enabled()); - retVal.setAdvancedHSearchIndexing(appProperties.getAdvanced_lucene_indexing()); - retVal.setTreatBaseUrlsAsLocal(new HashSet<>(appProperties.getLocal_base_urls())); + daoConfig.setFilterParameterEnabled(appProperties.getFilter_search_enabled()); + daoConfig.setAdvancedHSearchIndexing(appProperties.getAdvanced_lucene_indexing()); + daoConfig.setTreatBaseUrlsAsLocal(new HashSet<>(appProperties.getLocal_base_urls())); - return retVal; + 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 @@ -183,24 +218,6 @@ public ModelConfig modelConfig(AppProperties appProperties, DaoConfig daoConfig) return modelConfig; } - /** - * The following bean configures the database connection. The 'url' property value of "jdbc:derby:directory:jpaserver_derby_files;create=true" indicates that the server should save resources in a - * directory called "jpaserver_derby_files". - *

- * A URL to a remote database could also be placed here, along with login credentials and other properties supported by BasicDataSource. - */ - /*@Bean(destroyMethod = "close") - public BasicDataSource dataSource() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { - BasicDataSource retVal = new BasicDataSource(); - Driver driver = (Driver) Class.forName(HapiProperties.getDataSourceDriver()).getConstructor().newInstance(); - retVal.setDriver(driver); - retVal.setUrl(HapiProperties.getDataSourceUrl()); - retVal.setUsername(HapiProperties.getDataSourceUsername()); - retVal.setPassword(HapiProperties.getDataSourcePassword()); - retVal.setMaxTotal(HapiProperties.getDataSourceMaxPoolSize()); - return retVal; - }*/ - @Lazy @Bean public IBinaryStorageSvc binaryStorageSvc(AppProperties appProperties) { diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu2.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigDstu2.java similarity index 91% rename from src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu2.java rename to src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigDstu2.java index b8011389d20..151df82898f 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu2.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigDstu2.java @@ -1,4 +1,4 @@ -package ca.uhn.fhir.jpa.starter; +package ca.uhn.fhir.jpa.starter.common; import ca.uhn.fhir.jpa.config.JpaDstu2Config; import ca.uhn.fhir.jpa.starter.annotations.OnDSTU2Condition; diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigDstu3.java similarity index 87% rename from src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java rename to src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigDstu3.java index 35d1dabb49c..e0056bc1912 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigDstu3.java @@ -1,9 +1,8 @@ -package ca.uhn.fhir.jpa.starter; +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 ca.uhn.fhir.jpa.starter.mdm.MdmConfig; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigR4.java similarity index 87% rename from src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java rename to src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigR4.java index c31cf529edd..4c212ef2bce 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigR4.java @@ -1,9 +1,8 @@ -package ca.uhn.fhir.jpa.starter; +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 ca.uhn.fhir.jpa.starter.mdm.MdmConfig; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigR5.java similarity index 91% rename from src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java rename to src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigR5.java index 8ee03df272d..1552aef7aee 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigR5.java @@ -1,4 +1,4 @@ -package ca.uhn.fhir.jpa.starter; +package ca.uhn.fhir.jpa.starter.common; import ca.uhn.fhir.jpa.config.r5.JpaR5Config; import ca.uhn.fhir.jpa.starter.annotations.OnR5Condition; diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirTesterConfig.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirTesterConfig.java similarity index 78% rename from src/main/java/ca/uhn/fhir/jpa/starter/FhirTesterConfig.java rename to src/main/java/ca/uhn/fhir/jpa/starter/common/FhirTesterConfig.java index 7adf8bf4727..cb28659d52c 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirTesterConfig.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirTesterConfig.java @@ -1,5 +1,6 @@ -package ca.uhn.fhir.jpa.starter; +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; @@ -36,17 +37,17 @@ public class FhirTesterConfig { @Bean public TesterConfig testerConfig(AppProperties appProperties) { TesterConfig retVal = new TesterConfig(); - appProperties.getTester().entrySet().stream().forEach(t -> { - retVal - .addServer() - .withId(t.getKey()) - .withFhirVersion(t.getValue().getFhir_version()) - .withBaseUrl(t.getValue().getServer_address()) - .withName(t.getValue().getName()); - retVal.setRefuseToFetchThirdPartyUrls( - t.getValue().getRefuse_to_fetch_third_party_urls()); + 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; } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/common/StarterJpaConfig.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/StarterJpaConfig.java new file mode 100644 index 00000000000..b2f108a084e --- /dev/null +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/StarterJpaConfig.java @@ -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 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 mdmProviderProvider, IJpaSystemProvider jpaSystemProvider, ResourceProviderFactory resourceProviderFactory, DaoConfig daoConfig, ISearchParamRegistry searchParamRegistry, IValidationSupport theValidationSupport, DatabaseBackedPagingProvider databaseBackedPagingProvider, LoggingInterceptor loggingInterceptor, Optional terminologyUploaderProvider, Optional subscriptionTriggeringProvider, Optional corsInterceptor, IInterceptorBroadcaster interceptorBroadcaster, Optional binaryAccessProvider, BinaryStorageInterceptor binaryStorageInterceptor, IValidatorModule validatorModule, Optional graphQLProvider, BulkDataExportProvider bulkDataExportProvider, BulkDataImportProvider bulkDataImportProvider, ValueSetOperationProvider theValueSetOperationProvider, ReindexProvider reindexProvider, PartitionManagementProvider partitionManagementProvider, Optional repositoryValidatingInterceptor, IPackageInstallerSvc packageInstallerSvc) { + RestfulServer fhirServer = new RestfulServer(fhirSystemDao.getContext()); + + List 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 guides = appProperties.getImplementationGuides(); + for (Map.Entry 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(); + } + } +} + diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/IRepositoryValidationInterceptorFactory.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/validation/IRepositoryValidationInterceptorFactory.java similarity index 63% rename from src/main/java/ca/uhn/fhir/jpa/starter/IRepositoryValidationInterceptorFactory.java rename to src/main/java/ca/uhn/fhir/jpa/starter/common/validation/IRepositoryValidationInterceptorFactory.java index 13262a1cf1c..67a40c7761c 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/IRepositoryValidationInterceptorFactory.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/validation/IRepositoryValidationInterceptorFactory.java @@ -1,8 +1,10 @@ -package ca.uhn.fhir.jpa.starter; +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(); diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/RepositoryValidationInterceptorFactoryDstu3.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/validation/RepositoryValidationInterceptorFactoryDstu3.java similarity index 84% rename from src/main/java/ca/uhn/fhir/jpa/starter/RepositoryValidationInterceptorFactoryDstu3.java rename to src/main/java/ca/uhn/fhir/jpa/starter/common/validation/RepositoryValidationInterceptorFactoryDstu3.java index a68ae3eb2f0..f8874b31a50 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/RepositoryValidationInterceptorFactoryDstu3.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/validation/RepositoryValidationInterceptorFactoryDstu3.java @@ -1,4 +1,4 @@ -package ca.uhn.fhir.jpa.starter; +package ca.uhn.fhir.jpa.starter.common.validation; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; @@ -19,6 +19,8 @@ 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. @@ -26,7 +28,7 @@ * The enable_repository_validating_interceptor property must be enabled in application.yaml * in order to use this class. */ -@ConditionalOnProperty(prefix = "hapi.fhir", name = "enable_repository_validating_interceptor", havingValue = "true") +@ConditionalOnProperty(prefix = "hapi.fhir", name = ENABLE_REPOSITORY_VALIDATING_INTERCEPTOR, havingValue = "true") @Configuration @Conditional(OnDSTU3Condition.class) public class RepositoryValidationInterceptorFactoryDstu3 implements IRepositoryValidationInterceptorFactory { @@ -50,10 +52,9 @@ public RepositoryValidatingInterceptor buildUsingStoredStructureDefinitions() { .map(StructureDefinition.class::cast) .collect(Collectors.groupingBy(StructureDefinition::getType)); - structureDefintions.entrySet().forEach(structureDefinitionListEntry -> - { - String[] urls = structureDefinitionListEntry.getValue().stream().map(StructureDefinition::getUrl).toArray(String[]::new); - repositoryValidatingRuleBuilder.forResourcesOfType(structureDefinitionListEntry.getKey()).requireAtLeastOneProfileOf(urls).and().requireValidationToDeclaredProfiles(); + structureDefintions.forEach((key, value) -> { + String[] urls = value.stream().map(StructureDefinition::getUrl).toArray(String[]::new); + repositoryValidatingRuleBuilder.forResourcesOfType(key).requireAtLeastOneProfileOf(urls).and().requireValidationToDeclaredProfiles(); }); List rules = repositoryValidatingRuleBuilder.build(); diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/RepositoryValidationInterceptorFactoryR4.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/validation/RepositoryValidationInterceptorFactoryR4.java similarity index 84% rename from src/main/java/ca/uhn/fhir/jpa/starter/RepositoryValidationInterceptorFactoryR4.java rename to src/main/java/ca/uhn/fhir/jpa/starter/common/validation/RepositoryValidationInterceptorFactoryR4.java index a563d3cb354..c4fca012c6b 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/RepositoryValidationInterceptorFactoryR4.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/validation/RepositoryValidationInterceptorFactoryR4.java @@ -1,4 +1,4 @@ -package ca.uhn.fhir.jpa.starter; +package ca.uhn.fhir.jpa.starter.common.validation; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; @@ -19,6 +19,8 @@ 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. @@ -26,7 +28,7 @@ * The enable_repository_validating_interceptor property must be enabled in application.yaml * in order to use this class. */ -@ConditionalOnProperty(prefix = "hapi.fhir", name = "enable_repository_validating_interceptor", havingValue = "true") +@ConditionalOnProperty(prefix = "hapi.fhir", name = ENABLE_REPOSITORY_VALIDATING_INTERCEPTOR, havingValue = "true") @Configuration @Conditional(OnR4Condition.class) public class RepositoryValidationInterceptorFactoryR4 implements IRepositoryValidationInterceptorFactory { @@ -51,10 +53,9 @@ public RepositoryValidatingInterceptor buildUsingStoredStructureDefinitions() { .map(StructureDefinition.class::cast) .collect(Collectors.groupingBy(StructureDefinition::getType)); - structureDefintions.entrySet().forEach(structureDefinitionListEntry -> - { - String[] urls = structureDefinitionListEntry.getValue().stream().map(StructureDefinition::getUrl).toArray(String[]::new); - repositoryValidatingRuleBuilder.forResourcesOfType(structureDefinitionListEntry.getKey()).requireAtLeastOneProfileOf(urls).and().requireValidationToDeclaredProfiles(); + structureDefintions.forEach((key, value) -> { + String[] urls = value.stream().map(StructureDefinition::getUrl).toArray(String[]::new); + repositoryValidatingRuleBuilder.forResourcesOfType(key).requireAtLeastOneProfileOf(urls).and().requireValidationToDeclaredProfiles(); }); List rules = repositoryValidatingRuleBuilder.build(); diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/RepositoryValidationInterceptorFactoryR5.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/validation/RepositoryValidationInterceptorFactoryR5.java similarity index 84% rename from src/main/java/ca/uhn/fhir/jpa/starter/RepositoryValidationInterceptorFactoryR5.java rename to src/main/java/ca/uhn/fhir/jpa/starter/common/validation/RepositoryValidationInterceptorFactoryR5.java index 7702ef0baf2..594800dd2d6 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/RepositoryValidationInterceptorFactoryR5.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/validation/RepositoryValidationInterceptorFactoryR5.java @@ -1,4 +1,4 @@ -package ca.uhn.fhir.jpa.starter; +package ca.uhn.fhir.jpa.starter.common.validation; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; @@ -19,6 +19,8 @@ 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. @@ -26,7 +28,7 @@ * The enable_repository_validating_interceptor property must be enabled in application.yaml * in order to use this class. */ -@ConditionalOnProperty(prefix = "hapi.fhir", name = "enable_repository_validating_interceptor", havingValue = "true") +@ConditionalOnProperty(prefix = "hapi.fhir", name = ENABLE_REPOSITORY_VALIDATING_INTERCEPTOR, havingValue = "true") @Configuration @Conditional(OnR5Condition.class) public class RepositoryValidationInterceptorFactoryR5 implements IRepositoryValidationInterceptorFactory { @@ -50,10 +52,9 @@ public RepositoryValidatingInterceptor buildUsingStoredStructureDefinitions() { .map(StructureDefinition.class::cast) .collect(Collectors.groupingBy(StructureDefinition::getType)); - structureDefintions.entrySet().forEach(structureDefinitionListEntry -> - { - String[] urls = structureDefinitionListEntry.getValue().stream().map(StructureDefinition::getUrl).toArray(String[]::new); - repositoryValidatingRuleBuilder.forResourcesOfType(structureDefinitionListEntry.getKey()).requireAtLeastOneProfileOf(urls).and().requireValidationToDeclaredProfiles(); + structureDefintions.forEach((key, value) -> { + String[] urls = value.stream().map(StructureDefinition::getUrl).toArray(String[]::new); + repositoryValidatingRuleBuilder.forResourcesOfType(key).requireAtLeastOneProfileOf(urls).and().requireValidationToDeclaredProfiles(); }); List rules = repositoryValidatingRuleBuilder.build(); diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/cql/CqlConfigCondition.java b/src/main/java/ca/uhn/fhir/jpa/starter/cql/CqlConfigCondition.java index 0a5f7f41515..38622ba9d89 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/cql/CqlConfigCondition.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/cql/CqlConfigCondition.java @@ -9,7 +9,6 @@ public class CqlConfigCondition implements Condition { @Override public boolean matches(ConditionContext theConditionContext, AnnotatedTypeMetadata theAnnotatedTypeMetadata) { String property = theConditionContext.getEnvironment().getProperty("hapi.fhir.cql_enabled"); - boolean enabled = Boolean.parseBoolean(property); - return enabled; + return Boolean.parseBoolean(property); } } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/EnvironmentHelper.java b/src/main/java/ca/uhn/fhir/jpa/starter/util/EnvironmentHelper.java similarity index 95% rename from src/main/java/ca/uhn/fhir/jpa/starter/EnvironmentHelper.java rename to src/main/java/ca/uhn/fhir/jpa/starter/util/EnvironmentHelper.java index 732705ac521..41bb556c4c3 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/EnvironmentHelper.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/util/EnvironmentHelper.java @@ -1,9 +1,10 @@ -package ca.uhn.fhir.jpa.starter; +package ca.uhn.fhir.jpa.starter.util; import ca.uhn.fhir.jpa.config.HapiFhirLocalContainerEntityManagerFactoryBean; import ca.uhn.fhir.jpa.search.HapiHSearchAnalysisConfigurers; import ca.uhn.fhir.jpa.search.elastic.ElasticsearchHibernatePropertiesBuilder; import org.apache.lucene.util.Version; +import org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy; import org.hibernate.cfg.AvailableSettings; import org.hibernate.search.backend.elasticsearch.cfg.ElasticsearchBackendSettings; import org.hibernate.search.backend.elasticsearch.index.IndexStatus; @@ -27,6 +28,8 @@ import java.util.Map; import java.util.Properties; +import static java.util.Objects.requireNonNullElse; + public class EnvironmentHelper { public static Properties getHibernateProperties(ConfigurableEnvironment environment, @@ -41,7 +44,7 @@ public static Properties getHibernateProperties(ConfigurableEnvironment environm //Spring Boot Autoconfiguration defaults properties.putIfAbsent(AvailableSettings.SCANNER, "org.hibernate.boot.archive.scan.internal.DisabledScanner"); properties.putIfAbsent(AvailableSettings.IMPLICIT_NAMING_STRATEGY, SpringImplicitNamingStrategy.class.getName()); - properties.putIfAbsent(AvailableSettings.PHYSICAL_NAMING_STRATEGY, SpringPhysicalNamingStrategy.class.getName()); + properties.putIfAbsent(AvailableSettings.PHYSICAL_NAMING_STRATEGY, CamelCaseToUnderscoresNamingStrategy.class.getName()); //TODO The bean factory should be added as parameter but that requires that it can be injected from the entityManagerFactory bean from xBaseConfig //properties.putIfAbsent(AvailableSettings.BEAN_CONTAINER, new SpringBeanContainer(beanFactory)); @@ -102,18 +105,6 @@ public static Properties getHibernateProperties(ConfigurableEnvironment environm return properties; } - //TODO Removed when we're up on Java 11 - private static T requireNonNullElse(T obj, T defaultObj) { - return (obj != null) ? obj : requireNonNull(defaultObj, "defaultObj"); - } - - //TODO Removed when we're up on Java 11 - private static T requireNonNull(T obj, String message) { - if (obj == null) - throw new NullPointerException(message); - return obj; - } - public static String getElasticsearchServerUrl(ConfigurableEnvironment environment) { return environment.getProperty("elasticsearch.rest_url", String.class); } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/JpaHibernatePropertiesProvider.java b/src/main/java/ca/uhn/fhir/jpa/starter/util/JpaHibernatePropertiesProvider.java similarity index 96% rename from src/main/java/ca/uhn/fhir/jpa/starter/JpaHibernatePropertiesProvider.java rename to src/main/java/ca/uhn/fhir/jpa/starter/util/JpaHibernatePropertiesProvider.java index d090b9c29c7..83125a99e0f 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/JpaHibernatePropertiesProvider.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/util/JpaHibernatePropertiesProvider.java @@ -1,4 +1,4 @@ -package ca.uhn.fhir.jpa.starter; +package ca.uhn.fhir.jpa.starter.util; import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.jpa.config.HibernatePropertiesProvider; diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index b8c322ca8f6..b1baff58e91 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -89,7 +89,7 @@ hapi: # default_pretty_print: true # default_page_size: 20 # delete_expunge_enabled: true - # enable_repository_validating_interceptor: false + # enable_repository_validating_interceptor: true # enable_index_missing_fields: false # enable_index_of_type: true # enable_index_contained_resource: false diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/Demo.java b/src/test/java/ca/uhn/fhir/jpa/starter/Demo.java deleted file mode 100644 index d46094360ee..00000000000 --- a/src/test/java/ca/uhn/fhir/jpa/starter/Demo.java +++ /dev/null @@ -1,20 +0,0 @@ -package ca.uhn.fhir.jpa.starter; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientAutoConfiguration; -import org.springframework.boot.web.servlet.ServletComponentScan; - -@ServletComponentScan(basePackageClasses = {JpaRestfulServer.class}) -@SpringBootApplication(exclude = ElasticsearchRestClientAutoConfiguration.class) -public class Demo { - - public static void main(String[] args) { - - System.setProperty("spring.profiles.active", "r4"); - System.setProperty("spring.batch.job.enabled", "false"); - SpringApplication.run(Demo.class, args); - - //Server is now accessible at eg. http://localhost:8080/fhir/metadata - } -} 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 b4ca6c3cec3..0dfcf83da59 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu3IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu3IT.java @@ -113,7 +113,7 @@ public void testCQLEvaluateMeasureEXM104() throws IOException { .execute(); List response = outParams.getParameter(); - Assert.assertTrue(!response.isEmpty()); + Assert.assertFalse(response.isEmpty()); Parameters.ParametersParameterComponent component = response.get(0); Assert.assertTrue(component.getResource() instanceof MeasureReport); MeasureReport report = (MeasureReport) component.getResource(); @@ -149,7 +149,7 @@ private int loadDataFromDirectory(String theDirectoryName) throws IOException { private Bundle loadBundle(String theLocation, FhirContext theCtx, IGenericClient theClient) throws IOException { String json = stringFromResource(theLocation); Bundle bundle = (Bundle) theCtx.newJsonParser().parseResource(json); - Bundle result = (Bundle) theClient.transaction().withBundle(bundle).execute(); + Bundle result = theClient.transaction().withBundle(bundle).execute(); return result; } 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 7b0548747a7..47834f50c0e 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java @@ -1,15 +1,11 @@ package ca.uhn.fhir.jpa.starter; import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.jpa.partition.SystemRequestDetails; -import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; 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.api.server.IBundleProvider; import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; - import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.client.ClientUpgradeRequest; import org.eclipse.jetty.websocket.client.WebSocketClient; diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/SocketImplementation.java b/src/test/java/ca/uhn/fhir/jpa/starter/SocketImplementation.java index 9318a0aee6b..7734a23c6f2 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/SocketImplementation.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/SocketImplementation.java @@ -15,10 +15,10 @@ public class SocketImplementation { private static final Logger ourLog = org.slf4j.LoggerFactory.getLogger(SocketImplementation.class); - private String myCriteria; + private final String myCriteria; protected String myError; protected boolean myGotBound; - private List myMessages = new ArrayList(); + private final List myMessages = new ArrayList(); protected int myPingCount; protected String mySubsId; private Session session; From 43d50a0c71bb5c3194c05a17db74386773c518f2 Mon Sep 17 00:00:00 2001 From: Jens Kristian Villadsen Date: Tue, 13 Sep 2022 18:54:14 +0200 Subject: [PATCH 108/200] Extract IG loading (#426) --- .../OnImplementationGuidesPresent.java | 18 +++++++++ .../starter/common/FhirServerConfigDstu2.java | 21 ++++++++-- .../jpa/starter/common/StarterJpaConfig.java | 40 ++++++++++++++----- .../fhir/jpa/starter/ExampleServerR4IT.java | 2 + 4 files changed, 66 insertions(+), 15 deletions(-) create mode 100644 src/main/java/ca/uhn/fhir/jpa/starter/annotations/OnImplementationGuidesPresent.java diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/annotations/OnImplementationGuidesPresent.java b/src/main/java/ca/uhn/fhir/jpa/starter/annotations/OnImplementationGuidesPresent.java new file mode 100644 index 00000000000..7b11df1618e --- /dev/null +++ b/src/main/java/ca/uhn/fhir/jpa/starter/annotations/OnImplementationGuidesPresent.java @@ -0,0 +1,18 @@ +package ca.uhn.fhir.jpa.starter.annotations; + +import ca.uhn.fhir.jpa.starter.AppProperties; +import org.springframework.boot.context.properties.bind.Binder; +import org.springframework.context.annotation.Condition; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.core.type.AnnotatedTypeMetadata; + +public class OnImplementationGuidesPresent implements Condition { + @Override + public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata) { + + AppProperties config = Binder.get(conditionContext.getEnvironment()).bind("hapi.fhir", AppProperties.class).orElse(null); + if (config == null) return false; + if (config.getImplementationGuides() == null) return false; + return !config.getImplementationGuides().isEmpty(); + } +} \ No newline at end of file diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigDstu2.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigDstu2.java index 151df82898f..cedf16997eb 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigDstu2.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigDstu2.java @@ -2,15 +2,28 @@ import ca.uhn.fhir.jpa.config.JpaDstu2Config; import ca.uhn.fhir.jpa.starter.annotations.OnDSTU2Condition; +import ca.uhn.fhir.jpa.term.TermCodeSystemStorageSvcImpl; +import ca.uhn.fhir.jpa.term.TermLoaderSvcImpl; +import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc; +import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc; +import ca.uhn.fhir.jpa.term.api.ITermLoaderSvc; +import org.springframework.context.annotation.Bean; 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 -}) +@Import({StarterJpaConfig.class, JpaDstu2Config.class}) public class FhirServerConfigDstu2 { + @Bean + public ITermLoaderSvc termLoaderService(ITermDeferredStorageSvc theDeferredStorageSvc, ITermCodeSystemStorageSvc theCodeSystemStorageSvc) { + return new TermLoaderSvcImpl(theDeferredStorageSvc, theCodeSystemStorageSvc); + } + + @Bean + public ITermCodeSystemStorageSvc termCodeSystemStorageSvc() { + return new TermCodeSystemStorageSvcImpl(); + } + } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/common/StarterJpaConfig.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/StarterJpaConfig.java index b2f108a084e..5f311ddf527 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/common/StarterJpaConfig.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/StarterJpaConfig.java @@ -1,7 +1,10 @@ package ca.uhn.fhir.jpa.starter.common; +import ca.uhn.fhir.batch2.coordinator.JobDefinitionRegistry; import ca.uhn.fhir.batch2.jobs.imprt.BulkDataImportProvider; +import ca.uhn.fhir.batch2.jobs.reindex.ReindexJobParameters; import ca.uhn.fhir.batch2.jobs.reindex.ReindexProvider; +import ca.uhn.fhir.batch2.model.JobDefinition; import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; @@ -35,6 +38,7 @@ 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.annotations.OnImplementationGuidesPresent; 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; @@ -62,6 +66,7 @@ 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.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.core.env.ConfigurableEnvironment; @@ -177,6 +182,27 @@ public LoggingInterceptor loggingInterceptor(AppProperties appProperties) { return loggingInterceptor; } + @Bean("packageInstaller") + @Primary + @Conditional(OnImplementationGuidesPresent.class) + public IPackageInstallerSvc packageInstaller(AppProperties appProperties, JobDefinition reindexJobParametersJobDefinition, JobDefinitionRegistry jobDefinitionRegistry, IPackageInstallerSvc packageInstallerSvc) + { + jobDefinitionRegistry.addJobDefinitionIfNotRegistered(reindexJobParametersJobDefinition); + + if (appProperties.getImplementationGuides() != null) { + Map guides = appProperties.getImplementationGuides(); + for (Map.Entry 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 packageInstallerSvc; + } + @Bean @Primary /* @@ -222,6 +248,8 @@ public CorsInterceptor corsInterceptor(AppProperties appProperties) { public RestfulServer restfulServer(IFhirSystemDao fhirSystemDao, AppProperties appProperties, DaoRegistry daoRegistry, Optional mdmProviderProvider, IJpaSystemProvider jpaSystemProvider, ResourceProviderFactory resourceProviderFactory, DaoConfig daoConfig, ISearchParamRegistry searchParamRegistry, IValidationSupport theValidationSupport, DatabaseBackedPagingProvider databaseBackedPagingProvider, LoggingInterceptor loggingInterceptor, Optional terminologyUploaderProvider, Optional subscriptionTriggeringProvider, Optional corsInterceptor, IInterceptorBroadcaster interceptorBroadcaster, Optional binaryAccessProvider, BinaryStorageInterceptor binaryStorageInterceptor, IValidatorModule validatorModule, Optional graphQLProvider, BulkDataExportProvider bulkDataExportProvider, BulkDataImportProvider bulkDataImportProvider, ValueSetOperationProvider theValueSetOperationProvider, ReindexProvider reindexProvider, PartitionManagementProvider partitionManagementProvider, Optional repositoryValidatingInterceptor, IPackageInstallerSvc packageInstallerSvc) { RestfulServer fhirServer = new RestfulServer(fhirSystemDao.getContext()); + + List supportedResourceTypes = appProperties.getSupported_resource_types(); if (!supportedResourceTypes.isEmpty()) { @@ -388,17 +416,7 @@ public RestfulServer restfulServer(IFhirSystemDao fhirSystemDao, AppProper repositoryValidatingInterceptor.ifPresent(fhirServer::registerInterceptor); - if (appProperties.getImplementationGuides() != null) { - Map guides = appProperties.getImplementationGuides(); - for (Map.Entry 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; } 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 47834f50c0e..27a8c21be44 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java @@ -37,6 +37,8 @@ "hapi.fhir.fhir_version=r4", "hapi.fhir.subscription.websocket_enabled=true", "hapi.fhir.mdm_enabled=true", + "hapi.fhir.implementationguides.dk-core.name=hl7.fhir.dk.core", + "hapi.fhir.implementationguides.dk-core.version=1.1.0", // Override is currently required when using MDM as the construction of the MDM // beans are ambiguous as they are constructed multiple places. This is evident // when running in a spring boot environment From 38f37e4db04d3790432f70b1ccf4fb1329c74b36 Mon Sep 17 00:00:00 2001 From: Patrick Werner Date: Thu, 15 Sep 2022 13:10:22 +0200 Subject: [PATCH 109/200] added back lost config entry: allowed_bundle_types (#427) * added back lost config entry * disabled entry --- src/main/resources/application.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index b1baff58e91..a651fa9831f 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -78,6 +78,10 @@ hapi: # supported_resource_types: # - Patient # - Observation + ################################################## + # Allowed Bundle Types for persistence (defaults are: COLLECTION,DOCUMENT,MESSAGE) + ################################################## + # allowed_bundle_types: COLLECTION,DOCUMENT,MESSAGE,TRANSACTION,TRANSACTIONRESPONSE,BATCH,BATCHRESPONSE,HISTORY,SEARCHSET # allow_cascading_deletes: true # allow_contains_searches: true # allow_external_references: true From 5c1f99bf188de68dcf3dfc7beb3f38b58be75995 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Sep 2022 13:25:21 +0000 Subject: [PATCH 110/200] Bump snakeyaml from 1.30 to 1.31 Bumps [snakeyaml](https://bitbucket.org/snakeyaml/snakeyaml) from 1.30 to 1.31. - [Commits](https://bitbucket.org/snakeyaml/snakeyaml/branches/compare/snakeyaml-1.31..snakeyaml-1.30) --- updated-dependencies: - dependency-name: org.yaml:snakeyaml dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3b2596f6f54..d1eb03b2c87 100644 --- a/pom.xml +++ b/pom.xml @@ -169,7 +169,7 @@ org.yaml snakeyaml - 1.30 + 1.31 From 3d03cd00c56f48a1115adce5b266fe9c3f157109 Mon Sep 17 00:00:00 2001 From: Jens Kristian Villadsen Date: Wed, 28 Sep 2022 22:51:57 +0200 Subject: [PATCH 111/200] fix: configuration of cors Refs: #430 --- .../jpa/starter/annotations/OnCorsPresent.java | 18 ++++++++++++++++++ .../jpa/starter/common/StarterJpaConfig.java | 3 ++- 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 src/main/java/ca/uhn/fhir/jpa/starter/annotations/OnCorsPresent.java diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/annotations/OnCorsPresent.java b/src/main/java/ca/uhn/fhir/jpa/starter/annotations/OnCorsPresent.java new file mode 100644 index 00000000000..e4a44d7c90f --- /dev/null +++ b/src/main/java/ca/uhn/fhir/jpa/starter/annotations/OnCorsPresent.java @@ -0,0 +1,18 @@ +package ca.uhn.fhir.jpa.starter.annotations; + +import ca.uhn.fhir.jpa.starter.AppProperties; +import org.springframework.boot.context.properties.bind.Binder; +import org.springframework.context.annotation.Condition; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.core.type.AnnotatedTypeMetadata; + +public class OnCorsPresent implements Condition { + @Override + public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata) { + + AppProperties config = Binder.get(conditionContext.getEnvironment()).bind("hapi.fhir", AppProperties.class).orElse(null); + if (config == null) return false; + if (config.getCors() == null) return false; + return true; + } +} \ No newline at end of file diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/common/StarterJpaConfig.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/StarterJpaConfig.java index 5f311ddf527..5ff3e699c8a 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/common/StarterJpaConfig.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/StarterJpaConfig.java @@ -38,6 +38,7 @@ 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.annotations.OnCorsPresent; import ca.uhn.fhir.jpa.starter.annotations.OnImplementationGuidesPresent; import ca.uhn.fhir.jpa.starter.common.validation.IRepositoryValidationInterceptorFactory; import ca.uhn.fhir.jpa.starter.util.EnvironmentHelper; @@ -214,7 +215,7 @@ IMdmLinkDao mdmLinkDao() { @Bean - @ConditionalOnProperty(prefix = "hapi.fhir", name = "cors") + @Conditional(OnCorsPresent.class) public CorsInterceptor corsInterceptor(AppProperties appProperties) { // Define your CORS configuration. This is an example // showing a typical setup. You should customize this From 531d2557820b5ecbe725a9142dd231413a6dd716 Mon Sep 17 00:00:00 2001 From: Thomas Papke Date: Sat, 1 Oct 2022 17:19:01 +0200 Subject: [PATCH 112/200] Proper close DB connection after dialect was resolved (#435) --- .../jpa/starter/util/JpaHibernatePropertiesProvider.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/util/JpaHibernatePropertiesProvider.java b/src/main/java/ca/uhn/fhir/jpa/starter/util/JpaHibernatePropertiesProvider.java index 83125a99e0f..696f34842e2 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/util/JpaHibernatePropertiesProvider.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/util/JpaHibernatePropertiesProvider.java @@ -8,6 +8,8 @@ import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import javax.sql.DataSource; + +import java.sql.Connection; import java.sql.SQLException; public class JpaHibernatePropertiesProvider extends HibernatePropertiesProvider { @@ -16,9 +18,9 @@ public class JpaHibernatePropertiesProvider extends HibernatePropertiesProvider public JpaHibernatePropertiesProvider(LocalContainerEntityManagerFactoryBean myEntityManagerFactory) { DataSource connection = myEntityManagerFactory.getDataSource(); - try { + try ( Connection dbConnection = connection.getConnection()){ dialect = new StandardDialectResolver() - .resolveDialect(new DatabaseMetaDataDialectResolutionInfoAdapter(connection.getConnection().getMetaData())); + .resolveDialect(new DatabaseMetaDataDialectResolutionInfoAdapter(dbConnection.getMetaData())); } catch (SQLException sqlException) { throw new ConfigurationException(sqlException.getMessage(), sqlException); } From 7a72c86a637548d3bb1fb7ee69fe7c5d7884c8c8 Mon Sep 17 00:00:00 2001 From: Patrick Werner Date: Thu, 6 Oct 2022 14:26:43 +0200 Subject: [PATCH 113/200] removed duplicated and wrong subscription code (#440) --- .../fhir/jpa/starter/common/FhirServerConfigCommon.java | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigCommon.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigCommon.java index 4dd4412c77a..60cf2d8c355 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigCommon.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigCommon.java @@ -202,15 +202,6 @@ public ModelConfig modelConfig(AppProperties appProperties, DaoConfig daoConfig) 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()); From a95c40dd05af40434a86a7b03c475e4142dd3743 Mon Sep 17 00:00:00 2001 From: patrick-werner Date: Thu, 6 Oct 2022 16:34:29 +0200 Subject: [PATCH 114/200] fixed SubscriptionDebugLogInterceptor adding logic --- .../java/ca/uhn/fhir/jpa/starter/common/StarterJpaConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/common/StarterJpaConfig.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/StarterJpaConfig.java index 5ff3e699c8a..19bba57fa5b 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/common/StarterJpaConfig.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/StarterJpaConfig.java @@ -346,7 +346,7 @@ public RestfulServer restfulServer(IFhirSystemDao fhirSystemDao, AppProper corsInterceptor.ifPresent(fhirServer::registerInterceptor); - if (appProperties.getSubscription() != null) { + if (daoConfig.getSupportedSubscriptionTypes().size() > 0) { // Subscription debug logging fhirServer.registerInterceptor(new SubscriptionDebugLogInterceptor()); } From d61c8a5f96aca475b6b5ccc8aedef64614127f7b Mon Sep 17 00:00:00 2001 From: rti Date: Tue, 11 Oct 2022 20:02:45 +0200 Subject: [PATCH 115/200] switch to postgres db (#444) --- docker-compose.yml | 19 ++++++++----------- src/main/resources/application.yaml | 11 +++++------ 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index fcf1f2b83ef..0fe0e457bec 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,18 +6,15 @@ services: restart: on-failure ports: - "8080:8080" - hapi-fhir-mysql: - image: mysql:latest - container_name: hapi-fhir-mysql - #https://dev.mysql.com/doc/refman/8.0/en/identifier-case-sensitivity.html - command: --lower_case_table_names=1 + hapi-fhir-postgres: + image: postgres:13-alpine + container_name: hapi-fhir-postgres restart: always environment: - MYSQL_DATABASE: 'hapi' - MYSQL_USER: 'admin' - MYSQL_PASSWORD: 'admin' - MYSQL_ROOT_PASSWORD: 'admin' + POSTGRES_DB: "hapi" + POSTGRES_USER: "admin" + POSTGRES_PASSWORD: "admin" volumes: - - hapi-fhir-mysql:/var/lib/mysql + - hapi-fhir-postgres:/var/lib/postgresql/data volumes: - hapi-fhir-mysql: + hapi-fhir-postgres: diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index a651fa9831f..2d84609cfed 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -14,11 +14,10 @@ spring: check-location: false baselineOnMigrate: true datasource: - url: 'jdbc:h2:file:./target/database/h2' - #url: jdbc:h2:mem:test_mem - username: sa - password: null - driverClassName: org.h2.Driver + url: jdbc:postgresql://hapi-fhir-postgres:5432/hapi + username: admin + password: admin + driverClassName: org.postgresql.Driver max-active: 15 # database connection pool size @@ -32,7 +31,7 @@ spring: #If using H2, then supply the value of ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect #If using postgres, then supply the value of ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgres94Dialect - hibernate.dialect: ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect + hibernate.dialect: ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgres94Dialect # hibernate.hbm2ddl.auto: update # hibernate.jdbc.batch_size: 20 # hibernate.cache.use_query_cache: false From a1e2ca3ae13d24e7f66b856ac251c39de02d3eb5 Mon Sep 17 00:00:00 2001 From: Jens Kristian Villadsen Date: Thu, 27 Oct 2022 08:21:08 +0200 Subject: [PATCH 116/200] revering to H2 (#449) --- src/main/resources/application.yaml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 2d84609cfed..a651fa9831f 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -14,10 +14,11 @@ spring: check-location: false baselineOnMigrate: true datasource: - url: jdbc:postgresql://hapi-fhir-postgres:5432/hapi - username: admin - password: admin - driverClassName: org.postgresql.Driver + url: 'jdbc:h2:file:./target/database/h2' + #url: jdbc:h2:mem:test_mem + username: sa + password: null + driverClassName: org.h2.Driver max-active: 15 # database connection pool size @@ -31,7 +32,7 @@ spring: #If using H2, then supply the value of ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect #If using postgres, then supply the value of ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgres94Dialect - hibernate.dialect: ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgres94Dialect + hibernate.dialect: ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect # hibernate.hbm2ddl.auto: update # hibernate.jdbc.batch_size: 20 # hibernate.cache.use_query_cache: false From f8d749ae28dbccc173a18a28269456e87b1ea6a4 Mon Sep 17 00:00:00 2001 From: jmarchionatto <60409882+jmarchionatto@users.noreply.github.com> Date: Sat, 12 Nov 2022 08:16:38 -0500 Subject: [PATCH 117/200] Tracking Hapi 6.2-PRE releases (#408) * Tracking branch for 6.1 pre-releases. * Update to 6.1.0-PRE3-SNAPSHOT * Adjust for hapi-fhir namespace changes and version * Adjust version to include new hapi-fhir HSearch fast path feature * Bump hapi PRE * Update to PRE16 * Adjust configuration class name to HAPI-FHIR HSearch namespace consolidation. Add commented out sample properties for lucene and elastic. Move batch.job.enabled property under spring: prefix to have it considered. * Adjust enumeration renaming * Repoint FHIR 6.2.0-PRE1-SNAPSHOT * Add missing Bean to starter configuration * Update to hapi-fhir 6.2.0-PRE2-SNAPSHOT * Update application-integrationtest.yaml and rename it as application.yaml to make test configuration independent of prod. * Update to hapi-fhir 6.2.0-PRE5-SNAPSHOT * Update dep * Bump version fix failures * Remove dead import * Fix up * remove batch refs Co-authored-by: Michael Buckley Co-authored-by: michaelabuckley Co-authored-by: juan.marchionatto Co-authored-by: Tadgh --- pom.xml | 22 +-- .../common/FhirServerConfigCommon.java | 1 - .../starter/common/FhirServerConfigDstu2.java | 5 +- .../starter/common/FhirServerConfigDstu3.java | 2 +- .../starter/common/FhirServerConfigR4.java | 2 +- .../jpa/starter/common/StarterJpaConfig.java | 24 ++- src/main/resources/application.yaml | 21 +- .../jpa/starter/ElasticsearchLastNR4IT.java | 1 - .../jpa/starter/ExampleServerDstu2IT.java | 1 - .../jpa/starter/ExampleServerDstu3IT.java | 1 - .../fhir/jpa/starter/ExampleServerR4IT.java | 1 - .../fhir/jpa/starter/ExampleServerR5IT.java | 3 +- .../jpa/starter/MultitenantServerR4IT.java | 1 - .../application-integrationtest.yaml | 108 ---------- src/test/resources/application.yaml | 186 ++++++++++++++++++ 15 files changed, 219 insertions(+), 160 deletions(-) delete mode 100644 src/test/resources/application-integrationtest.yaml create mode 100644 src/test/resources/application.yaml diff --git a/pom.xml b/pom.xml index d1eb03b2c87..7293b3046bc 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 - + - @@ -566,8 +552,8 @@ - - + + jetty diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigCommon.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigCommon.java index 60cf2d8c355..a80524eba70 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigCommon.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigCommon.java @@ -137,7 +137,6 @@ public DaoConfig daoConfig(AppProperties appProperties) { 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()); diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigDstu2.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigDstu2.java index cedf16997eb..baf5044ce12 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigDstu2.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigDstu2.java @@ -14,7 +14,10 @@ @Configuration @Conditional(OnDSTU2Condition.class) -@Import({StarterJpaConfig.class, JpaDstu2Config.class}) +@Import({ + JpaDstu2Config.class, + StarterJpaConfig.class +}) public class FhirServerConfigDstu2 { @Bean public ITermLoaderSvc termLoaderService(ITermDeferredStorageSvc theDeferredStorageSvc, ITermCodeSystemStorageSvc theCodeSystemStorageSvc) { diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigDstu3.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigDstu3.java index e0056bc1912..0e0c76358d1 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigDstu3.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigDstu3.java @@ -10,8 +10,8 @@ @Configuration @Conditional(OnDSTU3Condition.class) @Import({ - StarterJpaConfig.class, JpaDstu3Config.class, + StarterJpaConfig.class, StarterCqlDstu3Config.class, ElasticsearchConfig.class}) public class FhirServerConfigDstu3 { diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigR4.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigR4.java index 4c212ef2bce..6ee7a990846 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigR4.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigR4.java @@ -10,8 +10,8 @@ @Configuration @Conditional(OnR4Condition.class) @Import({ - StarterJpaConfig.class, JpaR4Config.class, + StarterJpaConfig.class, StarterCqlR4Config.class, ElasticsearchConfig.class }) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/common/StarterJpaConfig.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/StarterJpaConfig.java index 19bba57fa5b..ed150154685 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/common/StarterJpaConfig.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/StarterJpaConfig.java @@ -12,9 +12,9 @@ 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.config.ThreadPoolFactoryConfig; 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; @@ -26,6 +26,8 @@ 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.dao.tx.HapiTransactionService; +import ca.uhn.fhir.jpa.delete.ThreadSafeResourceDeleterSvc; import ca.uhn.fhir.jpa.graphql.GraphQLProvider; import ca.uhn.fhir.jpa.interceptor.CascadingDeleteInterceptor; import ca.uhn.fhir.jpa.interceptor.validation.RepositoryValidatingInterceptor; @@ -62,14 +64,10 @@ 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.Conditional; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; +import org.springframework.context.annotation.*; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.http.HttpHeaders; import org.springframework.orm.jpa.JpaTransactionManager; @@ -83,6 +81,9 @@ import static ca.uhn.fhir.jpa.starter.common.validation.IRepositoryValidationInterceptorFactory.ENABLE_REPOSITORY_VALIDATING_INTERCEPTOR; @Configuration +@Import( + ThreadPoolFactoryConfig.class +) public class StarterJpaConfig { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(StarterJpaConfig.class); @@ -103,10 +104,6 @@ public CachingValidationSupport validationSupportChain(JpaValidationSupportChain return ValidationSupportConfigUtil.newCachingValidationSupport(theJpaValidationSupportChain); } - @Bean - public BatchConfigurer batchConfigurer() { - return new NonPersistedBatchConfigurer(); - } @Autowired private ConfigurableEnvironment configurableEnvironment; @@ -123,6 +120,7 @@ public DatabaseBackedPagingProvider databaseBackedPagingProvider(AppProperties a return pagingProvider; } + @Bean public IResourceSupportedSvc resourceSupportedSvc(IDaoRegistry theDaoRegistry) { return new DaoRegistryResourceSupportedSvc(theDaoRegistry); @@ -150,7 +148,7 @@ public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource my @Bean @Primary - public JpaTransactionManager hapiTransactionManager(EntityManagerFactory entityManagerFactory) { + public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) { JpaTransactionManager retVal = new JpaTransactionManager(); retVal.setEntityManagerFactory(entityManagerFactory); return retVal; @@ -246,7 +244,7 @@ public CorsInterceptor corsInterceptor(AppProperties appProperties) { } @Bean - public RestfulServer restfulServer(IFhirSystemDao fhirSystemDao, AppProperties appProperties, DaoRegistry daoRegistry, Optional mdmProviderProvider, IJpaSystemProvider jpaSystemProvider, ResourceProviderFactory resourceProviderFactory, DaoConfig daoConfig, ISearchParamRegistry searchParamRegistry, IValidationSupport theValidationSupport, DatabaseBackedPagingProvider databaseBackedPagingProvider, LoggingInterceptor loggingInterceptor, Optional terminologyUploaderProvider, Optional subscriptionTriggeringProvider, Optional corsInterceptor, IInterceptorBroadcaster interceptorBroadcaster, Optional binaryAccessProvider, BinaryStorageInterceptor binaryStorageInterceptor, IValidatorModule validatorModule, Optional graphQLProvider, BulkDataExportProvider bulkDataExportProvider, BulkDataImportProvider bulkDataImportProvider, ValueSetOperationProvider theValueSetOperationProvider, ReindexProvider reindexProvider, PartitionManagementProvider partitionManagementProvider, Optional repositoryValidatingInterceptor, IPackageInstallerSvc packageInstallerSvc) { + public RestfulServer restfulServer(IFhirSystemDao fhirSystemDao, AppProperties appProperties, DaoRegistry daoRegistry, Optional mdmProviderProvider, IJpaSystemProvider jpaSystemProvider, ResourceProviderFactory resourceProviderFactory, DaoConfig daoConfig, ISearchParamRegistry searchParamRegistry, IValidationSupport theValidationSupport, DatabaseBackedPagingProvider databaseBackedPagingProvider, LoggingInterceptor loggingInterceptor, Optional terminologyUploaderProvider, Optional subscriptionTriggeringProvider, Optional corsInterceptor, IInterceptorBroadcaster interceptorBroadcaster, Optional binaryAccessProvider, BinaryStorageInterceptor binaryStorageInterceptor, IValidatorModule validatorModule, Optional graphQLProvider, BulkDataExportProvider bulkDataExportProvider, BulkDataImportProvider bulkDataImportProvider, ValueSetOperationProvider theValueSetOperationProvider, ReindexProvider reindexProvider, PartitionManagementProvider partitionManagementProvider, Optional repositoryValidatingInterceptor, IPackageInstallerSvc packageInstallerSvc, ThreadSafeResourceDeleterSvc theThreadSafeResourceDeleterSvc) { RestfulServer fhirServer = new RestfulServer(fhirSystemDao.getContext()); @@ -352,7 +350,7 @@ public RestfulServer restfulServer(IFhirSystemDao fhirSystemDao, AppProper } if (appProperties.getAllow_cascading_deletes()) { - CascadingDeleteInterceptor cascadingDeleteInterceptor = new CascadingDeleteInterceptor(fhirSystemDao.getContext(), daoRegistry, interceptorBroadcaster); + CascadingDeleteInterceptor cascadingDeleteInterceptor = new CascadingDeleteInterceptor(fhirSystemDao.getContext(), daoRegistry, interceptorBroadcaster, theThreadSafeResourceDeleterSvc); fhirServer.registerInterceptor(cascadingDeleteInterceptor); } diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index a651fa9831f..19a166667d5 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -39,16 +39,17 @@ spring: # hibernate.cache.use_second_level_cache: false # hibernate.cache.use_structured_entries: false # hibernate.cache.use_minimal_puts: false - ### These settings will enable fulltext search with lucene - hibernate.search.enabled: false - # hibernate.search.backend.type: lucene - # hibernate.search.backend.analysis.configurer: ca.uhn.fhir.jpa.search.HapiHSearchAnalysisConfigurers$HapiLuceneAnalysisConfigurer - # hibernate.search.backend.directory.type: local-filesystem - # hibernate.search.backend.directory.root: target/lucenefiles - # hibernate.search.backend.lucene_version: lucene_current - batch: - job: - enabled: false + ### These settings will enable fulltext search with lucene or elastic + hibernate.search.enabled: true + ### lucene parameters +# hibernate.search.backend.type: lucene +# hibernate.search.backend.analysis.configurer: ca.uhn.fhir.jpa.search.HapiHSearchAnalysisConfigurers$HapiLuceneAnalysisConfigurer +# hibernate.search.backend.directory.type: local-filesystem +# hibernate.search.backend.directory.root: target/lucenefiles +# hibernate.search.backend.lucene_version: lucene_current + ### elastic parameters ===> see also elasticsearch section below <=== + hibernate.search.backend.type: elasticsearch + hibernate.search.backend.analysis.configurer: ca.uhn.fhir.jpa.search.HapiHSearchAnalysisConfigurers$HapiElasticAnalysisConfigurer hapi: fhir: ### This enables the swagger-ui at /fhir/swagger-ui/index.html as well as the /fhir/api-docs (see https://hapifhir.io/hapi-fhir/docs/server_plain/openapi.html) diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/ElasticsearchLastNR4IT.java b/src/test/java/ca/uhn/fhir/jpa/starter/ElasticsearchLastNR4IT.java index 6ad49c61cf5..3a8404772f2 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/ElasticsearchLastNR4IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ElasticsearchLastNR4IT.java @@ -38,7 +38,6 @@ @ExtendWith(SpringExtension.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class, properties = { - "spring.batch.job.enabled=false", "spring.datasource.url=jdbc:h2:mem:dbr4", "hapi.fhir.fhir_version=r4", "hapi.fhir.lastn_enabled=true", 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 964fae24a6f..e20aff8ff8a 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu2IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu2IT.java @@ -18,7 +18,6 @@ @ExtendWith(SpringExtension.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class, properties = { - "spring.batch.job.enabled=false", "hapi.fhir.fhir_version=dstu2", "spring.datasource.url=jdbc:h2:mem:dbr2", }) 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 0dfcf83da59..926a5003841 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu3IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu3IT.java @@ -39,7 +39,6 @@ @ExtendWith(SpringExtension.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class, properties = { - "spring.batch.job.enabled=false", "spring.datasource.url=jdbc:h2:mem:dbr3", "hapi.fhir.cql_enabled=true", "hapi.fhir.fhir_version=dstu3", 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 27a8c21be44..278a5f0ccc8 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java @@ -31,7 +31,6 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class, properties = { - "spring.batch.job.enabled=false", "spring.datasource.url=jdbc:h2:mem:dbr4", "hapi.fhir.enable_repository_validating_interceptor=true", "hapi.fhir.fhir_version=r4", diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR5IT.java b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR5IT.java index 055d1ab25d7..ddbd6a77ac1 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR5IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR5IT.java @@ -34,7 +34,6 @@ @ExtendWith(SpringExtension.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class, properties = { - "spring.batch.job.enabled=false", "spring.datasource.url=jdbc:h2:mem:dbr5", "hapi.fhir.fhir_version=r5", "hapi.fhir.subscription.websocket_enabled=true", @@ -80,7 +79,7 @@ public void testWebsocketSubscription() throws Exception { subscription.getContained().add(topic); subscription.setTopic("#1"); subscription.setReason("Monitor new neonatal function (note, age will be determined by the monitor)"); - subscription.setStatus(Enumerations.SubscriptionState.REQUESTED); + subscription.setStatus(Enumerations.SubscriptionStatusCodes.REQUESTED); subscription.getChannelType() .setSystem("http://terminology.hl7.org/CodeSystem/subscription-channel-type") .setCode("websocket"); diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/MultitenantServerR4IT.java b/src/test/java/ca/uhn/fhir/jpa/starter/MultitenantServerR4IT.java index 29af5e7482a..9d340b12444 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/MultitenantServerR4IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/MultitenantServerR4IT.java @@ -20,7 +20,6 @@ @ExtendWith(SpringExtension.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class, properties = { - "spring.batch.job.enabled=false", "spring.datasource.url=jdbc:h2:mem:dbr4-mt", "hapi.fhir.fhir_version=r4", "hapi.fhir.subscription.websocket_enabled=true", diff --git a/src/test/resources/application-integrationtest.yaml b/src/test/resources/application-integrationtest.yaml deleted file mode 100644 index 3ab724038f8..00000000000 --- a/src/test/resources/application-integrationtest.yaml +++ /dev/null @@ -1,108 +0,0 @@ -spring: - datasource: - url: 'jdbc:h2:file:./target/database/h2' - username: sa - password: null - driverClassName: org.h2.Driver - max-active: 15 - profiles: - ### This is the FHIR version. Choose between, dstu2, dstu3, r4 or r5 - active: r4 - -hapi: - fhir: - #supported_resource_types: - # - Patient - # - Observation -# allow_cascading_deletes: true -# allow_contains_searches: true -# allow_external_references: true -# allow_multiple_delete: true -# allow_override_default_search_params: true -# allow_placeholder_references: true -# auto_create_placeholder_reference_targets: false -# cql_enabled: false -# default_encoding: JSON -# default_pretty_print: true -# default_page_size: 20 -# delete_expunge_enabled: true -# enable_index_missing_fields: false -# enforce_referential_integrity_on_delete: false -# enforce_referential_integrity_on_write: false -# etag_support_enabled: true -# expunge_enabled: true -# daoconfig_client_id_strategy: null -# fhirpath_interceptor_enabled: false -# filter_search_enabled: true -# graphql_enabled: true -# local_base_urls: -# - http://hapi.fhir.org/baseR4 - #partitioning: - # cross_partition_reference_mode: true - # multitenancy_enabled: true - # partitioning_include_in_search_hashes: true - #cors: - # allow_Credentials: true - # Supports multiple, comma separated allowed origin entries - # cors.allowed_origin=http://localhost:8080,https://localhost:8080,https://fhirtest.uhn.ca - # allowed_origin: - # - '*' - -# logger: -# error_format: 'ERROR - ${requestVerb} ${requestUrl}' -# format: >- -# Path[${servletPath}] Source[${requestHeader.x-forwarded-for}] -# Operation[${operationType} ${operationName} ${idOrResourceName}] -# UA[${requestHeader.user-agent}] Params[${requestParameters}] -# ResponseEncoding[${responseEncodingNoDefault}] -# log_exceptions: true -# name: fhirtest.access -# max_binary_size: 104857600 -# max_page_size: 200 -# retain_cached_searches_mins: 60 -# reuse_cached_search_results_millis: 60000 - tester: - - - id: home - name: Local Tester - server_address: 'http://localhost:8080/hapi-fhir-jpaserver/fhir' - refuse_to_fetch_third_party_urls: false - fhir_version: R4 - - - id: global - name: Global Tester - server_address: "http://hapi.fhir.org/baseR4" - refuse_to_fetch_third_party_urls: false - fhir_version: R4 -# validation: -# requests_enabled: true -# responses_enabled: true -# binary_storage_enabled: true -# bulk_export_enabled: true -# partitioning_multitenancy_enabled: -# subscription: -# resthook_enabled: false -# websocket_enabled: false -# email: -# from: some@test.com -# host: google.com -# port: -# username: -# password: -# auth: -# startTlsEnable: -# startTlsRequired: -# quitWait: - - -# -#elasticsearch: -# debug: -# pretty_print_json_log: false -# refresh_after_write: false -# enabled: false -# password: SomePassword -# required_index_status: YELLOW -# rest_url: 'http://localhost:9200' -# schema_management_strategy: CREATE -# username: SomeUsername diff --git a/src/test/resources/application.yaml b/src/test/resources/application.yaml new file mode 100644 index 00000000000..4794df57468 --- /dev/null +++ b/src/test/resources/application.yaml @@ -0,0 +1,186 @@ +spring: + main: + allow-circular-references: true + #allow-bean-definition-overriding: true + flyway: + enabled: false + check-location: false + baselineOnMigrate: true + datasource: + url: jdbc:h2:mem:test_mem + username: sa + password: null + driverClassName: org.h2.Driver + max-active: 15 + + # database connection pool size + hikari: + maximum-pool-size: 10 + jpa: + properties: + hibernate.format_sql: false + hibernate.show_sql: false + #Hibernate dialect is automatically detected except Postgres and H2. + #If using H2, then supply the value of ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect + #If using postgres, then supply the value of ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgres94Dialect + + hibernate.dialect: ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect + # hibernate.hbm2ddl.auto: update + # hibernate.jdbc.batch_size: 20 + # hibernate.cache.use_query_cache: false + # hibernate.cache.use_second_level_cache: false + # hibernate.cache.use_structured_entries: false + # hibernate.cache.use_minimal_puts: false + ### These settings will enable fulltext search with lucene or elastic + hibernate.search.enabled: false + ### lucene parameters + # hibernate.search.backend.type: lucene + # hibernate.search.backend.analysis.configurer: ca.uhn.fhir.jpa.search.HapiHSearchAnalysisConfigurers$HapiLuceneAnalysisConfigurer + # hibernate.search.backend.directory.type: local-filesystem + # hibernate.search.backend.directory.root: target/lucenefiles + # hibernate.search.backend.lucene_version: lucene_current + ### elastic parameters ===> see also elasticsearch section below <=== +# hibernate.search.backend.type: elasticsearch +# hibernate.search.backend.analysis.configurer: ca.uhn.fhir.jpa.search.HapiHSearchAnalysisConfigurers$HapiElasticAnalysisConfigurer + +hapi: + fhir: + ### This enables the swagger-ui at /fhir/swagger-ui/index.html as well as the /fhir/api-docs (see https://hapifhir.io/hapi-fhir/docs/server_plain/openapi.html) + openapi_enabled: true + ### This is the FHIR version. Choose between, DSTU2, DSTU3, R4 or R5 + fhir_version: R4 + ### enable to use the ApacheProxyAddressStrategy which uses X-Forwarded-* headers + ### to determine the FHIR server address + # use_apache_address_strategy: false + ### forces the use of the https:// protocol for the returned server address. + ### alternatively, it may be set using the X-Forwarded-Proto header. + # use_apache_address_strategy_https: false + ### enable to set the Server URL + # server_address: http://hapi.fhir.org/baseR4 + # defer_indexing_for_codesystems_of_size: 101 + # install_transitive_ig_dependencies: true + # implementationguides: + ### example from registry (packages.fhir.org) + # swiss: + # name: swiss.mednet.fhir + # version: 0.8.0 + # example not from registry + # ips_1_0_0: + # url: https://build.fhir.org/ig/HL7/fhir-ips/package.tgz + # name: hl7.fhir.uv.ips + # version: 1.0.0 + # supported_resource_types: + # - Patient + # - Observation + # allow_cascading_deletes: true + # allow_contains_searches: true + # allow_external_references: true + # allow_multiple_delete: true + # allow_override_default_search_params: true + # auto_create_placeholder_reference_targets: false + # cql_enabled: true + # default_encoding: JSON + # default_pretty_print: true + # default_page_size: 20 + # delete_expunge_enabled: true + # enable_repository_validating_interceptor: false + # enable_index_missing_fields: false + # enable_index_of_type: true + # enable_index_contained_resource: false + ### !!Extended Lucene/Elasticsearch Indexing is still a experimental feature, expect some features (e.g. _total=accurate) to not work as expected!! + ### more information here: https://hapifhir.io/hapi-fhir/docs/server_jpa/elastic.html + advanced_lucene_indexing: false + # enforce_referential_integrity_on_delete: false + # This is an experimental feature, and does not fully support _total and other FHIR features. + # enforce_referential_integrity_on_delete: false + # enforce_referential_integrity_on_write: false + # etag_support_enabled: true + # expunge_enabled: true + # daoconfig_client_id_strategy: null + # client_id_strategy: ALPHANUMERIC + # fhirpath_interceptor_enabled: false + # filter_search_enabled: true + # graphql_enabled: true + # narrative_enabled: true + # mdm_enabled: true + # local_base_urls: + # - https://hapi.fhir.org/baseR4 + mdm_enabled: false + # partitioning: + # allow_references_across_partitions: false + # partitioning_include_in_search_hashes: false + #cors: + # allow_Credentials: true + # These are allowed_origin patterns, see: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/cors/CorsConfiguration.html#setAllowedOriginPatterns-java.util.List- + # allowed_origin: + # - '*' + + # Search coordinator thread pool sizes + search-coord-core-pool-size: 20 + search-coord-max-pool-size: 100 + search-coord-queue-capacity: 200 + + # Threadpool size for BATCH'ed GETs in a bundle. + # bundle_batch_pool_size: 10 + # bundle_batch_pool_max_size: 50 + + # logger: + # error_format: 'ERROR - ${requestVerb} ${requestUrl}' + # format: >- + # Path[${servletPath}] Source[${requestHeader.x-forwarded-for}] + # Operation[${operationType} ${operationName} ${idOrResourceName}] + # UA[${requestHeader.user-agent}] Params[${requestParameters}] + # ResponseEncoding[${responseEncodingNoDefault}] + # log_exceptions: true + # name: fhirtest.access + # max_binary_size: 104857600 + # max_page_size: 200 + # retain_cached_searches_mins: 60 + # reuse_cached_search_results_millis: 60000 + tester: + home: + name: Local Tester + server_address: 'http://localhost:8080/fhir' + refuse_to_fetch_third_party_urls: false + fhir_version: R4 + global: + name: Global Tester + server_address: "http://hapi.fhir.org/baseR4" + refuse_to_fetch_third_party_urls: false + fhir_version: R4 +# validation: +# requests_enabled: true +# responses_enabled: true +# binary_storage_enabled: true +# bulk_export_enabled: true +# subscription: +# resthook_enabled: true +# websocket_enabled: false +# email: +# from: some@test.com +# host: google.com +# port: +# username: +# password: +# auth: +# startTlsEnable: +# startTlsRequired: +# quitWait: +# lastn_enabled: true +# store_resource_in_lucene_index_enabled: true +### This is configuration for normalized quantity serach level default is 0 +### 0: NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED - default +### 1: NORMALIZED_QUANTITY_STORAGE_SUPPORTED +### 2: NORMALIZED_QUANTITY_SEARCH_SUPPORTED +# normalized_quantity_search_level: 2 +#elasticsearch: +# debug: +# pretty_print_json_log: false +# refresh_after_write: false +# enabled: false +# password: SomePassword +# required_index_status: YELLOW +# rest_url: 'localhost:9200' +# protocol: 'http' +# schema_management_strategy: CREATE +# username: SomeUsername From 64aeb9b2fe250ce843e47a60763d5012dee19016 Mon Sep 17 00:00:00 2001 From: Kai-W Date: Sat, 12 Nov 2022 16:43:56 +0100 Subject: [PATCH 118/200] Added hibernate.dialect for Postgress to Readme (#451) --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index c104d2398ff..b30dd87de9a 100644 --- a/README.md +++ b/README.md @@ -218,6 +218,9 @@ spring: username: admin password: admin driverClassName: org.postgresql.Driver + jpa: + properties: + hibernate.dialect: ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgres94Dialect ``` Because the integration tests within the project rely on the default H2 database configuration, it is important to either explicity skip the integration tests during the build process, i.e., `mvn install -DskipTests`, or delete the tests altogether. Failure to skip or delete the tests once you've configured PostgreSQL for the datasource.driver, datasource.url, and hibernate.dialect as outlined above will result in build errors and compilation failure. @@ -235,6 +238,12 @@ spring: driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver ``` +Also, make sure you are not setting the Hibernate dialect explicitly, in other words remove any lines similar to: + +``` +hibernate.dialect: {some none Microsoft SQL dialect} +``` + Because the integration tests within the project rely on the default H2 database configuration, it is important to either explicity skip the integration tests during the build process, i.e., `mvn install -DskipTests`, or delete the tests altogether. Failure to skip or delete the tests once you've configured PostgreSQL for the datasource.driver, datasource.url, and hibernate.dialect as outlined above will result in build errors and compilation failure. From 2e1f5f52769cf5f38883f3ac27e20f1fe1bdd84a Mon Sep 17 00:00:00 2001 From: Jens Kristian Villadsen Date: Sat, 12 Nov 2022 18:39:38 +0100 Subject: [PATCH 119/200] fixes for support of R4B / 6.2.0 (#455) --- pom.xml | 11 +- .../starter/annotations/OnEitherVersion.java | 4 + .../starter/annotations/OnR4BCondition.java | 18 +++ .../starter/common/FhirServerConfigR4B.java | 17 +++ .../jpa/starter/common/StarterJpaConfig.java | 7 +- ...sitoryValidationInterceptorFactoryR4B.java | 77 +++++++++++ src/main/resources/application.yaml | 4 +- .../fhir/jpa/starter/ExampleServerR4BIT.java | 120 ++++++++++++++++++ 8 files changed, 252 insertions(+), 6 deletions(-) create mode 100644 src/main/java/ca/uhn/fhir/jpa/starter/annotations/OnR4BCondition.java create mode 100644 src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigR4B.java create mode 100644 src/main/java/ca/uhn/fhir/jpa/starter/common/validation/RepositoryValidationInterceptorFactoryR4B.java create mode 100644 src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4BIT.java diff --git a/pom.xml b/pom.xml index 7293b3046bc..35ae0de8f2a 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.2.0-PRE18-SNAPSHOT + 6.2.0 hapi-fhir-jpaserver-starter @@ -316,11 +316,18 @@ ${spring_boot_version} + + + io.micrometer + micrometer-core + 1.9.4 + + io.micrometer micrometer-registry-prometheus - 1.8.5 + 1.9.4 diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/annotations/OnEitherVersion.java b/src/main/java/ca/uhn/fhir/jpa/starter/annotations/OnEitherVersion.java index abf564f7fbf..463f77908cd 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/annotations/OnEitherVersion.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/annotations/OnEitherVersion.java @@ -28,6 +28,10 @@ static class OnDSTU3 { static class OnR4 { } + @Conditional(OnR4BCondition.class) + static class OnR4B { + } + @Conditional(OnR5Condition.class) static class OnR5 { } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/annotations/OnR4BCondition.java b/src/main/java/ca/uhn/fhir/jpa/starter/annotations/OnR4BCondition.java new file mode 100644 index 00000000000..e323a225132 --- /dev/null +++ b/src/main/java/ca/uhn/fhir/jpa/starter/annotations/OnR4BCondition.java @@ -0,0 +1,18 @@ +package ca.uhn.fhir.jpa.starter.annotations; + +import ca.uhn.fhir.context.FhirVersionEnum; +import org.springframework.context.annotation.Condition; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.core.type.AnnotatedTypeMetadata; + +public class OnR4BCondition implements Condition { + @Override + public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata) { + String version = conditionContext. + getEnvironment() + .getProperty("hapi.fhir.fhir_version") + .toUpperCase(); + + return FhirVersionEnum.R4B.name().equals(version); + } +} diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigR4B.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigR4B.java new file mode 100644 index 00000000000..d99d1021058 --- /dev/null +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigR4B.java @@ -0,0 +1,17 @@ +package ca.uhn.fhir.jpa.starter.common; + +import ca.uhn.fhir.jpa.config.r4b.JpaR4BConfig; +import ca.uhn.fhir.jpa.starter.annotations.OnR4BCondition; +import org.springframework.context.annotation.Conditional; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +@Configuration +@Conditional(OnR4BCondition.class) +@Import({ + JpaR4BConfig.class, + StarterJpaConfig.class, + ElasticsearchConfig.class +}) +public class FhirServerConfigR4B { +} diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/common/StarterJpaConfig.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/StarterJpaConfig.java index ed150154685..3f481bdadcf 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/common/StarterJpaConfig.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/StarterJpaConfig.java @@ -26,7 +26,6 @@ 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.dao.tx.HapiTransactionService; import ca.uhn.fhir.jpa.delete.ThreadSafeResourceDeleterSvc; import ca.uhn.fhir.jpa.graphql.GraphQLProvider; import ca.uhn.fhir.jpa.interceptor.CascadingDeleteInterceptor; @@ -423,7 +422,6 @@ public RestfulServer restfulServer(IFhirSystemDao fhirSystemDao, AppProper 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; @@ -437,6 +435,11 @@ public static IServerConformanceProvider calculateConformanceProvider(IFhirSy JpaCapabilityStatementProvider confProvider = new JpaCapabilityStatementProvider(fhirServer, fhirSystemDao, daoConfig, searchParamRegistry, theValidationSupport); confProvider.setImplementationDescription("HAPI FHIR R4 Server"); return confProvider; + } else if (fhirVersion == FhirVersionEnum.R4B) { + + JpaCapabilityStatementProvider confProvider = new JpaCapabilityStatementProvider(fhirServer, fhirSystemDao, daoConfig, searchParamRegistry, theValidationSupport); + confProvider.setImplementationDescription("HAPI FHIR R4B Server"); + return confProvider; } else if (fhirVersion == FhirVersionEnum.R5) { JpaCapabilityStatementProvider confProvider = new JpaCapabilityStatementProvider(fhirServer, fhirSystemDao, daoConfig, searchParamRegistry, theValidationSupport); diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/common/validation/RepositoryValidationInterceptorFactoryR4B.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/validation/RepositoryValidationInterceptorFactoryR4B.java new file mode 100644 index 00000000000..277af2fcd5a --- /dev/null +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/validation/RepositoryValidationInterceptorFactoryR4B.java @@ -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.OnR4BCondition; +import ca.uhn.fhir.rest.api.server.IBundleProvider; +import ca.uhn.fhir.rest.param.TokenParam; +import org.hl7.fhir.r4b.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. + *

+ * The enable_repository_validating_interceptor property must be enabled in application.yaml + * in order to use this class. + */ +@ConditionalOnProperty(prefix = "hapi.fhir", name = ENABLE_REPOSITORY_VALIDATING_INTERCEPTOR, havingValue = "true") +@Configuration +@Conditional(OnR4BCondition.class) +public class RepositoryValidationInterceptorFactoryR4B implements IRepositoryValidationInterceptorFactory { + + private final FhirContext fhirContext; + private final RepositoryValidatingRuleBuilder repositoryValidatingRuleBuilder; + private final IFhirResourceDao structureDefinitionResourceProvider; + + public RepositoryValidationInterceptorFactoryR4B(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> 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 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 rules = repositoryValidatingRuleBuilder.build(); + return new RepositoryValidatingInterceptor(fhirContext, rules); + } + +} diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 19a166667d5..67a652fdbc8 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -48,8 +48,8 @@ spring: # hibernate.search.backend.directory.root: target/lucenefiles # hibernate.search.backend.lucene_version: lucene_current ### elastic parameters ===> see also elasticsearch section below <=== - hibernate.search.backend.type: elasticsearch - hibernate.search.backend.analysis.configurer: ca.uhn.fhir.jpa.search.HapiHSearchAnalysisConfigurers$HapiElasticAnalysisConfigurer +# hibernate.search.backend.type: elasticsearch +# hibernate.search.backend.analysis.configurer: ca.uhn.fhir.jpa.search.HapiHSearchAnalysisConfigurers$HapiElasticAnalysisConfigurer hapi: fhir: ### This enables the swagger-ui at /fhir/swagger-ui/index.html as well as the /fhir/api-docs (see https://hapifhir.io/hapi-fhir/docs/server_plain/openapi.html) diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4BIT.java b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4BIT.java new file mode 100644 index 00000000000..00927220aee --- /dev/null +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4BIT.java @@ -0,0 +1,120 @@ +package ca.uhn.fhir.jpa.starter; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.rest.client.api.IGenericClient; +import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.r4b.model.Bundle; +import org.hl7.fhir.r4b.model.Patient; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.web.server.LocalServerPort; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class, properties = { + "spring.datasource.url=jdbc:h2:mem:dbr4b", + "hapi.fhir.enable_repository_validating_interceptor=true", + "hapi.fhir.fhir_version=r4b", + "hapi.fhir.subscription.websocket_enabled=false", + "hapi.fhir.mdm_enabled=false", + "hapi.fhir.implementationguides.dk-core.name=hl7.fhir.dk.core", + "hapi.fhir.implementationguides.dk-core.version=1.1.0", + // Override is currently required when using MDM as the construction of the MDM + // beans are ambiguous as they are constructed multiple places. This is evident + // when running in a spring boot environment + "spring.main.allow-bean-definition-overriding=true"}) +class ExampleServerR4BIT { + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExampleServerR4BIT.class); + private IGenericClient ourClient; + private FhirContext ourCtx; + + @LocalServerPort + private int port; + + @Test + @Order(0) + void testCreateAndRead() { + String methodName = "testCreateAndRead"; + ourLog.info("Entering " + methodName + "()..."); + + Patient pt = new Patient(); + pt.setActive(true); + pt.getBirthDateElement().setValueAsString("2020-01-01"); + pt.addIdentifier().setSystem("http://foo").setValue("12345"); + 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 testBatchPutWithIdenticalTags() { + String batchPuts = "{\n" + + "\t\"resourceType\": \"Bundle\",\n" + + "\t\"id\": \"patients\",\n" + + "\t\"type\": \"batch\",\n" + + "\t\"entry\": [\n" + + "\t\t{\n" + + "\t\t\t\"request\": {\n" + + "\t\t\t\t\"method\": \"PUT\",\n" + + "\t\t\t\t\"url\": \"Patient/pat-1\"\n" + + "\t\t\t},\n" + + "\t\t\t\"resource\": {\n" + + "\t\t\t\t\"resourceType\": \"Patient\",\n" + + "\t\t\t\t\"id\": \"pat-1\",\n" + + "\t\t\t\t\"meta\": {\n" + + "\t\t\t\t\t\"tag\": [\n" + + "\t\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\t\"system\": \"http://mysystem.org\",\n" + + "\t\t\t\t\t\t\t\"code\": \"value2\"\n" + + "\t\t\t\t\t\t}\n" + + "\t\t\t\t\t]\n" + + "\t\t\t\t}\n" + + "\t\t\t},\n" + + "\t\t\t\"fullUrl\": \"/Patient/pat-1\"\n" + + "\t\t},\n" + + "\t\t{\n" + + "\t\t\t\"request\": {\n" + + "\t\t\t\t\"method\": \"PUT\",\n" + + "\t\t\t\t\"url\": \"Patient/pat-2\"\n" + + "\t\t\t},\n" + + "\t\t\t\"resource\": {\n" + + "\t\t\t\t\"resourceType\": \"Patient\",\n" + + "\t\t\t\t\"id\": \"pat-2\",\n" + + "\t\t\t\t\"meta\": {\n" + + "\t\t\t\t\t\"tag\": [\n" + + "\t\t\t\t\t\t{\n" + + "\t\t\t\t\t\t\t\"system\": \"http://mysystem.org\",\n" + + "\t\t\t\t\t\t\t\"code\": \"value2\"\n" + + "\t\t\t\t\t\t}\n" + + "\t\t\t\t\t]\n" + + "\t\t\t\t}\n" + + "\t\t\t},\n" + + "\t\t\t\"fullUrl\": \"/Patient/pat-2\"\n" + + "\t\t}\n" + + "\t]\n" + + "}"; + Bundle bundle = FhirContext.forR4B().newJsonParser().parseResource(Bundle.class, batchPuts); + ourClient.transaction().withBundle(bundle).execute(); + } + + + + @BeforeEach + void beforeEach() { + + ourCtx = FhirContext.forR4B(); + ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER); + ourCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000); + String ourServerBase = "http://localhost:" + port + "/fhir/"; + ourClient = ourCtx.newRestfulGenericClient(ourServerBase); + + + } +} From 4fb2558d7ce123a4c8187bba5d4788525f8e4bf3 Mon Sep 17 00:00:00 2001 From: markiantorno Date: Thu, 17 Nov 2022 10:07:25 -0500 Subject: [PATCH 120/200] upping hapi version to 6.2.1 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 35ae0de8f2a..fa56b60d47c 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.2.0 + 6.2.1 hapi-fhir-jpaserver-starter From 05d76c78ad8508dfe9bfa7d6ddaa3968804dd631 Mon Sep 17 00:00:00 2001 From: chgl Date: Thu, 24 Nov 2022 23:09:24 +0100 Subject: [PATCH 121/200] Updated Helm chart to use image 6.2.1 and latest PostgreSQL (#458) --- charts/hapi-fhir-jpaserver/Chart.lock | 6 +++--- charts/hapi-fhir-jpaserver/Chart.yaml | 14 +++++++++----- charts/hapi-fhir-jpaserver/README.md | 6 +++--- .../hapi-fhir-jpaserver/templates/deployment.yaml | 2 +- .../templates/tests/test-endpoints.yaml | 6 +++--- charts/hapi-fhir-jpaserver/values.yaml | 4 ++-- 6 files changed, 21 insertions(+), 17 deletions(-) diff --git a/charts/hapi-fhir-jpaserver/Chart.lock b/charts/hapi-fhir-jpaserver/Chart.lock index 411bc270ef5..5c8ec4a2289 100644 --- a/charts/hapi-fhir-jpaserver/Chart.lock +++ b/charts/hapi-fhir-jpaserver/Chart.lock @@ -1,6 +1,6 @@ dependencies: - name: postgresql repository: https://charts.bitnami.com/bitnami - version: 11.8.1 -digest: sha256:671325f8b3d0b85183fa241190e72705fb124a41254a5db6445bcc105e1ca7ec -generated: "2022-08-25T02:14:58.3432514+02:00" + version: 12.1.2 +digest: sha256:525689611a29f90b0bc8cd674df5d97024c99eda8104216390f6747904fd0208 +generated: "2022-11-21T22:55:45.1699395+01:00" diff --git a/charts/hapi-fhir-jpaserver/Chart.yaml b/charts/hapi-fhir-jpaserver/Chart.yaml index 4632de77230..736c5d5e577 100644 --- a/charts/hapi-fhir-jpaserver/Chart.yaml +++ b/charts/hapi-fhir-jpaserver/Chart.yaml @@ -7,17 +7,21 @@ sources: - https://github.com/hapifhir/hapi-fhir-jpaserver-starter dependencies: - name: postgresql - version: 11.8.1 + version: 12.1.2 repository: https://charts.bitnami.com/bitnami condition: postgresql.enabled -appVersion: v6.x -version: 0.10.1 +appVersion: 6.2.1 +version: 0.11.0 annotations: artifacthub.io/license: Apache-2.0 artifacthub.io/changes: | # When using the list of objects option the valid supported kinds are # added, changed, deprecated, removed, fixed, and security. - kind: changed - description: updated image version to v6.1.0 + description: updated HAPI FHIR JPA Server app image version to v6.2.1 - kind: changed - description: added section on configuring the chart for distributed tracing to the README.md + description: | + Reduced `startupProbe.initialDelaySeconds` to a more realistic `30` from `60`. + This should allow the server to become ready quicker and recover from failures faster. + - kind: changed + description: "⚠️ BREAKING CHANGE: updated included postgresql chart to v12, which is based on PostgreSQL 15.1" diff --git a/charts/hapi-fhir-jpaserver/README.md b/charts/hapi-fhir-jpaserver/README.md index 798e001bb0f..5ab71f70228 100644 --- a/charts/hapi-fhir-jpaserver/README.md +++ b/charts/hapi-fhir-jpaserver/README.md @@ -1,6 +1,6 @@ # HAPI FHIR JPA Server Starter Helm Chart -![Version: 0.10.1](https://img.shields.io/badge/Version-0.10.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v6.x](https://img.shields.io/badge/AppVersion-v6.x-informational?style=flat-square) +![Version: 0.11.0](https://img.shields.io/badge/Version-0.11.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 6.2.1](https://img.shields.io/badge/AppVersion-6.2.1-informational?style=flat-square) This helm chart will help you install the HAPI FHIR JPA Server in a Kubernetes environment. @@ -32,7 +32,7 @@ helm install --render-subchart-notes hapi-fhir-jpaserver hapifhir/hapi-fhir-jpas | image.pullPolicy | string | `"IfNotPresent"` | image pullPolicy to use | | image.registry | string | `"docker.io"` | registry where the HAPI FHIR server image is hosted | | image.repository | string | `"hapiproject/hapi"` | the path inside the repository | -| image.tag | string | `"v6.1.0@sha256:253f87bb1f5b7627f8e25f76a4b0aa7a83f31968c6e111ad74d3cc4ad9ae812e"` | the image tag. As of v5.7.0, this is the `distroless` flavor by default, add `-tomcat` to use the Tomcat-based image. | +| image.tag | string | `"v6.2.1@sha256:8d1b4c1c8abd613f685267a3dda494d87aba4cff449eed39902a6ece2c086f3c"` | the image tag. As of v5.7.0, this is the `distroless` flavor by default, add `-tomcat` to use the Tomcat-based image. | | imagePullSecrets | list | `[]` | image pull secrets to use when pulling the image | | ingress.annotations | object | `{}` | provide any additional annotations which may be required. Evaluated as a template. | | ingress.enabled | bool | `false` | whether to create an Ingress to expose the FHIR server HTTP endpoint | @@ -80,7 +80,7 @@ helm install --render-subchart-notes hapi-fhir-jpaserver hapifhir/hapi-fhir-jpas | service.port | int | `8080` | port where the server will be exposed at | | service.type | string | `"ClusterIP"` | service type | | startupProbe.failureThreshold | int | `10` | | -| startupProbe.initialDelaySeconds | int | `60` | | +| startupProbe.initialDelaySeconds | int | `30` | | | startupProbe.periodSeconds | int | `30` | | | startupProbe.successThreshold | int | `1` | | | startupProbe.timeoutSeconds | int | `30` | | diff --git a/charts/hapi-fhir-jpaserver/templates/deployment.yaml b/charts/hapi-fhir-jpaserver/templates/deployment.yaml index fa88745b016..8f3c65e3137 100644 --- a/charts/hapi-fhir-jpaserver/templates/deployment.yaml +++ b/charts/hapi-fhir-jpaserver/templates/deployment.yaml @@ -30,7 +30,7 @@ spec: {{- toYaml .Values.podSecurityContext | nindent 8 }} initContainers: - name: wait-for-db-to-be-ready - image: docker.io/bitnami/postgresql:14.5.0@sha256:4355265e33e9c2a786aa145884d4b36ffd4c41c516b35d60df0b7495141ec738 + image: docker.io/bitnami/postgresql:15.1.0-debian-11-r0@sha256:27915588d5203a10a1c23624d9c81644437f33b7c224e25f79bcd9bd09bbb8e2 imagePullPolicy: IfNotPresent {{- with .Values.restrictedContainerSecurityContext }} securityContext: diff --git a/charts/hapi-fhir-jpaserver/templates/tests/test-endpoints.yaml b/charts/hapi-fhir-jpaserver/templates/tests/test-endpoints.yaml index 30aab5aba93..bf99dbce369 100644 --- a/charts/hapi-fhir-jpaserver/templates/tests/test-endpoints.yaml +++ b/charts/hapi-fhir-jpaserver/templates/tests/test-endpoints.yaml @@ -11,7 +11,7 @@ spec: restartPolicy: Never containers: - name: test-metadata-endpoint - image: docker.io/curlimages/curl:7.84.0@sha256:5a2a25d96aa941ea2fc47acc50122f7c3d007399a075df61a82d6d2c3a567a2b + image: docker.io/curlimages/curl:7.86.0@sha256:cfdeba7f88bb85f6c87f2ec9135115b523a1c24943976a61fbf59c4f2eafd78e command: ["curl", "--fail-with-body"] args: ["http://{{ include "hapi-fhir-jpaserver.fullname" . }}:{{ .Values.service.port }}/fhir/metadata?_summary=true"] {{- with .Values.restrictedContainerSecurityContext }} @@ -32,7 +32,7 @@ spec: exec: command: ["true"] - name: test-patient-endpoint - image: docker.io/curlimages/curl:7.84.0@sha256:5a2a25d96aa941ea2fc47acc50122f7c3d007399a075df61a82d6d2c3a567a2b + image: docker.io/curlimages/curl:7.86.0@sha256:cfdeba7f88bb85f6c87f2ec9135115b523a1c24943976a61fbf59c4f2eafd78e command: ["curl", "--fail-with-body"] args: ["http://{{ include "hapi-fhir-jpaserver.fullname" . }}:{{ .Values.service.port }}/fhir/Patient?_count=1&_summary=true"] {{- with .Values.restrictedContainerSecurityContext }} @@ -53,7 +53,7 @@ spec: exec: command: ["true"] - name: test-metrics-endpoint - image: docker.io/curlimages/curl:7.84.0@sha256:5a2a25d96aa941ea2fc47acc50122f7c3d007399a075df61a82d6d2c3a567a2b + image: docker.io/curlimages/curl:7.86.0@sha256:cfdeba7f88bb85f6c87f2ec9135115b523a1c24943976a61fbf59c4f2eafd78e command: ["curl", "--fail-with-body"] args: ["http://{{ include "hapi-fhir-jpaserver.fullname" . }}:{{ .Values.metrics.service.port }}/actuator/prometheus"] {{- with .Values.restrictedContainerSecurityContext }} diff --git a/charts/hapi-fhir-jpaserver/values.yaml b/charts/hapi-fhir-jpaserver/values.yaml index 8b05fbe1146..b55d4ea7ea0 100644 --- a/charts/hapi-fhir-jpaserver/values.yaml +++ b/charts/hapi-fhir-jpaserver/values.yaml @@ -7,7 +7,7 @@ image: # -- the path inside the repository repository: hapiproject/hapi # -- the image tag. As of v5.7.0, this is the `distroless` flavor by default, add `-tomcat` to use the Tomcat-based image. - tag: "v6.1.0@sha256:253f87bb1f5b7627f8e25f76a4b0aa7a83f31968c6e111ad74d3cc4ad9ae812e" + tag: "v6.2.1@sha256:8d1b4c1c8abd613f685267a3dda494d87aba4cff449eed39902a6ece2c086f3c" # -- image pullPolicy to use pullPolicy: IfNotPresent @@ -140,7 +140,7 @@ readinessProbe: startupProbe: failureThreshold: 10 - initialDelaySeconds: 60 + initialDelaySeconds: 30 periodSeconds: 30 successThreshold: 1 timeoutSeconds: 30 From 19c68e7cfcbb0eec064da8b4c9b5a43dc1879e83 Mon Sep 17 00:00:00 2001 From: Jens Kristian Villadsen Date: Sat, 3 Dec 2022 14:43:37 +0100 Subject: [PATCH 122/200] Bumped version (#462) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fa56b60d47c..700161b66d7 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.2.1 + 6.2.2 hapi-fhir-jpaserver-starter From 56318b4f5b42ebf20a71c5a441407781646f6cff Mon Sep 17 00:00:00 2001 From: Shubham Parikh Date: Mon, 8 Jun 2020 17:29:09 +0530 Subject: [PATCH 123/200] Added custom property file --- src/main/resources/hapi-r4.properties | 151 ++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 src/main/resources/hapi-r4.properties diff --git a/src/main/resources/hapi-r4.properties b/src/main/resources/hapi-r4.properties new file mode 100644 index 00000000000..44736f51f7f --- /dev/null +++ b/src/main/resources/hapi-r4.properties @@ -0,0 +1,151 @@ +# Adjust this to set the version of FHIR supported by this server. See +# FhirVersionEnum for a list of available constants. Example values include +# DSTU2, DSTU3, R4. +fhir_version=R4 + +# This is the address that the FHIR server will report as its own address. +# If this server will be deployed (for example) to an internet accessible +# server, put the DNS name of that server here. +# +# Note that this is also the address that the hapi-fhir-testpage-overlay +# (the web UI similar to the one at http://hapi.fhir.org) will use to +# connect internally to the FHIR server, so this also needs to be a name +# accessible from the server itself. +server_address=http://localhost:8080/hapi-fhir-jpaserver/fhir/ + +enable_index_missing_fields=false +auto_create_placeholder_reference_targets=false +enforce_referential_integrity_on_write=false +enforce_referential_integrity_on_delete=false +default_encoding=JSON +etag_support=ENABLED +reuse_cached_search_results_millis=60000 +retain_cached_searches_mins=60 +default_page_size=20 +max_page_size=200 +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 +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.h2.Driver +datasource.url=jdbc:h2:file:./target/database/h2 +datasource.username= +datasource.password= +server.name=Local Tester +server.id=home +test.port= + +################################################### +# Binary Storage (104857600 = 100mb) +################################################### +max_binary_size=104857600 + +################################################### +# Validation +################################################### +# Should all incoming requests be validated +validation.requests.enabled=false +# Should outgoing responses be validated +validation.responses.enabled=false + +################################################### +# Search Features +################################################### +filter_search.enabled=true +graphql.enabled=true +# See FhirPathFilterInterceptor +fhirpath_interceptor.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) +#supported_resource_types=Patient,Observation,Encounter + +################################################### +# Database Settings +################################################### +hibernate.dialect=org.hibernate.dialect.H2Dialect +hibernate.search.model_mapping=ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory +hibernate.format_sql=false +hibernate.show_sql=false +hibernate.hbm2ddl.auto=update +hibernate.jdbc.batch_size=20 +hibernate.cache.use_query_cache=false +hibernate.cache.use_second_level_cache=false +hibernate.cache.use_structured_entries=false +hibernate.cache.use_minimal_puts=false +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 + +################################################## +# ElasticSearch +# Note that using ElasticSearch is disabled by +# default and the server will use Lucene instead. +################################################## +elasticsearch.enabled=false +elasticsearch.rest_url=http://localhost:9200 +elasticsearch.username=SomeUsername +elasticsearch.password=SomePassword +elasticsearch.required_index_status=YELLOW +elasticsearch.schema_management_strategy=CREATE +# Immediately refresh indexes after every write. This is very bad for +# performance, but can be helpful for testing. +elasticsearch.debug.refresh_after_write=false +elasticsearch.debug.pretty_print_json_log=false + + +################################################## +# Binary Storage Operations +################################################## +binary_storage.enabled=true + +################################################## +# Bulk Data Specification +################################################## +bulk.export.enabled=true + +################################################## +# CORS Settings +################################################## +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=* + +################################################## +# Allowed Bundle Types for persistence (defaults are: COLLECTION,DOCUMENT,MESSAGE) +################################################## +#allowed_bundle_types=COLLECTION,DOCUMENT,MESSAGE,TRANSACTION,TRANSACTIONRESPONSE,BATCH,BATCHRESPONSE,HISTORY,SEARCHSET + +################################################## +# Subscriptions +################################################## + +# Enable REST Hook Subscription Channel +subscription.resthook.enabled=false + +# Enable Email Subscription Channel +subscription.email.enabled=false +email.enabled=false +email.from=some@test.com +email.host= +email.port=0 +email.username= +email.password= + +# Enable Websocket Subscription Channel +subscription.websocket.enabled=false From 01e019989a0255121a1f2c08d6d9bed5fca90150 Mon Sep 17 00:00:00 2001 From: Shubham Parikh Date: Wed, 10 Jun 2020 16:53:27 +0530 Subject: [PATCH 124/200] SAP-1596 Updated custom property file to read values from env --- src/main/resources/hapi-r4.properties | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/resources/hapi-r4.properties b/src/main/resources/hapi-r4.properties index 44736f51f7f..0817b4bd896 100644 --- a/src/main/resources/hapi-r4.properties +++ b/src/main/resources/hapi-r4.properties @@ -1,7 +1,7 @@ # Adjust this to set the version of FHIR supported by this server. See # FhirVersionEnum for a list of available constants. Example values include # DSTU2, DSTU3, R4. -fhir_version=R4 +fhir_version=${fhir_version} # This is the address that the FHIR server will report as its own address. # If this server will be deployed (for example) to an internet accessible @@ -11,7 +11,7 @@ fhir_version=R4 # (the web UI similar to the one at http://hapi.fhir.org) will use to # connect internally to the FHIR server, so this also needs to be a name # accessible from the server itself. -server_address=http://localhost:8080/hapi-fhir-jpaserver/fhir/ +server_address=${server_address} enable_index_missing_fields=false auto_create_placeholder_reference_targets=false @@ -35,10 +35,10 @@ 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.h2.Driver -datasource.url=jdbc:h2:file:./target/database/h2 -datasource.username= -datasource.password= +datasource.driver=${datasource.driver} +datasource.url=${datasource.url} +datasource.username=${datasource.username} +datasource.password=${datasource.password} server.name=Local Tester server.id=home test.port= @@ -75,7 +75,7 @@ fhirpath_interceptor.enabled=false ################################################### # Database Settings ################################################### -hibernate.dialect=org.hibernate.dialect.H2Dialect +hibernate.dialect=${hibernate.dialect} hibernate.search.model_mapping=ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory hibernate.format_sql=false hibernate.show_sql=false @@ -136,7 +136,7 @@ cors.allow_origin=* ################################################## # Enable REST Hook Subscription Channel -subscription.resthook.enabled=false +subscription.resthook.enabled=${subscription.resthook.enabled} # Enable Email Subscription Channel subscription.email.enabled=false @@ -148,4 +148,4 @@ email.username= email.password= # Enable Websocket Subscription Channel -subscription.websocket.enabled=false +subscription.websocket.enabled=${subscription.websocket.enabled} From 1246cf8d09c07a58b54770a4a60031ef89491285 Mon Sep 17 00:00:00 2001 From: Shubham Parikh Date: Thu, 30 Jul 2020 22:23:42 +0530 Subject: [PATCH 125/200] SAP-1687 Updated code to allow metadata request for server healthcheck (#7) Co-authored-by: Shubham Parikh --- .../CustomAuthorizationInterceptor.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java new file mode 100644 index 00000000000..09065039ec0 --- /dev/null +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java @@ -0,0 +1,47 @@ +package ca.uhn.fhir.jpa.starter; + +import java.util.List; + +import org.springframework.util.StringUtils; + +import ca.uhn.fhir.interceptor.api.Interceptor; +import ca.uhn.fhir.rest.api.RestOperationTypeEnum; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationInterceptor; +import ca.uhn.fhir.rest.server.interceptor.auth.IAuthRule; +import ca.uhn.fhir.rest.server.interceptor.auth.RuleBuilder; + +@Interceptor +public class CustomAuthorizationInterceptor extends AuthorizationInterceptor { + + private static final String HEADER_NAME = "x-api-key"; + private static final String VALIDATE_API_KEY = "VALIDATE_API_KEY"; + private static final String API_KEY = "API_KEY"; + + @Override + public List buildRuleList(RequestDetails theRequestDetails) { + try { + + boolean validateApiKey = Boolean.parseBoolean(System.getenv(VALIDATE_API_KEY)); + String token = theRequestDetails.getHeader(HEADER_NAME); + + if (!validateApiKey) { + return authorizeRequest(); + } else if (!StringUtils.isEmpty(token) && token.equals(System.getenv(API_KEY))) { + return authorizeRequest(); + } else { + return (theRequestDetails.getOperation().equals(RestOperationTypeEnum.METADATA.getCode())) ? authorizeRequest() : denyRequest(); + } + } catch (Exception e) { + return denyRequest(); + } + } + + private List denyRequest() { + return new RuleBuilder().denyAll().build(); + } + + private List authorizeRequest() { + return new RuleBuilder().allowAll().build(); + } +} From 98784eb971e1d83e4be69e802533100a493f2d85 Mon Sep 17 00:00:00 2001 From: Hank Wallace Date: Thu, 10 Sep 2020 07:13:37 -0400 Subject: [PATCH 126/200] DEV-769: Update from upstream branch (#9) * Adding environment variables to override hapi properties file settings * Only override existing hapi properties with env ones * Start Release branch for 5.1.0 * Add property for multitenancy * Get test passing * Add repo * Bump to current release * Rework the JPA server class a bit * Updating Dockerfile to use context code instead of re-cloning the hapi-fhir-jpaserver-starter project. Still re-builds the base hapi libraries, though. * Adding .dockerignore file to improve the efficiency of docker builds * Added support for configurable client ID strategy * Bump mysql-connector-java from 8.0.11 to 8.0.16 Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.11 to 8.0.16. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.11...8.0.16) Signed-off-by: dependabot[bot] * Resolve some compile errors * Fix EMPI rules json * Have EmpiSettings actually return an EmpiSettings bean * Update readme * Remove dead space * Update property * Update README to include Docker info * Prepare for release * Merge master * Removing commands from Dockerfile that clone and build the core hapi-fhir libraries since it is not required for building hapi-fhir-jpaserver-starter; it pulls the core libraries from the maven repo regardless. * Combining base Dockerfile with main dockerfile so that there is a single image related to hapi-jpaserver-starter * Update Dockerfile Added caching of maven dependency resolving * Fix #122 - EMPI error on startup when EMPI not enabled Co-authored-by: Peter Micuch Co-authored-by: Vladimir Nemergut Co-authored-by: jamesagnew Co-authored-by: Sean McIlvenna Co-authored-by: jvi Co-authored-by: Sean McIlvenna Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Tadgh Co-authored-by: Tadgh Co-authored-by: Ken Stevens Co-authored-by: Jens Kristian Villadsen <46567685+jvitrifork@users.noreply.github.com> --- README.md | 55 +++++++++++++++++++ src/main/resources/hapi-r4.properties | 2 +- .../jpa/starter/MultitenantServerR4IT.java | 1 - 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b30dd87de9a..3c2626026d2 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,57 @@ volumes: external: true ``` +## 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 entirely depends on your environment requirements. At least, the following 4 ways are supported: @@ -372,6 +423,10 @@ Set `hapi.fhir.cql_enabled=true` in the [application.yaml](https://github.com/ha Set `hapi.fhir.mdm_enabled=true` in the [application.yaml](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/application.yaml) file to enable MDM on this server. The MDM matching rules are configured in [mdm-rules.json](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/mdm-rules.json). The rules in this example file should be replaced with actual matching rules appropriate to your data. Note that MDM relies on subscriptions, so for MDM to work, subscriptions must be enabled. +## Enabling EMPI + +Set `empi.enabled=true` in the [hapi.properties](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/hapi.properties) file to enable EMPI on this server. The EMPI matching rules are configured in [empi-rules.json](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/empi-rules.json). The rules in this example file should be replaced with actual matching rules appropriate to your data. Note that EMPI relies on subscriptions, so for EMPI to work, subscriptions must be enabled. + ## Using Elasticsearch By default, the server will use embedded lucene indexes for terminology and fulltext indexing purposes. You can switch this to using lucene by editing the properties in [application.yaml](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/application.yaml) diff --git a/src/main/resources/hapi-r4.properties b/src/main/resources/hapi-r4.properties index 0817b4bd896..a06d07dbbc0 100644 --- a/src/main/resources/hapi-r4.properties +++ b/src/main/resources/hapi-r4.properties @@ -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) diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/MultitenantServerR4IT.java b/src/test/java/ca/uhn/fhir/jpa/starter/MultitenantServerR4IT.java index 9d340b12444..743c38e2b17 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/MultitenantServerR4IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/MultitenantServerR4IT.java @@ -68,7 +68,6 @@ public void testCreateAndReadInTenantA() { @Test public void testCreateAndReadInTenantB() { - // Create tenant A ourClientTenantInterceptor.setTenantId("DEFAULT"); ourClient From f70a02ada2b4614c3bdf546707f5ec9e295ffade Mon Sep 17 00:00:00 2001 From: Shubham Parikh Date: Fri, 5 Feb 2021 18:27:40 +0530 Subject: [PATCH 127/200] Updated docker compose file with required env variables --- docker-compose.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/docker-compose.yml b/docker-compose.yml index 0fe0e457bec..130f2584e85 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,7 @@ version: "3" services: hapi-fhir-jpaserver-start: + network_mode: host build: . container_name: hapi-fhir-jpaserver-start restart: on-failure From 8ef381e764c8e1373be6a3eb96bb6f8947d1c29e Mon Sep 17 00:00:00 2001 From: Shubham Parikh Date: Fri, 26 Jun 2020 16:41:00 +0530 Subject: [PATCH 128/200] SAP-1656 Added enabled parameter for webservlet to enable/disable webapp (#4) Co-authored-by: Shubham Parikh --- src/main/webapp/WEB-INF/web.xml | 55 +++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 src/main/webapp/WEB-INF/web.xml diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000000..a1da422e45e --- /dev/null +++ b/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,55 @@ + + + + org.springframework.web.context.ContextLoaderListener + + + contextClass + + ca.uhn.fhir.jpa.starter.ApplicationContext + + + + contextConfigLocation + + + + + + + spring + org.springframework.web.servlet.DispatcherServlet + + contextClass + org.springframework.web.context.support.AnnotationConfigWebApplicationContext + + + contextConfigLocation + + ca.uhn.fhir.jpa.starter.FhirTesterConfig + + + 2 + ${enable_web} + + + spring + / + + + + fhirServlet + ca.uhn.fhir.jpa.starter.JpaRestfulServer + 1 + + + fhirServlet + /fhir/* + + + From 99377d99181d0ddbd3c597616b6b254ca5e31d32 Mon Sep 17 00:00:00 2001 From: Hank Wallace Date: Thu, 10 Sep 2020 07:13:37 -0400 Subject: [PATCH 129/200] DEV-769: Update from upstream branch (#9) * Adding environment variables to override hapi properties file settings * Only override existing hapi properties with env ones * Start Release branch for 5.1.0 * Add property for multitenancy * Get test passing * Add repo * Bump to current release * Rework the JPA server class a bit * Updating Dockerfile to use context code instead of re-cloning the hapi-fhir-jpaserver-starter project. Still re-builds the base hapi libraries, though. * Adding .dockerignore file to improve the efficiency of docker builds * Added support for configurable client ID strategy * Bump mysql-connector-java from 8.0.11 to 8.0.16 Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.11 to 8.0.16. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.11...8.0.16) Signed-off-by: dependabot[bot] * Resolve some compile errors * Fix EMPI rules json * Have EmpiSettings actually return an EmpiSettings bean * Update readme * Remove dead space * Update property * Update README to include Docker info * Prepare for release * Merge master * Removing commands from Dockerfile that clone and build the core hapi-fhir libraries since it is not required for building hapi-fhir-jpaserver-starter; it pulls the core libraries from the maven repo regardless. * Combining base Dockerfile with main dockerfile so that there is a single image related to hapi-jpaserver-starter * Update Dockerfile Added caching of maven dependency resolving * Fix #122 - EMPI error on startup when EMPI not enabled Co-authored-by: Peter Micuch Co-authored-by: Vladimir Nemergut Co-authored-by: jamesagnew Co-authored-by: Sean McIlvenna Co-authored-by: jvi Co-authored-by: Sean McIlvenna Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Tadgh Co-authored-by: Tadgh Co-authored-by: Ken Stevens Co-authored-by: Jens Kristian Villadsen <46567685+jvitrifork@users.noreply.github.com> --- README.md | 55 ++ pom.xml | 2 +- .../java/ca/uhn/fhir/jpa/empi/EmpiConfig.java | 31 + .../fhir/jpa/starter/ApplicationContext.java | 49 ++ .../uhn/fhir/jpa/starter/HapiProperties.java | 535 ++++++++++++++++++ src/main/resources/empi-rules.json | 47 ++ src/main/resources/hapi.properties | 167 ++++++ .../fhir/jpa/starter/ExampleServerR4IT.java | 2 + 8 files changed, 887 insertions(+), 1 deletion(-) create mode 100644 src/main/java/ca/uhn/fhir/jpa/empi/EmpiConfig.java create mode 100644 src/main/java/ca/uhn/fhir/jpa/starter/ApplicationContext.java create mode 100644 src/main/java/ca/uhn/fhir/jpa/starter/HapiProperties.java create mode 100644 src/main/resources/empi-rules.json create mode 100644 src/main/resources/hapi.properties diff --git a/README.md b/README.md index 3c2626026d2..c10ac0625d7 100644 --- a/README.md +++ b/README.md @@ -133,6 +133,57 @@ volumes: external: true ``` +## 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 entirely depends on your environment requirements. At least, the following 4 ways are supported: @@ -427,6 +478,10 @@ Set `hapi.fhir.mdm_enabled=true` in the [application.yaml](https://github.com/ha Set `empi.enabled=true` in the [hapi.properties](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/hapi.properties) file to enable EMPI on this server. The EMPI matching rules are configured in [empi-rules.json](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/empi-rules.json). The rules in this example file should be replaced with actual matching rules appropriate to your data. Note that EMPI relies on subscriptions, so for EMPI to work, subscriptions must be enabled. +## Enabling EMPI + +Set `empi.enabled=true` in the [hapi.properties](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/hapi.properties) file to enable EMPI on this server. The EMPI matching rules are configured in [empi-rules.json](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/empi-rules.json). The rules in this example file should be replaced with actual matching rules appropriate to your data. Note that EMPI relies on subscriptions, so for EMPI to work, subscriptions must be enabled. + ## Using Elasticsearch By default, the server will use embedded lucene indexes for terminology and fulltext indexing purposes. You can switch this to using lucene by editing the properties in [application.yaml](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/application.yaml) diff --git a/pom.xml b/pom.xml index 700161b66d7..56c04ab78f8 100644 --- a/pom.xml +++ b/pom.xml @@ -40,7 +40,6 @@ - org.eclipse.jetty.websocket @@ -425,6 +424,7 @@ org.apache.maven.plugins maven-failsafe-plugin + 3.0.0-M5 true diff --git a/src/main/java/ca/uhn/fhir/jpa/empi/EmpiConfig.java b/src/main/java/ca/uhn/fhir/jpa/empi/EmpiConfig.java new file mode 100644 index 00000000000..b1f74a2d99c --- /dev/null +++ b/src/main/java/ca/uhn/fhir/jpa/empi/EmpiConfig.java @@ -0,0 +1,31 @@ +package ca.uhn.fhir.jpa.empi; + +import ca.uhn.fhir.empi.api.IEmpiSettings; +import ca.uhn.fhir.empi.rules.config.EmpiRuleValidator; +import ca.uhn.fhir.empi.rules.config.EmpiSettings; +import ca.uhn.fhir.jpa.starter.HapiProperties; +import com.google.common.base.Charsets; +import org.apache.commons.io.IOUtils; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.core.io.Resource; + +import java.io.IOException; + +/** + * TODO: Move this to package "ca.uhn.fhir.jpa.starter" in HAPI FHIR 5.2.0+. The lousy component scan + * in 5.1.0 picks this up even if EMPI is disabled currently. + */ +@Configuration +public class EmpiConfig { + + @Bean + IEmpiSettings empiSettings(EmpiRuleValidator theEmpiRuleValidator) throws IOException { + DefaultResourceLoader resourceLoader = new DefaultResourceLoader(); + Resource resource = resourceLoader.getResource("empi-rules.json"); + String json = IOUtils.toString(resource.getInputStream(), Charsets.UTF_8); + return new EmpiSettings(theEmpiRuleValidator).setEnabled(HapiProperties.getEmpiEnabled()).setScriptText(json); + } + +} diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/ApplicationContext.java b/src/main/java/ca/uhn/fhir/jpa/starter/ApplicationContext.java new file mode 100644 index 00000000000..0beda1db72a --- /dev/null +++ b/src/main/java/ca/uhn/fhir/jpa/starter/ApplicationContext.java @@ -0,0 +1,49 @@ +package ca.uhn.fhir.jpa.starter; + +import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.fhir.jpa.empi.EmpiConfig; +import ca.uhn.fhir.jpa.empi.config.EmpiConsumerConfig; +import ca.uhn.fhir.jpa.empi.config.EmpiSubmitterConfig; +import ca.uhn.fhir.jpa.subscription.channel.config.SubscriptionChannelConfig; +import ca.uhn.fhir.jpa.subscription.match.config.SubscriptionProcessorConfig; +import ca.uhn.fhir.jpa.subscription.match.config.WebsocketDispatcherConfig; +import ca.uhn.fhir.jpa.subscription.submit.config.SubscriptionSubmitterConfig; +import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; + +public class ApplicationContext extends AnnotationConfigWebApplicationContext { + + public ApplicationContext() { + FhirVersionEnum fhirVersion = HapiProperties.getFhirVersion(); + if (fhirVersion == FhirVersionEnum.DSTU2) { + register(FhirServerConfigDstu2.class, FhirServerConfigCommon.class); + } else if (fhirVersion == FhirVersionEnum.DSTU3) { + 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(); + } + + if (HapiProperties.getSubscriptionWebsocketEnabled()) { + register(WebsocketDispatcherConfig.class); + } + + if (HapiProperties.getSubscriptionEmailEnabled() + || HapiProperties.getSubscriptionRestHookEnabled() + || HapiProperties.getSubscriptionWebsocketEnabled()) { + register(SubscriptionSubmitterConfig.class); + register(SubscriptionProcessorConfig.class); + register(SubscriptionChannelConfig.class); + } + + if (HapiProperties.getEmpiEnabled()) { + register(EmpiSubmitterConfig.class); + register(EmpiConsumerConfig.class); + register(EmpiConfig.class); + } + + } + +} diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/HapiProperties.java b/src/main/java/ca/uhn/fhir/jpa/starter/HapiProperties.java new file mode 100644 index 00000000000..20628e53229 --- /dev/null +++ b/src/main/java/ca/uhn/fhir/jpa/starter/HapiProperties.java @@ -0,0 +1,535 @@ +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; +import com.google.common.annotations.VisibleForTesting; +import org.apache.commons.lang3.StringUtils; +import org.hibernate.search.elasticsearch.cfg.ElasticsearchIndexStatus; +import org.hibernate.search.elasticsearch.cfg.IndexSchemaManagementStrategy; +import org.jetbrains.annotations.NotNull; + +import javax.annotation.Nonnull; +import java.io.FileInputStream; +import java.io.InputStream; +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; +import static org.apache.commons.lang3.StringUtils.isNotBlank; +import static org.apache.commons.lang3.StringUtils.trim; + +public class HapiProperties { + static final String ENABLE_INDEX_MISSING_FIELDS = "enable_index_missing_fields"; + static final String AUTO_CREATE_PLACEHOLDER_REFERENCE_TARGETS = "auto_create_placeholder_reference_targets"; + static final String ENFORCE_REFERENTIAL_INTEGRITY_ON_WRITE = "enforce_referential_integrity_on_write"; + static final String ENFORCE_REFERENTIAL_INTEGRITY_ON_DELETE = "enforce_referential_integrity_on_delete"; + 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"; + static final String REUSE_CACHED_SEARCH_RESULTS_MILLIS = "reuse_cached_search_results_millis"; + static final String DATASOURCE_DRIVER = "datasource.driver"; + static final String DATASOURCE_MAX_POOL_SIZE = "datasource.max_pool_size"; + static final String DATASOURCE_PASSWORD = "datasource.password"; + static final String DATASOURCE_URL = "datasource.url"; + static final String DATASOURCE_USERNAME = "datasource.username"; + static final String DEFAULT_ENCODING = "default_encoding"; + static final String DEFAULT_PAGE_SIZE = "default_page_size"; + 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"; + static final String LOGGER_LOG_EXCEPTIONS = "logger.log_exceptions"; + static final String LOGGER_NAME = "logger.name"; + static final String MAX_FETCH_SIZE = "max_fetch_size"; + static final String MAX_PAGE_SIZE = "max_page_size"; + static final String SERVER_ADDRESS = "server_address"; + static final String SERVER_ID = "server.id"; + static final String SERVER_NAME = "server.name"; + static final String SUBSCRIPTION_EMAIL_ENABLED = "subscription.email.enabled"; + static final String SUBSCRIPTION_RESTHOOK_ENABLED = "subscription.resthook.enabled"; + static final String SUBSCRIPTION_WEBSOCKET_ENABLED = "subscription.websocket.enabled"; + static final String EMPI_ENABLED = "empi.enabled"; + static final String PARTITIONING_ENABLED = "partitioning.enabled"; + static final String PARTITIONING_CROSS_PARTITION_REFERENCE_MODE = "partitioning.cross_partition_reference_mode"; + static final String ALLOWED_BUNDLE_TYPES = "allowed_bundle_types"; + static final String TEST_PORT = "test.port"; + static final String TESTER_CONFIG_REFUSE_TO_FETCH_THIRD_PARTY_URLS = "tester.config.refuse_to_fetch_third_party_urls"; + static final String CORS_ENABLED = "cors.enabled"; + static final String CORS_ALLOWED_ORIGIN = "cors.allowed_origin"; + static final String CORS_ALLOW_CREDENTIALS = "cors.allowCredentials"; + 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"; + static final String VALIDATE_REQUESTS_ENABLED = "validation.requests.enabled"; + static final String VALIDATE_RESPONSES_ENABLED = "validation.responses.enabled"; + static final String FILTER_SEARCH_ENABLED = "filter_search.enabled"; + static final String GRAPHQL_ENABLED = "graphql.enabled"; + static final String BULK_EXPORT_ENABLED = "bulk.export.enabled"; + static final String EXPIRE_SEARCH_RESULTS_AFTER_MINS = "retain_cached_searches_mins"; + 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() { + return HapiProperties.getPropertyBoolean("elasticsearch.enabled", false); + } + + /* + * Force the configuration to be reloaded + */ + public static void forceReload() { + ourProperties = null; + getProperties(); + } + + /** + * This is mostly here for unit tests. Use the actual properties file + * to set values + */ + @VisibleForTesting + public static void setProperty(String theKey, String theValue) { + getProperties().setProperty(theKey, theValue); + } + + public static Properties getJpaProperties() { + Properties retVal = loadProperties(); + + if (isElasticSearchEnabled()) { + ElasticsearchHibernatePropertiesBuilder builder = new ElasticsearchHibernatePropertiesBuilder(); + builder.setRequiredIndexStatus(getPropertyEnum("elasticsearch.required_index_status", ElasticsearchIndexStatus.class, ElasticsearchIndexStatus.YELLOW)); + builder.setRestUrl(getProperty("elasticsearch.rest_url")); + builder.setUsername(getProperty("elasticsearch.username")); + builder.setPassword(getProperty("elasticsearch.password")); + builder.setIndexSchemaManagementStrategy(getPropertyEnum("elasticsearch.schema_management_strategy", IndexSchemaManagementStrategy.class, IndexSchemaManagementStrategy.CREATE)); + builder.setDebugRefreshAfterWrite(getPropertyBoolean("elasticsearch.debug.refresh_after_write", false)); + builder.setDebugPrettyPrintJsonLog(getPropertyBoolean("elasticsearch.debug.pretty_print_json_log", false)); + builder.apply(retVal); + } + + return retVal; + } + + private static Properties getProperties() { + if (ourProperties == null) { + Properties properties = loadProperties(); + HapiProperties.ourProperties = properties; + } + + return ourProperties; + } + + @NotNull + private static Properties loadProperties() { + // Load the configurable properties file + Properties properties; + try (InputStream in = HapiProperties.class.getClassLoader().getResourceAsStream(HAPI_PROPERTIES)) { + properties = new Properties(); + properties.load(in); + } catch (Exception e) { + throw new ConfigurationException("Could not load HAPI properties", e); + } + + Properties overrideProps = loadOverrideProperties(); + 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; + } + + /** + * 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) { + try { + Properties props = new Properties(); + props.load(new FileInputStream(confFile)); + return props; + } catch (Exception e) { + throw new ConfigurationException("Could not load HAPI properties file: " + confFile, e); + } + } + + return null; + } + + private static String getProperty(String propertyName) { + String env = "HAPI_" + propertyName.toUpperCase(Locale.US); + env = env.replace(".", "_"); + env = env.replace("-", "_"); + + String propertyValue = System.getenv(env); + if (propertyValue != null) { + return propertyValue; + } + + Properties properties = HapiProperties.getProperties(); + if (properties != null) { + propertyValue = properties.getProperty(propertyName); + } + + return propertyValue; + } + + private static String getProperty(String propertyName, String defaultValue) { + String value = getProperty(propertyName); + + if (value != null && value.length() > 0) { + return value; + } + + return defaultValue; + } + + private static Boolean getBooleanProperty(String propertyName, Boolean defaultValue) { + String value = HapiProperties.getProperty(propertyName); + + if (value == null || value.length() == 0) { + return defaultValue; + } + + 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); + + if (value == null || value.length() == 0) { + return defaultValue; + } + + return Integer.parseInt(value); + } + + public static FhirVersionEnum getFhirVersion() { + String fhirVersionString = HapiProperties.getProperty(FHIR_VERSION); + + if (fhirVersionString != null && fhirVersionString.length() > 0) { + return FhirVersionEnum.valueOf(fhirVersionString); + } + + return FhirVersionEnum.DSTU3; + } + + public static boolean isBinaryStorageEnabled() { + return HapiProperties.getBooleanProperty(BINARY_STORAGE_ENABLED, true); + } + + public static ETagSupportEnum getEtagSupport() { + String etagSupportString = HapiProperties.getProperty(ETAG_SUPPORT); + + if (etagSupportString != null && etagSupportString.length() > 0) { + return ETagSupportEnum.valueOf(etagSupportString); + } + + 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); + + if (defaultEncodingString != null && defaultEncodingString.length() > 0) { + return EncodingEnum.valueOf(defaultEncodingString); + } + + return EncodingEnum.JSON; + } + + public static Boolean getDefaultPrettyPrint() { + return HapiProperties.getBooleanProperty(DEFAULT_PRETTY_PRINT, true); + } + + public static String getServerAddress() { + return HapiProperties.getProperty(SERVER_ADDRESS); + } + + public static Integer getDefaultPageSize() { + return HapiProperties.getIntegerProperty(DEFAULT_PAGE_SIZE, 20); + } + + public static Integer getMaximumPageSize() { + return HapiProperties.getIntegerProperty(MAX_PAGE_SIZE, 200); + } + + public static Integer getMaximumFetchSize() { + return HapiProperties.getIntegerProperty(MAX_FETCH_SIZE, Integer.MAX_VALUE); + } + + public static String getLoggerName() { + return HapiProperties.getProperty(LOGGER_NAME, "fhirtest.access"); + } + + public static String getLoggerFormat() { + return HapiProperties.getProperty(LOGGER_FORMAT, "Path[${servletPath}] Source[${requestHeader.x-forwarded-for}] Operation[${operationType} ${operationName} ${idOrResourceName}] UA[${requestHeader.user-agent}] Params[${requestParameters}] ResponseEncoding[${responseEncodingNoDefault}]"); + } + + public static String getLoggerErrorFormat() { + return HapiProperties.getProperty(LOGGER_ERROR_FORMAT, "ERROR - ${requestVerb} ${requestUrl}"); + } + + public static Boolean getLoggerLogExceptions() { + return HapiProperties.getBooleanProperty(LOGGER_LOG_EXCEPTIONS, true); + } + + public static String getDataSourceDriver() { + return HapiProperties.getProperty(DATASOURCE_DRIVER, "org.apache.derby.jdbc.EmbeddedDriver"); + } + + public static Integer getDataSourceMaxPoolSize() { + return HapiProperties.getIntegerProperty(DATASOURCE_MAX_POOL_SIZE, 10); + } + + public static String getDataSourceUrl() { + return HapiProperties.getProperty(DATASOURCE_URL, "jdbc:derby:directory:target/jpaserver_derby_files;create=true"); + } + + public static String getDataSourceUsername() { + return HapiProperties.getProperty(DATASOURCE_USERNAME); + } + + public static String getDataSourcePassword() { + return HapiProperties.getProperty(DATASOURCE_PASSWORD); + } + + public static Boolean getAllowMultipleDelete() { + 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); + } + + public static Boolean getExpungeEnabled() { + return HapiProperties.getBooleanProperty("expunge_enabled", true); + } + + public static Boolean getTesterConfigRefustToFetchThirdPartyUrls() { + return HapiProperties.getBooleanProperty(TESTER_CONFIG_REFUSE_TO_FETCH_THIRD_PARTY_URLS, false); + } + + public static Boolean getCorsEnabled() { + return HapiProperties.getBooleanProperty(CORS_ENABLED, true); + } + + public static String getCorsAllowedOrigin() { + return HapiProperties.getProperty(CORS_ALLOWED_ORIGIN, "*"); + } + + public static String getAllowedBundleTypes() { + return HapiProperties.getProperty(ALLOWED_BUNDLE_TYPES, ""); + } + + @Nonnull + public static Set getSupportedResourceTypes() { + String[] types = defaultString(getProperty("supported_resource_types")).split(","); + return Arrays.stream(types) + .map(StringUtils::trim) + .filter(StringUtils::isNotBlank) + .collect(Collectors.toSet()); + } + + public static String getServerName() { + return HapiProperties.getProperty(SERVER_NAME, "Local Tester"); + } + + public static String getServerId() { + return HapiProperties.getProperty(SERVER_ID, "home"); + } + + public static Boolean getAllowPlaceholderReferences() { + return HapiProperties.getBooleanProperty(ALLOW_PLACEHOLDER_REFERENCES, true); + } + + public static Boolean getSubscriptionEmailEnabled() { + return HapiProperties.getBooleanProperty(SUBSCRIPTION_EMAIL_ENABLED, false); + } + + public static Boolean getSubscriptionRestHookEnabled() { + return HapiProperties.getBooleanProperty(SUBSCRIPTION_RESTHOOK_ENABLED, false); + } + + public static Boolean getSubscriptionWebsocketEnabled() { + return HapiProperties.getBooleanProperty(SUBSCRIPTION_WEBSOCKET_ENABLED, false); + } + + public static Boolean getEmpiEnabled() { + return HapiProperties.getBooleanProperty(EMPI_ENABLED, false); + } + + public static Boolean getPartitioningEnabled() { + return HapiProperties.getBooleanProperty(PARTITIONING_ENABLED, false); + } + + + public static String getPartitioningCrossPartitionReferenceMode() { + return HapiProperties.getProperty(PARTITIONING_CROSS_PARTITION_REFERENCE_MODE, "NOT_ALLOWED"); + } + + public static Boolean getIncludePartitionInSearchHashes() { + return HapiProperties.getBooleanProperty(PARTITIONING_INCLUDE_PARTITION_IN_SEARCH_HASHES, true); + } + + public static Boolean getAllowContainsSearches() { + return HapiProperties.getBooleanProperty(ALLOW_CONTAINS_SEARCHES, true); + } + + public static Boolean getAllowOverrideDefaultSearchParams() { + return HapiProperties.getBooleanProperty(ALLOW_OVERRIDE_DEFAULT_SEARCH_PARAMS, true); + } + + public static String getEmailFrom() { + return HapiProperties.getProperty(EMAIL_FROM, "some@test.com"); + } + + public static Boolean getEmailEnabled() { + return HapiProperties.getBooleanProperty("email.enabled", false); + } + + public static String getEmailHost() { + return HapiProperties.getProperty("email.host"); + } + + public static Integer getEmailPort() { + return HapiProperties.getIntegerProperty("email.port", 0); + } + + public static String getEmailUsername() { + return HapiProperties.getProperty("email.username"); + } + + public static String getEmailPassword() { + return HapiProperties.getProperty("email.password"); + } + + // Defaults from https://javaee.github.io/javamail/docs/api/com/sun/mail/smtp/package-summary.html + public static Boolean getEmailAuth() { + return HapiProperties.getBooleanProperty("email.auth", false); + } + + public static Boolean getEmailStartTlsEnable() { + return HapiProperties.getBooleanProperty("email.starttls.enable", false); + } + + public static Boolean getEmailStartTlsRequired() { + return HapiProperties.getBooleanProperty("email.starttls.required", false); + } + + public static Boolean getEmailQuitWait() { + return HapiProperties.getBooleanProperty("email.quitwait", true); + } + + public static Long getReuseCachedSearchResultsMillis() { + String value = HapiProperties.getProperty(REUSE_CACHED_SEARCH_RESULTS_MILLIS, "60000"); + return Long.valueOf(value); + } + + public static Long getExpireSearchResultsAfterMins() { + String value = HapiProperties.getProperty(EXPIRE_SEARCH_RESULTS_AFTER_MINS, "60"); + return Long.valueOf(value); + } + + public static Boolean getCorsAllowedCredentials() { + return HapiProperties.getBooleanProperty(CORS_ALLOW_CREDENTIALS, false); + } + + public static boolean getValidateRequestsEnabled() { + return HapiProperties.getBooleanProperty(VALIDATE_REQUESTS_ENABLED, false); + } + + public static boolean getValidateResponsesEnabled() { + 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); + } + + public static boolean getEnforceReferentialIntegrityOnDelete() { + return HapiProperties.getBooleanProperty(ENFORCE_REFERENTIAL_INTEGRITY_ON_DELETE, true); + } + + public static boolean getEnforceReferentialIntegrityOnWrite() { + return HapiProperties.getBooleanProperty(ENFORCE_REFERENTIAL_INTEGRITY_ON_WRITE, true); + } + + public static boolean getAutoCreatePlaceholderReferenceTargets() { + return HapiProperties.getBooleanProperty(AUTO_CREATE_PLACEHOLDER_REFERENCE_TARGETS, true); + } + + public static boolean getEnableIndexMissingFields() { + return HapiProperties.getBooleanProperty(ENABLE_INDEX_MISSING_FIELDS, false); + } + + public static Integer getMaxBinarySize() { + return getIntegerProperty(MAX_BINARY_SIZE, null); + } + + private static boolean getPropertyBoolean(String thePropertyName, boolean theDefaultValue) { + String value = getProperty(thePropertyName, Boolean.toString(theDefaultValue)); + return Boolean.parseBoolean(value); + } + + private static T getPropertyEnum(String thePropertyName, Class theEnumType, T theDefaultValue) { + String value = getProperty(thePropertyName, theDefaultValue.name()); + return (T) Enum.valueOf(theEnumType, value); + } + + public static boolean getBulkExportEnabled() { + return HapiProperties.getBooleanProperty(BULK_EXPORT_ENABLED, true); + } + + public static boolean isFhirPathFilterInterceptorEnabled() { + return HapiProperties.getBooleanProperty("fhirpath_interceptor.enabled", false); + } + + public static boolean getPartitioningMultitenancyEnabled() { + return HapiProperties.getBooleanProperty(PARTITIONING_MULTITENANCY_ENABLED, false); + } + + +} + diff --git a/src/main/resources/empi-rules.json b/src/main/resources/empi-rules.json new file mode 100644 index 00000000000..9f2706abaec --- /dev/null +++ b/src/main/resources/empi-rules.json @@ -0,0 +1,47 @@ +{ + "version": "1", + "candidateSearchParams": [ + { + "resourceType": "Patient", + "searchParams": ["birthdate"] + }, + { + "resourceType": "*", + "searchParams": ["identifier"] + }, + { + "resourceType": "Patient", + "searchParams": ["general-practitioner"] + } + ], + "candidateFilterSearchParams": [ + { + "resourceType": "*", + "searchParam": "active", + "fixedValue": "true" + } + ], + "matchFields": [ + { + "name": "cosine-given-name", + "resourceType": "*", + "resourcePath": "name.given", + "metric": "COSINE", + "matchThreshold": 0.8, + "exact": true + }, + { + "name": "jaro-last-name", + "resourceType": "*", + "resourcePath": "name.family", + "metric": "JARO_WINKLER", + "matchThreshold": 0.8, + "exact": true + } + ], + "matchResultMap": { + "cosine-given-name" : "POSSIBLE_MATCH", + "cosine-given-name,jaro-last-name" : "MATCH" + }, + "eidSystem": "http://company.io/fhir/NamingSystem/custom-eid-system" +} diff --git a/src/main/resources/hapi.properties b/src/main/resources/hapi.properties new file mode 100644 index 00000000000..b7ef0f8ce21 --- /dev/null +++ b/src/main/resources/hapi.properties @@ -0,0 +1,167 @@ +# Adjust this to set the version of FHIR supported by this server. See +# FhirVersionEnum for a list of available constants. Example values include +# DSTU2, DSTU3, R4. +fhir_version=R4 + +# This is the address that the FHIR server will report as its own address. +# If this server will be deployed (for example) to an internet accessible +# server, put the DNS name of that server here. +# +# Note that this is also the address that the hapi-fhir-testpage-overlay +# (the web UI similar to the one at http://hapi.fhir.org) will use to +# connect internally to the FHIR server, so this also needs to be a name +# accessible from the server itself. +server_address=http://localhost:8080/hapi-fhir-jpaserver/fhir/ + +enable_index_missing_fields=false +auto_create_placeholder_reference_targets=false +enforce_referential_integrity_on_write=false +enforce_referential_integrity_on_delete=false +default_encoding=JSON +etag_support=ENABLED +reuse_cached_search_results_millis=60000 +retain_cached_searches_mins=60 +default_page_size=20 +max_page_size=200 +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 +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.h2.Driver +datasource.url=jdbc:h2:file:./target/database/h2 +datasource.username= +datasource.password= +server.name=Local Tester +server.id=home +test.port= + +################################################### +# Binary Storage (104857600 = 100mb) +################################################### +max_binary_size=104857600 + +################################################### +# Validation +################################################### +# Should all incoming requests be validated +validation.requests.enabled=false +# Should outgoing responses be validated +validation.responses.enabled=false + +################################################### +# Search Features +################################################### +filter_search.enabled=true +graphql.enabled=true +# See FhirPathFilterInterceptor +fhirpath_interceptor.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) +#supported_resource_types=Patient,Observation,Encounter + +################################################### +# Database Settings +################################################### +hibernate.dialect=org.hibernate.dialect.H2Dialect +hibernate.search.model_mapping=ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory +hibernate.format_sql=false +hibernate.show_sql=false +hibernate.hbm2ddl.auto=update +hibernate.jdbc.batch_size=20 +hibernate.cache.use_query_cache=false +hibernate.cache.use_second_level_cache=false +hibernate.cache.use_structured_entries=false +hibernate.cache.use_minimal_puts=false +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 + +################################################## +# ElasticSearch +# Note that using ElasticSearch is disabled by +# default and the server will use Lucene instead. +################################################## +elasticsearch.enabled=false +elasticsearch.rest_url=http://localhost:9200 +elasticsearch.username=SomeUsername +elasticsearch.password=SomePassword +elasticsearch.required_index_status=YELLOW +elasticsearch.schema_management_strategy=CREATE +# Immediately refresh indexes after every write. This is very bad for +# performance, but can be helpful for testing. +elasticsearch.debug.refresh_after_write=false +elasticsearch.debug.pretty_print_json_log=false + + +################################################## +# Binary Storage Operations +################################################## +binary_storage.enabled=true + +################################################## +# Bulk Data Specification +################################################## +bulk.export.enabled=true + +################################################## +# CORS Settings +################################################## +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.allowed_origin=* + +################################################## +# Allowed Bundle Types for persistence (defaults are: COLLECTION,DOCUMENT,MESSAGE) +################################################## +#allowed_bundle_types=COLLECTION,DOCUMENT,MESSAGE,TRANSACTION,TRANSACTIONRESPONSE,BATCH,BATCHRESPONSE,HISTORY,SEARCHSET + +################################################## +# Subscriptions +################################################## + +# Enable REST Hook Subscription Channel +subscription.resthook.enabled=false + +# Enable Email Subscription Channel +subscription.email.enabled=false +email.enabled=false +email.from=some@test.com +email.host= +email.port=0 +email.username= +email.password= + +# Enable Websocket Subscription Channel +subscription.websocket.enabled=false + +################################################### +# EMPI +################################################### +empi.enabled=false + +################################################### +# Partitioning And Multitenancy +################################################### +partitioning.enabled=false +partitioning.cross_partition_reference_mode=NOT_ALLOWED +partitioning.partitioning_include_in_search_hashes=true +partitioning.multitenancy.enabled=false + +#daoconfig.client_id_strategy=ANY + 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 278a5f0ccc8..3fb52f4040b 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java @@ -13,6 +13,8 @@ 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.Person; +import org.hl7.fhir.r4.model.Reference; import org.hl7.fhir.r4.model.Subscription; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Order; From cbb718413edc6195df2f37aead7a5754d2ccca00 Mon Sep 17 00:00:00 2001 From: Shubham Parikh Date: Tue, 22 Sep 2020 00:12:42 +0530 Subject: [PATCH 130/200] Updated code to read url pattern from environment. (#10) --- src/main/webapp/WEB-INF/web.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml index a1da422e45e..960e5b79b9c 100644 --- a/src/main/webapp/WEB-INF/web.xml +++ b/src/main/webapp/WEB-INF/web.xml @@ -49,7 +49,7 @@ fhirServlet - /fhir/* + ${url_pattern} From f8ce3879e66b22c14089dabe9f379ece99a9e419 Mon Sep 17 00:00:00 2001 From: Shubham Parikh Date: Fri, 19 Feb 2021 18:53:29 +0530 Subject: [PATCH 131/200] BT-96 Added OAuth support --- pom.xml | 17 +++ .../CustomAuthorizationInterceptor.java | 116 ++++++++++++++--- .../ca/uhn/fhir/jpa/starter/OAuth2Helper.java | 123 ++++++++++++++++++ 3 files changed, 239 insertions(+), 17 deletions(-) create mode 100644 src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java diff --git a/pom.xml b/pom.xml index 56c04ab78f8..0adff8ed5e4 100644 --- a/pom.xml +++ b/pom.xml @@ -75,6 +75,23 @@ + + + + com.auth0 + java-jwt + 3.12.1 + + + com.auth0 + jwks-rsa + 0.15.0 + + + org.json + json + 20201115 + diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java index 09065039ec0..998f7cc2934 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java @@ -1,9 +1,19 @@ package ca.uhn.fhir.jpa.starter; +import java.security.PublicKey; import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpHeaders; import org.springframework.util.StringUtils; +import com.auth0.jwt.JWT; +import com.auth0.jwt.JWTVerifier; +import com.auth0.jwt.exceptions.TokenExpiredException; +import com.auth0.jwt.interfaces.DecodedJWT; + +import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.interceptor.api.Interceptor; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; @@ -13,35 +23,107 @@ @Interceptor public class CustomAuthorizationInterceptor extends AuthorizationInterceptor { - - private static final String HEADER_NAME = "x-api-key"; - private static final String VALIDATE_API_KEY = "VALIDATE_API_KEY"; - private static final String API_KEY = "API_KEY"; + private static final Logger logger = LoggerFactory.getLogger(CustomAuthorizationInterceptor.class); + private static final String OAUTH_URL = System.getenv("OAUTH_URL"); + private static final String APIKEY_ENABLED = System.getenv("APIKEY_ENABLED"); + private static final String APIKEY_HEADER = "x-api-key"; + private static final String APIKEY = System.getenv("APIKEY"); + private static final String FHIR_VERSION = System.getenv("fhir_version"); + private static final String TOKEN_PREFIX = "BEARER "; + private static PublicKey publicKey = null; + private static OAuth2Helper oAuth2Helper = new OAuth2Helper(); @Override - public List buildRuleList(RequestDetails theRequestDetails) { + public List buildRuleList(RequestDetails theRequest) { try { - boolean validateApiKey = Boolean.parseBoolean(System.getenv(VALIDATE_API_KEY)); - String token = theRequestDetails.getHeader(HEADER_NAME); + if (theRequest.getRequestPath().equals(RestOperationTypeEnum.METADATA.getCode())) { + return allowAll(); + } + + if(!oAuth2Helper.isOAuthEnabled() && !isApiKeyEnabled()) { + logger.info("Apikey & Oauth2 are disabled"); + return allowAll(); + } - if (!validateApiKey) { - return authorizeRequest(); - } else if (!StringUtils.isEmpty(token) && token.equals(System.getenv(API_KEY))) { - return authorizeRequest(); - } else { - return (theRequestDetails.getOperation().equals(RestOperationTypeEnum.METADATA.getCode())) ? authorizeRequest() : denyRequest(); + if (oAuth2Helper.isOAuthEnabled() && oAuth2Helper.isOAuthHeaderPresent(theRequest)) { + logger.info("Auhorizing via OAuth"); + return authorizeOAuth(theRequest); + } + + if (isApiKeyEnabled() && isApiKeyHeaderPresent(theRequest)) { + logger.info("Auhorizing via X-API-KEY"); + return authorizeApiKey(theRequest); } } catch (Exception e) { - return denyRequest(); + logger.info("Unexpected authorization error", e); + return denyAll(); } + + logger.info("Authorization failure - fall through"); + return denyAll(); } - private List denyRequest() { + private List denyAll() { return new RuleBuilder().denyAll().build(); } - private List authorizeRequest() { + private List allowAll() { return new RuleBuilder().allowAll().build(); } -} + + private List authorizeOAuth(RequestDetails theRequest) throws Exception { + String token = theRequest.getHeader(HttpHeaders.AUTHORIZATION); + if (StringUtils.isEmpty(token)) { + logger.info("Authorization failure - missing authorization header"); + return denyAll(); + } + + if (!token.toUpperCase().startsWith(TOKEN_PREFIX)) { + logger.info("Authorization failure - invalid authorization header"); + return denyAll(); + } + + token = token.substring(TOKEN_PREFIX.length()); + + try { + DecodedJWT jwt = JWT.decode(token); + String kid = oAuth2Helper.getJwtKeyId(token); + publicKey = StringUtils.isEmpty(publicKey) ? oAuth2Helper.getJwtPublicKey(kid, OAUTH_URL) : publicKey; + JWTVerifier verifier = oAuth2Helper.getJWTVerifier(jwt, publicKey); + jwt = verifier.verify(token); + return allowAll(); + } catch (TokenExpiredException e) { + logger.info("Authorization failure - token has expired"); + } catch (Exception e) { + logger.info("Unexpected exception verifying token", e); + } + + logger.info("Authentication failure"); + return denyAll(); + } + + private Boolean isApiKeyEnabled() { + return ((APIKEY_ENABLED != null) && Boolean.parseBoolean(APIKEY_ENABLED)); + } + + private Boolean isApiKeyHeaderPresent(RequestDetails theRequest) { + String apiKey = theRequest.getHeader(APIKEY_HEADER); + return (!StringUtils.isEmpty(apiKey)); + } + + private List authorizeApiKey(RequestDetails theRequest) { + String apiKey = theRequest.getHeader(APIKEY_HEADER); + if (StringUtils.isEmpty(apiKey)) { + logger.info("Authorization failure - missing X-API-KEY header"); + return denyAll(); + } + + if (apiKey.equals(APIKEY)) { + return allowAll(); + } + + logger.info("Authorization failure - invalid X-API-KEY header"); + return denyAll(); + } +} \ No newline at end of file diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java new file mode 100644 index 00000000000..851b00b4508 --- /dev/null +++ b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java @@ -0,0 +1,123 @@ +package ca.uhn.fhir.jpa.starter; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.KeyFactory; +import java.security.PublicKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.RSAPublicKeySpec; +import java.util.Base64; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.util.StringUtils; +import org.springframework.web.client.RestTemplate; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.JWTVerifier; +import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.exceptions.JWTVerificationException; +import com.auth0.jwt.interfaces.DecodedJWT; + +import ca.uhn.fhir.rest.api.server.RequestDetails; + +public class OAuth2Helper { + private static final Logger logger = LoggerFactory.getLogger(OAuth2Helper.class); + private static final String OAUTH_ENABLED = System.getenv("OAUTH_ENABLED"); + + protected Boolean isOAuthEnabled() { + return ((OAUTH_ENABLED != null) && Boolean.parseBoolean(OAUTH_ENABLED)); + } + + protected Boolean isOAuthHeaderPresent(RequestDetails theRequest) { + String token = theRequest.getHeader(HttpHeaders.AUTHORIZATION); + return (!StringUtils.isEmpty(token)); + } + + protected String getJwtKeyId(String token) { + String tokenHeader = token.split("\\.")[0]; + tokenHeader = new String(Base64.getDecoder().decode(tokenHeader.getBytes())); + String kid = null; + try { + JSONObject obj = new JSONObject(tokenHeader); + kid = obj.getString("kid"); + } catch (JSONException e) { + logger.error("Unexpected exception parsing JWT token", e); + } + return kid; + } + + // The Base64 strings that come from a JWKS need some manipilation before they + // can be decoded, + // so we do that here + protected byte[] base64Decode(String base64) throws IOException { + base64 = base64.replaceAll("-", "+"); + base64 = base64.replaceAll("_", "/"); + switch (base64.length() % 4) // Pad with trailing '='s + { + case 0: + break; // No pad chars in this case + case 2: + base64 += "=="; + break; // Two pad chars + case 3: + base64 += "="; + break; // One pad char + default: + throw new RuntimeException("Illegal base64url string!"); + } + return Base64.getDecoder().decode(base64); + } + + protected JWTVerifier getJWTVerifier(DecodedJWT jwt, PublicKey publicKey) { + Algorithm algorithm = Algorithm.RSA256((RSAPublicKey) publicKey, null); + JWTVerifier verifier = JWT.require(algorithm).withIssuer(jwt.getIssuer()).build(); + return verifier; + } + + protected PublicKey getJwtPublicKey(String kid, String oauthUrl) { + PublicKey publicKey = null; + RestTemplate restTemplate = new RestTemplate(); + ResponseEntity exchange = restTemplate.exchange(oauthUrl + "/certs", HttpMethod.GET, null, + String.class); + + String response = exchange.getBody(); + try { + String modulusStr = null; + String exponentStr = null; + JSONObject obj = new JSONObject(response); + JSONArray keylist = obj.getJSONArray("keys"); + for (int i = 0; i < keylist.length(); i++) { + JSONObject key = keylist.getJSONObject(i); + String id = key.getString("kid"); + if (kid.equals(id)) { + modulusStr = key.getString("n"); + exponentStr = key.getString("e"); + } + } + + BigInteger modulus = new BigInteger(1, base64Decode(modulusStr)); + BigInteger publicExponent = new BigInteger(1, base64Decode(exponentStr)); + + try { + KeyFactory kf = KeyFactory.getInstance("RSA"); + return kf.generatePublic(new RSAPublicKeySpec(modulus, publicExponent)); + } catch (Exception e) { + throw new RuntimeException(e); + } + } catch (JWTVerificationException e) { + logger.warn("Authorization failed - unable to verify token"); + } catch (Exception e) { + logger.error("Unexpected error verifying OAuth2 token", e); + } + + return publicKey; + } + +} \ No newline at end of file From af1993c2aafd7dc35d06b4c23b084eb177c096b3 Mon Sep 17 00:00:00 2001 From: Shubham Parikh Date: Fri, 19 Feb 2021 18:58:29 +0530 Subject: [PATCH 132/200] BT-96 Fixed indentation --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 0adff8ed5e4..3c1d15803f6 100644 --- a/pom.xml +++ b/pom.xml @@ -88,10 +88,10 @@ 0.15.0 - org.json - json - 20201115 - + org.json + json + 20201115 + From 1eede57c8b57561155894f24bc42d99e9eb0895e Mon Sep 17 00:00:00 2001 From: Shubham Parikh Date: Fri, 19 Feb 2021 19:01:55 +0530 Subject: [PATCH 133/200] BT-96 Fixed spacing --- .classpath | 39 +++++++++++++++++++ .project | 37 ++++++++++++++++++ .../CustomAuthorizationInterceptor.java | 6 +-- .../ca/uhn/fhir/jpa/starter/OAuth2Helper.java | 2 +- 4 files changed, 80 insertions(+), 4 deletions(-) create mode 100644 .classpath create mode 100644 .project diff --git a/.classpath b/.classpath new file mode 100644 index 00000000000..54347b7b36a --- /dev/null +++ b/.classpath @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.project b/.project new file mode 100644 index 00000000000..773a7c66e48 --- /dev/null +++ b/.project @@ -0,0 +1,37 @@ + + + hapi-fhir-jpaserver-starter + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.wst.common.project.facet.core.builder + + + + + org.eclipse.wst.validation.validationbuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jem.workbench.JavaEMFNature + org.eclipse.wst.common.modulecore.ModuleCoreNature + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + org.eclipse.wst.common.project.facet.core.nature + org.eclipse.wst.jsdt.core.jsNature + + diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java index 998f7cc2934..e2bef13b4d5 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java @@ -40,8 +40,8 @@ public List buildRuleList(RequestDetails theRequest) { if (theRequest.getRequestPath().equals(RestOperationTypeEnum.METADATA.getCode())) { return allowAll(); } - - if(!oAuth2Helper.isOAuthEnabled() && !isApiKeyEnabled()) { + + if (!oAuth2Helper.isOAuthEnabled() && !isApiKeyEnabled()) { logger.info("Apikey & Oauth2 are disabled"); return allowAll(); } @@ -126,4 +126,4 @@ private List authorizeApiKey(RequestDetails theRequest) { logger.info("Authorization failure - invalid X-API-KEY header"); return denyAll(); } -} \ No newline at end of file +} diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java index 851b00b4508..20f8c1031ae 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java @@ -120,4 +120,4 @@ protected PublicKey getJwtPublicKey(String kid, String oauthUrl) { return publicKey; } -} \ No newline at end of file +} From f9e75fcff722ea845b6d4509cc996bc8d165b68f Mon Sep 17 00:00:00 2001 From: Shubham Parikh Date: Fri, 19 Feb 2021 19:03:16 +0530 Subject: [PATCH 134/200] BT-96 Removed unwanted filed --- .classpath | 39 --------------------------------------- .project | 37 ------------------------------------- 2 files changed, 76 deletions(-) delete mode 100644 .classpath delete mode 100644 .project diff --git a/.classpath b/.classpath deleted file mode 100644 index 54347b7b36a..00000000000 --- a/.classpath +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/.project b/.project deleted file mode 100644 index 773a7c66e48..00000000000 --- a/.project +++ /dev/null @@ -1,37 +0,0 @@ - - - hapi-fhir-jpaserver-starter - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.wst.common.project.facet.core.builder - - - - - org.eclipse.wst.validation.validationbuilder - - - - - org.eclipse.m2e.core.maven2Builder - - - - - - org.eclipse.jem.workbench.JavaEMFNature - org.eclipse.wst.common.modulecore.ModuleCoreNature - org.eclipse.jdt.core.javanature - org.eclipse.m2e.core.maven2Nature - org.eclipse.wst.common.project.facet.core.nature - org.eclipse.wst.jsdt.core.jsNature - - From 8f6f04ce383da7e32da70f1fc07527e89906d7f0 Mon Sep 17 00:00:00 2001 From: shubhamparikh927 Date: Wed, 24 Feb 2021 12:45:42 +0530 Subject: [PATCH 135/200] Changes in code as per PR suggestions --- .../CustomAuthorizationInterceptor.java | 20 +++++++++++++------ .../ca/uhn/fhir/jpa/starter/OAuth2Helper.java | 17 +--------------- 2 files changed, 15 insertions(+), 22 deletions(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java index e2bef13b4d5..b173647f97a 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java @@ -13,7 +13,6 @@ import com.auth0.jwt.exceptions.TokenExpiredException; import com.auth0.jwt.interfaces.DecodedJWT; -import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.interceptor.api.Interceptor; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; @@ -25,10 +24,10 @@ public class CustomAuthorizationInterceptor extends AuthorizationInterceptor { private static final Logger logger = LoggerFactory.getLogger(CustomAuthorizationInterceptor.class); private static final String OAUTH_URL = System.getenv("OAUTH_URL"); + private static final String OAUTH_ENABLED = System.getenv("OAUTH_ENABLED"); private static final String APIKEY_ENABLED = System.getenv("APIKEY_ENABLED"); private static final String APIKEY_HEADER = "x-api-key"; private static final String APIKEY = System.getenv("APIKEY"); - private static final String FHIR_VERSION = System.getenv("fhir_version"); private static final String TOKEN_PREFIX = "BEARER "; private static PublicKey publicKey = null; private static OAuth2Helper oAuth2Helper = new OAuth2Helper(); @@ -41,12 +40,12 @@ public List buildRuleList(RequestDetails theRequest) { return allowAll(); } - if (!oAuth2Helper.isOAuthEnabled() && !isApiKeyEnabled()) { - logger.info("Apikey & Oauth2 are disabled"); + if (!isOAuthEnabled() && !isApiKeyEnabled()) { + logger.warn("APIKEY and OAuth2 authentication are disabled"); return allowAll(); } - if (oAuth2Helper.isOAuthEnabled() && oAuth2Helper.isOAuthHeaderPresent(theRequest)) { + if (isOAuthEnabled() && isOAuthHeaderPresent(theRequest)) { logger.info("Auhorizing via OAuth"); return authorizeOAuth(theRequest); } @@ -60,7 +59,7 @@ public List buildRuleList(RequestDetails theRequest) { return denyAll(); } - logger.info("Authorization failure - fall through"); + logger.warn("Authorization failure - fall through"); return denyAll(); } @@ -103,6 +102,15 @@ private List authorizeOAuth(RequestDetails theRequest) throws Excepti return denyAll(); } + protected Boolean isOAuthEnabled() { + return ((OAUTH_ENABLED != null) && Boolean.parseBoolean(OAUTH_ENABLED)); + } + + protected Boolean isOAuthHeaderPresent(RequestDetails theRequest) { + String token = theRequest.getHeader(HttpHeaders.AUTHORIZATION); + return (!StringUtils.isEmpty(token)); + } + private Boolean isApiKeyEnabled() { return ((APIKEY_ENABLED != null) && Boolean.parseBoolean(APIKEY_ENABLED)); } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java index 20f8c1031ae..2e96fdf83d2 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java @@ -13,10 +13,8 @@ import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; -import org.springframework.util.StringUtils; import org.springframework.web.client.RestTemplate; import com.auth0.jwt.JWT; @@ -25,20 +23,8 @@ import com.auth0.jwt.exceptions.JWTVerificationException; import com.auth0.jwt.interfaces.DecodedJWT; -import ca.uhn.fhir.rest.api.server.RequestDetails; - public class OAuth2Helper { private static final Logger logger = LoggerFactory.getLogger(OAuth2Helper.class); - private static final String OAUTH_ENABLED = System.getenv("OAUTH_ENABLED"); - - protected Boolean isOAuthEnabled() { - return ((OAUTH_ENABLED != null) && Boolean.parseBoolean(OAUTH_ENABLED)); - } - - protected Boolean isOAuthHeaderPresent(RequestDetails theRequest) { - String token = theRequest.getHeader(HttpHeaders.AUTHORIZATION); - return (!StringUtils.isEmpty(token)); - } protected String getJwtKeyId(String token) { String tokenHeader = token.split("\\.")[0]; @@ -54,8 +40,7 @@ protected String getJwtKeyId(String token) { } // The Base64 strings that come from a JWKS need some manipilation before they - // can be decoded, - // so we do that here + // can be decoded, so we do that here protected byte[] base64Decode(String base64) throws IOException { base64 = base64.replaceAll("-", "+"); base64 = base64.replaceAll("_", "/"); From c76c62b96550e852c1c9bc9319ab6c0709cc6e69 Mon Sep 17 00:00:00 2001 From: shubhamparikh927 Date: Wed, 10 Mar 2021 15:35:19 +0530 Subject: [PATCH 136/200] added rest security extension in metadata --- ...omServerCapabilityStatementProviderR4.java | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 src/main/java/ca/uhn/fhir/jpa/starter/CustomServerCapabilityStatementProviderR4.java diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomServerCapabilityStatementProviderR4.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomServerCapabilityStatementProviderR4.java new file mode 100644 index 00000000000..76bbf51d2fa --- /dev/null +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomServerCapabilityStatementProviderR4.java @@ -0,0 +1,66 @@ +package ca.uhn.fhir.jpa.starter; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.annotation.Nonnull; +import javax.servlet.http.HttpServletRequest; + +import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.CapabilityStatement; +import org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestSecurityComponent; +import org.hl7.fhir.r4.model.Extension; +import org.hl7.fhir.r4.model.Meta; +import org.hl7.fhir.r4.model.UriType; +import org.springframework.context.annotation.Configuration; + +import com.google.gson.Gson; + +import ca.uhn.fhir.jpa.api.config.DaoConfig; +import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao; +import ca.uhn.fhir.jpa.provider.r4.JpaConformanceProviderR4; +import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.server.RestfulServer; + +@Configuration +public class CustomServerCapabilityStatementProviderR4 extends JpaConformanceProviderR4 { + + private static final String EXTENSION_MAP = System.getenv("OAUTH_CAPABILITY_EXTENSION"); + + private CapabilityStatement capabilityStatement; + + public CustomServerCapabilityStatementProviderR4 () { + super(); + } + + public CustomServerCapabilityStatementProviderR4(@Nonnull RestfulServer theRestfulServer, @Nonnull IFhirSystemDao theSystemDao, @Nonnull DaoConfig theDaoConfig, @Nonnull ISearchParamRegistry theSearchParamRegistry) { + super(theRestfulServer, theSystemDao, theDaoConfig, theSearchParamRegistry); + } + + + @Override + public CapabilityStatement getServerConformance(HttpServletRequest theRequest, RequestDetails theRequestDetails) { + capabilityStatement = super.getServerConformance(theRequest, theRequestDetails); + capabilityStatement.getRest().get(0).setSecurity(getSecurityComponent()); + return capabilityStatement; + } + + private static CapabilityStatementRestSecurityComponent getSecurityComponent() { + Gson json = new Gson(); + Map oauthUrl = json.fromJson(EXTENSION_MAP, HashMap.class); + CapabilityStatementRestSecurityComponent security = new CapabilityStatementRestSecurityComponent(); + List extensions = new ArrayList(); + oauthUrl.entrySet().forEach(entry -> { + extensions.add(new Extension(entry.getValue(), new UriType(entry.getKey()))); + }); + List extensionsList = new ArrayList(); + extensionsList.add((Extension) new Extension( + new UriType("http://fhir-registry.smarthealthit.org/StructureDefinition/oauth-uris")) + .setExtension(extensions)); + security.setExtension(extensionsList); + return security; + } +} From f3cc5064552ad8a76d5f9b6562da6910719daf47 Mon Sep 17 00:00:00 2001 From: shubhamparikh927 Date: Fri, 12 Mar 2021 16:05:36 +0530 Subject: [PATCH 137/200] Updated PR as per suggestions --- .../CustomServerCapabilityStatementProviderR4.java | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomServerCapabilityStatementProviderR4.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomServerCapabilityStatementProviderR4.java index 76bbf51d2fa..a41308d7d67 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/CustomServerCapabilityStatementProviderR4.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomServerCapabilityStatementProviderR4.java @@ -1,9 +1,7 @@ package ca.uhn.fhir.jpa.starter; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; import javax.annotation.Nonnull; import javax.servlet.http.HttpServletRequest; @@ -16,8 +14,6 @@ import org.hl7.fhir.r4.model.UriType; import org.springframework.context.annotation.Configuration; -import com.google.gson.Gson; - import ca.uhn.fhir.jpa.api.config.DaoConfig; import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao; import ca.uhn.fhir.jpa.provider.r4.JpaConformanceProviderR4; @@ -28,7 +24,8 @@ @Configuration public class CustomServerCapabilityStatementProviderR4 extends JpaConformanceProviderR4 { - private static final String EXTENSION_MAP = System.getenv("OAUTH_CAPABILITY_EXTENSION"); + private static final String OAUTH_TOKEN_URL = System.getenv("OAUTH_TOKEN_URL"); + private static final String OAUTH_MANAGE_URL = System.getenv("OAUTH_MANAGE_URL"); private CapabilityStatement capabilityStatement; @@ -49,13 +46,10 @@ public CapabilityStatement getServerConformance(HttpServletRequest theRequest, R } private static CapabilityStatementRestSecurityComponent getSecurityComponent() { - Gson json = new Gson(); - Map oauthUrl = json.fromJson(EXTENSION_MAP, HashMap.class); CapabilityStatementRestSecurityComponent security = new CapabilityStatementRestSecurityComponent(); List extensions = new ArrayList(); - oauthUrl.entrySet().forEach(entry -> { - extensions.add(new Extension(entry.getValue(), new UriType(entry.getKey()))); - }); + extensions.add(new Extension("token", new UriType(OAUTH_TOKEN_URL))); + extensions.add(new Extension("manage", new UriType(OAUTH_MANAGE_URL))); List extensionsList = new ArrayList(); extensionsList.add((Extension) new Extension( new UriType("http://fhir-registry.smarthealthit.org/StructureDefinition/oauth-uris")) From a69da7434cb0dc9e2ea0c4d86b5cb00bfdd17b76 Mon Sep 17 00:00:00 2001 From: Shubham Parikh Date: Thu, 11 Mar 2021 23:26:18 +0530 Subject: [PATCH 138/200] Updated README.md file to run the app with custom property file & env variables --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index c10ac0625d7..c454c3ab67d 100644 --- a/README.md +++ b/README.md @@ -274,6 +274,13 @@ Server will then be accessible at http://localhost:8080/ and eg. http://localhos refuse_to_fetch_third_party_urls: false fhir_version: R4 ``` +Also with custom property file and enviornment variables , use below docker command, replace with actual values + +```bash +./build-docker-image.sh && docker run -p 8080:8080 -e APIKEY= -e APIKEY_ENABLED= -e datasource.driver= -e datasource.password= -e datasource.url= -e datasource.username= -e enable_web= -e fhir_version= -e hapi.fhir.server_address=/fhir/ -e hibernate.dialect= -e OAUTH_ENABLED= -e OAUTH_URL= -e reuse_cached_search_results_millis= -e spring.config.location='' -e subscription.resthook.enabled= -e subscription.websocket.enabled= -e url_pattern= hapi-fhir/hapi-fhir-jpaserver-starter:latest +``` + + ## Configurations From 64ef31f06d5a3e962c981edad5ecd8bdb448bf04 Mon Sep 17 00:00:00 2001 From: shubhamparikh927 Date: Fri, 12 Mar 2021 12:48:16 +0530 Subject: [PATCH 139/200] Updated PR as per suggestions --- README.md | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c454c3ab67d..973fd28fb1d 100644 --- a/README.md +++ b/README.md @@ -274,10 +274,28 @@ Server will then be accessible at http://localhost:8080/ and eg. http://localhos refuse_to_fetch_third_party_urls: false fhir_version: R4 ``` -Also with custom property file and enviornment variables , use below docker command, replace with actual values +You can use a custom property file that utilizes environment variables for many configuration properties. For example, to use the application-custom.yaml file. Replace with actual values. ```bash -./build-docker-image.sh && docker run -p 8080:8080 -e APIKEY= -e APIKEY_ENABLED= -e datasource.driver= -e datasource.password= -e datasource.url= -e datasource.username= -e enable_web= -e fhir_version= -e hapi.fhir.server_address=/fhir/ -e hibernate.dialect= -e OAUTH_ENABLED= -e OAUTH_URL= -e reuse_cached_search_results_millis= -e spring.config.location='' -e subscription.resthook.enabled= -e subscription.websocket.enabled= -e url_pattern= hapi-fhir/hapi-fhir-jpaserver-starter:latest +./build-docker-image.sh && docker run -p 8080:8080 \ +-e APIKEY= \ +-e APIKEY_ENABLED= \ +-e datasource.driver= \ +-e datasource.password= \ +-e datasource.url= \ +-e datasource.username= \ +-e enable_web= \ +-e fhir_version= \ +-e hapi.fhir.server_address=/fhir/ \ +-e hibernate.dialect= \ +-e OAUTH_ENABLED= \ +-e OAUTH_URL= \ +-e reuse_cached_search_results_millis= \ +-e spring.config.location='' \ +-e subscription.resthook.enabled= \ +-e subscription.websocket.enabled= \ +-e url_pattern= \ +hapi-fhir/hapi-fhir-jpaserver-starter:latest ``` From 96d9c86070a447d3e5b5051892282cf4673b34ec Mon Sep 17 00:00:00 2001 From: shubhamparikh927 Date: Tue, 9 Mar 2021 17:38:41 +0530 Subject: [PATCH 140/200] Added user role validation in Oauth --- .../CustomAuthorizationInterceptor.java | 21 +++++++++++++++---- .../ca/uhn/fhir/jpa/starter/OAuth2Helper.java | 14 ++++++++++++- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java index b173647f97a..ebd720a5e32 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java @@ -1,6 +1,9 @@ package ca.uhn.fhir.jpa.starter; import java.security.PublicKey; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.List; import org.slf4j.Logger; @@ -29,6 +32,7 @@ public class CustomAuthorizationInterceptor extends AuthorizationInterceptor { private static final String APIKEY_HEADER = "x-api-key"; private static final String APIKEY = System.getenv("APIKEY"); private static final String TOKEN_PREFIX = "BEARER "; + private static final List ROLES = getRolesList(); private static PublicKey publicKey = null; private static OAuth2Helper oAuth2Helper = new OAuth2Helper(); @@ -91,7 +95,11 @@ private List authorizeOAuth(RequestDetails theRequest) throws Excepti publicKey = StringUtils.isEmpty(publicKey) ? oAuth2Helper.getJwtPublicKey(kid, OAUTH_URL) : publicKey; JWTVerifier verifier = oAuth2Helper.getJWTVerifier(jwt, publicKey); jwt = verifier.verify(token); - return allowAll(); + ArrayList roles = oAuth2Helper.getRoles(jwt); + if(!roles.isEmpty() && !Collections.disjoint(roles, ROLES)) { + return allowAll(); + } + return denyAll(); } catch (TokenExpiredException e) { logger.info("Authorization failure - token has expired"); } catch (Exception e) { @@ -102,11 +110,11 @@ private List authorizeOAuth(RequestDetails theRequest) throws Excepti return denyAll(); } - protected Boolean isOAuthEnabled() { + private Boolean isOAuthEnabled() { return ((OAUTH_ENABLED != null) && Boolean.parseBoolean(OAUTH_ENABLED)); } - protected Boolean isOAuthHeaderPresent(RequestDetails theRequest) { + private Boolean isOAuthHeaderPresent(RequestDetails theRequest) { String token = theRequest.getHeader(HttpHeaders.AUTHORIZATION); return (!StringUtils.isEmpty(token)); } @@ -134,4 +142,9 @@ private List authorizeApiKey(RequestDetails theRequest) { logger.info("Authorization failure - invalid X-API-KEY header"); return denyAll(); } -} + + private static List getRolesList() { + String role = System.getenv("OAUTH_USER_ROLE"); + return StringUtils.isEmpty(role) ? new ArrayList() : Arrays.asList(role.replaceAll(" ", "").split(",")); + } +} \ No newline at end of file diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java index 2e96fdf83d2..18abe854fa3 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java @@ -6,7 +6,9 @@ import java.security.PublicKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.RSAPublicKeySpec; +import java.util.ArrayList; import java.util.Base64; +import java.util.HashMap; import org.json.JSONArray; import org.json.JSONException; @@ -21,10 +23,12 @@ import com.auth0.jwt.JWTVerifier; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.exceptions.JWTVerificationException; +import com.auth0.jwt.interfaces.Claim; import com.auth0.jwt.interfaces.DecodedJWT; public class OAuth2Helper { private static final Logger logger = LoggerFactory.getLogger(OAuth2Helper.class); + private static final String CLIENT = System.getenv("OAUTH_CLIENT_NAME"); protected String getJwtKeyId(String token) { String tokenHeader = token.split("\\.")[0]; @@ -104,5 +108,13 @@ protected PublicKey getJwtPublicKey(String kid, String oauthUrl) { return publicKey; } + + protected ArrayList getRoles(DecodedJWT jwt) { + Claim claim = jwt.getClaim("resource_access"); + HashMap>> resources = claim.as(HashMap.class); + HashMap> clientMap = resources.getOrDefault(CLIENT, new HashMap>()); + ArrayList roles = clientMap.getOrDefault("roles", new ArrayList()); + return roles; + } -} +} \ No newline at end of file From 79a885d84e7907e5e5ae71a5cde6661a7a7a0922 Mon Sep 17 00:00:00 2001 From: shubhamparikh927 Date: Tue, 9 Mar 2021 17:45:40 +0530 Subject: [PATCH 141/200] code refactor --- .../fhir/jpa/starter/CustomAuthorizationInterceptor.java | 3 +-- src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java index ebd720a5e32..5c74b9fc09f 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java @@ -99,7 +99,6 @@ private List authorizeOAuth(RequestDetails theRequest) throws Excepti if(!roles.isEmpty() && !Collections.disjoint(roles, ROLES)) { return allowAll(); } - return denyAll(); } catch (TokenExpiredException e) { logger.info("Authorization failure - token has expired"); } catch (Exception e) { @@ -147,4 +146,4 @@ private static List getRolesList() { String role = System.getenv("OAUTH_USER_ROLE"); return StringUtils.isEmpty(role) ? new ArrayList() : Arrays.asList(role.replaceAll(" ", "").split(",")); } -} \ No newline at end of file +} diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java index 18abe854fa3..df867d5f96b 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java @@ -28,7 +28,7 @@ public class OAuth2Helper { private static final Logger logger = LoggerFactory.getLogger(OAuth2Helper.class); - private static final String CLIENT = System.getenv("OAUTH_CLIENT_NAME"); + private static final String CLIENT_NAME = System.getenv("OAUTH_CLIENT_NAME"); protected String getJwtKeyId(String token) { String tokenHeader = token.split("\\.")[0]; @@ -112,9 +112,9 @@ protected PublicKey getJwtPublicKey(String kid, String oauthUrl) { protected ArrayList getRoles(DecodedJWT jwt) { Claim claim = jwt.getClaim("resource_access"); HashMap>> resources = claim.as(HashMap.class); - HashMap> clientMap = resources.getOrDefault(CLIENT, new HashMap>()); + HashMap> clientMap = resources.getOrDefault(CLIENT_NAME, new HashMap>()); ArrayList roles = clientMap.getOrDefault("roles", new ArrayList()); return roles; } -} \ No newline at end of file +} From 6d77b6b156c481a77220995886e5969051973e27 Mon Sep 17 00:00:00 2001 From: shubhamparikh927 Date: Thu, 11 Mar 2021 18:11:44 +0530 Subject: [PATCH 142/200] Updated code for using single oauth user role --- .../starter/CustomAuthorizationInterceptor.java | 14 +++----------- .../java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java | 4 ++-- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java index 5c74b9fc09f..46df6fb3a7b 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java @@ -1,9 +1,6 @@ package ca.uhn.fhir.jpa.starter; import java.security.PublicKey; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; import java.util.List; import org.slf4j.Logger; @@ -32,7 +29,7 @@ public class CustomAuthorizationInterceptor extends AuthorizationInterceptor { private static final String APIKEY_HEADER = "x-api-key"; private static final String APIKEY = System.getenv("APIKEY"); private static final String TOKEN_PREFIX = "BEARER "; - private static final List ROLES = getRolesList(); + private static final String ROLE = System.getenv("OAUTH_USER_ROLE"); private static PublicKey publicKey = null; private static OAuth2Helper oAuth2Helper = new OAuth2Helper(); @@ -95,8 +92,8 @@ private List authorizeOAuth(RequestDetails theRequest) throws Excepti publicKey = StringUtils.isEmpty(publicKey) ? oAuth2Helper.getJwtPublicKey(kid, OAUTH_URL) : publicKey; JWTVerifier verifier = oAuth2Helper.getJWTVerifier(jwt, publicKey); jwt = verifier.verify(token); - ArrayList roles = oAuth2Helper.getRoles(jwt); - if(!roles.isEmpty() && !Collections.disjoint(roles, ROLES)) { + + if (oAuth2Helper.checkRole(jwt, ROLE)) { return allowAll(); } } catch (TokenExpiredException e) { @@ -141,9 +138,4 @@ private List authorizeApiKey(RequestDetails theRequest) { logger.info("Authorization failure - invalid X-API-KEY header"); return denyAll(); } - - private static List getRolesList() { - String role = System.getenv("OAUTH_USER_ROLE"); - return StringUtils.isEmpty(role) ? new ArrayList() : Arrays.asList(role.replaceAll(" ", "").split(",")); - } } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java index df867d5f96b..7ee6de69560 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java @@ -109,12 +109,12 @@ protected PublicKey getJwtPublicKey(String kid, String oauthUrl) { return publicKey; } - protected ArrayList getRoles(DecodedJWT jwt) { + protected Boolean checkRole(DecodedJWT jwt, String role) { Claim claim = jwt.getClaim("resource_access"); HashMap>> resources = claim.as(HashMap.class); HashMap> clientMap = resources.getOrDefault(CLIENT_NAME, new HashMap>()); ArrayList roles = clientMap.getOrDefault("roles", new ArrayList()); - return roles; + return roles.contains(role); } } From a36d5a6dac44243914c290a2523435ac7b08beaf Mon Sep 17 00:00:00 2001 From: shubhamparikh927 Date: Fri, 12 Mar 2021 17:06:38 +0530 Subject: [PATCH 143/200] Updated code as per PR suggestions --- .../fhir/jpa/starter/CustomAuthorizationInterceptor.java | 5 +++-- src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java | 7 +++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java index 46df6fb3a7b..bf0687211d6 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java @@ -29,7 +29,8 @@ public class CustomAuthorizationInterceptor extends AuthorizationInterceptor { private static final String APIKEY_HEADER = "x-api-key"; private static final String APIKEY = System.getenv("APIKEY"); private static final String TOKEN_PREFIX = "BEARER "; - private static final String ROLE = System.getenv("OAUTH_USER_ROLE"); + private static final String OAUTH_USER_ROLE = System.getenv("OAUTH_USER_ROLE"); + private static final String OAUTH_CLIENT_ID = System.getenv("OAUTH_CLIENT_ID"); private static PublicKey publicKey = null; private static OAuth2Helper oAuth2Helper = new OAuth2Helper(); @@ -93,7 +94,7 @@ private List authorizeOAuth(RequestDetails theRequest) throws Excepti JWTVerifier verifier = oAuth2Helper.getJWTVerifier(jwt, publicKey); jwt = verifier.verify(token); - if (oAuth2Helper.checkRole(jwt, ROLE)) { + if (oAuth2Helper.hasClientRole(jwt, OAUTH_USER_ROLE, OAUTH_CLIENT_ID)) { return allowAll(); } } catch (TokenExpiredException e) { diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java index 7ee6de69560..a04697fd7b9 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java @@ -28,7 +28,6 @@ public class OAuth2Helper { private static final Logger logger = LoggerFactory.getLogger(OAuth2Helper.class); - private static final String CLIENT_NAME = System.getenv("OAUTH_CLIENT_NAME"); protected String getJwtKeyId(String token) { String tokenHeader = token.split("\\.")[0]; @@ -109,12 +108,12 @@ protected PublicKey getJwtPublicKey(String kid, String oauthUrl) { return publicKey; } - protected Boolean checkRole(DecodedJWT jwt, String role) { + protected Boolean hasClientRole(DecodedJWT jwt, String roleName, String clientId) { Claim claim = jwt.getClaim("resource_access"); HashMap>> resources = claim.as(HashMap.class); - HashMap> clientMap = resources.getOrDefault(CLIENT_NAME, new HashMap>()); + HashMap> clientMap = resources.getOrDefault(clientId, new HashMap>()); ArrayList roles = clientMap.getOrDefault("roles", new ArrayList()); - return roles.contains(role); + return roles.contains(roleName); } } From 4624b07a83a197b6bfaf65cb5cbf8cf311b02050 Mon Sep 17 00:00:00 2001 From: shubhamparikh927 Date: Fri, 19 Mar 2021 14:50:27 +0530 Subject: [PATCH 144/200] Updated code as pr PR siggestions --- docker-compose.yml | 1 - .../uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java | 2 +- src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 130f2584e85..0fe0e457bec 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,6 @@ version: "3" services: hapi-fhir-jpaserver-start: - network_mode: host build: . container_name: hapi-fhir-jpaserver-start restart: on-failure diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java index bf0687211d6..2ca0de7050d 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java @@ -94,7 +94,7 @@ private List authorizeOAuth(RequestDetails theRequest) throws Excepti JWTVerifier verifier = oAuth2Helper.getJWTVerifier(jwt, publicKey); jwt = verifier.verify(token); - if (oAuth2Helper.hasClientRole(jwt, OAUTH_USER_ROLE, OAUTH_CLIENT_ID)) { + if (oAuth2Helper.hasClientRole(jwt, OAUTH_CLIENT_ID, OAUTH_USER_ROLE)) { return allowAll(); } } catch (TokenExpiredException e) { diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java index a04697fd7b9..eba33eed504 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java @@ -108,12 +108,12 @@ protected PublicKey getJwtPublicKey(String kid, String oauthUrl) { return publicKey; } - protected Boolean hasClientRole(DecodedJWT jwt, String roleName, String clientId) { + protected Boolean hasClientRole(DecodedJWT jwt, String clientId, String userRole) { Claim claim = jwt.getClaim("resource_access"); HashMap>> resources = claim.as(HashMap.class); HashMap> clientMap = resources.getOrDefault(clientId, new HashMap>()); ArrayList roles = clientMap.getOrDefault("roles", new ArrayList()); - return roles.contains(roleName); + return roles.contains(userRole); } } From 76d7ac587611d6d2335329f4b3364b20cfb4966f Mon Sep 17 00:00:00 2001 From: shubhamparikh927 Date: Thu, 25 Mar 2021 14:51:18 +0530 Subject: [PATCH 145/200] Added suppoet of delete operation to admin only --- .../fhir/jpa/starter/CustomAuthorizationInterceptor.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java index 2ca0de7050d..8db2a6b3ec6 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java @@ -14,6 +14,7 @@ import com.auth0.jwt.interfaces.DecodedJWT; import ca.uhn.fhir.interceptor.api.Interceptor; +import ca.uhn.fhir.rest.api.RequestTypeEnum; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationInterceptor; @@ -31,6 +32,7 @@ public class CustomAuthorizationInterceptor extends AuthorizationInterceptor { private static final String TOKEN_PREFIX = "BEARER "; private static final String OAUTH_USER_ROLE = System.getenv("OAUTH_USER_ROLE"); private static final String OAUTH_CLIENT_ID = System.getenv("OAUTH_CLIENT_ID"); + private static final String OAUTH_ADMIN_ROLE = System.getenv("OAUTH_ADMIN_ROLE");; private static PublicKey publicKey = null; private static OAuth2Helper oAuth2Helper = new OAuth2Helper(); @@ -94,7 +96,11 @@ private List authorizeOAuth(RequestDetails theRequest) throws Excepti JWTVerifier verifier = oAuth2Helper.getJWTVerifier(jwt, publicKey); jwt = verifier.verify(token); - if (oAuth2Helper.hasClientRole(jwt, OAUTH_CLIENT_ID, OAUTH_USER_ROLE)) { + if (theRequest.getRequestType().equals(RequestTypeEnum.DELETE)) { + if (oAuth2Helper.hasClientRole(jwt, OAUTH_CLIENT_ID, OAUTH_ADMIN_ROLE)) { + return allowAll(); + } + } else if (oAuth2Helper.hasClientRole(jwt, OAUTH_CLIENT_ID, OAUTH_USER_ROLE)) { return allowAll(); } } catch (TokenExpiredException e) { From d29624fee3272af6c3b58450db97aedb3af866b3 Mon Sep 17 00:00:00 2001 From: shubhamparikh927 Date: Thu, 25 Mar 2021 19:12:36 +0530 Subject: [PATCH 146/200] Refactored code --- .../ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java index 8db2a6b3ec6..5b616abda09 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java @@ -32,7 +32,7 @@ public class CustomAuthorizationInterceptor extends AuthorizationInterceptor { private static final String TOKEN_PREFIX = "BEARER "; private static final String OAUTH_USER_ROLE = System.getenv("OAUTH_USER_ROLE"); private static final String OAUTH_CLIENT_ID = System.getenv("OAUTH_CLIENT_ID"); - private static final String OAUTH_ADMIN_ROLE = System.getenv("OAUTH_ADMIN_ROLE");; + private static final String OAUTH_ADMIN_ROLE = System.getenv("OAUTH_ADMIN_ROLE"); private static PublicKey publicKey = null; private static OAuth2Helper oAuth2Helper = new OAuth2Helper(); From af83852be0a7a78c7dfc11a7aaad01d350217864 Mon Sep 17 00:00:00 2001 From: shubhamparikh927 Date: Thu, 25 Mar 2021 15:20:57 +0530 Subject: [PATCH 147/200] Added custom search narrowing filter --- .../CustomAuthorizationInterceptor.java | 4 +++ .../CustomSearchNarrowingInterceptor.java | 29 +++++++++++++++++++ .../ca/uhn/fhir/jpa/starter/OAuth2Helper.java | 6 ++++ 3 files changed, 39 insertions(+) create mode 100644 src/main/java/ca/uhn/fhir/jpa/starter/CustomSearchNarrowingInterceptor.java diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java index 5b616abda09..a57f719ad40 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java @@ -145,4 +145,8 @@ private List authorizeApiKey(RequestDetails theRequest) { logger.info("Authorization failure - invalid X-API-KEY header"); return denyAll(); } + + public static String getTokenPrefix() { + return TOKEN_PREFIX; + } } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomSearchNarrowingInterceptor.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomSearchNarrowingInterceptor.java new file mode 100644 index 00000000000..34ffb2aeda2 --- /dev/null +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomSearchNarrowingInterceptor.java @@ -0,0 +1,29 @@ +package ca.uhn.fhir.jpa.starter; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.interfaces.DecodedJWT; +import ca.uhn.fhir.interceptor.api.Interceptor; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizedList; +import ca.uhn.fhir.rest.server.interceptor.auth.SearchNarrowingInterceptor; + +@Interceptor +public class CustomSearchNarrowingInterceptor extends SearchNarrowingInterceptor { + private OAuth2Helper oAuth2Helper = new OAuth2Helper(); + + @Override + protected AuthorizedList buildAuthorizedList(RequestDetails theRequestDetails) { + String patientId = getPatientFromToken(theRequestDetails); + String patientRef = "Patient/" + patientId; + return new AuthorizedList().addCompartment(patientRef); + } + + private String getPatientFromToken(RequestDetails theRequestDetails) { + String token = theRequestDetails.getHeader("Authorization"); + token = token.substring(CustomAuthorizationInterceptor.getTokenPrefix().length()); + DecodedJWT jwt = JWT.decode(token); + String patRefId = oAuth2Helper.getPatientReferenceFromToken(jwt); + return patRefId; + } + +} diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java index eba33eed504..3370a5315bb 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java @@ -115,5 +115,11 @@ protected Boolean hasClientRole(DecodedJWT jwt, String clientId, String userRole ArrayList roles = clientMap.getOrDefault("roles", new ArrayList()); return roles.contains(userRole); } + + protected String getPatientReferenceFromToken(DecodedJWT jwt) { + Claim claim = jwt.getClaim("Patient"); + String patientRef = claim.as(String.class); + return patientRef; + } } From 020de262afebb04fdd5c5cdcc28eaa20640edf2c Mon Sep 17 00:00:00 2001 From: shubhamparikh927 Date: Thu, 25 Mar 2021 18:54:48 +0530 Subject: [PATCH 148/200] Updated code to work with oauth disabled --- .../CustomSearchNarrowingInterceptor.java | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomSearchNarrowingInterceptor.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomSearchNarrowingInterceptor.java index 34ffb2aeda2..1321c4d9eb6 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/CustomSearchNarrowingInterceptor.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomSearchNarrowingInterceptor.java @@ -14,16 +14,22 @@ public class CustomSearchNarrowingInterceptor extends SearchNarrowingInterceptor @Override protected AuthorizedList buildAuthorizedList(RequestDetails theRequestDetails) { String patientId = getPatientFromToken(theRequestDetails); - String patientRef = "Patient/" + patientId; - return new AuthorizedList().addCompartment(patientRef); + if (patientId != null) { + String patientRef = "Patient/" + patientId; + return new AuthorizedList().addCompartment(patientRef); + } + return new AuthorizedList(); } private String getPatientFromToken(RequestDetails theRequestDetails) { String token = theRequestDetails.getHeader("Authorization"); - token = token.substring(CustomAuthorizationInterceptor.getTokenPrefix().length()); - DecodedJWT jwt = JWT.decode(token); - String patRefId = oAuth2Helper.getPatientReferenceFromToken(jwt); - return patRefId; + if (token != null) { + token = token.substring(CustomAuthorizationInterceptor.getTokenPrefix().length()); + DecodedJWT jwt = JWT.decode(token); + String patRefId = oAuth2Helper.getPatientReferenceFromToken(jwt); + return patRefId; + } + return null; } } From 62f07aa924c2f93f53b43459660ca021c4be0f36 Mon Sep 17 00:00:00 2001 From: shubhamparikh927 Date: Tue, 30 Mar 2021 16:04:26 +0530 Subject: [PATCH 149/200] Updated code to get claim name from environment --- .../starter/CustomSearchNarrowingInterceptor.java | 5 ++++- .../java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java | 13 ++++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomSearchNarrowingInterceptor.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomSearchNarrowingInterceptor.java index 1321c4d9eb6..53c8868be04 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/CustomSearchNarrowingInterceptor.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomSearchNarrowingInterceptor.java @@ -9,6 +9,9 @@ @Interceptor public class CustomSearchNarrowingInterceptor extends SearchNarrowingInterceptor { + + private static final String CLAIM_NAME = System.getenv("claim_name"); + private OAuth2Helper oAuth2Helper = new OAuth2Helper(); @Override @@ -26,7 +29,7 @@ private String getPatientFromToken(RequestDetails theRequestDetails) { if (token != null) { token = token.substring(CustomAuthorizationInterceptor.getTokenPrefix().length()); DecodedJWT jwt = JWT.decode(token); - String patRefId = oAuth2Helper.getPatientReferenceFromToken(jwt); + String patRefId = oAuth2Helper.getPatientReferenceFromToken(jwt, CLAIM_NAME); return patRefId; } return null; diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java index 3370a5315bb..1184542fbc4 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java @@ -115,11 +115,14 @@ protected Boolean hasClientRole(DecodedJWT jwt, String clientId, String userRole ArrayList roles = clientMap.getOrDefault("roles", new ArrayList()); return roles.contains(userRole); } - - protected String getPatientReferenceFromToken(DecodedJWT jwt) { - Claim claim = jwt.getClaim("Patient"); - String patientRef = claim.as(String.class); - return patientRef; + + protected String getPatientReferenceFromToken(DecodedJWT jwt, String claimName) { + if (claimName != null) { + Claim claim = jwt.getClaim(claimName); + String patientRef = claim.as(String.class); + return patientRef; } + return null; + } } From 09ed89fc4f7f57159d5f8fe2c8d7cf705bcfdd7a Mon Sep 17 00:00:00 2001 From: shubhamparikh927 Date: Tue, 30 Mar 2021 18:38:07 +0530 Subject: [PATCH 150/200] change claim name env varible to proper name --- .../fhir/jpa/starter/CustomSearchNarrowingInterceptor.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomSearchNarrowingInterceptor.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomSearchNarrowingInterceptor.java index 53c8868be04..3792113b4a6 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/CustomSearchNarrowingInterceptor.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomSearchNarrowingInterceptor.java @@ -10,7 +10,7 @@ @Interceptor public class CustomSearchNarrowingInterceptor extends SearchNarrowingInterceptor { - private static final String CLAIM_NAME = System.getenv("claim_name"); + private static final String OAUTH_CLAIM_NAME = System.getenv("OAUTH_CLAIM_NAME"); private OAuth2Helper oAuth2Helper = new OAuth2Helper(); @@ -29,7 +29,7 @@ private String getPatientFromToken(RequestDetails theRequestDetails) { if (token != null) { token = token.substring(CustomAuthorizationInterceptor.getTokenPrefix().length()); DecodedJWT jwt = JWT.decode(token); - String patRefId = oAuth2Helper.getPatientReferenceFromToken(jwt, CLAIM_NAME); + String patRefId = oAuth2Helper.getPatientReferenceFromToken(jwt, OAUTH_CLAIM_NAME); return patRefId; } return null; From d2f36f0c807b93a7561da9dda24352ca21b58d78 Mon Sep 17 00:00:00 2001 From: shubhamparikh927 Date: Mon, 5 Apr 2021 15:02:04 +0530 Subject: [PATCH 151/200] Updated code to use hard-coded claim name --- .../uhn/fhir/jpa/starter/CustomSearchNarrowingInterceptor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomSearchNarrowingInterceptor.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomSearchNarrowingInterceptor.java index 3792113b4a6..08d10ab1e02 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/CustomSearchNarrowingInterceptor.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomSearchNarrowingInterceptor.java @@ -10,7 +10,7 @@ @Interceptor public class CustomSearchNarrowingInterceptor extends SearchNarrowingInterceptor { - private static final String OAUTH_CLAIM_NAME = System.getenv("OAUTH_CLAIM_NAME"); + private static final String OAUTH_CLAIM_NAME = "subject"; private OAuth2Helper oAuth2Helper = new OAuth2Helper(); From e8645339326e53b97f9989e0d20e644d8e8f6fc7 Mon Sep 17 00:00:00 2001 From: shubhamparikh927 Date: Tue, 6 Apr 2021 20:43:24 +0530 Subject: [PATCH 152/200] Changed custom mapper claim name to patient from subject --- .../uhn/fhir/jpa/starter/CustomSearchNarrowingInterceptor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomSearchNarrowingInterceptor.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomSearchNarrowingInterceptor.java index 08d10ab1e02..0c21195d328 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/CustomSearchNarrowingInterceptor.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomSearchNarrowingInterceptor.java @@ -10,7 +10,7 @@ @Interceptor public class CustomSearchNarrowingInterceptor extends SearchNarrowingInterceptor { - private static final String OAUTH_CLAIM_NAME = "subject"; + private static final String OAUTH_CLAIM_NAME = "patient"; private OAuth2Helper oAuth2Helper = new OAuth2Helper(); From 34171e200ffbbf04f3fdd2fe2cb61f77d9b331c9 Mon Sep 17 00:00:00 2001 From: shubhamparikh927 Date: Mon, 5 Apr 2021 22:09:21 +0530 Subject: [PATCH 153/200] Added rule builder to filter resources --- .../CustomAuthorizationInterceptor.java | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java index a57f719ad40..39c52de28f7 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java @@ -3,6 +3,7 @@ import java.security.PublicKey; import java.util.List; +import org.hl7.fhir.r4.model.IdType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpHeaders; @@ -75,6 +76,23 @@ private List allowAll() { return new RuleBuilder().allowAll().build(); } + private List allowForClaimResourceId(RequestDetails theRequestDetails,String patientId) { + return new RuleBuilder().allow().read().allResources() + .inCompartment("Patient", new IdType("Patient", patientId)).andThen().allow().write().allResources() + .inCompartment("Patient", new IdType("Patient", patientId)).andThen().denyAll().build(); + } + + private String getPatientFromToken(RequestDetails theRequestDetails) { + String token = theRequestDetails.getHeader("Authorization"); + if (token != null) { + token = token.substring(CustomAuthorizationInterceptor.getTokenPrefix().length()); + DecodedJWT jwt = JWT.decode(token); + String patRefId = oAuth2Helper.getPatientReferenceFromToken(jwt, "patient"); + return patRefId; + } + return null; + } + private List authorizeOAuth(RequestDetails theRequest) throws Exception { String token = theRequest.getHeader(HttpHeaders.AUTHORIZATION); if (StringUtils.isEmpty(token)) { @@ -101,7 +119,8 @@ private List authorizeOAuth(RequestDetails theRequest) throws Excepti return allowAll(); } } else if (oAuth2Helper.hasClientRole(jwt, OAUTH_CLIENT_ID, OAUTH_USER_ROLE)) { - return allowAll(); + String patientId = getPatientFromToken(theRequest); + return StringUtils.isEmpty(patientId) ? allowAll() : allowForClaimResourceId(theRequest,patientId); } } catch (TokenExpiredException e) { logger.info("Authorization failure - token has expired"); From 641caacf5f5a9f8f7afc18af0ef4cd7158dd03b4 Mon Sep 17 00:00:00 2001 From: shubhamparikh927 Date: Tue, 6 Apr 2021 17:05:21 +0530 Subject: [PATCH 154/200] code refactor --- .../CustomAuthorizationInterceptor.java | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java index 39c52de28f7..40fc59ff0e8 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java @@ -76,23 +76,6 @@ private List allowAll() { return new RuleBuilder().allowAll().build(); } - private List allowForClaimResourceId(RequestDetails theRequestDetails,String patientId) { - return new RuleBuilder().allow().read().allResources() - .inCompartment("Patient", new IdType("Patient", patientId)).andThen().allow().write().allResources() - .inCompartment("Patient", new IdType("Patient", patientId)).andThen().denyAll().build(); - } - - private String getPatientFromToken(RequestDetails theRequestDetails) { - String token = theRequestDetails.getHeader("Authorization"); - if (token != null) { - token = token.substring(CustomAuthorizationInterceptor.getTokenPrefix().length()); - DecodedJWT jwt = JWT.decode(token); - String patRefId = oAuth2Helper.getPatientReferenceFromToken(jwt, "patient"); - return patRefId; - } - return null; - } - private List authorizeOAuth(RequestDetails theRequest) throws Exception { String token = theRequest.getHeader(HttpHeaders.AUTHORIZATION); if (StringUtils.isEmpty(token)) { @@ -131,6 +114,23 @@ private List authorizeOAuth(RequestDetails theRequest) throws Excepti logger.info("Authentication failure"); return denyAll(); } + + private List allowForClaimResourceId(RequestDetails theRequestDetails,String patientId) { + return new RuleBuilder().allow().read().allResources() + .inCompartment("Patient", new IdType("Patient", patientId)).andThen().allow().write().allResources() + .inCompartment("Patient", new IdType("Patient", patientId)).andThen().denyAll().build(); + } + + private String getPatientFromToken(RequestDetails theRequestDetails) { + String token = theRequestDetails.getHeader("Authorization"); + if (token != null) { + token = token.substring(CustomAuthorizationInterceptor.getTokenPrefix().length()); + DecodedJWT jwt = JWT.decode(token); + String patRefId = oAuth2Helper.getPatientReferenceFromToken(jwt, "patient"); + return patRefId; + } + return null; + } private Boolean isOAuthEnabled() { return ((OAUTH_ENABLED != null) && Boolean.parseBoolean(OAUTH_ENABLED)); From a17863f5acab831a942d3eecb5bc3468b9bda19b Mon Sep 17 00:00:00 2001 From: Shubham Parikh Date: Wed, 7 Apr 2021 00:06:01 +0530 Subject: [PATCH 155/200] Update CustomAuthorizationInterceptor.java --- .../fhir/jpa/starter/CustomAuthorizationInterceptor.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java index 40fc59ff0e8..9e3db4546f1 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java @@ -116,9 +116,11 @@ private List authorizeOAuth(RequestDetails theRequest) throws Excepti } private List allowForClaimResourceId(RequestDetails theRequestDetails,String patientId) { - return new RuleBuilder().allow().read().allResources() - .inCompartment("Patient", new IdType("Patient", patientId)).andThen().allow().write().allResources() - .inCompartment("Patient", new IdType("Patient", patientId)).andThen().denyAll().build(); + return new RuleBuilder() + .allow().read().allResources().inCompartment("Patient", new IdType("Patient", patientId)).andThen() + .allow().write().allResources().inCompartment("Patient", new IdType("Patient", patientId)).andThen() + .denyAll() + .build(); } private String getPatientFromToken(RequestDetails theRequestDetails) { From cff040f226165bf7d1f8aadfb1dedb8a52cb6b3a Mon Sep 17 00:00:00 2001 From: shubhamparikh927 Date: Fri, 9 Apr 2021 13:23:03 +0530 Subject: [PATCH 156/200] updated code to support operations on bundle resources --- .../ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java index 9e3db4546f1..afc4709651d 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java @@ -119,6 +119,7 @@ private List allowForClaimResourceId(RequestDetails theRequestDetails return new RuleBuilder() .allow().read().allResources().inCompartment("Patient", new IdType("Patient", patientId)).andThen() .allow().write().allResources().inCompartment("Patient", new IdType("Patient", patientId)).andThen() + .allow().transaction().withAnyOperation().andApplyNormalRules().andThen() .denyAll() .build(); } From 435fda1cd24edf96cdea4106f99839bd95fcfc15 Mon Sep 17 00:00:00 2001 From: shubhamparikh927 Date: Tue, 25 May 2021 16:51:46 +0530 Subject: [PATCH 157/200] Added consent rule to allow patch request --- .../ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java index afc4709651d..9a38b7decdb 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java @@ -120,6 +120,7 @@ private List allowForClaimResourceId(RequestDetails theRequestDetails .allow().read().allResources().inCompartment("Patient", new IdType("Patient", patientId)).andThen() .allow().write().allResources().inCompartment("Patient", new IdType("Patient", patientId)).andThen() .allow().transaction().withAnyOperation().andApplyNormalRules().andThen() + .allow().patch().allRequests().andThen() .denyAll() .build(); } From 031d50528afc3f0611dbbcbeda8b5112c276027b Mon Sep 17 00:00:00 2001 From: shubhamparikh927 Date: Fri, 25 Jun 2021 19:52:04 +0530 Subject: [PATCH 158/200] Added oauth support for task resource --- .../CustomAuthorizationInterceptor.java | 17 ++- .../jpa/starter/CustomConsentService.java | 129 ++++++++++++++++++ .../ca/uhn/fhir/jpa/starter/OAuth2Helper.java | 11 ++ 3 files changed, 150 insertions(+), 7 deletions(-) create mode 100644 src/main/java/ca/uhn/fhir/jpa/starter/CustomConsentService.java diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java index 9a38b7decdb..c712d2e5a29 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java @@ -116,13 +116,16 @@ private List authorizeOAuth(RequestDetails theRequest) throws Excepti } private List allowForClaimResourceId(RequestDetails theRequestDetails,String patientId) { - return new RuleBuilder() - .allow().read().allResources().inCompartment("Patient", new IdType("Patient", patientId)).andThen() - .allow().write().allResources().inCompartment("Patient", new IdType("Patient", patientId)).andThen() - .allow().transaction().withAnyOperation().andApplyNormalRules().andThen() - .allow().patch().allRequests().andThen() - .denyAll() - .build(); + if (oAuth2Helper.checkInPatientCompartment(theRequestDetails.getResourceName())) { + return new RuleBuilder() + .allow().read().allResources().inCompartment("Patient", new IdType("Patient", patientId)).andThen() + .allow().write().allResources().inCompartment("Patient", new IdType("Patient", patientId)).andThen() + .allow().transaction().withAnyOperation().andApplyNormalRules().andThen() + .allow().patch().allRequests().andThen() + .denyAll() + .build(); + } + return allowAll(); } private String getPatientFromToken(RequestDetails theRequestDetails) { diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomConsentService.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomConsentService.java new file mode 100644 index 00000000000..d175782b492 --- /dev/null +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomConsentService.java @@ -0,0 +1,129 @@ +package ca.uhn.fhir.jpa.starter; + +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r4.model.IdType; +import org.hl7.fhir.r4.model.Reference; +import org.hl7.fhir.r4.model.Task; +import org.springframework.stereotype.Service; +import com.auth0.jwt.JWT; +import com.auth0.jwt.interfaces.DecodedJWT; +import ca.uhn.fhir.jpa.api.dao.DaoRegistry; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.server.interceptor.consent.ConsentOutcome; +import ca.uhn.fhir.rest.server.interceptor.consent.IConsentContextServices; +import ca.uhn.fhir.rest.server.interceptor.consent.IConsentService; + +@Service +public class CustomConsentService implements IConsentService { + + private static final String OAUTH_CLAIM_NAME = "patient"; + + private static final org.slf4j.Logger log = + org.slf4j.LoggerFactory.getLogger(CustomConsentService.class); + + private OAuth2Helper oAuth2Helper = new OAuth2Helper(); + DaoRegistry daoRegistry; + + public CustomConsentService() {} + + public CustomConsentService(DaoRegistry daoRegistry) { + this.daoRegistry = daoRegistry; + } + + @Override + public ConsentOutcome startOperation(RequestDetails theRequestDetails, + IConsentContextServices theContextServices) { + if (oAuth2Helper.checkInPatientCompartment(theRequestDetails.getResourceName())) { + return ConsentOutcome.AUTHORIZED; + } + if (theRequestDetails.getHeader("Authorization") != null) { + String patientId = getPatientFromToken(theRequestDetails); + String resourceName = null; + boolean proceed = false; + switch (theRequestDetails.getRequestType().toString()) { + case "POST": + resourceName = theRequestDetails.getResourceName(); + proceed = validateResource(resourceName, patientId, theRequestDetails.getResource()); + break; + case "PUT": + resourceName = theRequestDetails.getResourceName(); + IBaseResource putResource = getResourceFromDB(theRequestDetails.getRequestPath()); + if (validateResource(resourceName, patientId, putResource)) { + proceed = validateResource(resourceName, patientId, theRequestDetails.getResource()); + } + break; + case "PATCH": + resourceName = theRequestDetails.getResourceName(); + IBaseResource patchResource = getResourceFromDB(theRequestDetails.getRequestPath()); + proceed = validateResource(resourceName, patientId, patchResource); + break; + case "GET": + resourceName = theRequestDetails.getResourceName(); + if (theRequestDetails.getRequestPath().split("/").length > 1) { + IBaseResource getResource = getResourceFromDB(theRequestDetails.getRequestPath()); + proceed = validateResource(resourceName, patientId, getResource); + } + proceed = true; + break; + default: + proceed = true; + break; + } + return proceed ? ConsentOutcome.PROCEED : ConsentOutcome.REJECT; + } + return ConsentOutcome.AUTHORIZED; + } + + @Override + public ConsentOutcome canSeeResource(RequestDetails theRequestDetails, IBaseResource theResource, + IConsentContextServices theContextServices) { + if (oAuth2Helper.checkInPatientCompartment(theRequestDetails.getResourceName())) { + return ConsentOutcome.AUTHORIZED; + } + String patientId = getPatientFromToken(theRequestDetails); + String resourceName = theResource.getClass().getSimpleName(); + return validateResource(resourceName, patientId, theResource) ? ConsentOutcome.PROCEED + : ConsentOutcome.REJECT; + } + + private IBaseResource getResourceFromDB(String requestedPath) { + String[] requestDetail = requestedPath.split("/"); + return daoRegistry.getResourceDao(requestDetail[0]).read(new IdType(requestDetail[1])); + } + + + private boolean validateResource(String resourceName, String patientId, + IBaseResource theResource) { + if (patientId != null) { + String patientRef = "Patient/" + patientId; + switch (resourceName) { + case "Task": + return validateTaskResource((Task) theResource, patientRef); + default: + return false; + } + } + return true; + } + + private boolean validateTaskResource(Task task, String patientRef) { + try { + Reference ref = (Reference) task.getNamedProperty("for").getValues().get(0); + if (ref.getReference().equals(patientRef)) { + return true; + } + return false; + } catch (Exception e) { + log.error("Unable to find patient reference in Task resource"); + return false; + } + } + + private String getPatientFromToken(RequestDetails theRequestDetails) { + String token = theRequestDetails.getHeader("Authorization"); + token = token.substring(CustomAuthorizationInterceptor.getTokenPrefix().length()); + DecodedJWT jwt = JWT.decode(token); + String patRefId = oAuth2Helper.getPatientReferenceFromToken(jwt, OAUTH_CLAIM_NAME); + return patRefId; + } +} diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java index 1184542fbc4..71add0fe66f 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java @@ -9,6 +9,7 @@ import java.util.ArrayList; import java.util.Base64; import java.util.HashMap; +import java.util.List; import org.json.JSONArray; import org.json.JSONException; @@ -25,6 +26,9 @@ import com.auth0.jwt.exceptions.JWTVerificationException; import com.auth0.jwt.interfaces.Claim; import com.auth0.jwt.interfaces.DecodedJWT; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.RuntimeResourceDefinition; +import ca.uhn.fhir.context.RuntimeSearchParam; public class OAuth2Helper { private static final Logger logger = LoggerFactory.getLogger(OAuth2Helper.class); @@ -125,4 +129,11 @@ protected String getPatientReferenceFromToken(DecodedJWT jwt, String claimName) return null; } + protected boolean checkInPatientCompartment(String resourceType) { + FhirContext ctx = FhirContext.forR4(); + RuntimeResourceDefinition data = ctx.getResourceDefinition(resourceType); + List compartmentList = data.getSearchParamsForCompartmentName("Patient"); + return !compartmentList.isEmpty(); + } + } From c2ae6300caa3be49bee65a78f1bff8ee049a84c8 Mon Sep 17 00:00:00 2001 From: shubhamparikh927 Date: Tue, 29 Jun 2021 17:23:14 +0530 Subject: [PATCH 159/200] Updated code to make refrence check generic method --- .../ca/uhn/fhir/jpa/starter/CustomConsentService.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomConsentService.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomConsentService.java index d175782b492..7a350d011ab 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/CustomConsentService.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomConsentService.java @@ -3,7 +3,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Reference; -import org.hl7.fhir.r4.model.Task; +import org.hl7.fhir.r4.model.Resource; import org.springframework.stereotype.Service; import com.auth0.jwt.JWT; import com.auth0.jwt.interfaces.DecodedJWT; @@ -98,7 +98,7 @@ private boolean validateResource(String resourceName, String patientId, String patientRef = "Patient/" + patientId; switch (resourceName) { case "Task": - return validateTaskResource((Task) theResource, patientRef); + return validateResourceRef((Resource) theResource, patientRef, "for"); default: return false; } @@ -106,15 +106,15 @@ private boolean validateResource(String resourceName, String patientId, return true; } - private boolean validateTaskResource(Task task, String patientRef) { + private boolean validateResourceRef(Resource theResource, String patientRef, String refPropertyName) { try { - Reference ref = (Reference) task.getNamedProperty("for").getValues().get(0); + Reference ref = (Reference) theResource.getNamedProperty(refPropertyName).getValues().get(0); if (ref.getReference().equals(patientRef)) { return true; } return false; } catch (Exception e) { - log.error("Unable to find patient reference in Task resource"); + log.error("Unable to find patient reference in "+ theResource.getClass().getCanonicalName() +" resource"); return false; } } From 4d18c16e8d74250a1560cbf080723420bc89847a Mon Sep 17 00:00:00 2001 From: shubhamparikh927 Date: Wed, 30 Jun 2021 18:37:20 +0530 Subject: [PATCH 160/200] Updated method names --- .../CustomAuthorizationInterceptor.java | 2 +- .../jpa/starter/CustomConsentService.java | 46 +++++++++++-------- .../ca/uhn/fhir/jpa/starter/OAuth2Helper.java | 2 +- 3 files changed, 28 insertions(+), 22 deletions(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java index c712d2e5a29..d6193c75e41 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java @@ -116,7 +116,7 @@ private List authorizeOAuth(RequestDetails theRequest) throws Excepti } private List allowForClaimResourceId(RequestDetails theRequestDetails,String patientId) { - if (oAuth2Helper.checkInPatientCompartment(theRequestDetails.getResourceName())) { + if (oAuth2Helper.canBeInPatientCompartment(theRequestDetails.getResourceName())) { return new RuleBuilder() .allow().read().allResources().inCompartment("Patient", new IdType("Patient", patientId)).andThen() .allow().write().allResources().inCompartment("Patient", new IdType("Patient", patientId)).andThen() diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomConsentService.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomConsentService.java index 7a350d011ab..c827ec40f43 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/CustomConsentService.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomConsentService.java @@ -1,5 +1,6 @@ package ca.uhn.fhir.jpa.starter; +import org.apache.commons.lang3.ObjectUtils; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Reference; @@ -22,7 +23,7 @@ public class CustomConsentService implements IConsentService { org.slf4j.LoggerFactory.getLogger(CustomConsentService.class); private OAuth2Helper oAuth2Helper = new OAuth2Helper(); - DaoRegistry daoRegistry; + private DaoRegistry daoRegistry; public CustomConsentService() {} @@ -33,35 +34,35 @@ public CustomConsentService(DaoRegistry daoRegistry) { @Override public ConsentOutcome startOperation(RequestDetails theRequestDetails, IConsentContextServices theContextServices) { - if (oAuth2Helper.checkInPatientCompartment(theRequestDetails.getResourceName())) { - return ConsentOutcome.AUTHORIZED; - } - if (theRequestDetails.getHeader("Authorization") != null) { + if (!ObjectUtils.isEmpty(theRequestDetails.getHeader("Authorization"))) { + if (oAuth2Helper.canBeInPatientCompartment(theRequestDetails.getResourceName())) { + return ConsentOutcome.AUTHORIZED; + } String patientId = getPatientFromToken(theRequestDetails); String resourceName = null; boolean proceed = false; switch (theRequestDetails.getRequestType().toString()) { case "POST": resourceName = theRequestDetails.getResourceName(); - proceed = validateResource(resourceName, patientId, theRequestDetails.getResource()); + proceed = isResourceValid(resourceName, patientId, theRequestDetails.getResource()); break; case "PUT": resourceName = theRequestDetails.getResourceName(); IBaseResource putResource = getResourceFromDB(theRequestDetails.getRequestPath()); - if (validateResource(resourceName, patientId, putResource)) { - proceed = validateResource(resourceName, patientId, theRequestDetails.getResource()); + if (isResourceValid(resourceName, patientId, putResource)) { + proceed = isResourceValid(resourceName, patientId, theRequestDetails.getResource()); } break; case "PATCH": resourceName = theRequestDetails.getResourceName(); IBaseResource patchResource = getResourceFromDB(theRequestDetails.getRequestPath()); - proceed = validateResource(resourceName, patientId, patchResource); + proceed = isResourceValid(resourceName, patientId, patchResource); break; case "GET": resourceName = theRequestDetails.getResourceName(); if (theRequestDetails.getRequestPath().split("/").length > 1) { IBaseResource getResource = getResourceFromDB(theRequestDetails.getRequestPath()); - proceed = validateResource(resourceName, patientId, getResource); + proceed = isResourceValid(resourceName, patientId, getResource); } proceed = true; break; @@ -77,13 +78,16 @@ public ConsentOutcome startOperation(RequestDetails theRequestDetails, @Override public ConsentOutcome canSeeResource(RequestDetails theRequestDetails, IBaseResource theResource, IConsentContextServices theContextServices) { - if (oAuth2Helper.checkInPatientCompartment(theRequestDetails.getResourceName())) { - return ConsentOutcome.AUTHORIZED; + if (!ObjectUtils.isEmpty(theRequestDetails.getHeader("Authorization"))) { + if (oAuth2Helper.canBeInPatientCompartment(theRequestDetails.getResourceName())) { + return ConsentOutcome.AUTHORIZED; + } + String patientId = getPatientFromToken(theRequestDetails); + String resourceName = theResource.getClass().getSimpleName(); + return isResourceValid(resourceName, patientId, theResource) ? ConsentOutcome.PROCEED + : ConsentOutcome.REJECT; } - String patientId = getPatientFromToken(theRequestDetails); - String resourceName = theResource.getClass().getSimpleName(); - return validateResource(resourceName, patientId, theResource) ? ConsentOutcome.PROCEED - : ConsentOutcome.REJECT; + return ConsentOutcome.AUTHORIZED; } private IBaseResource getResourceFromDB(String requestedPath) { @@ -92,13 +96,13 @@ private IBaseResource getResourceFromDB(String requestedPath) { } - private boolean validateResource(String resourceName, String patientId, + private boolean isResourceValid(String resourceName, String patientId, IBaseResource theResource) { if (patientId != null) { String patientRef = "Patient/" + patientId; switch (resourceName) { case "Task": - return validateResourceRef((Resource) theResource, patientRef, "for"); + return isReferanceValid((Resource) theResource, patientRef, "for"); default: return false; } @@ -106,7 +110,8 @@ private boolean validateResource(String resourceName, String patientId, return true; } - private boolean validateResourceRef(Resource theResource, String patientRef, String refPropertyName) { + private boolean isReferanceValid(Resource theResource, String patientRef, + String refPropertyName) { try { Reference ref = (Reference) theResource.getNamedProperty(refPropertyName).getValues().get(0); if (ref.getReference().equals(patientRef)) { @@ -114,7 +119,8 @@ private boolean validateResourceRef(Resource theResource, String patientRef, Str } return false; } catch (Exception e) { - log.error("Unable to find patient reference in "+ theResource.getClass().getCanonicalName() +" resource"); + log.error("Unable to find patient reference in " + theResource.getClass().getCanonicalName() + + " resource"); return false; } } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java index 71add0fe66f..e6cd4c62d58 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java @@ -129,7 +129,7 @@ protected String getPatientReferenceFromToken(DecodedJWT jwt, String claimName) return null; } - protected boolean checkInPatientCompartment(String resourceType) { + protected boolean canBeInPatientCompartment(String resourceType) { FhirContext ctx = FhirContext.forR4(); RuntimeResourceDefinition data = ctx.getResourceDefinition(resourceType); List compartmentList = data.getSearchParamsForCompartmentName("Patient"); From 948778f693e9df4fce5d815d77f36df7e6cb611e Mon Sep 17 00:00:00 2001 From: shubhamparikh927 Date: Fri, 2 Jul 2021 18:23:00 +0530 Subject: [PATCH 161/200] Updated code as per PR suggestions --- .../jpa/starter/CustomConsentService.java | 131 +++++++++++------- .../ca/uhn/fhir/jpa/starter/OAuth2Helper.java | 10 +- 2 files changed, 93 insertions(+), 48 deletions(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomConsentService.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomConsentService.java index c827ec40f43..5402ced41d8 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/CustomConsentService.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomConsentService.java @@ -1,5 +1,7 @@ package ca.uhn.fhir.jpa.starter; +import java.util.List; +import java.util.Map; import org.apache.commons.lang3.ObjectUtils; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.IdType; @@ -8,6 +10,7 @@ import org.springframework.stereotype.Service; import com.auth0.jwt.JWT; import com.auth0.jwt.interfaces.DecodedJWT; +import com.fasterxml.jackson.databind.ObjectMapper; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.interceptor.consent.ConsentOutcome; @@ -34,60 +37,63 @@ public CustomConsentService(DaoRegistry daoRegistry) { @Override public ConsentOutcome startOperation(RequestDetails theRequestDetails, IConsentContextServices theContextServices) { - if (!ObjectUtils.isEmpty(theRequestDetails.getHeader("Authorization"))) { - if (oAuth2Helper.canBeInPatientCompartment(theRequestDetails.getResourceName())) { - return ConsentOutcome.AUTHORIZED; - } - String patientId = getPatientFromToken(theRequestDetails); - String resourceName = null; - boolean proceed = false; - switch (theRequestDetails.getRequestType().toString()) { - case "POST": - resourceName = theRequestDetails.getResourceName(); + /* + * Returning authorized if there is no Authorization header present or if requested resource is + * present in Patient Compartment For both these cases all the consent logic is in authorization + * intercepter rules. + */ + if (ObjectUtils.isEmpty(theRequestDetails.getHeader("Authorization")) + || oAuth2Helper.canBeInPatientCompartment(theRequestDetails.getResourceName())) { + return ConsentOutcome.AUTHORIZED; + } + String patientId = getPatientFromToken(theRequestDetails); + String resourceName = null; + boolean proceed = false; + switch (theRequestDetails.getRequestType().toString()) { + case "POST": + resourceName = theRequestDetails.getResourceName(); + proceed = isResourceValid(resourceName, patientId, theRequestDetails.getResource()); + break; + case "PUT": + resourceName = theRequestDetails.getResourceName(); + IBaseResource putResource = getResourceFromDB(theRequestDetails.getRequestPath()); + if (isResourceValid(resourceName, patientId, putResource)) { proceed = isResourceValid(resourceName, patientId, theRequestDetails.getResource()); - break; - case "PUT": - resourceName = theRequestDetails.getResourceName(); - IBaseResource putResource = getResourceFromDB(theRequestDetails.getRequestPath()); - if (isResourceValid(resourceName, patientId, putResource)) { - proceed = isResourceValid(resourceName, patientId, theRequestDetails.getResource()); - } - break; - case "PATCH": - resourceName = theRequestDetails.getResourceName(); - IBaseResource patchResource = getResourceFromDB(theRequestDetails.getRequestPath()); - proceed = isResourceValid(resourceName, patientId, patchResource); - break; - case "GET": - resourceName = theRequestDetails.getResourceName(); - if (theRequestDetails.getRequestPath().split("/").length > 1) { - IBaseResource getResource = getResourceFromDB(theRequestDetails.getRequestPath()); - proceed = isResourceValid(resourceName, patientId, getResource); - } - proceed = true; - break; - default: - proceed = true; - break; - } - return proceed ? ConsentOutcome.PROCEED : ConsentOutcome.REJECT; + } + break; + case "PATCH": + resourceName = theRequestDetails.getResourceName(); + IBaseResource patchResource = getResourceFromDB(theRequestDetails.getRequestPath()); + if (isResourceValid(resourceName, patientId, patchResource)) { + // As Patch request body doesn't contain any Resource we need to handle it + // differently + proceed = isPatchRequestBodyValid(resourceName, + new String(theRequestDetails.getRequestContentsIfLoaded())); + } + break; + default: + proceed = true; + break; } - return ConsentOutcome.AUTHORIZED; + return proceed ? ConsentOutcome.PROCEED : ConsentOutcome.REJECT; } @Override public ConsentOutcome canSeeResource(RequestDetails theRequestDetails, IBaseResource theResource, IConsentContextServices theContextServices) { - if (!ObjectUtils.isEmpty(theRequestDetails.getHeader("Authorization"))) { - if (oAuth2Helper.canBeInPatientCompartment(theRequestDetails.getResourceName())) { - return ConsentOutcome.AUTHORIZED; - } - String patientId = getPatientFromToken(theRequestDetails); - String resourceName = theResource.getClass().getSimpleName(); - return isResourceValid(resourceName, patientId, theResource) ? ConsentOutcome.PROCEED - : ConsentOutcome.REJECT; + /* + * Returning authorized if there is no Authorization header present or if requested resource is + * present in Patient Compartment For both this cases all the consent logic is in authorization + * intercepter rules + */ + if (ObjectUtils.isEmpty(theRequestDetails.getHeader("Authorization")) + || oAuth2Helper.canBeInPatientCompartment(theRequestDetails.getResourceName())) { + return ConsentOutcome.AUTHORIZED; } - return ConsentOutcome.AUTHORIZED; + String patientId = getPatientFromToken(theRequestDetails); + String resourceName = theResource.getClass().getSimpleName(); + return isResourceValid(resourceName, patientId, theResource) ? ConsentOutcome.PROCEED + : ConsentOutcome.REJECT; } private IBaseResource getResourceFromDB(String requestedPath) { @@ -95,7 +101,6 @@ private IBaseResource getResourceFromDB(String requestedPath) { return daoRegistry.getResourceDao(requestDetail[0]).read(new IdType(requestDetail[1])); } - private boolean isResourceValid(String resourceName, String patientId, IBaseResource theResource) { if (patientId != null) { @@ -132,4 +137,36 @@ private String getPatientFromToken(RequestDetails theRequestDetails) { String patRefId = oAuth2Helper.getPatientReferenceFromToken(jwt, OAUTH_CLAIM_NAME); return patRefId; } + + /* + * For Patch Request we take Request body(byte array) and convert to json string Then using + * ObjectMapper we can convert json string to List of key value pairs(map object) + */ + @SuppressWarnings("unchecked") + private boolean isPatchRequestBodyValid(String resourceName, String content) { + try { + ObjectMapper mapper = new ObjectMapper(); + List> opreationList = mapper.readValue(content, List.class); + switch (resourceName) { + case "Task": + return isPatchRequestValid(opreationList, "/for/reference"); + default: + return false; + } + } catch (Exception e) { + return false; + } + } + + /* + * Here we check if request performs any patch operation type (add, insert, delete, replace or + * move) on reference. If so we deny request by returning false + */ + private boolean isPatchRequestValid(List> opreationList, String refPath) { + for (Map opreation : opreationList) { + if (opreation.get("path").equals(refPath)) + return false; + } + return true; + } } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java index e6cd4c62d58..ada5b020910 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java @@ -11,6 +11,7 @@ import java.util.HashMap; import java.util.List; +import org.apache.commons.lang3.ObjectUtils; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -29,6 +30,7 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.context.RuntimeSearchParam; +import ca.uhn.fhir.rest.server.provider.ProviderConstants; public class OAuth2Helper { private static final Logger logger = LoggerFactory.getLogger(OAuth2Helper.class); @@ -130,10 +132,16 @@ protected String getPatientReferenceFromToken(DecodedJWT jwt, String claimName) } protected boolean canBeInPatientCompartment(String resourceType) { + /* + * For Bundle Request resourceType would be null. + * For now we allow all bundle operations this will apply normal rules from authorization intercepter + */ + if (ObjectUtils.isEmpty(resourceType)) { + return true; + } FhirContext ctx = FhirContext.forR4(); RuntimeResourceDefinition data = ctx.getResourceDefinition(resourceType); List compartmentList = data.getSearchParamsForCompartmentName("Patient"); return !compartmentList.isEmpty(); } - } From c680638ce5e93a2f85e5a47349d08e3bf66028b9 Mon Sep 17 00:00:00 2001 From: shubhamparikh927 Date: Wed, 7 Jul 2021 22:31:30 +0530 Subject: [PATCH 162/200] Update code as per PR suggestion --- .../ca/uhn/fhir/jpa/starter/CustomConsentService.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomConsentService.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomConsentService.java index 5402ced41d8..9cb5ffd6936 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/CustomConsentService.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomConsentService.java @@ -48,6 +48,7 @@ public ConsentOutcome startOperation(RequestDetails theRequestDetails, } String patientId = getPatientFromToken(theRequestDetails); String resourceName = null; + IBaseResource existingResource = null; boolean proceed = false; switch (theRequestDetails.getRequestType().toString()) { case "POST": @@ -56,15 +57,15 @@ public ConsentOutcome startOperation(RequestDetails theRequestDetails, break; case "PUT": resourceName = theRequestDetails.getResourceName(); - IBaseResource putResource = getResourceFromDB(theRequestDetails.getRequestPath()); - if (isResourceValid(resourceName, patientId, putResource)) { + existingResource = getResourceFromDB(theRequestDetails.getRequestPath()); + if (isResourceValid(resourceName, patientId, existingResource)) { proceed = isResourceValid(resourceName, patientId, theRequestDetails.getResource()); } break; case "PATCH": resourceName = theRequestDetails.getResourceName(); - IBaseResource patchResource = getResourceFromDB(theRequestDetails.getRequestPath()); - if (isResourceValid(resourceName, patientId, patchResource)) { + existingResource = getResourceFromDB(theRequestDetails.getRequestPath()); + if (isResourceValid(resourceName, patientId, existingResource)) { // As Patch request body doesn't contain any Resource we need to handle it // differently proceed = isPatchRequestBodyValid(resourceName, From 6d92b354e90526fba58230adddca89422c4b1993 Mon Sep 17 00:00:00 2001 From: shubhamparikh927 Date: Thu, 8 Jul 2021 17:42:16 +0530 Subject: [PATCH 163/200] Updated code as per PR --- .../ca/uhn/fhir/jpa/starter/CustomConsentService.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomConsentService.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomConsentService.java index 9cb5ffd6936..3c1b8f4b69c 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/CustomConsentService.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomConsentService.java @@ -4,6 +4,7 @@ import java.util.Map; import org.apache.commons.lang3.ObjectUtils; import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r4.model.Base; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Reference; import org.hl7.fhir.r4.model.Resource; @@ -119,9 +120,11 @@ private boolean isResourceValid(String resourceName, String patientId, private boolean isReferanceValid(Resource theResource, String patientRef, String refPropertyName) { try { - Reference ref = (Reference) theResource.getNamedProperty(refPropertyName).getValues().get(0); - if (ref.getReference().equals(patientRef)) { - return true; + List refList = theResource.getNamedProperty(refPropertyName).getValues(); + for (Base ref : refList) { + if (((Reference)ref).getReference().equals(patientRef)) { + return true; + } } return false; } catch (Exception e) { From 94231de6dd3a3a7c14679cdabb01d2c19174dbe6 Mon Sep 17 00:00:00 2001 From: shubhamparikh927 Date: Thu, 11 Nov 2021 16:51:50 +0530 Subject: [PATCH 164/200] Added verification of clientId from token --- .../starter/CustomAuthorizationInterceptor.java | 17 +++++++++-------- .../ca/uhn/fhir/jpa/starter/OAuth2Helper.java | 6 ++++++ 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java index d6193c75e41..3b953dab61f 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java @@ -96,14 +96,15 @@ private List authorizeOAuth(RequestDetails theRequest) throws Excepti publicKey = StringUtils.isEmpty(publicKey) ? oAuth2Helper.getJwtPublicKey(kid, OAUTH_URL) : publicKey; JWTVerifier verifier = oAuth2Helper.getJWTVerifier(jwt, publicKey); jwt = verifier.verify(token); - - if (theRequest.getRequestType().equals(RequestTypeEnum.DELETE)) { - if (oAuth2Helper.hasClientRole(jwt, OAUTH_CLIENT_ID, OAUTH_ADMIN_ROLE)) { - return allowAll(); - } - } else if (oAuth2Helper.hasClientRole(jwt, OAUTH_CLIENT_ID, OAUTH_USER_ROLE)) { - String patientId = getPatientFromToken(theRequest); - return StringUtils.isEmpty(patientId) ? allowAll() : allowForClaimResourceId(theRequest,patientId); + if (oAuth2Helper.verifyClientId(jwt, OAUTH_CLIENT_ID)) { + if (theRequest.getRequestType().equals(RequestTypeEnum.DELETE)) { + if (oAuth2Helper.hasClientRole(jwt, OAUTH_CLIENT_ID, OAUTH_ADMIN_ROLE)) { + return allowAll(); + } + } else if (oAuth2Helper.hasClientRole(jwt, OAUTH_CLIENT_ID, OAUTH_USER_ROLE)) { + String patientId = getPatientFromToken(theRequest); + return StringUtils.isEmpty(patientId) ? allowAll() : allowForClaimResourceId(theRequest,patientId); + } } } catch (TokenExpiredException e) { logger.info("Authorization failure - token has expired"); diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java index ada5b020910..0a96ab7bbc4 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java @@ -144,4 +144,10 @@ protected boolean canBeInPatientCompartment(String resourceType) { List compartmentList = data.getSearchParamsForCompartmentName("Patient"); return !compartmentList.isEmpty(); } + + public boolean verifyClientId(DecodedJWT jwt, String oauthClientId) { + Claim claim = jwt.getClaim("azp"); + String azp = claim.asString(); + return azp.equals(oauthClientId); + } } From efa86b42efbff62f2ae52c614ff01b255d928231 Mon Sep 17 00:00:00 2001 From: shubhamparikh927 Date: Thu, 25 Nov 2021 18:04:15 +0530 Subject: [PATCH 165/200] Added basic authentication --- .../uhn/fhir/jpa/starter/BasicAuthHelper.java | 12 +++++ .../CustomAuthorizationInterceptor.java | 47 +++++++++++++++---- .../CustomSearchNarrowingInterceptor.java | 14 +++--- .../ca/uhn/fhir/jpa/starter/OAuth2Helper.java | 11 +++-- 4 files changed, 65 insertions(+), 19 deletions(-) create mode 100644 src/main/java/ca/uhn/fhir/jpa/starter/BasicAuthHelper.java diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/BasicAuthHelper.java b/src/main/java/ca/uhn/fhir/jpa/starter/BasicAuthHelper.java new file mode 100644 index 00000000000..66d7db40ef8 --- /dev/null +++ b/src/main/java/ca/uhn/fhir/jpa/starter/BasicAuthHelper.java @@ -0,0 +1,12 @@ +package ca.uhn.fhir.jpa.starter; + +import java.util.Base64; + +public class BasicAuthHelper { + + public boolean isValid(String basicAuthUsername, String basicAuthPass, String basicAuthToken) { + String unEncodedUsernamePass = basicAuthUsername + ":" + basicAuthPass; + String encodedUsernamePass = Base64.getEncoder().encodeToString(unEncodedUsernamePass.getBytes()); + return encodedUsernamePass.equals(basicAuthToken); + } +} diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java index 3b953dab61f..7e739bea540 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java @@ -30,12 +30,17 @@ public class CustomAuthorizationInterceptor extends AuthorizationInterceptor { private static final String APIKEY_ENABLED = System.getenv("APIKEY_ENABLED"); private static final String APIKEY_HEADER = "x-api-key"; private static final String APIKEY = System.getenv("APIKEY"); - private static final String TOKEN_PREFIX = "BEARER "; + private static final String OAUTH_TOKEN_PREFIX = "BEARER "; private static final String OAUTH_USER_ROLE = System.getenv("OAUTH_USER_ROLE"); private static final String OAUTH_CLIENT_ID = System.getenv("OAUTH_CLIENT_ID"); private static final String OAUTH_ADMIN_ROLE = System.getenv("OAUTH_ADMIN_ROLE"); + private static final String BASIC_AUTH_ENABLED = System.getenv("BASIC_AUTH_ENABLED"); + private static final String BASIC_AUTH_USERNAME = System.getenv("BASIC_AUTH_USERNAME"); + private static final String BASIC_AUTH_PASS = System.getenv("BASIC_AUTH_PASS"); + private static final String BASIC_AUTH_TOKEN_PREFIX = "BASIC "; private static PublicKey publicKey = null; private static OAuth2Helper oAuth2Helper = new OAuth2Helper(); + private static BasicAuthHelper basicAuthHelper = new BasicAuthHelper(); @Override public List buildRuleList(RequestDetails theRequest) { @@ -45,12 +50,12 @@ public List buildRuleList(RequestDetails theRequest) { return allowAll(); } - if (!isOAuthEnabled() && !isApiKeyEnabled()) { - logger.warn("APIKEY and OAuth2 authentication are disabled"); + if (!isOAuthEnabled() && !isApiKeyEnabled() && !isBasicAuthEnabled()) { + logger.warn("APIKEY, BasicAuth and OAuth2 authentication are disabled"); return allowAll(); } - if (isOAuthEnabled() && isOAuthHeaderPresent(theRequest)) { + if (isOAuthEnabled() && oAuth2Helper.isOAuthHeaderPresent(theRequest)) { logger.info("Auhorizing via OAuth"); return authorizeOAuth(theRequest); } @@ -59,6 +64,10 @@ public List buildRuleList(RequestDetails theRequest) { logger.info("Auhorizing via X-API-KEY"); return authorizeApiKey(theRequest); } + if (isBasicAuthEnabled() && isBasicAuthHeaderPresent(theRequest)) { + logger.info("Auhorizing via BasicAuth"); + return authorizeBasicAuth(theRequest); + } } catch (Exception e) { logger.info("Unexpected authorization error", e); return denyAll(); @@ -83,12 +92,12 @@ private List authorizeOAuth(RequestDetails theRequest) throws Excepti return denyAll(); } - if (!token.toUpperCase().startsWith(TOKEN_PREFIX)) { + if (!token.toUpperCase().startsWith(OAUTH_TOKEN_PREFIX)) { logger.info("Authorization failure - invalid authorization header"); return denyAll(); } - token = token.substring(TOKEN_PREFIX.length()); + token = token.substring(OAUTH_TOKEN_PREFIX.length()); try { DecodedJWT jwt = JWT.decode(token); @@ -143,10 +152,14 @@ private String getPatientFromToken(RequestDetails theRequestDetails) { private Boolean isOAuthEnabled() { return ((OAUTH_ENABLED != null) && Boolean.parseBoolean(OAUTH_ENABLED)); } - - private Boolean isOAuthHeaderPresent(RequestDetails theRequest) { + + private Boolean isBasicAuthEnabled() { + return ((BASIC_AUTH_ENABLED != null) && Boolean.parseBoolean(BASIC_AUTH_ENABLED)); + } + + private Boolean isBasicAuthHeaderPresent(RequestDetails theRequest) { String token = theRequest.getHeader(HttpHeaders.AUTHORIZATION); - return (!StringUtils.isEmpty(token)); + return (!StringUtils.isEmpty(token) && token.toUpperCase().contains(BASIC_AUTH_TOKEN_PREFIX)); } private Boolean isApiKeyEnabled() { @@ -172,8 +185,22 @@ private List authorizeApiKey(RequestDetails theRequest) { logger.info("Authorization failure - invalid X-API-KEY header"); return denyAll(); } + + private List authorizeBasicAuth(RequestDetails theRequest) { + String basicAuthToken = theRequest.getHeader(HttpHeaders.AUTHORIZATION); + if (StringUtils.isEmpty(basicAuthToken)) { + logger.info("Authorization failure - missing authorization header"); + return denyAll(); + } + basicAuthToken = basicAuthToken.substring(BASIC_AUTH_TOKEN_PREFIX.length()); + if (basicAuthHelper.isValid(BASIC_AUTH_USERNAME, BASIC_AUTH_PASS, basicAuthToken)) { + return allowAll(); + } + logger.info("Authorization failure - invalid credentials"); + return denyAll(); + } public static String getTokenPrefix() { - return TOKEN_PREFIX; + return OAUTH_TOKEN_PREFIX; } } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomSearchNarrowingInterceptor.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomSearchNarrowingInterceptor.java index 0c21195d328..6cd68b27700 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/CustomSearchNarrowingInterceptor.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomSearchNarrowingInterceptor.java @@ -25,12 +25,14 @@ protected AuthorizedList buildAuthorizedList(RequestDetails theRequestDetails) { } private String getPatientFromToken(RequestDetails theRequestDetails) { - String token = theRequestDetails.getHeader("Authorization"); - if (token != null) { - token = token.substring(CustomAuthorizationInterceptor.getTokenPrefix().length()); - DecodedJWT jwt = JWT.decode(token); - String patRefId = oAuth2Helper.getPatientReferenceFromToken(jwt, OAUTH_CLAIM_NAME); - return patRefId; + if (oAuth2Helper.isOAuthHeaderPresent(theRequestDetails)) { + String token = theRequestDetails.getHeader("Authorization"); + if (token != null) { + token = token.substring(CustomAuthorizationInterceptor.getTokenPrefix().length()); + DecodedJWT jwt = JWT.decode(token); + String patRefId = oAuth2Helper.getPatientReferenceFromToken(jwt, OAUTH_CLAIM_NAME); + return patRefId; + } } return null; } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java index 0a96ab7bbc4..d5c64e091b0 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java @@ -10,17 +10,17 @@ import java.util.Base64; import java.util.HashMap; import java.util.List; - import org.apache.commons.lang3.ObjectUtils; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; +import org.springframework.util.StringUtils; import org.springframework.web.client.RestTemplate; - import com.auth0.jwt.JWT; import com.auth0.jwt.JWTVerifier; import com.auth0.jwt.algorithms.Algorithm; @@ -30,7 +30,7 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.context.RuntimeSearchParam; -import ca.uhn.fhir.rest.server.provider.ProviderConstants; +import ca.uhn.fhir.rest.api.server.RequestDetails; public class OAuth2Helper { private static final Logger logger = LoggerFactory.getLogger(OAuth2Helper.class); @@ -150,4 +150,9 @@ public boolean verifyClientId(DecodedJWT jwt, String oauthClientId) { String azp = claim.asString(); return azp.equals(oauthClientId); } + + public boolean isOAuthHeaderPresent(RequestDetails theRequest) { + String token = theRequest.getHeader(HttpHeaders.AUTHORIZATION); + return (!StringUtils.isEmpty(token) && token.toUpperCase().contains(CustomAuthorizationInterceptor.getTokenPrefix())); + } } From 0afd3f950b8456a12ad982c177b45a2eccaf0550 Mon Sep 17 00:00:00 2001 From: shubhamparikh927 Date: Fri, 26 Nov 2021 14:00:41 +0530 Subject: [PATCH 166/200] Code refactor --- .../uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java index 7e739bea540..c65d68b0258 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java @@ -51,7 +51,7 @@ public List buildRuleList(RequestDetails theRequest) { } if (!isOAuthEnabled() && !isApiKeyEnabled() && !isBasicAuthEnabled()) { - logger.warn("APIKEY, BasicAuth and OAuth2 authentication are disabled"); + logger.warn("APIKEY, basicAuth and OAuth2 authentication are disabled"); return allowAll(); } @@ -65,7 +65,7 @@ public List buildRuleList(RequestDetails theRequest) { return authorizeApiKey(theRequest); } if (isBasicAuthEnabled() && isBasicAuthHeaderPresent(theRequest)) { - logger.info("Auhorizing via BasicAuth"); + logger.info("Auhorizing via basic auth"); return authorizeBasicAuth(theRequest); } } catch (Exception e) { From 0359fa87eb6e83117ae35d3ec0d6e1999d6f0fdd Mon Sep 17 00:00:00 2001 From: shubhamparikh927 Date: Mon, 29 Nov 2021 11:56:02 +0530 Subject: [PATCH 167/200] Added username/password check while checking is basic auth enabled --- .../fhir/jpa/starter/CustomAuthorizationInterceptor.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java index c65d68b0258..03a2f8972a3 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java @@ -3,6 +3,7 @@ import java.security.PublicKey; import java.util.List; +import org.apache.commons.lang3.ObjectUtils; import org.hl7.fhir.r4.model.IdType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -154,7 +155,13 @@ private Boolean isOAuthEnabled() { } private Boolean isBasicAuthEnabled() { - return ((BASIC_AUTH_ENABLED != null) && Boolean.parseBoolean(BASIC_AUTH_ENABLED)); + if ((BASIC_AUTH_ENABLED != null) && Boolean.parseBoolean(BASIC_AUTH_ENABLED)) { + if (ObjectUtils.isEmpty(BASIC_AUTH_USERNAME) || ObjectUtils.isEmpty(BASIC_AUTH_PASS)) { + logger.warn("Using basic auth without setting username and password"); + } + return true; + } + return false; } private Boolean isBasicAuthHeaderPresent(RequestDetails theRequest) { From 76c20036f95b7712f238336299b5ab09fd3ccfee Mon Sep 17 00:00:00 2001 From: Hank Wallace Date: Thu, 10 Sep 2020 07:13:37 -0400 Subject: [PATCH 168/200] DEV-769: Update from upstream branch (#9) * Adding environment variables to override hapi properties file settings * Only override existing hapi properties with env ones * Start Release branch for 5.1.0 * Add property for multitenancy * Get test passing * Add repo * Bump to current release * Rework the JPA server class a bit * Updating Dockerfile to use context code instead of re-cloning the hapi-fhir-jpaserver-starter project. Still re-builds the base hapi libraries, though. * Adding .dockerignore file to improve the efficiency of docker builds * Added support for configurable client ID strategy * Bump mysql-connector-java from 8.0.11 to 8.0.16 Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.11 to 8.0.16. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.11...8.0.16) Signed-off-by: dependabot[bot] * Resolve some compile errors * Fix EMPI rules json * Have EmpiSettings actually return an EmpiSettings bean * Update readme * Remove dead space * Update property * Update README to include Docker info * Prepare for release * Merge master * Removing commands from Dockerfile that clone and build the core hapi-fhir libraries since it is not required for building hapi-fhir-jpaserver-starter; it pulls the core libraries from the maven repo regardless. * Combining base Dockerfile with main dockerfile so that there is a single image related to hapi-jpaserver-starter * Update Dockerfile Added caching of maven dependency resolving * Fix #122 - EMPI error on startup when EMPI not enabled Co-authored-by: Peter Micuch Co-authored-by: Vladimir Nemergut Co-authored-by: jamesagnew Co-authored-by: Sean McIlvenna Co-authored-by: jvi Co-authored-by: Sean McIlvenna Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Tadgh Co-authored-by: Tadgh Co-authored-by: Ken Stevens Co-authored-by: Jens Kristian Villadsen <46567685+jvitrifork@users.noreply.github.com> --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 973fd28fb1d..3385768c22d 100644 --- a/README.md +++ b/README.md @@ -507,6 +507,10 @@ Set `empi.enabled=true` in the [hapi.properties](https://github.com/hapifhir/hap Set `empi.enabled=true` in the [hapi.properties](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/hapi.properties) file to enable EMPI on this server. The EMPI matching rules are configured in [empi-rules.json](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/empi-rules.json). The rules in this example file should be replaced with actual matching rules appropriate to your data. Note that EMPI relies on subscriptions, so for EMPI to work, subscriptions must be enabled. +## Enabling EMPI + +Set `empi.enabled=true` in the [hapi.properties](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/hapi.properties) file to enable EMPI on this server. The EMPI matching rules are configured in [empi-rules.json](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/empi-rules.json). The rules in this example file should be replaced with actual matching rules appropriate to your data. Note that EMPI relies on subscriptions, so for EMPI to work, subscriptions must be enabled. + ## Using Elasticsearch By default, the server will use embedded lucene indexes for terminology and fulltext indexing purposes. You can switch this to using lucene by editing the properties in [application.yaml](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/application.yaml) From 01c323e4ffab393f7e134998f319fbd16d94c34f Mon Sep 17 00:00:00 2001 From: Shubham Parikh Date: Fri, 5 Feb 2021 18:27:40 +0530 Subject: [PATCH 169/200] Updated docker compose file with required env variables --- docker-compose.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/docker-compose.yml b/docker-compose.yml index 0fe0e457bec..130f2584e85 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,7 @@ version: "3" services: hapi-fhir-jpaserver-start: + network_mode: host build: . container_name: hapi-fhir-jpaserver-start restart: on-failure From e2a64d81f1f33a32079da460c635a987a5a83b83 Mon Sep 17 00:00:00 2001 From: Hank Wallace Date: Thu, 10 Sep 2020 07:13:37 -0400 Subject: [PATCH 170/200] DEV-769: Update from upstream branch (#9) * Adding environment variables to override hapi properties file settings * Only override existing hapi properties with env ones * Start Release branch for 5.1.0 * Add property for multitenancy * Get test passing * Add repo * Bump to current release * Rework the JPA server class a bit * Updating Dockerfile to use context code instead of re-cloning the hapi-fhir-jpaserver-starter project. Still re-builds the base hapi libraries, though. * Adding .dockerignore file to improve the efficiency of docker builds * Added support for configurable client ID strategy * Bump mysql-connector-java from 8.0.11 to 8.0.16 Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.11 to 8.0.16. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.11...8.0.16) Signed-off-by: dependabot[bot] * Resolve some compile errors * Fix EMPI rules json * Have EmpiSettings actually return an EmpiSettings bean * Update readme * Remove dead space * Update property * Update README to include Docker info * Prepare for release * Merge master * Removing commands from Dockerfile that clone and build the core hapi-fhir libraries since it is not required for building hapi-fhir-jpaserver-starter; it pulls the core libraries from the maven repo regardless. * Combining base Dockerfile with main dockerfile so that there is a single image related to hapi-jpaserver-starter * Update Dockerfile Added caching of maven dependency resolving * Fix #122 - EMPI error on startup when EMPI not enabled Co-authored-by: Peter Micuch Co-authored-by: Vladimir Nemergut Co-authored-by: jamesagnew Co-authored-by: Sean McIlvenna Co-authored-by: jvi Co-authored-by: Sean McIlvenna Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Tadgh Co-authored-by: Tadgh Co-authored-by: Ken Stevens Co-authored-by: Jens Kristian Villadsen <46567685+jvitrifork@users.noreply.github.com> --- README.md | 55 +++ .../jpa/starter/BaseJpaRestfulServer.java | 337 ++++++++++++++++++ src/main/resources/logback.xml | 2 +- .../jpa/starter/ExampleServerDstu2IT.java | 2 +- .../jpa/starter/ExampleServerDstu3IT.java | 2 +- .../fhir/jpa/starter/ExampleServerR4IT.java | 4 +- .../fhir/jpa/starter/ExampleServerR5IT.java | 2 +- .../jpa/starter/MultitenantServerR4IT.java | 3 +- 8 files changed, 399 insertions(+), 8 deletions(-) create mode 100644 src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java diff --git a/README.md b/README.md index 3385768c22d..3e514e08306 100644 --- a/README.md +++ b/README.md @@ -184,6 +184,57 @@ volumes: external: true ``` +## 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 entirely depends on your environment requirements. At least, the following 4 ways are supported: @@ -511,6 +562,10 @@ Set `empi.enabled=true` in the [hapi.properties](https://github.com/hapifhir/hap Set `empi.enabled=true` in the [hapi.properties](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/hapi.properties) file to enable EMPI on this server. The EMPI matching rules are configured in [empi-rules.json](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/empi-rules.json). The rules in this example file should be replaced with actual matching rules appropriate to your data. Note that EMPI relies on subscriptions, so for EMPI to work, subscriptions must be enabled. +## Enabling EMPI + +Set `empi.enabled=true` in the [hapi.properties](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/hapi.properties) file to enable EMPI on this server. The EMPI matching rules are configured in [empi-rules.json](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/empi-rules.json). The rules in this example file should be replaced with actual matching rules appropriate to your data. Note that EMPI relies on subscriptions, so for EMPI to work, subscriptions must be enabled. + ## Using Elasticsearch By default, the server will use embedded lucene indexes for terminology and fulltext indexing purposes. You can switch this to using lucene by editing the properties in [application.yaml](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/application.yaml) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java new file mode 100644 index 00000000000..5b482c84f33 --- /dev/null +++ b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java @@ -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.binstore.BinaryStorageInterceptor; +import ca.uhn.fhir.jpa.bulk.provider.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.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 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 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 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 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 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 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 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()); + } + } + +} diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index 9625c50de37..d2dd0a1d894 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -20,4 +20,4 @@ - + \ No newline at end of file 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 e20aff8ff8a..c3bbad60bdc 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu2IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu2IT.java @@ -53,4 +53,4 @@ void beforeEach() { ourClient = ourCtx.newRestfulGenericClient(ourServerBase); ourClient.registerInterceptor(new LoggingInterceptor(true)); } -} +} \ No newline at end of file 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 926a5003841..a790ce31b24 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu3IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu3IT.java @@ -210,4 +210,4 @@ public void testWebsocketSubscription() throws Exception { ourClient.delete().resourceById(mySubscriptionId).execute(); } -} +} \ No newline at end of file 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 3fb52f4040b..66d949d1ae8 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java @@ -13,8 +13,6 @@ 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.Person; -import org.hl7.fhir.r4.model.Reference; import org.hl7.fhir.r4.model.Subscription; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Order; @@ -216,4 +214,4 @@ void beforeEach() { return activeSubscriptionCount() == 2; // 2 subscription based on mdm-rules.json }); } -} +} \ No newline at end of file diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR5IT.java b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR5IT.java index ddbd6a77ac1..ffeee8e3960 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR5IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR5IT.java @@ -135,4 +135,4 @@ void beforeEach() { ourClient = ourCtx.newRestfulGenericClient(ourServerBase); ourClient.registerInterceptor(new LoggingInterceptor(true)); } -} +} \ No newline at end of file diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/MultitenantServerR4IT.java b/src/test/java/ca/uhn/fhir/jpa/starter/MultitenantServerR4IT.java index 743c38e2b17..e9daae17035 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/MultitenantServerR4IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/MultitenantServerR4IT.java @@ -68,6 +68,7 @@ public void testCreateAndReadInTenantA() { @Test public void testCreateAndReadInTenantB() { + // Create tenant A ourClientTenantInterceptor.setTenantId("DEFAULT"); ourClient @@ -102,4 +103,4 @@ void beforeEach() { ourClient.registerInterceptor(new LoggingInterceptor(true)); ourClient.registerInterceptor(ourClientTenantInterceptor); } -} +} \ No newline at end of file From 011f96f025ad91c071c72dc3652b6cf964b59ab2 Mon Sep 17 00:00:00 2001 From: Shubham Parikh Date: Wed, 13 Jan 2021 13:50:33 +0530 Subject: [PATCH 171/200] BT-80 Updated code to allow header x-api-key --- src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java index 5b482c84f33..d967656bdb2 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java @@ -235,6 +235,7 @@ protected void initialize() throws ServletException { config.addAllowedHeader(HttpHeaders.CONTENT_TYPE); config.addAllowedHeader(HttpHeaders.AUTHORIZATION); config.addAllowedHeader(HttpHeaders.CACHE_CONTROL); + config.addAllowedHeader("x-api-key"); config.addAllowedHeader("x-fhir-starter"); config.addAllowedHeader("X-Requested-With"); config.addAllowedHeader("Prefer"); From e1d8e14065eb79314a9e69bb6a97c91cf01153cf Mon Sep 17 00:00:00 2001 From: Shubham Parikh Date: Fri, 19 Feb 2021 18:31:01 +0530 Subject: [PATCH 172/200] BT-140 set legacy sql builder based on hibernate to true --- src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java index d967656bdb2..490e13ecd48 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java @@ -62,7 +62,7 @@ public class BaseJpaRestfulServer extends RestfulServer { @Override protected void initialize() throws ServletException { super.initialize(); - + daoConfig.setUseLegacySearchBuilder(true); /* * Create a FhirContext object that uses the version of FHIR * specified in the properties file. From 828364c533ad8d711619e2f2937d796d290e9996 Mon Sep 17 00:00:00 2001 From: Shubham Parikh Date: Fri, 19 Feb 2021 19:01:55 +0530 Subject: [PATCH 173/200] BT-96 Fixed spacing --- .classpath | 39 +++++++++++++++++++++++++++++++++++++++ .project | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 .classpath create mode 100644 .project diff --git a/.classpath b/.classpath new file mode 100644 index 00000000000..54347b7b36a --- /dev/null +++ b/.classpath @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.project b/.project new file mode 100644 index 00000000000..773a7c66e48 --- /dev/null +++ b/.project @@ -0,0 +1,37 @@ + + + hapi-fhir-jpaserver-starter + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.wst.common.project.facet.core.builder + + + + + org.eclipse.wst.validation.validationbuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jem.workbench.JavaEMFNature + org.eclipse.wst.common.modulecore.ModuleCoreNature + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + org.eclipse.wst.common.project.facet.core.nature + org.eclipse.wst.jsdt.core.jsNature + + From 8123f954a67f75d5b0bddeff6e11d89f553bb603 Mon Sep 17 00:00:00 2001 From: Shubham Parikh Date: Fri, 19 Feb 2021 19:03:16 +0530 Subject: [PATCH 174/200] BT-96 Removed unwanted filed --- .classpath | 39 --------------------------------------- .project | 37 ------------------------------------- 2 files changed, 76 deletions(-) delete mode 100644 .classpath delete mode 100644 .project diff --git a/.classpath b/.classpath deleted file mode 100644 index 54347b7b36a..00000000000 --- a/.classpath +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/.project b/.project deleted file mode 100644 index 773a7c66e48..00000000000 --- a/.project +++ /dev/null @@ -1,37 +0,0 @@ - - - hapi-fhir-jpaserver-starter - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.wst.common.project.facet.core.builder - - - - - org.eclipse.wst.validation.validationbuilder - - - - - org.eclipse.m2e.core.maven2Builder - - - - - - org.eclipse.jem.workbench.JavaEMFNature - org.eclipse.wst.common.modulecore.ModuleCoreNature - org.eclipse.jdt.core.javanature - org.eclipse.m2e.core.maven2Nature - org.eclipse.wst.common.project.facet.core.nature - org.eclipse.wst.jsdt.core.jsNature - - From 5ff5c070390d0179a78f196b3332282f857752d9 Mon Sep 17 00:00:00 2001 From: shubhamparikh927 Date: Wed, 24 Feb 2021 12:45:42 +0530 Subject: [PATCH 175/200] Changes in code as per PR suggestions --- src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java index d5c64e091b0..7a741d2c038 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java @@ -16,10 +16,8 @@ import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; -import org.springframework.util.StringUtils; import org.springframework.web.client.RestTemplate; import com.auth0.jwt.JWT; import com.auth0.jwt.JWTVerifier; From fb24b69c11e5ef925eff04d70328dfeb1eaa07d9 Mon Sep 17 00:00:00 2001 From: shubhamparikh927 Date: Tue, 9 Mar 2021 17:38:41 +0530 Subject: [PATCH 176/200] Added user role validation in Oauth --- .../uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java | 3 +++ src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java | 1 + 2 files changed, 4 insertions(+) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java index 03a2f8972a3..c3a8f60e459 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java @@ -1,6 +1,9 @@ package ca.uhn.fhir.jpa.starter; import java.security.PublicKey; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.List; import org.apache.commons.lang3.ObjectUtils; diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java index 7a741d2c038..335adf83107 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java @@ -32,6 +32,7 @@ public class OAuth2Helper { private static final Logger logger = LoggerFactory.getLogger(OAuth2Helper.class); + private static final String CLIENT = System.getenv("OAUTH_CLIENT_NAME"); protected String getJwtKeyId(String token) { String tokenHeader = token.split("\\.")[0]; From d256b2142cb89b9205dcb9edcefd70b942ab5c68 Mon Sep 17 00:00:00 2001 From: shubhamparikh927 Date: Tue, 9 Mar 2021 17:45:40 +0530 Subject: [PATCH 177/200] code refactor --- src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java index 335adf83107..99ff0b40a05 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java @@ -32,7 +32,7 @@ public class OAuth2Helper { private static final Logger logger = LoggerFactory.getLogger(OAuth2Helper.class); - private static final String CLIENT = System.getenv("OAUTH_CLIENT_NAME"); + private static final String CLIENT_NAME = System.getenv("OAUTH_CLIENT_NAME"); protected String getJwtKeyId(String token) { String tokenHeader = token.split("\\.")[0]; From 00764fd5502c2507a633206eb57841f4cb9fd8af Mon Sep 17 00:00:00 2001 From: shubhamparikh927 Date: Thu, 11 Mar 2021 18:11:44 +0530 Subject: [PATCH 178/200] Updated code for using single oauth user role --- .../uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java index c3a8f60e459..03a2f8972a3 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java @@ -1,9 +1,6 @@ package ca.uhn.fhir.jpa.starter; import java.security.PublicKey; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; import java.util.List; import org.apache.commons.lang3.ObjectUtils; From 867f42b07c976a55f3817c1a6ce5dbcf84d898e9 Mon Sep 17 00:00:00 2001 From: shubhamparikh927 Date: Fri, 12 Mar 2021 17:06:38 +0530 Subject: [PATCH 179/200] Updated code as per PR suggestions --- src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java index 99ff0b40a05..7a741d2c038 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java @@ -32,7 +32,6 @@ public class OAuth2Helper { private static final Logger logger = LoggerFactory.getLogger(OAuth2Helper.class); - private static final String CLIENT_NAME = System.getenv("OAUTH_CLIENT_NAME"); protected String getJwtKeyId(String token) { String tokenHeader = token.split("\\.")[0]; From b0f223861d870f0493076d0e7222e684df716d85 Mon Sep 17 00:00:00 2001 From: shubhamparikh927 Date: Fri, 19 Mar 2021 14:50:27 +0530 Subject: [PATCH 180/200] Updated code as pr PR siggestions --- docker-compose.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 130f2584e85..0fe0e457bec 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,6 @@ version: "3" services: hapi-fhir-jpaserver-start: - network_mode: host build: . container_name: hapi-fhir-jpaserver-start restart: on-failure From 9ba8b1aad6ba1c449b8a46ff5fc412bdcebf739d Mon Sep 17 00:00:00 2001 From: shubhamparikh927 Date: Thu, 25 Mar 2021 15:20:57 +0530 Subject: [PATCH 181/200] Added custom search narrowing filter --- src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java index 7a741d2c038..0313727dd8b 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java @@ -119,6 +119,12 @@ protected Boolean hasClientRole(DecodedJWT jwt, String clientId, String userRole ArrayList roles = clientMap.getOrDefault("roles", new ArrayList()); return roles.contains(userRole); } + + protected String getPatientReferenceFromToken(DecodedJWT jwt) { + Claim claim = jwt.getClaim("Patient"); + String patientRef = claim.as(String.class); + return patientRef; + } protected String getPatientReferenceFromToken(DecodedJWT jwt, String claimName) { if (claimName != null) { From 79df30b3d6bca3d6b4a9fcb665d3e6cb2a3c0b15 Mon Sep 17 00:00:00 2001 From: shubhamparikh927 Date: Tue, 30 Mar 2021 16:04:26 +0530 Subject: [PATCH 182/200] Updated code to get claim name from environment --- .../java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java index 0313727dd8b..30b6a549f54 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java @@ -119,12 +119,15 @@ protected Boolean hasClientRole(DecodedJWT jwt, String clientId, String userRole ArrayList roles = clientMap.getOrDefault("roles", new ArrayList()); return roles.contains(userRole); } - - protected String getPatientReferenceFromToken(DecodedJWT jwt) { - Claim claim = jwt.getClaim("Patient"); - String patientRef = claim.as(String.class); - return patientRef; + + protected String getPatientReferenceFromToken(DecodedJWT jwt, String claimName) { + if (claimName != null) { + Claim claim = jwt.getClaim(claimName); + String patientRef = claim.as(String.class); + return patientRef; } + return null; + } protected String getPatientReferenceFromToken(DecodedJWT jwt, String claimName) { if (claimName != null) { From f7227575d9088faeb0d72395f381d857f64b48e9 Mon Sep 17 00:00:00 2001 From: shubhamparikh927 Date: Mon, 5 Apr 2021 22:09:21 +0530 Subject: [PATCH 183/200] Added rule builder to filter resources --- .../starter/CustomAuthorizationInterceptor.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java index 03a2f8972a3..a741a0e6ca4 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java @@ -86,6 +86,23 @@ private List allowAll() { return new RuleBuilder().allowAll().build(); } + private List allowForClaimResourceId(RequestDetails theRequestDetails,String patientId) { + return new RuleBuilder().allow().read().allResources() + .inCompartment("Patient", new IdType("Patient", patientId)).andThen().allow().write().allResources() + .inCompartment("Patient", new IdType("Patient", patientId)).andThen().denyAll().build(); + } + + private String getPatientFromToken(RequestDetails theRequestDetails) { + String token = theRequestDetails.getHeader("Authorization"); + if (token != null) { + token = token.substring(CustomAuthorizationInterceptor.getTokenPrefix().length()); + DecodedJWT jwt = JWT.decode(token); + String patRefId = oAuth2Helper.getPatientReferenceFromToken(jwt, "patient"); + return patRefId; + } + return null; + } + private List authorizeOAuth(RequestDetails theRequest) throws Exception { String token = theRequest.getHeader(HttpHeaders.AUTHORIZATION); if (StringUtils.isEmpty(token)) { From a1251dda89b420254fd2cf5e7e1d8dd7b9b6db16 Mon Sep 17 00:00:00 2001 From: shubhamparikh927 Date: Tue, 6 Apr 2021 17:05:21 +0530 Subject: [PATCH 184/200] code refactor --- .../starter/CustomAuthorizationInterceptor.java | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java index a741a0e6ca4..03a2f8972a3 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java @@ -86,23 +86,6 @@ private List allowAll() { return new RuleBuilder().allowAll().build(); } - private List allowForClaimResourceId(RequestDetails theRequestDetails,String patientId) { - return new RuleBuilder().allow().read().allResources() - .inCompartment("Patient", new IdType("Patient", patientId)).andThen().allow().write().allResources() - .inCompartment("Patient", new IdType("Patient", patientId)).andThen().denyAll().build(); - } - - private String getPatientFromToken(RequestDetails theRequestDetails) { - String token = theRequestDetails.getHeader("Authorization"); - if (token != null) { - token = token.substring(CustomAuthorizationInterceptor.getTokenPrefix().length()); - DecodedJWT jwt = JWT.decode(token); - String patRefId = oAuth2Helper.getPatientReferenceFromToken(jwt, "patient"); - return patRefId; - } - return null; - } - private List authorizeOAuth(RequestDetails theRequest) throws Exception { String token = theRequest.getHeader(HttpHeaders.AUTHORIZATION); if (StringUtils.isEmpty(token)) { From f3e3222fc72d2a566faff673878442743d582eb3 Mon Sep 17 00:00:00 2001 From: shubhamparikh927 Date: Thu, 25 Nov 2021 18:04:15 +0530 Subject: [PATCH 185/200] Added basic authentication --- src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java index 30b6a549f54..6cc0407d02a 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java @@ -16,8 +16,10 @@ import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; +import org.springframework.util.StringUtils; import org.springframework.web.client.RestTemplate; import com.auth0.jwt.JWT; import com.auth0.jwt.JWTVerifier; From 16333a7229da7357a2273a0798fd38c967669fc0 Mon Sep 17 00:00:00 2001 From: shubhamparikh927 Date: Wed, 29 Dec 2021 15:07:48 +0530 Subject: [PATCH 186/200] Added hibernate physical_naming_strategy to use proper table names. --- src/main/resources/application-custom.yaml | 158 +++++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 src/main/resources/application-custom.yaml diff --git a/src/main/resources/application-custom.yaml b/src/main/resources/application-custom.yaml new file mode 100644 index 00000000000..4f1686c952f --- /dev/null +++ b/src/main/resources/application-custom.yaml @@ -0,0 +1,158 @@ +spring: + datasource: + url: ${datasource.url} + username: ${datasource.username} + password: ${datasource.password} + driverClassName: ${datasource.driver} + max-active: 15 + jpa: + properties: + hibernate.dialect: ${hibernate.dialect} + hibernate.format_sql: false + hibernate.show_sql: false + hibernate.hbm2ddl.auto: update + hibernate.jdbc.batch_size: 20 + hibernate.cache.use_query_cache: false + hibernate.cache.use_second_level_cache: false + hibernate.cache.use_structured_entries: false + hibernate.cache.use_minimal_puts: false +### These settings will enable fulltext search with lucene + hibernate.search.enabled: true + hibernate.search.backend.type: lucene + hibernate.search.backend.analysis.configurer: ca.uhn.fhir.jpa.search.HapiLuceneAnalysisConfigurer + hibernate.search.backend.directory.type: local-filesystem + hibernate.search.backend.directory.root: target/lucenefiles + hibernate.search.backend.lucene_version: lucene_current + hibernate.physical_naming_strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl + batch: + job: + enabled: false + main: +# TODO 5.6.0 -> Prevent duplicate bean definitions in the Spring batch config in HAPI: see: + allow-bean-definition-overriding: true +hapi: + fhir: + ### This is the FHIR version. Choose between, DSTU2, DSTU3, R4 or R5 + fhir_version: ${fhir_version} +### enable to use the ApacheProxyAddressStrategy which uses X-Forwarded-* headers +### to determine the FHIR server address +# use_apache_address_strategy: false +### forces the use of the https:// protocol for the returned server address. +### alternatively, it may be set using the X-Forwarded-Proto header. +# use_apache_address_strategy_https: false +### enable to set the Server URL +# server_address: http://hapi.fhir.org/baseR4 +# defer_indexing_for_codesystems_of_size: 101 + #implementationguides: + #example from registry (packages.fhir.org) + #swiss: + #name: swiss.mednet.fhir + #version: 0.8.0 + #example not from registry + #ips_1_0_0: + #url: https://build.fhir.org/ig/HL7/fhir-ips/package.tgz + #name: hl7.fhir.uv.ips + #version: 1.0.0 + + #supported_resource_types: + # - Patient + # - Observation + allow_cascading_deletes: true + allow_contains_searches: true + allow_external_references: true + allow_multiple_delete: true + allow_override_default_search_params: true + allow_placeholder_references: true + auto_create_placeholder_reference_targets: false +# cql_enabled: true + default_encoding: JSON +# default_pretty_print: true + default_page_size: 20 +# delete_expunge_enabled: true +# enable_repository_validating_interceptor: false + enable_index_missing_fields: false +# enable_index_contained_resource: false + enforce_referential_integrity_on_delete: false + enforce_referential_integrity_on_write: false + etag_support_enabled: true + expunge_enabled: true +# daoconfig_client_id_strategy: null +# client_id_strategy: ALPHANUMERIC + fhirpath_interceptor_enabled: true + filter_search_enabled: true + graphql_enabled: true +# narrative_enabled: true +# mdm_enabled: true +# partitioning: +# allow_references_across_partitions: false +# partitioning_include_in_search_hashes: false + cors: + allow_Credentials: true + # These are allowed_origin patterns, see: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/cors/CorsConfiguration.html#setAllowedOriginPatterns-java.util.List- + allowed_origin: + - '*' + + # Search coordinator thread pool sizes + search-coord-core-pool-size: 20 + search-coord-max-pool-size: 100 + search-coord-queue-capacity: 200 + +# logger: +# error_format: 'ERROR - ${requestVerb} ${requestUrl}' +# format: >- +# Path[${servletPath}] Source[${requestHeader.x-forwarded-for}] +# Operation[${operationType} ${operationName} ${idOrResourceName}] +# UA[${requestHeader.user-agent}] Params[${requestParameters}] +# ResponseEncoding[${responseEncodingNoDefault}] +# log_exceptions: true +# name: fhirtest.access + max_binary_size: 104857600 + max_page_size: 200 + retain_cached_searches_mins: 60 + reuse_cached_search_results_millis: ${reuse_cached_search_results_millis} + tester: + home: + name: Local Tester + server_address: ${server_address} + refuse_to_fetch_third_party_urls: false + fhir_version: ${fhir_version} + global: + name: Global Tester + server_address: "http://hapi.fhir.org/baseR4" + refuse_to_fetch_third_party_urls: false + fhir_version: ${fhir_version} + validation: + requests_enabled: false + responses_enabled: false + binary_storage_enabled: true + bulk_export_enabled: true + subscription: + resthook_enabled: ${subscription.resthook.enabled} + websocket_enabled: ${subscription.websocket.enabled} + email: + from: some@test.com + host: google.com + port: + username: + password: +# auth: +# startTlsEnable: +# startTlsRequired: +# quitWait: +# lastn_enabled: true +### This is configuration for normalized quantity serach level default is 0 +### 0: NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED - default +### 1: NORMALIZED_QUANTITY_STORAGE_SUPPORTED +### 2: NORMALIZED_QUANTITY_SEARCH_SUPPORTED +# normalized_quantity_search_level: 2 +#elasticsearch: +# debug: +# pretty_print_json_log: false +# refresh_after_write: false +# enabled: false +# password: SomePassword +# required_index_status: YELLOW +# rest_url: 'localhost:9200' +# protocol: 'http' +# schema_management_strategy: CREATE +# username: SomeUsername From 00942135785109e03fda00f104b26e6c9d6b9015 Mon Sep 17 00:00:00 2001 From: vpanhale Date: Tue, 1 Mar 2022 16:13:02 +0530 Subject: [PATCH 187/200] Removed client id validation from oauth token --- .../fhir/jpa/starter/CustomAuthorizationInterceptor.java | 4 +--- src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java | 8 +------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java index 03a2f8972a3..e3fd8bfc7c1 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java @@ -105,8 +105,7 @@ private List authorizeOAuth(RequestDetails theRequest) throws Excepti String kid = oAuth2Helper.getJwtKeyId(token); publicKey = StringUtils.isEmpty(publicKey) ? oAuth2Helper.getJwtPublicKey(kid, OAUTH_URL) : publicKey; JWTVerifier verifier = oAuth2Helper.getJWTVerifier(jwt, publicKey); - jwt = verifier.verify(token); - if (oAuth2Helper.verifyClientId(jwt, OAUTH_CLIENT_ID)) { + jwt = verifier.verify(token); if (theRequest.getRequestType().equals(RequestTypeEnum.DELETE)) { if (oAuth2Helper.hasClientRole(jwt, OAUTH_CLIENT_ID, OAUTH_ADMIN_ROLE)) { return allowAll(); @@ -115,7 +114,6 @@ private List authorizeOAuth(RequestDetails theRequest) throws Excepti String patientId = getPatientFromToken(theRequest); return StringUtils.isEmpty(patientId) ? allowAll() : allowForClaimResourceId(theRequest,patientId); } - } } catch (TokenExpiredException e) { logger.info("Authorization failure - token has expired"); } catch (Exception e) { diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java index 6cc0407d02a..2c1e06aa815 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java @@ -153,13 +153,7 @@ protected boolean canBeInPatientCompartment(String resourceType) { List compartmentList = data.getSearchParamsForCompartmentName("Patient"); return !compartmentList.isEmpty(); } - - public boolean verifyClientId(DecodedJWT jwt, String oauthClientId) { - Claim claim = jwt.getClaim("azp"); - String azp = claim.asString(); - return azp.equals(oauthClientId); - } - + public boolean isOAuthHeaderPresent(RequestDetails theRequest) { String token = theRequest.getHeader(HttpHeaders.AUTHORIZATION); return (!StringUtils.isEmpty(token) && token.toUpperCase().contains(CustomAuthorizationInterceptor.getTokenPrefix())); From 01ff44fb8936111a865ae2f2a5ab168071a85419 Mon Sep 17 00:00:00 2001 From: vpanhale Date: Wed, 2 Mar 2022 13:40:13 +0530 Subject: [PATCH 188/200] fixed indentation --- .../uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java index e3fd8bfc7c1..cd3b2a146b1 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java @@ -114,6 +114,10 @@ private List authorizeOAuth(RequestDetails theRequest) throws Excepti String patientId = getPatientFromToken(theRequest); return StringUtils.isEmpty(patientId) ? allowAll() : allowForClaimResourceId(theRequest,patientId); } + } else if (oAuth2Helper.hasClientRole(jwt, OAUTH_CLIENT_ID, OAUTH_USER_ROLE)) { + String patientId = getPatientFromToken(theRequest); + return ObjectUtils.isEmpty(patientId) ? allowAll() : allowForClaimResourceId(theRequest,patientId); + } } catch (TokenExpiredException e) { logger.info("Authorization failure - token has expired"); } catch (Exception e) { From 326cb7c7520dbb57de60119593cf4baba1efd099 Mon Sep 17 00:00:00 2001 From: Shubham Date: Tue, 28 Jun 2022 19:48:04 +0530 Subject: [PATCH 189/200] PT-669 Updated dockerfile to use user dir path for build --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 89ada58c6cc..a50d92b0c19 100644 --- a/Dockerfile +++ b/Dockerfile @@ -46,4 +46,4 @@ WORKDIR /app COPY --chown=nonroot:nonroot --from=build-distroless /app /app COPY --chown=nonroot:nonroot --from=build-hapi /tmp/hapi-fhir-jpaserver-starter/opentelemetry-javaagent.jar /app -CMD ["/app/main.war"] +CMD ["/app/main.war"] \ No newline at end of file From 1b124e40b35ef61868547893d8db5b257155a8e8 Mon Sep 17 00:00:00 2001 From: Shubham Parikh Date: Fri, 2 Dec 2022 15:50:49 +0530 Subject: [PATCH 190/200] Sync 6.1.0 (#38) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Bump dep version * Remove javamail and replace with simple-java-mail * WIP * Remove print * Remove bean override * Bump version * Add broken test * Update for new style of container bean * Bump for new version * Update to 5.6.0-PRE7_NIH-SNAPSHOT and activate advanced index * Disable default flyway processing * Add local_base_urls configuration to feed DaoConfig.setTreatBaseUrlsAsLocal() * Added OpenAPI / Swagger option * Upgraded to 5.6.0 Subscription tests fail ... * Bumped version of Spring Boot in order to fix same issue as https://github.com/Haulmont/jmix-security/issues/90 * Update application.yaml See https://github.com/hapifhir/hapi-fhir-jpaserver-starter/issues/292 * documented use of Values.extraEnv * added options for specifying a PodDisruptionBudget * simplified chart release workflow the Ubuntu runner base image already includes Helm 3.7.0 * Support HTTPS * Use default application.yaml * registering ValueSetOperationProvider * Upgrade to 5.7.0-PRE4-SNAPSHOT for testing * Share elasticsearch configuration * Fixed compile issues * Fix misconfiguration modelConfig is part of DaoConfig and should not have a separate lifecycle. * Bump to 5.7.0-PRE8-SNAPSHOT * Typo fix in README * Add configuration flag to enable storing of resources in lucene index (#304) * Add configuration flag to enable storing of resources in lucene index * Fix build issue * Fix code review suggestions Co-authored-by: Jaison B * Add ES native aggregation builder for lastN * Revert "Add ES native aggregation builder for lastN" This reverts commit 5312f78b956d4ad4863580035972263586d616c3. * bump version * Update for 5.7.x changes * Fix h2 dialect, replace mail dep * Bump postgresql from 42.2.23 to 42.2.25 Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.2.23 to 42.2.25. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.2.23...REL42.2.25) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production ... Signed-off-by: dependabot[bot] * bump Hapi PRE version * Bump to Hapi 6.0-SNAPSHOT and register the ValueSet provider. * Version bump to 5.7.0 and a few other components now draw the version from parent * Update application.yaml Momentarily added `allow-circular-references: true` * bump to real version * Bump version * Remove search coord thread pool * Added instructions about removing Hibernate dialect To fix the problem raised in [this](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/issues/318) issue. * Bump HAPI-FHIR version * Move to newlines * fix to work with latest hapi-fhir * Feature/update docker support (#319) * Updated docker image according to discussion on https://github.com/hapifhir/hapi-fhir-jpaserver-starter/pull/305 * Added doc * Added corrections according to comments * Update Dockerfile * Update build-images.yaml Updated to default to distroless * add dao_scheduling_enabled configuration property (#324) * added reindexProvider to Config (#326) * Update application.yaml * Update application.yaml Roll back - mistake from my side * Bump hapi-fhir version to 'PRE5' (#329) * Add actuator * Bump to PRE8 * disable springboot actuator endpoints other than 'health' for security reasons (#338) Co-authored-by: Craig McClendon * Bump to PRE9 * Update application.yaml (#345) * add support for ms sql server (#347) * Fix comments in Demo that lead to 404 (#348) Co-authored-by: dotasek * updated helm chart to use v5.7.0 and latest PostgreSQL sub-chart (#346) * Update application.yaml Reverted to sane defaults * Bump to PRE10 * Updated to HAPI FHIR version 5.7.2 (#349) * Revert accidental default activation of experimental lucene indexing * Add disclaimer for advanced_lucene_indexing added warning to advanced_lucene_indexing: false property * Bump to hapi PRE11 * Expose Prometheus metrics (#355) * Add: of-type modifier option in application.yaml (#363) Co-authored-by: Alejandro Medina * Fix applying supported_resource_types option with list that already includes SearchParameter (#365) * Bump pom and minimum java version * Bump ES version * hibernate search application properties updates. * make lastN test pass * Remove value set provider as it causes a boot failure without lucene * Fix reindex provider * Bump java version for test * Bump to pre-01 * fix build * Re-add valuesetoperation provider * Disable lucene by default * Re-add valueset operation provider * adding smoke test files * adding base documentation * wip * Update src/test/smoketest/SMOKE_TEST.md Co-authored-by: Ken Stevens * Update src/test/smoketest/SMOKE_TEST.md Co-authored-by: Ken Stevens * Update src/test/smoketest/SMOKE_TEST.md Co-authored-by: Ken Stevens * bumping to non-snapshot version (#377) * updated helm chart to use latest v6.0.1 version of the image (#382) * updated helm chart to use latest v6.0.1 version of the image * updated workflow to run against multiple k8s versions * Tracking branch for 6.1 pre-releases. * add BinaryAccessProvider to BaseJpaRestfulServer * Update to 6.1.0-PRE3-SNAPSHOT * Update src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java Co-authored-by: Kevin Dougan SmileCDR <72025369+KevinDougan-SmileCDR@users.noreply.github.com> * Adjust for hapi-fhir namespace changes and version * Adjust version to include new hapi-fhir HSearch fast path feature * Bump hapi PRE * Update to PRE16 * Adjust configuration class name to HAPI-FHIR HSearch namespace consolidation. Add commented out sample properties for lucene and elastic. Move batch.job.enabled property under spring: prefix to have it considered. * Bump to release * Add missing bean * Added custom property file * SAP-1596 Updated custom property file to read values from env * SAP-1687 Updated code to allow metadata request for server healthcheck (#7) Co-authored-by: Shubham Parikh * DEV-769: Update from upstream branch (#9) * Adding environment variables to override hapi properties file settings * Only override existing hapi properties with env ones * Start Release branch for 5.1.0 * Add property for multitenancy * Get test passing * Add repo * Bump to current release * Rework the JPA server class a bit * Updating Dockerfile to use context code instead of re-cloning the hapi-fhir-jpaserver-starter project. Still re-builds the base hapi libraries, though. * Adding .dockerignore file to improve the efficiency of docker builds * Added support for configurable client ID strategy * Bump mysql-connector-java from 8.0.11 to 8.0.16 Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.11 to 8.0.16. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.11...8.0.16) Signed-off-by: dependabot[bot] * Resolve some compile errors * Fix EMPI rules json * Have EmpiSettings actually return an EmpiSettings bean * Update readme * Remove dead space * Update property * Update README to include Docker info * Prepare for release * Merge master * Removing commands from Dockerfile that clone and build the core hapi-fhir libraries since it is not required for building hapi-fhir-jpaserver-starter; it pulls the core libraries from the maven repo regardless. * Combining base Dockerfile with main dockerfile so that there is a single image related to hapi-jpaserver-starter * Update Dockerfile Added caching of maven dependency resolving * Fix #122 - EMPI error on startup when EMPI not enabled Co-authored-by: Peter Micuch Co-authored-by: Vladimir Nemergut Co-authored-by: jamesagnew Co-authored-by: Sean McIlvenna Co-authored-by: jvi Co-authored-by: Sean McIlvenna Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Tadgh Co-authored-by: Tadgh Co-authored-by: Ken Stevens Co-authored-by: Jens Kristian Villadsen <46567685+jvitrifork@users.noreply.github.com> * BT-80 Updated code to allow header x-api-key * Updated docker compose file with required env variables * SAP-1656 Added enabled parameter for webservlet to enable/disable webapp (#4) Co-authored-by: Shubham Parikh * DEV-769: Update from upstream branch (#9) * Adding environment variables to override hapi properties file settings * Only override existing hapi properties with env ones * Start Release branch for 5.1.0 * Add property for multitenancy * Get test passing * Add repo * Bump to current release * Rework the JPA server class a bit * Updating Dockerfile to use context code instead of re-cloning the hapi-fhir-jpaserver-starter project. Still re-builds the base hapi libraries, though. * Adding .dockerignore file to improve the efficiency of docker builds * Added support for configurable client ID strategy * Bump mysql-connector-java from 8.0.11 to 8.0.16 Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.11 to 8.0.16. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.11...8.0.16) Signed-off-by: dependabot[bot] * Resolve some compile errors * Fix EMPI rules json * Have EmpiSettings actually return an EmpiSettings bean * Update readme * Remove dead space * Update property * Update README to include Docker info * Prepare for release * Merge master * Removing commands from Dockerfile that clone and build the core hapi-fhir libraries since it is not required for building hapi-fhir-jpaserver-starter; it pulls the core libraries from the maven repo regardless. * Combining base Dockerfile with main dockerfile so that there is a single image related to hapi-jpaserver-starter * Update Dockerfile Added caching of maven dependency resolving * Fix #122 - EMPI error on startup when EMPI not enabled Co-authored-by: Peter Micuch Co-authored-by: Vladimir Nemergut Co-authored-by: jamesagnew Co-authored-by: Sean McIlvenna Co-authored-by: jvi Co-authored-by: Sean McIlvenna Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Tadgh Co-authored-by: Tadgh Co-authored-by: Ken Stevens Co-authored-by: Jens Kristian Villadsen <46567685+jvitrifork@users.noreply.github.com> * Updated code to read url pattern from environment. (#10) * BT-140 set legacy sql builder based on hibernate to true * BT-96 Added OAuth support * BT-96 Fixed indentation * BT-96 Fixed spacing * BT-96 Removed unwanted filed * Changes in code as per PR suggestions * added rest security extension in metadata * code refactor * Updated PR as per suggestions * Updated README.md file to run the app with custom property file & env variables * Updated PR as per suggestions * Added user role validation in Oauth * code refactor * Updated code for using single oauth user role * Updated code as per PR suggestions * Updated code as pr PR siggestions * Added suppoet of delete operation to admin only * Refactored code * Added custom search narrowing filter * Updated code to work with oauth disabled * Updated code to get claim name from environment * change claim name env varible to proper name * Updated code to use hard-coded claim name * Changed custom mapper claim name to patient from subject * Added rule builder to filter resources * code refactor * Update CustomAuthorizationInterceptor.java * updated code to support operations on bundle resources * Added consent rule to allow patch request * Added oauth support for task resource * Updated code to make refrence check generic method * Updated method names * Updated code as per PR suggestions * Update code as per PR suggestion * Updated code as per PR * Added verification of clientId from token * Added basic authentication * Code refactor * Added username/password check while checking is basic auth enabled * Added custom property file * DEV-769: Update from upstream branch (#9) * Adding environment variables to override hapi properties file settings * Only override existing hapi properties with env ones * Start Release branch for 5.1.0 * Add property for multitenancy * Get test passing * Add repo * Bump to current release * Rework the JPA server class a bit * Updating Dockerfile to use context code instead of re-cloning the hapi-fhir-jpaserver-starter project. Still re-builds the base hapi libraries, though. * Adding .dockerignore file to improve the efficiency of docker builds * Added support for configurable client ID strategy * Bump mysql-connector-java from 8.0.11 to 8.0.16 Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.11 to 8.0.16. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.11...8.0.16) Signed-off-by: dependabot[bot] * Resolve some compile errors * Fix EMPI rules json * Have EmpiSettings actually return an EmpiSettings bean * Update readme * Remove dead space * Update property * Update README to include Docker info * Prepare for release * Merge master * Removing commands from Dockerfile that clone and build the core hapi-fhir libraries since it is not required for building hapi-fhir-jpaserver-starter; it pulls the core libraries from the maven repo regardless. * Combining base Dockerfile with main dockerfile so that there is a single image related to hapi-jpaserver-starter * Update Dockerfile Added caching of maven dependency resolving * Fix #122 - EMPI error on startup when EMPI not enabled Co-authored-by: Peter Micuch Co-authored-by: Vladimir Nemergut Co-authored-by: jamesagnew Co-authored-by: Sean McIlvenna Co-authored-by: jvi Co-authored-by: Sean McIlvenna Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Tadgh Co-authored-by: Tadgh Co-authored-by: Ken Stevens Co-authored-by: Jens Kristian Villadsen <46567685+jvitrifork@users.noreply.github.com> * Updated docker compose file with required env variables * Update docker-compose.yml Updated file as per suggestions * Added custom property file * SAP-1596 Updated custom property file to read values from env * SAP-1667 Added interceptor for authorization (#5) * SAP-1667 Added interceptor for authorization * SAP-1667 Removed unused space. * SAP-1667 Added newline at the end of the file. Co-authored-by: Shubham Parikh <> * DEV-769: Update from upstream branch (#9) * Adding environment variables to override hapi properties file settings * Only override existing hapi properties with env ones * Start Release branch for 5.1.0 * Add property for multitenancy * Get test passing * Add repo * Bump to current release * Rework the JPA server class a bit * Updating Dockerfile to use context code instead of re-cloning the hapi-fhir-jpaserver-starter project. Still re-builds the base hapi libraries, though. * Adding .dockerignore file to improve the efficiency of docker builds * Added support for configurable client ID strategy * Bump mysql-connector-java from 8.0.11 to 8.0.16 Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.11 to 8.0.16. - [Release notes](https://github.com/mysql/mysql-connector-j/releases) - [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES) - [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.11...8.0.16) Signed-off-by: dependabot[bot] * Resolve some compile errors * Fix EMPI rules json * Have EmpiSettings actually return an EmpiSettings bean * Update readme * Remove dead space * Update property * Update README to include Docker info * Prepare for release * Merge master * Removing commands from Dockerfile that clone and build the core hapi-fhir libraries since it is not required for building hapi-fhir-jpaserver-starter; it pulls the core libraries from the maven repo regardless. * Combining base Dockerfile with main dockerfile so that there is a single image related to hapi-jpaserver-starter * Update Dockerfile Added caching of maven dependency resolving * Fix #122 - EMPI error on startup when EMPI not enabled Co-authored-by: Peter Micuch Co-authored-by: Vladimir Nemergut Co-authored-by: jamesagnew Co-authored-by: Sean McIlvenna Co-authored-by: jvi Co-authored-by: Sean McIlvenna Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Tadgh Co-authored-by: Tadgh Co-authored-by: Ken Stevens Co-authored-by: Jens Kristian Villadsen <46567685+jvitrifork@users.noreply.github.com> * BT-96 Fixed spacing * BT-96 Removed unwanted filed * added rest security extension in metadata * code refactor * Update JpaRestfulServer.java * Updated PR as per suggestions * Added user role validation in Oauth * code refactor * Updated code for using single oauth user role * Updated code as per PR suggestions * Updated code as pr PR siggestions * Added custom search narrowing filter * Updated code to get claim name from environment * Added rule builder to filter resources * code refactor * Added oauth support for task resource * Added verification of clientId from token * Added hibernate physical_naming_strategy to use proper table names. * updated code to use url mapping from environment * Removed client id validation from oauth token * fixed indentation * Updated fork with upstream * Removed unwanted files * removed unwanted imports and files merged during rebase 6.1.0 * removed unwanted imports and files merged during rebase 6.1.0 * removed unwanted imports and files merged during rebase 6.1.0 * removed unwanted imports and files merged during rebase 6.1.0 * Updated application-custom.yaml * commented allow-bean-definition-overriding * Sync application-custom.yaml * commented unused hibernate settings Signed-off-by: dependabot[bot] Co-authored-by: Tadgh Co-authored-by: Michael Buckley Co-authored-by: jkv Co-authored-by: chgl Co-authored-by: ppalacin Co-authored-by: Ally Shaban Co-authored-by: Vadim Peretokin Co-authored-by: Jaison B Co-authored-by: Jaison B Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Patrick Werner Co-authored-by: Håkan MacLean Co-authored-by: Ken Stevens Co-authored-by: Joel Schneider (NMDP) Co-authored-by: craig mcclendon Co-authored-by: Craig McClendon Co-authored-by: dotasek Co-authored-by: dotasek Co-authored-by: michaelabuckley Co-authored-by: Alejandro Medina Co-authored-by: Alejandro Medina Co-authored-by: Dennis Verspuij <6680484+dennisverspuij@users.noreply.github.com> Co-authored-by: markiantorno Co-authored-by: Ibrohim Kholilul Islam Co-authored-by: Ibrohim Kholilul Islam Co-authored-by: Kevin Dougan SmileCDR <72025369+KevinDougan-SmileCDR@users.noreply.github.com> Co-authored-by: juan.marchionatto Co-authored-by: jmarchionatto <60409882+jmarchionatto@users.noreply.github.com> Co-authored-by: Shubham Parikh Co-authored-by: Hank Wallace Co-authored-by: Peter Micuch Co-authored-by: Vladimir Nemergut Co-authored-by: jamesagnew Co-authored-by: Sean McIlvenna Co-authored-by: jvi Co-authored-by: Sean McIlvenna Co-authored-by: Tadgh Co-authored-by: Jens Kristian Villadsen <46567685+jvitrifork@users.noreply.github.com> Co-authored-by: Shubham Parikh Co-authored-by: vpanhale --- Dockerfile | 2 +- README.md | 3 +- .../jpa/starter/BaseJpaRestfulServer.java | 7 + ...omServerCapabilityStatementProviderR4.java | 2 +- .../fhir/jpa/starter/ElasticsearchConfig.java | 33 +++ .../jpa/starter/FhirServerConfigDstu2.java | 16 ++ .../jpa/starter/FhirServerConfigDstu3.java | 19 ++ .../fhir/jpa/starter/FhirServerConfigR4.java | 20 ++ .../fhir/jpa/starter/FhirServerConfigR5.java | 17 ++ .../fhir/jpa/starter/StarterJpaConfig.java | 128 +++++++++++ src/main/resources/application-custom.yaml | 199 ++++++++++-------- .../fhir/jpa/starter/ExampleServerR4IT.java | 3 + 12 files changed, 363 insertions(+), 86 deletions(-) create mode 100644 src/main/java/ca/uhn/fhir/jpa/starter/ElasticsearchConfig.java create mode 100644 src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu2.java create mode 100644 src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java create mode 100644 src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java create mode 100644 src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java create mode 100644 src/main/java/ca/uhn/fhir/jpa/starter/StarterJpaConfig.java diff --git a/Dockerfile b/Dockerfile index a50d92b0c19..89ada58c6cc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -46,4 +46,4 @@ WORKDIR /app COPY --chown=nonroot:nonroot --from=build-distroless /app /app COPY --chown=nonroot:nonroot --from=build-hapi /tmp/hapi-fhir-jpaserver-starter/opentelemetry-javaagent.jar /app -CMD ["/app/main.war"] \ No newline at end of file +CMD ["/app/main.war"] diff --git a/README.md b/README.md index 3e514e08306..b98e150170c 100644 --- a/README.md +++ b/README.md @@ -342,7 +342,7 @@ You can use a custom property file that utilizes environment variables for many -e OAUTH_ENABLED= \ -e OAUTH_URL= \ -e reuse_cached_search_results_millis= \ --e spring.config.location='' \ +-e spring.config.location='' \ -e subscription.resthook.enabled= \ -e subscription.websocket.enabled= \ -e url_pattern= \ @@ -554,6 +554,7 @@ Set `hapi.fhir.mdm_enabled=true` in the [application.yaml](https://github.com/ha Set `empi.enabled=true` in the [hapi.properties](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/hapi.properties) file to enable EMPI on this server. The EMPI matching rules are configured in [empi-rules.json](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/empi-rules.json). The rules in this example file should be replaced with actual matching rules appropriate to your data. Note that EMPI relies on subscriptions, so for EMPI to work, subscriptions must be enabled. + ## Enabling EMPI Set `empi.enabled=true` in the [hapi.properties](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/hapi.properties) file to enable EMPI on this server. The EMPI matching rules are configured in [empi-rules.json](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/empi-rules.json). The rules in this example file should be replaced with actual matching rules appropriate to your data. Note that EMPI relies on subscriptions, so for EMPI to work, subscriptions must be enabled. diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java index 490e13ecd48..764e9687260 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java @@ -1,5 +1,6 @@ package ca.uhn.fhir.jpa.starter; +import ca.uhn.fhir.batch2.jobs.reindex.ReindexProvider; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster; @@ -322,6 +323,12 @@ protected void initialize() throws ServletException { registerProvider(appCtx.getBean(BulkDataExportProvider.class)); } + // valueSet Operations i.e $expand + registerProvider(myValueSetOperationProvider); + + //reindex Provider $reindex + registerProvider(reindexProvider); + // Partitioning if (HapiProperties.getPartitioningMultitenancyEnabled()) { registerInterceptor(new RequestTenantPartitionInterceptor()); diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomServerCapabilityStatementProviderR4.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomServerCapabilityStatementProviderR4.java index a41308d7d67..bd36fe6ff30 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/CustomServerCapabilityStatementProviderR4.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomServerCapabilityStatementProviderR4.java @@ -44,7 +44,7 @@ public CapabilityStatement getServerConformance(HttpServletRequest theRequest, R capabilityStatement.getRest().get(0).setSecurity(getSecurityComponent()); return capabilityStatement; } - + private static CapabilityStatementRestSecurityComponent getSecurityComponent() { CapabilityStatementRestSecurityComponent security = new CapabilityStatementRestSecurityComponent(); List extensions = new ArrayList(); diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/ElasticsearchConfig.java b/src/main/java/ca/uhn/fhir/jpa/starter/ElasticsearchConfig.java new file mode 100644 index 00000000000..21216e6dd59 --- /dev/null +++ b/src/main/java/ca/uhn/fhir/jpa/starter/ElasticsearchConfig.java @@ -0,0 +1,33 @@ +package ca.uhn.fhir.jpa.starter; + +import ca.uhn.fhir.jpa.search.lastn.ElasticsearchSvcImpl; +import org.springframework.beans.factory.annotation.Autowired; +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); + + @Autowired + private ConfigurableEnvironment configurableEnvironment; + + @Bean + public ElasticsearchSvcImpl elasticsearchSvc() { + 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; + } + } +} diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu2.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu2.java new file mode 100644 index 00000000000..b8011389d20 --- /dev/null +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu2.java @@ -0,0 +1,16 @@ +package ca.uhn.fhir.jpa.starter; + +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 { +} diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java new file mode 100644 index 00000000000..35d1dabb49c --- /dev/null +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java @@ -0,0 +1,19 @@ +package ca.uhn.fhir.jpa.starter; + +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 ca.uhn.fhir.jpa.starter.mdm.MdmConfig; +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 { +} diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java new file mode 100644 index 00000000000..c31cf529edd --- /dev/null +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java @@ -0,0 +1,20 @@ +package ca.uhn.fhir.jpa.starter; + +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 ca.uhn.fhir.jpa.starter.mdm.MdmConfig; +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 { +} 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 00000000000..8ee03df272d --- /dev/null +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java @@ -0,0 +1,17 @@ +package ca.uhn.fhir.jpa.starter; + +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 { +} diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/StarterJpaConfig.java b/src/main/java/ca/uhn/fhir/jpa/starter/StarterJpaConfig.java new file mode 100644 index 00000000000..c0ccba70e73 --- /dev/null +++ b/src/main/java/ca/uhn/fhir/jpa/starter/StarterJpaConfig.java @@ -0,0 +1,128 @@ +package ca.uhn.fhir.jpa.starter; + +import ca.uhn.fhir.context.ConfigurationException; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jpa.api.IDaoRegistry; +import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao; +import ca.uhn.fhir.jpa.batch.config.NonPersistedBatchConfigurer; +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.provider.DaoRegistryResourceSupportedSvc; +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.util.ResourceCountCache; +import ca.uhn.fhir.jpa.validation.JpaValidationSupportChain; +import ca.uhn.fhir.mdm.dao.IMdmLinkDao; +import ca.uhn.fhir.rest.api.IResourceSupportedSvc; +import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; +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.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.orm.jpa.JpaTransactionManager; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; + +import javax.persistence.EntityManagerFactory; +import javax.sql.DataSource; + +@Configuration +public class StarterJpaConfig { + @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 + AppProperties appProperties; + @Autowired + private DataSource myDataSource; + @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() { + 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( + 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; + } + + @Autowired + private ISearchParamRegistry mySearchParamRegistry; + + @Bean + public IHSearchSortHelper hSearchSortHelper() { + return new HSearchSortHelperImpl(mySearchParamRegistry); + } + @Bean + public static IMdmLinkDao mdmLinkDao(){ + return new MdmLinkDaoJpaImpl(); + } + + +} diff --git a/src/main/resources/application-custom.yaml b/src/main/resources/application-custom.yaml index 4f1686c952f..bd7f35ad9db 100644 --- a/src/main/resources/application-custom.yaml +++ b/src/main/resources/application-custom.yaml @@ -1,91 +1,119 @@ +#Adds the option to go to eg. http://localhost:8080/actuator/health for seeing the running configuration +#see https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html#actuator.endpoints +management: + endpoints: + web: + exposure: + include: "health,prometheus" spring: + main: + allow-circular-references: true + #allow-bean-definition-overriding: true + batch.job.enabled: false + flyway: + enabled: false + check-location: false + baselineOnMigrate: true datasource: url: ${datasource.url} username: ${datasource.username} password: ${datasource.password} driverClassName: ${datasource.driver} max-active: 15 + + # database connection pool size + hikari: + maximum-pool-size: 10 jpa: properties: - hibernate.dialect: ${hibernate.dialect} hibernate.format_sql: false hibernate.show_sql: false + hibernate.dialect: ${hibernate.dialect} hibernate.hbm2ddl.auto: update hibernate.jdbc.batch_size: 20 hibernate.cache.use_query_cache: false hibernate.cache.use_second_level_cache: false hibernate.cache.use_structured_entries: false hibernate.cache.use_minimal_puts: false -### These settings will enable fulltext search with lucene - hibernate.search.enabled: true - hibernate.search.backend.type: lucene - hibernate.search.backend.analysis.configurer: ca.uhn.fhir.jpa.search.HapiLuceneAnalysisConfigurer - hibernate.search.backend.directory.type: local-filesystem - hibernate.search.backend.directory.root: target/lucenefiles - hibernate.search.backend.lucene_version: lucene_current + ### These settings will enable fulltext search with lucene or elastic + hibernate.search.enabled: false + ### lucene parameters + # hibernate.search.backend.type: lucene + # hibernate.search.backend.analysis.configurer: ca.uhn.fhir.jpa.search.HapiHSearchAnalysisConfigurers$HapiLuceneAnalysisConfigurer + # hibernate.search.backend.directory.type: local-filesystem + # hibernate.search.backend.directory.root: target/lucenefiles + # hibernate.search.backend.lucene_version: lucene_current hibernate.physical_naming_strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl batch: job: enabled: false - main: -# TODO 5.6.0 -> Prevent duplicate bean definitions in the Spring batch config in HAPI: see: - allow-bean-definition-overriding: true + hapi: fhir: + ### This enables the swagger-ui at /fhir/swagger-ui/index.html as well as the /fhir/api-docs (see https://hapifhir.io/hapi-fhir/docs/server_plain/openapi.html) + openapi_enabled: true ### This is the FHIR version. Choose between, DSTU2, DSTU3, R4 or R5 fhir_version: ${fhir_version} -### enable to use the ApacheProxyAddressStrategy which uses X-Forwarded-* headers -### to determine the FHIR server address -# use_apache_address_strategy: false -### forces the use of the https:// protocol for the returned server address. -### alternatively, it may be set using the X-Forwarded-Proto header. -# use_apache_address_strategy_https: false -### enable to set the Server URL -# server_address: http://hapi.fhir.org/baseR4 -# defer_indexing_for_codesystems_of_size: 101 - #implementationguides: - #example from registry (packages.fhir.org) - #swiss: - #name: swiss.mednet.fhir - #version: 0.8.0 - #example not from registry - #ips_1_0_0: - #url: https://build.fhir.org/ig/HL7/fhir-ips/package.tgz - #name: hl7.fhir.uv.ips - #version: 1.0.0 - - #supported_resource_types: - # - Patient - # - Observation + ### enable to use the ApacheProxyAddressStrategy which uses X-Forwarded-* headers + ### to determine the FHIR server address + # use_apache_address_strategy: false + ### forces the use of the https:// protocol for the returned server address. + ### alternatively, it may be set using the X-Forwarded-Proto header. + # use_apache_address_strategy_https: false + ### enable to set the Server URL + # server_address: http://hapi.fhir.org/baseR4 + # defer_indexing_for_codesystems_of_size: 101 + # install_transitive_ig_dependencies: true + # implementationguides: + ### example from registry (packages.fhir.org) + # swiss: + # name: swiss.mednet.fhir + # version: 0.8.0 + # example not from registry + # ips_1_0_0: + # url: https://build.fhir.org/ig/HL7/fhir-ips/package.tgz + # name: hl7.fhir.uv.ips + # version: 1.0.0 + # supported_resource_types: + # - Patient + # - Observation allow_cascading_deletes: true allow_contains_searches: true allow_external_references: true allow_multiple_delete: true allow_override_default_search_params: true - allow_placeholder_references: true auto_create_placeholder_reference_targets: false -# cql_enabled: true + # cql_enabled: true default_encoding: JSON -# default_pretty_print: true + # default_pretty_print: true default_page_size: 20 -# delete_expunge_enabled: true -# enable_repository_validating_interceptor: false - enable_index_missing_fields: false -# enable_index_contained_resource: false + # delete_expunge_enabled: true + # enable_repository_validating_interceptor: false + # enable_index_missing_fields: false + # enable_index_of_type: true + # enable_index_contained_resource: false + ### !!Extended Lucene/Elasticsearch Indexing is still a experimental feature, expect some features (e.g. _total=accurate) to not work as expected!! + ### more information here: https://hapifhir.io/hapi-fhir/docs/server_jpa/elastic.html + advanced_lucene_indexing: false + # enforce_referential_integrity_on_delete: false + # This is an experimental feature, and does not fully support _total and other FHIR features. enforce_referential_integrity_on_delete: false enforce_referential_integrity_on_write: false etag_support_enabled: true expunge_enabled: true -# daoconfig_client_id_strategy: null -# client_id_strategy: ALPHANUMERIC - fhirpath_interceptor_enabled: true + # daoconfig_client_id_strategy: null + # client_id_strategy: ALPHANUMERIC + # fhirpath_interceptor_enabled: false filter_search_enabled: true graphql_enabled: true -# narrative_enabled: true -# mdm_enabled: true -# partitioning: -# allow_references_across_partitions: false -# partitioning_include_in_search_hashes: false + # narrative_enabled: true + # mdm_enabled: true +# local_base_urls: +# - https://hapi.fhir.org/baseR4 + mdm_enabled: false + # partitioning: + # allow_references_across_partitions: false + # partitioning_include_in_search_hashes: false cors: allow_Credentials: true # These are allowed_origin patterns, see: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/cors/CorsConfiguration.html#setAllowedOriginPatterns-java.util.List- @@ -97,49 +125,54 @@ hapi: search-coord-max-pool-size: 100 search-coord-queue-capacity: 200 + # Threadpool size for BATCH'ed GETs in a bundle. +# bundle_batch_pool_size: 10 +# bundle_batch_pool_max_size: 50 + # logger: -# error_format: 'ERROR - ${requestVerb} ${requestUrl}' -# format: >- -# Path[${servletPath}] Source[${requestHeader.x-forwarded-for}] -# Operation[${operationType} ${operationName} ${idOrResourceName}] -# UA[${requestHeader.user-agent}] Params[${requestParameters}] -# ResponseEncoding[${responseEncodingNoDefault}] -# log_exceptions: true -# name: fhirtest.access + # error_format: 'ERROR - ${requestVerb} ${requestUrl}' + # format: >- + # Path[${servletPath}] Source[${requestHeader.x-forwarded-for}] + # Operation[${operationType} ${operationName} ${idOrResourceName}] + # UA[${requestHeader.user-agent}] Params[${requestParameters}] + # ResponseEncoding[${responseEncodingNoDefault}] + # log_exceptions: true + # name: fhirtest.access max_binary_size: 104857600 max_page_size: 200 retain_cached_searches_mins: 60 reuse_cached_search_results_millis: ${reuse_cached_search_results_millis} tester: - home: - name: Local Tester - server_address: ${server_address} - refuse_to_fetch_third_party_urls: false - fhir_version: ${fhir_version} - global: - name: Global Tester - server_address: "http://hapi.fhir.org/baseR4" - refuse_to_fetch_third_party_urls: false - fhir_version: ${fhir_version} - validation: - requests_enabled: false - responses_enabled: false + home: + name: Local Tester + server_address: ${server_address} + refuse_to_fetch_third_party_urls: false + fhir_version: ${fhir_version} + global: + name: Global Tester + server_address: "http://hapi.fhir.org/baseR4" + refuse_to_fetch_third_party_urls: false + fhir_version: ${fhir_version} + # validation: + # requests_enabled: true + # responses_enabled: true binary_storage_enabled: true bulk_export_enabled: true subscription: - resthook_enabled: ${subscription.resthook.enabled} - websocket_enabled: ${subscription.websocket.enabled} - email: - from: some@test.com - host: google.com - port: - username: - password: -# auth: -# startTlsEnable: -# startTlsRequired: -# quitWait: -# lastn_enabled: true + resthook_enabled: ${subscription.resthook.enabled} + websocket_enabled: ${subscription.websocket.enabled} +# email: +# from: some@test.com +# host: google.com +# port: +# username: +# password: +# auth: +# startTlsEnable: +# startTlsRequired: +# quitWait: +# lastn_enabled: true +# store_resource_in_lucene_index_enabled: true ### This is configuration for normalized quantity serach level default is 0 ### 0: NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED - default ### 1: NORMALIZED_QUANTITY_STORAGE_SUPPORTED 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 66d949d1ae8..78053792cff 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java @@ -1,9 +1,12 @@ package ca.uhn.fhir.jpa.starter; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jpa.partition.SystemRequestDetails; +import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; 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.api.server.IBundleProvider; import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; import org.eclipse.jetty.websocket.api.Session; From 11d541490da073fb6e2a4089922e59f2fbfc630a Mon Sep 17 00:00:00 2001 From: Shubham Parikh Date: Mon, 5 Dec 2022 13:05:32 +0530 Subject: [PATCH 191/200] Updated docker file to use /user/app dir (#39) --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 89ada58c6cc..a50d92b0c19 100644 --- a/Dockerfile +++ b/Dockerfile @@ -46,4 +46,4 @@ WORKDIR /app COPY --chown=nonroot:nonroot --from=build-distroless /app /app COPY --chown=nonroot:nonroot --from=build-hapi /tmp/hapi-fhir-jpaserver-starter/opentelemetry-javaagent.jar /app -CMD ["/app/main.war"] +CMD ["/app/main.war"] \ No newline at end of file From 4537fe8d222c12198574ebc6916ed54c81f90480 Mon Sep 17 00:00:00 2001 From: Vadim Dribinsky Date: Tue, 3 Jan 2023 11:37:06 -0500 Subject: [PATCH 192/200] PT-689 split url (#40) * PT-689 splitting the URL of datasource * PT-689 splitting the URL of datasource * PT-689 adding an easier ability to switch to another DB server * PT-689 standardizing variable names Co-authored-by: Vadim Dribinsky --- src/main/resources/application-custom.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/resources/application-custom.yaml b/src/main/resources/application-custom.yaml index bd7f35ad9db..ee3d181e9aa 100644 --- a/src/main/resources/application-custom.yaml +++ b/src/main/resources/application-custom.yaml @@ -15,9 +15,9 @@ spring: check-location: false baselineOnMigrate: true datasource: - url: ${datasource.url} - username: ${datasource.username} - password: ${datasource.password} + url: ${DB_PROTOCOL:jdbc:mysql}://${DB_HOST}:${DB_PORT}/${DB_NAME}?${DB_PARAMS} + username: ${DB_USER} + password: ${DB_PASSWORD} driverClassName: ${datasource.driver} max-active: 15 From 0cd081031e9e9c63a3b7a0a77a0bb2e04c6531a2 Mon Sep 17 00:00:00 2001 From: Shubham Parikh Date: Fri, 6 Jan 2023 13:57:54 +0530 Subject: [PATCH 193/200] sync change with version 6.2.2 --- .../templates/deployment.yaml | 10 +- docker-compose.yml | 14 +- pom.xml | 1 + .../java/ca/uhn/fhir/jpa/empi/EmpiConfig.java | 31 - .../ca/uhn/fhir/jpa/starter/Application.java | 3 +- .../fhir/jpa/starter/ApplicationContext.java | 49 -- .../jpa/starter/BaseJpaRestfulServer.java | 345 ----------- .../CustomAuthorizationInterceptor.java | 25 +- ...omServerCapabilityStatementProviderR4.java | 33 +- .../fhir/jpa/starter/ElasticsearchConfig.java | 33 -- .../jpa/starter/FhirServerConfigDstu2.java | 16 - .../jpa/starter/FhirServerConfigDstu3.java | 19 - .../fhir/jpa/starter/FhirServerConfigR4.java | 20 - .../fhir/jpa/starter/FhirServerConfigR5.java | 17 - .../uhn/fhir/jpa/starter/HapiProperties.java | 535 ------------------ .../ca/uhn/fhir/jpa/starter/OAuth2Helper.java | 15 +- .../fhir/jpa/starter/StarterJpaConfig.java | 128 ----- .../jpa/starter/common/StarterJpaConfig.java | 21 +- src/main/resources/application.yaml | 2 +- src/main/resources/empi-rules.json | 47 -- src/main/resources/hapi-r4.properties | 151 ----- src/main/resources/hapi.properties | 167 ------ src/main/webapp/WEB-INF/web.xml | 55 -- .../fhir/jpa/starter/ExampleServerR4IT.java | 5 +- 24 files changed, 68 insertions(+), 1674 deletions(-) delete mode 100644 src/main/java/ca/uhn/fhir/jpa/empi/EmpiConfig.java delete mode 100644 src/main/java/ca/uhn/fhir/jpa/starter/ApplicationContext.java delete mode 100644 src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java delete mode 100644 src/main/java/ca/uhn/fhir/jpa/starter/ElasticsearchConfig.java delete mode 100644 src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu2.java delete mode 100644 src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java delete mode 100644 src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java delete mode 100644 src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java delete mode 100644 src/main/java/ca/uhn/fhir/jpa/starter/HapiProperties.java delete mode 100644 src/main/java/ca/uhn/fhir/jpa/starter/StarterJpaConfig.java delete mode 100644 src/main/resources/empi-rules.json delete mode 100644 src/main/resources/hapi-r4.properties delete mode 100644 src/main/resources/hapi.properties delete mode 100644 src/main/webapp/WEB-INF/web.xml diff --git a/charts/hapi-fhir-jpaserver/templates/deployment.yaml b/charts/hapi-fhir-jpaserver/templates/deployment.yaml index 8f3c65e3137..a2d3170b03b 100644 --- a/charts/hapi-fhir-jpaserver/templates/deployment.yaml +++ b/charts/hapi-fhir-jpaserver/templates/deployment.yaml @@ -121,11 +121,11 @@ spec: {{- if .Values.extraEnv }} {{ toYaml .Values.extraEnv | nindent 12 }} {{- end }} - volumeMounts: - - mountPath: /tmp - name: tmp-volume - - mountPath: /app/target - name: lucenefiles-volume + # volumeMounts: + # - mountPath: /tmp + # name: tmp-volume + # - mountPath: /app/target + # name: lucenefiles-volume {{- with .Values.nodeSelector }} nodeSelector: {{- toYaml . | nindent 8 }} diff --git a/docker-compose.yml b/docker-compose.yml index 0fe0e457bec..dffb0414c64 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,6 +6,18 @@ services: restart: on-failure ports: - "8080:8080" + environment: + fhir_version: 'DSTU2' + hapi.fhir.server_address: 'http://localhost:8080/baseDstu2/' + reuse_cached_search_results_millis: '0' + subscription.resthook.enabled: 'true' + subscription.websocket.enabled: 'true' + spring.datasource.url: 'jdbc:postgresql://hapi-fhir-postgres:5432/hapi' + spring.datasource.username: admin + spring.datasource.password: admin + spring.datasource.driverClassName: org.postgresql.Driver + spring.jpa.properties.hibernate.dialect: ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgres94Dialect + spring.jpa.hibernate.search.enabled: 'false' hapi-fhir-postgres: image: postgres:13-alpine container_name: hapi-fhir-postgres @@ -17,4 +29,4 @@ services: volumes: - hapi-fhir-postgres:/var/lib/postgresql/data volumes: - hapi-fhir-postgres: + hapi-fhir-postgres: \ No newline at end of file diff --git a/pom.xml b/pom.xml index 3c1d15803f6..bfa8c5d364b 100644 --- a/pom.xml +++ b/pom.xml @@ -40,6 +40,7 @@ + org.eclipse.jetty.websocket diff --git a/src/main/java/ca/uhn/fhir/jpa/empi/EmpiConfig.java b/src/main/java/ca/uhn/fhir/jpa/empi/EmpiConfig.java deleted file mode 100644 index b1f74a2d99c..00000000000 --- a/src/main/java/ca/uhn/fhir/jpa/empi/EmpiConfig.java +++ /dev/null @@ -1,31 +0,0 @@ -package ca.uhn.fhir.jpa.empi; - -import ca.uhn.fhir.empi.api.IEmpiSettings; -import ca.uhn.fhir.empi.rules.config.EmpiRuleValidator; -import ca.uhn.fhir.empi.rules.config.EmpiSettings; -import ca.uhn.fhir.jpa.starter.HapiProperties; -import com.google.common.base.Charsets; -import org.apache.commons.io.IOUtils; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.io.DefaultResourceLoader; -import org.springframework.core.io.Resource; - -import java.io.IOException; - -/** - * TODO: Move this to package "ca.uhn.fhir.jpa.starter" in HAPI FHIR 5.2.0+. The lousy component scan - * in 5.1.0 picks this up even if EMPI is disabled currently. - */ -@Configuration -public class EmpiConfig { - - @Bean - IEmpiSettings empiSettings(EmpiRuleValidator theEmpiRuleValidator) throws IOException { - DefaultResourceLoader resourceLoader = new DefaultResourceLoader(); - Resource resource = resourceLoader.getResource("empi-rules.json"); - String json = IOUtils.toString(resource.getInputStream(), Charsets.UTF_8); - return new EmpiSettings(theEmpiRuleValidator).setEnabled(HapiProperties.getEmpiEnabled()).setScriptText(json); - } - -} diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/Application.java b/src/main/java/ca/uhn/fhir/jpa/starter/Application.java index 8031c598f7e..3b4e851a2f6 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/Application.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/Application.java @@ -58,10 +58,11 @@ protected SpringApplicationBuilder configure( @Bean @Conditional(OnEitherVersion.class) public ServletRegistrationBean hapiServletRegistration(RestfulServer restfulServer) { + String urlMapping = System.getenv("url_pattern"); ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(); beanFactory.autowireBean(restfulServer); servletRegistrationBean.setServlet(restfulServer); - servletRegistrationBean.addUrlMappings("/fhir/*"); + servletRegistrationBean.addUrlMappings(urlMapping); servletRegistrationBean.setLoadOnStartup(1); return servletRegistrationBean; diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/ApplicationContext.java b/src/main/java/ca/uhn/fhir/jpa/starter/ApplicationContext.java deleted file mode 100644 index 0beda1db72a..00000000000 --- a/src/main/java/ca/uhn/fhir/jpa/starter/ApplicationContext.java +++ /dev/null @@ -1,49 +0,0 @@ -package ca.uhn.fhir.jpa.starter; - -import ca.uhn.fhir.context.FhirVersionEnum; -import ca.uhn.fhir.jpa.empi.EmpiConfig; -import ca.uhn.fhir.jpa.empi.config.EmpiConsumerConfig; -import ca.uhn.fhir.jpa.empi.config.EmpiSubmitterConfig; -import ca.uhn.fhir.jpa.subscription.channel.config.SubscriptionChannelConfig; -import ca.uhn.fhir.jpa.subscription.match.config.SubscriptionProcessorConfig; -import ca.uhn.fhir.jpa.subscription.match.config.WebsocketDispatcherConfig; -import ca.uhn.fhir.jpa.subscription.submit.config.SubscriptionSubmitterConfig; -import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; - -public class ApplicationContext extends AnnotationConfigWebApplicationContext { - - public ApplicationContext() { - FhirVersionEnum fhirVersion = HapiProperties.getFhirVersion(); - if (fhirVersion == FhirVersionEnum.DSTU2) { - register(FhirServerConfigDstu2.class, FhirServerConfigCommon.class); - } else if (fhirVersion == FhirVersionEnum.DSTU3) { - 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(); - } - - if (HapiProperties.getSubscriptionWebsocketEnabled()) { - register(WebsocketDispatcherConfig.class); - } - - if (HapiProperties.getSubscriptionEmailEnabled() - || HapiProperties.getSubscriptionRestHookEnabled() - || HapiProperties.getSubscriptionWebsocketEnabled()) { - register(SubscriptionSubmitterConfig.class); - register(SubscriptionProcessorConfig.class); - register(SubscriptionChannelConfig.class); - } - - if (HapiProperties.getEmpiEnabled()) { - register(EmpiSubmitterConfig.class); - register(EmpiConsumerConfig.class); - register(EmpiConfig.class); - } - - } - -} diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java deleted file mode 100644 index 764e9687260..00000000000 --- a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java +++ /dev/null @@ -1,345 +0,0 @@ -package ca.uhn.fhir.jpa.starter; - -import ca.uhn.fhir.batch2.jobs.reindex.ReindexProvider; -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.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 BaseJpaRestfulServer extends RestfulServer { - - private static final long serialVersionUID = 1L; - - @SuppressWarnings("unchecked") - @Override - protected void initialize() throws ServletException { - super.initialize(); - daoConfig.setUseLegacySearchBuilder(true); - /* - * 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 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 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 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 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 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-api-key"); - 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 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)); - } - - // valueSet Operations i.e $expand - registerProvider(myValueSetOperationProvider); - - //reindex Provider $reindex - registerProvider(reindexProvider); - - // 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()); - } - } - -} diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java index cd3b2a146b1..cd912a54a28 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomAuthorizationInterceptor.java @@ -8,7 +8,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpHeaders; -import org.springframework.util.StringUtils; import com.auth0.jwt.JWT; import com.auth0.jwt.JWTVerifier; @@ -88,7 +87,7 @@ private List allowAll() { private List authorizeOAuth(RequestDetails theRequest) throws Exception { String token = theRequest.getHeader(HttpHeaders.AUTHORIZATION); - if (StringUtils.isEmpty(token)) { + if (ObjectUtils.isEmpty(token)) { logger.info("Authorization failure - missing authorization header"); return denyAll(); } @@ -103,16 +102,12 @@ private List authorizeOAuth(RequestDetails theRequest) throws Excepti try { DecodedJWT jwt = JWT.decode(token); String kid = oAuth2Helper.getJwtKeyId(token); - publicKey = StringUtils.isEmpty(publicKey) ? oAuth2Helper.getJwtPublicKey(kid, OAUTH_URL) : publicKey; + publicKey = ObjectUtils.isEmpty(publicKey) ? oAuth2Helper.getJwtPublicKey(kid, OAUTH_URL) : publicKey; JWTVerifier verifier = oAuth2Helper.getJWTVerifier(jwt, publicKey); jwt = verifier.verify(token); - if (theRequest.getRequestType().equals(RequestTypeEnum.DELETE)) { - if (oAuth2Helper.hasClientRole(jwt, OAUTH_CLIENT_ID, OAUTH_ADMIN_ROLE)) { - return allowAll(); - } - } else if (oAuth2Helper.hasClientRole(jwt, OAUTH_CLIENT_ID, OAUTH_USER_ROLE)) { - String patientId = getPatientFromToken(theRequest); - return StringUtils.isEmpty(patientId) ? allowAll() : allowForClaimResourceId(theRequest,patientId); + if (theRequest.getRequestType().equals(RequestTypeEnum.DELETE)) { + if (oAuth2Helper.hasClientRole(jwt, OAUTH_CLIENT_ID, OAUTH_ADMIN_ROLE)) { + return allowAll(); } } else if (oAuth2Helper.hasClientRole(jwt, OAUTH_CLIENT_ID, OAUTH_USER_ROLE)) { String patientId = getPatientFromToken(theRequest); @@ -168,7 +163,7 @@ private Boolean isBasicAuthEnabled() { private Boolean isBasicAuthHeaderPresent(RequestDetails theRequest) { String token = theRequest.getHeader(HttpHeaders.AUTHORIZATION); - return (!StringUtils.isEmpty(token) && token.toUpperCase().contains(BASIC_AUTH_TOKEN_PREFIX)); + return (!ObjectUtils.isEmpty(token) && token.toUpperCase().contains(BASIC_AUTH_TOKEN_PREFIX)); } private Boolean isApiKeyEnabled() { @@ -177,12 +172,12 @@ private Boolean isApiKeyEnabled() { private Boolean isApiKeyHeaderPresent(RequestDetails theRequest) { String apiKey = theRequest.getHeader(APIKEY_HEADER); - return (!StringUtils.isEmpty(apiKey)); + return (!ObjectUtils.isEmpty(apiKey)); } private List authorizeApiKey(RequestDetails theRequest) { String apiKey = theRequest.getHeader(APIKEY_HEADER); - if (StringUtils.isEmpty(apiKey)) { + if (ObjectUtils.isEmpty(apiKey)) { logger.info("Authorization failure - missing X-API-KEY header"); return denyAll(); } @@ -197,7 +192,7 @@ private List authorizeApiKey(RequestDetails theRequest) { private List authorizeBasicAuth(RequestDetails theRequest) { String basicAuthToken = theRequest.getHeader(HttpHeaders.AUTHORIZATION); - if (StringUtils.isEmpty(basicAuthToken)) { + if (ObjectUtils.isEmpty(basicAuthToken)) { logger.info("Authorization failure - missing authorization header"); return denyAll(); } @@ -212,4 +207,4 @@ private List authorizeBasicAuth(RequestDetails theRequest) { public static String getTokenPrefix() { return OAUTH_TOKEN_PREFIX; } -} +} \ No newline at end of file diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/CustomServerCapabilityStatementProviderR4.java b/src/main/java/ca/uhn/fhir/jpa/starter/CustomServerCapabilityStatementProviderR4.java index bd36fe6ff30..fba2ae8950a 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/CustomServerCapabilityStatementProviderR4.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/CustomServerCapabilityStatementProviderR4.java @@ -3,45 +3,38 @@ import java.util.ArrayList; import java.util.List; -import javax.annotation.Nonnull; import javax.servlet.http.HttpServletRequest; -import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.instance.model.api.IBaseConformance; import org.hl7.fhir.r4.model.CapabilityStatement; +import org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementImplementationComponent; import org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestSecurityComponent; import org.hl7.fhir.r4.model.Extension; -import org.hl7.fhir.r4.model.Meta; import org.hl7.fhir.r4.model.UriType; -import org.springframework.context.annotation.Configuration; -import ca.uhn.fhir.jpa.api.config.DaoConfig; -import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao; -import ca.uhn.fhir.jpa.provider.r4.JpaConformanceProviderR4; -import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.RestfulServer; +import ca.uhn.fhir.rest.server.provider.ServerCapabilityStatementProvider; -@Configuration -public class CustomServerCapabilityStatementProviderR4 extends JpaConformanceProviderR4 { + +public class CustomServerCapabilityStatementProviderR4 extends ServerCapabilityStatementProvider { private static final String OAUTH_TOKEN_URL = System.getenv("OAUTH_TOKEN_URL"); private static final String OAUTH_MANAGE_URL = System.getenv("OAUTH_MANAGE_URL"); private CapabilityStatement capabilityStatement; - - public CustomServerCapabilityStatementProviderR4 () { - super(); - } + private String myImplementationDescription; - public CustomServerCapabilityStatementProviderR4(@Nonnull RestfulServer theRestfulServer, @Nonnull IFhirSystemDao theSystemDao, @Nonnull DaoConfig theDaoConfig, @Nonnull ISearchParamRegistry theSearchParamRegistry) { - super(theRestfulServer, theSystemDao, theDaoConfig, theSearchParamRegistry); + public CustomServerCapabilityStatementProviderR4(RestfulServer theServer,String myImplementationDescription) { + super(theServer); + this.myImplementationDescription = myImplementationDescription; } - @Override - public CapabilityStatement getServerConformance(HttpServletRequest theRequest, RequestDetails theRequestDetails) { - capabilityStatement = super.getServerConformance(theRequest, theRequestDetails); + public IBaseConformance getServerConformance(HttpServletRequest theRequest, RequestDetails theRequestDetails) { + capabilityStatement = (CapabilityStatement) super.getServerConformance(theRequest, theRequestDetails); capabilityStatement.getRest().get(0).setSecurity(getSecurityComponent()); + capabilityStatement.setImplementation(new CapabilityStatementImplementationComponent().setDescription(myImplementationDescription)); return capabilityStatement; } @@ -57,4 +50,4 @@ private static CapabilityStatementRestSecurityComponent getSecurityComponent() { security.setExtension(extensionsList); return security; } -} +} \ No newline at end of file diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/ElasticsearchConfig.java b/src/main/java/ca/uhn/fhir/jpa/starter/ElasticsearchConfig.java deleted file mode 100644 index 21216e6dd59..00000000000 --- a/src/main/java/ca/uhn/fhir/jpa/starter/ElasticsearchConfig.java +++ /dev/null @@ -1,33 +0,0 @@ -package ca.uhn.fhir.jpa.starter; - -import ca.uhn.fhir.jpa.search.lastn.ElasticsearchSvcImpl; -import org.springframework.beans.factory.annotation.Autowired; -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); - - @Autowired - private ConfigurableEnvironment configurableEnvironment; - - @Bean - public ElasticsearchSvcImpl elasticsearchSvc() { - 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; - } - } -} diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu2.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu2.java deleted file mode 100644 index b8011389d20..00000000000 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu2.java +++ /dev/null @@ -1,16 +0,0 @@ -package ca.uhn.fhir.jpa.starter; - -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 { -} diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java deleted file mode 100644 index 35d1dabb49c..00000000000 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java +++ /dev/null @@ -1,19 +0,0 @@ -package ca.uhn.fhir.jpa.starter; - -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 ca.uhn.fhir.jpa.starter.mdm.MdmConfig; -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 { -} diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java deleted file mode 100644 index c31cf529edd..00000000000 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java +++ /dev/null @@ -1,20 +0,0 @@ -package ca.uhn.fhir.jpa.starter; - -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 ca.uhn.fhir.jpa.starter.mdm.MdmConfig; -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 { -} diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java deleted file mode 100644 index 8ee03df272d..00000000000 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java +++ /dev/null @@ -1,17 +0,0 @@ -package ca.uhn.fhir.jpa.starter; - -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 { -} diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/HapiProperties.java b/src/main/java/ca/uhn/fhir/jpa/starter/HapiProperties.java deleted file mode 100644 index 20628e53229..00000000000 --- a/src/main/java/ca/uhn/fhir/jpa/starter/HapiProperties.java +++ /dev/null @@ -1,535 +0,0 @@ -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; -import com.google.common.annotations.VisibleForTesting; -import org.apache.commons.lang3.StringUtils; -import org.hibernate.search.elasticsearch.cfg.ElasticsearchIndexStatus; -import org.hibernate.search.elasticsearch.cfg.IndexSchemaManagementStrategy; -import org.jetbrains.annotations.NotNull; - -import javax.annotation.Nonnull; -import java.io.FileInputStream; -import java.io.InputStream; -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; -import static org.apache.commons.lang3.StringUtils.isNotBlank; -import static org.apache.commons.lang3.StringUtils.trim; - -public class HapiProperties { - static final String ENABLE_INDEX_MISSING_FIELDS = "enable_index_missing_fields"; - static final String AUTO_CREATE_PLACEHOLDER_REFERENCE_TARGETS = "auto_create_placeholder_reference_targets"; - static final String ENFORCE_REFERENTIAL_INTEGRITY_ON_WRITE = "enforce_referential_integrity_on_write"; - static final String ENFORCE_REFERENTIAL_INTEGRITY_ON_DELETE = "enforce_referential_integrity_on_delete"; - 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"; - static final String REUSE_CACHED_SEARCH_RESULTS_MILLIS = "reuse_cached_search_results_millis"; - static final String DATASOURCE_DRIVER = "datasource.driver"; - static final String DATASOURCE_MAX_POOL_SIZE = "datasource.max_pool_size"; - static final String DATASOURCE_PASSWORD = "datasource.password"; - static final String DATASOURCE_URL = "datasource.url"; - static final String DATASOURCE_USERNAME = "datasource.username"; - static final String DEFAULT_ENCODING = "default_encoding"; - static final String DEFAULT_PAGE_SIZE = "default_page_size"; - 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"; - static final String LOGGER_LOG_EXCEPTIONS = "logger.log_exceptions"; - static final String LOGGER_NAME = "logger.name"; - static final String MAX_FETCH_SIZE = "max_fetch_size"; - static final String MAX_PAGE_SIZE = "max_page_size"; - static final String SERVER_ADDRESS = "server_address"; - static final String SERVER_ID = "server.id"; - static final String SERVER_NAME = "server.name"; - static final String SUBSCRIPTION_EMAIL_ENABLED = "subscription.email.enabled"; - static final String SUBSCRIPTION_RESTHOOK_ENABLED = "subscription.resthook.enabled"; - static final String SUBSCRIPTION_WEBSOCKET_ENABLED = "subscription.websocket.enabled"; - static final String EMPI_ENABLED = "empi.enabled"; - static final String PARTITIONING_ENABLED = "partitioning.enabled"; - static final String PARTITIONING_CROSS_PARTITION_REFERENCE_MODE = "partitioning.cross_partition_reference_mode"; - static final String ALLOWED_BUNDLE_TYPES = "allowed_bundle_types"; - static final String TEST_PORT = "test.port"; - static final String TESTER_CONFIG_REFUSE_TO_FETCH_THIRD_PARTY_URLS = "tester.config.refuse_to_fetch_third_party_urls"; - static final String CORS_ENABLED = "cors.enabled"; - static final String CORS_ALLOWED_ORIGIN = "cors.allowed_origin"; - static final String CORS_ALLOW_CREDENTIALS = "cors.allowCredentials"; - 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"; - static final String VALIDATE_REQUESTS_ENABLED = "validation.requests.enabled"; - static final String VALIDATE_RESPONSES_ENABLED = "validation.responses.enabled"; - static final String FILTER_SEARCH_ENABLED = "filter_search.enabled"; - static final String GRAPHQL_ENABLED = "graphql.enabled"; - static final String BULK_EXPORT_ENABLED = "bulk.export.enabled"; - static final String EXPIRE_SEARCH_RESULTS_AFTER_MINS = "retain_cached_searches_mins"; - 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() { - return HapiProperties.getPropertyBoolean("elasticsearch.enabled", false); - } - - /* - * Force the configuration to be reloaded - */ - public static void forceReload() { - ourProperties = null; - getProperties(); - } - - /** - * This is mostly here for unit tests. Use the actual properties file - * to set values - */ - @VisibleForTesting - public static void setProperty(String theKey, String theValue) { - getProperties().setProperty(theKey, theValue); - } - - public static Properties getJpaProperties() { - Properties retVal = loadProperties(); - - if (isElasticSearchEnabled()) { - ElasticsearchHibernatePropertiesBuilder builder = new ElasticsearchHibernatePropertiesBuilder(); - builder.setRequiredIndexStatus(getPropertyEnum("elasticsearch.required_index_status", ElasticsearchIndexStatus.class, ElasticsearchIndexStatus.YELLOW)); - builder.setRestUrl(getProperty("elasticsearch.rest_url")); - builder.setUsername(getProperty("elasticsearch.username")); - builder.setPassword(getProperty("elasticsearch.password")); - builder.setIndexSchemaManagementStrategy(getPropertyEnum("elasticsearch.schema_management_strategy", IndexSchemaManagementStrategy.class, IndexSchemaManagementStrategy.CREATE)); - builder.setDebugRefreshAfterWrite(getPropertyBoolean("elasticsearch.debug.refresh_after_write", false)); - builder.setDebugPrettyPrintJsonLog(getPropertyBoolean("elasticsearch.debug.pretty_print_json_log", false)); - builder.apply(retVal); - } - - return retVal; - } - - private static Properties getProperties() { - if (ourProperties == null) { - Properties properties = loadProperties(); - HapiProperties.ourProperties = properties; - } - - return ourProperties; - } - - @NotNull - private static Properties loadProperties() { - // Load the configurable properties file - Properties properties; - try (InputStream in = HapiProperties.class.getClassLoader().getResourceAsStream(HAPI_PROPERTIES)) { - properties = new Properties(); - properties.load(in); - } catch (Exception e) { - throw new ConfigurationException("Could not load HAPI properties", e); - } - - Properties overrideProps = loadOverrideProperties(); - 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; - } - - /** - * 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) { - try { - Properties props = new Properties(); - props.load(new FileInputStream(confFile)); - return props; - } catch (Exception e) { - throw new ConfigurationException("Could not load HAPI properties file: " + confFile, e); - } - } - - return null; - } - - private static String getProperty(String propertyName) { - String env = "HAPI_" + propertyName.toUpperCase(Locale.US); - env = env.replace(".", "_"); - env = env.replace("-", "_"); - - String propertyValue = System.getenv(env); - if (propertyValue != null) { - return propertyValue; - } - - Properties properties = HapiProperties.getProperties(); - if (properties != null) { - propertyValue = properties.getProperty(propertyName); - } - - return propertyValue; - } - - private static String getProperty(String propertyName, String defaultValue) { - String value = getProperty(propertyName); - - if (value != null && value.length() > 0) { - return value; - } - - return defaultValue; - } - - private static Boolean getBooleanProperty(String propertyName, Boolean defaultValue) { - String value = HapiProperties.getProperty(propertyName); - - if (value == null || value.length() == 0) { - return defaultValue; - } - - 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); - - if (value == null || value.length() == 0) { - return defaultValue; - } - - return Integer.parseInt(value); - } - - public static FhirVersionEnum getFhirVersion() { - String fhirVersionString = HapiProperties.getProperty(FHIR_VERSION); - - if (fhirVersionString != null && fhirVersionString.length() > 0) { - return FhirVersionEnum.valueOf(fhirVersionString); - } - - return FhirVersionEnum.DSTU3; - } - - public static boolean isBinaryStorageEnabled() { - return HapiProperties.getBooleanProperty(BINARY_STORAGE_ENABLED, true); - } - - public static ETagSupportEnum getEtagSupport() { - String etagSupportString = HapiProperties.getProperty(ETAG_SUPPORT); - - if (etagSupportString != null && etagSupportString.length() > 0) { - return ETagSupportEnum.valueOf(etagSupportString); - } - - 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); - - if (defaultEncodingString != null && defaultEncodingString.length() > 0) { - return EncodingEnum.valueOf(defaultEncodingString); - } - - return EncodingEnum.JSON; - } - - public static Boolean getDefaultPrettyPrint() { - return HapiProperties.getBooleanProperty(DEFAULT_PRETTY_PRINT, true); - } - - public static String getServerAddress() { - return HapiProperties.getProperty(SERVER_ADDRESS); - } - - public static Integer getDefaultPageSize() { - return HapiProperties.getIntegerProperty(DEFAULT_PAGE_SIZE, 20); - } - - public static Integer getMaximumPageSize() { - return HapiProperties.getIntegerProperty(MAX_PAGE_SIZE, 200); - } - - public static Integer getMaximumFetchSize() { - return HapiProperties.getIntegerProperty(MAX_FETCH_SIZE, Integer.MAX_VALUE); - } - - public static String getLoggerName() { - return HapiProperties.getProperty(LOGGER_NAME, "fhirtest.access"); - } - - public static String getLoggerFormat() { - return HapiProperties.getProperty(LOGGER_FORMAT, "Path[${servletPath}] Source[${requestHeader.x-forwarded-for}] Operation[${operationType} ${operationName} ${idOrResourceName}] UA[${requestHeader.user-agent}] Params[${requestParameters}] ResponseEncoding[${responseEncodingNoDefault}]"); - } - - public static String getLoggerErrorFormat() { - return HapiProperties.getProperty(LOGGER_ERROR_FORMAT, "ERROR - ${requestVerb} ${requestUrl}"); - } - - public static Boolean getLoggerLogExceptions() { - return HapiProperties.getBooleanProperty(LOGGER_LOG_EXCEPTIONS, true); - } - - public static String getDataSourceDriver() { - return HapiProperties.getProperty(DATASOURCE_DRIVER, "org.apache.derby.jdbc.EmbeddedDriver"); - } - - public static Integer getDataSourceMaxPoolSize() { - return HapiProperties.getIntegerProperty(DATASOURCE_MAX_POOL_SIZE, 10); - } - - public static String getDataSourceUrl() { - return HapiProperties.getProperty(DATASOURCE_URL, "jdbc:derby:directory:target/jpaserver_derby_files;create=true"); - } - - public static String getDataSourceUsername() { - return HapiProperties.getProperty(DATASOURCE_USERNAME); - } - - public static String getDataSourcePassword() { - return HapiProperties.getProperty(DATASOURCE_PASSWORD); - } - - public static Boolean getAllowMultipleDelete() { - 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); - } - - public static Boolean getExpungeEnabled() { - return HapiProperties.getBooleanProperty("expunge_enabled", true); - } - - public static Boolean getTesterConfigRefustToFetchThirdPartyUrls() { - return HapiProperties.getBooleanProperty(TESTER_CONFIG_REFUSE_TO_FETCH_THIRD_PARTY_URLS, false); - } - - public static Boolean getCorsEnabled() { - return HapiProperties.getBooleanProperty(CORS_ENABLED, true); - } - - public static String getCorsAllowedOrigin() { - return HapiProperties.getProperty(CORS_ALLOWED_ORIGIN, "*"); - } - - public static String getAllowedBundleTypes() { - return HapiProperties.getProperty(ALLOWED_BUNDLE_TYPES, ""); - } - - @Nonnull - public static Set getSupportedResourceTypes() { - String[] types = defaultString(getProperty("supported_resource_types")).split(","); - return Arrays.stream(types) - .map(StringUtils::trim) - .filter(StringUtils::isNotBlank) - .collect(Collectors.toSet()); - } - - public static String getServerName() { - return HapiProperties.getProperty(SERVER_NAME, "Local Tester"); - } - - public static String getServerId() { - return HapiProperties.getProperty(SERVER_ID, "home"); - } - - public static Boolean getAllowPlaceholderReferences() { - return HapiProperties.getBooleanProperty(ALLOW_PLACEHOLDER_REFERENCES, true); - } - - public static Boolean getSubscriptionEmailEnabled() { - return HapiProperties.getBooleanProperty(SUBSCRIPTION_EMAIL_ENABLED, false); - } - - public static Boolean getSubscriptionRestHookEnabled() { - return HapiProperties.getBooleanProperty(SUBSCRIPTION_RESTHOOK_ENABLED, false); - } - - public static Boolean getSubscriptionWebsocketEnabled() { - return HapiProperties.getBooleanProperty(SUBSCRIPTION_WEBSOCKET_ENABLED, false); - } - - public static Boolean getEmpiEnabled() { - return HapiProperties.getBooleanProperty(EMPI_ENABLED, false); - } - - public static Boolean getPartitioningEnabled() { - return HapiProperties.getBooleanProperty(PARTITIONING_ENABLED, false); - } - - - public static String getPartitioningCrossPartitionReferenceMode() { - return HapiProperties.getProperty(PARTITIONING_CROSS_PARTITION_REFERENCE_MODE, "NOT_ALLOWED"); - } - - public static Boolean getIncludePartitionInSearchHashes() { - return HapiProperties.getBooleanProperty(PARTITIONING_INCLUDE_PARTITION_IN_SEARCH_HASHES, true); - } - - public static Boolean getAllowContainsSearches() { - return HapiProperties.getBooleanProperty(ALLOW_CONTAINS_SEARCHES, true); - } - - public static Boolean getAllowOverrideDefaultSearchParams() { - return HapiProperties.getBooleanProperty(ALLOW_OVERRIDE_DEFAULT_SEARCH_PARAMS, true); - } - - public static String getEmailFrom() { - return HapiProperties.getProperty(EMAIL_FROM, "some@test.com"); - } - - public static Boolean getEmailEnabled() { - return HapiProperties.getBooleanProperty("email.enabled", false); - } - - public static String getEmailHost() { - return HapiProperties.getProperty("email.host"); - } - - public static Integer getEmailPort() { - return HapiProperties.getIntegerProperty("email.port", 0); - } - - public static String getEmailUsername() { - return HapiProperties.getProperty("email.username"); - } - - public static String getEmailPassword() { - return HapiProperties.getProperty("email.password"); - } - - // Defaults from https://javaee.github.io/javamail/docs/api/com/sun/mail/smtp/package-summary.html - public static Boolean getEmailAuth() { - return HapiProperties.getBooleanProperty("email.auth", false); - } - - public static Boolean getEmailStartTlsEnable() { - return HapiProperties.getBooleanProperty("email.starttls.enable", false); - } - - public static Boolean getEmailStartTlsRequired() { - return HapiProperties.getBooleanProperty("email.starttls.required", false); - } - - public static Boolean getEmailQuitWait() { - return HapiProperties.getBooleanProperty("email.quitwait", true); - } - - public static Long getReuseCachedSearchResultsMillis() { - String value = HapiProperties.getProperty(REUSE_CACHED_SEARCH_RESULTS_MILLIS, "60000"); - return Long.valueOf(value); - } - - public static Long getExpireSearchResultsAfterMins() { - String value = HapiProperties.getProperty(EXPIRE_SEARCH_RESULTS_AFTER_MINS, "60"); - return Long.valueOf(value); - } - - public static Boolean getCorsAllowedCredentials() { - return HapiProperties.getBooleanProperty(CORS_ALLOW_CREDENTIALS, false); - } - - public static boolean getValidateRequestsEnabled() { - return HapiProperties.getBooleanProperty(VALIDATE_REQUESTS_ENABLED, false); - } - - public static boolean getValidateResponsesEnabled() { - 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); - } - - public static boolean getEnforceReferentialIntegrityOnDelete() { - return HapiProperties.getBooleanProperty(ENFORCE_REFERENTIAL_INTEGRITY_ON_DELETE, true); - } - - public static boolean getEnforceReferentialIntegrityOnWrite() { - return HapiProperties.getBooleanProperty(ENFORCE_REFERENTIAL_INTEGRITY_ON_WRITE, true); - } - - public static boolean getAutoCreatePlaceholderReferenceTargets() { - return HapiProperties.getBooleanProperty(AUTO_CREATE_PLACEHOLDER_REFERENCE_TARGETS, true); - } - - public static boolean getEnableIndexMissingFields() { - return HapiProperties.getBooleanProperty(ENABLE_INDEX_MISSING_FIELDS, false); - } - - public static Integer getMaxBinarySize() { - return getIntegerProperty(MAX_BINARY_SIZE, null); - } - - private static boolean getPropertyBoolean(String thePropertyName, boolean theDefaultValue) { - String value = getProperty(thePropertyName, Boolean.toString(theDefaultValue)); - return Boolean.parseBoolean(value); - } - - private static T getPropertyEnum(String thePropertyName, Class theEnumType, T theDefaultValue) { - String value = getProperty(thePropertyName, theDefaultValue.name()); - return (T) Enum.valueOf(theEnumType, value); - } - - public static boolean getBulkExportEnabled() { - return HapiProperties.getBooleanProperty(BULK_EXPORT_ENABLED, true); - } - - public static boolean isFhirPathFilterInterceptorEnabled() { - return HapiProperties.getBooleanProperty("fhirpath_interceptor.enabled", false); - } - - public static boolean getPartitioningMultitenancyEnabled() { - return HapiProperties.getBooleanProperty(PARTITIONING_MULTITENANCY_ENABLED, false); - } - - -} - diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java index 2c1e06aa815..129bacde55c 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/OAuth2Helper.java @@ -19,8 +19,8 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; -import org.springframework.util.StringUtils; import org.springframework.web.client.RestTemplate; + import com.auth0.jwt.JWT; import com.auth0.jwt.JWTVerifier; import com.auth0.jwt.algorithms.Algorithm; @@ -131,15 +131,6 @@ protected String getPatientReferenceFromToken(DecodedJWT jwt, String claimName) return null; } - protected String getPatientReferenceFromToken(DecodedJWT jwt, String claimName) { - if (claimName != null) { - Claim claim = jwt.getClaim(claimName); - String patientRef = claim.as(String.class); - return patientRef; - } - return null; - } - protected boolean canBeInPatientCompartment(String resourceType) { /* * For Bundle Request resourceType would be null. @@ -156,6 +147,6 @@ protected boolean canBeInPatientCompartment(String resourceType) { public boolean isOAuthHeaderPresent(RequestDetails theRequest) { String token = theRequest.getHeader(HttpHeaders.AUTHORIZATION); - return (!StringUtils.isEmpty(token) && token.toUpperCase().contains(CustomAuthorizationInterceptor.getTokenPrefix())); + return (!ObjectUtils.isEmpty(token) && token.toUpperCase().contains(CustomAuthorizationInterceptor.getTokenPrefix())); } -} +} \ No newline at end of file diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/StarterJpaConfig.java b/src/main/java/ca/uhn/fhir/jpa/starter/StarterJpaConfig.java deleted file mode 100644 index c0ccba70e73..00000000000 --- a/src/main/java/ca/uhn/fhir/jpa/starter/StarterJpaConfig.java +++ /dev/null @@ -1,128 +0,0 @@ -package ca.uhn.fhir.jpa.starter; - -import ca.uhn.fhir.context.ConfigurationException; -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.jpa.api.IDaoRegistry; -import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao; -import ca.uhn.fhir.jpa.batch.config.NonPersistedBatchConfigurer; -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.provider.DaoRegistryResourceSupportedSvc; -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.util.ResourceCountCache; -import ca.uhn.fhir.jpa.validation.JpaValidationSupportChain; -import ca.uhn.fhir.mdm.dao.IMdmLinkDao; -import ca.uhn.fhir.rest.api.IResourceSupportedSvc; -import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; -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.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; -import org.springframework.core.env.ConfigurableEnvironment; -import org.springframework.orm.jpa.JpaTransactionManager; -import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; - -import javax.persistence.EntityManagerFactory; -import javax.sql.DataSource; - -@Configuration -public class StarterJpaConfig { - @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 - AppProperties appProperties; - @Autowired - private DataSource myDataSource; - @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() { - 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( - 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; - } - - @Autowired - private ISearchParamRegistry mySearchParamRegistry; - - @Bean - public IHSearchSortHelper hSearchSortHelper() { - return new HSearchSortHelperImpl(mySearchParamRegistry); - } - @Bean - public static IMdmLinkDao mdmLinkDao(){ - return new MdmLinkDaoJpaImpl(); - } - - -} diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/common/StarterJpaConfig.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/StarterJpaConfig.java index 3f481bdadcf..bb9cc121fe6 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/common/StarterJpaConfig.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/StarterJpaConfig.java @@ -39,6 +39,10 @@ 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.CustomAuthorizationInterceptor; +import ca.uhn.fhir.jpa.starter.CustomConsentService; +import ca.uhn.fhir.jpa.starter.CustomSearchNarrowingInterceptor; +import ca.uhn.fhir.jpa.starter.CustomServerCapabilityStatementProviderR4; import ca.uhn.fhir.jpa.starter.annotations.OnCorsPresent; import ca.uhn.fhir.jpa.starter.annotations.OnImplementationGuidesPresent; import ca.uhn.fhir.jpa.starter.common.validation.IRepositoryValidationInterceptorFactory; @@ -54,6 +58,9 @@ 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.auth.AuthorizationInterceptor; +import ca.uhn.fhir.rest.server.interceptor.auth.SearchNarrowingInterceptor; +import ca.uhn.fhir.rest.server.interceptor.consent.ConsentInterceptor; 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; @@ -84,6 +91,8 @@ ThreadPoolFactoryConfig.class ) public class StarterJpaConfig { + private static final String FHIR_VERSION = System.getenv("fhir_version"); + private static final String OAUTH_ENABLED = System.getenv("OAUTH_ENABLED"); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(StarterJpaConfig.class); @@ -247,6 +256,14 @@ public RestfulServer restfulServer(IFhirSystemDao fhirSystemDao, AppProper RestfulServer fhirServer = new RestfulServer(fhirSystemDao.getContext()); + SearchNarrowingInterceptor customSearchNarrowingInterceptor = new CustomSearchNarrowingInterceptor(); + fhirServer.registerInterceptor(customSearchNarrowingInterceptor); + ConsentInterceptor consentInterceptor = new ConsentInterceptor(new CustomConsentService(daoRegistry)); + fhirServer.registerInterceptor(consentInterceptor); + AuthorizationInterceptor authorizationInterceptor = new CustomAuthorizationInterceptor(); + fhirServer.registerInterceptor(authorizationInterceptor); + + List supportedResourceTypes = appProperties.getSupported_resource_types(); @@ -432,8 +449,8 @@ public static IServerConformanceProvider calculateConformanceProvider(IFhirSy return confProvider; } else if (fhirVersion == FhirVersionEnum.R4) { - JpaCapabilityStatementProvider confProvider = new JpaCapabilityStatementProvider(fhirServer, fhirSystemDao, daoConfig, searchParamRegistry, theValidationSupport); - confProvider.setImplementationDescription("HAPI FHIR R4 Server"); + CustomServerCapabilityStatementProviderR4 confProvider = new CustomServerCapabilityStatementProviderR4(fhirServer,"HAPI FHIR R4 Server"); + //confProvider.setImplementationDescription("HAPI FHIR R4 Server"); return confProvider; } else if (fhirVersion == FhirVersionEnum.R4B) { diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 67a652fdbc8..1ba013370aa 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -40,7 +40,7 @@ spring: # hibernate.cache.use_structured_entries: false # hibernate.cache.use_minimal_puts: false ### These settings will enable fulltext search with lucene or elastic - hibernate.search.enabled: true + hibernate.search.enabled: false ### lucene parameters # hibernate.search.backend.type: lucene # hibernate.search.backend.analysis.configurer: ca.uhn.fhir.jpa.search.HapiHSearchAnalysisConfigurers$HapiLuceneAnalysisConfigurer diff --git a/src/main/resources/empi-rules.json b/src/main/resources/empi-rules.json deleted file mode 100644 index 9f2706abaec..00000000000 --- a/src/main/resources/empi-rules.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "version": "1", - "candidateSearchParams": [ - { - "resourceType": "Patient", - "searchParams": ["birthdate"] - }, - { - "resourceType": "*", - "searchParams": ["identifier"] - }, - { - "resourceType": "Patient", - "searchParams": ["general-practitioner"] - } - ], - "candidateFilterSearchParams": [ - { - "resourceType": "*", - "searchParam": "active", - "fixedValue": "true" - } - ], - "matchFields": [ - { - "name": "cosine-given-name", - "resourceType": "*", - "resourcePath": "name.given", - "metric": "COSINE", - "matchThreshold": 0.8, - "exact": true - }, - { - "name": "jaro-last-name", - "resourceType": "*", - "resourcePath": "name.family", - "metric": "JARO_WINKLER", - "matchThreshold": 0.8, - "exact": true - } - ], - "matchResultMap": { - "cosine-given-name" : "POSSIBLE_MATCH", - "cosine-given-name,jaro-last-name" : "MATCH" - }, - "eidSystem": "http://company.io/fhir/NamingSystem/custom-eid-system" -} diff --git a/src/main/resources/hapi-r4.properties b/src/main/resources/hapi-r4.properties deleted file mode 100644 index a06d07dbbc0..00000000000 --- a/src/main/resources/hapi-r4.properties +++ /dev/null @@ -1,151 +0,0 @@ -# Adjust this to set the version of FHIR supported by this server. See -# FhirVersionEnum for a list of available constants. Example values include -# DSTU2, DSTU3, R4. -fhir_version=${fhir_version} - -# This is the address that the FHIR server will report as its own address. -# If this server will be deployed (for example) to an internet accessible -# server, put the DNS name of that server here. -# -# Note that this is also the address that the hapi-fhir-testpage-overlay -# (the web UI similar to the one at http://hapi.fhir.org) will use to -# connect internally to the FHIR server, so this also needs to be a name -# accessible from the server itself. -server_address=${server_address} - -enable_index_missing_fields=false -auto_create_placeholder_reference_targets=false -enforce_referential_integrity_on_write=false -enforce_referential_integrity_on_delete=false -default_encoding=JSON -etag_support=ENABLED -reuse_cached_search_results_millis=60000 -retain_cached_searches_mins=60 -default_page_size=20 -max_page_size=200 -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 -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=${datasource.driver} -datasource.url=${datasource.url} -datasource.username=${datasource.username} -datasource.password=${datasource.password} -server.name=Local Tester -server.id=home -test.port= - -################################################### -# Binary Storage (104857600 = 100mb) -################################################### -max_binary_size=104857600 - -################################################### -# Validation -################################################### -# Should all incoming requests be validated -validation.requests.enabled=false -# Should outgoing responses be validated -validation.responses.enabled=false - -################################################### -# Search Features -################################################### -filter_search.enabled=true -graphql.enabled=true -# See FhirPathFilterInterceptor -fhirpath_interceptor.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) -#supported_resource_types=Patient,Observation,Encounter - -################################################### -# Database Settings -################################################### -hibernate.dialect=${hibernate.dialect} -hibernate.search.model_mapping=ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory -hibernate.format_sql=false -hibernate.show_sql=false -hibernate.hbm2ddl.auto=update -hibernate.jdbc.batch_size=20 -hibernate.cache.use_query_cache=false -hibernate.cache.use_second_level_cache=false -hibernate.cache.use_structured_entries=false -hibernate.cache.use_minimal_puts=false -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 - -################################################## -# ElasticSearch -# Note that using ElasticSearch is disabled by -# default and the server will use Lucene instead. -################################################## -elasticsearch.enabled=false -elasticsearch.rest_url=http://localhost:9200 -elasticsearch.username=SomeUsername -elasticsearch.password=SomePassword -elasticsearch.required_index_status=YELLOW -elasticsearch.schema_management_strategy=CREATE -# Immediately refresh indexes after every write. This is very bad for -# performance, but can be helpful for testing. -elasticsearch.debug.refresh_after_write=false -elasticsearch.debug.pretty_print_json_log=false - - -################################################## -# Binary Storage Operations -################################################## -binary_storage.enabled=true - -################################################## -# Bulk Data Specification -################################################## -bulk.export.enabled=true - -################################################## -# CORS Settings -################################################## -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.allowed_origin=* - -################################################## -# Allowed Bundle Types for persistence (defaults are: COLLECTION,DOCUMENT,MESSAGE) -################################################## -#allowed_bundle_types=COLLECTION,DOCUMENT,MESSAGE,TRANSACTION,TRANSACTIONRESPONSE,BATCH,BATCHRESPONSE,HISTORY,SEARCHSET - -################################################## -# Subscriptions -################################################## - -# Enable REST Hook Subscription Channel -subscription.resthook.enabled=${subscription.resthook.enabled} - -# Enable Email Subscription Channel -subscription.email.enabled=false -email.enabled=false -email.from=some@test.com -email.host= -email.port=0 -email.username= -email.password= - -# Enable Websocket Subscription Channel -subscription.websocket.enabled=${subscription.websocket.enabled} diff --git a/src/main/resources/hapi.properties b/src/main/resources/hapi.properties deleted file mode 100644 index b7ef0f8ce21..00000000000 --- a/src/main/resources/hapi.properties +++ /dev/null @@ -1,167 +0,0 @@ -# Adjust this to set the version of FHIR supported by this server. See -# FhirVersionEnum for a list of available constants. Example values include -# DSTU2, DSTU3, R4. -fhir_version=R4 - -# This is the address that the FHIR server will report as its own address. -# If this server will be deployed (for example) to an internet accessible -# server, put the DNS name of that server here. -# -# Note that this is also the address that the hapi-fhir-testpage-overlay -# (the web UI similar to the one at http://hapi.fhir.org) will use to -# connect internally to the FHIR server, so this also needs to be a name -# accessible from the server itself. -server_address=http://localhost:8080/hapi-fhir-jpaserver/fhir/ - -enable_index_missing_fields=false -auto_create_placeholder_reference_targets=false -enforce_referential_integrity_on_write=false -enforce_referential_integrity_on_delete=false -default_encoding=JSON -etag_support=ENABLED -reuse_cached_search_results_millis=60000 -retain_cached_searches_mins=60 -default_page_size=20 -max_page_size=200 -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 -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.h2.Driver -datasource.url=jdbc:h2:file:./target/database/h2 -datasource.username= -datasource.password= -server.name=Local Tester -server.id=home -test.port= - -################################################### -# Binary Storage (104857600 = 100mb) -################################################### -max_binary_size=104857600 - -################################################### -# Validation -################################################### -# Should all incoming requests be validated -validation.requests.enabled=false -# Should outgoing responses be validated -validation.responses.enabled=false - -################################################### -# Search Features -################################################### -filter_search.enabled=true -graphql.enabled=true -# See FhirPathFilterInterceptor -fhirpath_interceptor.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) -#supported_resource_types=Patient,Observation,Encounter - -################################################### -# Database Settings -################################################### -hibernate.dialect=org.hibernate.dialect.H2Dialect -hibernate.search.model_mapping=ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory -hibernate.format_sql=false -hibernate.show_sql=false -hibernate.hbm2ddl.auto=update -hibernate.jdbc.batch_size=20 -hibernate.cache.use_query_cache=false -hibernate.cache.use_second_level_cache=false -hibernate.cache.use_structured_entries=false -hibernate.cache.use_minimal_puts=false -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 - -################################################## -# ElasticSearch -# Note that using ElasticSearch is disabled by -# default and the server will use Lucene instead. -################################################## -elasticsearch.enabled=false -elasticsearch.rest_url=http://localhost:9200 -elasticsearch.username=SomeUsername -elasticsearch.password=SomePassword -elasticsearch.required_index_status=YELLOW -elasticsearch.schema_management_strategy=CREATE -# Immediately refresh indexes after every write. This is very bad for -# performance, but can be helpful for testing. -elasticsearch.debug.refresh_after_write=false -elasticsearch.debug.pretty_print_json_log=false - - -################################################## -# Binary Storage Operations -################################################## -binary_storage.enabled=true - -################################################## -# Bulk Data Specification -################################################## -bulk.export.enabled=true - -################################################## -# CORS Settings -################################################## -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.allowed_origin=* - -################################################## -# Allowed Bundle Types for persistence (defaults are: COLLECTION,DOCUMENT,MESSAGE) -################################################## -#allowed_bundle_types=COLLECTION,DOCUMENT,MESSAGE,TRANSACTION,TRANSACTIONRESPONSE,BATCH,BATCHRESPONSE,HISTORY,SEARCHSET - -################################################## -# Subscriptions -################################################## - -# Enable REST Hook Subscription Channel -subscription.resthook.enabled=false - -# Enable Email Subscription Channel -subscription.email.enabled=false -email.enabled=false -email.from=some@test.com -email.host= -email.port=0 -email.username= -email.password= - -# Enable Websocket Subscription Channel -subscription.websocket.enabled=false - -################################################### -# EMPI -################################################### -empi.enabled=false - -################################################### -# Partitioning And Multitenancy -################################################### -partitioning.enabled=false -partitioning.cross_partition_reference_mode=NOT_ALLOWED -partitioning.partitioning_include_in_search_hashes=true -partitioning.multitenancy.enabled=false - -#daoconfig.client_id_strategy=ANY - diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index 960e5b79b9c..00000000000 --- a/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,55 +0,0 @@ - - - - org.springframework.web.context.ContextLoaderListener - - - contextClass - - ca.uhn.fhir.jpa.starter.ApplicationContext - - - - contextConfigLocation - - - - - - - spring - org.springframework.web.servlet.DispatcherServlet - - contextClass - org.springframework.web.context.support.AnnotationConfigWebApplicationContext - - - contextConfigLocation - - ca.uhn.fhir.jpa.starter.FhirTesterConfig - - - 2 - ${enable_web} - - - spring - / - - - - fhirServlet - ca.uhn.fhir.jpa.starter.JpaRestfulServer - 1 - - - fhirServlet - ${url_pattern} - - - 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 78053792cff..278a5f0ccc8 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java @@ -1,12 +1,9 @@ package ca.uhn.fhir.jpa.starter; import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.jpa.partition.SystemRequestDetails; -import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; 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.api.server.IBundleProvider; import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; import org.eclipse.jetty.websocket.api.Session; @@ -217,4 +214,4 @@ void beforeEach() { return activeSubscriptionCount() == 2; // 2 subscription based on mdm-rules.json }); } -} \ No newline at end of file +} From b91616faef2fbcbe41f4fcab0e5e5b6535e82e02 Mon Sep 17 00:00:00 2001 From: Shubham Parikh Date: Fri, 6 Jan 2023 14:04:02 +0530 Subject: [PATCH 194/200] sync deployment.yaml --- .../templates/deployment.yaml | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/charts/hapi-fhir-jpaserver/templates/deployment.yaml b/charts/hapi-fhir-jpaserver/templates/deployment.yaml index a2d3170b03b..adec377df64 100644 --- a/charts/hapi-fhir-jpaserver/templates/deployment.yaml +++ b/charts/hapi-fhir-jpaserver/templates/deployment.yaml @@ -30,7 +30,11 @@ spec: {{- toYaml .Values.podSecurityContext | nindent 8 }} initContainers: - name: wait-for-db-to-be-ready +<<<<<<< HEAD image: docker.io/bitnami/postgresql:15.1.0-debian-11-r0@sha256:27915588d5203a10a1c23624d9c81644437f33b7c224e25f79bcd9bd09bbb8e2 +======= + image: docker.io/bitnami/postgresql:14.3.0-debian-10-r20 +>>>>>>> 8790a8f (Sync 6.1.0 (#38)) imagePullPolicy: IfNotPresent {{- with .Values.restrictedContainerSecurityContext }} securityContext: @@ -54,13 +58,21 @@ spec: - name: {{ .Chart.Name }} securityContext: {{- toYaml .Values.securityContext | nindent 12 }} +<<<<<<< HEAD image: {{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag }} +======= + image: {{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ default .Chart.AppVersion .Values.image.tag }} +>>>>>>> 8790a8f (Sync 6.1.0 (#38)) imagePullPolicy: {{ .Values.image.pullPolicy }} ports: - name: http containerPort: 8080 protocol: TCP +<<<<<<< HEAD - name: http-metrics +======= + - name: metrics +>>>>>>> 8790a8f (Sync 6.1.0 (#38)) containerPort: 8081 protocol: TCP startupProbe: @@ -121,11 +133,11 @@ spec: {{- if .Values.extraEnv }} {{ toYaml .Values.extraEnv | nindent 12 }} {{- end }} - # volumeMounts: - # - mountPath: /tmp - # name: tmp-volume - # - mountPath: /app/target - # name: lucenefiles-volume + volumeMounts: + - mountPath: /tmp + name: tmp-volume + - mountPath: /app/target + name: lucenefiles-volume {{- with .Values.nodeSelector }} nodeSelector: {{- toYaml . | nindent 8 }} From c6c663f34e38a9ef37d56b198bb4a74b708ef4ea Mon Sep 17 00:00:00 2001 From: Shubham Parikh Date: Fri, 6 Jan 2023 14:05:33 +0530 Subject: [PATCH 195/200] added newline in dockerfile --- Dockerfile | 2 +- .../hapi-fhir-jpaserver/templates/deployment.yaml | 14 +------------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/Dockerfile b/Dockerfile index a50d92b0c19..89ada58c6cc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -46,4 +46,4 @@ WORKDIR /app COPY --chown=nonroot:nonroot --from=build-distroless /app /app COPY --chown=nonroot:nonroot --from=build-hapi /tmp/hapi-fhir-jpaserver-starter/opentelemetry-javaagent.jar /app -CMD ["/app/main.war"] \ No newline at end of file +CMD ["/app/main.war"] diff --git a/charts/hapi-fhir-jpaserver/templates/deployment.yaml b/charts/hapi-fhir-jpaserver/templates/deployment.yaml index adec377df64..65c375c5adf 100644 --- a/charts/hapi-fhir-jpaserver/templates/deployment.yaml +++ b/charts/hapi-fhir-jpaserver/templates/deployment.yaml @@ -30,11 +30,7 @@ spec: {{- toYaml .Values.podSecurityContext | nindent 8 }} initContainers: - name: wait-for-db-to-be-ready -<<<<<<< HEAD image: docker.io/bitnami/postgresql:15.1.0-debian-11-r0@sha256:27915588d5203a10a1c23624d9c81644437f33b7c224e25f79bcd9bd09bbb8e2 -======= - image: docker.io/bitnami/postgresql:14.3.0-debian-10-r20 ->>>>>>> 8790a8f (Sync 6.1.0 (#38)) imagePullPolicy: IfNotPresent {{- with .Values.restrictedContainerSecurityContext }} securityContext: @@ -58,21 +54,13 @@ spec: - name: {{ .Chart.Name }} securityContext: {{- toYaml .Values.securityContext | nindent 12 }} -<<<<<<< HEAD image: {{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag }} -======= - image: {{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ default .Chart.AppVersion .Values.image.tag }} ->>>>>>> 8790a8f (Sync 6.1.0 (#38)) imagePullPolicy: {{ .Values.image.pullPolicy }} ports: - name: http containerPort: 8080 protocol: TCP -<<<<<<< HEAD - name: http-metrics -======= - - name: metrics ->>>>>>> 8790a8f (Sync 6.1.0 (#38)) containerPort: 8081 protocol: TCP startupProbe: @@ -158,4 +146,4 @@ spec: - name: tmp-volume emptyDir: {} - name: lucenefiles-volume - emptyDir: {} + emptyDir: {} \ No newline at end of file From 36d3283d87e7b2d0dc61e86c405ad4ff84738443 Mon Sep 17 00:00:00 2001 From: Shubham Parikh Date: Fri, 6 Jan 2023 14:45:15 +0530 Subject: [PATCH 196/200] added new line EOF --- charts/hapi-fhir-jpaserver/templates/deployment.yaml | 2 +- docker-compose.yml | 2 +- pom.xml | 1 - src/main/resources/logback.xml | 2 +- src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu2IT.java | 2 +- src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu3IT.java | 2 +- src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR5IT.java | 2 +- .../java/ca/uhn/fhir/jpa/starter/MultitenantServerR4IT.java | 2 +- 8 files changed, 7 insertions(+), 8 deletions(-) diff --git a/charts/hapi-fhir-jpaserver/templates/deployment.yaml b/charts/hapi-fhir-jpaserver/templates/deployment.yaml index 65c375c5adf..8f3c65e3137 100644 --- a/charts/hapi-fhir-jpaserver/templates/deployment.yaml +++ b/charts/hapi-fhir-jpaserver/templates/deployment.yaml @@ -146,4 +146,4 @@ spec: - name: tmp-volume emptyDir: {} - name: lucenefiles-volume - emptyDir: {} \ No newline at end of file + emptyDir: {} diff --git a/docker-compose.yml b/docker-compose.yml index dffb0414c64..6e48890dfd5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -29,4 +29,4 @@ services: volumes: - hapi-fhir-postgres:/var/lib/postgresql/data volumes: - hapi-fhir-postgres: \ No newline at end of file + hapi-fhir-postgres: diff --git a/pom.xml b/pom.xml index bfa8c5d364b..3fc9e60a742 100644 --- a/pom.xml +++ b/pom.xml @@ -442,7 +442,6 @@ org.apache.maven.plugins maven-failsafe-plugin - 3.0.0-M5 true diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index d2dd0a1d894..9625c50de37 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -20,4 +20,4 @@ - \ No newline at end of file + 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 c3bbad60bdc..e20aff8ff8a 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu2IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu2IT.java @@ -53,4 +53,4 @@ void beforeEach() { ourClient = ourCtx.newRestfulGenericClient(ourServerBase); ourClient.registerInterceptor(new LoggingInterceptor(true)); } -} \ No newline at end of file +} 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 a790ce31b24..926a5003841 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu3IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu3IT.java @@ -210,4 +210,4 @@ public void testWebsocketSubscription() throws Exception { ourClient.delete().resourceById(mySubscriptionId).execute(); } -} \ No newline at end of file +} diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR5IT.java b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR5IT.java index ffeee8e3960..ddbd6a77ac1 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR5IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR5IT.java @@ -135,4 +135,4 @@ void beforeEach() { ourClient = ourCtx.newRestfulGenericClient(ourServerBase); ourClient.registerInterceptor(new LoggingInterceptor(true)); } -} \ No newline at end of file +} diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/MultitenantServerR4IT.java b/src/test/java/ca/uhn/fhir/jpa/starter/MultitenantServerR4IT.java index e9daae17035..9d340b12444 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/MultitenantServerR4IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/MultitenantServerR4IT.java @@ -103,4 +103,4 @@ void beforeEach() { ourClient.registerInterceptor(new LoggingInterceptor(true)); ourClient.registerInterceptor(ourClientTenantInterceptor); } -} \ No newline at end of file +} From 47b036a7984d7d2457b7b9fb1f27daa3f186318e Mon Sep 17 00:00:00 2001 From: Shubham Parikh Date: Fri, 6 Jan 2023 17:48:30 +0530 Subject: [PATCH 197/200] Updated code to use non tmp directory --- Dockerfile | 12 ++++++------ .../ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java | 3 --- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/Dockerfile b/Dockerfile index 89ada58c6cc..8afcb88fd7c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ FROM maven:3.8-openjdk-17-slim as build-hapi -WORKDIR /tmp/hapi-fhir-jpaserver-starter +WORKDIR /usr/app//hapi-fhir-jpaserver-starter ARG OPENTELEMETRY_JAVA_AGENT_VERSION=1.17.0 RUN curl -LSsO https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v${OPENTELEMETRY_JAVA_AGENT_VERSION}/opentelemetry-javaagent.jar @@ -8,12 +8,12 @@ COPY pom.xml . COPY server.xml . RUN mvn -ntp dependency:go-offline -COPY src/ /tmp/hapi-fhir-jpaserver-starter/src/ +COPY src/ /usr/app//hapi-fhir-jpaserver-starter/src/ RUN mvn clean install -DskipTests -Djdk.lang.Process.launchMechanism=vfork FROM build-hapi AS build-distroless RUN mvn package spring-boot:repackage -Pboot -RUN mkdir /app && cp /tmp/hapi-fhir-jpaserver-starter/target/ROOT.war /app/main.war +RUN mkdir /app && cp /usr/app//hapi-fhir-jpaserver-starter/target/ROOT.war /app/main.war ########### bitnami tomcat version is suitable for debugging and comes with a shell @@ -30,8 +30,8 @@ USER 1001 COPY --chown=1001:1001 catalina.properties /opt/bitnami/tomcat/conf/catalina.properties COPY --chown=1001:1001 server.xml /opt/bitnami/tomcat/conf/server.xml -COPY --from=build-hapi --chown=1001:1001 /tmp/hapi-fhir-jpaserver-starter/target/ROOT.war /opt/bitnami/tomcat/webapps/ROOT.war -COPY --from=build-hapi --chown=1001:1001 /tmp/hapi-fhir-jpaserver-starter/opentelemetry-javaagent.jar /app +COPY --from=build-hapi --chown=1001:1001 /usr/app//hapi-fhir-jpaserver-starter/target/ROOT.war /opt/bitnami/tomcat/webapps/ROOT.war +COPY --from=build-hapi --chown=1001:1001 /usr/app//hapi-fhir-jpaserver-starter/opentelemetry-javaagent.jar /app ENV ALLOW_EMPTY_PASSWORD=yes @@ -44,6 +44,6 @@ USER 65532:65532 WORKDIR /app COPY --chown=nonroot:nonroot --from=build-distroless /app /app -COPY --chown=nonroot:nonroot --from=build-hapi /tmp/hapi-fhir-jpaserver-starter/opentelemetry-javaagent.jar /app +COPY --chown=nonroot:nonroot --from=build-hapi /usr/app//hapi-fhir-jpaserver-starter/opentelemetry-javaagent.jar /app CMD ["/app/main.war"] 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 8514b79c2e1..278a5f0ccc8 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java @@ -1,12 +1,9 @@ package ca.uhn.fhir.jpa.starter; import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.jpa.partition.SystemRequestDetails; -import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; 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.api.server.IBundleProvider; import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; import org.eclipse.jetty.websocket.api.Session; From 07f517423db24a932ebdb97834f32adfd72f48b3 Mon Sep 17 00:00:00 2001 From: Shubham Parikh Date: Fri, 6 Jan 2023 18:48:30 +0530 Subject: [PATCH 198/200] removed duplicate files --- .../fhir/jpa/starter/ElasticsearchConfig.java | 33 ----- .../fhir/jpa/starter/StarterJpaConfig.java | 128 ------------------ 2 files changed, 161 deletions(-) delete mode 100644 src/main/java/ca/uhn/fhir/jpa/starter/ElasticsearchConfig.java delete mode 100644 src/main/java/ca/uhn/fhir/jpa/starter/StarterJpaConfig.java diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/ElasticsearchConfig.java b/src/main/java/ca/uhn/fhir/jpa/starter/ElasticsearchConfig.java deleted file mode 100644 index 21216e6dd59..00000000000 --- a/src/main/java/ca/uhn/fhir/jpa/starter/ElasticsearchConfig.java +++ /dev/null @@ -1,33 +0,0 @@ -package ca.uhn.fhir.jpa.starter; - -import ca.uhn.fhir.jpa.search.lastn.ElasticsearchSvcImpl; -import org.springframework.beans.factory.annotation.Autowired; -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); - - @Autowired - private ConfigurableEnvironment configurableEnvironment; - - @Bean - public ElasticsearchSvcImpl elasticsearchSvc() { - 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; - } - } -} diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/StarterJpaConfig.java b/src/main/java/ca/uhn/fhir/jpa/starter/StarterJpaConfig.java deleted file mode 100644 index c0ccba70e73..00000000000 --- a/src/main/java/ca/uhn/fhir/jpa/starter/StarterJpaConfig.java +++ /dev/null @@ -1,128 +0,0 @@ -package ca.uhn.fhir.jpa.starter; - -import ca.uhn.fhir.context.ConfigurationException; -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.jpa.api.IDaoRegistry; -import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao; -import ca.uhn.fhir.jpa.batch.config.NonPersistedBatchConfigurer; -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.provider.DaoRegistryResourceSupportedSvc; -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.util.ResourceCountCache; -import ca.uhn.fhir.jpa.validation.JpaValidationSupportChain; -import ca.uhn.fhir.mdm.dao.IMdmLinkDao; -import ca.uhn.fhir.rest.api.IResourceSupportedSvc; -import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; -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.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; -import org.springframework.core.env.ConfigurableEnvironment; -import org.springframework.orm.jpa.JpaTransactionManager; -import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; - -import javax.persistence.EntityManagerFactory; -import javax.sql.DataSource; - -@Configuration -public class StarterJpaConfig { - @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 - AppProperties appProperties; - @Autowired - private DataSource myDataSource; - @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() { - 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( - 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; - } - - @Autowired - private ISearchParamRegistry mySearchParamRegistry; - - @Bean - public IHSearchSortHelper hSearchSortHelper() { - return new HSearchSortHelperImpl(mySearchParamRegistry); - } - @Bean - public static IMdmLinkDao mdmLinkDao(){ - return new MdmLinkDaoJpaImpl(); - } - - -} From 4bdd0a47c467bd9321ada191548d960fd7046f3e Mon Sep 17 00:00:00 2001 From: Shubham Parikh Date: Mon, 16 Jan 2023 12:39:35 +0530 Subject: [PATCH 199/200] Updated code as per PR suggestion --- README.md | 174 +-------------------------------------------- docker-compose.yml | 8 +-- 2 files changed, 4 insertions(+), 178 deletions(-) diff --git a/README.md b/README.md index b98e150170c..487ad5789d6 100644 --- a/README.md +++ b/README.md @@ -82,159 +82,6 @@ volumes: external: true ``` -## 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 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 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 entirely depends on your environment requirements. At least, the following 4 ways are supported: @@ -324,6 +171,7 @@ Server will then be accessible at http://localhost:8080/ and eg. http://localhos server_address: 'http://localhost:8080/fhir' refuse_to_fetch_third_party_urls: false fhir_version: R4 + ``` You can use a custom property file that utilizes environment variables for many configuration properties. For example, to use the application-custom.yaml file. Replace with actual values. @@ -349,8 +197,6 @@ You can use a custom property file that utilizes environment variables for many hapi-fhir/hapi-fhir-jpaserver-starter:latest ``` - - ## Configurations Much of this HAPI starter project can be configured using the yaml file in _src/main/resources/application.yaml_. By default, this starter project is configured to use H2 as the database. @@ -550,23 +396,6 @@ Set `hapi.fhir.cql_enabled=true` in the [application.yaml](https://github.com/ha Set `hapi.fhir.mdm_enabled=true` in the [application.yaml](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/application.yaml) file to enable MDM on this server. The MDM matching rules are configured in [mdm-rules.json](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/mdm-rules.json). The rules in this example file should be replaced with actual matching rules appropriate to your data. Note that MDM relies on subscriptions, so for MDM to work, subscriptions must be enabled. -## Enabling EMPI - -Set `empi.enabled=true` in the [hapi.properties](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/hapi.properties) file to enable EMPI on this server. The EMPI matching rules are configured in [empi-rules.json](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/empi-rules.json). The rules in this example file should be replaced with actual matching rules appropriate to your data. Note that EMPI relies on subscriptions, so for EMPI to work, subscriptions must be enabled. - - -## Enabling EMPI - -Set `empi.enabled=true` in the [hapi.properties](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/hapi.properties) file to enable EMPI on this server. The EMPI matching rules are configured in [empi-rules.json](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/empi-rules.json). The rules in this example file should be replaced with actual matching rules appropriate to your data. Note that EMPI relies on subscriptions, so for EMPI to work, subscriptions must be enabled. - -## Enabling EMPI - -Set `empi.enabled=true` in the [hapi.properties](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/hapi.properties) file to enable EMPI on this server. The EMPI matching rules are configured in [empi-rules.json](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/empi-rules.json). The rules in this example file should be replaced with actual matching rules appropriate to your data. Note that EMPI relies on subscriptions, so for EMPI to work, subscriptions must be enabled. - -## Enabling EMPI - -Set `empi.enabled=true` in the [hapi.properties](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/hapi.properties) file to enable EMPI on this server. The EMPI matching rules are configured in [empi-rules.json](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/empi-rules.json). The rules in this example file should be replaced with actual matching rules appropriate to your data. Note that EMPI relies on subscriptions, so for EMPI to work, subscriptions must be enabled. - ## Using Elasticsearch By default, the server will use embedded lucene indexes for terminology and fulltext indexing purposes. You can switch this to using lucene by editing the properties in [application.yaml](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/application.yaml) @@ -630,3 +459,4 @@ docker run --rm -it -p 8080:8080 \ ``` You can configure the agent using environment variables or Java system properties, see for details. + diff --git a/docker-compose.yml b/docker-compose.yml index 6e48890dfd5..ed04e0c586f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,17 +7,13 @@ services: ports: - "8080:8080" environment: - fhir_version: 'DSTU2' - hapi.fhir.server_address: 'http://localhost:8080/baseDstu2/' - reuse_cached_search_results_millis: '0' - subscription.resthook.enabled: 'true' - subscription.websocket.enabled: 'true' + fhir_version: 'R4' + hapi.fhir.server_address: 'http://localhost:8080/R4/' spring.datasource.url: 'jdbc:postgresql://hapi-fhir-postgres:5432/hapi' spring.datasource.username: admin spring.datasource.password: admin spring.datasource.driverClassName: org.postgresql.Driver spring.jpa.properties.hibernate.dialect: ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgres94Dialect - spring.jpa.hibernate.search.enabled: 'false' hapi-fhir-postgres: image: postgres:13-alpine container_name: hapi-fhir-postgres From 5ac9c44cc34d92c9ac3f3dc1f23cb8464326195a Mon Sep 17 00:00:00 2001 From: Shubham Parikh Date: Tue, 17 Jan 2023 12:21:16 +0530 Subject: [PATCH 200/200] Updated code as suggested on pr --- docker-compose.yml | 2 +- src/main/resources/application.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index ed04e0c586f..c549b34ca14 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,7 +8,7 @@ services: - "8080:8080" environment: fhir_version: 'R4' - hapi.fhir.server_address: 'http://localhost:8080/R4/' + hapi.fhir.server_address: 'http://localhost:8080/baseR4' spring.datasource.url: 'jdbc:postgresql://hapi-fhir-postgres:5432/hapi' spring.datasource.username: admin spring.datasource.password: admin diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 1ba013370aa..67a652fdbc8 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -40,7 +40,7 @@ spring: # hibernate.cache.use_structured_entries: false # hibernate.cache.use_minimal_puts: false ### These settings will enable fulltext search with lucene or elastic - hibernate.search.enabled: false + hibernate.search.enabled: true ### lucene parameters # hibernate.search.backend.type: lucene # hibernate.search.backend.analysis.configurer: ca.uhn.fhir.jpa.search.HapiHSearchAnalysisConfigurers$HapiLuceneAnalysisConfigurer