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

This commit is contained in:
dotasek
2024-07-02 12:00:55 -04:00
27 changed files with 1729 additions and 662 deletions

13
.vscode/launch.json vendored
View File

@@ -7,7 +7,18 @@
"cwd": "${workspaceFolder}",
"mainClass": "ca.uhn.fhir.jpa.starter.Application",
"projectName": "hapi-fhir-jpaserver-starter",
"args": "",
"vmArgs": [
"-XX:TieredStopAtLevel=1",
// "-Ddebug=true",
// "-Dloader.debug=true",
"-Dhapi.fhir.bulk_export_enabled=false",
"-Dspring.batch.job.enabled=false",
"-Dspring.main.allow-bean-definition-overriding=true",
"-Dhapi.fhir.cdshooks.enabled=true",
"-Dhapi.fhir.cr.enabled=true",
"-Dspring.main.allow-bean-definition-overriding=true"
],
"envFile": "${workspaceFolder}/.env"
}
]

View File

@@ -5,5 +5,6 @@
"**/.settings": true,
"**/.factorypath": true
},
"java.compile.nullAnalysis.mode": "disabled"
"java.compile.nullAnalysis.mode": "disabled",
"java.configuration.updateBuildConfiguration": "automatic"
}

View File

@@ -483,7 +483,11 @@ The server may be configured with subscription support by enabling properties in
## Enabling Clinical Reasoning
Set `hapi.fhir.cr_enabled=true` in the [application.yaml](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/application.yaml) file to enable [Clinical Quality Language](https://cql.hl7.org/) on this server.
Set `hapi.fhir.cr_enabled=true` in the [application.yaml](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/application.yaml) file to enable [Clinical Quality Language](https://cql.hl7.org/) on this server. An alternate settings file, [cds.application.yaml](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/cds.application.yaml), exists with the Clinical Reasoning module enabled and default settings that have been found to work with most CDS and dQM test cases.
## Enabling CDS Hooks
Set `hapi.fhir.cdshooks.enabled=true` in the [application.yaml](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/application.yaml) file to enable [CDS Hooks](https://cds-hooks.org/) on this server. The Clinical Reasoning module must also be enabled because this implementation of CDS Hooks includes [CDS on FHIR](https://build.fhir.org/clinicalreasoning-cds-on-fhir.html). An example CDS Service using CDS on FHIR is available in the CdsHooksServletIT test class.
## Enabling MDM (EMPI)

View File

@@ -42,6 +42,15 @@
<dependencyManagement>
<dependencies>
<!-- Temporarily override CR depedencies for debugging -->
<!-- <dependency>-->
<!-- <groupId>org.opencds.cqf.fhir</groupId>-->
<!-- <artifactId>cqf-fhir-bom</artifactId>-->
<!-- <version>3.4.0</version>-->
<!-- <type>pom</type>-->
<!-- <scope>import</scope>-->
<!-- </dependency>-->
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>

File diff suppressed because one or more lines are too long

View File

@@ -3,10 +3,6 @@ package ca.uhn.fhir.jpa.starter.cdshooks;
import ca.uhn.fhir.jpa.starter.cr.CrProperties;
public class ProviderConfiguration {
public static final ProviderConfiguration DEFAULT_PROVIDER_CONFIGURATION =
new ProviderConfiguration(false, "client_id");
private final String clientIdHeaderName;
private final boolean cqlLoggingEnabled;
@@ -16,8 +12,7 @@ public class ProviderConfiguration {
}
public ProviderConfiguration(CdsHooksProperties cdsProperties, CrProperties crProperties) {
this.clientIdHeaderName = cdsProperties.getClientIdHeaderName();
this.cqlLoggingEnabled = crProperties.isCqlRuntimeDebugLoggingEnabled();
this(crProperties.getCql().getRuntime().isDebugLoggingEnabled(), cdsProperties.getClientIdHeaderName());
}
public String getClientIdHeaderName() {

View File

@@ -1,11 +1,21 @@
package ca.uhn.fhir.jpa.starter.cdshooks;
import ca.uhn.fhir.jpa.starter.cr.CrCommonConfig;
import ca.uhn.fhir.jpa.starter.cr.CrConfigCondition;
import ca.uhn.fhir.jpa.starter.cr.CrProperties;
import ca.uhn.hapi.fhir.cdshooks.api.ICdsHooksDaoAuthorizationSvc;
import ca.uhn.hapi.fhir.cdshooks.config.CdsHooksConfig;
import ca.uhn.hapi.fhir.cdshooks.svc.CdsHooksContextBooter;
import ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrSettings;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrServiceRegistry;
import ca.uhn.hapi.fhir.cdshooks.svc.cr.ICdsCrServiceRegistry;
import ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery.CdsCrDiscoveryServiceRegistry;
import ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery.ICdsCrDiscoveryServiceRegistry;
import ca.uhn.hapi.fhir.cdshooks.svc.prefetch.CdsPrefetchDaoSvc;
import ca.uhn.hapi.fhir.cdshooks.svc.prefetch.CdsPrefetchFhirClientSvc;
import ca.uhn.hapi.fhir.cdshooks.svc.prefetch.CdsPrefetchSvc;
import ca.uhn.hapi.fhir.cdshooks.svc.prefetch.CdsResolutionStrategySvc;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
@@ -16,8 +26,38 @@ import org.springframework.context.annotation.Import;
@Configuration
@Conditional({CdsHooksConfigCondition.class, CrConfigCondition.class})
@Import(CdsHooksConfig.class)
@Import({CdsHooksConfig.class, CrCommonConfig.class})
public class StarterCdsHooksConfig {
// @Bean
// CdsPrefetchSvc cdsPrefetchSvc(
// CdsResolutionStrategySvc theCdsResolutionStrategySvc,
// CdsPrefetchDaoSvc theResourcePrefetchDao,
// CdsPrefetchFhirClientSvc theResourcePrefetchFhirClient,
// ICdsHooksDaoAuthorizationSvc theCdsHooksDaoAuthorizationSvc) {
// return new ModuleConfigurationPrefetchSvc(
// theCdsResolutionStrategySvc,
// theResourcePrefetchDao,
// theResourcePrefetchFhirClient,
// theCdsHooksDaoAuthorizationSvc);
// }
@Bean
public ICdsCrDiscoveryServiceRegistry cdsCrDiscoveryServiceRegistry() {
CdsCrDiscoveryServiceRegistry registry = new CdsCrDiscoveryServiceRegistry();
registry.unregister(FhirVersionEnum.R4);
registry.register(FhirVersionEnum.R4, UpdatedCrDiscoveryServiceR4.class);
return registry;
}
@Bean
public ICdsCrServiceRegistry cdsCrServiceRegistry() {
CdsCrServiceRegistry registry = new CdsCrServiceRegistry();
registry.unregister(FhirVersionEnum.R4);
registry.register(FhirVersionEnum.R4, UpdatedCdsCrServiceR4.class);
return registry;
}
@Bean
public CdsHooksProperties cdsHooksProperties() {
return new CdsHooksProperties();

View File

@@ -0,0 +1,30 @@
package ca.uhn.fhir.jpa.starter.cdshooks;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.hapi.fhir.cdshooks.api.ICdsConfigService;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestJson;
import ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrServiceR4;
import org.hl7.fhir.r4.model.BooleanType;
import org.hl7.fhir.r4.model.Parameters;
import org.opencds.cqf.fhir.api.Repository;
import java.util.stream.Collectors;
import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.APPLY_PARAMETER_DATA;
import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.APPLY_PARAMETER_DATA_ENDPOINT;
import static org.opencds.cqf.fhir.utility.r4.Parameters.part;
public class UpdatedCdsCrServiceR4 extends CdsCrServiceR4 {
public UpdatedCdsCrServiceR4(RequestDetails theRequestDetails, Repository theRepository, ICdsConfigService theCdsConfigService) {
super(theRequestDetails, theRepository, theCdsConfigService);
}
@Override
public Parameters encodeParams(CdsServiceRequestJson theJson) {
Parameters parameters = super.encodeParams(theJson);
if (parameters.hasParameter(APPLY_PARAMETER_DATA)) {
parameters.addParameter(part("useServerData", new BooleanType(false)));
}
return parameters;
}
}

View File

@@ -0,0 +1,12 @@
package ca.uhn.fhir.jpa.starter.cdshooks;
import ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery.CrDiscoveryServiceR4;
import org.hl7.fhir.instance.model.api.IIdType;
import org.opencds.cqf.fhir.api.Repository;
public class UpdatedCrDiscoveryServiceR4 extends CrDiscoveryServiceR4 {
public UpdatedCrDiscoveryServiceR4(IIdType thePlanDefinitionId, Repository theRepository) {
super(thePlanDefinitionId, theRepository);
myMaxUriLength = 6000;
}
}

View File

@@ -0,0 +1,22 @@
package ca.uhn.fhir.jpa.starter.cr;
public class CareGapsProperties {
private String reporter = "default";
private String section_author = "default";
public String getReporter() {
return reporter;
}
public void setReporter(String reporter) {
this.reporter = reporter;
}
public String getSection_author() {
return section_author;
}
public void setSection_author(String section_author) {
this.section_author = section_author;
}
}

View File

@@ -0,0 +1,198 @@
package ca.uhn.fhir.jpa.starter.cr;
import org.cqframework.cql.cql2elm.CqlCompilerException;
import org.cqframework.cql.cql2elm.CqlTranslator;
import org.cqframework.cql.cql2elm.LibraryBuilder;
public class CqlCompilerProperties {
private Boolean validate_units = true;
private Boolean verify_only = false;
private String compatibility_level = "1.5";
private CqlCompilerException.ErrorSeverity error_level = CqlCompilerException.ErrorSeverity.Info;
private LibraryBuilder.SignatureLevel signature_level = LibraryBuilder.SignatureLevel.All;
private Boolean analyze_data_requirements = false;
private Boolean collapse_data_requirements = false;
private CqlTranslator.Format translator_format = CqlTranslator.Format.JSON;
private Boolean enable_date_range_optimization = true;
private Boolean enable_annotations = true;
private Boolean enable_locators = true;
private Boolean enable_results_type = true;
private Boolean enable_detailed_errors = true;
private Boolean disable_list_traversal = false;
private Boolean disable_list_demotion = false;
private Boolean disable_list_promotion = false;
private Boolean enable_interval_demotion = false;
private Boolean enable_interval_promotion = false;
private Boolean disable_method_invocation = false;
private Boolean require_from_keyword = false;
private Boolean disable_default_model_info_load = false;
public boolean isValidateUnits() {
return validate_units;
}
public void setValidateUnits(boolean validateUnits) {
this.validate_units = validateUnits;
}
public boolean isVerifyOnly() {
return verify_only;
}
public void setVerifyOnly(boolean verifyOnly) {
this.verify_only = verifyOnly;
}
public String getCompatibilityLevel() {
return compatibility_level;
}
public void setCompatibilityLevel(String compatibilityLevel) {
this.compatibility_level = compatibilityLevel;
}
public CqlCompilerException.ErrorSeverity getErrorSeverityLevel() {
return error_level;
}
public void setErrorSeverityLevel(CqlCompilerException.ErrorSeverity errorSeverityLevel) {
this.error_level = errorSeverityLevel;
}
public LibraryBuilder.SignatureLevel getSignatureLevel() {
return signature_level;
}
public void setSignatureLevel(LibraryBuilder.SignatureLevel signatureLevel) {
this.signature_level = signatureLevel;
}
public boolean isAnalyzeDataRequirements() {
return analyze_data_requirements;
}
public void setAnalyzeDataRequirements(boolean analyzeDataRequirements) {
this.analyze_data_requirements = analyzeDataRequirements;
}
public boolean isCollapseDataRequirements() {
return collapse_data_requirements;
}
public void setCollapseDataRequirements(boolean collapseDataRequirements) {
this.collapse_data_requirements = collapseDataRequirements;
}
public boolean isEnableDateRangeOptimization() {
return enable_date_range_optimization;
}
public void setEnableDateRangeOptimization(boolean enableDateRangeOptimization) {
this.enable_date_range_optimization = enableDateRangeOptimization;
}
public boolean isEnableAnnotations() {
return enable_annotations;
}
public void setEnableAnnotations(boolean enableAnnotations) {
this.enable_annotations = enableAnnotations;
}
public boolean isEnableLocators() {
return enable_locators;
}
public void setEnableLocators(boolean enableLocators) {
this.enable_locators = enableLocators;
}
public boolean isEnableResultsType() {
return enable_results_type;
}
public void setEnableResultsType(boolean enableResultsType) {
this.enable_results_type = enableResultsType;
}
public boolean isEnableDetailedErrors() {
return enable_detailed_errors;
}
public void setEnableDetailedErrors(boolean enableDetailedErrors) {
this.enable_detailed_errors = enableDetailedErrors;
}
public boolean isDisableListTraversal() {
return disable_list_traversal;
}
public void setDisableListTraversal(boolean disableListTraversal) {
this.disable_list_traversal = disableListTraversal;
}
public boolean isDisableListDemotion() {
return disable_list_demotion;
}
public void setDisableListDemotion(boolean disableListDemotion) {
this.disable_list_demotion = disableListDemotion;
}
public boolean isDisableListPromotion() {
return disable_list_promotion;
}
public void setDisableListPromotion(boolean disableListPromotion) {
this.disable_list_promotion = disableListPromotion;
}
public boolean isEnableIntervalPromotion() {
return enable_interval_promotion;
}
public void setEnableIntervalPromotion(boolean enableIntervalPromotion) {
this.enable_interval_promotion = enableIntervalPromotion;
}
public boolean isEnableIntervalDemotion() {
return enable_interval_demotion;
}
public void setEnableIntervalDemotion(boolean enableIntervalDemotion) {
this.enable_interval_demotion = enableIntervalDemotion;
}
public boolean isDisableMethodInvocation() {
return disable_method_invocation;
}
public void setDisableMethodInvocation(boolean disableMethodInvocation) {
this.disable_method_invocation = disableMethodInvocation;
}
public boolean isRequireFromKeyword() {
return require_from_keyword;
}
public void setRequireFromKeyword(boolean requireFromKeyword) {
this.require_from_keyword = requireFromKeyword;
}
public boolean isDisableDefaultModelInfoLoad() {
return disable_default_model_info_load;
}
public void setDisableDefaultModelInfoLoad(boolean disableDefaultModelInfoLoad) {
this.disable_default_model_info_load = disableDefaultModelInfoLoad;
}
public CqlTranslator.Format getTranslatorFormat() {
return translator_format;
}
public void setTranslatorFormat(CqlTranslator.Format translatorFormat) {
this.translator_format = translatorFormat;
}
}

View File

@@ -0,0 +1,53 @@
package ca.uhn.fhir.jpa.starter.cr;
import org.opencds.cqf.fhir.cql.engine.retrieve.RetrieveSettings;
import org.opencds.cqf.fhir.cql.engine.terminology.TerminologySettings;
public class CqlProperties {
private Boolean use_embedded_libraries = true;
private CqlCompilerProperties compiler = new CqlCompilerProperties();
private CqlRuntimeProperties runtime = new CqlRuntimeProperties();
private TerminologySettings terminology = new TerminologySettings();
private RetrieveSettings data = new RetrieveSettings();
public Boolean getUse_embedded_libraries() {
return use_embedded_libraries;
}
public void setUse_embedded_libraries(Boolean use_embedded_libraries) {
this.use_embedded_libraries = use_embedded_libraries;
}
public CqlCompilerProperties getCompiler() {
return compiler;
}
public void setCompiler(CqlCompilerProperties compiler) {
this.compiler = compiler;
}
public CqlRuntimeProperties getRuntime() {
return runtime;
}
public void setRuntime(CqlRuntimeProperties runtime) {
this.runtime = runtime;
}
public TerminologySettings getTerminology() {
return terminology;
}
public void setTerminology(TerminologySettings terminology) {
this.terminology = terminology;
}
public RetrieveSettings getData() {
return data;
}
public void setData(RetrieveSettings data) {
this.data = data;
}
}

View File

@@ -0,0 +1,35 @@
package ca.uhn.fhir.jpa.starter.cr;
public class CqlRuntimeProperties {
private Boolean debug_logging_enabled = false;
private Boolean enable_validation = false;
private Boolean enable_expression_caching = true;
public boolean isDebugLoggingEnabled() {
return debug_logging_enabled;
}
public void setDebugLoggingEnabled(boolean debug_logging_enabled) {
this.debug_logging_enabled = debug_logging_enabled;
}
public boolean isEnableExpressionCaching() {
return enable_expression_caching;
}
public void setEnableExpressionCaching(boolean enable_expression_caching) {
this.enable_expression_caching = enable_expression_caching;
}
public boolean isEnableValidation() {
return enable_validation;
}
public void EnableValidation(boolean enable_validation) {
this.enable_validation = enable_validation;
}
}

View File

@@ -0,0 +1,228 @@
package ca.uhn.fhir.jpa.starter.cr;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.cqframework.cql.cql2elm.CqlCompilerOptions;
import org.cqframework.cql.cql2elm.model.CompiledLibrary;
import org.cqframework.cql.cql2elm.model.Model;
import org.hl7.cql.model.ModelIdentifier;
import org.hl7.elm.r1.VersionedIdentifier;
import org.opencds.cqf.cql.engine.execution.CqlEngine;
import org.opencds.cqf.cql.engine.runtime.Code;
import org.opencds.cqf.fhir.cql.EvaluationSettings;
import org.opencds.cqf.fhir.cql.engine.retrieve.RetrieveSettings;
import org.opencds.cqf.fhir.cql.engine.terminology.TerminologySettings;
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.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.security.concurrent.DelegatingSecurityContextExecutorService;
import ca.uhn.fhir.cr.common.CodeCacheResourceChangeListener;
import ca.uhn.fhir.cr.common.CqlThreadFactory;
import ca.uhn.fhir.cr.common.ElmCacheResourceChangeListener;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.cache.IResourceChangeListenerRegistry;
import ca.uhn.fhir.jpa.cache.ResourceChangeListenerRegistryInterceptor;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.provider.ResourceProviderFactory;
@Configuration
@Conditional({CrConfigCondition.class})
public class CrCommonConfig {
@Bean
@ConfigurationProperties(prefix = "hapi.fhir.cr")
CrProperties crProperties() {
return new CrProperties();
}
@Bean
RetrieveSettings retrieveSettings(CrProperties theCrProperties) {
return theCrProperties.getCql().getData();
}
@Bean
TerminologySettings terminologySettings(CrProperties theCrProperties) {
return theCrProperties.getCql().getTerminology();
}
@Bean
public EvaluationSettings evaluationSettings(
CrProperties theCrProperties,
RetrieveSettings theRetrieveSettings,
TerminologySettings theTerminologySettings,
Map<VersionedIdentifier, CompiledLibrary> theGlobalLibraryCache,
Map<ModelIdentifier, Model> theGlobalModelCache,
Map<String, List<Code>> theGlobalValueSetCache) {
var evaluationSettings = EvaluationSettings.getDefault();
var cqlOptions = evaluationSettings.getCqlOptions();
var cqlEngineOptions = cqlOptions.getCqlEngineOptions();
Set<CqlEngine.Options> options = EnumSet.noneOf(CqlEngine.Options.class);
var cqlRuntimeProperties = theCrProperties.getCql().getRuntime();
if (cqlRuntimeProperties.isEnableExpressionCaching()) {
options.add(CqlEngine.Options.EnableExpressionCaching);
}
if (cqlRuntimeProperties.isEnableValidation()) {
options.add(CqlEngine.Options.EnableValidation);
}
cqlEngineOptions.setOptions(options);
if (cqlRuntimeProperties.isDebugLoggingEnabled()) {
cqlEngineOptions.setDebugLoggingEnabled(true);
}
cqlOptions.setCqlEngineOptions(cqlEngineOptions);
var cqlCompilerOptions = new CqlCompilerOptions();
var cqlCompilerProperties = theCrProperties.getCql().getCompiler();
if (cqlCompilerProperties.isEnableDateRangeOptimization()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableDateRangeOptimization);
}
if (cqlCompilerProperties.isEnableAnnotations()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableAnnotations);
}
if (cqlCompilerProperties.isEnableLocators()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableLocators);
}
if (cqlCompilerProperties.isEnableResultsType()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableResultTypes);
}
cqlCompilerOptions.setVerifyOnly(cqlCompilerProperties.isVerifyOnly());
if (cqlCompilerProperties.isEnableDetailedErrors()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableDetailedErrors);
}
cqlCompilerOptions.setErrorLevel(cqlCompilerProperties.getErrorSeverityLevel());
if (cqlCompilerProperties.isDisableListTraversal()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableListTraversal);
}
if (cqlCompilerProperties.isDisableListDemotion()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableListDemotion);
}
if (cqlCompilerProperties.isDisableListPromotion()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableListPromotion);
}
if (cqlCompilerProperties.isEnableIntervalDemotion()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableIntervalDemotion);
}
if (cqlCompilerProperties.isEnableIntervalPromotion()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableIntervalPromotion);
}
if (cqlCompilerProperties.isDisableMethodInvocation()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableMethodInvocation);
}
if (cqlCompilerProperties.isRequireFromKeyword()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.RequireFromKeyword);
}
cqlCompilerOptions.setValidateUnits(cqlCompilerProperties.isValidateUnits());
if (cqlCompilerProperties.isDisableDefaultModelInfoLoad()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableDefaultModelInfoLoad);
}
cqlCompilerOptions.setSignatureLevel(cqlCompilerProperties.getSignatureLevel());
cqlCompilerOptions.setCompatibilityLevel(cqlCompilerProperties.getCompatibilityLevel());
cqlCompilerOptions.setAnalyzeDataRequirements(cqlCompilerProperties.isAnalyzeDataRequirements());
cqlCompilerOptions.setCollapseDataRequirements(cqlCompilerProperties.isCollapseDataRequirements());
cqlOptions.setCqlCompilerOptions(cqlCompilerOptions);
evaluationSettings.setLibraryCache(theGlobalLibraryCache);
evaluationSettings.setModelCache(theGlobalModelCache);
evaluationSettings.setValueSetCache(theGlobalValueSetCache);
evaluationSettings.setRetrieveSettings(theRetrieveSettings);
evaluationSettings.setTerminologySettings(theTerminologySettings);
return evaluationSettings;
}
@Primary
@Bean
public ExecutorService cqlExecutor() {
CqlThreadFactory factory = new CqlThreadFactory();
ExecutorService executor = Executors.newFixedThreadPool(2, factory);
executor = new DelegatingSecurityContextExecutorService(executor);
return executor;
}
@Bean
CareGapsProperties careGapsProperties(CrProperties theCrProperties) {
var careGapsProperties = new CareGapsProperties();
careGapsProperties.setCareGapsReporter(theCrProperties.getCareGaps().getReporter());
careGapsProperties.setCareGapsCompositionSectionAuthor(theCrProperties.getCareGaps().getSection_author());
return careGapsProperties;
}
@Bean
MeasureEvaluationOptions measureEvaluationOptions(
EvaluationSettings theEvaluationSettings, Map<String, ValidationProfile> theValidationProfiles) {
MeasureEvaluationOptions measureEvalOptions = new MeasureEvaluationOptions();
measureEvalOptions.setEvaluationSettings(theEvaluationSettings);
if (measureEvalOptions.isValidationEnabled()) {
measureEvalOptions.setValidationProfiles(theValidationProfiles);
}
return measureEvalOptions;
}
@Bean
public PostInitProviderRegisterer postInitProviderRegisterer(
RestfulServer theRestfulServer, ResourceProviderFactory theResourceProviderFactory) {
return new PostInitProviderRegisterer(theRestfulServer, theResourceProviderFactory);
}
@Bean
public Map<VersionedIdentifier, CompiledLibrary> globalLibraryCache() {
return new ConcurrentHashMap<>();
}
@Bean
public Map<ModelIdentifier, Model> globalModelCache() {
return new ConcurrentHashMap<>();
}
@Bean
public Map<String, List<Code>> globalValueSetCache() {
return new ConcurrentHashMap<>();
}
@Bean
public ElmCacheResourceChangeListener elmCacheResourceChangeListener(
IResourceChangeListenerRegistry theResourceChangeListenerRegistry,
DaoRegistry theDaoRegistry,
EvaluationSettings theEvaluationSettings) {
ElmCacheResourceChangeListener listener =
new ElmCacheResourceChangeListener(theDaoRegistry, theEvaluationSettings.getLibraryCache());
theResourceChangeListenerRegistry.registerResourceResourceChangeListener(
"Library", SearchParameterMap.newSynchronous(), listener, 1000);
return listener;
}
@Bean
public CodeCacheResourceChangeListener codeCacheResourceChangeListener(
IResourceChangeListenerRegistry theResourceChangeListenerRegistry,
EvaluationSettings theEvaluationSettings,
DaoRegistry theDaoRegistry) {
CodeCacheResourceChangeListener listener =
new CodeCacheResourceChangeListener(theDaoRegistry, theEvaluationSettings.getValueSetCache());
// registry
theResourceChangeListenerRegistry.registerResourceResourceChangeListener(
"ValueSet", SearchParameterMap.newSynchronous(), listener, 1000);
return listener;
}
@Bean
public ResourceChangeListenerRegistryInterceptor resourceChangeListenerRegistryInterceptor() {
return new ResourceChangeListenerRegistryInterceptor();
}
}

