From 08aabd2b3ea56120be347df51a63d5a2221375a2 Mon Sep 17 00:00:00 2001 From: Luke deGruchy Date: Tue, 10 Mar 2026 17:18:12 -0400 Subject: [PATCH] Introduce RepositoryProxyFactory to decouple processors from Repositories.proxy() Processors (ActivityDefinition, PlanDefinition, Library, CQL, Questionnaire, QuestionnaireResponse, GraphDefinition, Measure/CareGaps) previously called Repositories.proxy() and createRestRepository() directly, coupling them to the static helper and making proxy behaviour untestable in isolation. This change: - Adds a RepositoryProxyFactory interface in cqf-fhir-utility with a DefaultRepositoryProxyFactory that delegates to Repositories.proxy() - Injects RepositoryProxyFactory into all processors, replacing direct static calls to Repositories.proxy() and createRestRepository() - Removes telescope constructors from processors in favour of a single constructor that requires the factory - Adds withRepository() to R4MeasureProcessor and R4MeasureServiceUtils so R4MultiMeasureService can unconditionally call proxy() and avoid duplicated null-checking logic - Introduces NoOpRepositoryProxyFactory for tests that don't exercise proxy routing, replacing DefaultRepositoryProxyFactory in all test files except R4MultiMeasureServiceProxyTest - Adds R4MultiMeasureServiceProxyTest and TestRepositoryProxyFactory to verify proxy routing with custom endpoint repositories - Wires the factory through HAPI Spring configs (CrBaseConfig, CrProcessorConfig, CrR4Config) Co-Authored-By: Claude Opus 4.6 --- .../ActivityDefinitionProcessorFactory.java | 4 +- .../benchmark/NoOpRepositoryProxyFactory.java | 21 ++ .../fhir/benchmark/measure/r4/Measure.java | 8 +- .../plandefinition/TestPlanDefinition.java | 34 ++- .../questionnaire/TestQuestionnaire.java | 3 +- .../cqf/fhir/cr/hapi/config/CrBaseConfig.java | 7 + .../cr/hapi/config/CrProcessorConfig.java | 49 +++- .../fhir/cr/hapi/config/r4/CrR4Config.java | 22 +- .../ActivityDefinitionProcessor.java | 55 +--- .../opencds/cqf/fhir/cr/cql/CqlProcessor.java | 47 +-- .../apply/ApplyRequestBuilder.java | 50 ++-- .../cqf/fhir/cr/library/LibraryProcessor.java | 45 +-- .../measure/r4/R4CareGapsBundleBuilder.java | 8 +- .../cr/measure/r4/R4CareGapsProcessor.java | 7 +- .../fhir/cr/measure/r4/R4CareGapsService.java | 11 +- .../cr/measure/r4/R4MeasureProcessor.java | 7 + .../cr/measure/r4/R4MultiMeasureService.java | 24 +- .../r4/utils/R4MeasureServiceUtils.java | 7 + .../PlanDefinitionProcessor.java | 102 +------ .../plandefinition/apply/ApplyProcessor.java | 9 +- .../questionnaire/QuestionnaireProcessor.java | 61 +--- .../QuestionnaireResponseProcessor.java | 19 +- .../ActivityDefinitionProcessorFactory.java | 4 +- .../ActivityDefinitionProcessorTests.java | 5 +- .../cqf/fhir/cr/cql/CqlProcessorTests.java | 6 +- .../org/opencds/cqf/fhir/cr/cql/TestCql.java | 21 +- .../graphdefinition/TestGraphDefinition.java | 19 +- .../apply/ApplyProcessorTest.java | 7 +- .../apply/ApplyRequestBuilderTest.java | 12 +- .../cr/library/LibraryProcessorTests.java | 7 +- .../cqf/fhir/cr/library/TestLibrary.java | 27 +- .../cqf/fhir/cr/measure/r4/CareGaps.java | 7 +- .../cqf/fhir/cr/measure/r4/Measure.java | 14 +- .../cqf/fhir/cr/measure/r4/MultiMeasure.java | 14 +- .../r4/NoOpRepositoryProxyFactory.java | 21 ++ .../r4/R4MultiMeasureServiceProxyTest.java | 273 ++++++++++++++++++ .../r4/TestRepositoryProxyFactory.java | 41 +++ .../PlanDefinitionProcessorTests.java | 13 +- .../cr/plandefinition/TestPlanDefinition.java | 35 ++- .../cr/questionnaire/TestItemGenerator.java | 5 +- .../cr/questionnaire/TestQuestionnaire.java | 4 +- .../TestQuestionnaireResponse.java | 4 +- .../DefaultRepositoryProxyFactory.java | 20 ++ .../repository/RepositoryProxyFactory.java | 24 ++ 44 files changed, 758 insertions(+), 425 deletions(-) create mode 100644 cqf-fhir-benchmark/src/test/java/org/opencds/cqf/fhir/benchmark/NoOpRepositoryProxyFactory.java create mode 100644 cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/NoOpRepositoryProxyFactory.java create mode 100644 cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/R4MultiMeasureServiceProxyTest.java create mode 100644 cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/TestRepositoryProxyFactory.java create mode 100644 cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/repository/DefaultRepositoryProxyFactory.java create mode 100644 cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/repository/RepositoryProxyFactory.java diff --git a/cqf-fhir-benchmark/src/test/java/org/opencds/cqf/fhir/benchmark/ActivityDefinitionProcessorFactory.java b/cqf-fhir-benchmark/src/test/java/org/opencds/cqf/fhir/benchmark/ActivityDefinitionProcessorFactory.java index 74208193e9..48612b829e 100644 --- a/cqf-fhir-benchmark/src/test/java/org/opencds/cqf/fhir/benchmark/ActivityDefinitionProcessorFactory.java +++ b/cqf-fhir-benchmark/src/test/java/org/opencds/cqf/fhir/benchmark/ActivityDefinitionProcessorFactory.java @@ -1,6 +1,7 @@ package org.opencds.cqf.fhir.benchmark; import ca.uhn.fhir.repository.IRepository; +import org.opencds.cqf.fhir.cr.CrSettings; import org.opencds.cqf.fhir.cr.activitydefinition.ActivityDefinitionProcessor; import org.opencds.cqf.fhir.utility.repository.operations.IActivityDefinitionProcessor; import org.opencds.cqf.fhir.utility.repository.operations.IActivityDefinitionProcessorFactory; @@ -9,6 +10,7 @@ public class ActivityDefinitionProcessorFactory implements IActivityDefinitionPr @Override public IActivityDefinitionProcessor create(IRepository repository) { - return new ActivityDefinitionProcessor(repository); + return new ActivityDefinitionProcessor( + repository, CrSettings.getDefault(), null, null, new NoOpRepositoryProxyFactory()); } } diff --git a/cqf-fhir-benchmark/src/test/java/org/opencds/cqf/fhir/benchmark/NoOpRepositoryProxyFactory.java b/cqf-fhir-benchmark/src/test/java/org/opencds/cqf/fhir/benchmark/NoOpRepositoryProxyFactory.java new file mode 100644 index 0000000000..fda65bdd6e --- /dev/null +++ b/cqf-fhir-benchmark/src/test/java/org/opencds/cqf/fhir/benchmark/NoOpRepositoryProxyFactory.java @@ -0,0 +1,21 @@ +package org.opencds.cqf.fhir.benchmark; + +import ca.uhn.fhir.repository.IRepository; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.opencds.cqf.fhir.utility.repository.RepositoryProxyFactory; + +/** + * A RepositoryProxyFactory that always returns the local Repository for tests only. + */ +public class NoOpRepositoryProxyFactory implements RepositoryProxyFactory { + + @Override + public IRepository proxy( + IRepository localRepository, + Boolean useServerData, + IBaseResource dataEndpoint, + IBaseResource contentEndpoint, + IBaseResource terminologyEndpoint) { + return localRepository; + } +} diff --git a/cqf-fhir-benchmark/src/test/java/org/opencds/cqf/fhir/benchmark/measure/r4/Measure.java b/cqf-fhir-benchmark/src/test/java/org/opencds/cqf/fhir/benchmark/measure/r4/Measure.java index 0d0ebeb11c..f6cbfc97d1 100644 --- a/cqf-fhir-benchmark/src/test/java/org/opencds/cqf/fhir/benchmark/measure/r4/Measure.java +++ b/cqf-fhir-benchmark/src/test/java/org/opencds/cqf/fhir/benchmark/measure/r4/Measure.java @@ -10,6 +10,7 @@ import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.MeasureReport; +import org.opencds.cqf.fhir.benchmark.NoOpRepositoryProxyFactory; import org.opencds.cqf.fhir.cql.engine.retrieve.RetrieveSettings.SEARCH_FILTER_MODE; import org.opencds.cqf.fhir.cql.engine.retrieve.RetrieveSettings.TERMINOLOGY_FILTER_MODE; import org.opencds.cqf.fhir.cql.engine.terminology.TerminologySettings.VALUESET_EXPANSION_MODE; @@ -85,7 +86,12 @@ public Given evaluationOptions(MeasureEvaluationOptions evaluationOptions) { } private R4MultiMeasureService buildMultiMeasureService() { - return new R4MultiMeasureService(repository, evaluationOptions, serverBase, measurePeriodValidator); + return new R4MultiMeasureService( + repository, + evaluationOptions, + serverBase, + measurePeriodValidator, + new NoOpRepositoryProxyFactory()); } public When when() { diff --git a/cqf-fhir-benchmark/src/test/java/org/opencds/cqf/fhir/benchmark/plandefinition/TestPlanDefinition.java b/cqf-fhir-benchmark/src/test/java/org/opencds/cqf/fhir/benchmark/plandefinition/TestPlanDefinition.java index 974d047872..af031692f7 100644 --- a/cqf-fhir-benchmark/src/test/java/org/opencds/cqf/fhir/benchmark/plandefinition/TestPlanDefinition.java +++ b/cqf-fhir-benchmark/src/test/java/org/opencds/cqf/fhir/benchmark/plandefinition/TestPlanDefinition.java @@ -38,10 +38,12 @@ import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.json.JSONException; import org.opencds.cqf.cql.engine.model.ModelResolver; +import org.opencds.cqf.fhir.benchmark.NoOpRepositoryProxyFactory; import org.opencds.cqf.fhir.benchmark.TestOperationProvider; import org.opencds.cqf.fhir.benchmark.helpers.DataRequirementsLibrary; import org.opencds.cqf.fhir.benchmark.helpers.GeneratedPackage; import org.opencds.cqf.fhir.cql.EvaluationSettings; +import org.opencds.cqf.fhir.cql.LibraryEngine; import org.opencds.cqf.fhir.cql.engine.retrieve.RetrieveSettings.SEARCH_FILTER_MODE; import org.opencds.cqf.fhir.cql.engine.retrieve.RetrieveSettings.TERMINOLOGY_FILTER_MODE; import org.opencds.cqf.fhir.cql.engine.terminology.TerminologySettings.VALUESET_EXPANSION_MODE; @@ -53,6 +55,7 @@ import org.opencds.cqf.fhir.utility.model.FhirModelResolverCache; import org.opencds.cqf.fhir.utility.monad.Eithers; import org.opencds.cqf.fhir.utility.repository.InMemoryFhirRepository; +import org.opencds.cqf.fhir.utility.repository.Repositories; import org.opencds.cqf.fhir.utility.repository.ig.IgRepository; import org.skyscreamer.jsonassert.JSONAssert; @@ -138,16 +141,17 @@ public PlanDefinitionProcessor buildProcessor(IRepository repository) { .setValuesetExpansionMode(VALUESET_EXPANSION_MODE.PERFORM_NAIVE_EXPANSION); } var crSettings = CrSettings.getDefault().withEvaluationSettings(evaluationSettings); - return new PlanDefinitionProcessor(repository, crSettings); + return new PlanDefinitionProcessor(repository, crSettings, null, null, new NoOpRepositoryProxyFactory()); } public When when() { - return new When(repository, buildProcessor(repository)); + return new When(this, repository); } } @SuppressWarnings("UnstableApiUsage") public static class When { + private final Given given; private final IRepository repository; private final PlanDefinitionProcessor processor; private final IParser jsonParser; @@ -168,9 +172,10 @@ public static class When { private IBaseParameters parameters; private boolean isPackagePut; - public When(IRepository repository, PlanDefinitionProcessor processor) { + public When(Given given, IRepository repository) { + this.given = given; this.repository = repository; - this.processor = processor; + this.processor = given.buildProcessor(repository); jsonParser = repository.fhirContext().newJsonParser(); } @@ -272,7 +277,10 @@ public IBaseBundle applyR5() { if (additionalDataId != null) { loadAdditionalData(readRepository(repository, additionalDataId)); } - var param = (IParametersAdapter) IAdapterFactory.createAdapterForResource(processor.applyR5( + var proxiedRepo = Repositories.proxy( + repository, useServerData, dataRepository, contentRepository, terminologyRepository); + var applyProcessor = given.buildProcessor(proxiedRepo); + var param = (IParametersAdapter) IAdapterFactory.createAdapterForResource(applyProcessor.applyR5( Eithers.forMiddle3(Ids.newId(repository.fhirContext(), PLAN_DEFINITION, planDefinitionId)), List.of(subjectId), encounterId, @@ -284,12 +292,9 @@ public IBaseBundle applyR5() { null, null, parameters, - useServerData, additionalData, prefetchData, - dataRepository, - contentRepository, - terminologyRepository)); + new LibraryEngine(proxiedRepo, applyProcessor.settings().getEvaluationSettings()))); return (IBaseBundle) param.getParameter().get(0).getResource(); } @@ -301,9 +306,12 @@ public GeneratedCarePlan thenApply() { if (additionalDataId != null) { loadAdditionalData(readRepository(repository, additionalDataId)); } + var proxiedRepo = Repositories.proxy( + repository, useServerData, dataRepository, contentRepository, terminologyRepository); + var applyProcessor = given.buildProcessor(proxiedRepo); return new GeneratedCarePlan( repository, - processor.apply( + applyProcessor.apply( Eithers.forMiddle3(Ids.newId(repository.fhirContext(), PLAN_DEFINITION, planDefinitionId)), subjectId, encounterId, @@ -315,12 +323,10 @@ public GeneratedCarePlan thenApply() { null, null, parameters, - useServerData, additionalData, prefetchData, - dataRepository, - contentRepository, - terminologyRepository)); + new LibraryEngine( + proxiedRepo, applyProcessor.settings().getEvaluationSettings()))); } public GeneratedPackage thenPackage() { diff --git a/cqf-fhir-benchmark/src/test/java/org/opencds/cqf/fhir/benchmark/questionnaire/TestQuestionnaire.java b/cqf-fhir-benchmark/src/test/java/org/opencds/cqf/fhir/benchmark/questionnaire/TestQuestionnaire.java index adb5fc4321..56236a1c84 100644 --- a/cqf-fhir-benchmark/src/test/java/org/opencds/cqf/fhir/benchmark/questionnaire/TestQuestionnaire.java +++ b/cqf-fhir-benchmark/src/test/java/org/opencds/cqf/fhir/benchmark/questionnaire/TestQuestionnaire.java @@ -13,6 +13,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; +import org.opencds.cqf.fhir.benchmark.NoOpRepositoryProxyFactory; import org.opencds.cqf.fhir.cql.EvaluationSettings; import org.opencds.cqf.fhir.cql.engine.retrieve.RetrieveSettings.SEARCH_FILTER_MODE; import org.opencds.cqf.fhir.cql.engine.retrieve.RetrieveSettings.TERMINOLOGY_FILTER_MODE; @@ -53,7 +54,7 @@ public QuestionnaireProcessor buildProcessor(IRepository repository) { .setValuesetExpansionMode(VALUESET_EXPANSION_MODE.PERFORM_NAIVE_EXPANSION); } var crSettings = CrSettings.getDefault().withEvaluationSettings(evaluationSettings); - return new QuestionnaireProcessor(repository, crSettings, null); + return new QuestionnaireProcessor(repository, crSettings, null, new NoOpRepositoryProxyFactory()); } public When when() { diff --git a/cqf-fhir-cr-hapi/src/main/java/org/opencds/cqf/fhir/cr/hapi/config/CrBaseConfig.java b/cqf-fhir-cr-hapi/src/main/java/org/opencds/cqf/fhir/cr/hapi/config/CrBaseConfig.java index 5baf379761..7ec5c04b9d 100644 --- a/cqf-fhir-cr-hapi/src/main/java/org/opencds/cqf/fhir/cr/hapi/config/CrBaseConfig.java +++ b/cqf-fhir-cr-hapi/src/main/java/org/opencds/cqf/fhir/cr/hapi/config/CrBaseConfig.java @@ -6,11 +6,18 @@ import org.opencds.cqf.fhir.cr.hapi.common.StringTimePeriodHandler; import org.opencds.cqf.fhir.cr.measure.common.MeasurePeriodValidator; import org.opencds.cqf.fhir.utility.client.TerminologyServerClientSettings; +import org.opencds.cqf.fhir.utility.repository.DefaultRepositoryProxyFactory; +import org.opencds.cqf.fhir.utility.repository.RepositoryProxyFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class CrBaseConfig { + @Bean + RepositoryProxyFactory repositoryProxyFactory() { + return new DefaultRepositoryProxyFactory(); + } + @Bean CrSettings settings( EvaluationSettings evaluationSettings, TerminologyServerClientSettings terminologyServerClientSettings) { diff --git a/cqf-fhir-cr-hapi/src/main/java/org/opencds/cqf/fhir/cr/hapi/config/CrProcessorConfig.java b/cqf-fhir-cr-hapi/src/main/java/org/opencds/cqf/fhir/cr/hapi/config/CrProcessorConfig.java index ba13fa7a47..249e8d29a7 100644 --- a/cqf-fhir-cr-hapi/src/main/java/org/opencds/cqf/fhir/cr/hapi/config/CrProcessorConfig.java +++ b/cqf-fhir-cr-hapi/src/main/java/org/opencds/cqf/fhir/cr/hapi/config/CrProcessorConfig.java @@ -25,6 +25,7 @@ import org.opencds.cqf.fhir.cr.questionnaire.QuestionnaireProcessor; import org.opencds.cqf.fhir.cr.questionnaireresponse.QuestionnaireResponseProcessor; import org.opencds.cqf.fhir.cr.valueset.ValueSetProcessor; +import org.opencds.cqf.fhir.utility.repository.RepositoryProxyFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -34,14 +35,20 @@ @Import({CrBaseConfig.class}) public class CrProcessorConfig { @Bean - ICqlProcessorFactory cqlProcessorFactory(IRepositoryFactory repositoryFactory, CrSettings crSettings) { - return rd -> new CqlProcessor(repositoryFactory.create(rd), crSettings); + ICqlProcessorFactory cqlProcessorFactory( + IRepositoryFactory repositoryFactory, + CrSettings crSettings, + RepositoryProxyFactory repositoryProxyFactory) { + return rd -> new CqlProcessor(repositoryFactory.create(rd), crSettings, null, repositoryProxyFactory); } @Bean IActivityDefinitionProcessorFactory activityDefinitionProcessorFactory( - IRepositoryFactory repositoryFactory, CrSettings crSettings) { - return rd -> new ActivityDefinitionProcessor(repositoryFactory.create(rd), crSettings); + IRepositoryFactory repositoryFactory, + CrSettings crSettings, + RepositoryProxyFactory repositoryProxyFactory) { + return rd -> new ActivityDefinitionProcessor( + repositoryFactory.create(rd), crSettings, null, null, repositoryProxyFactory); } @Bean @@ -52,27 +59,39 @@ IImplementationGuideProcessorFactory implementationGuideProcessorFactory( @Bean IPlanDefinitionProcessorFactory planDefinitionProcessorFactory( - IRepositoryFactory repositoryFactory, CrSettings crSettings) { - return rd -> new PlanDefinitionProcessor(repositoryFactory.create(rd), crSettings); + IRepositoryFactory repositoryFactory, + CrSettings crSettings, + RepositoryProxyFactory repositoryProxyFactory) { + return rd -> new PlanDefinitionProcessor( + repositoryFactory.create(rd), crSettings, null, null, repositoryProxyFactory); } @Bean IQuestionnaireProcessorFactory questionnaireProcessorFactory( - IRepositoryFactory repositoryFactory, CrSettings crSettings) { - return rd -> new QuestionnaireProcessor(repositoryFactory.create(rd), crSettings); + IRepositoryFactory repositoryFactory, + CrSettings crSettings, + RepositoryProxyFactory repositoryProxyFactory) { + return rd -> new QuestionnaireProcessor(repositoryFactory.create(rd), crSettings, null, repositoryProxyFactory); } @Bean IQuestionnaireResponseProcessorFactory questionnaireResponseProcessorFactory( - IRepositoryFactory repositoryFactory, CrSettings crSettings) { - return rd -> new QuestionnaireResponseProcessor(repositoryFactory.create(rd), crSettings); + IRepositoryFactory repositoryFactory, + CrSettings crSettings, + RepositoryProxyFactory repositoryProxyFactory) { + return rd -> new QuestionnaireResponseProcessor( + repositoryFactory.create(rd), crSettings, null, repositoryProxyFactory); } @Bean - ILibraryProcessorFactory libraryProcessorFactory(IRepositoryFactory repositoryFactory, CrSettings crSettings) { + ILibraryProcessorFactory libraryProcessorFactory( + IRepositoryFactory repositoryFactory, + CrSettings crSettings, + RepositoryProxyFactory repositoryProxyFactory) { return rd -> { var repository = repositoryFactory.create(rd); - return new LibraryProcessor(repository, crSettings, List.of(new HapiArtifactDiffProcessor(repository))); + return new LibraryProcessor( + repository, crSettings, List.of(new HapiArtifactDiffProcessor(repository)), repositoryProxyFactory); }; } @@ -89,8 +108,10 @@ IGraphDefinitionProcessorFactory graphDefinitionProcessorFactory( @Bean IGraphDefinitionApplyRequestBuilderFactory graphDefinitionApplyRequestBuilderFactory( - IRepositoryFactory repositoryFactory, EvaluationSettings evaluationSettings) { + IRepositoryFactory repositoryFactory, + EvaluationSettings evaluationSettings, + RepositoryProxyFactory repositoryProxyFactory) { - return rd -> new ApplyRequestBuilder(repositoryFactory.create(rd), evaluationSettings); + return rd -> new ApplyRequestBuilder(repositoryFactory.create(rd), evaluationSettings, repositoryProxyFactory); } } diff --git a/cqf-fhir-cr-hapi/src/main/java/org/opencds/cqf/fhir/cr/hapi/config/r4/CrR4Config.java b/cqf-fhir-cr-hapi/src/main/java/org/opencds/cqf/fhir/cr/hapi/config/r4/CrR4Config.java index 0695e47b6f..a4db9d2dde 100644 --- a/cqf-fhir-cr-hapi/src/main/java/org/opencds/cqf/fhir/cr/hapi/config/r4/CrR4Config.java +++ b/cqf-fhir-cr-hapi/src/main/java/org/opencds/cqf/fhir/cr/hapi/config/r4/CrR4Config.java @@ -49,6 +49,7 @@ import org.opencds.cqf.fhir.cr.measure.r4.R4MultiMeasureService; import org.opencds.cqf.fhir.cr.measure.r4.R4SubmitDataService; import org.opencds.cqf.fhir.cr.measure.r4.utils.R4MeasureServiceUtils; +import org.opencds.cqf.fhir.utility.repository.RepositoryProxyFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -73,22 +74,29 @@ public class CrR4Config { R4MeasureEvaluatorSingleFactory r4MeasureServiceFactory( IRepositoryFactory repositoryFactory, MeasureEvaluationOptions evaluationOptions, - MeasurePeriodValidator measurePeriodValidator) { + MeasurePeriodValidator measurePeriodValidator, + RepositoryProxyFactory repositoryProxyFactory) { // We are effectively returning an R4MeasureEvaluatorSingle her return requestDetails -> new R4MultiMeasureService( repositoryFactory.create(requestDetails), evaluationOptions, requestDetails.getFhirServerBase(), - measurePeriodValidator); + measurePeriodValidator, + repositoryProxyFactory); } @Bean R4MeasureEvaluatorMultipleFactory r4MeasureEvaluatorMultipleFactory( IRepositoryFactory repositoryFactory, MeasureEvaluationOptions evaluationOptions, - MeasurePeriodValidator measurePeriodValidator) { + MeasurePeriodValidator measurePeriodValidator, + RepositoryProxyFactory repositoryProxyFactory) { return rd -> new R4MultiMeasureService( - repositoryFactory.create(rd), evaluationOptions, rd.getFhirServerBase(), measurePeriodValidator); + repositoryFactory.create(rd), + evaluationOptions, + rd.getFhirServerBase(), + measurePeriodValidator, + repositoryProxyFactory); } @Bean @@ -132,13 +140,15 @@ ICareGapsServiceFactory careGapsServiceFactory( IRepositoryFactory repositoryFactory, CareGapsProperties careGapsProperties, MeasureEvaluationOptions measureEvaluationOptions, - MeasurePeriodValidator measurePeriodValidator) { + MeasurePeriodValidator measurePeriodValidator, + RepositoryProxyFactory repositoryProxyFactory) { return rd -> new R4CareGapsService( careGapsProperties, repositoryFactory.create(rd), measureEvaluationOptions, rd.getFhirServerBase(), - measurePeriodValidator); + measurePeriodValidator, + repositoryProxyFactory); } @Bean diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/activitydefinition/ActivityDefinitionProcessor.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/activitydefinition/ActivityDefinitionProcessor.java index ece999bd99..c40d2f94f4 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/activitydefinition/ActivityDefinitionProcessor.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/activitydefinition/ActivityDefinitionProcessor.java @@ -1,8 +1,6 @@ package org.opencds.cqf.fhir.cr.activitydefinition; import static java.util.Objects.requireNonNull; -import static org.opencds.cqf.fhir.utility.repository.Repositories.createRestRepository; -import static org.opencds.cqf.fhir.utility.repository.Repositories.proxy; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; @@ -28,6 +26,7 @@ import org.opencds.cqf.fhir.utility.Ids; import org.opencds.cqf.fhir.utility.model.FhirModelResolverCache; import org.opencds.cqf.fhir.utility.monad.Either3; +import org.opencds.cqf.fhir.utility.repository.RepositoryProxyFactory; import org.opencds.cqf.fhir.utility.repository.operations.IActivityDefinitionProcessor; @SuppressWarnings("UnstableApiUsage") @@ -40,22 +39,17 @@ public class ActivityDefinitionProcessor implements IActivityDefinitionProcessor protected IRepository repository; protected CrSettings crSettings; protected ExtensionResolver extensionResolver; - - public ActivityDefinitionProcessor(IRepository repository) { - this(repository, CrSettings.getDefault()); - } - - public ActivityDefinitionProcessor(IRepository repository, CrSettings crSettings) { - this(repository, crSettings, null, null); - } + protected final RepositoryProxyFactory repositoryProxyFactory; public ActivityDefinitionProcessor( IRepository repository, CrSettings crSettings, IRequestResolverFactory requestResolverFactory, - List operationProcessors) { + List operationProcessors, + RepositoryProxyFactory repositoryProxyFactory) { this.repository = requireNonNull(repository, "repository can not be null"); this.crSettings = requireNonNull(crSettings, "crSettings can not be null"); + this.repositoryProxyFactory = requireNonNull(repositoryProxyFactory, "repositoryProxyFactory can not be null"); this.resourceResolver = new ResourceResolver("ActivityDefinition", this.repository); fhirVersion = repository.fhirContext().getVersion().getVersion(); modelResolver = FhirModelResolverCache.resolverForVersion(fhirVersion); @@ -118,43 +112,8 @@ public , R extends IBaseResource> IBaseResource IBaseResource dataEndpoint, IBaseResource contentEndpoint, IBaseResource terminologyEndpoint) { - return apply( - activityDefinition, - subjectId, - encounterId, - practitionerId, - organizationId, - userType, - userLanguage, - userTaskContext, - setting, - settingContext, - parameters, - useServerData, - data, - createRestRepository(repository.fhirContext(), dataEndpoint), - createRestRepository(repository.fhirContext(), contentEndpoint), - createRestRepository(repository.fhirContext(), terminologyEndpoint)); - } - - public , R extends IBaseResource> IBaseResource apply( - Either3 activityDefinition, - String subjectId, - String encounterId, - String practitionerId, - String organizationId, - IBaseDatatype userType, - IBaseDatatype userLanguage, - IBaseDatatype userTaskContext, - IBaseDatatype setting, - IBaseDatatype settingContext, - IBaseParameters parameters, - boolean useServerData, - IBaseBundle data, - IRepository dataRepository, - IRepository contentRepository, - IRepository terminologyRepository) { - repository = proxy(repository, useServerData, dataRepository, contentRepository, terminologyRepository); + repository = repositoryProxyFactory.proxy( + repository, useServerData, dataEndpoint, contentEndpoint, terminologyEndpoint); return apply( activityDefinition, subjectId, diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/cql/CqlProcessor.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/cql/CqlProcessor.java index f3c0c322e6..90f2681bc7 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/cql/CqlProcessor.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/cql/CqlProcessor.java @@ -1,8 +1,6 @@ package org.opencds.cqf.fhir.cr.cql; import static java.util.Objects.requireNonNull; -import static org.opencds.cqf.fhir.utility.repository.Repositories.createRestRepository; -import static org.opencds.cqf.fhir.utility.repository.Repositories.proxy; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.repository.IRepository; @@ -21,28 +19,26 @@ import org.opencds.cqf.fhir.cr.cql.evaluate.ICqlEvaluationProcessor; import org.opencds.cqf.fhir.utility.Ids; import org.opencds.cqf.fhir.utility.model.FhirModelResolverCache; +import org.opencds.cqf.fhir.utility.repository.RepositoryProxyFactory; @SuppressWarnings("UnstableApiUsage") public class CqlProcessor { protected final ModelResolver modelResolver; protected final FhirVersionEnum fhirVersion; + protected final RepositoryProxyFactory repositoryProxyFactory; protected ICqlEvaluationProcessor cqlEvaluationProcessor; protected IRepository repository; protected CrSettings crSettings; - public CqlProcessor(IRepository repository) { - this(repository, CrSettings.getDefault()); - } - - public CqlProcessor(IRepository repository, CrSettings crSettings) { - this(repository, crSettings, null); - } - public CqlProcessor( - IRepository repository, CrSettings crSettings, List operationProcessors) { + IRepository repository, + CrSettings crSettings, + List operationProcessors, + RepositoryProxyFactory repositoryProxyFactory) { this.repository = requireNonNull(repository, "repository can not be null"); this.crSettings = requireNonNull(crSettings, "crSettings can not be null"); + this.repositoryProxyFactory = requireNonNull(repositoryProxyFactory, "repositoryProxyFactory can not be null"); fhirVersion = this.repository.fhirContext().getVersion().getVersion(); modelResolver = FhirModelResolverCache.resolverForVersion(fhirVersion); if (operationProcessors != null && !operationProcessors.isEmpty()) { @@ -70,33 +66,8 @@ public IBaseParameters evaluate( IBaseResource dataEndpoint, IBaseResource contentEndpoint, IBaseResource terminologyEndpoint) { - return evaluate( - subject, - expression, - parameters, - library, - useServerData, - data, - prefetchData, - content, - createRestRepository(repository.fhirContext(), dataEndpoint), - createRestRepository(repository.fhirContext(), contentEndpoint), - createRestRepository(repository.fhirContext(), terminologyEndpoint)); - } - - public IBaseParameters evaluate( - String subject, - String expression, - IBaseParameters parameters, - List library, - boolean useServerData, - IBaseBundle data, - List prefetchData, - String content, - IRepository dataRepository, - IRepository contentRepository, - IRepository terminologyRepository) { - repository = proxy(repository, useServerData, dataRepository, contentRepository, terminologyRepository); + repository = repositoryProxyFactory.proxy( + repository, useServerData, dataEndpoint, contentEndpoint, terminologyEndpoint); return evaluate( subject, expression, diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/graphdefinition/apply/ApplyRequestBuilder.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/graphdefinition/apply/ApplyRequestBuilder.java index 0d9bdf1a68..5a493de904 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/graphdefinition/apply/ApplyRequestBuilder.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/graphdefinition/apply/ApplyRequestBuilder.java @@ -1,7 +1,6 @@ package org.opencds.cqf.fhir.cr.graphdefinition.apply; -import static org.opencds.cqf.fhir.utility.repository.Repositories.createRestRepository; -import static org.opencds.cqf.fhir.utility.repository.Repositories.proxy; +import static java.util.Objects.requireNonNull; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.repository.IRepository; @@ -26,6 +25,7 @@ import org.opencds.cqf.fhir.utility.model.FhirModelResolverCache; import org.opencds.cqf.fhir.utility.monad.Either3; import org.opencds.cqf.fhir.utility.monad.Eithers; +import org.opencds.cqf.fhir.utility.repository.RepositoryProxyFactory; @SuppressWarnings("UnstableApiUsage") public class ApplyRequestBuilder { @@ -33,6 +33,7 @@ public class ApplyRequestBuilder { private IRepository repository; private final EvaluationSettings evaluationSettings; private final FhirVersionEnum fhirVersion; + private final RepositoryProxyFactory repositoryProxyFactory; private GraphDefinition graphDefinition; private String subject; private String encounter; @@ -46,19 +47,23 @@ public class ApplyRequestBuilder { private boolean useServerData = true; private IBaseBundle data; private List prefetchData; - private IRepository dataRepository; - private IRepository contentRepository; - private IRepository terminologyRepository; + private IBaseResource dataEndpoint; + private IBaseResource contentEndpoint; + private IBaseResource terminologyEndpoint; private Parameters parameters; private IIdType id; private ZonedDateTime periodStartString; private ZonedDateTime periodEndString; private IPrimitiveType canonicalType; - public ApplyRequestBuilder(IRepository repository, EvaluationSettings evaluationSettings) { + public ApplyRequestBuilder( + IRepository repository, + EvaluationSettings evaluationSettings, + RepositoryProxyFactory repositoryProxyFactory) { this.repository = repository; this.fhirVersion = repository.fhirContext().getVersion().getVersion(); this.evaluationSettings = evaluationSettings; + this.repositoryProxyFactory = requireNonNull(repositoryProxyFactory, "repositoryProxyFactory can not be null"); } public ApplyRequestBuilder withGraphDefinitionId(IdType id) { @@ -141,36 +146,23 @@ public ApplyRequestBuilder withPrefetchData(List p return this; } - public ApplyRequestBuilder withDataEndpoint(ParametersParameterComponent dataEndpointParam) { - IBaseResource endpoint = EndpointHelper.getEndpoint(fhirVersion, dataEndpointParam); - this.dataRepository = createRestRepository(repository.fhirContext(), endpoint); + public ApplyRequestBuilder withRepository(IRepository repository) { + this.repository = repository; return this; } - public ApplyRequestBuilder withDataRepository(IRepository dataRepository) { - this.dataRepository = dataRepository; + public ApplyRequestBuilder withDataEndpoint(ParametersParameterComponent dataEndpointParam) { + this.dataEndpoint = EndpointHelper.getEndpoint(fhirVersion, dataEndpointParam); return this; } public ApplyRequestBuilder withContentEndpoint(ParametersParameterComponent contentEndpointParam) { - IBaseResource endpoint = EndpointHelper.getEndpoint(fhirVersion, contentEndpointParam); - this.contentRepository = createRestRepository(repository.fhirContext(), endpoint); - return this; - } - - public ApplyRequestBuilder withContentRepository(IRepository contentRepository) { - this.contentRepository = contentRepository; + this.contentEndpoint = EndpointHelper.getEndpoint(fhirVersion, contentEndpointParam); return this; } public ApplyRequestBuilder withTerminologyEndpoint(ParametersParameterComponent terminologyEndpointParam) { - IBaseResource endpoint = EndpointHelper.getEndpoint(fhirVersion, terminologyEndpointParam); - this.terminologyRepository = createRestRepository(repository.fhirContext(), endpoint); - return this; - } - - public ApplyRequestBuilder withTerminologyRepository(IRepository terminologyRepository) { - this.terminologyRepository = terminologyRepository; + this.terminologyEndpoint = EndpointHelper.getEndpoint(fhirVersion, terminologyEndpointParam); return this; } @@ -193,12 +185,8 @@ public ApplyRequest buildApplyRequest() { throw new IllegalArgumentException("Missing required parameter: 'practitioner'"); } - this.repository = proxy( - this.repository, - this.useServerData, - this.dataRepository, - this.contentRepository, - this.terminologyRepository); + this.repository = repositoryProxyFactory.proxy( + this.repository, this.useServerData, this.dataEndpoint, this.contentEndpoint, this.terminologyEndpoint); Either3, IIdType, IBaseResource> eitherGraphDefinition = Eithers.for3(canonicalType, this.id, this.graphDefinition); diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/library/LibraryProcessor.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/library/LibraryProcessor.java index 2e0c99bdab..a7fbc540db 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/library/LibraryProcessor.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/library/LibraryProcessor.java @@ -2,8 +2,6 @@ import static java.util.Objects.requireNonNull; import static org.opencds.cqf.fhir.utility.PackageHelper.packageParameters; -import static org.opencds.cqf.fhir.utility.repository.Repositories.createRestRepository; -import static org.opencds.cqf.fhir.utility.repository.Repositories.proxy; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.repository.IRepository; @@ -42,6 +40,7 @@ import org.opencds.cqf.fhir.utility.Ids; import org.opencds.cqf.fhir.utility.model.FhirModelResolverCache; import org.opencds.cqf.fhir.utility.monad.Either3; +import org.opencds.cqf.fhir.utility.repository.RepositoryProxyFactory; @SuppressWarnings("UnstableApiUsage") public class LibraryProcessor { @@ -59,19 +58,16 @@ public class LibraryProcessor { protected IRepository repository; protected CrSettings crSettings; - - public LibraryProcessor(IRepository repository) { - this(repository, CrSettings.getDefault()); - } - - public LibraryProcessor(IRepository repository, CrSettings crSettings) { - this(repository, crSettings, null); - } + protected final RepositoryProxyFactory repositoryProxyFactory; public LibraryProcessor( - IRepository repository, CrSettings crSettings, List operationProcessors) { + IRepository repository, + CrSettings crSettings, + List operationProcessors, + RepositoryProxyFactory repositoryProxyFactory) { this.repository = requireNonNull(repository, "repository can not be null"); this.crSettings = requireNonNull(crSettings, "crSettings can not be null"); + this.repositoryProxyFactory = requireNonNull(repositoryProxyFactory, "repositoryProxyFactory can not be null"); fhirVersion = this.repository.fhirContext().getVersion().getVersion(); modelResolver = FhirModelResolverCache.resolverForVersion(fhirVersion); if (operationProcessors != null && !operationProcessors.isEmpty()) { @@ -197,31 +193,8 @@ public , R extends IBaseResource> IBaseParamete IBaseResource dataEndpoint, IBaseResource contentEndpoint, IBaseResource terminologyEndpoint) { - return evaluate( - library, - subject, - expression, - parameters, - useServerData, - data, - prefetchData, - createRestRepository(repository.fhirContext(), dataEndpoint), - createRestRepository(repository.fhirContext(), contentEndpoint), - createRestRepository(repository.fhirContext(), terminologyEndpoint)); - } - - public , R extends IBaseResource> IBaseParameters evaluate( - Either3 library, - String subject, - List expression, - IBaseParameters parameters, - boolean useServerData, - IBaseBundle data, - List prefetchData, - IRepository dataRepository, - IRepository contentRepository, - IRepository terminologyRepository) { - repository = proxy(repository, useServerData, dataRepository, contentRepository, terminologyRepository); + repository = repositoryProxyFactory.proxy( + repository, useServerData, dataEndpoint, contentEndpoint, terminologyEndpoint); return evaluate( library, subject, diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4CareGapsBundleBuilder.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4CareGapsBundleBuilder.java index edb2ac2abd..3daec7f040 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4CareGapsBundleBuilder.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4CareGapsBundleBuilder.java @@ -57,6 +57,7 @@ import org.opencds.cqf.fhir.utility.builder.CompositionSectionComponentBuilder; import org.opencds.cqf.fhir.utility.builder.DetectedIssueBuilder; import org.opencds.cqf.fhir.utility.builder.NarrativeSettings; +import org.opencds.cqf.fhir.utility.repository.RepositoryProxyFactory; /** * Care Gaps Bundle Builder houses the logic for constructing a Care-Gaps Document Bundle for a Patient per Measures requested @@ -83,15 +84,16 @@ public R4CareGapsBundleBuilder( MeasureEvaluationOptions measureEvaluationOptions, String serverBase, Map configuredResources, - MeasurePeriodValidator measurePeriodValidator) { + MeasurePeriodValidator measurePeriodValidator, + RepositoryProxyFactory repositoryProxyFactory) { this.repository = repository; this.careGapsProperties = careGapsProperties; this.serverBase = serverBase; this.configuredResources = configuredResources; r4MeasureServiceUtils = new R4MeasureServiceUtils(repository); - r4MultiMeasureService = - new R4MultiMeasureService(repository, measureEvaluationOptions, serverBase, measurePeriodValidator); + r4MultiMeasureService = new R4MultiMeasureService( + repository, measureEvaluationOptions, serverBase, measurePeriodValidator, repositoryProxyFactory); } public List makePatientBundles( diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4CareGapsProcessor.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4CareGapsProcessor.java index d7443aa9a4..f0ca0cb49a 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4CareGapsProcessor.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4CareGapsProcessor.java @@ -31,6 +31,7 @@ import org.opencds.cqf.fhir.cr.measure.enumeration.CareGapsStatusCode; import org.opencds.cqf.fhir.cr.measure.r4.utils.R4MeasureServiceUtils; import org.opencds.cqf.fhir.utility.monad.Either3; +import org.opencds.cqf.fhir.utility.repository.RepositoryProxyFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -52,7 +53,8 @@ public R4CareGapsProcessor( IRepository repository, MeasureEvaluationOptions measureEvaluationOptions, String serverBase, - MeasurePeriodValidator measurePeriodValidator) { + MeasurePeriodValidator measurePeriodValidator, + RepositoryProxyFactory repositoryProxyFactory) { this.repository = repository; this.careGapsProperties = careGapsProperties; @@ -63,7 +65,8 @@ public R4CareGapsProcessor( measureEvaluationOptions, serverBase, configuredResources, - measurePeriodValidator); + measurePeriodValidator, + repositoryProxyFactory); subjectProvider = new R4RepositorySubjectProvider(measureEvaluationOptions.getSubjectProviderOptions()); } diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4CareGapsService.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4CareGapsService.java index 9ee34ef490..ca9e49a2f2 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4CareGapsService.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4CareGapsService.java @@ -18,6 +18,7 @@ import org.opencds.cqf.fhir.cr.measure.common.MeasurePeriodValidator; import org.opencds.cqf.fhir.utility.monad.Either3; import org.opencds.cqf.fhir.utility.monad.Eithers; +import org.opencds.cqf.fhir.utility.repository.RepositoryProxyFactory; /** * Care Gap service that processes and produces care-gaps report as a result @@ -30,10 +31,16 @@ public R4CareGapsService( IRepository repository, MeasureEvaluationOptions measureEvaluationOptions, String serverBase, - MeasurePeriodValidator measurePeriodEvalutator) { + MeasurePeriodValidator measurePeriodEvalutator, + RepositoryProxyFactory repositoryProxyFactory) { r4CareGapsProcessor = new R4CareGapsProcessor( - careGapsProperties, repository, measureEvaluationOptions, serverBase, measurePeriodEvalutator); + careGapsProperties, + repository, + measureEvaluationOptions, + serverBase, + measurePeriodEvalutator, + repositoryProxyFactory); } /** diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureProcessor.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureProcessor.java index d717af72cc..02b07d2c48 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureProcessor.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4MeasureProcessor.java @@ -508,6 +508,13 @@ private Map resolveParameterMap(Parameters parameters) { return parameterMap; } + public R4MeasureProcessor withRepository(IRepository repository) { + if (repository == this.repository) { + return this; + } + return new R4MeasureProcessor(repository, this.measureEvaluationOptions); + } + public Interval buildMeasurementPeriod(ZonedDateTime periodStart, ZonedDateTime periodEnd) { Interval measurementPeriod = null; if (periodStart != null && periodEnd != null) { diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4MultiMeasureService.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4MultiMeasureService.java index c497d15a29..73b77e7b05 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4MultiMeasureService.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4MultiMeasureService.java @@ -40,7 +40,7 @@ import org.opencds.cqf.fhir.utility.monad.Either3; import org.opencds.cqf.fhir.utility.repository.FederatedRepository; import org.opencds.cqf.fhir.utility.repository.InMemoryFhirRepository; -import org.opencds.cqf.fhir.utility.repository.Repositories; +import org.opencds.cqf.fhir.utility.repository.RepositoryProxyFactory; /** * Alternate MeasureService call to Process MeasureEvaluation for the selected population of subjects against n-number @@ -58,6 +58,7 @@ public class R4MultiMeasureService implements R4MeasureEvaluatorSingle, R4Measur private final R4RepositorySubjectProvider subjectProvider; private final R4MeasureProcessor r4MeasureProcessorStandardRepository; private final R4MeasureServiceUtils r4MeasureServiceUtilsStandardRepository; + private final RepositoryProxyFactory repositoryProxyFactory; private enum SingleOrMultiple { SINGLE, @@ -68,11 +69,13 @@ public R4MultiMeasureService( IRepository repository, MeasureEvaluationOptions measureEvaluationOptions, String serverBase, - MeasurePeriodValidator measurePeriodValidator) { + MeasurePeriodValidator measurePeriodValidator, + RepositoryProxyFactory repositoryProxyFactory) { this.repository = repository; this.measureEvaluationOptions = measureEvaluationOptions; this.measurePeriodValidator = measurePeriodValidator; this.serverBase = serverBase; + this.repositoryProxyFactory = repositoryProxyFactory; this.subjectProvider = new R4RepositorySubjectProvider(measureEvaluationOptions.getSubjectProviderOptions()); this.r4MeasureProcessorStandardRepository = new R4MeasureProcessor(repository, this.measureEvaluationOptions); this.r4MeasureServiceUtilsStandardRepository = new R4MeasureServiceUtils(repository); @@ -288,19 +291,10 @@ private List> evaluateToListOfList( measurePeriodValidator.validatePeriodStartAndEnd(periodStart, periodEnd); - final R4MeasureProcessor r4ProcessorToUse; - final R4MeasureServiceUtils r4MeasureServiceUtilsToUse; - if (dataEndpoint != null && contentEndpoint != null && terminologyEndpoint != null) { - var repositoryToUse = - Repositories.proxy(repository, true, dataEndpoint, contentEndpoint, terminologyEndpoint); - - r4ProcessorToUse = new R4MeasureProcessor(repositoryToUse, this.measureEvaluationOptions); - - r4MeasureServiceUtilsToUse = new R4MeasureServiceUtils(repositoryToUse); - } else { - r4ProcessorToUse = r4MeasureProcessorStandardRepository; - r4MeasureServiceUtilsToUse = r4MeasureServiceUtilsStandardRepository; - } + var repositoryToUse = + repositoryProxyFactory.proxy(repository, true, dataEndpoint, contentEndpoint, terminologyEndpoint); + var r4ProcessorToUse = r4MeasureProcessorStandardRepository.withRepository(repositoryToUse); + var r4MeasureServiceUtilsToUse = r4MeasureServiceUtilsStandardRepository.withRepository(repositoryToUse); r4MeasureServiceUtilsToUse.ensureSupplementalDataElementSearchParameter(); diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/utils/R4MeasureServiceUtils.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/utils/R4MeasureServiceUtils.java index a00edf8452..fc5e429f5d 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/utils/R4MeasureServiceUtils.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/utils/R4MeasureServiceUtils.java @@ -70,6 +70,13 @@ public R4MeasureServiceUtils(IRepository repository) { this.repository = repository; } + public R4MeasureServiceUtils withRepository(IRepository repository) { + if (repository == this.repository) { + return this; + } + return new R4MeasureServiceUtils(repository); + } + public MeasureReport addProductLineExtension(MeasureReport measureReport, String productLine) { if (productLine != null) { Extension ext = new Extension(); diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/plandefinition/PlanDefinitionProcessor.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/plandefinition/PlanDefinitionProcessor.java index dc1573cbf7..d133992e3b 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/plandefinition/PlanDefinitionProcessor.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/plandefinition/PlanDefinitionProcessor.java @@ -2,8 +2,6 @@ import static java.util.Objects.requireNonNull; import static org.opencds.cqf.fhir.utility.PackageHelper.packageParameters; -import static org.opencds.cqf.fhir.utility.repository.Repositories.createRestRepository; -import static org.opencds.cqf.fhir.utility.repository.Repositories.proxy; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.repository.IRepository; @@ -35,6 +33,7 @@ import org.opencds.cqf.fhir.utility.adapter.IAdapterFactory; import org.opencds.cqf.fhir.utility.model.FhirModelResolverCache; import org.opencds.cqf.fhir.utility.monad.Either3; +import org.opencds.cqf.fhir.utility.repository.RepositoryProxyFactory; @SuppressWarnings({"squid:S107", "squid:S1172", "UnstableApiUsage"}) public class PlanDefinitionProcessor { @@ -47,22 +46,17 @@ public class PlanDefinitionProcessor { protected IRequestResolverFactory requestResolverFactory; protected IRepository repository; protected CrSettings crSettings; - - public PlanDefinitionProcessor(IRepository repository) { - this(repository, CrSettings.getDefault()); - } - - public PlanDefinitionProcessor(IRepository repository, CrSettings crSettings) { - this(repository, crSettings, null, null); - } + protected final RepositoryProxyFactory repositoryProxyFactory; public PlanDefinitionProcessor( IRepository repository, CrSettings crSettings, IRequestResolverFactory requestResolverFactory, - List operationProcessors) { + List operationProcessors, + RepositoryProxyFactory repositoryProxyFactory) { this.repository = requireNonNull(repository, "repository can not be null"); this.crSettings = requireNonNull(crSettings, "crSettings can not be null"); + this.repositoryProxyFactory = requireNonNull(repositoryProxyFactory, "repositoryProxyFactory can not be null"); this.requestResolverFactory = requestResolverFactory; fhirVersion = this.repository.fhirContext().getVersion().getVersion(); modelResolver = FhirModelResolverCache.resolverForVersion(fhirVersion); @@ -96,7 +90,9 @@ protected void initApplyProcessor() { ? requestResolverFactory : IRequestResolverFactory.getDefault(fhirVersion)); } - applyProcessor = applyProcessor != null ? applyProcessor : new ApplyProcessor(repository, activityProcessor); + applyProcessor = applyProcessor != null + ? applyProcessor + : new ApplyProcessor(repository, activityProcessor, crSettings, repositoryProxyFactory); } protected , R extends IBaseResource> R resolvePlanDefinition( @@ -219,45 +215,8 @@ public , R extends IBaseResource> IBaseResource IBaseResource dataEndpoint, IBaseResource contentEndpoint, IBaseResource terminologyEndpoint) { - return apply( - planDefinition, - subject, - encounter, - practitioner, - organization, - userType, - userLanguage, - userTaskContext, - setting, - settingContext, - parameters, - useServerData, - data, - prefetchData, - createRestRepository(repository.fhirContext(), dataEndpoint), - createRestRepository(repository.fhirContext(), contentEndpoint), - createRestRepository(repository.fhirContext(), terminologyEndpoint)); - } - - public , R extends IBaseResource> IBaseResource apply( - Either3 planDefinition, - String subject, - String encounter, - String practitioner, - String organization, - IBaseDatatype userType, - IBaseDatatype userLanguage, - IBaseDatatype userTaskContext, - IBaseDatatype setting, - IBaseDatatype settingContext, - IBaseParameters parameters, - boolean useServerData, - IBaseBundle data, - List prefetchData, - IRepository dataRepository, - IRepository contentRepository, - IRepository terminologyRepository) { - repository = proxy(repository, useServerData, dataRepository, contentRepository, terminologyRepository); + repository = repositoryProxyFactory.proxy( + repository, useServerData, dataEndpoint, contentEndpoint, terminologyEndpoint); return apply( planDefinition, subject, @@ -347,45 +306,8 @@ public , R extends IBaseResource> IBaseParamete IBaseResource dataEndpoint, IBaseResource contentEndpoint, IBaseResource terminologyEndpoint) { - return applyR5( - planDefinition, - subject, - encounter, - practitioner, - organization, - userType, - userLanguage, - userTaskContext, - setting, - settingContext, - parameters, - useServerData, - data, - prefetchData, - createRestRepository(repository.fhirContext(), dataEndpoint), - createRestRepository(repository.fhirContext(), contentEndpoint), - createRestRepository(repository.fhirContext(), terminologyEndpoint)); - } - - public , R extends IBaseResource> IBaseParameters applyR5( - Either3 planDefinition, - List subject, - String encounter, - String practitioner, - String organization, - IBaseDatatype userType, - IBaseDatatype userLanguage, - IBaseDatatype userTaskContext, - IBaseDatatype setting, - IBaseDatatype settingContext, - IBaseParameters parameters, - boolean useServerData, - IBaseBundle data, - List prefetchData, - IRepository dataRepository, - IRepository contentRepository, - IRepository terminologyRepository) { - repository = proxy(repository, useServerData, dataRepository, contentRepository, terminologyRepository); + repository = repositoryProxyFactory.proxy( + repository, useServerData, dataEndpoint, contentEndpoint, terminologyEndpoint); return applyR5( planDefinition, subject, diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/plandefinition/apply/ApplyProcessor.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/plandefinition/apply/ApplyProcessor.java index ea63ee8bfd..f6a9fd9822 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/plandefinition/apply/ApplyProcessor.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/plandefinition/apply/ApplyProcessor.java @@ -17,6 +17,7 @@ import java.util.List; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IBaseResource; +import org.opencds.cqf.fhir.cr.CrSettings; import org.opencds.cqf.fhir.cr.common.ExtensionProcessor; import org.opencds.cqf.fhir.cr.common.ICpgRequest; import org.opencds.cqf.fhir.cr.questionnaire.generate.GenerateProcessor; @@ -25,6 +26,7 @@ import org.opencds.cqf.fhir.utility.Constants; import org.opencds.cqf.fhir.utility.Ids; import org.opencds.cqf.fhir.utility.monad.Eithers; +import org.opencds.cqf.fhir.utility.repository.RepositoryProxyFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -55,13 +57,16 @@ public class ApplyProcessor implements IApplyProcessor { public ApplyProcessor( IRepository repository, - org.opencds.cqf.fhir.cr.activitydefinition.apply.IApplyProcessor activityProcessor) { + org.opencds.cqf.fhir.cr.activitydefinition.apply.IApplyProcessor activityProcessor, + CrSettings crSettings, + RepositoryProxyFactory repositoryProxyFactory) { this.repository = repository; this.activityProcessor = activityProcessor; extensionProcessor = new ExtensionProcessor(); generateProcessor = new GenerateProcessor(this.repository); populateProcessor = new PopulateProcessor(); - extractProcessor = new QuestionnaireResponseProcessor(this.repository); + extractProcessor = + new QuestionnaireResponseProcessor(this.repository, crSettings, null, repositoryProxyFactory); processRequest = new ResponseBuilder(populateProcessor); processGoal = new ProcessGoal(); processAction = new ProcessAction(this.repository, this, generateProcessor); diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/questionnaire/QuestionnaireProcessor.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/questionnaire/QuestionnaireProcessor.java index 6dbf6d0dde..8bf0e878eb 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/questionnaire/QuestionnaireProcessor.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/questionnaire/QuestionnaireProcessor.java @@ -2,8 +2,6 @@ import static java.util.Objects.requireNonNull; import static org.opencds.cqf.fhir.utility.PackageHelper.packageParameters; -import static org.opencds.cqf.fhir.utility.repository.Repositories.createRestRepository; -import static org.opencds.cqf.fhir.utility.repository.Repositories.proxy; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.repository.IRepository; @@ -33,6 +31,7 @@ import org.opencds.cqf.fhir.utility.Ids; import org.opencds.cqf.fhir.utility.model.FhirModelResolverCache; import org.opencds.cqf.fhir.utility.monad.Either3; +import org.opencds.cqf.fhir.utility.repository.RepositoryProxyFactory; @SuppressWarnings("UnstableApiUsage") public class QuestionnaireProcessor { @@ -48,19 +47,16 @@ public class QuestionnaireProcessor { protected IPackageProcessor packageProcessor; protected IDataRequirementsProcessor dataRequirementsProcessor; protected IPopulateProcessor populateProcessor; - - public QuestionnaireProcessor(IRepository repository) { - this(repository, CrSettings.getDefault()); - } - - public QuestionnaireProcessor(IRepository repository, CrSettings crSettings) { - this(repository, crSettings, null); - } + protected final RepositoryProxyFactory repositoryProxyFactory; public QuestionnaireProcessor( - IRepository repository, CrSettings crSettings, List operationProcessors) { + IRepository repository, + CrSettings crSettings, + List operationProcessors, + RepositoryProxyFactory repositoryProxyFactory) { this.repository = requireNonNull(repository, "repository can not be null"); this.crSettings = requireNonNull(crSettings, "evaluationSettings can not be null"); + this.repositoryProxyFactory = requireNonNull(repositoryProxyFactory, "repositoryProxyFactory can not be null"); this.questionnaireResolver = new ResourceResolver("Questionnaire", this.repository); this.structureDefResolver = new ResourceResolver("StructureDefinition", this.repository); fhirVersion = this.repository.fhirContext().getVersion().getVersion(); @@ -122,23 +118,7 @@ public , R extends IBaseResource> IBaseResource IBaseResource contentEndpoint, IBaseResource terminologyEndpoint, String id) { - return generateQuestionnaire( - profile, - supportedOnly, - requiredOnly, - createRestRepository(repository.fhirContext(), contentEndpoint), - createRestRepository(repository.fhirContext(), terminologyEndpoint), - id); - } - - public , R extends IBaseResource> IBaseResource generateQuestionnaire( - Either3 profile, - boolean supportedOnly, - boolean requiredOnly, - IRepository contentRepository, - IRepository terminologyRepository, - String id) { - repository = proxy(repository, true, null, contentRepository, terminologyRepository); + repository = repositoryProxyFactory.proxy(repository, true, null, contentEndpoint, terminologyEndpoint); return generateQuestionnaire(profile, supportedOnly, requiredOnly, id); } @@ -230,29 +210,8 @@ public , R extends IBaseResource> IBaseResource IBaseResource dataEndpoint, IBaseResource contentEndpoint, IBaseResource terminologyEndpoint) { - return populate( - questionnaire, - subjectId, - context, - launchContext, - data, - useServerData, - createRestRepository(repository.fhirContext(), dataEndpoint), - createRestRepository(repository.fhirContext(), contentEndpoint), - createRestRepository(repository.fhirContext(), terminologyEndpoint)); - } - - public , R extends IBaseResource> IBaseResource populate( - Either3 questionnaire, - String subjectId, - List context, - IBaseExtension launchContext, - IBaseBundle data, - boolean useServerData, - IRepository dataRepository, - IRepository contentRepository, - IRepository terminologyRepository) { - repository = proxy(repository, useServerData, dataRepository, contentRepository, terminologyRepository); + repository = repositoryProxyFactory.proxy( + repository, useServerData, dataEndpoint, contentEndpoint, terminologyEndpoint); return populate( questionnaire, subjectId, diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/questionnaireresponse/QuestionnaireResponseProcessor.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/questionnaireresponse/QuestionnaireResponseProcessor.java index 3745d434c6..7bbd2bdada 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/questionnaireresponse/QuestionnaireResponseProcessor.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/questionnaireresponse/QuestionnaireResponseProcessor.java @@ -1,7 +1,6 @@ package org.opencds.cqf.fhir.cr.questionnaireresponse; import static java.util.Objects.requireNonNull; -import static org.opencds.cqf.fhir.utility.repository.Repositories.proxy; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; @@ -24,6 +23,7 @@ import org.opencds.cqf.fhir.utility.SearchHelper; import org.opencds.cqf.fhir.utility.model.FhirModelResolverCache; import org.opencds.cqf.fhir.utility.monad.Either; +import org.opencds.cqf.fhir.utility.repository.RepositoryProxyFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,19 +38,16 @@ public class QuestionnaireResponseProcessor { protected IRepository repository; protected CrSettings crSettings; protected IExtractProcessor extractProcessor; - - public QuestionnaireResponseProcessor(IRepository repository) { - this(repository, CrSettings.getDefault()); - } - - public QuestionnaireResponseProcessor(IRepository repository, CrSettings crSettings) { - this(repository, crSettings, null); - } + protected final RepositoryProxyFactory repositoryProxyFactory; public QuestionnaireResponseProcessor( - IRepository repository, CrSettings crSettings, List operationProcessors) { + IRepository repository, + CrSettings crSettings, + List operationProcessors, + RepositoryProxyFactory repositoryProxyFactory) { this.repository = requireNonNull(repository, "repository can not be null"); this.crSettings = requireNonNull(crSettings, "crSettings can not be null"); + this.repositoryProxyFactory = requireNonNull(repositoryProxyFactory, "repositoryProxyFactory can not be null"); this.questionnaireResponseResolver = new ResourceResolver("QuestionnaireResponse", this.repository); this.questionnaireResolver = new ResourceResolver(QUESTIONNAIRE, this.repository); this.fhirVersion = this.repository.fhirContext().getVersion().getVersion(); @@ -139,7 +136,7 @@ public IBaseBundle extract( IBaseParameters parameters, IBaseBundle data, boolean useServerData) { - repository = proxy(repository, useServerData, (IRepository) null, null, null); + repository = repositoryProxyFactory.proxy(repository, useServerData, (IBaseResource) null, null, null); return extract( questionnaireResponseId, questionnaireId, diff --git a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/ActivityDefinitionProcessorFactory.java b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/ActivityDefinitionProcessorFactory.java index e4c7dbb56c..b909dfeaa1 100644 --- a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/ActivityDefinitionProcessorFactory.java +++ b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/ActivityDefinitionProcessorFactory.java @@ -2,6 +2,7 @@ import ca.uhn.fhir.repository.IRepository; import org.opencds.cqf.fhir.cr.activitydefinition.ActivityDefinitionProcessor; +import org.opencds.cqf.fhir.cr.measure.r4.NoOpRepositoryProxyFactory; import org.opencds.cqf.fhir.utility.repository.operations.IActivityDefinitionProcessor; import org.opencds.cqf.fhir.utility.repository.operations.IActivityDefinitionProcessorFactory; @@ -9,6 +10,7 @@ public class ActivityDefinitionProcessorFactory implements IActivityDefinitionPr @Override public IActivityDefinitionProcessor create(IRepository repository) { - return new ActivityDefinitionProcessor(repository); + return new ActivityDefinitionProcessor( + repository, CrSettings.getDefault(), null, null, new NoOpRepositoryProxyFactory()); } } diff --git a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/activitydefinition/ActivityDefinitionProcessorTests.java b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/activitydefinition/ActivityDefinitionProcessorTests.java index a577d22d1e..da48c6aa39 100644 --- a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/activitydefinition/ActivityDefinitionProcessorTests.java +++ b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/activitydefinition/ActivityDefinitionProcessorTests.java @@ -33,7 +33,9 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.opencds.cqf.fhir.cql.LibraryEngine; +import org.opencds.cqf.fhir.cr.CrSettings; import org.opencds.cqf.fhir.cr.activitydefinition.apply.IRequestResolverFactory; +import org.opencds.cqf.fhir.cr.measure.r4.NoOpRepositoryProxyFactory; import org.opencds.cqf.fhir.utility.Ids; import org.opencds.cqf.fhir.utility.monad.Either3; import org.opencds.cqf.fhir.utility.monad.Eithers; @@ -58,7 +60,8 @@ private IRepository createRepository(FhirContext fhirContext, String version) { } private ActivityDefinitionProcessor createProcessor(IRepository repository) { - return new ActivityDefinitionProcessor(repository); + return new ActivityDefinitionProcessor( + repository, CrSettings.getDefault(), null, null, new NoOpRepositoryProxyFactory()); } @BeforeAll diff --git a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/cql/CqlProcessorTests.java b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/cql/CqlProcessorTests.java index 19d7ec0558..d1d2cf1260 100644 --- a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/cql/CqlProcessorTests.java +++ b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/cql/CqlProcessorTests.java @@ -24,6 +24,7 @@ import org.opencds.cqf.fhir.cql.EvaluationSettings; import org.opencds.cqf.fhir.cr.CrSettings; import org.opencds.cqf.fhir.cr.cql.evaluate.CqlEvaluationProcessor; +import org.opencds.cqf.fhir.cr.measure.r4.NoOpRepositoryProxyFactory; import org.opencds.cqf.fhir.utility.repository.ig.IgRepository; class CqlProcessorTests { @@ -54,7 +55,7 @@ class CqlProcessorTests { void defaultSettings() { var repository = new IgRepository(fhirContextR4, Path.of(getResourcePath(this.getClass()) + "/" + CLASS_PATH + "/r4")); - var processor = new CqlProcessor(repository); + var processor = new CqlProcessor(repository, CrSettings.getDefault(), null, new NoOpRepositoryProxyFactory()); assertNotNull(processor.settings()); } @@ -65,7 +66,8 @@ void processor() { var processor = new CqlProcessor( repository, CrSettings.getDefault(), - List.of(new CqlEvaluationProcessor(repository, EvaluationSettings.getDefault()))); + List.of(new CqlEvaluationProcessor(repository, EvaluationSettings.getDefault())), + new NoOpRepositoryProxyFactory()); assertNotNull(processor.settings()); } diff --git a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/cql/TestCql.java b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/cql/TestCql.java index 9b65913dc9..e53d5d4368 100644 --- a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/cql/TestCql.java +++ b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/cql/TestCql.java @@ -31,15 +31,18 @@ import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.opencds.cqf.cql.engine.model.ModelResolver; import org.opencds.cqf.fhir.cql.EvaluationSettings; +import org.opencds.cqf.fhir.cql.LibraryEngine; import org.opencds.cqf.fhir.cql.engine.retrieve.RetrieveSettings.SEARCH_FILTER_MODE; import org.opencds.cqf.fhir.cql.engine.retrieve.RetrieveSettings.TERMINOLOGY_FILTER_MODE; import org.opencds.cqf.fhir.cql.engine.terminology.TerminologySettings.VALUESET_EXPANSION_MODE; import org.opencds.cqf.fhir.cr.CrSettings; import org.opencds.cqf.fhir.cr.TestOperationProvider; +import org.opencds.cqf.fhir.cr.measure.r4.NoOpRepositoryProxyFactory; import org.opencds.cqf.fhir.utility.adapter.IAdapterFactory; import org.opencds.cqf.fhir.utility.adapter.IParametersParameterComponentAdapter; import org.opencds.cqf.fhir.utility.model.FhirModelResolverCache; import org.opencds.cqf.fhir.utility.repository.InMemoryFhirRepository; +import org.opencds.cqf.fhir.utility.repository.Repositories; import org.opencds.cqf.fhir.utility.repository.ig.IgRepository; public class TestCql { @@ -104,18 +107,18 @@ public CqlProcessor buildProcessor(IRepository repository) { .setValuesetExpansionMode(VALUESET_EXPANSION_MODE.PERFORM_NAIVE_EXPANSION); } var crSettings = CrSettings.getDefault().withEvaluationSettings(evaluationSettings); - return new CqlProcessor(repository, crSettings); + return new CqlProcessor(repository, crSettings, null, new NoOpRepositoryProxyFactory()); } public When when() { - return new When(repository, buildProcessor(repository)); + return new When(this, repository); } } @SuppressWarnings("UnstableApiUsage") public static class When { + private final Given given; private final IRepository repository; - private final CqlProcessor processor; private final IParser jsonParser; private String subject; @@ -131,9 +134,9 @@ public static class When { private List library; private String cqlContent; - public When(IRepository repository, CqlProcessor processor) { + public When(Given given, IRepository repository) { + this.given = given; this.repository = repository; - this.processor = processor; useServerData = true; jsonParser = repository.fhirContext().newJsonParser(); } @@ -230,6 +233,9 @@ public Evaluation thenEvaluate() { if (additionalDataId != null) { loadAdditionalData(readRepository(repository, additionalDataId)); } + var proxiedRepo = Repositories.proxy( + repository, useServerData, dataRepository, contentRepository, terminologyRepository); + var processor = given.buildProcessor(proxiedRepo); return new Evaluation( repository, processor.evaluate( @@ -237,13 +243,10 @@ public Evaluation thenEvaluate() { expression, parameters, library, - useServerData, additionalData, prefetchData, cqlContent, - dataRepository, - contentRepository, - terminologyRepository)); + new LibraryEngine(proxiedRepo, processor.settings().getEvaluationSettings()))); } } diff --git a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/graphdefinition/TestGraphDefinition.java b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/graphdefinition/TestGraphDefinition.java index 962d81a5eb..faf07e659f 100644 --- a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/graphdefinition/TestGraphDefinition.java +++ b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/graphdefinition/TestGraphDefinition.java @@ -28,8 +28,10 @@ import org.opencds.cqf.fhir.cr.TestOperationProvider; import org.opencds.cqf.fhir.cr.graphdefinition.apply.ApplyRequest; import org.opencds.cqf.fhir.cr.graphdefinition.apply.ApplyRequestBuilder; +import org.opencds.cqf.fhir.cr.measure.r4.NoOpRepositoryProxyFactory; import org.opencds.cqf.fhir.utility.BundleHelper; import org.opencds.cqf.fhir.utility.repository.InMemoryFhirRepository; +import org.opencds.cqf.fhir.utility.repository.Repositories; import org.opencds.cqf.fhir.utility.repository.ig.IgRepository; @SuppressWarnings("UnstableApiUsage") @@ -108,6 +110,9 @@ public static class When { private GraphDefinitionProcessor processor; private ApplyRequestBuilder applyRequestBuilder; private IParser jsonParser; + private IRepository dataRepository; + private IRepository contentRepository; + private IRepository terminologyRepository; public When( IRepository repository, @@ -115,7 +120,8 @@ public When( EvaluationSettings evaluationSettings) { this.repository = repository; this.processor = graphDefinitionProcessor; - this.applyRequestBuilder = new ApplyRequestBuilder(this.repository, evaluationSettings); + this.applyRequestBuilder = + new ApplyRequestBuilder(this.repository, evaluationSettings, new NoOpRepositoryProxyFactory()); this.jsonParser = repository.fhirContext().newJsonParser(); } @@ -135,17 +141,17 @@ public When practitionerId(String practitionerId) { } public When data(String dataAssetName) { - applyRequestBuilder.withDataRepository(createRepository(dataAssetName)); + this.dataRepository = createRepository(dataAssetName); return this; } public When content(String dataAssetName) { - applyRequestBuilder.withContentRepository(createRepository(dataAssetName)); + this.contentRepository = createRepository(dataAssetName); return this; } public When terminology(String dataAssetName) { - applyRequestBuilder.withTerminologyRepository(createRepository(dataAssetName)); + this.terminologyRepository = createRepository(dataAssetName); return this; } @@ -161,7 +167,7 @@ public When additionalData(IBaseResource resource) { } public When dataRepository(IgRepository theRepository) { - applyRequestBuilder.withDataRepository(theRepository); + this.dataRepository = theRepository; return this; } @@ -171,6 +177,9 @@ public When additionalData(String dataAssetName) { } public GeneratedBundle thenApply() { + var proxiedRepo = + Repositories.proxy(repository, true, dataRepository, contentRepository, terminologyRepository); + applyRequestBuilder.withRepository(proxiedRepo); ApplyRequest applyRequest = applyRequestBuilder.buildApplyRequest(); IBaseResource generatedBundle = this.processor.apply(applyRequest); diff --git a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/graphdefinition/apply/ApplyProcessorTest.java b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/graphdefinition/apply/ApplyProcessorTest.java index 9e7afbd06b..8ea919aa90 100644 --- a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/graphdefinition/apply/ApplyProcessorTest.java +++ b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/graphdefinition/apply/ApplyProcessorTest.java @@ -43,6 +43,7 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.opencds.cqf.cql.engine.model.ModelResolver; import org.opencds.cqf.fhir.cql.EvaluationSettings; +import org.opencds.cqf.fhir.cr.measure.r4.NoOpRepositoryProxyFactory; import org.opencds.cqf.fhir.utility.repository.ig.IgRepository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -80,7 +81,8 @@ void testApply_returnsDocumentBundleWithTimeStamp() { String id = "eras-postop"; IdType graphDefinitionId = new IdType("GraphDefinition", id); - ApplyRequestBuilder applyRequestBuilder = new ApplyRequestBuilder(repository, EvaluationSettings.getDefault()); + ApplyRequestBuilder applyRequestBuilder = + new ApplyRequestBuilder(repository, EvaluationSettings.getDefault(), new NoOpRepositoryProxyFactory()); applyRequestBuilder.withSubject("Patient/time-zero"); applyRequestBuilder.withPractitioner("Practitioner/ordering-md-1"); applyRequestBuilder.withGraphDefinitionId(graphDefinitionId); @@ -98,7 +100,8 @@ void testApply_firstEntryIsComposition() { String id = "eras-postop"; IdType graphDefinitionId = new IdType("GraphDefinition", id); - ApplyRequestBuilder applyRequestBuilder = new ApplyRequestBuilder(repository, EvaluationSettings.getDefault()); + ApplyRequestBuilder applyRequestBuilder = + new ApplyRequestBuilder(repository, EvaluationSettings.getDefault(), new NoOpRepositoryProxyFactory()); applyRequestBuilder.withSubject("Patient/time-zero"); applyRequestBuilder.withPractitioner("Practitioner/ordering-md-1"); applyRequestBuilder.withGraphDefinitionId(graphDefinitionId); diff --git a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/graphdefinition/apply/ApplyRequestBuilderTest.java b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/graphdefinition/apply/ApplyRequestBuilderTest.java index 8c80731041..380bb72b76 100644 --- a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/graphdefinition/apply/ApplyRequestBuilderTest.java +++ b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/graphdefinition/apply/ApplyRequestBuilderTest.java @@ -15,6 +15,7 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.opencds.cqf.fhir.cql.EvaluationSettings; import org.opencds.cqf.fhir.cql.LibraryEngine; +import org.opencds.cqf.fhir.cr.measure.r4.NoOpRepositoryProxyFactory; import org.opencds.cqf.fhir.utility.repository.InMemoryFhirRepository; @ExtendWith(MockitoExtension.class) @@ -31,7 +32,8 @@ class ApplyRequestBuilderTest { void build_withoutSubject_throwsIllegalArgumentException() { when(repository.fhirContext()).thenReturn(fhirContext); - ApplyRequestBuilder builder = new ApplyRequestBuilder(repository, evaluationSettings); + ApplyRequestBuilder builder = + new ApplyRequestBuilder(repository, evaluationSettings, new NoOpRepositoryProxyFactory()); IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, builder::buildApplyRequest); assertEquals("Missing required parameter: 'subject'", ex.getMessage()); @@ -41,8 +43,9 @@ void build_withoutSubject_throwsIllegalArgumentException() { void build_withoutPractitioner_throwsIllegalArgumentException() { when(repository.fhirContext()).thenReturn(fhirContext); - ApplyRequestBuilder builder = - new ApplyRequestBuilder(repository, evaluationSettings).withSubject("Patient/123"); + ApplyRequestBuilder builder = new ApplyRequestBuilder( + repository, evaluationSettings, new NoOpRepositoryProxyFactory()) + .withSubject("Patient/123"); IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, builder::buildApplyRequest); assertEquals("Missing required parameter: 'practitioner'", ex.getMessage()); @@ -53,7 +56,8 @@ void build_withGraphDefinitionAndSubject_returnsApplyRequest() { IRepository localRepository = new InMemoryFhirRepository(fhirContext); IdType id = (IdType) localRepository.create(new GraphDefinition()).getId(); - ApplyRequestBuilder builder = new ApplyRequestBuilder(localRepository, evaluationSettings) + ApplyRequestBuilder builder = new ApplyRequestBuilder( + localRepository, evaluationSettings, new NoOpRepositoryProxyFactory()) .withGraphDefinitionId(id) .withSubject("Patient/123") .withPractitioner("Practitioner/456") diff --git a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/library/LibraryProcessorTests.java b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/library/LibraryProcessorTests.java index 1ea41ba6c1..14daa60d6d 100644 --- a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/library/LibraryProcessorTests.java +++ b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/library/LibraryProcessorTests.java @@ -29,6 +29,7 @@ import org.opencds.cqf.fhir.cr.common.WithdrawProcessor; import org.opencds.cqf.fhir.cr.helpers.RequestHelpers; import org.opencds.cqf.fhir.cr.library.evaluate.EvaluateProcessor; +import org.opencds.cqf.fhir.cr.measure.r4.NoOpRepositoryProxyFactory; import org.opencds.cqf.fhir.utility.Ids; import org.opencds.cqf.fhir.utility.monad.Eithers; import org.opencds.cqf.fhir.utility.repository.ig.IgRepository; @@ -47,7 +48,8 @@ class LibraryProcessorTests { void defaultSettings() { var repository = new IgRepository(fhirContextR4, Path.of(getResourcePath(this.getClass()) + "/" + CLASS_PATH + "/r4")); - var processor = new LibraryProcessor(repository); + var processor = + new LibraryProcessor(repository, CrSettings.getDefault(), null, new NoOpRepositoryProxyFactory()); assertNotNull(processor.settings()); } @@ -76,7 +78,8 @@ void processor() { new RetireProcessor(repository), new WithdrawProcessor(repository), new ReviseProcessor(repository), - new ArtifactDiffProcessor())); + new ArtifactDiffProcessor()), + new NoOpRepositoryProxyFactory()); assertNotNull(processor.settings()); var result = processor.resolveLibrary(Eithers.forMiddle3( diff --git a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/library/TestLibrary.java b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/library/TestLibrary.java index a15120ee3f..b8808fe701 100644 --- a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/library/TestLibrary.java +++ b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/library/TestLibrary.java @@ -30,6 +30,7 @@ import org.hl7.fhir.instance.model.api.IIdType; import org.opencds.cqf.cql.engine.model.ModelResolver; import org.opencds.cqf.fhir.cql.EvaluationSettings; +import org.opencds.cqf.fhir.cql.LibraryEngine; import org.opencds.cqf.fhir.cql.engine.retrieve.RetrieveSettings.SEARCH_FILTER_MODE; import org.opencds.cqf.fhir.cql.engine.retrieve.RetrieveSettings.TERMINOLOGY_FILTER_MODE; import org.opencds.cqf.fhir.cql.engine.terminology.TerminologySettings.VALUESET_EXPANSION_MODE; @@ -37,10 +38,12 @@ import org.opencds.cqf.fhir.cr.TestOperationProvider; import org.opencds.cqf.fhir.cr.helpers.DataRequirementsLibrary; import org.opencds.cqf.fhir.cr.helpers.GeneratedPackage; +import org.opencds.cqf.fhir.cr.measure.r4.NoOpRepositoryProxyFactory; import org.opencds.cqf.fhir.utility.Ids; import org.opencds.cqf.fhir.utility.model.FhirModelResolverCache; import org.opencds.cqf.fhir.utility.monad.Eithers; import org.opencds.cqf.fhir.utility.repository.InMemoryFhirRepository; +import org.opencds.cqf.fhir.utility.repository.Repositories; import org.opencds.cqf.fhir.utility.repository.ig.IgRepository; public class TestLibrary { @@ -105,18 +108,18 @@ public LibraryProcessor buildProcessor(IRepository repository) { .setValuesetExpansionMode(VALUESET_EXPANSION_MODE.PERFORM_NAIVE_EXPANSION); } var crSettings = CrSettings.getDefault().withEvaluationSettings(evaluationSettings); - return new LibraryProcessor(repository, crSettings); + return new LibraryProcessor(repository, crSettings, null, new NoOpRepositoryProxyFactory()); } public When when() { - return new When(repository, buildProcessor(repository)); + return new When(this, repository); } } @SuppressWarnings("UnstableApiUsage") public static class When { + private final Given given; private final IRepository repository; - private final LibraryProcessor processor; private final IParser jsonParser; private String libraryId; @@ -134,9 +137,12 @@ public static class When { private IBaseParameters parameters; private Boolean isPackagePut; - public When(IRepository repository, LibraryProcessor processor) { + private final LibraryProcessor processor; + + public When(Given given, IRepository repository) { + this.given = given; this.repository = repository; - this.processor = processor; + this.processor = given.buildProcessor(repository); useServerData = true; jsonParser = repository.fhirContext().newJsonParser(); } @@ -254,9 +260,12 @@ public Evaluation thenEvaluate() { if (additionalDataId != null) { loadAdditionalData(readRepository(repository, additionalDataId)); } + var proxiedRepo = Repositories.proxy( + repository, useServerData, dataRepository, contentRepository, terminologyRepository); + var evalProcessor = given.buildProcessor(proxiedRepo); return new Evaluation( repository, - processor.evaluate( + evalProcessor.evaluate( Eithers.for3( libraryUrl == null ? null @@ -273,12 +282,10 @@ public Evaluation thenEvaluate() { subjectId, expression, parameters, - useServerData, additionalData, prefetchData, - dataRepository, - contentRepository, - terminologyRepository)); + new LibraryEngine( + proxiedRepo, evalProcessor.settings().getEvaluationSettings()))); } } diff --git a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/CareGaps.java b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/CareGaps.java index 92c3e702f4..ea8c8d3188 100644 --- a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/CareGaps.java +++ b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/CareGaps.java @@ -139,7 +139,12 @@ public CareGaps.Given careGapsProperties(CareGapsProperties careGapsProperties) private R4CareGapsService buildCareGapsService() { return new R4CareGapsService( - careGapsProperties, repository, evaluationOptions, serverBase, measurePeriodEvaluator); + careGapsProperties, + repository, + evaluationOptions, + serverBase, + measurePeriodEvaluator, + new NoOpRepositoryProxyFactory()); } public When when() { diff --git a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/Measure.java b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/Measure.java index 3d1bb1ba41..9548a07ca0 100644 --- a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/Measure.java +++ b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/Measure.java @@ -31,6 +31,7 @@ import org.opencds.cqf.fhir.cr.measure.r4.selected.report.SelectedMeasureReportGroup; import org.opencds.cqf.fhir.cr.measure.r4.selected.report.SelectedMeasureReportReference; import org.opencds.cqf.fhir.utility.monad.Eithers; +import org.opencds.cqf.fhir.utility.repository.RepositoryProxyFactory; import org.opencds.cqf.fhir.utility.repository.ig.IgRepository; // consider rolling this entire thing into MultiMeasure with "single measure" assertions @@ -89,6 +90,7 @@ public static class Given { private MeasureEvaluationOptions evaluationOptions; private String serverBase; private final MeasurePeriodValidator measurePeriodValidator; + private RepositoryProxyFactory repositoryProxyFactory; public Given(@Nullable Boolean applyScoringSetMembership) { this.evaluationOptions = MeasureEvaluationOptions.defaultOptions(); @@ -138,8 +140,18 @@ public Given evaluationOptions(MeasureEvaluationOptions evaluationOptions) { return this; } + public Given repositoryProxyFactory(RepositoryProxyFactory repositoryProxyFactory) { + this.repositoryProxyFactory = repositoryProxyFactory; + return this; + } + private R4MultiMeasureService buildMultiMeasureService() { - return new R4MultiMeasureService(repository, evaluationOptions, serverBase, measurePeriodValidator); + return new R4MultiMeasureService( + repository, + evaluationOptions, + serverBase, + measurePeriodValidator, + repositoryProxyFactory != null ? repositoryProxyFactory : new NoOpRepositoryProxyFactory()); } public When when() { diff --git a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/MultiMeasure.java b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/MultiMeasure.java index a5201b1de6..12dc57504f 100644 --- a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/MultiMeasure.java +++ b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/MultiMeasure.java @@ -48,6 +48,7 @@ import org.opencds.cqf.fhir.cr.measure.constant.MeasureConstants; import org.opencds.cqf.fhir.cr.measure.r4.selected.def.SelectedMeasureDefCollection; import org.opencds.cqf.fhir.utility.BundleHelper; +import org.opencds.cqf.fhir.utility.repository.RepositoryProxyFactory; import org.opencds.cqf.fhir.utility.repository.ig.IgRepository; @SuppressWarnings("squid:S1135") @@ -101,6 +102,7 @@ public static class Given { private MeasureEvaluationOptions evaluationOptions; private String serverBase; private MeasurePeriodValidator measurePeriodValidator; + private RepositoryProxyFactory repositoryProxyFactory; public Given() { this.evaluationOptions = MeasureEvaluationOptions.defaultOptions(); @@ -142,6 +144,11 @@ public MultiMeasure.Given serverBase(String serverBase) { return this; } + public MultiMeasure.Given repositoryProxyFactory(RepositoryProxyFactory repositoryProxyFactory) { + this.repositoryProxyFactory = repositoryProxyFactory; + return this; + } + // Exposed for unit tests that only want to use part of this framework when passing in // an IgRepository public IRepository getRepository() { @@ -153,7 +160,12 @@ public IRepository getRepository() { } private R4MultiMeasureService buildMeasureService() { - return new R4MultiMeasureService(repository, evaluationOptions, serverBase, measurePeriodValidator); + return new R4MultiMeasureService( + repository, + evaluationOptions, + serverBase, + measurePeriodValidator, + repositoryProxyFactory != null ? repositoryProxyFactory : new NoOpRepositoryProxyFactory()); } public MultiMeasure.When when() { diff --git a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/NoOpRepositoryProxyFactory.java b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/NoOpRepositoryProxyFactory.java new file mode 100644 index 0000000000..3fa2fdaa3a --- /dev/null +++ b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/NoOpRepositoryProxyFactory.java @@ -0,0 +1,21 @@ +package org.opencds.cqf.fhir.cr.measure.r4; + +import ca.uhn.fhir.repository.IRepository; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.opencds.cqf.fhir.utility.repository.RepositoryProxyFactory; + +/** + * A RepositoryProxyFactory that always returns the local Repository for tests only. + */ +public class NoOpRepositoryProxyFactory implements RepositoryProxyFactory { + + @Override + public IRepository proxy( + IRepository localRepository, + Boolean useServerData, + IBaseResource dataEndpoint, + IBaseResource contentEndpoint, + IBaseResource terminologyEndpoint) { + return localRepository; + } +} diff --git a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/R4MultiMeasureServiceProxyTest.java b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/R4MultiMeasureServiceProxyTest.java new file mode 100644 index 0000000000..a9a77ebe63 --- /dev/null +++ b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/R4MultiMeasureServiceProxyTest.java @@ -0,0 +1,273 @@ +package org.opencds.cqf.fhir.cr.measure.r4; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.opencds.cqf.fhir.test.Resources.getResourcePath; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.repository.IRepository; +import java.nio.file.Path; +import java.util.Collections; +import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.Endpoint; +import org.hl7.fhir.r4.model.IdType; +import org.hl7.fhir.r4.model.Library; +import org.hl7.fhir.r4.model.Measure; +import org.hl7.fhir.r4.model.Patient; +import org.hl7.fhir.r4.model.ValueSet; +import org.junit.jupiter.api.Test; +import org.opencds.cqf.fhir.cql.engine.retrieve.RetrieveSettings.SEARCH_FILTER_MODE; +import org.opencds.cqf.fhir.cql.engine.retrieve.RetrieveSettings.TERMINOLOGY_FILTER_MODE; +import org.opencds.cqf.fhir.cql.engine.terminology.TerminologySettings.VALUESET_EXPANSION_MODE; +import org.opencds.cqf.fhir.cr.measure.MeasureEvaluationOptions; +import org.opencds.cqf.fhir.cr.measure.common.MeasurePeriodValidator; +import org.opencds.cqf.fhir.utility.BundleHelper; +import org.opencds.cqf.fhir.utility.repository.InMemoryFhirRepository; +import org.opencds.cqf.fhir.utility.repository.ig.IgRepository; + +/** + * Tests that verify the RepositoryProxyFactory correctly routes requests + * to different repositories based on endpoint configuration. + * + *

