diff --git a/pom.xml b/pom.xml
index d9eff39b565..c0d77bc4b4a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -14,7 +14,7 @@
ca.uhn.hapi.fhir
hapi-fhir
- 7.4.0
+ 7.6.0
hapi-fhir-jpaserver-starter
@@ -44,15 +44,7 @@
-
-
-
-
-
-
-
-
-
+
org.glassfish.jaxb
jaxb-runtime
2.3.8
@@ -381,6 +373,14 @@
1.13.3
+
+
+ io.micrometer
+ micrometer-registry-prometheus-simpleclient
+ 1.13.3
+
+
+
com.zaxxer
HikariCP
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 7b4ab085208..8b6569d0f05 100644
--- a/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java
+++ b/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java
@@ -83,6 +83,7 @@ public class AppProperties {
private Subscription subscription = new Subscription();
private Cors cors = null;
private Partitioning partitioning = null;
+ private Boolean validate_resource_status_for_package_upload = true;
private Boolean install_transitive_ig_dependencies = true;
private Map implementationGuides = null;
@@ -106,7 +107,11 @@ public class AppProperties {
private final List custom_interceptor_classes = new ArrayList<>();
private final List custom_provider_classes = new ArrayList<>();
+ private Boolean upliftedRefchains_enabled = false;
+ private boolean userRequestRetryVersionConflictsInterceptorEnabled = false;
+
+ private List search_prefetch_thresholds = new ArrayList<>();
public List getCustomInterceptorClasses() {
return custom_interceptor_classes;
@@ -613,6 +618,14 @@ public void setInstall_transitive_ig_dependencies(boolean install_transitive_ig_
this.install_transitive_ig_dependencies = install_transitive_ig_dependencies;
}
+ public Boolean getValidate_resource_status_for_package_upload() {
+ return validate_resource_status_for_package_upload;
+ }
+
+ public void setValidate_resource_status_for_package_upload(Boolean validate_resource_status_for_package_upload) {
+ this.validate_resource_status_for_package_upload = validate_resource_status_for_package_upload;
+ }
+
public Integer getBundle_batch_pool_size() {
return this.bundle_batch_pool_size;
}
@@ -670,6 +683,30 @@ public void setLanguage_search_parameter_enabled(Boolean language_search_paramet
this.language_search_parameter_enabled = language_search_parameter_enabled;
}
+ public List getSearch_prefetch_thresholds() {
+ return this.search_prefetch_thresholds;
+ }
+
+ public void setSearch_prefetch_thresholds(List thePrefetchThresholds) {
+ this.search_prefetch_thresholds = thePrefetchThresholds;
+ }
+
+ public boolean getUpliftedRefchains_enabled() {
+ return upliftedRefchains_enabled;
+ }
+
+ public void setUpliftedRefchains_enabled(boolean upliftedRefchains_enabled) {
+ this.upliftedRefchains_enabled = upliftedRefchains_enabled;
+ }
+
+ public Boolean getUserRequestRetryVersionConflictsInterceptorEnabled() {
+ return userRequestRetryVersionConflictsInterceptorEnabled;
+ }
+
+ public void setUserRequestRetryVersionConflictsInterceptorEnabled(Boolean userRequestRetryVersionConflictsInterceptorEnabled) {
+ this.userRequestRetryVersionConflictsInterceptorEnabled = userRequestRetryVersionConflictsInterceptorEnabled;
+ }
+
public static class Cors {
private Boolean allow_Credentials = true;
private List allowed_origin = List.of("*");
@@ -689,8 +726,6 @@ public Boolean getAllow_Credentials() {
public void setAllow_Credentials(Boolean allow_Credentials) {
this.allow_Credentials = allow_Credentials;
}
-
-
}
public static class Oauth {
@@ -951,6 +986,7 @@ public static class Partitioning {
private Boolean partitioning_include_in_search_hashes = false;
private Boolean allow_references_across_partitions = false;
+ private Boolean conditional_create_duplicate_identifiers_enabled = false;
public Boolean getPartitioning_include_in_search_hashes() {
return partitioning_include_in_search_hashes;
@@ -966,6 +1002,14 @@ public Boolean getAllow_references_across_partitions() {
public void setAllow_references_across_partitions(Boolean allow_references_across_partitions) {
this.allow_references_across_partitions = allow_references_across_partitions;
}
+
+ public Boolean getConditional_create_duplicate_identifiers_enabled() {
+ return conditional_create_duplicate_identifiers_enabled;
+ }
+
+ public void setConditional_create_duplicate_identifiers_enabled(Boolean conditional_create_duplicate_identifiers_enabled) {
+ this.conditional_create_duplicate_identifiers_enabled = conditional_create_duplicate_identifiers_enabled;
+ }
}
public static class Subscription {
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 b6b8cf754bf..1dc2fdbf10d 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
@@ -151,7 +151,12 @@ public JpaStorageSettings jpaStorageSettings(AppProperties appProperties) {
jpaStorageSettings.setDeleteExpungeEnabled(appProperties.getDelete_expunge_enabled());
jpaStorageSettings.setExpungeEnabled(appProperties.getExpunge_enabled());
jpaStorageSettings.setLanguageSearchParameterEnabled(appProperties.getLanguage_search_parameter_enabled());
+ jpaStorageSettings.setValidateResourceStatusForPackageUpload(appProperties.getValidate_resource_status_for_package_upload());
+ jpaStorageSettings.setIndexOnUpliftedRefchains(appProperties.getUpliftedRefchains_enabled());
+ if (!appProperties.getSearch_prefetch_thresholds().isEmpty()) {
+ jpaStorageSettings.setSearchPreFetchThresholds(appProperties.getSearch_prefetch_thresholds());
+ }
Integer maxFetchSize = appProperties.getMax_page_size();
jpaStorageSettings.setFetchSizeDefaultMaximum(maxFetchSize);
@@ -244,6 +249,8 @@ public PartitionSettings partitionSettings(AppProperties appProperties) {
} else {
retVal.setAllowReferencesAcrossPartitions(CrossPartitionReferenceMode.NOT_ALLOWED);
}
+ retVal.setConditionalCreateDuplicateIdentifiersEnabled(
+ appProperties.getPartitioning().getConditional_create_duplicate_identifiers_enabled());
}
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
index 2ef61d33c18..d94d16659bd 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,5 +1,6 @@
package ca.uhn.fhir.jpa.starter.common;
+import ca.uhn.fhir.batch2.config.Batch2JobRegisterer;
import ca.uhn.fhir.batch2.coordinator.JobDefinitionRegistry;
import ca.uhn.fhir.batch2.jobs.export.BulkDataExportProvider;
import ca.uhn.fhir.batch2.jobs.imprt.BulkDataImportProvider;
@@ -28,6 +29,7 @@
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.UserRequestRetryVersionConflictsInterceptor;
import ca.uhn.fhir.jpa.interceptor.validation.RepositoryValidatingInterceptor;
import ca.uhn.fhir.jpa.ips.provider.IpsOperationProvider;
import ca.uhn.fhir.jpa.model.config.SubscriptionSettings;
@@ -50,7 +52,6 @@
import ca.uhn.fhir.jpa.starter.interceptor.SmartWellKnownInterceptor;
import ca.uhn.fhir.jpa.starter.ig.IImplementationGuideOperationProvider;
import ca.uhn.fhir.jpa.starter.util.EnvironmentHelper;
-import ca.uhn.fhir.jpa.starter.ig.IImplementationGuideOperationProvider;
import ca.uhn.fhir.jpa.subscription.util.SubscriptionDebugLogInterceptor;
import ca.uhn.fhir.jpa.util.ResourceCountCache;
import ca.uhn.fhir.jpa.validation.JpaValidationSupportChain;
@@ -71,6 +72,8 @@
import com.google.common.base.Strings;
import jakarta.persistence.EntityManagerFactory;
import org.hl7.fhir.common.hapi.validation.support.CachingValidationSupport;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
@@ -94,7 +97,7 @@
@Import(ThreadPoolFactoryConfig.class)
public class StarterJpaConfig {
- private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(StarterJpaConfig.class);
+ private static final Logger ourLog = LoggerFactory.getLogger(StarterJpaConfig.class);
@Bean
public IFulltextSearchSvc fullTextSearchSvc() {
@@ -196,11 +199,11 @@ public LoggingInterceptor loggingInterceptor(AppProperties appProperties) {
@Primary
@Conditional(OnImplementationGuidesPresent.class)
public IPackageInstallerSvc packageInstaller(
- AppProperties appProperties,
- JobDefinition reindexJobParametersJobDefinition,
- JobDefinitionRegistry jobDefinitionRegistry,
- IPackageInstallerSvc packageInstallerSvc) {
- jobDefinitionRegistry.addJobDefinitionIfNotRegistered(reindexJobParametersJobDefinition);
+ AppProperties appProperties,
+ IPackageInstallerSvc packageInstallerSvc,
+ Batch2JobRegisterer batch2JobRegisterer) {
+
+ batch2JobRegisterer.start();
if (appProperties.getImplementationGuides() != null) {
Map guides = appProperties.getImplementationGuides();
@@ -481,6 +484,10 @@ public RestfulServer restfulServer(
fhirServer.registerProvider(theIpsOperationProvider.get());
}
+ if (appProperties.getUserRequestRetryVersionConflictsInterceptorEnabled() ) {
+ fhirServer.registerInterceptor(new UserRequestRetryVersionConflictsInterceptor());
+ }
+
// register custom providers
registerCustomProviders(fhirServer, appContext, appProperties.getCustomProviderClasses());
diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/cr/CareGapsProperties.java b/src/main/java/ca/uhn/fhir/jpa/starter/cr/CareGapsProperties.java
index fe4565b08fe..f25d7b2b73f 100644
--- a/src/main/java/ca/uhn/fhir/jpa/starter/cr/CareGapsProperties.java
+++ b/src/main/java/ca/uhn/fhir/jpa/starter/cr/CareGapsProperties.java
@@ -1,22 +1,22 @@
package ca.uhn.fhir.jpa.starter.cr;
public class CareGapsProperties {
- private String reporter = "default";
+ private String reporter = "default";
private String section_author = "default";
- public String getReporter() {
- return reporter;
- }
+ public String getReporter() {
+ return reporter;
+ }
- public void setReporter(String reporter) {
- this.reporter = reporter;
- }
+ public void setReporter(String reporter) {
+ this.reporter = reporter;
+ }
- public String getSection_author() {
- return section_author;
- }
+ public String getSection_author() {
+ return section_author;
+ }
- public void setSection_author(String section_author) {
- this.section_author = section_author;
- }
+ public void setSection_author(String section_author) {
+ this.section_author = section_author;
+ }
}
diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/cr/CqlCompilerProperties.java b/src/main/java/ca/uhn/fhir/jpa/starter/cr/CqlCompilerProperties.java
index ce44ca30cd8..f392b04978a 100644
--- a/src/main/java/ca/uhn/fhir/jpa/starter/cr/CqlCompilerProperties.java
+++ b/src/main/java/ca/uhn/fhir/jpa/starter/cr/CqlCompilerProperties.java
@@ -5,7 +5,7 @@
import org.cqframework.cql.cql2elm.LibraryBuilder;
public class CqlCompilerProperties {
- private Boolean validate_units = true;
+ private Boolean validate_units = true;
private Boolean verify_only = false;
private String compatibility_level = "1.5";
private CqlCompilerException.ErrorSeverity error_level = CqlCompilerException.ErrorSeverity.Info;
@@ -27,7 +27,6 @@ public class CqlCompilerProperties {
private Boolean require_from_keyword = false;
private Boolean disable_default_model_info_load = false;
-
public boolean isValidateUnits() {
return validate_units;
}
@@ -194,5 +193,5 @@ public CqlTranslator.Format getTranslatorFormat() {
public void setTranslatorFormat(CqlTranslator.Format translatorFormat) {
this.translator_format = translatorFormat;
- }
+ }
}
diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/cr/CqlProperties.java b/src/main/java/ca/uhn/fhir/jpa/starter/cr/CqlProperties.java
index 53f297fd871..845e0c37a9c 100644
--- a/src/main/java/ca/uhn/fhir/jpa/starter/cr/CqlProperties.java
+++ b/src/main/java/ca/uhn/fhir/jpa/starter/cr/CqlProperties.java
@@ -5,49 +5,49 @@
public class CqlProperties {
- private Boolean use_embedded_libraries = true;
- private CqlCompilerProperties compiler = new CqlCompilerProperties();
- private CqlRuntimeProperties runtime = new CqlRuntimeProperties();
- private TerminologySettings terminology = new TerminologySettings();
- private RetrieveSettings data = new RetrieveSettings();
-
- public Boolean getUse_embedded_libraries() {
- return use_embedded_libraries;
- }
-
- public void setUse_embedded_libraries(Boolean use_embedded_libraries) {
- this.use_embedded_libraries = use_embedded_libraries;
- }
-
- public CqlCompilerProperties getCompiler() {
- return compiler;
- }
-
- public void setCompiler(CqlCompilerProperties compiler) {
- this.compiler = compiler;
- }
-
- public CqlRuntimeProperties getRuntime() {
- return runtime;
- }
-
- public void setRuntime(CqlRuntimeProperties runtime) {
- this.runtime = runtime;
- }
-
- public TerminologySettings getTerminology() {
- return terminology;
- }
-
- public void setTerminology(TerminologySettings terminology) {
- this.terminology = terminology;
- }
-
- public RetrieveSettings getData() {
- return data;
- }
-
- public void setData(RetrieveSettings data) {
- this.data = data;
- }
+ private Boolean use_embedded_libraries = true;
+ private CqlCompilerProperties compiler = new CqlCompilerProperties();
+ private CqlRuntimeProperties runtime = new CqlRuntimeProperties();
+ private TerminologySettings terminology = new TerminologySettings();
+ private RetrieveSettings data = new RetrieveSettings();
+
+ public Boolean getUse_embedded_libraries() {
+ return use_embedded_libraries;
+ }
+
+ public void setUse_embedded_libraries(Boolean use_embedded_libraries) {
+ this.use_embedded_libraries = use_embedded_libraries;
+ }
+
+ public CqlCompilerProperties getCompiler() {
+ return compiler;
+ }
+
+ public void setCompiler(CqlCompilerProperties compiler) {
+ this.compiler = compiler;
+ }
+
+ public CqlRuntimeProperties getRuntime() {
+ return runtime;
+ }
+
+ public void setRuntime(CqlRuntimeProperties runtime) {
+ this.runtime = runtime;
+ }
+
+ public TerminologySettings getTerminology() {
+ return terminology;
+ }
+
+ public void setTerminology(TerminologySettings terminology) {
+ this.terminology = terminology;
+ }
+
+ public RetrieveSettings getData() {
+ return data;
+ }
+
+ public void setData(RetrieveSettings data) {
+ this.data = data;
+ }
}
diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/cr/CqlRuntimeProperties.java b/src/main/java/ca/uhn/fhir/jpa/starter/cr/CqlRuntimeProperties.java
index f0dbfdb9af4..0677d05e9d6 100644
--- a/src/main/java/ca/uhn/fhir/jpa/starter/cr/CqlRuntimeProperties.java
+++ b/src/main/java/ca/uhn/fhir/jpa/starter/cr/CqlRuntimeProperties.java
@@ -2,7 +2,7 @@
public class CqlRuntimeProperties {
- private Boolean debug_logging_enabled = false;
+ private Boolean debug_logging_enabled = false;
private Boolean enable_validation = false;
private Boolean enable_expression_caching = true;
@@ -14,7 +14,6 @@ public void setDebugLoggingEnabled(boolean debug_logging_enabled) {
this.debug_logging_enabled = debug_logging_enabled;
}
-
public boolean isEnableExpressionCaching() {
return enable_expression_caching;
}
@@ -30,6 +29,4 @@ public boolean isEnableValidation() {
public void EnableValidation(boolean enable_validation) {
this.enable_validation = enable_validation;
}
-
-
}
diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/cr/CrCommonConfig.java b/src/main/java/ca/uhn/fhir/jpa/starter/cr/CrCommonConfig.java
index 59274ed8670..26d7f5f9fa6 100644
--- a/src/main/java/ca/uhn/fhir/jpa/starter/cr/CrCommonConfig.java
+++ b/src/main/java/ca/uhn/fhir/jpa/starter/cr/CrCommonConfig.java
@@ -1,13 +1,14 @@
package ca.uhn.fhir.jpa.starter.cr;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
+import ca.uhn.fhir.cr.common.CodeCacheResourceChangeListener;
+import ca.uhn.fhir.cr.common.CqlThreadFactory;
+import ca.uhn.fhir.cr.common.ElmCacheResourceChangeListener;
+import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
+import ca.uhn.fhir.jpa.cache.IResourceChangeListenerRegistry;
+import ca.uhn.fhir.jpa.cache.ResourceChangeListenerRegistryInterceptor;
+import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
+import ca.uhn.fhir.rest.server.RestfulServer;
+import ca.uhn.fhir.rest.server.provider.ResourceProviderFactory;
import org.cqframework.cql.cql2elm.CqlCompilerOptions;
import org.cqframework.cql.cql2elm.model.CompiledLibrary;
import org.cqframework.cql.cql2elm.model.Model;
@@ -28,15 +29,13 @@
import org.springframework.context.annotation.Primary;
import org.springframework.security.concurrent.DelegatingSecurityContextExecutorService;
-import ca.uhn.fhir.cr.common.CodeCacheResourceChangeListener;
-import ca.uhn.fhir.cr.common.CqlThreadFactory;
-import ca.uhn.fhir.cr.common.ElmCacheResourceChangeListener;
-import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
-import ca.uhn.fhir.jpa.cache.IResourceChangeListenerRegistry;
-import ca.uhn.fhir.jpa.cache.ResourceChangeListenerRegistryInterceptor;
-import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
-import ca.uhn.fhir.rest.server.RestfulServer;
-import ca.uhn.fhir.rest.server.provider.ResourceProviderFactory;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
@Configuration
@Conditional({CrConfigCondition.class})
@@ -157,8 +156,12 @@ public ExecutorService cqlExecutor() {
@Bean
CareGapsProperties careGapsProperties(CrProperties theCrProperties) {
var careGapsProperties = new CareGapsProperties();
- careGapsProperties.setCareGapsReporter(theCrProperties.getCareGaps().getReporter());
- careGapsProperties.setCareGapsCompositionSectionAuthor(theCrProperties.getCareGaps().getSection_author());
+ // This check for the resource type really should be happening down in CR where the setting is actually used but
+ // that will have to wait for a future CR release
+ careGapsProperties.setCareGapsReporter(
+ theCrProperties.getCareGaps().getReporter().replace("Organization/", ""));
+ careGapsProperties.setCareGapsCompositionSectionAuthor(
+ theCrProperties.getCareGaps().getSection_author().replace("Organization/", ""));
return careGapsProperties;
}
@@ -224,5 +227,5 @@ public CodeCacheResourceChangeListener codeCacheResourceChangeListener(
@Bean
public ResourceChangeListenerRegistryInterceptor resourceChangeListenerRegistryInterceptor() {
return new ResourceChangeListenerRegistryInterceptor();
- }
+ }
}
diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/cr/CrProperties.java b/src/main/java/ca/uhn/fhir/jpa/starter/cr/CrProperties.java
index 58ca3c2e82e..8ba5ccf0e79 100644
--- a/src/main/java/ca/uhn/fhir/jpa/starter/cr/CrProperties.java
+++ b/src/main/java/ca/uhn/fhir/jpa/starter/cr/CrProperties.java
@@ -1,9 +1,5 @@
package ca.uhn.fhir.jpa.starter.cr;
-import org.cqframework.cql.cql2elm.CqlCompilerException;
-import org.cqframework.cql.cql2elm.CqlTranslator;
-import org.cqframework.cql.cql2elm.LibraryBuilder;
-
public class CrProperties {
private Boolean enabled;
diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/cr/StarterCrDstu3Config.java b/src/main/java/ca/uhn/fhir/jpa/starter/cr/StarterCrDstu3Config.java
index 77e91bbfcfe..44f7d42a01c 100644
--- a/src/main/java/ca/uhn/fhir/jpa/starter/cr/StarterCrDstu3Config.java
+++ b/src/main/java/ca/uhn/fhir/jpa/starter/cr/StarterCrDstu3Config.java
@@ -2,10 +2,9 @@
import ca.uhn.fhir.cr.config.dstu3.ApplyOperationConfig;
import ca.uhn.fhir.cr.config.dstu3.CrDstu3Config;
-import ca.uhn.fhir.cr.config.dstu3.ExtractOperationConfig;
+import ca.uhn.fhir.cr.config.dstu3.DataRequirementsOperationConfig;
+import ca.uhn.fhir.cr.config.dstu3.EvaluateOperationConfig;
import ca.uhn.fhir.cr.config.dstu3.PackageOperationConfig;
-import ca.uhn.fhir.cr.config.dstu3.PopulateOperationConfig;
-import ca.uhn.fhir.cr.config.dstu3.QuestionnaireOperationConfig;
import ca.uhn.fhir.jpa.starter.annotations.OnDSTU3Condition;
import org.springframework.context.annotation.*;
@@ -15,11 +14,8 @@
CrCommonConfig.class,
CrDstu3Config.class,
ApplyOperationConfig.class,
- ExtractOperationConfig.class,
- PackageOperationConfig.class,
- PopulateOperationConfig.class,
- QuestionnaireOperationConfig.class
+ DataRequirementsOperationConfig.class,
+ EvaluateOperationConfig.class,
+ PackageOperationConfig.class
})
-public class StarterCrDstu3Config {
-
-}
+public class StarterCrDstu3Config {}
diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/cr/StarterCrR4Config.java b/src/main/java/ca/uhn/fhir/jpa/starter/cr/StarterCrR4Config.java
index ee53bf2e872..28c10cf9b82 100644
--- a/src/main/java/ca/uhn/fhir/jpa/starter/cr/StarterCrR4Config.java
+++ b/src/main/java/ca/uhn/fhir/jpa/starter/cr/StarterCrR4Config.java
@@ -2,6 +2,8 @@
import ca.uhn.fhir.cr.config.r4.ApplyOperationConfig;
import ca.uhn.fhir.cr.config.r4.CrR4Config;
+import ca.uhn.fhir.cr.config.r4.DataRequirementsOperationConfig;
+import ca.uhn.fhir.cr.config.r4.EvaluateOperationConfig;
import ca.uhn.fhir.cr.config.r4.ExtractOperationConfig;
import ca.uhn.fhir.cr.config.r4.PackageOperationConfig;
import ca.uhn.fhir.cr.config.r4.PopulateOperationConfig;
@@ -17,11 +19,11 @@
CrCommonConfig.class,
CrR4Config.class,
ApplyOperationConfig.class,
+ DataRequirementsOperationConfig.class,
+ EvaluateOperationConfig.class,
ExtractOperationConfig.class,
PackageOperationConfig.class,
PopulateOperationConfig.class,
QuestionnaireOperationConfig.class
})
-public class StarterCrR4Config {
-
-}
+public class StarterCrR4Config {}
diff --git a/src/main/resources/application-custom.yaml b/src/main/resources/application-custom.yaml
index a294fd2ab45..3c9fb91af1d 100644
--- a/src/main/resources/application-custom.yaml
+++ b/src/main/resources/application-custom.yaml
@@ -1,31 +1,37 @@
#Uncomment the "servlet" and "context-path" lines below to make the fhir endpoint available at /example/path/fhir instead of the default value of /fhir
server:
-# servlet:
-# context-path: /example/path
+ # servlet:
+ # context-path: /example/path
port: 8080
#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:
+ #The following configuration will enable the actuator endpoints at /actuator/health, /actuator/info, /actuator/prometheus, /actuator/metrics. For security purposes, only /actuator/health is enabled by default.
+ endpoints:
+ enabled-by-default: false
+ web:
+ exposure:
+ include: 'health' # or e.g. 'info,health,prometheus,metrics' or '*' for all'
endpoint:
- endpoints:
- enabled-by-default: false
- web:
- exposure:
- include: health,prometheus
+ info:
+ enabled: true
+ metrics:
+ enabled: true
health:
enabled: true
probes:
enabled: true
- livenessState:
- enabled: true
- readinessState:
- enabled: true
+ group:
+ liveness:
+ include:
+ - livenessState
+ - readinessState
prometheus:
enabled: true
- metrics:
- export:
- enabled: true
-
+ prometheus:
+ metrics:
+ export:
+ enabled: true
spring:
main:
allow-circular-references: true
@@ -123,7 +129,7 @@ hapi:
valueset_membership_mode: USE_EXPANSION # AUTO, USE_VALIDATE_CODE_OPERATION, USE_EXPANSION
code_lookup_mode: USE_VALIDATE_CODE_OPERATION # AUTO, USE_VALIDATE_CODE_OPERATION, USE_CODESYSTEM_URL
data:
- search_parameter_mode: FILTER_IN_MEMORY # AUTO, USE_SEARCH_PARAMETERS, FILTER_IN_MEMORY
+ search_parameter_mode: USE_SEARCH_PARAMETERS # AUTO, USE_SEARCH_PARAMETERS, FILTER_IN_MEMORY
terminology_parameter_mode: FILTER_IN_MEMORY # AUTO, USE_VALUE_SET_URL, USE_INLINE_CODES, FILTER_IN_MEMORY
profile_mode: DECLARED # ENFORCED, DECLARED, OPTIONAL, TRUST, OFF
@@ -155,6 +161,8 @@ hapi:
# server_path: /baseR4
# server_address: http://hapi.fhir.org/baseR4
# defer_indexing_for_codesystems_of_size: 101
+ ### Flag is true by default. This flag filters resources during package installation, allowing only those resources with a valid status (e.g. active) to be installed.
+ # validate_resource_status_for_package_upload: false
# install_transitive_ig_dependencies: true
#implementationguides:
### example from registry (packages.fhir.org)
@@ -193,6 +201,7 @@ hapi:
# enable_index_missing_fields: false
# enable_index_of_type: true
# enable_index_contained_resource: false
+ # upliftedRefchains_enabled: true
# resource_dbhistory_enabled: 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
@@ -214,6 +223,8 @@ hapi:
narrative_enabled: false
mdm_enabled: false
mdm_rules_json_location: "mdm-rules.json"
+ ## see: https://hapifhir.io/hapi-fhir/docs/interceptors/built_in_server_interceptors.html#jpa-server-retry-on-version-conflicts
+ # userRequestRetryVersionConflictsInterceptorEnabled : false
# local_base_urls:
# - https://hapi.fhir.org/baseR4
logical_urls:
@@ -228,6 +239,7 @@ hapi:
# partitioning:
# allow_references_across_partitions: false
# partitioning_include_in_search_hashes: false
+ # conditional_create_duplicate_identifiers_enabled: 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-
@@ -239,6 +251,17 @@ hapi:
search-coord-max-pool-size: 100
search-coord-queue-capacity: 200
+ # Search Prefetch Thresholds.
+
+ # This setting sets the number of search results to prefetch. For example, if this list
+ # is set to [100, 1000, -1] then the server will initially load 100 results and not
+ # attempt to load more. If the user requests subsequent page(s) of results and goes
+ # past 100 results, the system will load the next 900 (up to the following threshold of 1000).
+ # The system will progressively work through these thresholds.
+ # A threshold of -1 means to load all results. Note that if the final threshold is a
+ # number other than -1, the system will never prefetch more than the given number.
+ search_prefetch_thresholds: 13,503,2003,-1
+
# comma-separated package names, will be @ComponentScan'ed by Spring to allow for creating custom Spring beans
#custom-bean-packages:
diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml
index f395ea3d278..80bc2d7627a 100644
--- a/src/main/resources/application.yaml
+++ b/src/main/resources/application.yaml
@@ -1,31 +1,37 @@
#Uncomment the "servlet" and "context-path" lines below to make the fhir endpoint available at /example/path/fhir instead of the default value of /fhir
server:
-# servlet:
-# context-path: /example/path
+ # servlet:
+ # context-path: /example/path
port: 8080
#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:
+ #The following configuration will enable the actuator endpoints at /actuator/health, /actuator/info, /actuator/prometheus, /actuator/metrics. For security purposes, only /actuator/health is enabled by default.
+ endpoints:
+ enabled-by-default: false
+ web:
+ exposure:
+ include: 'health' # or e.g. 'info,health,prometheus,metrics' or '*' for all'
endpoint:
- endpoints:
- enabled-by-default: false
- web:
- exposure:
- include: health,prometheus
+ info:
+ enabled: true
+ metrics:
+ enabled: true
health:
enabled: true
probes:
enabled: true
- livenessState:
- enabled: true
- readinessState:
- enabled: true
+ group:
+ liveness:
+ include:
+ - livenessState
+ - readinessState
prometheus:
enabled: true
- metrics:
- export:
- enabled: true
-
+ prometheus:
+ metrics:
+ export:
+ enabled: true
spring:
main:
allow-circular-references: true
@@ -115,7 +121,7 @@ hapi:
valueset_membership_mode: USE_EXPANSION # AUTO, USE_VALIDATE_CODE_OPERATION, USE_EXPANSION
code_lookup_mode: USE_VALIDATE_CODE_OPERATION # AUTO, USE_VALIDATE_CODE_OPERATION, USE_CODESYSTEM_URL
data:
- search_parameter_mode: FILTER_IN_MEMORY # AUTO, USE_SEARCH_PARAMETERS, FILTER_IN_MEMORY
+ search_parameter_mode: USE_SEARCH_PARAMETERS # AUTO, USE_SEARCH_PARAMETERS, FILTER_IN_MEMORY
terminology_parameter_mode: FILTER_IN_MEMORY # AUTO, USE_VALUE_SET_URL, USE_INLINE_CODES, FILTER_IN_MEMORY
profile_mode: DECLARED # ENFORCED, DECLARED, OPTIONAL, TRUST, OFF
@@ -147,6 +153,8 @@ hapi:
# server_path: /baseR4
# server_address: http://hapi.fhir.org/baseR4
# defer_indexing_for_codesystems_of_size: 101
+ ### Flag is true by default. This flag filters resources during package installation, allowing only those resources with a valid status (e.g. active) to be installed.
+ # validate_resource_status_for_package_upload: false
# install_transitive_ig_dependencies: true
#implementationguides:
### example from registry (packages.fhir.org)
@@ -185,6 +193,7 @@ hapi:
# enable_index_missing_fields: false
# enable_index_of_type: true
# enable_index_contained_resource: false
+ # upliftedRefchains_enabled: true
# resource_dbhistory_enabled: 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
@@ -206,6 +215,8 @@ hapi:
narrative_enabled: false
mdm_enabled: false
mdm_rules_json_location: "mdm-rules.json"
+ ## see: https://hapifhir.io/hapi-fhir/docs/interceptors/built_in_server_interceptors.html#jpa-server-retry-on-version-conflicts
+ # userRequestRetryVersionConflictsInterceptorEnabled : false
# local_base_urls:
# - https://hapi.fhir.org/baseR4
logical_urls:
@@ -220,6 +231,7 @@ hapi:
# partitioning:
# allow_references_across_partitions: false
# partitioning_include_in_search_hashes: false
+ # conditional_create_duplicate_identifiers_enabled: 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-
@@ -231,6 +243,17 @@ hapi:
search-coord-max-pool-size: 100
search-coord-queue-capacity: 200
+ # Search Prefetch Thresholds.
+
+ # This setting sets the number of search results to prefetch. For example, if this list
+ # is set to [100, 1000, -1] then the server will initially load 100 results and not
+ # attempt to load more. If the user requests subsequent page(s) of results and goes
+ # past 100 results, the system will load the next 900 (up to the following threshold of 1000).
+ # The system will progressively work through these thresholds.
+ # A threshold of -1 means to load all results. Note that if the final threshold is a
+ # number other than -1, the system will never prefetch more than the given number.
+ search_prefetch_thresholds: 13,503,2003,-1
+
# comma-separated package names, will be @ComponentScan'ed by Spring to allow for creating custom Spring beans
#custom-bean-packages:
diff --git a/src/main/resources/cds.application.yaml b/src/main/resources/cds.application.yaml
index b1703d86c4c..e94030c4478 100644
--- a/src/main/resources/cds.application.yaml
+++ b/src/main/resources/cds.application.yaml
@@ -6,10 +6,32 @@ server:
#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:
+ #The following configuration will enable the actuator endpoints at /actuator/health, /actuator/info, /actuator/prometheus, /actuator/metrics. For security purposes, only /actuator/health is enabled by default.
endpoints:
+ enabled-by-default: false
web:
exposure:
- include: "health,prometheus"
+ include: 'health' # or e.g. 'info,health,prometheus,metrics' or '*' for all'
+ endpoint:
+ info:
+ enabled: true
+ metrics:
+ enabled: true
+ health:
+ enabled: true
+ probes:
+ enabled: true
+ group:
+ liveness:
+ include:
+ - livenessState
+ - readinessState
+ prometheus:
+ enabled: true
+ prometheus:
+ metrics:
+ export:
+ enabled: true
spring:
main:
allow-circular-references: true
@@ -131,6 +153,8 @@ hapi:
### enable to set the Server URL
# server_address: http://hapi.fhir.org/baseR4
# defer_indexing_for_codesystems_of_size: 101
+ ### Flag is true by default. This flag filters resources during package installation, allowing only those resources with a valid status (e.g. active) to be installed.
+ # validate_resource_status_for_package_upload: false
# install_transitive_ig_dependencies: true
#implementationguides:
### example from registry (packages.fhir.org)
@@ -153,7 +177,7 @@ hapi:
# allowed_bundle_types: COLLECTION,DOCUMENT,MESSAGE,TRANSACTION,TRANSACTIONRESPONSE,BATCH,BATCHRESPONSE,HISTORY,SEARCHSET
# allow_cascading_deletes: true
# allow_contains_searches: true
- # allow_external_references: true
+ allow_external_references: true
# allow_multiple_delete: true
# allow_override_default_search_params: true
# auto_create_placeholder_reference_targets: false
@@ -203,6 +227,7 @@ hapi:
# partitioning:
# allow_references_across_partitions: false
# partitioning_include_in_search_hashes: false
+ # conditional_create_duplicate_identifiers_enabled: 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-
diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/CdsHooksServletIT.java b/src/test/java/ca/uhn/fhir/jpa/starter/CdsHooksServletIT.java
index 129a63258c7..65714878974 100644
--- a/src/test/java/ca/uhn/fhir/jpa/starter/CdsHooksServletIT.java
+++ b/src/test/java/ca/uhn/fhir/jpa/starter/CdsHooksServletIT.java
@@ -88,7 +88,14 @@ void beforeEach() {
private Boolean hasCdsServices() throws IOException {
var response = callCdsServicesDiscovery();
- return response.getEntity().getContentLength() > 21 || response.getEntity().isChunked();
+
+ // NOTE: this is looking for a repsonse that indicates there are CDS services availalble.
+ // And empty response looks like: {"services": []}
+ // Looking at the actual response string consumes the InputStream which has side-effects, making it tricky to compare the actual contents.
+ // Hence the test just looks at the length to make this determination.
+ // The actual response has newlines in it which vary in size on some systems, but a value of 25 seems to work across linux/mac/windows
+ // to ensure the repsonse actually contains CDS services in it
+ return response.getEntity().getContentLength() > 25 || response.getEntity().isChunked();
}
private CloseableHttpResponse callCdsServicesDiscovery() {
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 9d4c033c436..b8279468869 100644
--- a/src/test/java/ca/uhn/fhir/jpa/starter/ElasticsearchLastNR4IT.java
+++ b/src/test/java/ca/uhn/fhir/jpa/starter/ElasticsearchLastNR4IT.java
@@ -16,6 +16,7 @@
import java.util.List;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
+import co.elastic.clients.elasticsearch.indices.IndexSettings;
import co.elastic.clients.json.JsonData;
import jakarta.annotation.PreDestroy;
import org.elasticsearch.client.RequestOptions;
@@ -91,7 +92,7 @@ public static void beforeClass() throws IOException {
elasticsearchHighLevelRestClient.indices().putTemplate(t->{
t.name("hapi_fhir_template");
t.indexPatterns("*");
- t.settings("index.max_result_window", JsonData.of(50000));
+ t.settings(new IndexSettings.Builder().maxResultWindow(50000).build());
return t;
});
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 d7b46315dbd..041a4fce646 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,10 @@
import jakarta.websocket.ContainerProvider;
import jakarta.websocket.Session;
import jakarta.websocket.WebSocketContainer;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.Bundle;
@@ -29,6 +33,8 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort;
@@ -63,6 +69,7 @@
"hapi.fhir.cr.enabled=true",
"hapi.fhir.cr.caregaps.section_author=Organization/alphora-author",
"hapi.fhir.cr.caregaps.reporter=Organization/alphora",
+ "hapi.fhir.cr.cql.data.search_parameter_mode=USE_SEARCH_PARAMETERS",
"hapi.fhir.implementationguides.dk-core.name=hl7.fhir.dk.core",
"hapi.fhir.implementationguides.dk-core.version=1.1.0",
"hapi.fhir.auto_create_placeholder_reference_targets=true",
@@ -306,6 +313,17 @@ private int activeSubscriptionCount() {
.size();
}
+ @ParameterizedTest
+ @ValueSource(strings = {"prometheus", "health", "metrics", "info"})
+ void testActuatorEndpointExists(String endpoint) throws IOException {
+
+ CloseableHttpClient httpclient = HttpClients.createDefault();
+ CloseableHttpResponse response = httpclient.execute(new HttpGet("http://localhost:" + port + "/actuator/" + endpoint));
+ int statusCode = response.getStatusLine().getStatusCode();
+ assertEquals(200, statusCode);
+
+ }
+
@BeforeEach
void beforeEach() {
diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/ParallelUpdatesVersionConflictTest.java b/src/test/java/ca/uhn/fhir/jpa/starter/ParallelUpdatesVersionConflictTest.java
new file mode 100644
index 00000000000..29dd059bff0
--- /dev/null
+++ b/src/test/java/ca/uhn/fhir/jpa/starter/ParallelUpdatesVersionConflictTest.java
@@ -0,0 +1,164 @@
+package ca.uhn.fhir.jpa.starter;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
+import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;
+import org.hl7.fhir.r4.model.Patient;
+import org.hl7.fhir.r4.model.Bundle;
+import org.hl7.fhir.r4.model.Bundle.BundleType;
+import org.hl7.fhir.r4.model.Bundle.HTTPVerb;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.web.server.LocalServerPort;
+
+import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
+import ca.uhn.fhir.rest.api.MethodOutcome;
+import ca.uhn.fhir.rest.client.api.IGenericClient;
+import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = {Application.class}, properties = {
+ "spring.datasource.url=jdbc:h2:mem:dbr4",
+ "hapi.fhir.fhir_version=r4",
+ "hapi.fhir.userRequestRetryVersionConflictsInterceptorEnabled=true"
+})
+
+/**
+ * This class tests running parallel updates to a single resource with and without setting the 'X-Retry-On-Version-Conflict' header
+ * to ensure we get the expected behavior of detecting conflicts
+ */
+public class ParallelUpdatesVersionConflictTest {
+
+ private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ParallelUpdatesVersionConflictTest.class);
+
+ @LocalServerPort
+ private int port;
+
+ private IGenericClient client;
+ private FhirContext ctx;
+
+ @BeforeEach
+ void setUp() {
+ ctx = FhirContext.forR4();
+ ctx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
+ ctx.getRestfulClientFactory().setSocketTimeout(1200 * 1000);
+ String ourServerBase = "http://localhost:" + port + "/fhir/";
+ client = ctx.newRestfulGenericClient(ourServerBase);
+ }
+
+ @Test
+ void testParallelResourceUpdateBundle() throws Throwable {
+ //send 10 bundles with updates to the patient in parallel, except the header to deconflict them
+ Patient pat = new Patient();
+ String patId = client.create().resource(pat).execute().getId().getIdPart();
+ launchThreads(patId, true, "X-Retry-On-Version-Conflict");
+ }
+
+ @Test
+ void testParallelResourceUpdateNoBundle() throws Throwable {
+ //send 10 resource puts to the patient in parallel, except the header to deconflict them
+ Patient pat = new Patient();
+ String patId = client.create().resource(pat).execute().getId().getIdPart();
+ launchThreads(patId, false, "X-Retry-On-Version-Conflict");
+ }
+
+ @Test
+ void testParallelResourceUpdateBundleExpectConflict() {
+ //send 10 bundles with updates to the patient in parallel, expect a ResourceVersionConflictException since we are not setting the retry header
+ Patient pat = new Patient();
+ String patId = client.create().resource(pat).execute().getId().getIdPart();
+ ResourceVersionConflictException exception = assertThrows(ResourceVersionConflictException.class, () ->
+ launchThreads(patId, true, "someotherheader"));
+ }
+
+ @Test
+ void testParallelResourceUpdateNoBundleExpectConflict() {
+ //send 10 resource puts to the patient in parallel, expect a ResourceVersionConflictException since we are not setting the retry header
+ Patient pat = new Patient();
+ String patId = client.create().resource(pat).execute().getId().getIdPart();
+ ResourceVersionConflictException exception = assertThrows(ResourceVersionConflictException.class, () ->
+ launchThreads(patId, false, "someotherheader"));
+ }
+
+ private void launchThreads(String patientId, boolean useBundles, String headerName) throws Throwable {
+ int threadCnt = 10;
+ ExecutorService execSvc = Executors.newFixedThreadPool(threadCnt);
+
+ //launch a bunch of threads at the same time that update the same patient
+ List> callables = new ArrayList<>();
+ for (int i = 0; i < threadCnt; i++) {
+ final int cnt = i;
+ Callable callable = new Callable<>() {
+ @Override
+ public Integer call() throws Exception {
+ Patient pat = new Patient();
+ //make sure to change something so the server doesnt short circuit on a no-op
+ pat.addName().setFamily("fam-" + cnt);
+ pat.setId(patientId);
+
+ if( useBundles) {
+ Bundle b = new Bundle();
+ b.setType(BundleType.TRANSACTION);
+ BundleEntryComponent bec = b.addEntry();
+ bec.setResource(pat);
+ //bec.setFullUrl("Patient/" + patId);
+ Bundle.BundleEntryRequestComponent req = bec.getRequest();
+ req.setUrl("Patient/" + patientId);
+ req.setMethod(HTTPVerb.PUT);
+ bec.setRequest(req);
+
+ Bundle returnBundle = client.transaction().withBundle(b)
+ .withAdditionalHeader(headerName, "retry; max-retries=10")
+ .execute();
+
+ String statusString = returnBundle.getEntryFirstRep().getResponse().getStatus();
+ ourLog.trace("statusString->{}", statusString);
+ try {
+ return Integer.parseInt(statusString.substring(0,3));
+ }catch(NumberFormatException nfe) {
+ return 500;
+ }
+ }
+ else {
+ MethodOutcome outcome = client.update().resource(pat).withId(patientId)
+ .withAdditionalHeader(headerName, "retry; max-retries=10")
+ .execute();
+ ourLog.trace("updated patient: " + outcome.getResponseStatusCode());
+ return outcome.getResponseStatusCode();
+ }
+ }
+ };
+ callables.add(callable);
+ }
+
+ List> futures = new ArrayList<>();
+
+ //launch them all at once
+ for (Callable callable : callables) {
+ futures.add(execSvc.submit(callable));
+ }
+
+ //wait for calls to complete
+ for (Future future : futures) {
+ try {
+ Integer httpResponseCode = future.get();
+ Assertions.assertEquals(200, httpResponseCode);
+ } catch (InterruptedException | ExecutionException e) {
+ //throw the ResourceVersionConflictException back up so we can test it
+ throw e.getCause();
+ }
+ }
+ }
+}
diff --git a/src/test/resources/application.yaml b/src/test/resources/application.yaml
index 911d9709034..a4943599298 100644
--- a/src/test/resources/application.yaml
+++ b/src/test/resources/application.yaml
@@ -1,3 +1,30 @@
+management:
+ #The following configuration will enable the actuator endpoints at /actuator/health, /actuator/info, /actuator/prometheus
+ endpoints:
+ enabled-by-default: false
+ web:
+ exposure:
+ include: 'info,health,prometheus,metrics' # or '*' for all'
+ endpoint:
+ info:
+ enabled: true
+ metrics:
+ enabled: true
+ health:
+ enabled: true
+ probes:
+ enabled: true
+ group:
+ liveness:
+ include:
+ - livenessState
+ - readinessState
+ prometheus:
+ enabled: true
+ prometheus:
+ metrics:
+ export:
+ enabled: true
spring:
main:
allow-circular-references: true
@@ -67,6 +94,8 @@ hapi:
### enable to set the Server URL
# server_address: http://hapi.fhir.org/baseR4
# defer_indexing_for_codesystems_of_size: 101
+ ### Flag is true by default. This flag filters resources during package installation, allowing only those resources with a valid status (e.g. active) to be installed.
+ # validate_resource_status_for_package_upload: false
# install_transitive_ig_dependencies: true
# implementationguides:
### example from registry (packages.fhir.org)
@@ -119,6 +148,7 @@ hapi:
# partitioning:
# allow_references_across_partitions: false
# partitioning_include_in_search_hashes: false
+ # partitioning_include_in_search_hashes
#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-