View File

@@ -3,40 +3,12 @@ package ca.uhn.fhir.jpa.starter.cr;
import org.cqframework.cql.cql2elm.CqlCompilerException;
import org.cqframework.cql.cql2elm.CqlTranslator;
import org.cqframework.cql.cql2elm.LibraryBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "hapi.fhir.cr")
public class CrProperties {
private Boolean enabled;
// cql settings
private Boolean cql_use_embedded_libraries = true;
private Boolean cql_runtime_debug_logging_enabled = false;
private Boolean cql_runtime_enable_validation = false;
private Boolean cql_runtime_enable_expression_caching = false;
private Boolean cql_compiler_validate_units = true;
private Boolean cql_compiler_verify_only = false;
private String cql_compiler_compatibility_level = "1.5";
private CqlCompilerException.ErrorSeverity cql_compiler_error_level = CqlCompilerException.ErrorSeverity.Info;
private LibraryBuilder.SignatureLevel cql_compiler_signature_level = LibraryBuilder.SignatureLevel.All;
private Boolean cql_compiler_analyze_data_requirements = false;
private Boolean cql_compiler_collapse_data_requirements = false;
private CqlTranslator.Format cql_compiler_translator_format = CqlTranslator.Format.JSON;
private Boolean cql_compiler_enable_date_range_optimization = false;
private Boolean cql_compiler_enable_annotations = false;
private Boolean cql_compiler_enable_locators = false;
private Boolean cql_compiler_enable_results_type = false;
private Boolean cql_compiler_enable_detailed_errors = false;
private Boolean cql_compiler_disable_list_traversal = false;
private Boolean cql_compiler_disable_list_demotion = false;
private Boolean cql_compiler_disable_list_promotion = false;
private Boolean cql_compiler_enable_interval_demotion = false;
private Boolean cql_compiler_enable_interval_promotion = false;
private Boolean cql_compiler_disable_method_invocation = false;
private Boolean cql_compiler_require_from_keyword = false;
private Boolean cql_compiler_disable_default_model_info_load = false;
// Care-gaps Settings
private String caregaps_reporter = "default";
private String caregaps_section_author = "default";
private CareGapsProperties careGaps = new CareGapsProperties();
private CqlProperties cql = new CqlProperties();
public Boolean getEnabled() {
return enabled;
@@ -46,219 +18,19 @@ public class CrProperties {
this.enabled = enabled;
}
public boolean isCqlUseEmbeddedLibraries() {
return cql_use_embedded_libraries;
public CareGapsProperties getCareGaps() {
return careGaps;
}
public void setCqlUseEmbeddedLibraries(boolean cql_use_embedded_libraries) {
this.cql_use_embedded_libraries = cql_use_embedded_libraries;
public void setCareGaps(CareGapsProperties careGaps) {
this.careGaps = careGaps;
}
public boolean isCqlRuntimeDebugLoggingEnabled() {
return cql_runtime_debug_logging_enabled;
public CqlProperties getCql() {
return cql;
}
public void setCqlRuntimeDebugLoggingEnabled(boolean cqlRuntimeDebugLoggingEnabled) {
this.cql_runtime_debug_logging_enabled = cqlRuntimeDebugLoggingEnabled;
}
public boolean isCqlCompilerValidateUnits() {
return cql_compiler_validate_units;
}
public void setCqlCompilerValidateUnits(boolean cqlCompilerValidateUnits) {
this.cql_compiler_validate_units = cqlCompilerValidateUnits;
}
public boolean isCqlCompilerVerifyOnly() {
return cql_compiler_verify_only;
}
public void setCqlCompilerVerifyOnly(boolean cqlCompilerVerifyOnly) {
this.cql_compiler_verify_only = cqlCompilerVerifyOnly;
}
public String getCqlCompilerCompatibilityLevel() {
return cql_compiler_compatibility_level;
}
public void setCqlCompilerCompatibilityLevel(String cqlCompilerCompatibilityLevel) {
this.cql_compiler_compatibility_level = cqlCompilerCompatibilityLevel;
}
public CqlCompilerException.ErrorSeverity getCqlCompilerErrorSeverityLevel() {
return cql_compiler_error_level;
}
public void setCqlCompilerErrorSeverityLevel(CqlCompilerException.ErrorSeverity cqlCompilerErrorSeverityLevel) {
this.cql_compiler_error_level = cqlCompilerErrorSeverityLevel;
}
public LibraryBuilder.SignatureLevel getCqlCompilerSignatureLevel() {
return cql_compiler_signature_level;
}
public void setCqlCompilerSignatureLevel(LibraryBuilder.SignatureLevel cqlCompilerSignatureLevel) {
this.cql_compiler_signature_level = cqlCompilerSignatureLevel;
}
public boolean isCqlCompilerAnalyzeDataRequirements() {
return cql_compiler_analyze_data_requirements;
}
public void setCqlCompilerAnalyzeDataRequirements(boolean cqlCompilerAnalyzeDataRequirements) {
this.cql_compiler_analyze_data_requirements = cqlCompilerAnalyzeDataRequirements;
}
public boolean isCqlCompilerCollapseDataRequirements() {
return cql_compiler_collapse_data_requirements;
}
public void setCqlCompilerCollapseDataRequirements(boolean cqlCompilerCollapseDataRequirements) {
this.cql_compiler_collapse_data_requirements = cqlCompilerCollapseDataRequirements;
}
public boolean isEnableDateRangeOptimization() {
return cql_compiler_enable_date_range_optimization;
}
public void setEnableDateRangeOptimization(boolean enableDateRangeOptimization) {
this.cql_compiler_enable_date_range_optimization = enableDateRangeOptimization;
}
public boolean isEnableAnnotations() {
return cql_compiler_enable_annotations;
}
public void setEnableAnnotations(boolean enableAnnotations) {
this.cql_compiler_enable_annotations = enableAnnotations;
}
public boolean isEnableLocators() {
return cql_compiler_enable_locators;
}
public void setEnableLocators(boolean enableLocators) {
this.cql_compiler_enable_locators = enableLocators;
}
public boolean isEnableResultsType() {
return cql_compiler_enable_results_type;
}
public void setEnableResultsType(boolean enableResultsType) {
this.cql_compiler_enable_results_type = enableResultsType;
}
public boolean isEnableDetailedErrors() {
return cql_compiler_enable_detailed_errors;
}
public void setEnableDetailedErrors(boolean enableDetailedErrors) {
this.cql_compiler_enable_detailed_errors = enableDetailedErrors;
}
public boolean isDisableListTraversal() {
return cql_compiler_disable_list_traversal;
}
public void setDisableListTraversal(boolean disableListTraversal) {
this.cql_compiler_disable_list_traversal = disableListTraversal;
}
public boolean isDisableListDemotion() {
return cql_compiler_disable_list_demotion;
}
public void setDisableListDemotion(boolean disableListDemotion) {
this.cql_compiler_disable_list_demotion = disableListDemotion;
}
public boolean isDisableListPromotion() {
return cql_compiler_disable_list_promotion;
}
public void setDisableListPromotion(boolean disableListPromotion) {
this.cql_compiler_disable_list_promotion = disableListPromotion;
}
public boolean isEnableIntervalPromotion() {
return cql_compiler_enable_interval_promotion;
}
public void setEnableIntervalPromotion(boolean enableIntervalPromotion) {
this.cql_compiler_enable_interval_promotion = enableIntervalPromotion;
}
public boolean isEnableIntervalDemotion() {
return cql_compiler_enable_interval_demotion;
}
public void setEnableIntervalDemotion(boolean enableIntervalDemotion) {
this.cql_compiler_enable_interval_demotion = enableIntervalDemotion;
}
public boolean isDisableMethodInvocation() {
return cql_compiler_disable_method_invocation;
}
public void setDisableMethodInvocation(boolean disableMethodInvocation) {
this.cql_compiler_disable_method_invocation = disableMethodInvocation;
}
public boolean isRequireFromKeyword() {
return cql_compiler_require_from_keyword;
}
public void setRequireFromKeyword(boolean requireFromKeyword) {
this.cql_compiler_require_from_keyword = requireFromKeyword;
}
public boolean isDisableDefaultModelInfoLoad() {
return cql_compiler_disable_default_model_info_load;
}
public void setDisableDefaultModelInfoLoad(boolean disableDefaultModelInfoLoad) {
this.cql_compiler_disable_default_model_info_load = disableDefaultModelInfoLoad;
}
public boolean isCqlRuntimeEnableExpressionCaching() {
return cql_runtime_enable_expression_caching;
}
public void setCqlRuntimeEnableExpressionCaching(boolean cqlRuntimeEnableExpressionCaching) {
this.cql_runtime_enable_expression_caching = cqlRuntimeEnableExpressionCaching;
}
public boolean isCqlRuntimeEnableValidation() {
return cql_runtime_enable_validation;
}
public void setCqlRuntimeEnableValidation(boolean cqlRuntimeEnableValidation) {
this.cql_runtime_enable_validation = cqlRuntimeEnableValidation;
}
public CqlTranslator.Format getCqlTranslatorFormat() {
return cql_compiler_translator_format;
}
public void setCqlTranslatorFormat(CqlTranslator.Format cqlTranslatorFormat) {
this.cql_compiler_translator_format = cqlTranslatorFormat;
}
public String getCareGapsReporter() {
return caregaps_reporter;
}
public String getCareGapsSectionAuthor() {
return caregaps_section_author;
}
public void setCareGapsSectionAuthor(String theCareGapsSectionAuthor) {
this.caregaps_section_author = theCareGapsSectionAuthor;
}
public void setCareGapsReporter(String theCareGapsReporter) {
this.caregaps_reporter = theCareGapsReporter;
public void setCql(CqlProperties cql) {
this.cql = cql;
}
}

View File

@@ -1,195 +1,25 @@
package ca.uhn.fhir.jpa.starter.cr;
import ca.uhn.fhir.cr.common.CodeCacheResourceChangeListener;
import ca.uhn.fhir.cr.common.ElmCacheResourceChangeListener;
import ca.uhn.fhir.cr.config.dstu3.ApplyOperationConfig;
import ca.uhn.fhir.cr.config.dstu3.CrDstu3Config;
import ca.uhn.fhir.cr.config.dstu3.ExtractOperationConfig;
import ca.uhn.fhir.cr.config.dstu3.PackageOperationConfig;
import ca.uhn.fhir.cr.config.dstu3.PopulateOperationConfig;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.cache.IResourceChangeListenerRegistry;
import ca.uhn.fhir.jpa.cache.ResourceChangeListenerRegistryInterceptor;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.cr.config.dstu3.QuestionnaireOperationConfig;
import ca.uhn.fhir.jpa.starter.annotations.OnDSTU3Condition;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.provider.ResourceProviderFactory;
import org.cqframework.cql.cql2elm.CqlCompilerOptions;
import org.cqframework.cql.cql2elm.model.CompiledLibrary;
import org.cqframework.cql.cql2elm.model.Model;
import org.hl7.cql.model.ModelIdentifier;
import org.hl7.elm.r1.VersionedIdentifier;
import org.opencds.cqf.cql.engine.execution.CqlEngine;
import org.opencds.cqf.cql.engine.runtime.Code;
import org.opencds.cqf.fhir.cql.EvaluationSettings;
import org.opencds.cqf.fhir.cr.measure.MeasureEvaluationOptions;
import org.opencds.cqf.fhir.utility.ValidationProfile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.*;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@Configuration
@Conditional({OnDSTU3Condition.class, CrConfigCondition.class})
@Import({
// BaseCrConfig.class,
CrCommonConfig.class,
CrDstu3Config.class,
ApplyOperationConfig.class,
ExtractOperationConfig.class,
PackageOperationConfig.class,
PopulateOperationConfig.class
PopulateOperationConfig.class,
QuestionnaireOperationConfig.class
})
public class StarterCrDstu3Config {
private static final Logger ourLogger = LoggerFactory.getLogger(StarterCrDstu3Config.class);
@Bean
MeasureEvaluationOptions measureEvaluationOptions(
EvaluationSettings theEvaluationSettings, Map<String, ValidationProfile> theValidationProfiles) {
MeasureEvaluationOptions measureEvalOptions = new MeasureEvaluationOptions();
measureEvalOptions.setEvaluationSettings(theEvaluationSettings);
if (measureEvalOptions.isValidationEnabled()) {
measureEvalOptions.setValidationProfiles(theValidationProfiles);
}
return measureEvalOptions;
}
@Bean
public EvaluationSettings evaluationSettings(
CrProperties theCrProperties,
Map<VersionedIdentifier, CompiledLibrary> theGlobalLibraryCache,
Map<ModelIdentifier, Model> theGlobalModelCache,
Map<String, List<Code>> theGlobalValueSetCache) {
var evaluationSettings = EvaluationSettings.getDefault();
var cqlOptions = evaluationSettings.getCqlOptions();
var cqlEngineOptions = cqlOptions.getCqlEngineOptions();
Set<CqlEngine.Options> options = EnumSet.noneOf(CqlEngine.Options.class);
if (theCrProperties.isCqlRuntimeEnableExpressionCaching()) {
options.add(CqlEngine.Options.EnableExpressionCaching);
}
if (theCrProperties.isCqlRuntimeEnableValidation()) {
options.add(CqlEngine.Options.EnableValidation);
}
cqlEngineOptions.setOptions(options);
cqlOptions.setCqlEngineOptions(cqlEngineOptions);
var cqlCompilerOptions = new CqlCompilerOptions();
if (theCrProperties.isEnableDateRangeOptimization()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableDateRangeOptimization);
}
if (theCrProperties.isEnableAnnotations()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableAnnotations);
}
if (theCrProperties.isEnableLocators()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableLocators);
}
if (theCrProperties.isEnableResultsType()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableResultTypes);
}
cqlCompilerOptions.setVerifyOnly(theCrProperties.isCqlCompilerVerifyOnly());
if (theCrProperties.isEnableDetailedErrors()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableDetailedErrors);
}
cqlCompilerOptions.setErrorLevel(theCrProperties.getCqlCompilerErrorSeverityLevel());
if (theCrProperties.isDisableListTraversal()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableListTraversal);
}
if (theCrProperties.isDisableListDemotion()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableListDemotion);
}
if (theCrProperties.isDisableListPromotion()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableListPromotion);
}
if (theCrProperties.isEnableIntervalDemotion()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableIntervalDemotion);
}
if (theCrProperties.isEnableIntervalPromotion()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableIntervalPromotion);
}
if (theCrProperties.isDisableMethodInvocation()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableMethodInvocation);
}
if (theCrProperties.isRequireFromKeyword()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.RequireFromKeyword);
}
cqlCompilerOptions.setValidateUnits(theCrProperties.isCqlCompilerValidateUnits());
if (theCrProperties.isDisableDefaultModelInfoLoad()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableDefaultModelInfoLoad);
}
cqlCompilerOptions.setSignatureLevel(theCrProperties.getCqlCompilerSignatureLevel());
cqlCompilerOptions.setCompatibilityLevel(theCrProperties.getCqlCompilerCompatibilityLevel());
cqlCompilerOptions.setAnalyzeDataRequirements(theCrProperties.isCqlCompilerAnalyzeDataRequirements());
cqlCompilerOptions.setCollapseDataRequirements(theCrProperties.isCqlCompilerCollapseDataRequirements());
cqlOptions.setCqlCompilerOptions(cqlCompilerOptions);
evaluationSettings.setLibraryCache(theGlobalLibraryCache);
evaluationSettings.setModelCache(theGlobalModelCache);
evaluationSettings.setValueSetCache(theGlobalValueSetCache);
return evaluationSettings;
}
@Bean
public PostInitProviderRegisterer postInitProviderRegisterer(
RestfulServer theRestfulServer, ResourceProviderFactory theResourceProviderFactory) {
return new PostInitProviderRegisterer(theRestfulServer, theResourceProviderFactory);
}
@Bean
public CrProperties crProperties() {
return new CrProperties();
}
@Bean
public Map<VersionedIdentifier, CompiledLibrary> globalLibraryCache() {
return new ConcurrentHashMap<>();
}
@Bean
public Map<ModelIdentifier, Model> globalModelCache() {
return new ConcurrentHashMap<>();
}
@Bean
public Map<String, List<Code>> globalValueSetCache() {
return new ConcurrentHashMap<>();
}
@Bean
public ElmCacheResourceChangeListener elmCacheResourceChangeListener(
IResourceChangeListenerRegistry theResourceChangeListenerRegistry,
DaoRegistry theDaoRegistry,
EvaluationSettings theEvaluationSettings) {
ElmCacheResourceChangeListener listener =
new ElmCacheResourceChangeListener(theDaoRegistry, theEvaluationSettings.getLibraryCache());
theResourceChangeListenerRegistry.registerResourceResourceChangeListener(
"Library", SearchParameterMap.newSynchronous(), listener, 1000);
return listener;
}
@Bean
public CodeCacheResourceChangeListener codeCacheResourceChangeListener(
IResourceChangeListenerRegistry theResourceChangeListenerRegistry,
EvaluationSettings theEvaluationSettings,
DaoRegistry theDaoRegistry) {
CodeCacheResourceChangeListener listener =
new CodeCacheResourceChangeListener(theDaoRegistry, theEvaluationSettings.getValueSetCache());
// registry
theResourceChangeListenerRegistry.registerResourceResourceChangeListener(
"ValueSet", SearchParameterMap.newSynchronous(), listener, 1000);
return listener;
}
@Bean
public ResourceChangeListenerRegistryInterceptor resourceChangeListenerRegistryInterceptor() {
return new ResourceChangeListenerRegistryInterceptor();
}
}