Each test splits resources across InMemoryFhirRepositories so that + * evaluation can only succeed if the proxy routes to the correct repository. + */ +class R4MultiMeasureServiceProxyTest { + + private static final String CLASS_PATH = "org/opencds/cqf/fhir/cr/measure/r4"; + private static final FhirContext FHIR_CONTEXT = FhirContext.forR4Cached(); + + private IgRepository fullRepository() { + return new IgRepository( + FHIR_CONTEXT, + Path.of(getResourcePath(this.getClass()) + "/" + CLASS_PATH + "/MinimalMeasureEvaluation")); + } + + private MeasureEvaluationOptions defaultOptions() { + var options = MeasureEvaluationOptions.defaultOptions(); + options.getEvaluationSettings() + .getRetrieveSettings() + .setSearchParameterMode(SEARCH_FILTER_MODE.FILTER_IN_MEMORY) + .setTerminologyParameterMode(TERMINOLOGY_FILTER_MODE.FILTER_IN_MEMORY); + options.getEvaluationSettings() + .getTerminologySettings() + .setValuesetExpansionMode(VALUESET_EXPANSION_MODE.PERFORM_NAIVE_EXPANSION); + return options; + } + + /** + * Sentinel endpoint: a non-null Endpoint signals the TestRepositoryProxyFactory + * to substitute the corresponding InMemoryFhirRepository. + */ + private Endpoint sentinel() { + return new Endpoint().setAddress("http://test"); + } + + /** + * Builds an InMemoryFhirRepository containing only content resources + * (Measure and Library) from the full repository. + */ + private IRepository buildContentRepository(IgRepository source) { + var repo = new InMemoryFhirRepository(FHIR_CONTEXT); + // Copy all Measure resources + var measures = source.search(Bundle.class, Measure.class, Collections.emptyMap()); + for (var resource : BundleHelper.getEntryResources(measures)) { + repo.update(resource); + } + // Copy all Library resources + var libraries = source.search(Bundle.class, Library.class, Collections.emptyMap()); + for (var resource : BundleHelper.getEntryResources(libraries)) { + repo.update(resource); + } + return repo; + } + + /** + * Builds an InMemoryFhirRepository containing only terminology resources + * (ValueSet) from the full repository. + */ + private IRepository buildTerminologyRepository(IgRepository source) { + var repo = new InMemoryFhirRepository(FHIR_CONTEXT); + var valueSets = source.search(Bundle.class, ValueSet.class, Collections.emptyMap()); + for (var resource : BundleHelper.getEntryResources(valueSets)) { + repo.update(resource); + } + return repo; + } + + /** + * Builds an InMemoryFhirRepository containing only data resources + * (Patient, Encounter, etc.) from the full repository. + */ + private IRepository buildDataRepository(IgRepository source) { + var repo = new InMemoryFhirRepository(FHIR_CONTEXT); + var patients = source.search(Bundle.class, Patient.class, Collections.emptyMap()); + for (var resource : BundleHelper.getEntryResources(patients)) { + repo.update(resource); + } + // Also copy Encounter resources + var encounters = source.search(Bundle.class, org.hl7.fhir.r4.model.Encounter.class, Collections.emptyMap()); + for (var resource : BundleHelper.getEntryResources(encounters)) { + repo.update(resource); + } + return repo; + } + + @Test + void noEndpoints_usesStandardRepository() { + var repo = fullRepository(); + var service = new R4MultiMeasureService( + repo, + defaultOptions(), + "http://localhost", + new MeasurePeriodValidator(), + new TestRepositoryProxyFactory(null, null, null)); + + // Evaluate with no endpoints - should use the standard repository + var report = service.evaluate( + org.opencds.cqf.fhir.utility.monad.Eithers.forMiddle3( + new IdType("Measure", "MinimalProportionBooleanBasisSingleGroup")), + null, + null, + "subject", + "Patient/female-1988", + null, + null, + null, + null, + null, + null, + null, + null); + assertNotNull(report); + assertNotNull(report.getId()); + } + + @Test + void onlyContentEndpoint_contentFromProxy() { + var source = fullRepository(); + var contentRepo = buildContentRepository(source); + + // Create a local repo WITHOUT content (Measures/Libraries) + // The content must come from the proxy's content repository + var service = new R4MultiMeasureService( + source, + defaultOptions(), + "http://localhost", + new MeasurePeriodValidator(), + new TestRepositoryProxyFactory(null, contentRepo, null)); + + // Passing only content endpoint sentinel - content should route to contentRepo + var report = service.evaluate( + org.opencds.cqf.fhir.utility.monad.Eithers.forMiddle3( + new IdType("Measure", "MinimalProportionBooleanBasisSingleGroup")), + null, + null, + "subject", + "Patient/female-1988", + null, + sentinel(), + null, + null, + null, + null, + null, + null); + assertNotNull(report); + } + + @Test + void onlyTerminologyEndpoint_terminologyFromProxy() { + var source = fullRepository(); + var terminologyRepo = buildTerminologyRepository(source); + + var service = new R4MultiMeasureService( + source, + defaultOptions(), + "http://localhost", + new MeasurePeriodValidator(), + new TestRepositoryProxyFactory(null, null, terminologyRepo)); + + // Passing only terminology endpoint sentinel + var report = service.evaluate( + org.opencds.cqf.fhir.utility.monad.Eithers.forMiddle3( + new IdType("Measure", "MinimalProportionBooleanBasisSingleGroup")), + null, + null, + "subject", + "Patient/female-1988", + null, + null, + sentinel(), + null, + null, + null, + null, + null); + assertNotNull(report); + } + + @Test + void allThreeEndpoints_eachTypeSeparatelyRouted() { + var source = fullRepository(); + var dataRepo = buildDataRepository(source); + var contentRepo = buildContentRepository(source); + var terminologyRepo = buildTerminologyRepository(source); + + var service = new R4MultiMeasureService( + source, + defaultOptions(), + "http://localhost", + new MeasurePeriodValidator(), + new TestRepositoryProxyFactory(dataRepo, contentRepo, terminologyRepo)); + + // All three endpoints specified - each type routed to its respective repo + var report = service.evaluate( + org.opencds.cqf.fhir.utility.monad.Eithers.forMiddle3( + new IdType("Measure", "MinimalProportionBooleanBasisSingleGroup")), + null, + null, + "subject", + "Patient/female-1988", + null, + sentinel(), + sentinel(), + sentinel(), + null, + null, + null, + null); + assertNotNull(report); + } + + @Test + void contentAndTerminologyEndpoints_dataFromLocal() { + var source = fullRepository(); + var contentRepo = buildContentRepository(source); + var terminologyRepo = buildTerminologyRepository(source); + + var service = new R4MultiMeasureService( + source, + defaultOptions(), + "http://localhost", + new MeasurePeriodValidator(), + new TestRepositoryProxyFactory(null, contentRepo, terminologyRepo)); + + // Content + terminology endpoints, data from local + var report = service.evaluate( + org.opencds.cqf.fhir.utility.monad.Eithers.forMiddle3( + new IdType("Measure", "MinimalProportionBooleanBasisSingleGroup")), + null, + null, + "subject", + "Patient/female-1988", + null, + sentinel(), + sentinel(), + null, + null, + null, + null, + null); + assertNotNull(report); + } +} diff --git a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/TestRepositoryProxyFactory.java b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/TestRepositoryProxyFactory.java new file mode 100644 index 0000000000..35c0e0726d --- /dev/null +++ b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/TestRepositoryProxyFactory.java @@ -0,0 +1,41 @@ +package org.opencds.cqf.fhir.cr.measure.r4; + +import ca.uhn.fhir.repository.IRepository; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.opencds.cqf.fhir.utility.repository.Repositories; +import org.opencds.cqf.fhir.utility.repository.RepositoryProxyFactory; + +/** + * A test-only RepositoryProxyFactory that maps non-null endpoint sentinel objects + * to pre-built InMemoryFhirRepository instances, avoiding the need for live FHIR servers. + * + *

For the endpoint-based proxy method, any non-null endpoint is treated as a sentinel + * indicating that the corresponding pre-built repository should be used. + */ +class TestRepositoryProxyFactory implements RepositoryProxyFactory { + private final IRepository dataRepository; + private final IRepository contentRepository; + private final IRepository terminologyRepository; + + TestRepositoryProxyFactory( + IRepository dataRepository, IRepository contentRepository, IRepository terminologyRepository) { + this.dataRepository = dataRepository; + this.contentRepository = contentRepository; + this.terminologyRepository = terminologyRepository; + } + + @Override + public IRepository proxy( + IRepository localRepository, + Boolean useServerData, + IBaseResource dataEndpoint, + IBaseResource contentEndpoint, + IBaseResource terminologyEndpoint) { + return Repositories.proxy( + localRepository, + useServerData, + dataEndpoint != null ? dataRepository : null, + contentEndpoint != null ? contentRepository : null, + terminologyEndpoint != null ? terminologyRepository : null); + } +} diff --git a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/plandefinition/PlanDefinitionProcessorTests.java b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/plandefinition/PlanDefinitionProcessorTests.java index 667d721153..e9835a5a7b 100644 --- a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/plandefinition/PlanDefinitionProcessorTests.java +++ b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/plandefinition/PlanDefinitionProcessorTests.java @@ -26,6 +26,7 @@ import org.opencds.cqf.fhir.cr.activitydefinition.apply.IRequestResolverFactory; import org.opencds.cqf.fhir.cr.common.DataRequirementsProcessor; import org.opencds.cqf.fhir.cr.common.PackageProcessor; +import org.opencds.cqf.fhir.cr.measure.r4.NoOpRepositoryProxyFactory; import org.opencds.cqf.fhir.cr.plandefinition.apply.ApplyProcessor; import org.opencds.cqf.fhir.utility.BundleHelper; import org.opencds.cqf.fhir.utility.Ids; @@ -42,7 +43,8 @@ class PlanDefinitionProcessorTests { void defaultSettings() { var repository = new IgRepository(fhirContextR4, Path.of(getResourcePath(this.getClass()) + "/" + CLASS_PATH + "/r4")); - var processor = new PlanDefinitionProcessor(repository); + var processor = new PlanDefinitionProcessor( + repository, CrSettings.getDefault(), null, null, new NoOpRepositoryProxyFactory()); assertNotNull(processor.settings()); } @@ -60,10 +62,15 @@ void processor() { CrSettings.getDefault(), requestResolverFactory, List.of( - new ApplyProcessor(repository, activityProcessor), + new ApplyProcessor( + repository, + activityProcessor, + CrSettings.getDefault(), + new NoOpRepositoryProxyFactory()), packageProcessor, dataRequirementsProcessor, - activityProcessor)); + activityProcessor), + new NoOpRepositoryProxyFactory()); assertNotNull(processor.settings()); var result = processor.apply( Eithers.forMiddle3(Ids.newId(repository.fhirContext(), "PlanDefinition", "DischargeInstructionsPlan")), diff --git a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/plandefinition/TestPlanDefinition.java b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/plandefinition/TestPlanDefinition.java index b6a3d48496..de156eaca1 100644 --- a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/plandefinition/TestPlanDefinition.java +++ b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/plandefinition/TestPlanDefinition.java @@ -40,6 +40,7 @@ import org.json.JSONException; import org.opencds.cqf.cql.engine.model.ModelResolver; import org.opencds.cqf.fhir.cql.EvaluationSettings; +import org.opencds.cqf.fhir.cql.LibraryEngine; import org.opencds.cqf.fhir.cql.engine.retrieve.RetrieveSettings.SEARCH_FILTER_MODE; import org.opencds.cqf.fhir.cql.engine.retrieve.RetrieveSettings.TERMINOLOGY_FILTER_MODE; import org.opencds.cqf.fhir.cql.engine.terminology.TerminologySettings.VALUESET_EXPANSION_MODE; @@ -48,12 +49,14 @@ import org.opencds.cqf.fhir.cr.common.IOperationProcessor; import org.opencds.cqf.fhir.cr.helpers.DataRequirementsLibrary; import org.opencds.cqf.fhir.cr.helpers.GeneratedPackage; +import org.opencds.cqf.fhir.cr.measure.r4.NoOpRepositoryProxyFactory; import org.opencds.cqf.fhir.utility.Ids; import org.opencds.cqf.fhir.utility.adapter.IAdapterFactory; import org.opencds.cqf.fhir.utility.adapter.IParametersAdapter; import org.opencds.cqf.fhir.utility.model.FhirModelResolverCache; import org.opencds.cqf.fhir.utility.monad.Eithers; import org.opencds.cqf.fhir.utility.repository.InMemoryFhirRepository; +import org.opencds.cqf.fhir.utility.repository.Repositories; import org.opencds.cqf.fhir.utility.repository.ig.IgRepository; import org.skyscreamer.jsonassert.JSONAssert; @@ -120,16 +123,18 @@ public PlanDefinitionProcessor buildProcessor(IRepository repository) { .setValuesetExpansionMode(VALUESET_EXPANSION_MODE.PERFORM_NAIVE_EXPANSION); } var crSettings = CrSettings.getDefault().withEvaluationSettings(evaluationSettings); - return new PlanDefinitionProcessor(repository, crSettings, null, operationProcessors); + return new PlanDefinitionProcessor( + repository, crSettings, null, operationProcessors, new NoOpRepositoryProxyFactory()); } public When when() { - return new When(repository, buildProcessor(repository)); + return new When(this, repository); } } @SuppressWarnings("UnstableApiUsage") public static class When { + private final Given given; private final IRepository repository; private final PlanDefinitionProcessor processor; private final IParser jsonParser; @@ -150,9 +155,10 @@ public static class When { private IBaseParameters parameters; private boolean isPackagePut; - public When(IRepository repository, PlanDefinitionProcessor processor) { + public When(Given given, IRepository repository) { + this.given = given; this.repository = repository; - this.processor = processor; + this.processor = given.buildProcessor(repository); useServerData = true; jsonParser = repository.fhirContext().newJsonParser(); } @@ -255,7 +261,10 @@ public IBaseBundle applyR5() { if (additionalDataId != null) { additionalData(readRepository(repository, additionalDataId)); } - var param = (IParametersAdapter) IAdapterFactory.createAdapterForResource(processor.applyR5( + var proxiedRepo = Repositories.proxy( + repository, useServerData, dataRepository, contentRepository, terminologyRepository); + var applyProcessor = given.buildProcessor(proxiedRepo); + var param = (IParametersAdapter) IAdapterFactory.createAdapterForResource(applyProcessor.applyR5( Eithers.forMiddle3(Ids.newId(repository.fhirContext(), "PlanDefinition", planDefinitionId)), List.of(subjectId), encounterId, @@ -267,12 +276,9 @@ public IBaseBundle applyR5() { null, null, parameters, - useServerData, additionalData, prefetchData, - dataRepository, - contentRepository, - terminologyRepository)); + new LibraryEngine(proxiedRepo, applyProcessor.settings().getEvaluationSettings()))); return (IBaseBundle) param.getParameter().get(0).getResource(); } @@ -284,9 +290,12 @@ public GeneratedCarePlan thenApply() { if (additionalDataId != null) { additionalData(readRepository(repository, additionalDataId)); } + var proxiedRepo = Repositories.proxy( + repository, useServerData, dataRepository, contentRepository, terminologyRepository); + var applyProcessor = given.buildProcessor(proxiedRepo); return new GeneratedCarePlan( repository, - processor.apply( + applyProcessor.apply( Eithers.forMiddle3(Ids.newId(repository.fhirContext(), "PlanDefinition", planDefinitionId)), subjectId, encounterId, @@ -298,12 +307,10 @@ public GeneratedCarePlan thenApply() { null, null, parameters, - useServerData, additionalData, prefetchData, - dataRepository, - contentRepository, - terminologyRepository)); + new LibraryEngine( + proxiedRepo, applyProcessor.settings().getEvaluationSettings()))); } public GeneratedPackage thenPackage() { diff --git a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/questionnaire/TestItemGenerator.java b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/questionnaire/TestItemGenerator.java index b3404ada44..67ffffd177 100644 --- a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/questionnaire/TestItemGenerator.java +++ b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/questionnaire/TestItemGenerator.java @@ -26,6 +26,8 @@ import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.json.JSONException; import org.opencds.cqf.cql.engine.model.ModelResolver; +import org.opencds.cqf.fhir.cr.CrSettings; +import org.opencds.cqf.fhir.cr.measure.r4.NoOpRepositoryProxyFactory; import org.opencds.cqf.fhir.utility.Constants; import org.opencds.cqf.fhir.utility.model.FhirModelResolverCache; import org.opencds.cqf.fhir.utility.monad.Eithers; @@ -66,7 +68,8 @@ public Given repositoryFor(FhirContext fhirContext, String repositoryPath) { } public static QuestionnaireProcessor buildProcessor(IRepository repository) { - return new QuestionnaireProcessor(repository); + return new QuestionnaireProcessor( + repository, CrSettings.getDefault(), null, new NoOpRepositoryProxyFactory()); } public When when() { diff --git a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/questionnaire/TestQuestionnaire.java b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/questionnaire/TestQuestionnaire.java index 0408a7dcb8..66947d558c 100644 --- a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/questionnaire/TestQuestionnaire.java +++ b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/questionnaire/TestQuestionnaire.java @@ -36,6 +36,7 @@ import org.opencds.cqf.fhir.cr.common.IPackageProcessor; import org.opencds.cqf.fhir.cr.helpers.DataRequirementsLibrary; import org.opencds.cqf.fhir.cr.helpers.GeneratedPackage; +import org.opencds.cqf.fhir.cr.measure.r4.NoOpRepositoryProxyFactory; import org.opencds.cqf.fhir.cr.questionnaire.generate.GenerateRequest; import org.opencds.cqf.fhir.cr.questionnaire.generate.IGenerateProcessor; import org.opencds.cqf.fhir.cr.questionnaire.populate.IPopulateProcessor; @@ -116,7 +117,8 @@ public QuestionnaireProcessor buildProcessor(IRepository repository) { .setValuesetExpansionMode(VALUESET_EXPANSION_MODE.PERFORM_NAIVE_EXPANSION); } var crSettings = CrSettings.getDefault().withEvaluationSettings(evaluationSettings); - return new QuestionnaireProcessor(repository, crSettings, operationProcessors); + return new QuestionnaireProcessor( + repository, crSettings, operationProcessors, new NoOpRepositoryProxyFactory()); } public When when() { diff --git a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/questionnaireresponse/TestQuestionnaireResponse.java b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/questionnaireresponse/TestQuestionnaireResponse.java index 988985acaa..889b922e8e 100644 --- a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/questionnaireresponse/TestQuestionnaireResponse.java +++ b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/questionnaireresponse/TestQuestionnaireResponse.java @@ -21,6 +21,7 @@ import org.opencds.cqf.cql.engine.model.ModelResolver; import org.opencds.cqf.fhir.cr.CrSettings; import org.opencds.cqf.fhir.cr.common.IOperationProcessor; +import org.opencds.cqf.fhir.cr.measure.r4.NoOpRepositoryProxyFactory; import org.opencds.cqf.fhir.cr.questionnaireresponse.extract.IExtractProcessor; import org.opencds.cqf.fhir.utility.Ids; import org.opencds.cqf.fhir.utility.model.FhirModelResolverCache; @@ -75,7 +76,8 @@ public Given extractProcessor(IExtractProcessor extractProcessor) { } private QuestionnaireResponseProcessor buildProcessor() { - return new QuestionnaireResponseProcessor(repository, CrSettings.getDefault(), operationProcessors); + return new QuestionnaireResponseProcessor( + repository, CrSettings.getDefault(), operationProcessors, new NoOpRepositoryProxyFactory()); } public When when() { diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/repository/DefaultRepositoryProxyFactory.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/repository/DefaultRepositoryProxyFactory.java new file mode 100644 index 0000000000..c4e99efc2b --- /dev/null +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/repository/DefaultRepositoryProxyFactory.java @@ -0,0 +1,20 @@ +package org.opencds.cqf.fhir.utility.repository; + +import ca.uhn.fhir.repository.IRepository; +import org.hl7.fhir.instance.model.api.IBaseResource; + +/** + * Default implementation that delegates to the static methods in {@link Repositories}. + */ +public class DefaultRepositoryProxyFactory implements RepositoryProxyFactory { + + @Override + public IRepository proxy( + IRepository localRepository, + Boolean useServerData, + IBaseResource dataEndpoint, + IBaseResource contentEndpoint, + IBaseResource terminologyEndpoint) { + return Repositories.proxy(localRepository, useServerData, dataEndpoint, contentEndpoint, terminologyEndpoint); + } +} diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/repository/RepositoryProxyFactory.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/repository/RepositoryProxyFactory.java new file mode 100644 index 0000000000..c815b7c9a1 --- /dev/null +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/repository/RepositoryProxyFactory.java @@ -0,0 +1,24 @@ +package org.opencds.cqf.fhir.utility.repository; + +import ca.uhn.fhir.repository.IRepository; +import org.hl7.fhir.instance.model.api.IBaseResource; + +/** + * Factory for creating proxy repositories that route data, content, and terminology + * requests to different endpoints. This abstraction allows tests to substitute + * in-memory repositories instead of requiring live FHIR servers. + */ +public interface RepositoryProxyFactory { + + /** + * Creates a proxy repository from endpoint resources (e.g., FHIR Endpoint). + * Each non-null endpoint is converted to a repository via + * {@link Repositories#createRestRepository}. + */ + IRepository proxy( + IRepository localRepository, + Boolean useServerData, + IBaseResource dataEndpoint, + IBaseResource contentEndpoint, + IBaseResource terminologyEndpoint); +}