Merge remote-tracking branch 'origin/master' into rel_8_3_tracking

This commit is contained in:
dotasek
2025-08-22 15:21:43 -04:00
36 changed files with 301 additions and 301 deletions

1
.gitignore vendored
View File

@@ -124,7 +124,6 @@ local.properties
.factorypath
.project
.settings
.springBeans
.sts4-cache
# Code Recommenders

View File

@@ -51,7 +51,7 @@ HAPI looks in the environment variables for properties in the [application.yaml]
### Configuration via overridden application.yaml file and using Docker
You can customize HAPI by telling HAPI to look for the configuration file in a different location, eg.:
You can customize HAPI by telling HAPI to look for the configuration file in a different location, e.g.:
```
docker run -p 8090:8080 -v $(pwd)/yourLocalFolder:/configs -e "--spring.config.location=file:///configs/another.application.yaml" hapiproject/hapi:latest
@@ -237,7 +237,7 @@ The Server will then be accessible at http://localhost:8888/fhir and the Capabil
```bash
mvn clean spring-boot:run -Pboot
```
Server will then be accessible at http://localhost:8080/ and eg. http://localhost:8080/fhir/metadata. Remember to adjust you overlay configuration in the application.yaml to the following:
Server will then be accessible at http://localhost:8080/ and e.g. http://localhost:8080/fhir/metadata. Remember to adjust you overlay configuration in the application.yaml to the following:
```yaml
tester:
@@ -253,7 +253,7 @@ Server will then be accessible at http://localhost:8080/ and eg. http://localhos
```bash
mvn clean package spring-boot:repackage -DskipTests=true -Pboot && java -jar target/ROOT.war
```
Server will then be accessible at http://localhost:8080/ and eg. http://localhost:8080/fhir/metadata. Remember to adjust your overlay configuration in the application.yaml to the following:
Server will then be accessible at http://localhost:8080/ and e.g. http://localhost:8080/fhir/metadata. Remember to adjust your overlay configuration in the application.yaml to the following:
```yaml
tester:
@@ -268,7 +268,7 @@ Server will then be accessible at http://localhost:8080/ and eg. http://localhos
```bash
mvn clean package com.google.cloud.tools:jib-maven-plugin:dockerBuild -Dimage=distroless-hapi && docker run -p 8080:8080 distroless-hapi
```
Server will then be accessible at http://localhost:8080/ and eg. http://localhost:8080/fhir/metadata. Remember to adjust your overlay configuration in the application.yaml to the following:
Server will then be accessible at http://localhost:8080/ and e.g. http://localhost:8080/fhir/metadata. Remember to adjust your overlay configuration in the application.yaml to the following:
```yaml
tester:
@@ -284,7 +284,7 @@ Server will then be accessible at http://localhost:8080/ and eg. http://localhos
```bash
./build-docker-image.sh && docker run -p 8080:8080 hapi-fhir/hapi-fhir-jpaserver-starter:latest
```
Server will then be accessible at http://localhost:8080/ and eg. http://localhost:8080/fhir/metadata. Remember to adjust your overlay configuration in the application.yaml to the following:
Server will then be accessible at http://localhost:8080/ and e.g. http://localhost:8080/fhir/metadata. Remember to adjust your overlay configuration in the application.yaml to the following:
```yaml
tester:
@@ -384,7 +384,7 @@ Several template files that can be customized are found in the following directo
Using the Maven-Embedded Jetty method above is convenient, but it is not a good solution if you want to leave the server running in the background.
Most people who are using HAPI FHIR JPA as a server that is accessible to other people (whether internally on your network or publically hosted) will do so using an Application Server, such as [Apache Tomcat](http://tomcat.apache.org/) or [Jetty](https://www.eclipse.org/jetty/). Note that any Servlet 3.0+ compatible Web Container will work (e.g Wildfly, Websphere, etc.).
Most people who are using HAPI FHIR JPA as a server that is accessible to other people (whether internally on your network or publicly hosted) will do so using an Application Server, such as [Apache Tomcat](http://tomcat.apache.org/) or [Jetty](https://www.eclipse.org/jetty/). Note that any Servlet 3.0+ compatible Web Container will work (e.g. Wildfly, Websphere, etc.).
Tomcat is very popular, so it is a good choice simply because you will be able to find many tutorials online. Jetty is a great alternative due to its fast startup time and good overall performance.
@@ -402,7 +402,7 @@ Again, browse to the following link to use the server (note that the port 8080 m
You will then be able to access the JPA server e.g. using http://localhost:8080/fhir/metadata.
If you would like it to be hosted at eg. hapi-fhir-jpaserver, eg. http://localhost:8080/hapi-fhir-jpaserver/ or http://localhost:8080/hapi-fhir-jpaserver/fhir/metadata - then rename the WAR file to ```hapi-fhir-jpaserver.war``` and adjust the overlay configuration accordingly e.g.
If you would like it to be hosted at e.g. hapi-fhir-jpaserver, e.g. http://localhost:8080/hapi-fhir-jpaserver/ or http://localhost:8080/hapi-fhir-jpaserver/fhir/metadata - then rename the WAR file to ```hapi-fhir-jpaserver.war``` and adjust the overlay configuration accordingly e.g.
```yaml
tester:
@@ -525,7 +525,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.
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 milliseconds 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)

View File

@@ -14,7 +14,7 @@ dependencies:
repository: oci://registry-1.docker.io/bitnamicharts
version: 2.31.3
appVersion: 8.2.0
version: 0.20.0
version: 0.20.1
annotations:
artifacthub.io/license: Apache-2.0
artifacthub.io/containsSecurityUpdates: "false"
@@ -27,14 +27,4 @@ annotations:
# When using the list of objects option the valid supported kinds are
# added, changed, deprecated, removed, fixed, and security.
- kind: changed
description: "updated postgresql sub-chart to 16.7.11"
- kind: changed
description: "updated common sub-chart to 2.31.3"
- kind: changed
description: "updated curlimages/curl to 8.14.1"
- kind: changed
description: "updated hapiproject/hapi to v8.2.0-1"
- kind: changed
description: "use ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgresDialect dialect"
- kind: changed
description: "made the init container waiting for the database to be ready configurable"
description: "fixed typo in README.md"