View File

@@ -1,220 +1,27 @@
package ca.uhn.fhir.jpa.starter.cr;
import ca.uhn.fhir.cr.common.CodeCacheResourceChangeListener;
import ca.uhn.fhir.cr.common.CqlThreadFactory;
import ca.uhn.fhir.cr.common.ElmCacheResourceChangeListener;
import ca.uhn.fhir.cr.config.r4.ApplyOperationConfig;
import ca.uhn.fhir.cr.config.r4.CrR4Config;
import ca.uhn.fhir.cr.config.r4.ExtractOperationConfig;
import ca.uhn.fhir.cr.config.r4.PackageOperationConfig;
import ca.uhn.fhir.cr.config.r4.PopulateOperationConfig;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.cache.IResourceChangeListenerRegistry;
import ca.uhn.fhir.jpa.cache.ResourceChangeListenerRegistryInterceptor;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.cr.config.r4.QuestionnaireOperationConfig;
import ca.uhn.fhir.jpa.starter.annotations.OnR4Condition;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.provider.ResourceProviderFactory;
import org.cqframework.cql.cql2elm.CqlCompilerOptions;
import org.cqframework.cql.cql2elm.model.CompiledLibrary;
import org.cqframework.cql.cql2elm.model.Model;
import org.hl7.cql.model.ModelIdentifier;
import org.hl7.elm.r1.VersionedIdentifier;
import org.opencds.cqf.cql.engine.execution.CqlEngine;
import org.opencds.cqf.cql.engine.runtime.Code;
import org.opencds.cqf.fhir.cql.EvaluationSettings;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Primary;
import org.springframework.security.concurrent.DelegatingSecurityContextExecutorService;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Configuration
@Conditional({OnR4Condition.class, CrConfigCondition.class})
@Import({
CrCommonConfig.class,
CrR4Config.class,
ApplyOperationConfig.class,
ExtractOperationConfig.class,
PackageOperationConfig.class,
PopulateOperationConfig.class
PopulateOperationConfig.class,
QuestionnaireOperationConfig.class
})
public class StarterCrR4Config {
private static final Logger ourLogger = LoggerFactory.getLogger(StarterCrR4Config.class);
@Primary
@Bean
public ExecutorService cqlExecutor() {
CqlThreadFactory factory = new CqlThreadFactory();
ExecutorService executor = Executors.newFixedThreadPool(2, factory);
executor = new DelegatingSecurityContextExecutorService(executor);
return executor;
}
@Bean
CareGapsProperties careGapsProperties(CrProperties theCrProperties) {
var careGapsProperties = new CareGapsProperties();
careGapsProperties.setCareGapsReporter(theCrProperties.getCareGapsReporter());
careGapsProperties.setCareGapsCompositionSectionAuthor(theCrProperties.getCareGapsSectionAuthor());
return careGapsProperties;
}
@Bean
MeasureEvaluationOptions measureEvaluationOptions(
EvaluationSettings theEvaluationSettings, Map<String, ValidationProfile> theValidationProfiles) {
MeasureEvaluationOptions measureEvalOptions = new MeasureEvaluationOptions();
measureEvalOptions.setEvaluationSettings(theEvaluationSettings);
if (measureEvalOptions.isValidationEnabled()) {
measureEvalOptions.setValidationProfiles(theValidationProfiles);
}
return measureEvalOptions;
}
@Bean
public EvaluationSettings evaluationSettings(
CrProperties theCrProperties,
Map<VersionedIdentifier, CompiledLibrary> theGlobalLibraryCache,
Map<ModelIdentifier, Model> theGlobalModelCache,
Map<String, List<Code>> theGlobalValueSetCache) {
var evaluationSettings = EvaluationSettings.getDefault();
var cqlOptions = evaluationSettings.getCqlOptions();
var cqlEngineOptions = cqlOptions.getCqlEngineOptions();
Set<CqlEngine.Options> options = EnumSet.noneOf(CqlEngine.Options.class);
if (theCrProperties.isCqlRuntimeEnableExpressionCaching()) {
options.add(CqlEngine.Options.EnableExpressionCaching);
}
if (theCrProperties.isCqlRuntimeEnableValidation()) {
options.add(CqlEngine.Options.EnableValidation);
}
cqlEngineOptions.setOptions(options);
cqlOptions.setCqlEngineOptions(cqlEngineOptions);
var cqlCompilerOptions = new CqlCompilerOptions();
if (theCrProperties.isEnableDateRangeOptimization()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableDateRangeOptimization);
}
if (theCrProperties.isEnableAnnotations()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableAnnotations);
}
if (theCrProperties.isEnableLocators()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableLocators);
}
if (theCrProperties.isEnableResultsType()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableResultTypes);
}
cqlCompilerOptions.setVerifyOnly(theCrProperties.isCqlCompilerVerifyOnly());
if (theCrProperties.isEnableDetailedErrors()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableDetailedErrors);
}
cqlCompilerOptions.setErrorLevel(theCrProperties.getCqlCompilerErrorSeverityLevel());
if (theCrProperties.isDisableListTraversal()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableListTraversal);
}
if (theCrProperties.isDisableListDemotion()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableListDemotion);
}
if (theCrProperties.isDisableListPromotion()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableListPromotion);
}
if (theCrProperties.isEnableIntervalDemotion()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableIntervalDemotion);
}
if (theCrProperties.isEnableIntervalPromotion()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableIntervalPromotion);
}
if (theCrProperties.isDisableMethodInvocation()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableMethodInvocation);
}
if (theCrProperties.isRequireFromKeyword()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.RequireFromKeyword);
}
cqlCompilerOptions.setValidateUnits(theCrProperties.isCqlCompilerValidateUnits());
if (theCrProperties.isDisableDefaultModelInfoLoad()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableDefaultModelInfoLoad);
}
cqlCompilerOptions.setSignatureLevel(theCrProperties.getCqlCompilerSignatureLevel());
cqlCompilerOptions.setCompatibilityLevel(theCrProperties.getCqlCompilerCompatibilityLevel());
cqlCompilerOptions.setAnalyzeDataRequirements(theCrProperties.isCqlCompilerAnalyzeDataRequirements());
cqlCompilerOptions.setCollapseDataRequirements(theCrProperties.isCqlCompilerCollapseDataRequirements());
cqlOptions.setCqlCompilerOptions(cqlCompilerOptions);
evaluationSettings.setLibraryCache(theGlobalLibraryCache);
evaluationSettings.setModelCache(theGlobalModelCache);
evaluationSettings.setValueSetCache(theGlobalValueSetCache);
return evaluationSettings;
}
@Bean
public PostInitProviderRegisterer postInitProviderRegisterer(
RestfulServer theRestfulServer, ResourceProviderFactory theResourceProviderFactory) {
return new PostInitProviderRegisterer(theRestfulServer, theResourceProviderFactory);
}
@Bean
public CrProperties crProperties() {
return new CrProperties();
}
@Bean
public Map<VersionedIdentifier, CompiledLibrary> globalLibraryCache() {
return new ConcurrentHashMap<>();
}
@Bean
public Map<ModelIdentifier, Model> globalModelCache() {
return new ConcurrentHashMap<>();
}
@Bean
public Map<String, List<Code>> globalValueSetCache() {
return new ConcurrentHashMap<>();
}
@Bean
public ElmCacheResourceChangeListener elmCacheResourceChangeListener(
IResourceChangeListenerRegistry theResourceChangeListenerRegistry,
DaoRegistry theDaoRegistry,
EvaluationSettings theEvaluationSettings) {
ElmCacheResourceChangeListener listener =
new ElmCacheResourceChangeListener(theDaoRegistry, theEvaluationSettings.getLibraryCache());
theResourceChangeListenerRegistry.registerResourceResourceChangeListener(
"Library", SearchParameterMap.newSynchronous(), listener, 1000);
return listener;
}
@Bean
public CodeCacheResourceChangeListener codeCacheResourceChangeListener(
IResourceChangeListenerRegistry theResourceChangeListenerRegistry,
EvaluationSettings theEvaluationSettings,
DaoRegistry theDaoRegistry) {
CodeCacheResourceChangeListener listener =
new CodeCacheResourceChangeListener(theDaoRegistry, theEvaluationSettings.getValueSetCache());
// registry
theResourceChangeListenerRegistry.registerResourceResourceChangeListener(
"ValueSet", SearchParameterMap.newSynchronous(), listener, 1000);
return listener;
}
@Bean
public ResourceChangeListenerRegistryInterceptor resourceChangeListenerRegistryInterceptor() {
return new ResourceChangeListenerRegistryInterceptor();
}
}

