some cleanup (#840)

* some cleanup

* results from mvn spotless:apply

* fix contructor --> constructor

* revert change to fix CdsHooksServletIT

* revert change to charts README.md

* bump chart version, required due to changes in README.md
This commit is contained in:
winfriedgerlach
2025-08-19 20:00:34 +02:00
committed by GitHub
parent 2358fba2d2
commit 8224cae131
34 changed files with 272 additions and 300 deletions

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

@@ -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
@@ -293,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
@@ -149,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
@@ -198,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
@@ -257,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,13 +65,12 @@ 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 {
private IGenericClient ourClient;
class ElasticsearchLastNR4IT {
private IGenericClient ourClient;
private FhirContext ourCtx;
@Container
public static ElasticsearchContainer embeddedElastic = TestElasticsearchContainerHelper.getEmbeddedElasticSearch();
@Container
public static ElasticsearchContainer embeddedElastic = TestElasticsearchContainerHelper.getEmbeddedElasticSearch();
@Autowired
private ElasticsearchSvcImpl myElasticsearchSvc;
@@ -109,7 +105,7 @@ public class ElasticsearchLastNR4IT {
//@Test
void testLastN() throws IOException, InterruptedException {
Thread.sleep(2000);
Thread.sleep(2000);
Patient pt = new Patient();
pt.addName().setFamily("Lastn").addGiven("Arthur");

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,45 +98,42 @@ 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 {
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);
Callable<Integer> callable = () -> {
Patient pat = new Patient();
//make sure to change something so the server doesn't 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);
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();
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();
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);
}

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);
%}