View File

@@ -1,6 +1,6 @@
# HAPI FHIR JPA Server Starter Helm Chart
![Version: 0.20.0](https://img.shields.io/badge/Version-0.20.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 8.2.0](https://img.shields.io/badge/AppVersion-8.2.0-informational?style=flat-square)
![Version: 0.20.1](https://img.shields.io/badge/Version-0.20.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 8.2.0](https://img.shields.io/badge/AppVersion-8.2.0-informational?style=flat-square)
This helm chart will help you install the HAPI FHIR JPA Server in a Kubernetes environment.
@@ -98,7 +98,7 @@ 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
## Enable Distributed Tracing based on the OpenTelemetry 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,

View File

@@ -27,7 +27,7 @@ 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
## Enable Distributed Tracing based on the OpenTelemetry 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,

View File

@@ -6,7 +6,7 @@
<properties>
<java.version>17</java.version>
<hapi.fhir.jpa.server.starter.revision>1</hapi.fhir.jpa.server.starter.revision>
<clinical-reasoning.version>3.23.0</clinical-reasoning.version>
<clinical-reasoning.version>3.24.0</clinical-reasoning.version>
</properties>
<!-- one-liner to take you to the cloud with settings form the application.yaml file: -->
@@ -195,7 +195,7 @@
<scope>provided</scope>
</dependency>
<!-- If you are using HAPI narrative generation, you will need to include Thymeleaf as well. Otherwise the following can be omitted. -->
<!-- If you are using HAPI narrative generation, you will need to include Thymeleaf as well. Otherwise, the following can be omitted. -->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>

View File

@@ -45,7 +45,7 @@ public class Application extends SpringBootServletInitializer {
SpringApplication.run(Application.class, args);
// Server is now accessible at eg. http://localhost:8080/fhir/metadata
// Server is now accessible at e.g. http://localhost:8080/fhir/metadata
// UI is now accessible at http://localhost:8080/
}

View File

@@ -22,6 +22,7 @@ import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.beans.factory.annotation.Qualifier;
import java.io.IOException;
import java.io.Serial;
import java.util.stream.Collectors;
import static org.opencds.cqf.fhir.cr.hapi.config.test.TestCdsHooksConfig.CDS_HOOKS_OBJECT_MAPPER_FACTORY;
@@ -29,6 +30,8 @@ import static org.opencds.cqf.fhir.cr.hapi.config.test.TestCdsHooksConfig.CDS_HO
@Configurable
public class CdsHooksServlet extends HttpServlet {
private static final Logger logger = LoggerFactory.getLogger(CdsHooksServlet.class);
@Serial
private static final long serialVersionUID = 1L;
@Autowired

View File

@@ -179,9 +179,8 @@ public class ModuleConfigurationPrefetchSvc extends CdsPrefetchSvc {
return true;
}
if (resource instanceof IBaseBundle) {
return BundleUtil.toListOfEntries(fhirContext, (IBaseBundle) resource)
.size()
> 0;
return !BundleUtil.toListOfEntries(fhirContext, (IBaseBundle) resource)
.isEmpty();
}
return false;
}

View File

@@ -41,24 +41,28 @@ public class FhirServerConfigCommon {
private static final Logger ourLog = LoggerFactory.getLogger(FhirServerConfigCommon.class);
public FhirServerConfigCommon(AppProperties appProperties) {
ourLog.info("Server configured to " + (appProperties.getAllow_contains_searches() ? "allow" : "deny")
+ " contains searches");
ourLog.info("Server configured to " + (appProperties.getAllow_multiple_delete() ? "allow" : "deny")
+ " multiple deletes");
ourLog.info("Server configured to " + (appProperties.getAllow_external_references() ? "allow" : "deny")
+ " external references");
ourLog.info("Server configured to " + (appProperties.getDao_scheduling_enabled() ? "enable" : "disable")
+ " DAO scheduling");
ourLog.info("Server configured to " + (appProperties.getDelete_expunge_enabled() ? "enable" : "disable")
+ " delete expunges");
ourLog.info(
"Server configured to " + (appProperties.getExpunge_enabled() ? "enable" : "disable") + " expunges");
"Server configured to {} contains searches",
appProperties.getAllow_contains_searches() ? "allow" : "deny");
ourLog.info(
"Server configured to " + (appProperties.getAllow_override_default_search_params() ? "allow" : "deny")
+ " overriding default search params");
ourLog.info("Server configured to "
+ (appProperties.getAuto_create_placeholder_reference_targets() ? "allow" : "disable")
+ " auto-creating placeholder references");
"Server configured to {} multiple deletes",
appProperties.getAllow_multiple_delete() ? "allow" : "deny");
ourLog.info(
"Server configured to {} external references",
appProperties.getAllow_external_references() ? "allow" : "deny");
ourLog.info(
"Server configured to {} DAO scheduling",
appProperties.getDao_scheduling_enabled() ? "enable" : "disable");
ourLog.info(
"Server configured to {} delete expunges",
appProperties.getDelete_expunge_enabled() ? "enable" : "disable");
ourLog.info("Server configured to {} expunges", appProperties.getExpunge_enabled() ? "enable" : "disable");
ourLog.info(
"Server configured to {} overriding default search params",
appProperties.getAllow_override_default_search_params() ? "allow" : "deny");
ourLog.info(
"Server configured to {} auto-creating placeholder references",
appProperties.getAuto_create_placeholder_reference_targets() ? "allow" : "disable");
ourLog.info(
"Server configured to auto-version references at paths {}",
appProperties.getAuto_version_reference_at_paths());
@@ -66,12 +70,14 @@ public class FhirServerConfigCommon {
if (appProperties.getSubscription().getEmail() != null) {
AppProperties.Subscription.Email email =
appProperties.getSubscription().getEmail();
ourLog.info("Server is configured to enable email with host '" + email.getHost() + "' and port "
+ email.getPort());
ourLog.info("Server will use '" + email.getFrom() + "' as the from email address");
ourLog.info(
"Server is configured to enable email with host '{}' and port {}",
email.getHost(),
email.getPort());
ourLog.info("Server will use '{}' as the from email address", email.getFrom());
if (!Strings.isNullOrEmpty(email.getUsername())) {
ourLog.info("Server is configured to use username '" + email.getUsername() + "' for email");
ourLog.info("Server is configured to use username '{}' for email", email.getUsername());
}
if (!Strings.isNullOrEmpty(email.getPassword())) {
@@ -91,17 +97,19 @@ public class FhirServerConfigCommon {
ourLog.info("Indexed on contained resource enabled");
}
ourLog.info("Server configured to " + (appProperties.getPre_expand_value_sets() ? "enable" : "disable")
+ " value set pre-expansion");
ourLog.info(
"Server configured to " + (appProperties.getEnable_task_pre_expand_value_sets() ? "enable" : "disable")
+ " value set pre-expansion task");
ourLog.info("Server configured for pre-expand value set default count of "
+ (appProperties.getPre_expand_value_sets_default_count().toString()));
ourLog.info("Server configured for pre-expand value set max count of "
+ (appProperties.getPre_expand_value_sets_max_count().toString()));
ourLog.info("Server configured for maximum expansion size of "
+ (appProperties.getMaximum_expansion_size().toString()));
"Server configured to {} value set pre-expansion",
appProperties.getPre_expand_value_sets() ? "enable" : "disable");
ourLog.info(
"Server configured to {} value set pre-expansion task",
appProperties.getEnable_task_pre_expand_value_sets() ? "enable" : "disable");
ourLog.info(
"Server configured for pre-expand value set default count of {}",
appProperties.getPre_expand_value_sets_default_count());
ourLog.info(
"Server configured for pre-expand value set max count of {}",
appProperties.getPre_expand_value_sets_max_count());
ourLog.info("Server configured for maximum expansion size of {}", appProperties.getMaximum_expansion_size());
}
@Bean
@@ -194,8 +202,9 @@ public class FhirServerConfigCommon {
Integer maxFetchSize = appProperties.getMax_page_size();
jpaStorageSettings.setFetchSizeDefaultMaximum(maxFetchSize);
ourLog.info("Server configured to have a maximum fetch size of "
+ (maxFetchSize == Integer.MAX_VALUE ? "'unlimited'" : 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();
jpaStorageSettings.setReuseCachedSearchResultsForMillis(reuseCachedSearchResultsMillis);
@@ -237,19 +246,21 @@ public class FhirServerConfigCommon {
// Set and/or recommend default Server ID Strategy of UUID when using the ANY Client ID Strategy
if (appProperties.getClient_id_strategy() == JpaStorageSettings.ClientIdStrategyEnum.ANY) {
if (appProperties.getServer_id_strategy() == null) {
ourLog.info("Defaulting server to use '" + JpaStorageSettings.IdStrategyEnum.UUID
+ "' Server ID Strategy when using the '" + JpaStorageSettings.ClientIdStrategyEnum.ANY
+ "' Client ID Strategy");
ourLog.info(
"Defaulting server to use '{}' Server ID Strategy when using the '{}' Client ID Strategy",
JpaStorageSettings.IdStrategyEnum.UUID,
JpaStorageSettings.ClientIdStrategyEnum.ANY);
appProperties.setServer_id_strategy(JpaStorageSettings.IdStrategyEnum.UUID);
} else if (appProperties.getServer_id_strategy() != JpaStorageSettings.IdStrategyEnum.UUID) {
ourLog.warn("WARNING: '" + JpaStorageSettings.IdStrategyEnum.UUID
+ "' Server ID Strategy is highly recommended when using the '"
+ JpaStorageSettings.ClientIdStrategyEnum.ANY + "' Client ID Strategy");
ourLog.warn(
"WARNING: '{}' Server ID Strategy is highly recommended when using the '{}' Client ID Strategy",
JpaStorageSettings.IdStrategyEnum.UUID,
JpaStorageSettings.ClientIdStrategyEnum.ANY);
}
}
if (appProperties.getServer_id_strategy() != null) {
jpaStorageSettings.setResourceServerIdStrategy(appProperties.getServer_id_strategy());
ourLog.info("Server configured to use '" + appProperties.getServer_id_strategy() + "' Server ID Strategy");
ourLog.info("Server configured to use '{}' Server ID Strategy", appProperties.getServer_id_strategy());
}
// to Disable the Resource History

View File

@@ -248,7 +248,7 @@ public class StarterJpaConfig {
List<String> allAllowedCORSOrigins = appProperties.getCors().getAllowed_origin();
allAllowedCORSOrigins.forEach(config::addAllowedOriginPattern);
ourLog.info("CORS allows the following origins: " + String.join(", ", allAllowedCORSOrigins));
ourLog.info("CORS allows the following origins: {}", String.join(", ", allAllowedCORSOrigins));
config.addExposedHeader("Location");
config.addExposedHeader("Content-Location");
@@ -467,9 +467,7 @@ public class StarterJpaConfig {
registerCustomInterceptors(fhirServer, appContext, appProperties.getCustomInterceptorClasses());
// register the IPS Provider
if (!theIpsOperationProvider.isEmpty()) {
fhirServer.registerProvider(theIpsOperationProvider.get());
}
theIpsOperationProvider.ifPresent(fhirServer::registerProvider);
if (appProperties.getUserRequestRetryVersionConflictsInterceptorEnabled()) {
fhirServer.registerInterceptor(new UserRequestRetryVersionConflictsInterceptor());
@@ -500,7 +498,7 @@ public class StarterJpaConfig {
throw new ConfigurationException("Interceptor class was not found on classpath: " + className, e);
}
// first check if the class a Bean in the app context
// first check if the class is a Bean in the app context
Object interceptor = null;
try {
interceptor = theAppContext.getBean(clazz);
@@ -541,7 +539,7 @@ public class StarterJpaConfig {
throw new ConfigurationException("Provider class was not found on classpath: " + className, e);
}
// first check if the class a Bean in the app context
// first check if the class is a Bean in the app context
Object provider = null;
try {
provider = theAppContext.getBean(clazz);

View File

@@ -50,11 +50,11 @@ public class RepositoryValidationInterceptorFactoryR4 implements IRepositoryVali
IBundleProvider results = structureDefinitionResourceProvider.search(new SearchParameterMap()
.setLoadSynchronous(true)
.add(StructureDefinition.SP_KIND, new TokenParam("resource")));
Map<String, List<StructureDefinition>> structureDefintions = results.getResources(0, results.size()).stream()
Map<String, List<StructureDefinition>> structureDefinitions = results.getResources(0, results.size()).stream()
.map(StructureDefinition.class::cast)
.collect(Collectors.groupingBy(StructureDefinition::getType));
structureDefintions.forEach((key, value) -> {
structureDefinitions.forEach((key, value) -> {
String[] urls = value.stream().map(StructureDefinition::getUrl).toArray(String[]::new);
repositoryValidatingRuleBuilder
.forResourcesOfType(key)

View File

@@ -50,11 +50,11 @@ public class RepositoryValidationInterceptorFactoryR4B implements IRepositoryVal
IBundleProvider results = structureDefinitionResourceProvider.search(new SearchParameterMap()
.setLoadSynchronous(true)
.add(StructureDefinition.SP_KIND, new TokenParam("resource")));
Map<String, List<StructureDefinition>> structureDefintions = results.getResources(0, results.size()).stream()
Map<String, List<StructureDefinition>> structureDefinitions = results.getResources(0, results.size()).stream()
.map(StructureDefinition.class::cast)
.collect(Collectors.groupingBy(StructureDefinition::getType));
structureDefintions.forEach((key, value) -> {
structureDefinitions.forEach((key, value) -> {
String[] urls = value.stream().map(StructureDefinition::getUrl).toArray(String[]::new);
repositoryValidatingRuleBuilder
.forResourcesOfType(key)

View File

@@ -49,11 +49,11 @@ public class RepositoryValidationInterceptorFactoryR5 implements IRepositoryVali
IBundleProvider results = structureDefinitionResourceProvider.search(new SearchParameterMap()
.setLoadSynchronous(true)
.add(StructureDefinition.SP_KIND, new TokenParam("resource")));
Map<String, List<StructureDefinition>> structureDefintions = results.getResources(0, results.size()).stream()
Map<String, List<StructureDefinition>> structureDefinitions = results.getResources(0, results.size()).stream()
.map(StructureDefinition.class::cast)
.collect(Collectors.groupingBy(StructureDefinition::getType));
structureDefintions.forEach((key, value) -> {
structureDefinitions.forEach((key, value) -> {
String[] urls = value.stream().map(StructureDefinition::getUrl).toArray(String[]::new);
repositoryValidatingRuleBuilder
.forResourcesOfType(key)

View File

@@ -22,6 +22,7 @@ import org.opencds.cqf.fhir.cr.hapi.common.ElmCacheResourceChangeListener;
import org.opencds.cqf.fhir.cr.measure.CareGapsProperties;
import org.opencds.cqf.fhir.cr.measure.MeasureEvaluationOptions;
import org.opencds.cqf.fhir.utility.ValidationProfile;
import org.opencds.cqf.fhir.utility.client.TerminologyServerClientSettings;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
@@ -57,6 +58,11 @@ public class CrCommonConfig {
return theCrProperties.getCql().getTerminology();
}
@Bean
TerminologyServerClientSettings terminologyServerClientSettings(CrProperties theCrProperties) {
return theCrProperties.getTerminologyServerClientSettings();
}
@Bean
public EvaluationSettings evaluationSettings(
CrProperties theCrProperties,

View File

@@ -1,11 +1,15 @@
package ca.uhn.fhir.jpa.starter.cr;
import org.opencds.cqf.fhir.utility.client.TerminologyServerClientSettings;
public class CrProperties {
private Boolean enabled;
private CareGapsProperties careGaps = new CareGapsProperties();
private CqlProperties cql = new CqlProperties();
private TerminologyServerClientSettings terminologyServerClientSettings = new TerminologyServerClientSettings();
public Boolean getEnabled() {
return enabled;
}
@@ -29,4 +33,12 @@ public class CrProperties {
public void setCql(CqlProperties cql) {
this.cql = cql;
}
public TerminologyServerClientSettings getTerminologyServerClientSettings() {
return terminologyServerClientSettings;
}
public void setTerminologyServerClientSettings(TerminologyServerClientSettings terminologyServerClientSettings) {
this.terminologyServerClientSettings = terminologyServerClientSettings;
}
}

View File

@@ -11,7 +11,7 @@ public class PostInitProviderRegisterer {
resourceProviderFactory.attach(new Observer(restfulServer));
}
private class Observer implements IResourceProviderFactoryObserver {
private static class Observer implements IResourceProviderFactoryObserver {
private RestfulServer restfulServer;
public Observer(RestfulServer restfulServer) {

View File

@@ -188,14 +188,12 @@ public class EnvironmentHelper {
public static Map<String, Object> getAllProperties(PropertySource<?> aPropSource) {
Map<String, Object> result = new HashMap<>();
if (aPropSource instanceof CompositePropertySource) {
CompositePropertySource cps = (CompositePropertySource) aPropSource;
if (aPropSource instanceof CompositePropertySource cps) {
cps.getPropertySources().forEach(ps -> addAll(result, getAllProperties(ps)));
return result;
}
if (aPropSource instanceof EnumerablePropertySource<?>) {
EnumerablePropertySource<?> ps = (EnumerablePropertySource<?>) aPropSource;
if (aPropSource instanceof EnumerablePropertySource<?> ps) {
Arrays.asList(ps.getPropertyNames()).forEach(key -> result.put(key, ps.getProperty(key)));
return result;
}

View File

@@ -6,7 +6,7 @@ server:
tomcat:
# allow | as a separator in the URL
relaxed-query-chars: "|"
#Adds the option to go to eg. http://localhost:8080/actuator/health for seeing the running configuration
#Adds the option to go to e.g. 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.
@@ -14,7 +14,7 @@ management:
enabled-by-default: false
web:
exposure:
include: 'health' # or e.g. 'info,health,prometheus,metrics' or '*' for all'
include: 'health' # or e.g. 'info,health,prometheus,metrics' or '*' for all
endpoint:
info:
enabled: true
@@ -89,6 +89,11 @@ hapi:
caregaps:
reporter: "default"
section_author: "default"
terminologyServerClientSettings:
maxRetryCount: 3
retryIntervalMillis: 1000
timeoutSeconds: 30
socketTimeout: 60
cql:
use_embedded_libraries: true
compiler:
@@ -288,12 +293,12 @@ hapi:
# comma-separated list of fully qualified interceptor classes.
# classes listed here will be fetched from the Spring context when combined with 'custom-bean-packages',
# or will be instantiated via reflection using an no-arg contructor; then registered with the server
# or will be instantiated via reflection using a no-arg constructor; then registered with the server
#custom-interceptor-classes:
# comma-separated list of fully qualified provider classes.
# classes listed here will be fetched from the Spring context when combined with 'custom-bean-packages',
# or will be instantiated via reflection using an no-arg contructor; then registered with the server
# or will be instantiated via reflection using a no-arg constructor; then registered with the server
#custom-provider-classes:
# specify what should be stored in meta.source based on StoreMetaSourceInformationEnum defaults to NONE
# store_meta_source_information: NONE

View File

@@ -3,7 +3,7 @@ server:
# servlet:
# context-path: /example/path
port: 8080
#Adds the option to go to eg. http://localhost:8080/actuator/health for seeing the running configuration
#Adds the option to go to e.g. 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.
@@ -11,7 +11,7 @@ management:
enabled-by-default: false
web:
exposure:
include: 'health' # or e.g. 'info,health,prometheus,metrics' or '*' for all'
include: 'health' # or e.g. 'info,health,prometheus,metrics' or '*' for all
endpoint:
info:
enabled: true
@@ -87,6 +87,11 @@ hapi:
caregaps:
reporter: "default"
section_author: "default"
terminologyServerClientSettings:
maxRetryCount: 3
retryIntervalMillis: 1000
timeoutSeconds: 30
socketTimeout: 60
cql:
use_embedded_libraries: true
compiler:
@@ -144,7 +149,7 @@ hapi:
### 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
### enables the server to overwrite defaults on HTML, css, etc. under the url pattern of eg. /content/custom **
### enables the server to overwrite defaults on HTML, css, etc. under the url pattern of e.g. /content/custom **
### Folder with custom content MUST be named custom. If omitted then default content applies
#custom_content_path: ./custom
### enables the server host custom content. If e.g. the value ./configs/app is supplied then the content
@@ -193,7 +198,7 @@ hapi:
# enable_index_of_type: true
# enable_index_contained_resource: false
# 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!!
### !!Extended Lucene/Elasticsearch Indexing is still an 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
search_index_full_text_enabled: false
@@ -252,12 +257,12 @@ hapi:
# comma-separated list of fully qualified interceptor classes.
# classes listed here will be fetched from the Spring context when combined with 'custom-bean-packages',
# or will be instantiated via reflection using an no-arg contructor; then registered with the server
# or will be instantiated via reflection using a no-arg constructor; then registered with the server
#custom-interceptor-classes:
# comma-separated list of fully qualified provider classes.
# classes listed here will be fetched from the Spring context when combined with 'custom-bean-packages',
# or will be instantiated via reflection using an no-arg contructor; then registered with the server
# or will be instantiated via reflection using a no-arg constructor; then registered with the server
#custom-provider-classes:
# Threadpool size for BATCH'ed GETs in a bundle.

View File

@@ -47,7 +47,7 @@
<p>
This UI can be customized!
You might want to put rules on who can use this server here, or
notices about privacy, or whatever else you want..
notices about privacy, or whatever else you want.
</p>
</div>
</div>
@@ -63,7 +63,7 @@
<script>
// Function to check if a file exists using AJAX
function fileExists(filePath, callback) {
var xhr = new XMLHttpRequest();
let xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
callback(xhr.status === 200);
@@ -77,7 +77,7 @@
fileExists("content/custom/about.html", function(exists) {
if (exists) {
loadFile("content/custom/about.html", function(content) {
var replacementContainer = document.getElementById("replacementAbout");
let replacementContainer = document.getElementById("replacementAbout");
replacementContainer.innerHTML = content;
});
}
@@ -85,7 +85,7 @@
// Function to load file content using AJAX
function loadFile(filePath, callback) {
var xhr = new XMLHttpRequest();
let xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
callback(xhr.responseText);

View File

@@ -80,7 +80,7 @@ class CdsHooksServletIT implements IServerSupport {
ourCdsBase = "http://localhost:" + port + "/cds-services";
var cdsServicesJson = myCdsServiceRegistry.getCdsServicesJson();
if (cdsServicesJson != null && cdsServicesJson.getServices() != null && !cdsServicesJson.getServices().isEmpty()) {
if (cdsServicesJson != null && cdsServicesJson.getServices() != null) {
var services = cdsServicesJson.getServices();
for (int i = 0; i < services.size(); i++) {
myCdsServiceRegistry.unregisterService(services.get(i).getId(), "CR");
@@ -91,12 +91,12 @@ class CdsHooksServletIT implements IServerSupport {
private Boolean hasCdsServices() throws IOException {
var response = callCdsServicesDiscovery();
// NOTE: this is looking for a repsonse that indicates there are CDS services availalble.
// NOTE: this is looking for a response that indicates there are CDS services available.
// 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.
// 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
// to ensure the response actually contains CDS services in it
return response.getEntity().getContentLength() > 25 || response.getEntity().isChunked();
}
@@ -120,23 +120,24 @@ class CdsHooksServletIT implements IServerSupport {
@Test
void testCdsHooks() throws IOException {
loadBundle("r4/HelloWorld-Bundle.json", ourCtx, ourClient);
await().atMost(10000, TimeUnit.MILLISECONDS).until(() -> hasCdsServices());
var cdsRequest = "{\n" +
" \"hookInstance\": \"12345\",\n" +
" \"hook\": \"patient-view\",\n" +
" \"context\": {\n" +
" \"userId\": \"Practitioner/example\",\n" +
" \"patientId\": \"Patient/example-hello-world\"\n" +
" },\n" +
" \"prefetch\": {\n" +
" \"item1\": {\n" +
" \"resourceType\": \"Patient\",\n" +
" \"id\": \"example-hello-world\",\n" +
" \"gender\": \"male\",\n" +
" \"birthDate\": \"2000-01-01\"\n" +
" }\n" +
" }\n" +
"}";
await().atMost(10000, TimeUnit.MILLISECONDS).until(this::hasCdsServices);
var cdsRequest = """
{
"hookInstance": "12345",
"hook": "patient-view",
"context": {
"userId": "Practitioner/example",
"patientId": "Patient/example-hello-world"
},
"prefetch": {
"item1": {
"resourceType": "Patient",
"id": "example-hello-world",
"gender": "male",
"birthDate": "2000-01-01"
}
}
}""";
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
HttpPost request = new HttpPost(ourCdsBase + "/hello-world");
request.setEntity(new StringEntity(cdsRequest));
@@ -158,7 +159,7 @@ class CdsHooksServletIT implements IServerSupport {
@Test
void testRec10() throws IOException {
loadBundle("r4/opioidcds-10-order-sign-bundle.json", ourCtx, ourClient);
await().atMost(20000, TimeUnit.MILLISECONDS).until(() -> hasCdsServices());
await().atMost(20000, TimeUnit.MILLISECONDS).until(this::hasCdsServices);
var fhirServer = " \"fhirServer\": " + "\"" + ourServerBase + "\"" + ",\n";
var cdsRequest = "{\n" +
" \"hookInstance\": \"055b009c-4a7d-4db4-a35e-0e5198918ed1\",\n" +

View File

@@ -13,13 +13,10 @@ import java.io.IOException;
import java.util.Date;
import java.util.GregorianCalendar;
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;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.DateTimeType;
@@ -68,8 +65,7 @@ import org.testcontainers.junit.jupiter.Testcontainers;
"spring.jpa.properties.hibernate.search.backend.analysis.configurer=ca.uhn.fhir.jpa.search.elastic.HapiElasticsearchAnalysisConfigurer"
})
@ContextConfiguration(initializers = ElasticsearchLastNR4IT.Initializer.class)
public class ElasticsearchLastNR4IT {
class ElasticsearchLastNR4IT {
private IGenericClient ourClient;
private FhirContext ourCtx;

View File

@@ -33,7 +33,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
"hapi.fhir.partitioning.database_partition_mode_enabled=true",
"hapi.fhir.partitioning.patient_id_partitioning_mode=true"
})
public class ExampleServerDbpmR5IT {
class ExampleServerDbpmR5IT {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExampleServerDstu2IT.class);
private IGenericClient ourClient;

View File

@@ -51,8 +51,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
"hapi.fhir.allow_placeholder_references=true",
"spring.main.allow-bean-definition-overriding=true"
})
class ExampleServerDstu3IT implements IServerSupport {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExampleServerDstu3IT.class);

View File

@@ -39,7 +39,7 @@ class ExampleServerR4BIT {
@Order(0)
void testCreateAndRead() {
String methodName = "testCreateAndRead";
ourLog.info("Entering " + methodName + "()...");
ourLog.info("Entering {}()...", methodName);
Patient pt = new Patient();
pt.setActive(true);
@@ -56,51 +56,52 @@ class ExampleServerR4BIT {
@Test
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" +
"}";
String batchPuts = """
{
\t"resourceType": "Bundle",
\t"id": "patients",
\t"type": "batch",
\t"entry": [
\t\t{
\t\t\t"request": {
\t\t\t\t"method": "PUT",
\t\t\t\t"url": "Patient/pat-1"
\t\t\t},
\t\t\t"resource": {
\t\t\t\t"resourceType": "Patient",
\t\t\t\t"id": "pat-1",
\t\t\t\t"meta": {
\t\t\t\t\t"tag": [
\t\t\t\t\t\t{
\t\t\t\t\t\t\t"system": "http://mysystem.org",
\t\t\t\t\t\t\t"code": "value2"
\t\t\t\t\t\t}
\t\t\t\t\t]
\t\t\t\t}
\t\t\t},
\t\t\t"fullUrl": "/Patient/pat-1"
\t\t},
\t\t{
\t\t\t"request": {
\t\t\t\t"method": "PUT",
\t\t\t\t"url": "Patient/pat-2"
\t\t\t},
\t\t\t"resource": {
\t\t\t\t"resourceType": "Patient",
\t\t\t\t"id": "pat-2",
\t\t\t\t"meta": {
\t\t\t\t\t"tag": [
\t\t\t\t\t\t{
\t\t\t\t\t\t\t"system": "http://mysystem.org",
\t\t\t\t\t\t\t"code": "value2"
\t\t\t\t\t\t}
\t\t\t\t\t]
\t\t\t\t}
\t\t\t},
\t\t\t"fullUrl": "/Patient/pat-2"
\t\t}
\t]
}""";
Bundle bundle = FhirContext.forR4B().newJsonParser().parseResource(Bundle.class, batchPuts);
ourClient.transaction().withBundle(bundle).execute();
}

View File

@@ -18,7 +18,6 @@ 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.*;
import org.junit.jupiter.api.BeforeEach;
@@ -34,7 +33,6 @@ import org.springframework.boot.test.web.server.LocalServerPort;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -44,6 +42,7 @@ import static org.hamcrest.Matchers.equalTo;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.opencds.cqf.fhir.utility.r4.Parameters.parameters;
import static org.opencds.cqf.fhir.utility.r4.Parameters.stringPart;
@@ -92,7 +91,7 @@ class ExampleServerR4IT implements IServerSupport {
@Order(0)
void testCreateAndRead() {
String methodName = "testCreateAndRead";
ourLog.info("Entering " + methodName + "()...");
ourLog.info("Entering {}()...", methodName);
Patient pt = new Patient();
pt.setActive(true);
@@ -132,7 +131,7 @@ class ExampleServerR4IT implements IServerSupport {
List<Parameters.ParametersParameterComponent> response = outParams.getParameter();
assertFalse(response.isEmpty());
Parameters.ParametersParameterComponent component = response.get(0);
assertTrue(component.getResource() instanceof MeasureReport);
assertInstanceOf(MeasureReport.class, component.getResource());
MeasureReport report = (MeasureReport) component.getResource();
assertEquals(measureUrl + "|0.0.003", report.getMeasure());
}
@@ -150,66 +149,57 @@ class ExampleServerR4IT implements IServerSupport {
void testSimpleDateCqlExecutionProvider() {
Parameters params = parameters(stringPart("expression", "Interval[Today() - 2 years, Today())"));
Parameters results = runCqlExecution(params);
assertTrue(results.getParameter("return").getValue() instanceof Period);
}
private IBaseResource loadRec(String theLocation, FhirContext theCtx, IGenericClient theClient) throws IOException {
String json = stringFromResource(theLocation);
List<IBaseResource> resList = new ArrayList<>();
IBaseResource resource = (IBaseResource) theCtx.newJsonParser().parseResource(json);
resList.add(resource);
var result = theClient.transaction().withResources(resList).execute();
//.withResources(resource).execute();
return result.get(0);
assertInstanceOf(Period.class, results.getParameter("return").getValue());
}
@Test
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" +
"}";
String batchPuts = """
{
\t"resourceType": "Bundle",
\t"id": "patients",
\t"type": "batch",
\t"entry": [
\t\t{
\t\t\t"request": {
\t\t\t\t"method": "PUT",
\t\t\t\t"url": "Patient/pat-1"
\t\t\t},
\t\t\t"resource": {
\t\t\t\t"resourceType": "Patient",
\t\t\t\t"id": "pat-1",
\t\t\t\t"meta": {
\t\t\t\t\t"tag": [
\t\t\t\t\t\t{
\t\t\t\t\t\t\t"system": "http://mysystem.org",
\t\t\t\t\t\t\t"code": "value2"
\t\t\t\t\t\t}
\t\t\t\t\t]
\t\t\t\t}
\t\t\t},
\t\t\t"fullUrl": "/Patient/pat-1"
\t\t},
\t\t{
\t\t\t"request": {
\t\t\t\t"method": "PUT",
\t\t\t\t"url": "Patient/pat-2"
\t\t\t},
\t\t\t"resource": {
\t\t\t\t"resourceType": "Patient",
\t\t\t\t"id": "pat-2",
\t\t\t\t"meta": {
\t\t\t\t\t"tag": [
\t\t\t\t\t\t{
\t\t\t\t\t\t\t"system": "http://mysystem.org",
\t\t\t\t\t\t\t"code": "value2"
\t\t\t\t\t\t}
\t\t\t\t\t]
\t\t\t\t}
\t\t\t},
\t\t\t"fullUrl": "/Patient/pat-2"
\t\t}
\t]
}""";
Bundle bundle = FhirContext.forR4().newJsonParser().parseResource(Bundle.class, batchPuts);
ourClient.transaction().withBundle(bundle).execute();
}
@@ -277,8 +267,8 @@ class ExampleServerR4IT implements IServerSupport {
var reporter = crProperties.getCareGaps().getReporter();
var author = crProperties.getCareGaps().getSection_author();
assertTrue(reporter.equals("Organization/alphora"));
assertTrue(author.equals("Organization/alphora-author"));
assertEquals("Organization/alphora", reporter);
assertEquals("Organization/alphora-author", author);
String periodStartValid = "2019-01-01";
String periodEndValid = "2019-12-31";
@@ -327,7 +317,7 @@ class ExampleServerR4IT implements IServerSupport {
@Test
void testDiffOperationIsRegistered() {
String methodName = "testDiff";
ourLog.info("Entering " + methodName + "()...");
ourLog.info("Entering {}()...", methodName);
Patient pt = new Patient();
pt.setActive(true);

View File

@@ -4,7 +4,6 @@ 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.rest.client.api.IGenericClient;
import com.google.common.base.Charsets;
import org.apache.commons.io.IOUtils;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IBaseResource;
@@ -15,6 +14,7 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
public interface IServerSupport {
@@ -45,6 +45,6 @@ public interface IServerSupport {
Resource resource = resourceLoader.getResource(theLocation);
is = resource.getInputStream();
}
return IOUtils.toString(is, Charsets.UTF_8);
return IOUtils.toString(is, StandardCharsets.UTF_8);
}
}

View File

@@ -1,13 +1,9 @@
package ca.uhn.fhir.jpa.starter;
import org.opencds.cqf.fhir.cql.EvaluationSettings;
import org.opencds.cqf.fhir.cr.measure.MeasureEvaluationOptions;
import org.opencds.cqf.fhir.utility.ValidationProfile;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Map;
@Configuration
public class MeasureEvaluationConfig {

View File

@@ -17,12 +17,10 @@ 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;
@@ -100,11 +98,9 @@ public class ParallelUpdatesVersionConflictTest {
List<Callable<Integer>> callables = new ArrayList<>();
for (int i = 0; i < threadCnt; i++) {
final int cnt = i;
Callable<Integer> callable = new Callable<>() {
@Override
public Integer call() throws Exception {
Callable<Integer> callable = () -> {
Patient pat = new Patient();
//make sure to change something so the server doesnt short circuit on a no-op
//make sure to change something so the server doesn't short circuit on a no-op
pat.addName().setFamily("fam-" + cnt);
pat.setId(patientId);
@@ -135,10 +131,9 @@ public class ParallelUpdatesVersionConflictTest {
MethodOutcome outcome = client.update().resource(pat).withId(patientId)
.withAdditionalHeader(headerName, "retry; max-retries=10")
.execute();
ourLog.trace("updated patient: " + outcome.getResponseStatusCode());
ourLog.trace("updated patient: {}", outcome.getResponseStatusCode());
return outcome.getResponseStatusCode();
}
}
};
callables.add(callable);
}

View File

@@ -43,7 +43,7 @@ public class SocketImplementation {
/**
* This method is executed when the client is connecting to the server.
* In this case, we are sending a message to create the subscription dynamiclly
* In this case, we are sending a message to create the subscription dynamically
*
* @param session
*/
@@ -58,7 +58,6 @@ public class SocketImplementation {
ourLog.info("Connection: DONE");
} catch (Throwable t) {
t.printStackTrace();
ourLog.error("Failure", t);
}
}
@@ -70,7 +69,7 @@ public class SocketImplementation {
*/
@OnMessage
public void onMessage(String theMsg) {
ourLog.info("Got msg: " + theMsg);
ourLog.info("Got msg: {}", theMsg);
myMessages.add(theMsg);
if (theMsg.startsWith("bound ")) {
@@ -78,7 +77,7 @@ public class SocketImplementation {
mySubsId = (theMsg.substring("bound ".length()));
} else if (myGotBound && theMsg.startsWith("add " + mySubsId + "\n")) {
String text = theMsg.substring(("add " + mySubsId + "\n").length());
ourLog.info("text: " + text);
ourLog.info("text: {}", text);
} else if (theMsg.startsWith("ping ")) {
myPingCount++;
} else {

View File

@@ -21,8 +21,7 @@ public class CustomInterceptorBean {
IBaseResource resource = servletRequestDetails.getResource();
// add an extension before saving the resource to mark it
if (opType == RestOperationTypeEnum.CREATE && resource instanceof Patient) {
Patient pat = (Patient) resource;
if (opType == RestOperationTypeEnum.CREATE && resource instanceof Patient pat) {
Extension ext = pat.addExtension();
ext.setUrl("http://some.custom.pkg1/CustomInterceptorBean");
ext.setValue(new StringType("CustomInterceptorBean wuz here"));

View File

@@ -16,8 +16,7 @@ public class CustomInterceptorPojo {
IBaseResource resource = servletRequestDetails.getResource();
// add an extension before saving the resource to mark it
if (opType == RestOperationTypeEnum.CREATE && resource instanceof Patient) {
Patient pat = (Patient) resource;
if (opType == RestOperationTypeEnum.CREATE && resource instanceof Patient pat) {
Extension ext = pat.addExtension();
ext.setUrl("http://some.custom.pkg1/CustomInterceptorPojo");
ext.setValue(new StringType("CustomInterceptorPojo wuz here"));

View File

@@ -4,7 +4,7 @@ management:
enabled-by-default: false
web:
exposure:
include: 'info,health,prometheus,metrics' # or '*' for all'
include: 'info,health,prometheus,metrics' # or '*' for all
endpoint:
info:
enabled: true
@@ -31,7 +31,7 @@ spring:
allow-bean-definition-overriding: true
flyway:
enabled: false
check-location: false
fail-on-missing-locations: false
baselineOnMigrate: true
datasource:
url: jdbc:h2:mem:test_mem
@@ -126,7 +126,7 @@ hapi:
# 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!!
### !!Extended Lucene/Elasticsearch Indexing is still an 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
search_index_full_text_enabled: false

View File

@@ -15,7 +15,7 @@ on the jetbrains website, in addition to the [API reference][Link-HTTP-Client-Re
### Formatting
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
example, there is a `plain_server.rest` file, which attempts 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:
@@ -38,7 +38,7 @@ To run these tests against a specific server, configure the server details withi
```
### Running the Tests
Within IntelliJ, right click the file you wish to run tests in and select the `Run All` option from the menu.
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,

View File

@@ -81,13 +81,13 @@ Content-Type: application/json
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++) {
for (let 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");
var trimmed_location = batch_patient_location.substring(0, indexOfHistory);
let trimmed_location = batch_patient_location.substring(0, indexOfHistory);
trimmed_location = trimmed_location.replace("Patient/", "")
client.global.set("batch_patient_id", trimmed_location);
%}