View File

@@ -6,7 +6,6 @@ import ca.uhn.fhir.jpa.ips.generator.IIpsGeneratorSvc;
import ca.uhn.fhir.jpa.ips.generator.IpsGeneratorSvcImpl;
import ca.uhn.fhir.jpa.ips.jpa.DefaultJpaIpsGenerationStrategy;
import ca.uhn.fhir.jpa.ips.provider.IpsOperationProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;

View File

@@ -29,7 +29,6 @@ management:
spring:
main:
allow-circular-references: true
#allow-bean-definition-overriding: true
flyway:
enabled: false
baselineOnMigrate: true
@@ -78,9 +77,50 @@ hapi:
### Flag is false by default, can be passed as command line argument to override.
cr:
enabled: false
caregaps:
reporter: "default"
section_author: "default"
cql:
use_embedded_libraries: true
compiler:
### These are low-level compiler options.
### They are not typically needed by most users.
# validate_units: true
# verify_only: false
# compatibility_level: "1.5"
error_level: Info
signature_level: All
# analyze_data_requirements: false
# collapse_data_requirements: false
# translator_format: JSON
# enable_date_range_optimization: true
enable_annotations: true
enable_locators: true
enable_results_type: true
enable_detailed_errors: true
# disable_list_traversal: false
# disable_list_demotion: false
# enable_interval_demotion: false
# enable_interval_promotion: false
# disable_method_invocation: false
# require_from_keyword: false
# disable_default_model_info_load: false
runtime:
debug_logging_enabled: false
# enable_validation: false
# enable_expression_caching: true
terminology:
valueset_preexpansion_mode: REQUIRE # USE_IF_PRESENT, REQUIRE, IGNORE
valueset_expansion_mode: PERFORM_NAIVE_EXPANSION # AUTO, USE_EXPANSION_OPERATION, PERFORM_NAIVE_EXPANSION
valueset_membership_mode: USE_EXPANSION # AUTO, USE_VALIDATE_CODE_OPERATION, USE_EXPANSION
code_lookup_mode: USE_VALIDATE_CODE_OPERATION # AUTO, USE_VALIDATE_CODE_OPERATION, USE_CODESYSTEM_URL
data:
search_parameter_mode: FILTER_IN_MEMORY # AUTO, USE_SEARCH_PARAMETERS, FILTER_IN_MEMORY
terminology_parameter_mode: FILTER_IN_MEMORY # AUTO, USE_VALUE_SET_URL, USE_INLINE_CODES, FILTER_IN_MEMORY
profile_mode: DECLARED # ENFORCED, DECLARED, OPTIONAL, TRUST, OFF
cdshooks:
enabled: true
enabled: false
clientIdHeaderName: client_id
### This enables the swagger-ui at /fhir/swagger-ui/index.html as well as the /fhir/api-docs (see https://hapifhir.io/hapi-fhir/docs/server_plain/openapi.html)

View File

@@ -0,0 +1,294 @@
#Uncomment the "servlet" and "context-path" lines below to make the fhir endpoint available at /example/path/fhir instead of the default value of /fhir
server:
# servlet:
# context-path: /example/path
port: 8080
#Adds the option to go to eg. http://localhost:8080/actuator/health for seeing the running configuration
#see https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html#actuator.endpoints
management:
endpoints:
web:
exposure:
include: "health,prometheus"
spring:
main:
allow-circular-references: true
allow-bean-definition-overriding: true
flyway:
enabled: false
baselineOnMigrate: true
fail-on-missing-locations: false
datasource:
#url: 'jdbc:h2:file:./target/database/h2'
url: jdbc:h2:mem:test_mem
username: sa
password: null
driverClassName: org.h2.Driver
max-active: 15
# database connection pool size
hikari:
maximum-pool-size: 10
jpa:
properties:
hibernate.format_sql: false
hibernate.show_sql: false
#Hibernate dialect is automatically detected except Postgres and H2.
#If using H2, then supply the value of ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect
#If using postgres, then supply the value of ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgres94Dialect
hibernate.dialect: ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect
# hibernate.hbm2ddl.auto: update
# hibernate.jdbc.batch_size: 20
# hibernate.cache.use_query_cache: false
# hibernate.cache.use_second_level_cache: false
# hibernate.cache.use_structured_entries: false
# hibernate.cache.use_minimal_puts: false
### These settings will enable fulltext search with lucene or elastic
hibernate.search.enabled: true
### lucene parameters
# hibernate.search.backend.type: lucene
# hibernate.search.backend.analysis.configurer: ca.uhn.fhir.jpa.search.HapiHSearchAnalysisConfigurers$HapiLuceneAnalysisConfigurer
# hibernate.search.backend.directory.type: local-filesystem
# hibernate.search.backend.directory.root: target/lucenefiles
# hibernate.search.backend.lucene_version: lucene_current
### elastic parameters ===> see also elasticsearch section below <===
# hibernate.search.backend.type: elasticsearch
# hibernate.search.backend.analysis.configurer: ca.uhn.fhir.jpa.search.HapiHSearchAnalysisConfigurers$HapiElasticAnalysisConfigurer
hapi:
fhir:
### This flag when enabled to true, will avail evaluate measure operations from CR Module.
### Flag is false by default, can be passed as command line argument to override.
cr:
enabled: true
caregaps:
reporter: "default"
section_author: "default"
cql:
use_embedded_libraries: true
compiler:
### These are low-level compiler options.
### They are not typically needed by most users.
# validate_units: true
# verify_only: false
# compatibility_level: "1.5"
error_level: Info
signature_level: All
# analyze_data_requirements: false
# collapse_data_requirements: false
# translator_format: JSON
# enable_date_range_optimization: true
enable_annotations: true
enable_locators: true
enable_results_type: true
enable_detailed_errors: true
# disable_list_traversal: false
# disable_list_demotion: false
# enable_interval_demotion: false
# enable_interval_promotion: false
# disable_method_invocation: false
# require_from_keyword: false
# disable_default_model_info_load: false
runtime:
debug_logging_enabled: false
# enable_validation: false
# enable_expression_caching: true
terminology:
valueset_preexpansion_mode: REQUIRE # USE_IF_PRESENT, REQUIRE, IGNORE
valueset_expansion_mode: PERFORM_NAIVE_EXPANSION # AUTO, USE_EXPANSION_OPERATION, PERFORM_NAIVE_EXPANSION
valueset_membership_mode: USE_EXPANSION # AUTO, USE_VALIDATE_CODE_OPERATION, USE_EXPANSION
code_lookup_mode: USE_CODESYSTEM_URL # AUTO, USE_VALIDATE_CODE_OPERATION, USE_CODESYSTEM_URL
data:
search_parameter_mode: USE_SEARCH_PARAMETERS # AUTO, USE_SEARCH_PARAMETERS, FILTER_IN_MEMORY
terminology_parameter_mode: FILTER_IN_MEMORY # AUTO, USE_VALUE_SET_URL, USE_INLINE_CODES, FILTER_IN_MEMORY
profile_mode: OFF # ENFORCED, DECLARED, OPTIONAL, TRUST, OFF
cdshooks:
enabled: true
clientIdHeaderName: client_id
### This enables the swagger-ui at /fhir/swagger-ui/index.html as well as the /fhir/api-docs (see https://hapifhir.io/hapi-fhir/docs/server_plain/openapi.html)
openapi_enabled: true
### This is the FHIR version. Choose between, DSTU2, DSTU3, R4 or R5
fhir_version: R4
### Flag is false by default. This flag enables runtime installation of IG's.
ig_runtime_upload_enabled: false
### This flag when enabled to true, will avail evaluate measure operations from CR Module.
### enable to use the ApacheProxyAddressStrategy which uses X-Forwarded-* headers
### to determine the FHIR server address
# use_apache_address_strategy: false
### forces the use of the https:// protocol for the returned server address.
### alternatively, it may be set using the X-Forwarded-Proto header.
# use_apache_address_strategy_https: false
### enables the server to overwrite defaults on HTML, css, etc. under the url pattern of eg. /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
### will be served under /web/app
#app_content_path: ./configs/app
### enable to set the Server URL
# server_address: http://hapi.fhir.org/baseR4
# defer_indexing_for_codesystems_of_size: 101
# install_transitive_ig_dependencies: true
#implementationguides:
### example from registry (packages.fhir.org)
# swiss:
# name: swiss.mednet.fhir
# version: 0.8.0
# reloadExisting: false
# installMode: STORE_AND_INSTALL
# example not from registry
# ips_1_0_0:
# packageUrl: https://build.fhir.org/ig/HL7/fhir-ips/package.tgz
# name: hl7.fhir.uv.ips
# version: 1.0.0
# supported_resource_types:
# - Patient
# - Observation
##################################################
# Allowed Bundle Types for persistence (defaults are: COLLECTION,DOCUMENT,MESSAGE)
##################################################
# allowed_bundle_types: COLLECTION,DOCUMENT,MESSAGE,TRANSACTION,TRANSACTIONRESPONSE,BATCH,BATCHRESPONSE,HISTORY,SEARCHSET
# allow_cascading_deletes: true
# allow_contains_searches: true
# allow_external_references: true
# allow_multiple_delete: true
# allow_override_default_search_params: true
# auto_create_placeholder_reference_targets: false
### tells the server to automatically append the current version of the target resource to references at these paths
# auto_version_reference_at_paths: Device.patient, Device.location, Device.parent, DeviceMetric.parent, DeviceMetric.source, Observation.device, Observation.subject
# ips_enabled: false
# default_encoding: JSON
# default_pretty_print: true
# default_page_size: 20
# delete_expunge_enabled: true
# enable_repository_validating_interceptor: true
# enable_index_missing_fields: false
# 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!!
### more information here: https://hapifhir.io/hapi-fhir/docs/server_jpa/elastic.html
advanced_lucene_indexing: false
bulk_export_enabled: false
bulk_import_enabled: false
# language_search_parameter_enabled: true
# enforce_referential_integrity_on_delete: false
# This is an experimental feature, and does not fully support _total and other FHIR features.
# enforce_referential_integrity_on_delete: false
# enforce_referential_integrity_on_write: false
# etag_support_enabled: true
# expunge_enabled: true
# client_id_strategy: ALPHANUMERIC
# server_id_strategy: SEQUENTIAL_NUMERIC
# fhirpath_interceptor_enabled: false
# filter_search_enabled: true
# graphql_enabled: true
narrative_enabled: false
mdm_enabled: false
mdm_rules_json_location: "mdm-rules.json"
# local_base_urls:
# - https://hapi.fhir.org/baseR4
logical_urls:
- http://terminology.hl7.org/*
- https://terminology.hl7.org/*
- http://snomed.info/*
- https://snomed.info/*
- http://unitsofmeasure.org/*
- https://unitsofmeasure.org/*
- http://loinc.org/*
- https://loinc.org/*
# partitioning:
# allow_references_across_partitions: false
# partitioning_include_in_search_hashes: false
cors:
allow_Credentials: true
# These are allowed_origin patterns, see: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/cors/CorsConfiguration.html#setAllowedOriginPatterns-java.util.List-
allowed_origin:
- '*'
# Search coordinator thread pool sizes
search-coord-core-pool-size: 20
search-coord-max-pool-size: 100
search-coord-queue-capacity: 200
# comma-separated package names, will be @ComponentScan'ed by Spring to allow for creating custom Spring beans
#custom-bean-packages:
# 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
#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
#custom-provider-classes:
# Threadpool size for BATCH'ed GETs in a bundle.
# bundle_batch_pool_size: 10
# bundle_batch_pool_max_size: 50
# logger:
# error_format: 'ERROR - ${requestVerb} ${requestUrl}'
# format: >-
# Path[${servletPath}] Source[${requestHeader.x-forwarded-for}]
# Operation[${operationType} ${operationName} ${idOrResourceName}]
# UA[${requestHeader.user-agent}] Params[${requestParameters}]
# ResponseEncoding[${responseEncodingNoDefault}]
# log_exceptions: true
# name: fhirtest.access
# max_binary_size: 104857600
# max_page_size: 200
# retain_cached_searches_mins: 60
# reuse_cached_search_results_millis: 60000
tester:
home:
name: Local Tester
server_address: 'http://localhost:8080/fhir'
refuse_to_fetch_third_party_urls: false
fhir_version: R4
global:
name: Global Tester
server_address: "http://hapi.fhir.org/baseR4"
refuse_to_fetch_third_party_urls: false
fhir_version: R4
# validation:
# requests_enabled: true
# responses_enabled: true
# binary_storage_enabled: true
inline_resource_storage_below_size: 4000
# bulk_export_enabled: true
# subscription:
# resthook_enabled: true
# websocket_enabled: false
# email:
# from: some@test.com
# host: google.com
# port:
# username:
# password:
# auth:
# startTlsEnable:
# startTlsRequired:
# quitWait:
# lastn_enabled: true
# store_resource_in_lucene_index_enabled: true
### This is configuration for normalized quantity search level default is 0
### 0: NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED - default
### 1: NORMALIZED_QUANTITY_STORAGE_SUPPORTED
### 2: NORMALIZED_QUANTITY_SEARCH_SUPPORTED
# normalized_quantity_search_level: 2
#elasticsearch:
# debug:
# pretty_print_json_log: false
# refresh_after_write: false
# enabled: false
# password: SomePassword
# required_index_status: YELLOW
# rest_url: 'localhost:9200'
# protocol: 'http'
# schema_management_strategy: CREATE
# username: SomeUsername

View File

@@ -1,23 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="30 seconds">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%file:%line] %msg%n</pattern>
</encoder>
</appender>
<logger name="org.springframework.beans" level="INFO">
<appender-ref ref="STDOUT" />
</logger>
<logger name="org.springframework.core" level="INFO">
<appender-ref ref="STDOUT" />
</logger>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml" />
<root level="INFO">
<appender-ref ref="STDOUT" />
<appender-ref ref="CONSOLE" />
</root>
<logger name = "org.opencds.cqf.cql.engine.debug" level="DEBUG"/>
</configuration>

View File

@@ -0,0 +1,242 @@
package ca.uhn.fhir.jpa.starter;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.cr.config.RepositoryConfig;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.searchparam.config.NicknameServiceConfig;
import ca.uhn.fhir.jpa.starter.cdshooks.StarterCdsHooksConfig;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
import ca.uhn.hapi.fhir.cdshooks.api.ICdsServiceRegistry;
import ca.uhn.hapi.fhir.cdshooks.config.CdsHooksConfig;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
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 java.io.IOException;
import java.util.concurrent.TimeUnit;
import static org.awaitility.Awaitility.await;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.fail;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
classes = {
Application.class,
NicknameServiceConfig.class,
RepositoryConfig.class,
CdsHooksConfig.class,
StarterCdsHooksConfig.class
}, properties = {
"spring.profiles.include=storageSettingsTest",
"spring.datasource.url=jdbc:h2:mem:dbr4",
"hapi.fhir.enable_repository_validating_interceptor=true",
"hapi.fhir.fhir_version=r4",
"hapi.fhir.cr.enabled=true",
"hapi.fhir.cr.caregaps.section_author=Organization/alphora-author",
"hapi.fhir.cr.caregaps.reporter=Organization/alphora",
"hapi.fhir.cdshooks.enabled=true",
"spring.main.allow-bean-definition-overriding=true"})
class CdsHooksServletIT implements IServerSupport {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CdsHooksServletIT.class);
private final FhirContext ourCtx = FhirContext.forR4Cached();
private final IParser ourParser = ourCtx.newJsonParser();
private IGenericClient ourClient;
private String ourCdsBase;
@Autowired
DaoRegistry myDaoRegistry;
@Autowired
ICdsServiceRegistry myCdsServiceRegistry;
@LocalServerPort
private int port;
private String ourServerBase;
@BeforeEach
void beforeEach() {
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
ourCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000);
ourServerBase = "http://localhost:" + port + "/fhir/";
ourClient = ourCtx.newRestfulGenericClient(ourServerBase);
ourCdsBase = "http://localhost:" + port + "/cds-services";
var cdsServicesJson = myCdsServiceRegistry.getCdsServicesJson();
if (cdsServicesJson != null && cdsServicesJson.getServices() != null && !cdsServicesJson.getServices().isEmpty()) {
var services = cdsServicesJson.getServices();
for (int i = 0; i < services.size(); i++) {
myCdsServiceRegistry.unregisterService(services.get(i).getId(), "CR");
}
}
}
private Boolean hasCdsServices() throws IOException {
var response = callCdsServicesDiscovery();
return response.getEntity().getContentLength() > 21 || response.getEntity().isChunked();
}
private CloseableHttpResponse callCdsServicesDiscovery() {
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
HttpGet request = new HttpGet(ourCdsBase);
request.addHeader("Content-Type", "application/json");
return httpClient.execute(request);
} catch (IOException ioe) {
fail(ioe.getMessage());
return null;
}
}
@Test
void testGetCdsServices() {
var response = callCdsServicesDiscovery();
assertEquals(200, response.getStatusLine().getStatusCode());
}
@Test
void testCdsHooks() throws IOException, InterruptedException {
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" +
"}";
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
HttpPost request = new HttpPost(ourCdsBase + "/hello-world");
request.setEntity(new StringEntity(cdsRequest));
request.addHeader("Content-Type", "application/json");
CloseableHttpResponse httpResponse = httpClient.execute(request);
String result = EntityUtils.toString(httpResponse.getEntity());
Gson gsonResponse = new Gson();
JsonObject response = gsonResponse.fromJson(result, JsonObject.class);
assertNotNull(response);
JsonArray cards = response.getAsJsonArray("cards");
assertEquals(1, cards.size());
assertEquals("\"Hello World!\"", cards.get(0).getAsJsonObject().get("summary").toString());
} catch (IOException ioe) {
fail(ioe.getMessage());
}
}
@Test
void testRec10() throws IOException {
loadBundle("r4/opioidcds-10-order-sign-bundle.json", ourCtx, ourClient);
await().atMost(20000, TimeUnit.MILLISECONDS).until(() -> hasCdsServices());
var fhirServer = " \"fhirServer\": " + "\"" + ourServerBase + "\"" + ",\n";
var cdsRequest = "{\n" +
" \"hookInstance\": \"055b009c-4a7d-4db4-a35e-0e5198918ed1\",\n" +
" \"hook\": \"order-sign\",\n" +
fhirServer +
" \"context\": {\n" +
" \"patientId\": \"example-rec-10-order-sign-illicit-POS-Cocaine-drugs\",\n" +
" \"userId\": \"COREPRACTITIONER1\",\n" +
" \"draftOrders\": {\n" +
" \"resourceType\": \"Bundle\",\n" +
" \"entry\": [\n" +
" {\n" +
" \"resource\": {\n" +
" \"resourceType\": \"MedicationRequest\",\n" +
" \"id\": \"request-123\",\n" +
" \"status\": \"draft\",\n" +
" \"subject\": {\n" +
" \"reference\": \"Patient/example-rec-10-order-sign-illicit-POS-Cocaine-drugs\"\n" +
" },\n" +
" \"authoredOn\": \"2024-03-27\",\n" +
" \"dosageInstruction\": [\n" +
" {\n" +
" \"timing\": {\n" +
" \"repeat\": {\n" +
" \"frequency\": 1,\n" +
" \"period\": 1,\n" +
" \"periodUnit\": \"d\"\n" +
" }\n" +
" },\n" +
" \"doseAndRate\": [\n" +
" {\n" +
" \"doseQuantity\": {\n" +
" \"value\": 1,\n" +
" \"system\": \"http://unitsofmeasure.org\",\n" +
" \"code\": \"{pill}\"\n" +
" }\n" +
" }\n" +
" ]\n" +
" }\n" +
" ],\n" +
" \"dispenseRequest\": {\n" +
" \"expectedSupplyDuration\": {\n" +
" \"value\": 90,\n" +
" \"unit\": \"days\",\n" +
" \"system\": \"http://unitsofmeasure.org\",\n" +
" \"code\": \"d\"\n" +
" }\n" +
" },\n" +
" \"intent\": \"order\",\n" +
" \"category\": {\n" +
" \"coding\": [\n" +
" {\n" +
" \"system\": \"http://terminology.hl7.org/CodeSystem/medicationrequest-category\",\n" +
" \"code\": \"community\"\n" +
" }\n" +
" ]\n" +
" },\n" +
" \"medicationCodeableConcept\": {\n" +
" \"coding\": [\n" +
" {\n" +
" \"system\": \"http://www.nlm.nih.gov/research/umls/rxnorm\",\n" +
" \"code\": \"1049502\",\n" +
" \"display\": \"12 HR oxycodone hydrochloride 10 MG Extended Release Oral Tablet\"\n" +
" }\n" +
" ]\n" +
" }\n" +
" }\n" +
" }\n" +
" ]\n" +
" }\n" +
" }\n" +
"}";
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
HttpPost request = new HttpPost(ourCdsBase + "/opioidcds-10-order-sign");
request.setEntity(new StringEntity(cdsRequest));
request.addHeader("Content-Type", "application/json");
CloseableHttpResponse httpResponse = httpClient.execute(request);
String result = EntityUtils.toString(httpResponse.getEntity());
Gson gsonResponse = new Gson();
JsonObject response = gsonResponse.fromJson(result, JsonObject.class);
assertNotNull(response);
JsonArray cards = response.getAsJsonArray("cards");
assertEquals(0, cards.size());
// assertEquals("\"Hello World!\"", cards.get(0).getAsJsonObject().get("summary").toString());
} catch (IOException ioe) {
fail(ioe.getMessage());
}
}
}

View File

@@ -151,13 +151,6 @@ class ExampleServerDstu3IT implements IServerSupport {
return count;
}
private Bundle loadBundle(String theLocation, FhirContext theCtx, IGenericClient theClient) throws IOException {
String json = stringFromResource(theLocation);
Bundle bundle = (Bundle) theCtx.newJsonParser().parseResource(json);
Bundle result = theClient.transaction().withBundle(bundle).execute();
return result;
}
@Test
void testWebsocketSubscription() throws Exception {
/*

View File

@@ -61,8 +61,8 @@ import static org.opencds.cqf.fhir.utility.r4.Parameters.stringPart;
"hapi.fhir.subscription.websocket_enabled=true",
//"hapi.fhir.mdm_enabled=true",
"hapi.fhir.cr.enabled=true",
"hapi.fhir.cr.caregaps_section_author=Organization/alphora-author",
"hapi.fhir.cr.caregaps_reporter=Organization/alphora",
"hapi.fhir.cr.caregaps.section_author=Organization/alphora-author",
"hapi.fhir.cr.caregaps.reporter=Organization/alphora",
"hapi.fhir.implementationguides.dk-core.name=hl7.fhir.dk.core",
"hapi.fhir.implementationguides.dk-core.version=1.1.0",
"hapi.fhir.auto_create_placeholder_reference_targets=true",
@@ -130,13 +130,6 @@ class ExampleServerR4IT implements IServerSupport {
assertEquals(measureUrl + "|0.0.003", report.getMeasure());
}
private org.hl7.fhir.r4.model.Bundle loadBundle(String theLocation, FhirContext theCtx, IGenericClient theClient) throws IOException {
String json = stringFromResource(theLocation);
org.hl7.fhir.r4.model.Bundle bundle = (org.hl7.fhir.r4.model.Bundle) theCtx.newJsonParser().parseResource(json);
org.hl7.fhir.r4.model.Bundle result = theClient.transaction().withBundle(bundle).execute();
return result;
}
public Parameters runCqlExecution(Parameters parameters) {
var results = ourClient.operation().onServer()
@@ -274,8 +267,8 @@ class ExampleServerR4IT implements IServerSupport {
@Test
void testCareGaps() throws IOException {
var reporter = crProperties.getCareGapsReporter();
var author = crProperties.getCareGapsSectionAuthor();
var reporter = crProperties.getCareGaps().getReporter();
var author = crProperties.getCareGaps().getSection_author();
assertTrue(reporter.equals("Organization/alphora"));
assertTrue(author.equals("Organization/alphora-author"));

View File

@@ -3,8 +3,10 @@ package ca.uhn.fhir.jpa.starter;
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;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
@@ -28,6 +30,12 @@ public interface IServerSupport {
}
}
default IBaseBundle loadBundle(String theLocation, FhirContext theFhirContext, IGenericClient theClient) throws IOException {
String json = stringFromResource(theLocation);
IBaseBundle bundle = (IBaseBundle) theFhirContext.newJsonParser().parseResource(json);
return theClient.transaction().withBundle(bundle).execute();
}
default String stringFromResource(String theLocation) throws IOException {
InputStream is = null;
if (theLocation.startsWith(File.separator)) {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long