diff --git a/cqf-fhir-cql/src/main/java/org/opencds/cqf/fhir/cql/Engines.java b/cqf-fhir-cql/src/main/java/org/opencds/cqf/fhir/cql/Engines.java index f54d322a5..2abed3146 100644 --- a/cqf-fhir-cql/src/main/java/org/opencds/cqf/fhir/cql/Engines.java +++ b/cqf-fhir-cql/src/main/java/org/opencds/cqf/fhir/cql/Engines.java @@ -27,7 +27,6 @@ import org.opencds.cqf.fhir.api.Repository; import org.opencds.cqf.fhir.cql.cql2elm.content.RepositoryFhirLibrarySourceProvider; import org.opencds.cqf.fhir.cql.cql2elm.util.LibraryVersionSelector; -import org.opencds.cqf.fhir.cql.engine.model.FhirModelResolverCache; import org.opencds.cqf.fhir.cql.engine.parameters.CqlFhirParametersConverter; import org.opencds.cqf.fhir.cql.engine.retrieve.FederatedDataProvider; import org.opencds.cqf.fhir.cql.engine.retrieve.RepositoryRetrieveProvider; @@ -35,6 +34,7 @@ import org.opencds.cqf.fhir.cql.engine.terminology.RepositoryTerminologyProvider; import org.opencds.cqf.fhir.utility.Constants; import org.opencds.cqf.fhir.utility.adapter.AdapterFactory; +import org.opencds.cqf.fhir.utility.model.FhirModelResolverCache; import org.opencds.cqf.fhir.utility.repository.InMemoryFhirRepository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -106,7 +106,7 @@ public static CqlEngine forRepositoryAndSettings( } private static LibrarySourceProvider buildLibrarySource(Repository repository) { - AdapterFactory adapterFactory = getAdapterFactory(repository.fhirContext()); + var adapterFactory = AdapterFactory.forFhirContext(repository.fhirContext()); return new RepositoryFhirLibrarySourceProvider( repository, adapterFactory, new LibraryVersionSelector(adapterFactory)); } @@ -186,22 +186,10 @@ private static Map buildDataProviders( return dataProviders; } - public static AdapterFactory getAdapterFactory(FhirContext fhirContext) { - switch (fhirContext.getVersion().getVersion()) { - case DSTU3: - return new org.opencds.cqf.fhir.utility.adapter.dstu3.AdapterFactory(); - case R4: - return new org.opencds.cqf.fhir.utility.adapter.r4.AdapterFactory(); - case R5: - return new org.opencds.cqf.fhir.utility.adapter.r5.AdapterFactory(); - default: - throw new IllegalArgumentException(String.format("unsupported FHIR version: %s", fhirContext)); - } - } - public static CqlFhirParametersConverter getCqlFhirParametersConverter(FhirContext fhirContext) { var fhirTypeConverter = new FhirTypeConverterFactory().create(fhirContext.getVersion().getVersion()); - return new CqlFhirParametersConverter(fhirContext, getAdapterFactory(fhirContext), fhirTypeConverter); + return new CqlFhirParametersConverter( + fhirContext, AdapterFactory.forFhirContext(fhirContext), fhirTypeConverter); } } diff --git a/cqf-fhir-cql/src/main/java/org/opencds/cqf/fhir/cql/ExtensionResolver.java b/cqf-fhir-cql/src/main/java/org/opencds/cqf/fhir/cql/ExtensionResolver.java index 7221a6a25..ff16b1876 100644 --- a/cqf-fhir-cql/src/main/java/org/opencds/cqf/fhir/cql/ExtensionResolver.java +++ b/cqf-fhir-cql/src/main/java/org/opencds/cqf/fhir/cql/ExtensionResolver.java @@ -10,6 +10,7 @@ import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IIdType; import org.opencds.cqf.fhir.utility.Constants; +import org.opencds.cqf.fhir.utility.CqfExpression; /** * This class is used to resolve any CQFExpression extensions that exist on an extension. diff --git a/cqf-fhir-cql/src/main/java/org/opencds/cqf/fhir/cql/LibraryConstructor.java b/cqf-fhir-cql/src/main/java/org/opencds/cqf/fhir/cql/LibraryConstructor.java index b6a5a13ba..df237b4c9 100644 --- a/cqf-fhir-cql/src/main/java/org/opencds/cqf/fhir/cql/LibraryConstructor.java +++ b/cqf-fhir-cql/src/main/java/org/opencds/cqf/fhir/cql/LibraryConstructor.java @@ -3,6 +3,7 @@ import static java.util.Objects.requireNonNull; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.fhirpath.IFhirPath; import java.util.List; import org.apache.commons.lang3.tuple.Pair; @@ -46,10 +47,15 @@ private void constructExpression(StringBuilder sb, String expression) { sb.append(String.format("%ndefine \"return\":%n %s", expression)); } + private String getFhirVersionString(FhirVersionEnum fhirVersion) { + // The version of the DSTU3 enum is 3.0.2 which the CQL Engine does not support. + return fhirVersion == FhirVersionEnum.DSTU3 ? "3.0.1" : fhirVersion.getFhirVersionString(); + } + private void constructIncludes(StringBuilder sb, List> libraries) { sb.append(String.format( "include FHIRHelpers version '%s' called FHIRHelpers%n", - fhirContext.getVersion().getVersion().getFhirVersionString())); + getFhirVersionString(fhirContext.getVersion().getVersion()))); if (libraries != null) { for (Pair library : libraries) { @@ -93,7 +99,7 @@ private String getTypeDeclaration(String type, Boolean isList) { private void constructUsings(StringBuilder sb) { sb.append(String.format( "using FHIR version '%s'%n", - fhirContext.getVersion().getVersion().getFhirVersionString())); + getFhirVersionString(fhirContext.getVersion().getVersion()))); } private void constructHeader(StringBuilder sb) { diff --git a/cqf-fhir-cql/src/main/java/org/opencds/cqf/fhir/cql/LibraryEngine.java b/cqf-fhir-cql/src/main/java/org/opencds/cqf/fhir/cql/LibraryEngine.java index 137b1ab87..9dc5f7c67 100644 --- a/cqf-fhir-cql/src/main/java/org/opencds/cqf/fhir/cql/LibraryEngine.java +++ b/cqf-fhir-cql/src/main/java/org/opencds/cqf/fhir/cql/LibraryEngine.java @@ -10,6 +10,8 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; import org.cqframework.cql.cql2elm.LibrarySourceProvider; import org.cqframework.cql.cql2elm.StringLibrarySourceProvider; @@ -20,6 +22,8 @@ import org.hl7.fhir.instance.model.api.IBaseParameters; import org.opencds.cqf.fhir.api.Repository; import org.opencds.cqf.fhir.cql.engine.parameters.CqlParameterDefinition; +import org.opencds.cqf.fhir.utility.Canonicals; +import org.opencds.cqf.fhir.utility.CqfExpression; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -161,8 +165,13 @@ public List getExpressionResult( case "text/cql.expression": case "text/cql-expression": case "text/fhirpath": + var libraries = new ArrayList>(); + if (!StringUtils.isBlank(libraryToBeEvaluated)) { + libraries.add(new ImmutablePair( + libraryToBeEvaluated, Canonicals.getIdPart(libraryToBeEvaluated))); + } parametersResult = this.evaluateExpression( - expression, parameters, subjectId, null, bundle, contextParameter, resourceParameter); + expression, parameters, subjectId, libraries, bundle, contextParameter, resourceParameter); // The expression is assumed to be the parameter component name // The expression evaluator creates a library with a single expression defined as "return" results = resolveParameterValues( diff --git a/cqf-fhir-cql/src/main/java/org/opencds/cqf/fhir/cql/cql2elm/content/BaseFhirLibrarySourceProvider.java b/cqf-fhir-cql/src/main/java/org/opencds/cqf/fhir/cql/cql2elm/content/BaseFhirLibrarySourceProvider.java index 9dd2f1029..9f275ef25 100644 --- a/cqf-fhir-cql/src/main/java/org/opencds/cqf/fhir/cql/cql2elm/content/BaseFhirLibrarySourceProvider.java +++ b/cqf-fhir-cql/src/main/java/org/opencds/cqf/fhir/cql/cql2elm/content/BaseFhirLibrarySourceProvider.java @@ -46,6 +46,7 @@ protected InputStream getContentStream(IBaseResource library, String contentType for (ICompositeType attachment : libraryAdapter.getContent()) { AttachmentAdapter attachmentAdapter = this.adapterFactory.createAttachment(attachment); if (attachmentAdapter.getContentType().equals(contentType)) { + // get externalized extension if present and add custom load data return new ByteArrayInputStream(attachmentAdapter.getData()); } } diff --git a/cqf-fhir-cql/src/main/java/org/opencds/cqf/fhir/cql/engine/parameters/CqlFhirParametersConverter.java b/cqf-fhir-cql/src/main/java/org/opencds/cqf/fhir/cql/engine/parameters/CqlFhirParametersConverter.java index 87f49aee4..6c37d1d93 100644 --- a/cqf-fhir-cql/src/main/java/org/opencds/cqf/fhir/cql/engine/parameters/CqlFhirParametersConverter.java +++ b/cqf-fhir-cql/src/main/java/org/opencds/cqf/fhir/cql/engine/parameters/CqlFhirParametersConverter.java @@ -20,10 +20,10 @@ import org.opencds.cqf.cql.engine.execution.ExpressionResult; import org.opencds.cqf.cql.engine.fhir.converter.FhirTypeConverter; import org.opencds.cqf.cql.engine.model.ModelResolver; -import org.opencds.cqf.fhir.cql.engine.model.FhirModelResolverCache; import org.opencds.cqf.fhir.utility.adapter.AdapterFactory; import org.opencds.cqf.fhir.utility.adapter.ParametersAdapter; import org.opencds.cqf.fhir.utility.adapter.ParametersParameterComponentAdapter; +import org.opencds.cqf.fhir.utility.model.FhirModelResolverCache; import org.slf4j.LoggerFactory; public class CqlFhirParametersConverter { diff --git a/cqf-fhir-cql/src/main/java/org/opencds/cqf/fhir/cql/engine/terminology/RepositoryTerminologyProvider.java b/cqf-fhir-cql/src/main/java/org/opencds/cqf/fhir/cql/engine/terminology/RepositoryTerminologyProvider.java index 37eeb89e1..7d3620f1e 100644 --- a/cqf-fhir-cql/src/main/java/org/opencds/cqf/fhir/cql/engine/terminology/RepositoryTerminologyProvider.java +++ b/cqf-fhir-cql/src/main/java/org/opencds/cqf/fhir/cql/engine/terminology/RepositoryTerminologyProvider.java @@ -22,8 +22,8 @@ import org.opencds.cqf.fhir.api.Repository; import org.opencds.cqf.fhir.cql.engine.terminology.TerminologySettings.VALUESET_EXPANSION_MODE; import org.opencds.cqf.fhir.cql.engine.terminology.TerminologySettings.VALUESET_PRE_EXPANSION_MODE; -import org.opencds.cqf.fhir.cql.engine.utility.ValueSets; import org.opencds.cqf.fhir.utility.FhirPathCache; +import org.opencds.cqf.fhir.utility.ValueSets; import org.opencds.cqf.fhir.utility.search.Searches; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/cqf-fhir-cql/src/test/java/org/opencds/cqf/fhir/cql/LibraryEngineTests.java b/cqf-fhir-cql/src/test/java/org/opencds/cqf/fhir/cql/LibraryEngineTests.java index 0d9fd7798..df855222b 100644 --- a/cqf-fhir-cql/src/test/java/org/opencds/cqf/fhir/cql/LibraryEngineTests.java +++ b/cqf-fhir-cql/src/test/java/org/opencds/cqf/fhir/cql/LibraryEngineTests.java @@ -15,17 +15,18 @@ import org.hl7.fhir.r4.model.StringType; import org.hl7.fhir.r4.model.Task; import org.junit.jupiter.api.Test; +import org.opencds.cqf.fhir.api.Repository; +import org.opencds.cqf.fhir.utility.CqfExpression; import org.opencds.cqf.fhir.utility.repository.ig.IgRepository; class LibraryEngineTests { + Repository repository = + new IgRepository(FhirContext.forR4Cached(), Paths.get(getResourcePath(LibraryEngineTests.class))); + LibraryEngine libraryEngine = new LibraryEngine(repository, EvaluationSettings.getDefault()); @Test void fhirPath() { var patientId = "Patient/Patient1"; - var repository = - new IgRepository(FhirContext.forR4Cached(), Paths.get(getResourcePath(LibraryEngineTests.class))); - var libraryEngine = new LibraryEngine(repository, EvaluationSettings.getDefault()); - var params = parameters(); params.addParameter(part("%subject", new Patient().addName(new HumanName().addGiven("Alice")))); params.addParameter(part("%practitioner", new Practitioner().addName(new HumanName().addGiven("Michael")))); @@ -48,10 +49,6 @@ void fhirPath() { @Test void fhirPathWithResource() { var patientId = "Patient/Patient1"; - var repository = - new IgRepository(FhirContext.forR4Cached(), Paths.get(getResourcePath(LibraryEngineTests.class))); - var libraryEngine = new LibraryEngine(repository, EvaluationSettings.getDefault()); - var params = parameters(); params.addParameter(part("%subject", new Patient().addName(new HumanName().addGiven("Alice")))); params.addParameter(part("%practitioner", new Practitioner().addName(new HumanName().addGiven("Michael")))); @@ -67,4 +64,13 @@ void fhirPathWithResource() { "test-code", ((CodeableConcept) result.get(0)).getCodingFirstRep().getCode()); } + + @Test + void expressionWithLibraryReference() { + var patientId = "Patient/Patient1"; + var expression = + new CqfExpression("text/cql", "TestLibrary.testExpression", "http://fhir.test/Library/TestLibrary"); + var result = libraryEngine.resolveExpression(patientId, expression, null, null, null, null); + assertEquals(((StringType) result.get(0)).getValue(), "I am a test"); + } } diff --git a/cqf-fhir-cql/src/test/resources/org/opencds/cqf/fhir/cql/cql/TestLibrary.cql b/cqf-fhir-cql/src/test/resources/org/opencds/cqf/fhir/cql/cql/TestLibrary.cql new file mode 100644 index 000000000..62e821c83 --- /dev/null +++ b/cqf-fhir-cql/src/test/resources/org/opencds/cqf/fhir/cql/cql/TestLibrary.cql @@ -0,0 +1,8 @@ +library TestLibrary version '1.0.0' + +using FHIR version '4.0.1' + +context Patient + +define "testExpression": + 'I am a test' \ No newline at end of file diff --git a/cqf-fhir-cql/src/test/resources/org/opencds/cqf/fhir/cql/resources/Library-TestLibrary.json b/cqf-fhir-cql/src/test/resources/org/opencds/cqf/fhir/cql/resources/Library-TestLibrary.json new file mode 100644 index 000000000..187fbffea --- /dev/null +++ b/cqf-fhir-cql/src/test/resources/org/opencds/cqf/fhir/cql/resources/Library-TestLibrary.json @@ -0,0 +1,13 @@ +{ + "resourceType": "Library", + "id": "TestLibrary", + "url": "http://fhir.test/Library/TestLibrary", + "version": "1.0.0", + "name": "TestLibrary", + "content": [ + { + "contentType": "text/cql", + "url": "../cql/TestLibrary.cql" + } + ] +} \ No newline at end of file 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 854ae7b30..27aed1d8b 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 @@ -18,13 +18,13 @@ import org.opencds.cqf.fhir.cql.EvaluationSettings; import org.opencds.cqf.fhir.cql.ExtensionResolver; import org.opencds.cqf.fhir.cql.LibraryEngine; -import org.opencds.cqf.fhir.cql.engine.model.FhirModelResolverCache; import org.opencds.cqf.fhir.cr.activitydefinition.apply.ApplyProcessor; import org.opencds.cqf.fhir.cr.activitydefinition.apply.ApplyRequest; import org.opencds.cqf.fhir.cr.activitydefinition.apply.IApplyProcessor; import org.opencds.cqf.fhir.cr.activitydefinition.apply.IRequestResolverFactory; import org.opencds.cqf.fhir.cr.common.ResourceResolver; 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.operations.IActivityDefinitionProcessor; diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/common/DynamicValueProcessor.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/common/DynamicValueProcessor.java index ed6dadc7c..8946dd678 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/common/DynamicValueProcessor.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/common/DynamicValueProcessor.java @@ -6,7 +6,7 @@ import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseBackboneElement; import org.hl7.fhir.instance.model.api.IBaseResource; -import org.opencds.cqf.fhir.cql.CqfExpression; +import org.opencds.cqf.fhir.utility.CqfExpression; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/common/ExpressionProcessor.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/common/ExpressionProcessor.java index 49e6d1c40..ddb475552 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/common/ExpressionProcessor.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/common/ExpressionProcessor.java @@ -1,5 +1,6 @@ package org.opencds.cqf.fhir.cr.common; +import ca.uhn.fhir.context.FhirVersionEnum; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -8,8 +9,8 @@ import org.hl7.fhir.instance.model.api.IBaseBackboneElement; import org.hl7.fhir.instance.model.api.IBaseExtension; import org.hl7.fhir.instance.model.api.IBaseParameters; -import org.opencds.cqf.fhir.cql.CqfExpression; import org.opencds.cqf.fhir.utility.Constants; +import org.opencds.cqf.fhir.utility.CqfExpression; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -90,37 +91,42 @@ public List getExpressionResult( .filter(e -> e.getUrl().equals(extensionUrl)) .findFirst() .orElse(null); - if (extension == null) { - return null; - } - switch (request.getFhirVersion()) { - case DSTU3: - var languageExtension = extensions.stream() - .filter(e -> e.getUrl().equals(Constants.CQF_EXPRESSION_LANGUAGE)) - .findFirst() - .orElse(null); - var libraryExtension = extensions.stream() - .filter(e -> e.getUrl().equals(Constants.CQF_LIBRARY)) - .findFirst() - .orElse(null); - return new CqfExpression( - languageExtension == null - ? null - : languageExtension.getValue().toString(), - extension.getValue().toString(), - libraryExtension == null - ? request.getDefaultLibraryUrl() - : libraryExtension.getValue().toString()); - case R4: - return CqfExpression.of( - (org.hl7.fhir.r4.model.Expression) extension.getValue(), request.getDefaultLibraryUrl()); - case R5: - return CqfExpression.of( - (org.hl7.fhir.r5.model.Expression) extension.getValue(), request.getDefaultLibraryUrl()); + return extension == null ? null : CqfExpression.of(extension, request.getDefaultLibraryUrl()); + // if (extension == null) { + // return null; + // } + // switch (request.getFhirVersion()) { + // case DSTU3: + // // var languageExtension = extensions.stream() + // var languageExtension = extension.getExtension().stream() + // .map(e -> (IBaseExtension) e) + // .filter(e -> e.getUrl().equals(Constants.CQF_EXPRESSION_LANGUAGE)) + // .findFirst() + // .orElse(null); + // // var libraryExtension = extensions.stream() + // var libraryExtension = extension.getExtension().stream() + // .map(e -> (IBaseExtension) e) + // .filter(e -> e.getUrl().equals(Constants.CQF_LIBRARY)) + // .findFirst() + // .orElse(null); + // return new CqfExpression( + // languageExtension == null + // ? null + // : languageExtension.getValue().toString(), + // extension.getValue().toString(), + // libraryExtension == null + // ? request.getDefaultLibraryUrl() + // : libraryExtension.getValue().toString()); + // case R4: + // return CqfExpression.of( + // (org.hl7.fhir.r4.model.Expression) extension.getValue(), request.getDefaultLibraryUrl()); + // case R5: + // return CqfExpression.of( + // (org.hl7.fhir.r5.model.Expression) extension.getValue(), request.getDefaultLibraryUrl()); - default: - return null; - } + // default: + // return null; + // } } /** @@ -166,7 +172,10 @@ public CqfExpression getItemInitialExpression(IOperationRequest request, IBaseBa if (!item.hasExtension()) { return null; } - var cqfExpression = getCqfExpression(request, item.getExtension(), Constants.CQF_EXPRESSION); + var expressionExtensionUrl = request.getFhirVersion() == FhirVersionEnum.DSTU3 + ? Constants.CQIF_CQL_EXPRESSION + : Constants.CQF_EXPRESSION; + var cqfExpression = getCqfExpression(request, item.getExtension(), expressionExtensionUrl); return cqfExpression != null ? cqfExpression : getCqfExpression(request, item.getExtension(), Constants.SDC_QUESTIONNAIRE_INITIAL_EXPRESSION); diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/common/IPackageProcessor.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/common/IPackageProcessor.java index 215b05dd5..cd7330390 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/common/IPackageProcessor.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/common/IPackageProcessor.java @@ -1,10 +1,15 @@ package org.opencds.cqf.fhir.cr.common; import org.hl7.fhir.instance.model.api.IBaseBundle; +import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IBaseResource; public interface IPackageProcessor { + @Deprecated IBaseBundle packageResource(IBaseResource resource); + @Deprecated IBaseBundle packageResource(IBaseResource resource, String method); + + IBaseBundle packageResource(IBaseResource resource, IBaseParameters parameters); } diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/common/PackageProcessor.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/common/PackageProcessor.java new file mode 100644 index 000000000..dc191e03a --- /dev/null +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/common/PackageProcessor.java @@ -0,0 +1,48 @@ +package org.opencds.cqf.fhir.cr.common; + +import static org.opencds.cqf.fhir.utility.Parameters.newBooleanPart; +import static org.opencds.cqf.fhir.utility.Parameters.newParameters; + +import ca.uhn.fhir.context.FhirVersionEnum; +import org.hl7.fhir.instance.model.api.IBaseBundle; +import org.hl7.fhir.instance.model.api.IBaseParameters; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IDomainResource; +import org.opencds.cqf.fhir.api.Repository; +import org.opencds.cqf.fhir.utility.adapter.AdapterFactory; +import org.opencds.cqf.fhir.utility.visitor.PackageVisitor; + +public class PackageProcessor implements IPackageProcessor { + protected final Repository repository; + protected final FhirVersionEnum fhirVersion; + protected final PackageVisitor packageVisitor; + + public PackageProcessor(Repository repository) { + this.repository = repository; + this.fhirVersion = this.repository.fhirContext().getVersion().getVersion(); + packageVisitor = new PackageVisitor(this.repository.fhirContext()); + } + + @Override + public IBaseBundle packageResource(IBaseResource resource) { + return packageResource(resource, "POST"); + } + + @Override + public IBaseBundle packageResource(IBaseResource resource, String method) { + return packageResource( + resource, + newParameters( + repository.fhirContext(), + "package-parameters", + newBooleanPart(repository.fhirContext(), "isPut", method.equals("PUT")))); + } + + @Override + public IBaseBundle packageResource(IBaseResource resource, IBaseParameters parameters) { + return (IBaseBundle) packageVisitor.visit( + AdapterFactory.forFhirVersion(fhirVersion).createKnowledgeArtifactAdapter((IDomainResource) resource), + repository, + parameters); + } +} 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 new file mode 100644 index 000000000..06c82e427 --- /dev/null +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/library/LibraryProcessor.java @@ -0,0 +1,78 @@ +package org.opencds.cqf.fhir.cr.library; + +import static java.util.Objects.requireNonNull; +import static org.opencds.cqf.fhir.utility.Parameters.newBooleanPart; +import static org.opencds.cqf.fhir.utility.Parameters.newParameters; + +import ca.uhn.fhir.context.FhirVersionEnum; +import org.hl7.fhir.instance.model.api.IBaseBundle; +import org.hl7.fhir.instance.model.api.IBaseParameters; +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.cql.engine.model.ModelResolver; +import org.opencds.cqf.fhir.api.Repository; +import org.opencds.cqf.fhir.cql.EvaluationSettings; +import org.opencds.cqf.fhir.cr.common.IPackageProcessor; +import org.opencds.cqf.fhir.cr.common.PackageProcessor; +import org.opencds.cqf.fhir.cr.common.ResourceResolver; +import org.opencds.cqf.fhir.utility.model.FhirModelResolverCache; +import org.opencds.cqf.fhir.utility.monad.Either3; + +public class LibraryProcessor { + protected final ModelResolver modelResolver; + protected final FhirVersionEnum fhirVersion; + protected final IPackageProcessor packageProcessor; + protected Repository repository; + protected EvaluationSettings evaluationSettings; + + public LibraryProcessor(Repository repository) { + this(repository, EvaluationSettings.getDefault()); + } + + public LibraryProcessor(Repository repository, EvaluationSettings evaluationSettings) { + this(repository, evaluationSettings, null); + } + + public LibraryProcessor( + Repository repository, EvaluationSettings evaluationSettings, IPackageProcessor packageProcessor) { + this.repository = requireNonNull(repository, "repository can not be null"); + this.evaluationSettings = requireNonNull(evaluationSettings, "evaluationSettings can not be null"); + fhirVersion = this.repository.fhirContext().getVersion().getVersion(); + modelResolver = FhirModelResolverCache.resolverForVersion(fhirVersion); + this.packageProcessor = packageProcessor != null ? packageProcessor : new PackageProcessor(this.repository); + } + + public EvaluationSettings evaluationSettings() { + return evaluationSettings; + } + + protected , R extends IBaseResource> R resolveLibrary( + Either3 library) { + return new ResourceResolver("Library", repository).resolve(library); + } + + public , R extends IBaseResource> IBaseBundle packageLibrary( + Either3 library) { + return packageLibrary(library, false); + } + + public , R extends IBaseResource> IBaseBundle packageLibrary( + Either3 library, boolean isPut) { + return packageLibrary( + library, + newParameters( + repository.fhirContext(), + "package-parameters", + newBooleanPart(repository.fhirContext(), "isPut", isPut))); + } + + public , R extends IBaseResource> IBaseBundle packageLibrary( + Either3 library, IBaseParameters parameters) { + return packageLibrary(resolveLibrary(library), parameters); + } + + public IBaseBundle packageLibrary(IBaseResource questionnaire, IBaseParameters parameters) { + return packageProcessor.packageResource(questionnaire, parameters); + } +} diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4DataRequirementsService.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4DataRequirementsService.java index 1cfbd2bfc..56ccf055d 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4DataRequirementsService.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4DataRequirementsService.java @@ -46,7 +46,6 @@ import org.opencds.cqf.fhir.api.Repository; import org.opencds.cqf.fhir.cql.cql2elm.content.RepositoryFhirLibrarySourceProvider; import org.opencds.cqf.fhir.cql.cql2elm.util.LibraryVersionSelector; -import org.opencds.cqf.fhir.cql.engine.model.FhirModelResolverCache; import org.opencds.cqf.fhir.cql.engine.terminology.RepositoryTerminologyProvider; import org.opencds.cqf.fhir.cr.measure.MeasureEvaluationOptions; import org.opencds.cqf.fhir.cr.measure.helper.DateHelper; @@ -56,6 +55,7 @@ import org.opencds.cqf.fhir.utility.Libraries; import org.opencds.cqf.fhir.utility.adapter.LibraryAdapter; import org.opencds.cqf.fhir.utility.adapter.r4.AdapterFactory; +import org.opencds.cqf.fhir.utility.model.FhirModelResolverCache; import org.opencds.cqf.fhir.utility.search.Searches; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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 63ed74aac..873cdcfba 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 @@ -1,6 +1,8 @@ package org.opencds.cqf.fhir.cr.plandefinition; import static java.util.Objects.requireNonNull; +import static org.opencds.cqf.fhir.utility.Parameters.newBooleanPart; +import static org.opencds.cqf.fhir.utility.Parameters.newParameters; import static org.opencds.cqf.fhir.utility.repository.Repositories.createRestRepository; import static org.opencds.cqf.fhir.utility.repository.Repositories.proxy; @@ -16,15 +18,15 @@ import org.opencds.cqf.fhir.api.Repository; import org.opencds.cqf.fhir.cql.EvaluationSettings; import org.opencds.cqf.fhir.cql.LibraryEngine; -import org.opencds.cqf.fhir.cql.engine.model.FhirModelResolverCache; import org.opencds.cqf.fhir.cr.activitydefinition.apply.IRequestResolverFactory; import org.opencds.cqf.fhir.cr.common.IPackageProcessor; +import org.opencds.cqf.fhir.cr.common.PackageProcessor; import org.opencds.cqf.fhir.cr.common.ResourceResolver; import org.opencds.cqf.fhir.cr.plandefinition.apply.ApplyProcessor; import org.opencds.cqf.fhir.cr.plandefinition.apply.ApplyRequest; import org.opencds.cqf.fhir.cr.plandefinition.apply.IApplyProcessor; -import org.opencds.cqf.fhir.cr.plandefinition.packages.PackageProcessor; import org.opencds.cqf.fhir.utility.Ids; +import org.opencds.cqf.fhir.utility.model.FhirModelResolverCache; import org.opencds.cqf.fhir.utility.monad.Either3; @SuppressWarnings({"unused", "squid:S107", "squid:S1172"}) @@ -83,12 +85,26 @@ protected , R extends IBaseResource> R resolveP public , R extends IBaseResource> IBaseBundle packagePlanDefinition( Either3 planDefinition) { - return packageProcessor.packageResource(resolvePlanDefinition(planDefinition)); + return packagePlanDefinition(planDefinition, false); } public , R extends IBaseResource> IBaseBundle packagePlanDefinition( Either3 planDefinition, boolean isPut) { - return packageProcessor.packageResource(resolvePlanDefinition(planDefinition), isPut ? "PUT" : "POST"); + return packagePlanDefinition( + planDefinition, + newParameters( + repository.fhirContext(), + "package-parameters", + newBooleanPart(repository.fhirContext(), "isPut", isPut))); + } + + public , R extends IBaseResource> IBaseBundle packagePlanDefinition( + Either3 planDefinition, IBaseParameters parameters) { + return packagePlanDefinition(resolvePlanDefinition(planDefinition), parameters); + } + + public IBaseBundle packagePlanDefinition(IBaseResource planDefinition, IBaseParameters parameters) { + return packageProcessor.packageResource(planDefinition, parameters); } protected , R extends IBaseResource> ApplyRequest buildApplyRequest( diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/plandefinition/packages/PackageProcessor.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/plandefinition/packages/PackageProcessor.java deleted file mode 100644 index 788c46ed6..000000000 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/plandefinition/packages/PackageProcessor.java +++ /dev/null @@ -1,131 +0,0 @@ -package org.opencds.cqf.fhir.cr.plandefinition.packages; - -import ca.uhn.fhir.context.FhirVersionEnum; -import org.hl7.fhir.instance.model.api.IBaseBundle; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.opencds.cqf.fhir.api.Repository; -import org.opencds.cqf.fhir.cr.common.IPackageProcessor; -import org.opencds.cqf.fhir.utility.BundleHelper; - -public class PackageProcessor implements IPackageProcessor { - protected final Repository repository; - protected final FhirVersionEnum fhirVersion; - - public PackageProcessor(Repository repository) { - this.repository = repository; - this.fhirVersion = repository.fhirContext().getVersion().getVersion(); - } - - @Override - public IBaseBundle packageResource(IBaseResource resource) { - return packageResource(resource, "POST"); - } - - @Override - public IBaseBundle packageResource(IBaseResource resource, String method) { - return packagePlanDefinition(resource, method.equals("PUT")); - } - - protected IBaseBundle packagePlanDefinition(IBaseResource planDefinition, boolean isPut) { - switch (fhirVersion) { - case DSTU3: - return packageDstu3((org.hl7.fhir.dstu3.model.PlanDefinition) planDefinition, isPut); - case R4: - return packageR4((org.hl7.fhir.r4.model.PlanDefinition) planDefinition, isPut); - case R5: - return packageR5((org.hl7.fhir.r5.model.PlanDefinition) planDefinition, isPut); - - default: - return null; - } - } - - protected IBaseBundle packageDstu3(org.hl7.fhir.dstu3.model.PlanDefinition planDefinition, boolean isPut) { - var packageBundle = new org.hl7.fhir.dstu3.model.Bundle(); - packageBundle.setType(org.hl7.fhir.dstu3.model.Bundle.BundleType.TRANSACTION); - BundleHelper.addEntry( - packageBundle, org.opencds.cqf.fhir.utility.dstu3.PackageHelper.createEntry(planDefinition, isPut)); - // The CPG IG specifies a main cql library for a PlanDefinition - var libraryCanonical = planDefinition.hasLibrary() - ? new org.hl7.fhir.dstu3.model.StringType( - planDefinition.getLibrary().get(0).getReference()) - : null; - if (libraryCanonical != null) { - var library = (org.hl7.fhir.dstu3.model.Library) - org.opencds.cqf.fhir.utility.dstu3.SearchHelper.searchRepositoryByCanonical( - repository, libraryCanonical); - if (library != null) { - BundleHelper.addEntry( - packageBundle, org.opencds.cqf.fhir.utility.dstu3.PackageHelper.createEntry(library, isPut)); - if (library.hasRelatedArtifact()) { - org.opencds.cqf.fhir.utility.dstu3.PackageHelper.addRelatedArtifacts( - packageBundle, library.getRelatedArtifact(), repository, isPut); - } - } - } - if (planDefinition.hasRelatedArtifact()) { - org.opencds.cqf.fhir.utility.dstu3.PackageHelper.addRelatedArtifacts( - packageBundle, planDefinition.getRelatedArtifact(), repository, isPut); - } - - return packageBundle; - } - - protected IBaseBundle packageR4(org.hl7.fhir.r4.model.PlanDefinition planDefinition, boolean isPut) { - var packageBundle = new org.hl7.fhir.r4.model.Bundle(); - packageBundle.setType(org.hl7.fhir.r4.model.Bundle.BundleType.TRANSACTION); - packageBundle.addEntry((org.hl7.fhir.r4.model.Bundle.BundleEntryComponent) - org.opencds.cqf.fhir.utility.PackageHelper.createEntry(planDefinition, isPut)); - // The CPG IG specifies a main cql library for a PlanDefinition - var libraryCanonical = - planDefinition.hasLibrary() ? planDefinition.getLibrary().get(0) : null; - if (libraryCanonical != null) { - var library = (org.hl7.fhir.r4.model.Library) - org.opencds.cqf.fhir.utility.r4.SearchHelper.searchRepositoryByCanonical( - repository, libraryCanonical); - if (library != null) { - packageBundle.addEntry((org.hl7.fhir.r4.model.Bundle.BundleEntryComponent) - org.opencds.cqf.fhir.utility.PackageHelper.createEntry(library, isPut)); - if (library.hasRelatedArtifact()) { - org.opencds.cqf.fhir.utility.r4.PackageHelper.addRelatedArtifacts( - packageBundle, library.getRelatedArtifact(), repository, isPut); - } - } - } - if (planDefinition.hasRelatedArtifact()) { - org.opencds.cqf.fhir.utility.r4.PackageHelper.addRelatedArtifacts( - packageBundle, planDefinition.getRelatedArtifact(), repository, isPut); - } - - return packageBundle; - } - - protected IBaseBundle packageR5(org.hl7.fhir.r5.model.PlanDefinition planDefinition, boolean isPut) { - var packageBundle = new org.hl7.fhir.r5.model.Bundle(); - packageBundle.setType(org.hl7.fhir.r5.model.Bundle.BundleType.TRANSACTION); - packageBundle.addEntry((org.hl7.fhir.r5.model.Bundle.BundleEntryComponent) - org.opencds.cqf.fhir.utility.PackageHelper.createEntry(planDefinition, isPut)); - // The CPG IG specifies a main cql library for a PlanDefinition - var libraryCanonical = - planDefinition.hasLibrary() ? planDefinition.getLibrary().get(0) : null; - if (libraryCanonical != null) { - var library = (org.hl7.fhir.r5.model.Library) - org.opencds.cqf.fhir.utility.r5.SearchHelper.searchRepositoryByCanonical( - repository, libraryCanonical); - if (library != null) { - packageBundle.addEntry((org.hl7.fhir.r5.model.Bundle.BundleEntryComponent) - org.opencds.cqf.fhir.utility.PackageHelper.createEntry(library, isPut)); - if (library.hasRelatedArtifact()) { - org.opencds.cqf.fhir.utility.r5.PackageHelper.addRelatedArtifacts( - packageBundle, library.getRelatedArtifact(), repository, isPut); - } - } - } - if (planDefinition.hasRelatedArtifact()) { - org.opencds.cqf.fhir.utility.r5.PackageHelper.addRelatedArtifacts( - packageBundle, planDefinition.getRelatedArtifact(), repository, isPut); - } - - return packageBundle; - } -} 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 46d8c83fe..de15c5e34 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 @@ -1,6 +1,8 @@ package org.opencds.cqf.fhir.cr.questionnaire; import static java.util.Objects.requireNonNull; +import static org.opencds.cqf.fhir.utility.Parameters.newBooleanPart; +import static org.opencds.cqf.fhir.utility.Parameters.newParameters; import static org.opencds.cqf.fhir.utility.repository.Repositories.createRestRepository; import static org.opencds.cqf.fhir.utility.repository.Repositories.proxy; @@ -15,17 +17,17 @@ import org.opencds.cqf.fhir.api.Repository; import org.opencds.cqf.fhir.cql.EvaluationSettings; import org.opencds.cqf.fhir.cql.LibraryEngine; -import org.opencds.cqf.fhir.cql.engine.model.FhirModelResolverCache; import org.opencds.cqf.fhir.cr.common.IPackageProcessor; +import org.opencds.cqf.fhir.cr.common.PackageProcessor; import org.opencds.cqf.fhir.cr.common.ResourceResolver; import org.opencds.cqf.fhir.cr.questionnaire.generate.GenerateProcessor; 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.packages.PackageProcessor; import org.opencds.cqf.fhir.cr.questionnaire.populate.IPopulateProcessor; import org.opencds.cqf.fhir.cr.questionnaire.populate.PopulateProcessor; import org.opencds.cqf.fhir.cr.questionnaire.populate.PopulateRequest; import org.opencds.cqf.fhir.utility.Ids; +import org.opencds.cqf.fhir.utility.model.FhirModelResolverCache; import org.opencds.cqf.fhir.utility.monad.Either3; public class QuestionnaireProcessor { @@ -168,20 +170,26 @@ public IBaseResource generateQuestionnaire(GenerateRequest request, String id) { public > IBaseBundle packageQuestionnaire( Either3 questionnaire) { - return packageQuestionnaire(resolveQuestionnaire(questionnaire)); + return packageQuestionnaire(questionnaire, false); } - public IBaseBundle packageQuestionnaire(IBaseResource questionnaire) { - return packageProcessor.packageResource(questionnaire); + public > IBaseBundle packageQuestionnaire( + Either3 questionnaire, boolean isPut) { + return packageQuestionnaire( + questionnaire, + newParameters( + repository.fhirContext(), + "package-parameters", + newBooleanPart(repository.fhirContext(), "isPut", isPut))); } public > IBaseBundle packageQuestionnaire( - Either3 questionnaire, boolean isPut) { - return packageQuestionnaire(resolveQuestionnaire(questionnaire), isPut); + Either3 questionnaire, IBaseParameters parameters) { + return packageQuestionnaire(resolveQuestionnaire(questionnaire), parameters); } - public IBaseBundle packageQuestionnaire(IBaseResource questionnaire, boolean isPut) { - return packageProcessor.packageResource(questionnaire, isPut ? "PUT" : "POST"); + public IBaseBundle packageQuestionnaire(IBaseResource questionnaire, IBaseParameters parameters) { + return packageProcessor.packageResource(questionnaire, parameters); } public PopulateRequest buildPopulateRequest( diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/questionnaire/generate/ElementHasCqfExpression.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/questionnaire/generate/ElementHasCqfExpression.java index bf9d378c4..3f2c20bec 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/questionnaire/generate/ElementHasCqfExpression.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/questionnaire/generate/ElementHasCqfExpression.java @@ -3,6 +3,7 @@ import static org.opencds.cqf.fhir.cr.common.ExtensionBuilders.buildReference; import static org.opencds.cqf.fhir.cr.questionnaire.generate.IElementProcessor.createInitial; +import ca.uhn.fhir.context.FhirVersionEnum; import java.util.List; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IBase; @@ -26,7 +27,10 @@ public ElementHasCqfExpression(ExpressionProcessor expressionProcessor) { public IBaseBackboneElement addProperties( IOperationRequest request, List> extensions, IBaseBackboneElement questionnaireItem) { - final var expression = expressionProcessor.getCqfExpression(request, extensions, Constants.CQF_EXPRESSION); + final var expressionExtensionUrl = request.getFhirVersion() == FhirVersionEnum.DSTU3 + ? Constants.CQIF_CQL_EXPRESSION + : Constants.CQF_EXPRESSION; + final var expression = expressionProcessor.getCqfExpression(request, extensions, expressionExtensionUrl); final List results = expressionProcessor.getExpressionResult(request, expression); results.forEach(result -> { if (IAnyResource.class.isAssignableFrom(result.getClass())) { diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/questionnaire/generate/GenerateRequest.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/questionnaire/generate/GenerateRequest.java index 909e8a1a6..f7fa5b0f4 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/questionnaire/generate/GenerateRequest.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/questionnaire/generate/GenerateRequest.java @@ -2,15 +2,18 @@ import ca.uhn.fhir.context.FhirVersionEnum; import java.util.List; +import org.hl7.fhir.dstu3.model.Reference; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.ICompositeType; import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.opencds.cqf.cql.engine.model.ModelResolver; import org.opencds.cqf.fhir.cql.LibraryEngine; import org.opencds.cqf.fhir.cr.common.IQuestionnaireRequest; +import org.opencds.cqf.fhir.utility.Constants; public class GenerateRequest implements IQuestionnaireRequest { private final Boolean supportedOnly; @@ -47,7 +50,7 @@ public GenerateRequest( this.modelResolver = modelResolver; fhirVersion = this.libraryEngine.getRepository().fhirContext().getVersion().getVersion(); - defaultLibraryUrl = ""; + defaultLibraryUrl = resolveDefaultLibraryUrl(); profileUrl = resolvePathString(this.profile, "url"); } @@ -151,4 +154,18 @@ public void setOperationOutcome(IBaseOperationOutcome operationOutcome) { // Errors during Questionnaire generation manifest as error items throw new UnsupportedOperationException("Unimplemented method 'setOperationOutcome'"); } + + @SuppressWarnings("unchecked") + protected final String resolveDefaultLibraryUrl() { + var libraryExt = getExtensions(profile).stream() + .filter(e -> e.getUrl() + .equals(fhirVersion == FhirVersionEnum.DSTU3 ? Constants.CQIF_LIBRARY : Constants.CQF_LIBRARY)) + .findFirst() + .orElse(null); + return libraryExt == null + ? null + : fhirVersion == FhirVersionEnum.DSTU3 + ? ((Reference) libraryExt.getValue()).getReference() + : ((IPrimitiveType) libraryExt.getValue()).getValue(); + } } diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/questionnaire/generate/ItemGenerator.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/questionnaire/generate/ItemGenerator.java index d091f93e9..a41f757b2 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/questionnaire/generate/ItemGenerator.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/questionnaire/generate/ItemGenerator.java @@ -12,11 +12,11 @@ import org.hl7.fhir.instance.model.api.ICompositeType; import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.opencds.cqf.fhir.api.Repository; -import org.opencds.cqf.fhir.cql.CqfExpression; import org.opencds.cqf.fhir.cr.common.ExpressionProcessor; import org.opencds.cqf.fhir.cr.common.ExtensionProcessor; import org.opencds.cqf.fhir.cr.common.ResolveExpressionException; import org.opencds.cqf.fhir.utility.Constants; +import org.opencds.cqf.fhir.utility.CqfExpression; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/questionnaire/generate/dstu3/ElementProcessor.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/questionnaire/generate/dstu3/ElementProcessor.java index c6d53efd6..7d98f5fda 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/questionnaire/generate/dstu3/ElementProcessor.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/questionnaire/generate/dstu3/ElementProcessor.java @@ -58,7 +58,7 @@ public IBaseBackboneElement processElement( elementHasDefaultValue.addProperties(request, element.getPattern(), item); } else if (element.hasDefaultValue()) { elementHasDefaultValue.addProperties(request, element.getDefaultValue(), item); - } else if (element.hasExtension(Constants.CQF_EXPRESSION)) { + } else if (element.hasExtension(Constants.CQIF_CQL_EXPRESSION)) { elementHasCqfExpression.addProperties(request, request.getExtensions(element), item); } else if (caseFeature != null) { var pathValue = elementHasCaseFeature.getPathValue(request, caseFeature, element); diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/questionnaire/packages/PackageProcessor.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/questionnaire/packages/PackageProcessor.java deleted file mode 100644 index e7a199156..000000000 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/questionnaire/packages/PackageProcessor.java +++ /dev/null @@ -1,119 +0,0 @@ -package org.opencds.cqf.fhir.cr.questionnaire.packages; - -import ca.uhn.fhir.context.FhirVersionEnum; -import org.hl7.fhir.instance.model.api.IBaseBundle; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.opencds.cqf.fhir.api.Repository; -import org.opencds.cqf.fhir.cr.common.IPackageProcessor; -import org.opencds.cqf.fhir.utility.BundleHelper; -import org.opencds.cqf.fhir.utility.Constants; - -public class PackageProcessor implements IPackageProcessor { - protected final Repository repository; - protected final FhirVersionEnum fhirVersion; - - public PackageProcessor(Repository repository) { - this.repository = repository; - this.fhirVersion = repository.fhirContext().getVersion().getVersion(); - } - - @Override - public IBaseBundle packageResource(IBaseResource resource) { - return packageResource(resource, "POST"); - } - - @Override - public IBaseBundle packageResource(IBaseResource resource, String method) { - return packageQuestionnaire(resource, method.equals("PUT")); - } - - protected IBaseBundle packageQuestionnaire(IBaseResource questionnaire, boolean isPut) { - switch (fhirVersion) { - case DSTU3: - return packageDstu3(questionnaire, isPut); - case R4: - return packageR4(questionnaire, isPut); - case R5: - return packageR5(questionnaire, isPut); - - default: - return null; - } - } - - protected IBaseBundle packageDstu3(IBaseResource questionnaire, Boolean isPut) { - var bundle = new org.hl7.fhir.dstu3.model.Bundle(); - bundle.setType(org.hl7.fhir.dstu3.model.Bundle.BundleType.TRANSACTION); - BundleHelper.addEntry( - bundle, - org.opencds.cqf.fhir.utility.dstu3.PackageHelper.createEntry( - (org.hl7.fhir.dstu3.model.Resource) questionnaire, isPut)); - var libraryExtension = - ((org.hl7.fhir.dstu3.model.Questionnaire) questionnaire).getExtensionByUrl(Constants.CQF_LIBRARY); - if (libraryExtension != null) { - var libraryCanonical = (org.hl7.fhir.dstu3.model.UriType) libraryExtension.getValue(); - var library = (org.hl7.fhir.dstu3.model.Library) - org.opencds.cqf.fhir.utility.dstu3.SearchHelper.searchRepositoryByCanonical( - repository, libraryCanonical); - if (library != null) { - BundleHelper.addEntry( - bundle, org.opencds.cqf.fhir.utility.dstu3.PackageHelper.createEntry(library, isPut)); - if (library.hasRelatedArtifact()) { - org.opencds.cqf.fhir.utility.dstu3.PackageHelper.addRelatedArtifacts( - bundle, library.getRelatedArtifact(), repository, isPut); - } - } - } - return bundle; - } - - protected IBaseBundle packageR4(IBaseResource questionnaire, Boolean isPut) { - var bundle = new org.hl7.fhir.r4.model.Bundle(); - bundle.setType(org.hl7.fhir.r4.model.Bundle.BundleType.TRANSACTION); - bundle.addEntry((org.hl7.fhir.r4.model.Bundle.BundleEntryComponent) - org.opencds.cqf.fhir.utility.PackageHelper.createEntry( - (org.hl7.fhir.r4.model.Resource) questionnaire, isPut)); - var libraryExtension = - ((org.hl7.fhir.r4.model.Questionnaire) questionnaire).getExtensionByUrl(Constants.CQF_LIBRARY); - if (libraryExtension != null) { - var libraryCanonical = (org.hl7.fhir.r4.model.CanonicalType) libraryExtension.getValue(); - var library = (org.hl7.fhir.r4.model.Library) - org.opencds.cqf.fhir.utility.r4.SearchHelper.searchRepositoryByCanonical( - repository, libraryCanonical); - if (library != null) { - bundle.addEntry((org.hl7.fhir.r4.model.Bundle.BundleEntryComponent) - org.opencds.cqf.fhir.utility.PackageHelper.createEntry(library, isPut)); - if (library.hasRelatedArtifact()) { - org.opencds.cqf.fhir.utility.r4.PackageHelper.addRelatedArtifacts( - bundle, library.getRelatedArtifact(), repository, isPut); - } - } - } - return bundle; - } - - protected IBaseBundle packageR5(IBaseResource questionnaire, Boolean isPut) { - var bundle = new org.hl7.fhir.r5.model.Bundle(); - bundle.setType(org.hl7.fhir.r5.model.Bundle.BundleType.TRANSACTION); - bundle.addEntry((org.hl7.fhir.r5.model.Bundle.BundleEntryComponent) - org.opencds.cqf.fhir.utility.PackageHelper.createEntry( - (org.hl7.fhir.r5.model.Resource) questionnaire, isPut)); - var libraryExtension = - ((org.hl7.fhir.r5.model.Questionnaire) questionnaire).getExtensionByUrl(Constants.CQF_LIBRARY); - if (libraryExtension != null) { - var libraryCanonical = (org.hl7.fhir.r5.model.CanonicalType) libraryExtension.getValue(); - var library = (org.hl7.fhir.r5.model.Library) - org.opencds.cqf.fhir.utility.r5.SearchHelper.searchRepositoryByCanonical( - repository, libraryCanonical); - if (library != null) { - bundle.addEntry((org.hl7.fhir.r5.model.Bundle.BundleEntryComponent) - org.opencds.cqf.fhir.utility.PackageHelper.createEntry(library, isPut)); - if (library.hasRelatedArtifact()) { - org.opencds.cqf.fhir.utility.r5.PackageHelper.addRelatedArtifacts( - bundle, library.getRelatedArtifact(), repository, isPut); - } - } - } - return bundle; - } -} diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/questionnaire/populate/PopulateRequest.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/questionnaire/populate/PopulateRequest.java index 0349f2cb4..0dd544fb6 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/questionnaire/populate/PopulateRequest.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/questionnaire/populate/PopulateRequest.java @@ -3,6 +3,7 @@ import static com.google.common.base.Preconditions.checkNotNull; import ca.uhn.fhir.context.FhirVersionEnum; +import org.hl7.fhir.dstu3.model.Reference; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; import org.hl7.fhir.instance.model.api.IBaseParameters; @@ -117,9 +118,14 @@ public void setOperationOutcome(IBaseOperationOutcome operationOutcome) { @SuppressWarnings("unchecked") protected final String resolveDefaultLibraryUrl() { var libraryExt = getExtensions(questionnaire).stream() - .filter(e -> e.getUrl().equals(Constants.CQF_LIBRARY)) + .filter(e -> e.getUrl() + .equals(fhirVersion == FhirVersionEnum.DSTU3 ? Constants.CQIF_LIBRARY : Constants.CQF_LIBRARY)) .findFirst() .orElse(null); - return libraryExt == null ? null : ((IPrimitiveType) libraryExt.getValue()).getValue(); + return libraryExt == null + ? null + : fhirVersion == FhirVersionEnum.DSTU3 + ? ((Reference) libraryExt.getValue()).getReference() + : ((IPrimitiveType) libraryExt.getValue()).getValue(); } } diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/questionnaire/populate/ProcessItemWithContext.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/questionnaire/populate/ProcessItemWithContext.java index cad851f59..afc1aaa54 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/questionnaire/populate/ProcessItemWithContext.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/questionnaire/populate/ProcessItemWithContext.java @@ -13,10 +13,10 @@ import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseBackboneElement; import org.hl7.fhir.instance.model.api.IPrimitiveType; -import org.opencds.cqf.fhir.cql.CqfExpression; import org.opencds.cqf.fhir.cr.common.ExpressionProcessor; import org.opencds.cqf.fhir.cr.common.ResolveExpressionException; import org.opencds.cqf.fhir.utility.Constants; +import org.opencds.cqf.fhir.utility.CqfExpression; public class ProcessItemWithContext { private final ExpressionProcessor expressionProcessor; 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 6a9f07667..56bfabcad 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 @@ -16,12 +16,12 @@ import org.opencds.cqf.fhir.api.Repository; import org.opencds.cqf.fhir.cql.EvaluationSettings; import org.opencds.cqf.fhir.cql.LibraryEngine; -import org.opencds.cqf.fhir.cql.engine.model.FhirModelResolverCache; import org.opencds.cqf.fhir.cr.common.ResourceResolver; import org.opencds.cqf.fhir.cr.questionnaireresponse.extract.ExtractProcessor; import org.opencds.cqf.fhir.cr.questionnaireresponse.extract.ExtractRequest; import org.opencds.cqf.fhir.cr.questionnaireresponse.extract.IExtractProcessor; 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.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/common/DynamicValueProcessorTests.java b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/common/DynamicValueProcessorTests.java index cd061d65c..a183a7007 100644 --- a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/common/DynamicValueProcessorTests.java +++ b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/common/DynamicValueProcessorTests.java @@ -18,10 +18,10 @@ import org.mockito.Mock; import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; -import org.opencds.cqf.fhir.cql.CqfExpression; import org.opencds.cqf.fhir.cql.LibraryEngine; import org.opencds.cqf.fhir.cr.helpers.RequestHelpers; import org.opencds.cqf.fhir.cr.inputparameters.IInputParameterResolver; +import org.opencds.cqf.fhir.utility.CqfExpression; import org.slf4j.LoggerFactory; @ExtendWith(MockitoExtension.class) diff --git a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/common/PackageProcessorTests.java b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/common/PackageProcessorTests.java new file mode 100644 index 000000000..42b0c0a0b --- /dev/null +++ b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/common/PackageProcessorTests.java @@ -0,0 +1,53 @@ +package org.opencds.cqf.fhir.cr.common; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.Mockito.doReturn; + +import ca.uhn.fhir.context.FhirContext; +import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent; +import org.hl7.fhir.r4.model.Bundle.HTTPVerb; +import org.hl7.fhir.r4.model.PlanDefinition; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.opencds.cqf.fhir.api.Repository; +import org.opencds.cqf.fhir.utility.BundleHelper; + +@ExtendWith(MockitoExtension.class) +class PackageProcessorTests { + FhirContext fhirContext = FhirContext.forR4Cached(); + + @Mock + Repository repository; + + PackageProcessor packageProcessor; + + @BeforeEach + void setup() { + doReturn(fhirContext).when(repository).fhirContext(); + packageProcessor = new PackageProcessor(repository); + } + + @Test + void testPOST() { + var resource = new PlanDefinition().setId("test"); + var bundle = packageProcessor.packageResource(resource); + assertNotNull(bundle); + var entry = (BundleEntryComponent) BundleHelper.getEntryFirstRep(bundle); + assertEquals(HTTPVerb.POST, entry.getRequest().getMethod()); + assertEquals(resource, BundleHelper.getEntryResourceFirstRep(bundle)); + } + + @Test + void testPUT() { + var resource = new PlanDefinition().setId("test"); + var bundle = packageProcessor.packageResource(resource, "PUT"); + assertNotNull(bundle); + var entry = (BundleEntryComponent) BundleHelper.getEntryFirstRep(bundle); + assertEquals(HTTPVerb.PUT, entry.getRequest().getMethod()); + assertEquals(resource, BundleHelper.getEntryResourceFirstRep(bundle)); + } +} diff --git a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/helpers/GeneratedPackage.java b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/helpers/GeneratedPackage.java new file mode 100644 index 000000000..34b7df8ae --- /dev/null +++ b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/helpers/GeneratedPackage.java @@ -0,0 +1,34 @@ +package org.opencds.cqf.fhir.cr.helpers; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.opencds.cqf.fhir.utility.BundleHelper.getEntry; +import static org.opencds.cqf.fhir.utility.BundleHelper.getEntryResourceFirstRep; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.parser.IParser; +import org.hl7.fhir.instance.model.api.IBaseBundle; +import org.hl7.fhir.instance.model.api.IBaseResource; + +public class GeneratedPackage { + final IBaseBundle generatedBundle; + final IParser jsonParser; + + public GeneratedPackage(IBaseBundle generatedBundle, FhirContext fhirContext) { + this.generatedBundle = generatedBundle; + jsonParser = fhirContext.newJsonParser().setPrettyPrint(true); + } + + public GeneratedPackage hasEntry(int count) { + assertEquals(count, getEntry(generatedBundle).size()); + return this; + } + + public GeneratedPackage firstEntryIsType(Class resourceType) { + assertEquals(resourceType, getEntryResourceFirstRep(generatedBundle).getClass()); + return this; + } + + public IBaseBundle getBundle() { + return generatedBundle; + } +} diff --git a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/helpers/RequestHelpers.java b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/helpers/RequestHelpers.java index 98c4945a1..5c760e623 100644 --- a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/helpers/RequestHelpers.java +++ b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/helpers/RequestHelpers.java @@ -6,12 +6,12 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import org.opencds.cqf.cql.engine.model.ModelResolver; import org.opencds.cqf.fhir.cql.LibraryEngine; -import org.opencds.cqf.fhir.cql.engine.model.FhirModelResolverCache; import org.opencds.cqf.fhir.cr.inputparameters.IInputParameterResolver; import org.opencds.cqf.fhir.cr.plandefinition.apply.ApplyRequest; import org.opencds.cqf.fhir.cr.questionnaire.generate.GenerateRequest; import org.opencds.cqf.fhir.cr.questionnaire.populate.PopulateRequest; import org.opencds.cqf.fhir.utility.Ids; +import org.opencds.cqf.fhir.utility.model.FhirModelResolverCache; public class RequestHelpers { public static final String PATIENT_ID = "patientId"; 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 new file mode 100644 index 000000000..550a7f4b5 --- /dev/null +++ b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/library/LibraryProcessorTests.java @@ -0,0 +1,76 @@ +package org.opencds.cqf.fhir.cr.library; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.opencds.cqf.fhir.cr.library.TestLibrary.CLASS_PATH; +import static org.opencds.cqf.fhir.cr.library.TestLibrary.given; +import static org.opencds.cqf.fhir.test.Resources.getResourcePath; + +import ca.uhn.fhir.context.FhirContext; +import java.nio.file.Paths; +import org.junit.jupiter.api.Test; +import org.opencds.cqf.fhir.cql.EvaluationSettings; +import org.opencds.cqf.fhir.cr.common.PackageProcessor; +import org.opencds.cqf.fhir.utility.Ids; +import org.opencds.cqf.fhir.utility.monad.Eithers; +import org.opencds.cqf.fhir.utility.repository.ig.IgRepository; + +public class LibraryProcessorTests { + private final FhirContext fhirContextDstu3 = FhirContext.forDstu3Cached(); + private final FhirContext fhirContextR4 = FhirContext.forR4Cached(); + private final FhirContext fhirContextR5 = FhirContext.forR5Cached(); + + @Test + void defaultSettings() { + var repository = + new IgRepository(fhirContextR4, Paths.get(getResourcePath(this.getClass()) + "/" + CLASS_PATH + "/r4")); + var processor = new LibraryProcessor(repository); + assertNotNull(processor.evaluationSettings()); + } + + @Test + void processor() { + var repository = + new IgRepository(fhirContextR5, Paths.get(getResourcePath(this.getClass()) + "/" + CLASS_PATH + "/r5")); + var packageProcessor = new PackageProcessor(repository); + var processor = new LibraryProcessor(repository, EvaluationSettings.getDefault(), packageProcessor); + assertNotNull(processor.evaluationSettings()); + var result = processor.resolveLibrary(Eithers.forMiddle3( + Ids.newId(repository.fhirContext(), "Library", "OutpatientPriorAuthorizationPrepopulation"))); + assertNotNull(result); + } + + @Test + void packageDstu3() { + given().repositoryFor(fhirContextDstu3, "dstu3") + .when() + .libraryId("OutpatientPriorAuthorizationPrepopulation") + .thenPackage() + .hasEntry(2); + } + + @Test + void packageR4() { + given().repositoryFor(fhirContextR4, "r4/pa-aslp") + .when() + .libraryId("ASLPDataElements") + .thenPackage() + .hasEntry(10); + + given().repositoryFor(fhirContextR4, "r4") + .when() + .libraryId("OutpatientPriorAuthorizationPrepopulation") + .isPut(Boolean.TRUE) + .thenPackage() + .hasEntry(2); + } + + @Test + void packageR5() { + given().repositoryFor(fhirContextR5, "r5") + .when() + .libraryId("OutpatientPriorAuthorizationPrepopulation") + .isPut(Boolean.TRUE) + .thenPackage() + .hasEntry(2); + } +} 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 new file mode 100644 index 000000000..6ca348e8a --- /dev/null +++ b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/library/TestLibrary.java @@ -0,0 +1,194 @@ +package org.opencds.cqf.fhir.cr.library; + +import static org.opencds.cqf.fhir.test.Resources.getResourcePath; +import static org.opencds.cqf.fhir.utility.BundleHelper.addEntry; +import static org.opencds.cqf.fhir.utility.BundleHelper.newBundle; +import static org.opencds.cqf.fhir.utility.BundleHelper.newEntryWithResource; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.parser.IParser; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Paths; +import org.hl7.fhir.instance.model.api.IBaseBundle; +import org.hl7.fhir.instance.model.api.IBaseParameters; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; +import org.opencds.cqf.fhir.api.Repository; +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; +import org.opencds.cqf.fhir.cql.engine.terminology.TerminologySettings.VALUESET_EXPANSION_MODE; +import org.opencds.cqf.fhir.cr.TestOperationProvider; +import org.opencds.cqf.fhir.cr.helpers.GeneratedPackage; +import org.opencds.cqf.fhir.cr.plandefinition.PlanDefinition; +import org.opencds.cqf.fhir.utility.Ids; +import org.opencds.cqf.fhir.utility.monad.Eithers; +import org.opencds.cqf.fhir.utility.repository.InMemoryFhirRepository; +import org.opencds.cqf.fhir.utility.repository.ig.IgRepository; + +public class TestLibrary { + // Borrowing resources from PlanDefinition + public static final String CLASS_PATH = "org/opencds/cqf/fhir/cr/plandefinition"; + + private static InputStream open(String asset) { + return PlanDefinition.class.getResourceAsStream(asset); + } + + public static String load(InputStream asset) throws IOException { + return new String(asset.readAllBytes(), StandardCharsets.UTF_8); + } + + public static String load(String asset) throws IOException { + return load(open(asset)); + } + + public static Given given() { + return new Given(); + } + + public static class Given { + private Repository repository; + private EvaluationSettings evaluationSettings; + + public Given repository(Repository repository) { + this.repository = repository; + return this; + } + + public Given repositoryFor(FhirContext fhirContext, String repositoryPath) { + this.repository = new IgRepository( + fhirContext, Paths.get(getResourcePath(this.getClass()) + "/" + CLASS_PATH + "/" + repositoryPath)); + return this; + } + + public Given evaluationSettings(EvaluationSettings evaluationSettings) { + this.evaluationSettings = evaluationSettings; + return this; + } + + public LibraryProcessor buildProcessor(Repository repository) { + if (repository instanceof IgRepository) { + ((IgRepository) repository) + .setOperationProvider(TestOperationProvider.newProvider(repository.fhirContext())); + } + if (evaluationSettings == null) { + evaluationSettings = EvaluationSettings.getDefault(); + evaluationSettings + .getRetrieveSettings() + .setSearchParameterMode(SEARCH_FILTER_MODE.FILTER_IN_MEMORY) + .setTerminologyParameterMode(TERMINOLOGY_FILTER_MODE.FILTER_IN_MEMORY); + + evaluationSettings + .getTerminologySettings() + .setValuesetExpansionMode(VALUESET_EXPANSION_MODE.PERFORM_NAIVE_EXPANSION); + } + return new LibraryProcessor(repository, evaluationSettings); + } + + public When when() { + return new When(repository, buildProcessor(repository)); + } + } + + public static class When { + private final Repository repository; + private final LibraryProcessor processor; + private final IParser jsonParser; + + private String libraryId; + + private String subjectId; + private Boolean useServerData; + private Repository dataRepository; + private Repository contentRepository; + private Repository terminologyRepository; + private IBaseBundle additionalData; + private IIdType additionalDataId; + private IBaseParameters parameters; + private Boolean isPackagePut; + + public When(Repository repository, LibraryProcessor processor) { + this.repository = repository; + this.processor = processor; + jsonParser = repository.fhirContext().newJsonParser(); + } + + public When libraryId(String id) { + libraryId = id; + return this; + } + + public When subjectId(String id) { + subjectId = id; + return this; + } + + public When useServerData(Boolean value) { + useServerData = value; + return this; + } + + public When data(String dataAssetName) { + dataRepository = new InMemoryFhirRepository( + repository.fhirContext(), (IBaseBundle) jsonParser.parseResource(open(dataAssetName))); + return this; + } + + public When content(String dataAssetName) { + contentRepository = new InMemoryFhirRepository( + repository.fhirContext(), (IBaseBundle) jsonParser.parseResource(open(dataAssetName))); + return this; + } + + public When terminology(String dataAssetName) { + terminologyRepository = new InMemoryFhirRepository( + repository.fhirContext(), (IBaseBundle) jsonParser.parseResource(open(dataAssetName))); + return this; + } + + private void loadAdditionalData(IBaseResource resource) { + var fhirVersion = repository.fhirContext().getVersion().getVersion(); + additionalData = resource.getIdElement().getResourceType().equals("Bundle") + ? (IBaseBundle) resource + : addEntry(newBundle(fhirVersion), newEntryWithResource(fhirVersion, resource)); + } + + public When additionalData(String dataAssetName) { + var data = jsonParser.parseResource(open(dataAssetName)); + loadAdditionalData(data); + return this; + } + + public When additionalDataId(IIdType id) { + additionalDataId = id; + return this; + } + + public When parameters(IBaseParameters params) { + parameters = params; + return this; + } + + public When isPut(Boolean value) { + isPackagePut = value; + return this; + } + + public GeneratedPackage thenPackage() { + if (isPackagePut == null) { + return new GeneratedPackage( + processor.packageLibrary( + Eithers.forMiddle3(Ids.newId(repository.fhirContext(), "Library", libraryId))), + repository.fhirContext()); + } else { + return new GeneratedPackage( + processor.packageLibrary( + Eithers.forMiddle3(Ids.newId(repository.fhirContext(), "Library", libraryId)), + isPackagePut), + repository.fhirContext()); + } + } + } +} diff --git a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/plandefinition/PlanDefinition.java b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/plandefinition/PlanDefinition.java index 17f9d721e..1a1a0d20d 100644 --- a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/plandefinition/PlanDefinition.java +++ b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/plandefinition/PlanDefinition.java @@ -27,12 +27,13 @@ import org.opencds.cqf.cql.engine.model.ModelResolver; import org.opencds.cqf.fhir.api.Repository; import org.opencds.cqf.fhir.cql.EvaluationSettings; -import org.opencds.cqf.fhir.cql.engine.model.FhirModelResolverCache; 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.TestOperationProvider; +import org.opencds.cqf.fhir.cr.helpers.GeneratedPackage; 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.ig.IgRepository; @@ -259,12 +260,17 @@ public GeneratedCarePlan thenApply() { public GeneratedPackage thenPackage() { if (isPackagePut == null) { - return new GeneratedPackage(processor.packagePlanDefinition( - Eithers.forMiddle3(Ids.newId(repository.fhirContext(), "PlanDefinition", planDefinitionId)))); + return new GeneratedPackage( + processor.packagePlanDefinition(Eithers.forMiddle3( + Ids.newId(repository.fhirContext(), "PlanDefinition", planDefinitionId))), + repository.fhirContext()); } else { - return new GeneratedPackage(processor.packagePlanDefinition( - Eithers.forMiddle3(Ids.newId(repository.fhirContext(), "PlanDefinition", planDefinitionId)), - isPackagePut)); + return new GeneratedPackage( + processor.packagePlanDefinition( + Eithers.forMiddle3( + Ids.newId(repository.fhirContext(), "PlanDefinition", planDefinitionId)), + isPackagePut), + repository.fhirContext()); } } } @@ -404,17 +410,4 @@ public GeneratedCarePlan hasQuestionnaire() { return this; } } - - public static class GeneratedPackage { - IBaseBundle generatedBundle; - - public GeneratedPackage(IBaseBundle generatedBundle) { - this.generatedBundle = generatedBundle; - } - - public GeneratedPackage hasEntry(int count) { - assertEquals(count, getEntry(generatedBundle).size()); - return this; - } - } } 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 c1273ec19..b19b7028c 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 @@ -12,12 +12,12 @@ import java.nio.file.Paths; import org.junit.jupiter.api.Test; import org.opencds.cqf.fhir.cql.EvaluationSettings; -import org.opencds.cqf.fhir.cql.engine.model.FhirModelResolverCache; import org.opencds.cqf.fhir.cr.activitydefinition.apply.IRequestResolverFactory; +import org.opencds.cqf.fhir.cr.common.PackageProcessor; import org.opencds.cqf.fhir.cr.plandefinition.apply.ApplyProcessor; -import org.opencds.cqf.fhir.cr.plandefinition.packages.PackageProcessor; import org.opencds.cqf.fhir.utility.BundleHelper; 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.ig.IgRepository; @@ -470,7 +470,7 @@ void packageDstu3() { .when() .planDefinitionId("generate-questionnaire") .thenPackage() - .hasEntry(8); + .hasEntry(9); given().repositoryFor(fhirContextDstu3, "dstu3") .when() @@ -492,7 +492,7 @@ void packageR4() { .planDefinitionId("DischargeInstructionsPlan") .isPut(Boolean.TRUE) .thenPackage() - .hasEntry(1); + .hasEntry(2); } @Test @@ -501,14 +501,14 @@ void packageR5() { .when() .planDefinitionId("generate-questionnaire") .thenPackage() - .hasEntry(8); + .hasEntry(10); given().repositoryFor(fhirContextR5, "r5") .when() .planDefinitionId("DischargeInstructionsPlan") .isPut(Boolean.TRUE) .thenPackage() - .hasEntry(1); + .hasEntry(2); } @Test diff --git a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/questionnaire/QuestionnaireProcessorTests.java b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/questionnaire/QuestionnaireProcessorTests.java index 88f669171..fba40aa8c 100644 --- a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/questionnaire/QuestionnaireProcessorTests.java +++ b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/questionnaire/QuestionnaireProcessorTests.java @@ -1,7 +1,5 @@ package org.opencds.cqf.fhir.cr.questionnaire; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.opencds.cqf.fhir.cr.questionnaire.TestQuestionnaire.CLASS_PATH; @@ -15,8 +13,8 @@ import java.nio.file.Paths; import org.junit.jupiter.api.Test; import org.opencds.cqf.fhir.api.Repository; +import org.opencds.cqf.fhir.cr.common.PackageProcessor; import org.opencds.cqf.fhir.cr.questionnaire.generate.GenerateProcessor; -import org.opencds.cqf.fhir.cr.questionnaire.packages.PackageProcessor; import org.opencds.cqf.fhir.cr.questionnaire.populate.PopulateProcessor; import org.opencds.cqf.fhir.utility.Ids; import org.opencds.cqf.fhir.utility.repository.ig.IgRepository; @@ -184,35 +182,32 @@ void populateNoSubjectThrowsException() { @Test void questionnairePackageDstu3() { - var bundle = (org.hl7.fhir.dstu3.model.Bundle) given().repository(repositoryDstu3) + given().repository(repositoryDstu3) .when() .questionnaireId(Ids.newId(fhirContextDstu3, "Questionnaire", "OutpatientPriorAuthorizationRequest")) - .thenPackage(); - - assertEquals(3, bundle.getEntry().size()); - assertEquals("Questionnaire", bundle.getEntry().get(0).getResource().fhirType()); + .thenPackage() + .hasEntry(3) + .firstEntryIsType(org.hl7.fhir.dstu3.model.Questionnaire.class); } @Test void questionnairePackageR4() { - var bundle = (org.hl7.fhir.r4.model.Bundle) given().repository(repositoryR4) + given().repository(repositoryR4) .when() .questionnaireId(Ids.newId(fhirContextR4, "Questionnaire", "OutpatientPriorAuthorizationRequest")) - .thenPackage(); - - assertEquals(3, bundle.getEntry().size()); - assertEquals("Questionnaire", bundle.getEntry().get(0).getResource().fhirType()); + .thenPackage() + .hasEntry(3) + .firstEntryIsType(org.hl7.fhir.r4.model.Questionnaire.class); } @Test void questionnairePackageR5() { - var bundle = (org.hl7.fhir.r5.model.Bundle) given().repository(repositoryR5) + given().repository(repositoryR5) .when() .questionnaireId(Ids.newId(fhirContextR5, "Questionnaire", "OutpatientPriorAuthorizationRequest")) - .thenPackage(); - - assertEquals(3, bundle.getEntry().size()); - assertEquals("Questionnaire", bundle.getEntry().get(0).getResource().fhirType()); + .thenPackage() + .hasEntry(3) + .firstEntryIsType(org.hl7.fhir.r5.model.Questionnaire.class); } @Test @@ -251,14 +246,13 @@ void pa_aslp_Populate() { @Test void pa_aslp_Package() { - var bundle = (org.hl7.fhir.r4.model.Bundle) given().repositoryFor(fhirContextR4, "r4/pa-aslp") + given().repositoryFor(fhirContextR4, "r4/pa-aslp") .when() .questionnaireId(Ids.newId(fhirContextR4, "Questionnaire", "ASLPA1")) .isPut(Boolean.TRUE) - .thenPackage(); - - assertFalse(bundle.getEntry().isEmpty()); - assertEquals(11, bundle.getEntry().size()); + .thenPackage() + .hasEntry(18) + .firstEntryIsType(org.hl7.fhir.r4.model.Questionnaire.class); } @Test @@ -270,7 +264,8 @@ void processors() { .when() .questionnaireId(Ids.newId(fhirContextR4, "Questionnaire", "OutpatientPriorAuthorizationRequest")) .isPut(Boolean.FALSE) - .thenPackage(); + .thenPackage() + .getBundle(); assertNotNull(bundle); } } 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 551234559..9292eaf06 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 @@ -23,7 +23,7 @@ import org.json.JSONException; import org.opencds.cqf.cql.engine.model.ModelResolver; import org.opencds.cqf.fhir.api.Repository; -import org.opencds.cqf.fhir.cql.engine.model.FhirModelResolverCache; +import org.opencds.cqf.fhir.utility.model.FhirModelResolverCache; import org.opencds.cqf.fhir.utility.monad.Eithers; import org.opencds.cqf.fhir.utility.repository.ig.IgRepository; import org.skyscreamer.jsonassert.JSONAssert; 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 349f89bc5..fbbfe8913 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 @@ -27,6 +27,7 @@ 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.common.IPackageProcessor; +import org.opencds.cqf.fhir.cr.helpers.GeneratedPackage; import org.opencds.cqf.fhir.cr.questionnaire.generate.IGenerateProcessor; import org.opencds.cqf.fhir.cr.questionnaire.populate.IPopulateProcessor; import org.opencds.cqf.fhir.cr.questionnaire.populate.PopulateRequest; @@ -211,9 +212,13 @@ public GeneratedQuestionnaireResponse thenPopulate(Boolean buildRequest) { } } - public IBaseBundle thenPackage() { + public GeneratedPackage thenPackage() { var param = Eithers.for3(questionnaireUrl, questionnaireId, questionnaire); - return isPut == null ? processor.packageQuestionnaire(param) : processor.packageQuestionnaire(param, isPut); + return new GeneratedPackage( + isPut == null + ? processor.packageQuestionnaire(param) + : processor.packageQuestionnaire(param, isPut), + fhirContext()); } } diff --git a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/questionnaire/generate/ElementProcessorTests.java b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/questionnaire/generate/ElementProcessorTests.java index a5e31bfdd..88eb88a08 100644 --- a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/questionnaire/generate/ElementProcessorTests.java +++ b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/questionnaire/generate/ElementProcessorTests.java @@ -23,10 +23,10 @@ import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; import org.opencds.cqf.fhir.api.Repository; -import org.opencds.cqf.fhir.cql.CqfExpression; import org.opencds.cqf.fhir.cql.LibraryEngine; import org.opencds.cqf.fhir.cr.common.ExpressionProcessor; import org.opencds.cqf.fhir.utility.Constants; +import org.opencds.cqf.fhir.utility.CqfExpression; @ExtendWith(MockitoExtension.class) class ElementProcessorTests { diff --git a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/questionnaire/generate/ItemGeneratorTests.java b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/questionnaire/generate/ItemGeneratorTests.java index 1c8d8dab6..4a4ee5de9 100644 --- a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/questionnaire/generate/ItemGeneratorTests.java +++ b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/questionnaire/generate/ItemGeneratorTests.java @@ -20,10 +20,10 @@ import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; import org.opencds.cqf.fhir.api.Repository; -import org.opencds.cqf.fhir.cql.CqfExpression; import org.opencds.cqf.fhir.cql.LibraryEngine; import org.opencds.cqf.fhir.cr.common.ResolveExpressionException; import org.opencds.cqf.fhir.cr.helpers.RequestHelpers; +import org.opencds.cqf.fhir.utility.CqfExpression; import org.opencds.cqf.fhir.utility.Ids; @ExtendWith(MockitoExtension.class) diff --git a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/questionnaire/populate/ProcessItemTests.java b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/questionnaire/populate/ProcessItemTests.java index acd7cb1dd..23100c297 100644 --- a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/questionnaire/populate/ProcessItemTests.java +++ b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/questionnaire/populate/ProcessItemTests.java @@ -24,11 +24,11 @@ import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; import org.opencds.cqf.fhir.api.Repository; -import org.opencds.cqf.fhir.cql.CqfExpression; import org.opencds.cqf.fhir.cql.LibraryEngine; import org.opencds.cqf.fhir.cr.common.ExpressionProcessor; import org.opencds.cqf.fhir.cr.common.ResolveExpressionException; import org.opencds.cqf.fhir.utility.Constants; +import org.opencds.cqf.fhir.utility.CqfExpression; @ExtendWith(MockitoExtension.class) class ProcessItemTests { 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 d444feb1c..3016a5b2d 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 @@ -16,8 +16,8 @@ import org.hl7.fhir.instance.model.api.IIdType; import org.opencds.cqf.cql.engine.model.ModelResolver; import org.opencds.cqf.fhir.api.Repository; -import org.opencds.cqf.fhir.cql.engine.model.FhirModelResolverCache; 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.ig.IgRepository; import org.skyscreamer.jsonassert.JSONAssert; diff --git a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/plandefinition/dstu3/resources/Library-FHIRHelpers.json b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/plandefinition/dstu3/resources/Library-FHIRHelpers.json index 6a6aaad1e..bd27b6435 100644 --- a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/plandefinition/dstu3/resources/Library-FHIRHelpers.json +++ b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/plandefinition/dstu3/resources/Library-FHIRHelpers.json @@ -2,7 +2,7 @@ "resourceType": "Library", "id": "FHIRHelpers", "url": "http://fhir.org/guides/cqf/common/Library/FHIRHelpers", - "version": "3.2.0", + "version": "3.0.1", "name": "FHIRHelpers", "title": "FHIR Helpers", "status": "draft", diff --git a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/plandefinition/dstu3/resources/Library-OutpatientPriorAuthorizationPrepopulation.json b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/plandefinition/dstu3/resources/Library-OutpatientPriorAuthorizationPrepopulation.json index 6d7881148..7504d2cef 100644 --- a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/plandefinition/dstu3/resources/Library-OutpatientPriorAuthorizationPrepopulation.json +++ b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/plandefinition/dstu3/resources/Library-OutpatientPriorAuthorizationPrepopulation.json @@ -21,7 +21,9 @@ "relatedArtifact": [ { "type": "depends-on", - "resource": "http://fhir.org/guides/cqf/common/Library/FHIRHelpers|3.2.0" + "resource": { + "reference": "http://fhir.org/guides/cqf/common/Library/FHIRHelpers|3.0.1" + } } ], "dataRequirement": [ diff --git a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/plandefinition/dstu3/resources/PlanDefinition-route-one.json b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/plandefinition/dstu3/resources/PlanDefinition-route-one.json index 2ebd3e0fc..a1d3f40d1 100644 --- a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/plandefinition/dstu3/resources/PlanDefinition-route-one.json +++ b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/plandefinition/dstu3/resources/PlanDefinition-route-one.json @@ -74,7 +74,9 @@ } ], "library": [ - "http://somewhere.org/fhir/uv/mycontentig/Library/OutpatientPriorAuthorizationPrepopulation" + { + "reference": "http://somewhere.org/fhir/uv/mycontentig/Library/OutpatientPriorAuthorizationPrepopulation" + } ], "action": [ { diff --git a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/plandefinition/r5/resources/ActivityDefinition-SendMessageActivity.json b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/plandefinition/r5/resources/ActivityDefinition-SendMessageActivity.json new file mode 100644 index 000000000..a1a4c3394 --- /dev/null +++ b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/plandefinition/r5/resources/ActivityDefinition-SendMessageActivity.json @@ -0,0 +1,51 @@ +{ + "resourceType": "ActivityDefinition", + "id": "SendMessageActivity", + "meta": { + "profile": [ + "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-communicationactivity" + ] + }, + "kind": "CommunicationRequest", + "profile": "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-communicationrequest", + "intent": "proposal", + "extension": [ + { + "url": "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-knowledgeCapability", + "valueCode": "publishable" + }, + { + "url": "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-knowledgeRepresentationLevel", + "valueCode": "structured" + } + ], + "url": "http://example.org/ActivityDefinition/SendMessageActivity", + "name": "SendMessageActivity", + "title": "ActivityDefinition SendMessageActivity", + "status": "draft", + "experimental": true, + "publisher": "Example", + "jurisdiction": [ + { + "coding": [ + { + "code": "001", + "system": "http://unstats.un.org/unsd/methods/m49/m49.htm", + "display": "World" + } + ] + } + ], + "version": "0.1.0", + "description": "Example Activity Definition for a recommendation to send a message", + "code": { + "coding": [ + { + "code": "send-message", + "system": "http://hl7.org/fhir/uv/cpg/CodeSystem/cpg-activity-type", + "display": "Send a message" + } + ] + }, + "doNotPerform": false +} \ No newline at end of file diff --git a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/plandefinition/r5/resources/Library-FHIRHelpers.json b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/plandefinition/r5/resources/Library-FHIRHelpers.json index 643e6e67f..36b6cadff 100644 --- a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/plandefinition/r5/resources/Library-FHIRHelpers.json +++ b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/plandefinition/r5/resources/Library-FHIRHelpers.json @@ -1,7 +1,7 @@ { "resourceType": "Library", "id": "FHIRHelpers", - "url": "http://somewhere.org/fhir/uv/mycontentig/Library/OutpatientPriorAuthorizationPrepopulation", + "url": "http://fhir.org/guides/cqf/common/Library/FHIRHelpers", "version": "4.0.001", "name": "FHIRHelpers", "title": "FHIR Helpers", diff --git a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/plandefinition/r5/resources/Library-OutpatientPriorAuthorizationPrepopulation.json b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/plandefinition/r5/resources/Library-OutpatientPriorAuthorizationPrepopulation.json index de46c31b2..8cdb7cbd1 100644 --- a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/plandefinition/r5/resources/Library-OutpatientPriorAuthorizationPrepopulation.json +++ b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/plandefinition/r5/resources/Library-OutpatientPriorAuthorizationPrepopulation.json @@ -21,7 +21,7 @@ "relatedArtifact": [ { "type": "depends-on", - "resource": "http://fhir.org/guides/cqf/common/Library/FHIRHelpers|1.0.0" + "resource": "http://fhir.org/guides/cqf/common/Library/FHIRHelpers|4.0.001" } ], "dataRequirement": [ diff --git a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/questionnaire/dstu3/resources/Questionnaire-OutpatientPriorAuthorizationRequest-OPA-Patient1.json b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/questionnaire/dstu3/resources/Questionnaire-OutpatientPriorAuthorizationRequest-OPA-Patient1.json index 2f58f39dc..7a73e3327 100644 --- a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/questionnaire/dstu3/resources/Questionnaire-OutpatientPriorAuthorizationRequest-OPA-Patient1.json +++ b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/questionnaire/dstu3/resources/Questionnaire-OutpatientPriorAuthorizationRequest-OPA-Patient1.json @@ -3,8 +3,10 @@ "id": "OutpatientPriorAuthorizationRequest-OPA-Patient1", "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-library", - "valueUri": "http://somewhere.org/fhir/uv/mycontentig/Library/OutpatientPriorAuthorizationPrepopulation|1.0.0" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-library", + "valueReference": { + "reference": "http://somewhere.org/fhir/uv/mycontentig/Library/OutpatientPriorAuthorizationPrepopulation|1.0.0" + } }, { "url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-prepopulate-subject", @@ -48,12 +50,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "FacilityName" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.FacilityName" }, { "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-author", @@ -71,12 +69,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "FacilityNPI" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.FacilityNPI" }, { "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-author", @@ -113,12 +107,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "BeneficiaryFirstName" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.BeneficiaryFirstName" }, { "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-author", @@ -136,12 +126,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "BeneficiaryLastName" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.BeneficiaryLastName" }, { "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-author", @@ -159,12 +145,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "BeneficiaryDOB" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.BeneficiaryDOB" }, { "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-author", @@ -182,12 +164,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "BeneficiaryMedicareID" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.BeneficiaryMedicareID" }, { "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-author", @@ -205,12 +183,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "BeneficiaryGender" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.BeneficiaryGender" }, { "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-author", @@ -235,12 +209,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "OperatingPhysicianFirstName" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.OperatingPhysicianFirstName" }, { "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-author", @@ -258,12 +228,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "OperatingPhysicianLastName" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.OperatingPhysicianLastName" }, { "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-author", @@ -281,12 +247,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "OperatingPhysicianNPI" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.OperatingPhysicianNPI" }, { "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-author", @@ -316,12 +278,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "OperatingPhysicianAddress1" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.OperatingPhysicianAddress1" }, { "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-author", @@ -339,12 +297,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "OperatingPhysicianAddress2" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.OperatingPhysicianAddress2" } ], "linkId": "3.5.2", @@ -355,12 +309,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "OperatingPhysicianAddressCity" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.OperatingPhysicianAddressCity" }, { "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-author", @@ -378,12 +328,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "OperatingPhysicianAddressState" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.OperatingPhysicianAddressState" }, { "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-author", @@ -401,12 +347,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "OperatingPhysicianAddressZip" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.OperatingPhysicianAddressZip" }, { "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-author", @@ -433,12 +375,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "AttendingPhysicianSame" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.AttendingPhysicianSame" }, { "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-author", @@ -467,12 +405,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "AttendingPhysicianFirstName" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.AttendingPhysicianFirstName" }, { "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-author", @@ -490,12 +424,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "AttendingPhysicianLastName" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.AttendingPhysicianLastName" }, { "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-author", @@ -513,12 +443,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "AttendingPhysicianNPI" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.AttendingPhysicianNPI" }, { "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-author", @@ -548,12 +474,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "AttendingPhysicianAddress1" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.AttendingPhysicianAddress1" }, { "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-author", @@ -571,12 +493,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "AttendingPhysicianAddress2" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.AttendingPhysicianAddress2" } ], "linkId": "4.2.5.2", @@ -587,12 +505,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "AttendingPhysicianAddressCity" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.AttendingPhysicianAddressCity" }, { "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-author", @@ -610,12 +524,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "AttendingPhysicianAddressState" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.AttendingPhysicianAddressState" }, { "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-author", @@ -633,12 +543,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "AttendingPhysicianAddressZip" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.AttendingPhysicianAddressZip" }, { "url": "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-author", diff --git a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/questionnaire/dstu3/resources/Questionnaire-OutpatientPriorAuthorizationRequest.json b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/questionnaire/dstu3/resources/Questionnaire-OutpatientPriorAuthorizationRequest.json index e4cb4a04f..7ffa97d4a 100644 --- a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/questionnaire/dstu3/resources/Questionnaire-OutpatientPriorAuthorizationRequest.json +++ b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/questionnaire/dstu3/resources/Questionnaire-OutpatientPriorAuthorizationRequest.json @@ -3,8 +3,10 @@ "id": "OutpatientPriorAuthorizationRequest", "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-library", - "valueUri": "http://somewhere.org/fhir/uv/mycontentig/Library/OutpatientPriorAuthorizationPrepopulation|1.0.0" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-library", + "valueReference": { + "reference": "http://somewhere.org/fhir/uv/mycontentig/Library/OutpatientPriorAuthorizationPrepopulation|1.0.0" + } } ], "url": "http://hl7.org/fhir/Questionnaire/OutpatientPriorAuthorizationRequest", @@ -42,12 +44,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "FacilityName" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.FacilityName" } ], "linkId": "1.1", @@ -58,12 +56,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "FacilityNPI" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.FacilityNPI" } ], "linkId": "1.2", @@ -115,12 +109,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "BeneficiaryFirstName" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.BeneficiaryFirstName" } ], "linkId": "2.1", @@ -131,12 +121,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "BeneficiaryLastName" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.BeneficiaryLastName" } ], "linkId": "2.2", @@ -147,12 +133,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "BeneficiaryDOB" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.BeneficiaryDOB" } ], "linkId": "2.3", @@ -163,12 +145,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "BeneficiaryMedicareID" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.BeneficiaryMedicareID" } ], "linkId": "2.4", @@ -179,12 +157,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "BeneficiaryGender" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.BeneficiaryGender" } ], "linkId": "2.5", @@ -233,12 +207,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "OperatingPhysicianFirstName" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.OperatingPhysicianFirstName" } ], "linkId": "3.1", @@ -249,12 +219,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "OperatingPhysicianLastName" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.OperatingPhysicianLastName" } ], "linkId": "3.2", @@ -265,12 +231,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "OperatingPhysicianNPI" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.OperatingPhysicianNPI" } ], "linkId": "3.3", @@ -293,12 +255,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "OperatingPhysicianAddress1" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.OperatingPhysicianAddress1" } ], "linkId": "3.5.1", @@ -309,12 +267,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "OperatingPhysicianAddress2" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.OperatingPhysicianAddress2" } ], "linkId": "3.5.2", @@ -325,12 +279,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "OperatingPhysicianAddressCity" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.OperatingPhysicianAddressCity" } ], "linkId": "3.5.3", @@ -341,12 +291,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "OperatingPhysicianAddressState" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.OperatingPhysicianAddressState" } ], "linkId": "3.5.4", @@ -357,12 +303,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "OperatingPhysicianAddressZip" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.OperatingPhysicianAddressZip" } ], "linkId": "3.5.5", @@ -382,12 +324,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "AttendingPhysicianSame" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.AttendingPhysicianSame" } ], "linkId": "4.1", @@ -410,12 +348,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "AttendingPhysicianFirstName" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.AttendingPhysicianFirstName" } ], "linkId": "4.2.1", @@ -426,12 +360,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "AttendingPhysicianLastName" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.AttendingPhysicianLastName" } ], "linkId": "4.2.2", @@ -442,12 +372,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "AttendingPhysicianNPI" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.AttendingPhysicianNPI" } ], "linkId": "4.2.3", @@ -470,12 +396,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "AttendingPhysicianAddress1" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.AttendingPhysicianAddress1" } ], "linkId": "4.2.5.1", @@ -486,12 +408,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "AttendingPhysicianAddress2" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.AttendingPhysicianAddress2" } ], "linkId": "4.2.5.2", @@ -502,12 +420,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "AttendingPhysicianAddressCity" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.AttendingPhysicianAddressCity" } ], "linkId": "4.2.5.3", @@ -518,12 +432,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "AttendingPhysicianAddressState" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.AttendingPhysicianAddressState" } ], "linkId": "4.2.5.4", @@ -534,12 +444,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "AttendingPhysicianAddressZip" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.AttendingPhysicianAddressZip" } ], "linkId": "4.2.5.5", diff --git a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/questionnaire/dstu3/resources/StructureDefinition-RouteOnePatient.json b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/questionnaire/dstu3/resources/StructureDefinition-RouteOnePatient.json index d80fd8f64..aa52392dc 100644 --- a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/questionnaire/dstu3/resources/StructureDefinition-RouteOnePatient.json +++ b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/questionnaire/dstu3/resources/StructureDefinition-RouteOnePatient.json @@ -12,6 +12,12 @@ { "url": "http://hl7.org/fhir/StructureDefinition/structuredefinition-wg", "valueCode": "pa" + }, + { + "url": "http://hl7.org/fhir/StructureDefinition/cqif-library", + "valueReference": { + "reference": "http://somewhere.org/fhir/uv/mycontentig/Library/OutpatientPriorAuthorizationPrepopulation" + } } ], "url": "http://fhir.org/guides/cdc/opioid-cds/StructureDefinition/RouteOnePatient", @@ -1840,16 +1846,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "BeneficiaryFirstName" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-library", - "valueString": "http://somewhere.org/fhir/uv/mycontentig/Library/OutpatientPriorAuthorizationPrepopulation" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.BeneficiaryFirstName" } ], "id": "Patient.name.given", @@ -1865,16 +1863,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "BeneficiaryLastName" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-library", - "valueString": "http://somewhere.org/fhir/uv/mycontentig/Library/OutpatientPriorAuthorizationPrepopulation" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.BeneficiaryLastName" } ], "id": "Patient.name.family", @@ -1890,16 +1880,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "BeneficiaryDOB" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-library", - "valueString": "http://somewhere.org/fhir/uv/mycontentig/Library/OutpatientPriorAuthorizationPrepopulation" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.BeneficiaryDOB" } ], "id": "Patient.birthDate", @@ -1915,16 +1897,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "BeneficiaryGender" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-library", - "valueString": "http://somewhere.org/fhir/uv/mycontentig/Library/OutpatientPriorAuthorizationPrepopulation" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.BeneficiaryGender" } ], "id": "Patient.gender", @@ -1986,16 +1960,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "BeneficiaryMedicareID" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-library", - "valueString": "http://somewhere.org/fhir/uv/mycontentig/Library/OutpatientPriorAuthorizationPrepopulation" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.BeneficiaryMedicareID" } ], "id": "Patient.identifier:MedicareID.value", diff --git a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/questionnaire/dstu3/tests/QuestionnaireResponse-OutpatientPriorAuthorizationRequest-OPA-Patient1.json b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/questionnaire/dstu3/tests/QuestionnaireResponse-OutpatientPriorAuthorizationRequest-OPA-Patient1.json index 5486d73ad..959d678a4 100644 --- a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/questionnaire/dstu3/tests/QuestionnaireResponse-OutpatientPriorAuthorizationRequest-OPA-Patient1.json +++ b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/questionnaire/dstu3/tests/QuestionnaireResponse-OutpatientPriorAuthorizationRequest-OPA-Patient1.json @@ -7,15 +7,27 @@ "id": "OutpatientPriorAuthorizationRequest", "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-library", - "valueUri": "http://somewhere.org/fhir/uv/mycontentig/Library/OutpatientPriorAuthorizationPrepopulation|1.0.0" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-library", + "valueReference": { + "reference": "http://somewhere.org/fhir/uv/mycontentig/Library/OutpatientPriorAuthorizationPrepopulation|1.0.0" + } } ], "url": "http://hl7.org/fhir/Questionnaire/OutpatientPriorAuthorizationRequest", "name": "OutpatientPriorAuthorizationRequest", "title": "Outpatient Prior Authorization Request", "status": "active", + "subjectType": [ + "Patient", + "Organization", + "Claim" + ], "date": "2022-01-04T00:00:00+00:00", + "contact": [ + { + "name": "Palmetto GBA" + } + ], "description": "Testing the form", "jurisdiction": [ { @@ -27,16 +39,6 @@ ] } ], - "contact": [ - { - "name": "Palmetto GBA" - } - ], - "subjectType": [ - "Patient", - "Organization", - "Claim" - ], "item": [ { "linkId": "1", @@ -46,12 +48,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "FacilityName" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.FacilityName" } ], "linkId": "1.1", @@ -62,12 +60,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "FacilityNPI" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.FacilityNPI" } ], "linkId": "1.2", @@ -85,7 +79,29 @@ "linkId": "1.4", "text": "Contract/Region", "type": "choice", - "required": false + "required": false, + "answerOption": [ + { + "id": "FacilityContractRegion-11501", + "valueCoding": { + "code": "11001", + "display": "Part A South Carolina" + } + }, + { + "id": "FacilityContractRegion-11003", + "valueCoding": { + "code": "11501", + "display": "Part A North Carolina" + } + }, + { + "valueCoding": { + "code": "11003", + "display": "Part A Virginia/West Virginia" + } + } + ] } ] }, @@ -97,12 +113,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "BeneficiaryFirstName" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.BeneficiaryFirstName" } ], "linkId": "2.1", @@ -113,12 +125,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "BeneficiaryLastName" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.BeneficiaryLastName" } ], "linkId": "2.2", @@ -129,12 +137,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "BeneficiaryDOB" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.BeneficiaryDOB" } ], "linkId": "2.3", @@ -145,12 +149,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "BeneficiaryMedicareID" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.BeneficiaryMedicareID" } ], "linkId": "2.4", @@ -161,18 +161,45 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "BeneficiaryGender" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.BeneficiaryGender" } ], "linkId": "2.5", "text": "Gender", "type": "choice", - "required": true + "required": true, + "answerOption": [ + { + "id": "unknown", + "valueCoding": { + "system": "http://hl7.org/fhir/ValueSet/administrative-gender", + "code": "male", + "display": "Male" + } + }, + { + "valueCoding": { + "system": "http://hl7.org/fhir/ValueSet/administrative-gender", + "code": "female", + "display": "Female" + } + }, + { + "valueCoding": { + "system": "http://hl7.org/fhir/ValueSet/administrative-gender", + "code": "other", + "display": "Other" + } + }, + { + "valueCoding": { + "system": "http://hl7.org/fhir/ValueSet/administrative-gender", + "code": "unknown", + "display": "Unknown" + } + } + ] } ] }, @@ -184,12 +211,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "OperatingPhysicianFirstName" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.OperatingPhysicianFirstName" } ], "linkId": "3.1", @@ -200,12 +223,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "OperatingPhysicianLastName" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.OperatingPhysicianLastName" } ], "linkId": "3.2", @@ -216,12 +235,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "OperatingPhysicianNPI" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.OperatingPhysicianNPI" } ], "linkId": "3.3", @@ -244,12 +259,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "OperatingPhysicianAddress1" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.OperatingPhysicianAddress1" } ], "linkId": "3.5.1", @@ -260,12 +271,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "OperatingPhysicianAddress2" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.OperatingPhysicianAddress2" } ], "linkId": "3.5.2", @@ -276,12 +283,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "OperatingPhysicianAddressCity" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.OperatingPhysicianAddressCity" } ], "linkId": "3.5.3", @@ -292,12 +295,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "OperatingPhysicianAddressState" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.OperatingPhysicianAddressState" } ], "linkId": "3.5.4", @@ -308,12 +307,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "OperatingPhysicianAddressZip" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.OperatingPhysicianAddressZip" } ], "linkId": "3.5.5", @@ -333,12 +328,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "AttendingPhysicianSame" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.AttendingPhysicianSame" } ], "linkId": "4.1", @@ -353,6 +344,7 @@ "enableWhen": [ { "question": "4.1", + "operator": "=", "answerBoolean": false } ], @@ -360,12 +352,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "AttendingPhysicianFirstName" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.AttendingPhysicianFirstName" } ], "linkId": "4.2.1", @@ -376,12 +364,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "AttendingPhysicianLastName" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.AttendingPhysicianLastName" } ], "linkId": "4.2.2", @@ -392,12 +376,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "AttendingPhysicianNPI" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.AttendingPhysicianNPI" } ], "linkId": "4.2.3", @@ -420,12 +400,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "AttendingPhysicianAddress1" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.AttendingPhysicianAddress1" } ], "linkId": "4.2.5.1", @@ -436,12 +412,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "AttendingPhysicianAddress2" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.AttendingPhysicianAddress2" } ], "linkId": "4.2.5.2", @@ -452,12 +424,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "AttendingPhysicianAddressCity" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.AttendingPhysicianAddressCity" } ], "linkId": "4.2.5.3", @@ -468,12 +436,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "AttendingPhysicianAddressState" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.AttendingPhysicianAddressState" } ], "linkId": "4.2.5.4", @@ -484,12 +448,8 @@ { "extension": [ { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression-language", - "valueString": "text/cql.identifier" - }, - { - "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", - "valueString": "AttendingPhysicianAddressZip" + "url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression", + "valueString": "OutpatientPriorAuthorizationPrepopulation.AttendingPhysicianAddressZip" } ], "linkId": "4.2.5.5", diff --git a/cqf-fhir-utility/pom.xml b/cqf-fhir-utility/pom.xml index 6cfaf8101..11b677c89 100644 --- a/cqf-fhir-utility/pom.xml +++ b/cqf-fhir-utility/pom.xml @@ -32,6 +32,14 @@ 3.10.0-SNAPSHOT test + + info.cqframework + engine + + + info.cqframework + engine-fhir + ca.uhn.hapi.fhir hapi-fhir-base diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/Constants.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/Constants.java index 5eb742e72..a0c0f0246 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/Constants.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/Constants.java @@ -2,9 +2,6 @@ import ca.uhn.fhir.model.api.Tag; import com.google.common.collect.ImmutableMap; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; import java.util.Map; public class Constants { @@ -33,8 +30,19 @@ private Constants() {} public static final String REQUEST_DO_NOT_PERFORM = "http://hl7.org/fhir/StructureDefinition/request-doNotPerform"; public static final String QUESTIONNAIRE_RESPONSE_AUTHOR = "http://hl7.org/fhir/StructureDefinition/questionnaireresponse-author"; + public static final String QUESTIONNAIRE_REFERENCE_PROFILE = + "http://hl7.org/fhir/StructureDefinition/questionnaire-referenceProfile"; + public static final String QUESTIONNAIRE_UNIT_VALUE_SET = + "http://hl7.org/fhir/StructureDefinition/questionnaire-unitValueSet"; + + public static final String VARIABLE_EXTENSION = "http://hl7.org/fhir/StructureDefinition/variable"; + + public static final String CPG_ASSERTION_EXPRESSION = + "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-assertionExpression"; public static final String CPG_FEATURE_EXPRESSION = "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-featureExpression"; + public static final String CPG_INFERENCE_EXPRESSION = + "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-inferenceExpression"; public static final String CPG_KNOWLEDGE_CAPABILITY = "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-knowledgeCapability"; public static final String CPG_KNOWLEDGE_REPRESENTATION_LEVEL = @@ -48,18 +56,21 @@ private Constants() {} public static final String CPG_INPUT_TEXT = "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-input-text"; public static final String CPG_INPUT_DESCRIPTION = "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-input-description"; - // public static final String CQF_QUESTIONNAIRE = - // "http://hl7.org/fhir/StructureDefinition/cqf-questionnaire"; public static final String CPG_CUSTOM_ACTIVITY_KIND = "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-custom-activity-kind"; public static final String CPG_ACTIVITY_KIND = "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-activity-kind"; + // DSTU3 CQF Extensions + public static final String CQIF_LIBRARY = "http://hl7.org/fhir/StructureDefinition/cqif-library"; + public static final String CQIF_CQL_EXPRESSION = "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression"; + + public static final String CQF_CQL_OPTIONS = "http://hl7.org/fhir/StructureDefinition/cqf-cqlOptions"; + public static final String CQF_EXPANSION_PARAMETERS = + "http://hl7.org/fhir/StructureDefinition/cqf-expansionParameters"; public static final String CQF_EXPRESSION = "http://hl7.org/fhir/StructureDefinition/cqf-expression"; - // This is only for dstu3 since the Expression type does not exist in that version - public static final String CQF_EXPRESSION_LANGUAGE = - "http://hl7.org/fhir/StructureDefinition/cqf-expression-language"; public static final String CQF_LIBRARY = "http://hl7.org/fhir/StructureDefinition/cqf-library"; + public static final String CQF_CALCULATED_VALUE = "http://hl7.org/fhir/StructureDefinition/cqf-calculatedValue"; public static final String CQFM_EFFECTIVE_DATA_REQUIREMENTS = "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-effectiveDataRequirements"; @@ -67,6 +78,12 @@ private Constants() {} "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-logicDefinition"; public static final String CQFM_SOFTWARE_SYSTEM = "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-softwaresystem"; + public static final String CQFM_INPUT_PARAMETERS = + "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-inputParameters"; + public static final String CQFM_COMPONENT = "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-component"; + + public static final String CRMI_EFFECTIVE_DATA_REQUIREMENTS = + "http://hl7.org/fhir/uv/crmi/StructureDefinition/crmi-effectiveDataRequirements"; public static final String DTR_QUESTIONNAIRE_RESPONSE_QUESTIONNAIRE = "http://hl7.org/fhir/us/davinci-dtr/StructureDefinition/dtr-questionnaireresponse-questionnaire"; @@ -99,6 +116,12 @@ private Constants() {} "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-observation-extract-category"; public static final String SDC_OBSERVATION_CATEGORY = "http://hl7.org/fhir/observation-category"; public static final String SDC_CATEGORY_SURVEY = "survey"; + public static final String SDC_QUESTIONNAIRE_LAUNCH_CONTEXT = + "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-launchContext"; + public static final String SDC_QUESTIONNAIRE_SUB_QUESTIONNAIRE = + "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-subQuestionnaire"; + public static final String SDC_QUESTIONNAIRE_CALCULATED_EXPRESSION = + "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-calculatedExpression"; // $apply parameter names public static final String APPLY_PARAMETER_ACTIVITY_DEFINITION = "activityDefinition"; @@ -119,29 +142,14 @@ private Constants() {} public static final String APPLY_PARAMETER_DATA_ENDPOINT = "dataEndpoint"; public static final String APPLY_PARAMETER_CONTENT_ENDPOINT = "contentEndpoint"; public static final String APPLY_PARAMETER_TERMINOLOGY_ENDPOINT = "terminologyEndpoint"; + public static final String US_PH_CONTEXT_URL = "http://hl7.org/fhir/us/ecr/CodeSystem/us-ph-usage-context"; - public static final String VALUE_SET_CONDITION_CODE = "focus"; - public static final String VALUE_SET_PRIORITY_CODE = "priority"; - public static final String VALUE_SET_CONDITION_URL = - "http://aphl.org/fhir/vsm/StructureDefinition/vsm-valueset-condition"; - public static final String VALUE_SET_PRIORITY_URL = - "http://aphl.org/fhir/vsm/StructureDefinition/vsm-valueset-priority"; public static final String LIBRARY_TYPE = "http://terminology.hl7.org/CodeSystem/library-type"; public static final String ASSET_COLLECTION = "asset-collection"; - public static final String EXPANSION_PARAMETERS_URL = - "http://hl7.org/fhir/StructureDefinition/cqf-expansionParameters"; - - // can't use List.of for Android 26 compatibility - public static final List PRESERVED_EXTENSION_URLS = - Collections.unmodifiableList(Arrays.asList(VALUE_SET_PRIORITY_URL, VALUE_SET_CONDITION_URL)); public static final String AUTHORITATIVE_SOURCE_URL = "http://hl7.org/fhir/StructureDefinition/valueset-authoritativeSource"; - public static final String VSM_WORKFLOW_CODES_CODE_SYSTEM_URL = - "http://aphl.org/fhir/vsm/CodeSystem/vsm-workflow-codes"; - public static final String VSM_VALUE_SET_TAG_VSM_AUTHORED_CODE = "vsm-authored"; public static final String VSAC_USERNAME = "vsacUsername"; - public static final String APIKEY = "apiKey"; } diff --git a/cqf-fhir-cql/src/main/java/org/opencds/cqf/fhir/cql/CqfExpression.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/CqfExpression.java similarity index 74% rename from cqf-fhir-cql/src/main/java/org/opencds/cqf/fhir/cql/CqfExpression.java rename to cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/CqfExpression.java index eb2a0e91f..529a9ff99 100644 --- a/cqf-fhir-cql/src/main/java/org/opencds/cqf/fhir/cql/CqfExpression.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/CqfExpression.java @@ -1,6 +1,7 @@ -package org.opencds.cqf.fhir.cql; +package org.opencds.cqf.fhir.utility; -import org.opencds.cqf.fhir.utility.Constants; +import ca.uhn.fhir.context.FhirVersionEnum; +import org.hl7.fhir.instance.model.api.IBaseExtension; /** * This class is used to contain the various properties of a CqfExpression with an alternate so that @@ -15,6 +16,33 @@ public class CqfExpression { private String altExpression; private String altLibraryUrl; + public static CqfExpression of(IBaseExtension extension, String defaultLibraryUrl) { + if (extension == null) { + return null; + } + var fhirPackagePath = "org.hl7.fhir."; + var className = extension.getClass().getCanonicalName(); + var modelSplit = className.split(fhirPackagePath); + if (modelSplit.length < 2) { + throw new IllegalArgumentException(); + } + var model = modelSplit[1]; + model = model.substring(0, model.indexOf(".")).toUpperCase(); + var version = FhirVersionEnum.forVersionString(model); + switch (version) { + case DSTU3: + return new CqfExpression( + "text/cql.expression", extension.getValue().toString(), defaultLibraryUrl); + case R4: + return CqfExpression.of((org.hl7.fhir.r4.model.Expression) extension.getValue(), defaultLibraryUrl); + case R5: + return CqfExpression.of((org.hl7.fhir.r5.model.Expression) extension.getValue(), defaultLibraryUrl); + + default: + return null; + } + } + public static CqfExpression of(org.hl7.fhir.r4.model.Expression expression, String defaultLibraryUrl) { if (expression == null) { return null; @@ -28,9 +56,7 @@ public static CqfExpression of(org.hl7.fhir.r4.model.Expression expression, Stri expression.hasReference() ? expression.getReference() : defaultLibraryUrl, altExpression != null ? altExpression.getLanguage() : null, altExpression != null ? altExpression.getExpression() : null, - altExpression != null && altExpression.hasReference() - ? altExpression.getReference() - : defaultLibraryUrl); + altExpression != null && altExpression.hasReference() ? altExpression.getReference() : null); } public static CqfExpression of(org.hl7.fhir.r5.model.Expression expression, String defaultLibraryUrl) { @@ -46,9 +72,7 @@ public static CqfExpression of(org.hl7.fhir.r5.model.Expression expression, Stri expression.hasReference() ? expression.getReference() : defaultLibraryUrl, altExpression != null ? altExpression.getLanguage() : null, altExpression != null ? altExpression.getExpression() : null, - altExpression != null && altExpression.hasReference() - ? altExpression.getReference() - : defaultLibraryUrl); + altExpression != null && altExpression.hasReference() ? altExpression.getReference() : null); } public CqfExpression() {} diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/ExpandHelper.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/ExpandHelper.java new file mode 100644 index 000000000..1243a6477 --- /dev/null +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/ExpandHelper.java @@ -0,0 +1,116 @@ +package org.opencds.cqf.fhir.utility; + +import static org.opencds.cqf.fhir.utility.ValueSets.addCodeToExpansion; +import static org.opencds.cqf.fhir.utility.ValueSets.addParameterToExpansion; +import static org.opencds.cqf.fhir.utility.ValueSets.getCodesInExpansion; +import static org.opencds.cqf.fhir.utility.adapter.AdapterFactory.createAdapterForResource; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import java.util.List; +import java.util.Optional; +import org.apache.commons.lang3.StringUtils; +import org.hl7.fhir.instance.model.api.IPrimitiveType; +import org.opencds.cqf.fhir.utility.adapter.EndpointAdapter; +import org.opencds.cqf.fhir.utility.adapter.ParametersAdapter; +import org.opencds.cqf.fhir.utility.adapter.ValueSetAdapter; +import org.opencds.cqf.fhir.utility.client.TerminologyServerClient; + +public class ExpandHelper { + private final FhirContext fhirContext; + private final TerminologyServerClient terminologyServerClient; + + public ExpandHelper(FhirContext fhirContext, TerminologyServerClient server) { + this.fhirContext = fhirContext; + terminologyServerClient = server; + } + + public void expandValueSet( + ValueSetAdapter valueSet, + ParametersAdapter expansionParameters, + Optional terminologyEndpoint, + List valueSets, + List expandedList) { + // Have we already expanded this ValueSet? + if (expandedList.contains(valueSet.getUrl())) { + // Nothing to do here + return; + } + + // Gather the Terminology Service from the valueSet's authoritativeSourceUrl. + @SuppressWarnings("unchecked") + var authoritativeSourceUrl = valueSet.getExtension().stream() + .filter(e -> e.getUrl().equals(Constants.AUTHORITATIVE_SOURCE_URL)) + .findFirst() + .map(url -> ((IPrimitiveType) url.getValue()).getValueAsString()) + .orElse(null); + + // If terminologyEndpoint exists and we have no authoritativeSourceUrl or the authoritativeSourceUrl matches the + // terminologyEndpoint address then we will use the terminologyEndpoint for expansion + if (terminologyEndpoint.isPresent() + && (authoritativeSourceUrl == null + || authoritativeSourceUrl.equals( + terminologyEndpoint.get().getAddress()))) { + try { + var expandedValueSet = (ValueSetAdapter) createAdapterForResource( + terminologyServerClient.expand(valueSet, terminologyEndpoint.get(), expansionParameters)); + valueSet.setExpansion(expandedValueSet.getExpansion()); + } catch (Exception ex) { + throw new UnprocessableEntityException(String.format( + "Terminology Server expansion failed for ValueSet (%s): %s", + valueSet.getId(), ex.getMessage())); + } + } + // Else if the ValueSet has a simple compose then we will perform naive expansion. + else if (valueSet.hasSimpleCompose()) { + valueSet.naiveExpand(); + } + // Else if the ValueSet has a grouping compose then we will attempt to group. + else if (valueSet.hasGroupingCompose()) { + var expansion = valueSet.newExpansion(); + var includes = valueSet.getValueSetIncludes(); + includes.forEach(reference -> { + // Grab the ValueSet + var split = reference.split("\\|"); + var url = split.length == 1 ? reference : split[0]; + var version = split.length == 1 ? null : split[1]; + var vs = valueSets.stream() + .filter(v -> v.getUrl().equals(url) + && (version == null || v.getVersion().equals(version))) + .findFirst() + .orElse(null); + // Expand the ValueSet if we haven't already + if (!expandedList.contains(url)) { + expandValueSet(vs, expansionParameters, terminologyEndpoint, valueSets, expandedList); + } + getCodesInExpansion(fhirContext, vs.get()).forEach(code -> { + // Add the code if not already present + var existingCodes = getCodesInExpansion(fhirContext, expansion); + if (existingCodes != null + && existingCodes.stream() + .noneMatch(expandedCode -> code.getSystem().equals(expandedCode.getSystem()) + && code.getCode().equals(expandedCode.getCode()) + && (StringUtils.isEmpty(code.getVersion()) + || code.getVersion().equals(expandedCode.getVersion())))) { + try { + addCodeToExpansion(fhirContext, expansion, code); + } catch (Exception ex) { + throw new UnprocessableEntityException(String.format( + "Encountered exception attempting to expand ValueSet %s: %s", + vs.get().getId(), ex.getMessage())); + } + } + }); + // If any included expansion is naive it makes the expansion naive + if (vs.hasNaiveParameter() && !valueSet.hasNaiveParameter()) { + addParameterToExpansion(fhirContext, expansion, valueSet.createNaiveParameter()); + } + }); + valueSet.setExpansion(expansion); + } else { + throw new UnprocessableEntityException( + "Cannot expand ValueSet without a terminology server: " + valueSet.getId()); + } + expandedList.add(valueSet.getUrl()); + } +} diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/SearchHelper.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/SearchHelper.java index eba1849ef..b3095c6e8 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/SearchHelper.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/SearchHelper.java @@ -3,6 +3,7 @@ import static org.opencds.cqf.fhir.utility.BundleHelper.getEntryResourceFirstRep; import ca.uhn.fhir.model.api.IQueryParameterType; +import ca.uhn.fhir.parser.DataFormatException; import java.util.Collections; import java.util.List; import java.util.Map; @@ -51,14 +52,75 @@ public static IBaseResource readRepository(Repository repository, IIdType id) { */ public static > IBaseResource searchRepositoryByCanonical( Repository repository, CanonicalType canonical) { - var resourceType = repository - .fhirContext() - .getResourceDefinition(Canonicals.getResourceType(canonical)) - .getImplementingClass(); + var resourceType = getResourceType(repository, canonical); return searchRepositoryByCanonical(repository, canonical, resourceType); } + /** + * Gets the resource type for the given canonical, based on the convention that canonical + * URLs are of the form [base]/[resourceType]/[tail] + * + * If the URL does not conform to the convention, the cqf-resourceType extension is used + * to determine the type of the resource, if present. + * + * If no extension is present, the type of the canonical is assumed to be CodeSystem, on + * the grounds that most (if not all) non-conventional URLs are for CodeSystem uris. + * + * @param + * @param repository the repository to search + * @param canonical the canonical url to search for + * @return + */ + private static > Class getResourceType( + Repository repository, CanonicalType canonical) { + Class resourceType = null; + try { + resourceType = repository + .fhirContext() + .getResourceDefinition(Canonicals.getResourceType(canonical)) + .getImplementingClass(); + } catch (DataFormatException e) { + // TODO: Use the "cqf-resourceType" extension to figure this out, if it's present + // NOTE: This is based on the assumption that only CodeSystems don't follow the canonical pattern... + resourceType = + repository.fhirContext().getResourceDefinition("CodeSystem").getImplementingClass(); + } + + return resourceType; + } + + /** + * Gets the resource type for the given canonical, based on the convention that canonical + * URLs are of the form [base]/[resourceType]/[tail] + * + * If the URL does not conform to the convention, the cqf-resourceType extension is used + * to determine the type of the resource, if present. + * + * If no extension is present, the type of the canonical is assumed to be CodeSystem, on + * the grounds that most (if not all) non-conventional URLs are for CodeSystem uris. + * + * @param repository + * @param canonical + * @return + */ + private static Class getResourceType(Repository repository, String canonical) { + Class resourceType = null; + try { + resourceType = repository + .fhirContext() + .getResourceDefinition(Canonicals.getResourceType(canonical)) + .getImplementingClass(); + } catch (RuntimeException e) { + // TODO: Use the "cqf-resourceType" extension to figure this out, if it's present + // NOTE: This is based on the assumption that only CodeSystems don't follow the canonical pattern... + resourceType = + repository.fhirContext().getResourceDefinition("CodeSystem").getImplementingClass(); + } + + return resourceType; + } + /** * Searches the given Repository and returns the first entry found * @@ -95,10 +157,7 @@ IBaseResource searchRepositoryByCanonical( */ public static > IBaseBundle searchRepositoryByCanonicalWithPaging( Repository repository, CanonicalType canonical) { - var resourceType = repository - .fhirContext() - .getResourceDefinition(Canonicals.getResourceType(canonical)) - .getImplementingClass(); + var resourceType = getResourceType(repository, canonical); return searchRepositoryByCanonicalWithPaging(repository, canonical, resourceType); } @@ -113,10 +172,7 @@ public static > IBaseBundle searchR */ public static > IBaseBundle searchRepositoryByCanonicalWithPaging( Repository repository, String canonical) { - var resourceType = repository - .fhirContext() - .getResourceDefinition(Canonicals.getResourceType(canonical)) - .getImplementingClass(); + var resourceType = getResourceType(repository, canonical); return searchRepositoryByCanonicalWithPaging(repository, canonical, resourceType); } diff --git a/cqf-fhir-cql/src/main/java/org/opencds/cqf/fhir/cql/engine/utility/ValueSets.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/ValueSets.java similarity index 83% rename from cqf-fhir-cql/src/main/java/org/opencds/cqf/fhir/cql/engine/utility/ValueSets.java rename to cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/ValueSets.java index eea4c3e94..38eb63b09 100644 --- a/cqf-fhir-cql/src/main/java/org/opencds/cqf/fhir/cql/engine/utility/ValueSets.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/ValueSets.java @@ -1,4 +1,6 @@ -package org.opencds.cqf.fhir.cql.engine.utility; +package org.opencds.cqf.fhir.utility; + +import static org.opencds.cqf.fhir.utility.Resources.newBackboneElement; import ca.uhn.fhir.context.BaseRuntimeChildDefinition; import ca.uhn.fhir.context.BaseRuntimeChildDefinition.IAccessor; @@ -7,9 +9,11 @@ import ca.uhn.fhir.context.RuntimeResourceBlockDefinition; import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.fhirpath.IFhirPath; +import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.List; import org.hl7.fhir.instance.model.api.IBase; +import org.hl7.fhir.instance.model.api.IBaseBackboneElement; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.opencds.cqf.cql.engine.runtime.Code; @@ -172,9 +176,7 @@ public static IBase getExpansion(FhirContext fhirContext, IBaseResource valueSet return expansion.get(0); } - public static List getContains(FhirContext fhirContext, IBaseResource valueSet) { - IBase expansion = getExpansion(fhirContext, valueSet); - + public static List getContainsInExpansion(FhirContext fhirContext, IBase expansion) { if (expansion == null) { return null; } @@ -190,6 +192,10 @@ public static List getContains(FhirContext fhirContext, IBaseResource val return contains; } + public static List getContains(FhirContext fhirContext, IBaseResource valueSet) { + return getContainsInExpansion(fhirContext, getExpansion(fhirContext, valueSet)); + } + public static List getCodesInCompose(FhirContext fhirContext, IBaseResource valueSet) { List includes = getIncludes(fhirContext, valueSet); @@ -228,9 +234,7 @@ public static List getCodesInCompose(FhirContext fhirContext, IBaseResourc return codes; } - public static List getCodesInExpansion(FhirContext fhirContext, IBaseResource valueSet) { - List contains = getContains(fhirContext, valueSet); - + public static List getCodesInContains(FhirContext fhirContext, List contains) { if (contains == null) { return null; } @@ -258,6 +262,67 @@ public static List getCodesInExpansion(FhirContext fhirContext, IBaseResou return codes; } + public static List getCodesInExpansion(FhirContext fhirContext, IBase expansion) { + return getCodesInContains(fhirContext, getContainsInExpansion(fhirContext, expansion)); + } + + public static List getCodesInExpansion(FhirContext fhirContext, IBaseResource valueSet) { + return getCodesInContains(fhirContext, getContains(fhirContext, valueSet)); + } + + @SuppressWarnings("unchecked") + public static void addCodeToExpansion(FhirContext fhirContext, IBase expansion, Code code) + throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, + NoSuchMethodException, SecurityException { + var containsDef = getContainsDefinition(fhirContext); + var systemDef = getSystemDefinition(fhirContext); + var codeDef = getCodeDefinition(fhirContext); + var displayDef = getDisplayDefinition(fhirContext); + var versionDef = getVersionDefinition(fhirContext); + var newCode = newBackboneElement((Class) + containsDef.getChildByName("contains").getImplementingClass()); + systemDef + .getMutator() + .addValue( + newCode, + systemDef + .getChildByName("system") + .getImplementingClass() + .getConstructor(String.class) + .newInstance(code.getSystem())); + codeDef.getMutator() + .addValue( + newCode, + codeDef.getChildByName("code") + .getImplementingClass() + .getConstructor(String.class) + .newInstance(code.getCode())); + displayDef + .getMutator() + .addValue( + newCode, + displayDef + .getChildByName("display") + .getImplementingClass() + .getConstructor(String.class) + .newInstance(code.getDisplay())); + versionDef + .getMutator() + .addValue( + newCode, + versionDef + .getChildByName("version") + .getImplementingClass() + .getConstructor(String.class) + .newInstance(code.getVersion())); + containsDef.getMutator().addValue(expansion, newCode); + } + + public static void addParameterToExpansion( + FhirContext fhirContext, IBase expansion, IBaseBackboneElement parameter) { + getParameterDefinition(fhirContext).getMutator().addValue(expansion, parameter); + } + public static String getUrl(FhirContext fhirContext, IBaseResource valueSet) { BaseRuntimeChildDefinition urlDef = getUrlDefinition(fhirContext); return getStringValueFromPrimitiveAccessor(valueSet, urlDef.getAccessor()); @@ -268,11 +333,6 @@ public static String getId(FhirContext fhirContext, IBaseResource valueSet) { return getStringValueFromPrimitiveAccessor(valueSet, idDef.getAccessor()); } - public static String getResourceType(FhirContext fhirContext, IBaseResource resource) { - RuntimeResourceDefinition def = fhirContext.getResourceDefinition(resource); - return def.getName(); - } - private static String getStringValueFromPrimitiveAccessor(IBase value, IAccessor accessor) { if (value == null || accessor == null) { return null; @@ -370,6 +430,17 @@ private static BaseRuntimeChildDefinition getExpansionDefinition(FhirContext fhi return def.getChildByName("expansion"); } + private static BaseRuntimeChildDefinition getParameterDefinition(FhirContext fhirContext) { + BaseRuntimeChildDefinition expansionChild = getExpansionDefinition(fhirContext); + RuntimeResourceBlockDefinition expansionBlockChild = + (RuntimeResourceBlockDefinition) expansionChild.getChildByName("expansion"); + return getParameterDefinition(expansionBlockChild); + } + + private static BaseRuntimeChildDefinition getParameterDefinition(RuntimeResourceBlockDefinition expansionChild) { + return expansionChild.getChildByName("parameter"); + } + private static BaseRuntimeChildDefinition getContainsDefinition(FhirContext fhirContext) { BaseRuntimeChildDefinition expansionChild = getExpansionDefinition(fhirContext); RuntimeResourceBlockDefinition expansionBlockChild = diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/Adapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/Adapter.java index f5491d858..c18a97e6a 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/Adapter.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/Adapter.java @@ -1,6 +1,20 @@ package org.opencds.cqf.fhir.utility.adapter; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; import org.hl7.fhir.instance.model.api.IBase; +import org.hl7.fhir.instance.model.api.IBaseExtension; +import org.hl7.fhir.instance.model.api.ICompositeType; +import org.hl7.fhir.instance.model.api.IPrimitiveType; +import org.opencds.cqf.cql.engine.model.ModelResolver; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Marker interface for HL7 Structure adapters @@ -8,8 +22,180 @@ * @param An HL7 Structure Type */ public interface Adapter { + public static final Logger logger = LoggerFactory.getLogger(Adapter.class); + /** * @return returns the underlying HL7 Structure for this adapter */ T get(); + + public default void setExtension(List> extensions) { + try { + getModelResolver().setValue(get(), "extension", null); + getModelResolver().setValue(get(), "extension", extensions); + } catch (Exception e) { + // Do nothing + logger.debug("Field 'extension' does not exist on Element type {}", get().fhirType()); + } + } + + public default > void addExtension(E extension) { + try { + getModelResolver().setValue(get(), "extension", Collections.singletonList(extension)); + } catch (Exception e) { + // Do nothing + logger.debug("Field 'extension' does not exist on Element type {}", get().fhirType()); + } + } + + public default boolean hasExtension() { + return !getExtension().isEmpty(); + } + + public default boolean hasExtension(String url) { + return hasExtension(get(), url); + } + + public default List> getExtension() { + return getExtension(get()); + } + + public default IBaseExtension getExtensionByUrl(String url) { + return getExtensionByUrl(get(), url); + } + + public default List> getExtensionsByUrl(String url) { + return getExtensionsByUrl(get(), url); + } + + public FhirContext fhirContext(); + + public ModelResolver getModelResolver(); + + @SuppressWarnings("unchecked") + public default > List getExtension(IBase base) { + return resolvePathList(base, "extension").stream().map(e -> (E) e).collect(Collectors.toList()); + } + + public default List> getExtensionsByUrl(IBase base, String url) { + return getExtension(base).stream().filter(e -> e.getUrl().equals(url)).collect(Collectors.toList()); + } + + public default IBaseExtension getExtensionByUrl(IBase base, String url) { + return getExtensionsByUrl(base, url).stream().findFirst().orElse(null); + } + + public default Boolean hasExtension(IBase base, String url) { + return getExtension(base).stream().anyMatch(e -> e.getUrl().equals(url)); + } + + @SuppressWarnings("unchecked") + public default List resolvePathList(IBase base, String path) { + var pathResult = getModelResolver().resolvePath(base, path); + return pathResult instanceof List ? (List) pathResult : new ArrayList<>(); + } + + @SuppressWarnings("unchecked") + public default List resolvePathList(IBase base, String path, Class clazz) { + return resolvePathList(base, path).stream().map(i -> (B) i).collect(Collectors.toList()); + } + + @SuppressWarnings("unchecked") + public default String resolvePathString(IBase base, String path) { + var result = (IPrimitiveType) resolvePath(base, path); + return result == null ? null : result.getValue(); + } + + public default IBase resolvePath(IBase base, String path) { + return (IBase) getModelResolver().resolvePath(base, path); + } + + @SuppressWarnings("unchecked") + public default B resolvePath(IBase base, String path, Class clazz) { + return (B) resolvePath(base, path); + } + + @SuppressWarnings("unchecked") + static T newPeriod(FhirVersionEnum version) { + switch (version) { + case DSTU3: + return (T) new org.hl7.fhir.dstu3.model.Period(); + case R4: + return (T) new org.hl7.fhir.r4.model.Period(); + case R5: + return (T) new org.hl7.fhir.r5.model.Period(); + default: + throw new UnprocessableEntityException("Unsupported version: " + version.toString()); + } + } + + @SuppressWarnings("unchecked") + static > T newStringType(FhirVersionEnum version, String string) { + switch (version) { + case DSTU3: + return (T) new org.hl7.fhir.dstu3.model.StringType(string); + case R4: + return (T) new org.hl7.fhir.r4.model.StringType(string); + case R5: + return (T) new org.hl7.fhir.r5.model.StringType(string); + default: + throw new UnprocessableEntityException("Unsupported version: " + version.toString()); + } + } + + @SuppressWarnings("unchecked") + static > T newUriType(FhirVersionEnum version, String string) { + switch (version) { + case DSTU3: + return (T) new org.hl7.fhir.dstu3.model.UriType(string); + case R4: + return (T) new org.hl7.fhir.r4.model.UriType(string); + case R5: + return (T) new org.hl7.fhir.r5.model.UriType(string); + default: + throw new UnprocessableEntityException("Unsupported version: " + version.toString()); + } + } + + @SuppressWarnings("unchecked") + static > T newUrlType(FhirVersionEnum version, String string) { + switch (version) { + case DSTU3: + return (T) new org.hl7.fhir.dstu3.model.UriType(string); + case R4: + return (T) new org.hl7.fhir.r4.model.UrlType(string); + case R5: + return (T) new org.hl7.fhir.r5.model.UrlType(string); + default: + throw new UnprocessableEntityException("Unsupported version: " + version.toString()); + } + } + + @SuppressWarnings("unchecked") + static > T newDateType(FhirVersionEnum version, Date date) { + switch (version) { + case DSTU3: + return (T) new org.hl7.fhir.dstu3.model.DateType(date); + case R4: + return (T) new org.hl7.fhir.r4.model.DateType(date); + case R5: + return (T) new org.hl7.fhir.r5.model.DateType(date); + default: + throw new UnprocessableEntityException("Unsupported version: " + version.toString()); + } + } + + @SuppressWarnings("unchecked") + static > T newDateTimeType(FhirVersionEnum version, Date date) { + switch (version) { + case DSTU3: + return (T) new org.hl7.fhir.dstu3.model.DateTimeType(date); + case R4: + return (T) new org.hl7.fhir.r4.model.DateTimeType(date); + case R5: + return (T) new org.hl7.fhir.r5.model.DateTimeType(date); + default: + throw new UnprocessableEntityException("Unsupported version: " + version.toString()); + } + } } diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/AdapterFactory.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/AdapterFactory.java index 6bf1ab554..5566c10bc 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/AdapterFactory.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/AdapterFactory.java @@ -1,5 +1,6 @@ package org.opencds.cqf.fhir.utility.adapter; +import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; import org.hl7.fhir.instance.model.api.IBaseBackboneElement; import org.hl7.fhir.instance.model.api.IBaseParameters; @@ -9,6 +10,10 @@ public interface AdapterFactory { + public static AdapterFactory forFhirContext(FhirContext fhirContext) { + return forFhirVersion(fhirContext.getVersion().getVersion()); + } + public static AdapterFactory forFhirVersion(FhirVersionEnum fhirVersion) { switch (fhirVersion) { case DSTU3: @@ -24,6 +29,10 @@ public static AdapterFactory forFhirVersion(FhirVersionEnum fhirVersion) { } } + public static ResourceAdapter createAdapterForResource(IBaseResource resource) { + return forFhirVersion(resource.getStructureFhirVersionEnum()).createResource(resource); + } + /** * Creates an adapter that exposes common resource operations across multiple versions of FHIR * @@ -73,4 +82,12 @@ public static AdapterFactory forFhirVersion(FhirVersionEnum fhirVersion) { */ public ParametersParameterComponentAdapter createParametersParameters( IBaseBackboneElement parametersParametersComponent); + + /** + * Creates an adapter that exposes common Attachment operations across multiple versions of FHIR + * + * @param endpoint a FHIR Endpoint Resource + * @return an adapter exposing common api calls + */ + public EndpointAdapter createEndpoint(IBaseResource endpoint); } diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/BaseResourceAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/BaseResourceAdapter.java new file mode 100644 index 000000000..fb7f41123 --- /dev/null +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/BaseResourceAdapter.java @@ -0,0 +1,37 @@ +package org.opencds.cqf.fhir.utility.adapter; + +import ca.uhn.fhir.context.BaseRuntimeElementDefinition; +import ca.uhn.fhir.context.FhirContext; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.opencds.cqf.cql.engine.model.ModelResolver; +import org.opencds.cqf.fhir.utility.model.FhirModelResolverCache; + +public abstract class BaseResourceAdapter implements ResourceAdapter { + protected final FhirContext fhirContext; + protected final BaseRuntimeElementDefinition elementDefinition; + protected final IBaseResource resource; + protected final ModelResolver modelResolver; + + public BaseResourceAdapter(IBaseResource resource) { + if (resource == null) { + throw new IllegalArgumentException("resource can not be null"); + } + this.resource = resource; + fhirContext = FhirContext.forCached(resource.getStructureFhirVersionEnum()); + elementDefinition = fhirContext.getElementDefinition(this.resource.getClass()); + modelResolver = FhirModelResolverCache.resolverForVersion( + fhirContext.getVersion().getVersion()); + } + + public FhirContext fhirContext() { + return fhirContext; + } + + public ModelResolver getModelResolver() { + return modelResolver; + } + + public IBaseResource get() { + return resource; + } +} diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/EndpointAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/EndpointAdapter.java new file mode 100644 index 000000000..1fae75804 --- /dev/null +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/EndpointAdapter.java @@ -0,0 +1,15 @@ +package org.opencds.cqf.fhir.utility.adapter; + +import static org.opencds.cqf.fhir.utility.adapter.Adapter.newUrlType; + +public interface EndpointAdapter extends ResourceAdapter { + public default String getAddress() { + return resolvePathString(get(), "address"); + } + + public default void setAddress(String address) { + getModelResolver() + .setValue( + get(), "address", newUrlType(fhirContext().getVersion().getVersion(), address)); + } +} diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/KnowledgeArtifactAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/KnowledgeArtifactAdapter.java index 0072b98b6..5e0de4c0d 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/KnowledgeArtifactAdapter.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/KnowledgeArtifactAdapter.java @@ -1,5 +1,11 @@ package org.opencds.cqf.fhir.utility.adapter; +import static org.opencds.cqf.fhir.utility.adapter.Adapter.newDateTimeType; +import static org.opencds.cqf.fhir.utility.adapter.Adapter.newDateType; +import static org.opencds.cqf.fhir.utility.adapter.Adapter.newPeriod; +import static org.opencds.cqf.fhir.utility.adapter.Adapter.newStringType; +import static org.opencds.cqf.fhir.utility.adapter.Adapter.newUriType; + import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import java.util.Date; @@ -7,10 +13,10 @@ import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.Stream; +import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.dstu3.model.Reference; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseBundle; -import org.hl7.fhir.instance.model.api.IBaseExtension; import org.hl7.fhir.instance.model.api.IBaseHasExtensions; import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -21,173 +27,255 @@ import org.opencds.cqf.fhir.api.Repository; import org.opencds.cqf.fhir.utility.BundleHelper; import org.opencds.cqf.fhir.utility.visitor.KnowledgeArtifactVisitor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public interface KnowledgeArtifactAdapter extends ResourceAdapter { + public static final Logger logger = LoggerFactory.getLogger(KnowledgeArtifactAdapter.class); IDomainResource get(); IDomainResource copy(); default IIdType getId() { - return this.get().getIdElement(); + return get().getIdElement(); } default void setId(IIdType id) { - this.get().setId(id); + get().setId(id); } - String getName(); + default String getName() { + return resolvePathString(get(), "name"); + } - void setName(String name); + default void setName(String name) { + getModelResolver().setValue(get(), "name", newStringType(get().getStructureFhirVersionEnum(), name)); + } - boolean hasUrl(); + default boolean hasTitle() { + return StringUtils.isNotBlank(getTitle()); + } - String getUrl(); + default String getTitle() { + return resolvePathString(get(), "title"); + } - void setUrl(String url); + default void setTitle(String title) { + getModelResolver().setValue(get(), "title", title); + } - boolean hasVersion(); + default String getDescriptor() { + return String.format( + "%s %s%s", + this.get().fhirType(), + this.hasTitle() ? this.getTitle() : this.getName(), + this.hasVersion() ? ", " + this.getVersion() : ""); + } - String getVersion(); + default boolean hasUrl() { + return StringUtils.isNotBlank(getUrl()); + } - void setVersion(String version); + default String getUrl() { + return resolvePathString(get(), "url"); + } + + default void setUrl(String url) { + getModelResolver().setValue(get(), "url", newUriType(get().getStructureFhirVersionEnum(), url)); + } + + default boolean hasVersion() { + return StringUtils.isNotBlank(getVersion()); + } + + default String getVersion() { + return resolvePathString(get(), "version"); + } + + default void setVersion(String version) { + getModelResolver().setValue(get(), "version", newStringType(get().getStructureFhirVersionEnum(), version)); + } List getDependencies(); - Date getApprovalDate(); + default String getReferenceSource() { + return hasVersion() ? getUrl() + "|" + getVersion() : getUrl(); + } - void setApprovalDate(Date approvalDate); + default void addProfileReferences(List references, String referenceSource) { + get().getMeta().getProfile().stream() + .map(p -> (IBaseHasExtensions & IPrimitiveType) p) + .forEach(profile -> references.add(new DependencyInfo( + referenceSource, + profile.getValueAsString(), + profile.getExtension(), + (reference) -> profile.setValue(reference)))); + } - Date getDate(); + @SuppressWarnings("unchecked") + default Date getApprovalDate() { + IPrimitiveType approvalDate = resolvePath(get(), "approvalDate", IPrimitiveType.class); + return approvalDate == null ? null : approvalDate.getValue(); + } - void setDate(Date date); + default void setApprovalDate(Date approvalDate) { + try { + getModelResolver() + .setValue(get(), "approvalDate", newDateType(get().getStructureFhirVersionEnum(), approvalDate)); + } catch (Exception e) { + // Do nothing + logger.debug("Field 'approvalDate' does not exist on Resource type {}", get().fhirType()); + } + } - void setDateElement(IPrimitiveType approvalDate); + @SuppressWarnings("unchecked") + default Date getDate() { + IPrimitiveType date = resolvePath(get(), "date", IPrimitiveType.class); + return date == null ? null : date.getValue(); + } - String getStatus(); + default void setDate(Date date) { + getModelResolver().setValue(get(), "date", newDateTimeType(get().getStructureFhirVersionEnum(), date)); + } - String getPurpose(); + default void setDateElement(IPrimitiveType date) { + getModelResolver().setValue(get(), "date", date); + } + + default String getPurpose() { + return resolvePathString(get(), "purpose"); + } + + String getStatus(); void setStatus(String status); - ICompositeType getEffectivePeriod(); + default ICompositeType getEffectivePeriod() { + var effectivePeriod = resolvePath(get(), "effectivePeriod", ICompositeType.class); + return effectivePeriod == null ? newPeriod(get().getStructureFhirVersionEnum()) : effectivePeriod; + } - boolean getExperimental(); + default void setEffectivePeriod(ICompositeType period) { + try { + getModelResolver().setValue(get(), "effectivePeriod", period); + } catch (Exception e) { + // Do nothing + logger.debug("Field 'effectivePeriod' does not exist on Resource type {}", get().fhirType()); + } + } - void setExtension(List> extensions); + @SuppressWarnings("unchecked") + default boolean getExperimental() { + var experimental = (IPrimitiveType) resolvePath(get(), "experimental", IPrimitiveType.class); + return experimental == null ? false : experimental.getValue(); + } @SuppressWarnings("unchecked") static T newRelatedArtifact( - FhirVersionEnum version, String type, String reference) { + FhirVersionEnum version, String type, String reference, String display) { switch (version) { case DSTU3: var dstu3 = new org.hl7.fhir.dstu3.model.RelatedArtifact(); dstu3.setType(org.hl7.fhir.dstu3.model.RelatedArtifact.RelatedArtifactType.fromCode(type)) - .setResource(new Reference(reference)); + .setResource(new Reference(reference)) + .setDisplay(display); return (T) dstu3; case R4: var r4 = new org.hl7.fhir.r4.model.RelatedArtifact(); r4.setType(org.hl7.fhir.r4.model.RelatedArtifact.RelatedArtifactType.fromCode(type)) - .setResource(reference); + .setResource(reference) + .setDisplay(display); return (T) r4; case R5: var r5 = new org.hl7.fhir.r5.model.RelatedArtifact(); r5.setType(org.hl7.fhir.r5.model.RelatedArtifact.RelatedArtifactType.fromCode(type)) - .setResource(reference); + .setResource(reference) + .setDisplay(display); return (T) r5; default: - throw new UnprocessableEntityException("Upsupported version: " + version.toString()); + throw new UnprocessableEntityException("Unsupported version: " + version.toString()); } } static String getRelatedArtifactReference(T relatedArtifact) { if (relatedArtifact instanceof org.hl7.fhir.dstu3.model.RelatedArtifact) { - return getRelatedArtifactReference((org.hl7.fhir.dstu3.model.RelatedArtifact) relatedArtifact); + return ((org.hl7.fhir.dstu3.model.RelatedArtifact) relatedArtifact) + .getResource() + .getReference(); } else if (relatedArtifact instanceof org.hl7.fhir.r4.model.RelatedArtifact) { - return getRelatedArtifactReference((org.hl7.fhir.r4.model.RelatedArtifact) relatedArtifact); + return ((org.hl7.fhir.r4.model.RelatedArtifact) relatedArtifact).getResource(); } else if (relatedArtifact instanceof org.hl7.fhir.r5.model.RelatedArtifact) { - return getRelatedArtifactReference((org.hl7.fhir.r5.model.RelatedArtifact) relatedArtifact); + return ((org.hl7.fhir.r5.model.RelatedArtifact) relatedArtifact).getResource(); } else { throw new UnprocessableEntityException("Must be a valid RelatedArtifact"); } } - private static String getRelatedArtifactReference(org.hl7.fhir.dstu3.model.RelatedArtifact relatedArtifact) { - return relatedArtifact.getResource().getReference(); - } - - private static String getRelatedArtifactReference(org.hl7.fhir.r4.model.RelatedArtifact relatedArtifact) { - return relatedArtifact.getResource(); - } - - private static String getRelatedArtifactReference(org.hl7.fhir.r5.model.RelatedArtifact relatedArtifact) { - return relatedArtifact.getResource(); - } - static String getRelatedArtifactType(T relatedArtifact) { if (relatedArtifact instanceof org.hl7.fhir.dstu3.model.RelatedArtifact) { - return getRelatedArtifactType((org.hl7.fhir.dstu3.model.RelatedArtifact) relatedArtifact); + return ((org.hl7.fhir.dstu3.model.RelatedArtifact) relatedArtifact) + .getType() + .toCode(); } else if (relatedArtifact instanceof org.hl7.fhir.r4.model.RelatedArtifact) { - return getRelatedArtifactType((org.hl7.fhir.r4.model.RelatedArtifact) relatedArtifact); + return ((org.hl7.fhir.r4.model.RelatedArtifact) relatedArtifact) + .getType() + .toCode(); } else if (relatedArtifact instanceof org.hl7.fhir.r5.model.RelatedArtifact) { - return getRelatedArtifactType((org.hl7.fhir.r5.model.RelatedArtifact) relatedArtifact); + return ((org.hl7.fhir.r5.model.RelatedArtifact) relatedArtifact) + .getType() + .toCode(); } else { throw new UnprocessableEntityException("Must be a valid RelatedArtifact"); } } - private static String getRelatedArtifactType(org.hl7.fhir.dstu3.model.RelatedArtifact relatedArtifact) { - return relatedArtifact.getType().toCode(); - } - - private static String getRelatedArtifactType(org.hl7.fhir.r4.model.RelatedArtifact relatedArtifact) { - return relatedArtifact.getType().toCode(); - } - - private static String getRelatedArtifactType(org.hl7.fhir.r5.model.RelatedArtifact relatedArtifact) { - return relatedArtifact.getType().toCode(); - } - static void setRelatedArtifactReference( - T relatedArtifact, String reference) { + T relatedArtifact, String reference, String display) { if (relatedArtifact instanceof org.hl7.fhir.dstu3.model.RelatedArtifact) { - setRelatedArtifactReference((org.hl7.fhir.dstu3.model.RelatedArtifact) relatedArtifact, reference); + ((org.hl7.fhir.dstu3.model.RelatedArtifact) relatedArtifact) + .getResource() + .setReference(reference) + .setDisplay(display); } else if (relatedArtifact instanceof org.hl7.fhir.r4.model.RelatedArtifact) { - setRelatedArtifactReference((org.hl7.fhir.r4.model.RelatedArtifact) relatedArtifact, reference); + ((org.hl7.fhir.r4.model.RelatedArtifact) relatedArtifact) + .setResource(reference) + .setDisplay(display); } else if (relatedArtifact instanceof org.hl7.fhir.r5.model.RelatedArtifact) { - setRelatedArtifactReference((org.hl7.fhir.r5.model.RelatedArtifact) relatedArtifact, reference); + ((org.hl7.fhir.r5.model.RelatedArtifact) relatedArtifact) + .setResource(reference) + .setDisplay(display); } else { throw new UnprocessableEntityException("Must be a valid RelatedArtifact"); } } - ; - private static void setRelatedArtifactReference( - org.hl7.fhir.dstu3.model.RelatedArtifact relatedArtifact, String reference) { - relatedArtifact.getResource().setReference(reference); + default boolean hasRelatedArtifact() { + return !getRelatedArtifact().isEmpty(); } - private static void setRelatedArtifactReference( - org.hl7.fhir.r4.model.RelatedArtifact relatedArtifact, String reference) { - relatedArtifact.setResource(reference); + @SuppressWarnings("unchecked") + default List getRelatedArtifact() { + return resolvePathList(get(), "relatedArtifact").stream() + .map(r -> (T) r) + .collect(Collectors.toList()); } - private static void setRelatedArtifactReference( - org.hl7.fhir.r5.model.RelatedArtifact relatedArtifact, String reference) { - relatedArtifact.setResource(reference); + default void setRelatedArtifact(List relatedArtifacts) { + try { + getModelResolver().setValue(get(), "relatedArtifact", null); + getModelResolver().setValue(get(), "relatedArtifact", relatedArtifacts); + } catch (Exception e) { + // Do nothing + logger.debug("Field 'relatedArtifact' does not exist on Resource type {}", get().fhirType()); + } } - void setEffectivePeriod(ICompositeType effectivePeriod); - - boolean hasRelatedArtifact(); - - List getRelatedArtifact(); - List getRelatedArtifactsOfType(String codeString); default List getComponents() { - return this.getRelatedArtifactsOfType("composed-of"); + return getRelatedArtifactsOfType("composed-of"); } static boolean checkIfRelatedArtifactIsOwned(T relatedArtifact) { @@ -196,7 +284,7 @@ static boolean checkIfRelatedArt } default List combineComponentsAndDependencies() { - final String referenceSource = this.hasVersion() ? getUrl() + "|" + getVersion() : getUrl(); + final String referenceSource = hasVersion() ? getUrl() + "|" + getVersion() : getUrl(); return Stream.concat( getComponents().stream() .filter(ra -> ra != null) @@ -205,9 +293,9 @@ default List combineComponentsAndDependencies() { .collect(Collectors.toList()); } - void setRelatedArtifact(List relatedArtifacts); - - public IBase accept(KnowledgeArtifactVisitor visitor, Repository repository, IBaseParameters operationParameters); + default IBase accept(KnowledgeArtifactVisitor visitor, Repository repository, IBaseParameters operationParameters) { + return visitor.visit(this, repository, operationParameters); + } @SuppressWarnings("unchecked") default List getOwnedRelatedArtifacts() { diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/LibraryAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/LibraryAdapter.java index 00b108b4f..591050b8b 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/LibraryAdapter.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/LibraryAdapter.java @@ -1,5 +1,6 @@ package org.opencds.cqf.fhir.utility.adapter; +import java.util.Arrays; import java.util.List; import org.hl7.fhir.instance.model.api.ICompositeType; @@ -8,6 +9,9 @@ */ public interface LibraryAdapter extends KnowledgeArtifactAdapter { + List LIBRARY_TYPES = + Arrays.asList("logic-library", "model-definition", "asset-collection", "module-definition"); + boolean hasContent(); List getContent(); @@ -15,4 +19,16 @@ public interface LibraryAdapter extends KnowledgeArtifactAdapter { void setContent(List attachments); ICompositeType addContent(); + + ICompositeType getType(); + + LibraryAdapter setType(String type); + + List getDataRequirement(); + + LibraryAdapter addDataRequirement(ICompositeType dataRequirement); + + LibraryAdapter setDataRequirement(List dataRequirement); + + List getUseContext(); } diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/MeasureAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/MeasureAdapter.java new file mode 100644 index 000000000..fa5382518 --- /dev/null +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/MeasureAdapter.java @@ -0,0 +1,16 @@ +package org.opencds.cqf.fhir.utility.adapter; + +import java.util.Arrays; +import java.util.List; +import org.opencds.cqf.fhir.utility.Constants; + +/** + * This interface exposes common functionality across all FHIR Questionnaire versions. + */ +public interface MeasureAdapter { + public static List CANONICAL_EXTENSIONS = + Arrays.asList(Constants.CQFM_EFFECTIVE_DATA_REQUIREMENTS, Constants.CRMI_EFFECTIVE_DATA_REQUIREMENTS); + + public static List REFERENCE_EXTENSIONS = Arrays.asList( + Constants.CQFM_INPUT_PARAMETERS, Constants.CQF_EXPANSION_PARAMETERS, Constants.CQF_CQL_OPTIONS); +} diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/ParametersAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/ParametersAdapter.java index c83090ffc..0363e8ce8 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/ParametersAdapter.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/ParametersAdapter.java @@ -1,14 +1,12 @@ package org.opencds.cqf.fhir.utility.adapter; import java.util.List; +import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseBackboneElement; import org.hl7.fhir.instance.model.api.IBaseDatatype; -import org.hl7.fhir.instance.model.api.IBaseResource; public interface ParametersAdapter extends ResourceAdapter { - public IBaseResource get(); - public List getParameter(); public IBaseBackboneElement getParameter(String name); @@ -17,5 +15,9 @@ public interface ParametersAdapter extends ResourceAdapter { public void setParameter(List parametersParameterComponents); + public void addParameter(IBase parameter); + + public void addParameter(String name, IBase value); + public IBaseBackboneElement addParameter(); } diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/ParametersParameterComponentAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/ParametersParameterComponentAdapter.java index 2dfe345ab..8ce61c679 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/ParametersParameterComponentAdapter.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/ParametersParameterComponentAdapter.java @@ -3,7 +3,6 @@ import java.util.List; import org.hl7.fhir.instance.model.api.IBaseBackboneElement; import org.hl7.fhir.instance.model.api.IBaseDatatype; -import org.hl7.fhir.instance.model.api.IBaseExtension; import org.hl7.fhir.instance.model.api.IBaseResource; public interface ParametersParameterComponentAdapter extends Adapter { @@ -35,8 +34,4 @@ public interface ParametersParameterComponentAdapter extends Adapter> getExtension(); } diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/QuestionnaireAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/QuestionnaireAdapter.java new file mode 100644 index 000000000..80e9108cb --- /dev/null +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/QuestionnaireAdapter.java @@ -0,0 +1,23 @@ +package org.opencds.cqf.fhir.utility.adapter; + +import java.util.Arrays; +import java.util.List; +import org.opencds.cqf.fhir.utility.Constants; + +/** + * This interface exposes common functionality across all FHIR Questionnaire versions. + */ +public interface QuestionnaireAdapter { + public static List REFERENCE_EXTENSIONS = Arrays.asList( + Constants.QUESTIONNAIRE_UNIT_VALUE_SET, + Constants.QUESTIONNAIRE_REFERENCE_PROFILE, + Constants.SDC_QUESTIONNAIRE_LOOKUP_QUESTIONNAIRE, + Constants.SDC_QUESTIONNAIRE_SUB_QUESTIONNAIRE); + + public static List EXPRESSION_EXTENSIONS = Arrays.asList( + Constants.VARIABLE_EXTENSION, + Constants.SDC_QUESTIONNAIRE_CANDIDATE_EXPRESSION, + Constants.SDC_QUESTIONNAIRE_INITIAL_EXPRESSION, + Constants.SDC_QUESTIONNAIRE_CALCULATED_EXPRESSION, + Constants.CQF_EXPRESSION); +} diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/ResourceAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/ResourceAdapter.java index c2e6f2b13..174339e1f 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/ResourceAdapter.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/ResourceAdapter.java @@ -1,5 +1,6 @@ package org.opencds.cqf.fhir.utility.adapter; +import java.util.List; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -29,4 +30,20 @@ public interface ResourceAdapter extends Adapter { public boolean equalsDeep(IBase other); public boolean equalsShallow(IBase other); + + public default List getContained() { + return getContained(get()); + } + + public default boolean hasContained() { + return hasContained(get()); + } + + public default List getContained(IBaseResource base) { + return resolvePathList(base, "contained", IBaseResource.class); + } + + public default Boolean hasContained(IBaseResource base) { + return !getContained(base).isEmpty(); + } } diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/ValueSetAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/ValueSetAdapter.java index 4197ff380..ef60953c4 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/ValueSetAdapter.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/ValueSetAdapter.java @@ -1,6 +1,52 @@ package org.opencds.cqf.fhir.utility.adapter; +import java.util.List; +import org.hl7.fhir.instance.model.api.IBaseBackboneElement; + /** * This interface exposes common functionality across all FHIR ValueSet versions. */ -public interface ValueSetAdapter extends KnowledgeArtifactAdapter {} +public interface ValueSetAdapter extends KnowledgeArtifactAdapter { + public void setExpansion(T expansion); + + public T getExpansion(); + + public T newExpansion(); + + public List getValueSetIncludes(); + + // TODO: lots of duplicate code across version adapters + // public default List getValueSetIncludes() { + // return ValueSets.getIncludes(fhirContext(), get()).stream().map(i -> + // (List) resolvePathList(i, "valueSet", IPrimitiveType.class) + // ) + // .flatMap(Collection::stream) + // .map(i -> i.getValueAsString()) + // .collect(Collectors.toList()); + // } + + /** + * A simple compose element of a ValueSet must have a compose without an exclude element. Each element of the + * include cannot have a filter or reference a ValueSet and must have a system and enumerate concepts. + * + * @return boolean + */ + public boolean hasSimpleCompose(); + + /** + * A grouping compose element of a ValueSet must have a compose without an exclude element and each element of the + * include must reference a ValueSet. + * + * @return boolean + */ + public boolean hasGroupingCompose(); + + /** + * Performs a naive expansion on the ValueSet by collecting all codes within the compose. Can only be performed on a ValueSet with a simple compose. + */ + public void naiveExpand(); + + public boolean hasNaiveParameter(); + + public T createNaiveParameter(); +} diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/AdapterFactory.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/AdapterFactory.java index 5ca999c64..81b473f84 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/AdapterFactory.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/AdapterFactory.java @@ -1,16 +1,20 @@ package org.opencds.cqf.fhir.utility.adapter.dstu3; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import org.hl7.fhir.dstu3.model.Endpoint; import org.hl7.fhir.dstu3.model.Library; +import org.hl7.fhir.dstu3.model.Measure; import org.hl7.fhir.dstu3.model.MetadataResource; +import org.hl7.fhir.dstu3.model.Parameters; import org.hl7.fhir.dstu3.model.PlanDefinition; +import org.hl7.fhir.dstu3.model.Questionnaire; +import org.hl7.fhir.dstu3.model.StructureDefinition; import org.hl7.fhir.dstu3.model.ValueSet; import org.hl7.fhir.instance.model.api.IBaseBackboneElement; import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.ICompositeType; import org.hl7.fhir.instance.model.api.IDomainResource; -import org.opencds.cqf.fhir.utility.adapter.LibraryAdapter; public class AdapterFactory implements org.opencds.cqf.fhir.utility.adapter.AdapterFactory { @@ -18,6 +22,10 @@ public class AdapterFactory implements org.opencds.cqf.fhir.utility.adapter.Adap public org.opencds.cqf.fhir.utility.adapter.ResourceAdapter createResource(IBaseResource resource) { if (resource instanceof MetadataResource) { return createKnowledgeArtifactAdapter((MetadataResource) resource); + } else if (resource instanceof Endpoint) { + return createEndpoint(resource); + } else if (resource instanceof Parameters) { + return createParameters((Parameters) resource); } else { return new ResourceAdapter(resource); } @@ -26,28 +34,33 @@ public org.opencds.cqf.fhir.utility.adapter.ResourceAdapter createResource(IBase @Override public org.opencds.cqf.fhir.utility.adapter.KnowledgeArtifactAdapter createKnowledgeArtifactAdapter( IDomainResource resource) { - org.opencds.cqf.fhir.utility.adapter.KnowledgeArtifactAdapter retval; + org.opencds.cqf.fhir.utility.adapter.KnowledgeArtifactAdapter adapter; if (resource instanceof Library) { - retval = createLibrary(resource); + adapter = createLibrary(resource); + } else if (resource instanceof Measure) { + adapter = new MeasureAdapter((Measure) resource); } else if (resource instanceof PlanDefinition) { - retval = new org.opencds.cqf.fhir.utility.adapter.dstu3.PlanDefinitionAdapter((PlanDefinition) resource); + adapter = new PlanDefinitionAdapter((PlanDefinition) resource); + } else if (resource instanceof Questionnaire) { + adapter = new QuestionnaireAdapter((Questionnaire) resource); + } else if (resource instanceof StructureDefinition) { + adapter = new StructureDefinitionAdapter((StructureDefinition) resource); } else if (resource instanceof ValueSet) { - retval = new org.opencds.cqf.fhir.utility.adapter.dstu3.ValueSetAdapter((ValueSet) resource); + adapter = new ValueSetAdapter((ValueSet) resource); } else { if (resource instanceof MetadataResource) { - retval = new org.opencds.cqf.fhir.utility.adapter.dstu3.KnowledgeArtifactAdapter( - (MetadataResource) resource); + adapter = new KnowledgeArtifactAdapter((MetadataResource) resource); } else { throw new UnprocessableEntityException( - String.format("Resouce must be instance of %s", MetadataResource.class.getName())); + String.format("Resource must be instance of %s", MetadataResource.class.getName())); } } - return retval; + return adapter; } @Override - public LibraryAdapter createLibrary(IBaseResource library) { - return new org.opencds.cqf.fhir.utility.adapter.dstu3.LibraryAdapter((IDomainResource) library); + public org.opencds.cqf.fhir.utility.adapter.LibraryAdapter createLibrary(IBaseResource library) { + return new LibraryAdapter((IDomainResource) library); } @Override @@ -65,4 +78,9 @@ public org.opencds.cqf.fhir.utility.adapter.ParametersParameterComponentAdapter IBaseBackboneElement parametersParametersComponent) { return new ParametersParameterComponentAdapter(parametersParametersComponent); } + + @Override + public org.opencds.cqf.fhir.utility.adapter.EndpointAdapter createEndpoint(IBaseResource endpoint) { + return new EndpointAdapter(endpoint); + } } diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/AttachmentAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/AttachmentAdapter.java index da00e4daf..0512a5e49 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/AttachmentAdapter.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/AttachmentAdapter.java @@ -1,11 +1,16 @@ package org.opencds.cqf.fhir.utility.adapter.dstu3; +import ca.uhn.fhir.context.FhirContext; import org.hl7.fhir.dstu3.model.Attachment; import org.hl7.fhir.instance.model.api.ICompositeType; +import org.opencds.cqf.cql.engine.model.ModelResolver; +import org.opencds.cqf.fhir.utility.model.FhirModelResolverCache; class AttachmentAdapter implements org.opencds.cqf.fhir.utility.adapter.AttachmentAdapter { - private Attachment attachment; + private final Attachment attachment; + private final FhirContext fhirContext; + private final ModelResolver modelResolver; public AttachmentAdapter(ICompositeType attachment) { if (attachment == null) { @@ -21,6 +26,9 @@ public AttachmentAdapter(ICompositeType attachment) { } this.attachment = (Attachment) attachment; + fhirContext = FhirContext.forDstu3Cached(); + modelResolver = FhirModelResolverCache.resolverForVersion( + fhirContext.getVersion().getVersion()); } protected Attachment getAttachment() { @@ -51,4 +59,14 @@ public byte[] getData() { public void setData(byte[] data) { this.getAttachment().setData(data); } + + @Override + public FhirContext fhirContext() { + return fhirContext; + } + + @Override + public ModelResolver getModelResolver() { + return modelResolver; + } } diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/EndpointAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/EndpointAdapter.java new file mode 100644 index 000000000..ea60fb1cb --- /dev/null +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/EndpointAdapter.java @@ -0,0 +1,13 @@ +package org.opencds.cqf.fhir.utility.adapter.dstu3; + +import org.hl7.fhir.dstu3.model.Endpoint; +import org.hl7.fhir.instance.model.api.IBaseResource; + +public class EndpointAdapter extends ResourceAdapter implements org.opencds.cqf.fhir.utility.adapter.EndpointAdapter { + public EndpointAdapter(IBaseResource endpoint) { + super(endpoint); + if (!(endpoint instanceof Endpoint)) { + throw new IllegalArgumentException("resource passed as endpoint argument is not an Endpoint resource"); + } + } +} diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/KnowledgeArtifactAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/KnowledgeArtifactAdapter.java index 510383d12..fc7d566e0 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/KnowledgeArtifactAdapter.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/KnowledgeArtifactAdapter.java @@ -7,99 +7,43 @@ import java.util.stream.Collectors; import org.hl7.fhir.dstu3.model.DateTimeType; import org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus; -import org.hl7.fhir.dstu3.model.Extension; import org.hl7.fhir.dstu3.model.MetadataResource; import org.hl7.fhir.dstu3.model.Period; import org.hl7.fhir.dstu3.model.RelatedArtifact; import org.hl7.fhir.dstu3.model.RelatedArtifact.RelatedArtifactType; import org.hl7.fhir.exceptions.FHIRException; -import org.hl7.fhir.instance.model.api.IBase; -import org.hl7.fhir.instance.model.api.IBaseExtension; import org.hl7.fhir.instance.model.api.IBaseHasExtensions; -import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.ICompositeType; +import org.hl7.fhir.instance.model.api.IDomainResource; import org.hl7.fhir.instance.model.api.IPrimitiveType; -import org.opencds.cqf.fhir.api.Repository; import org.opencds.cqf.fhir.utility.adapter.IDependencyInfo; -import org.opencds.cqf.fhir.utility.visitor.KnowledgeArtifactVisitor; public class KnowledgeArtifactAdapter extends ResourceAdapter implements org.opencds.cqf.fhir.utility.adapter.KnowledgeArtifactAdapter { MetadataResource adaptedResource; + public KnowledgeArtifactAdapter(IDomainResource resource) { + super(resource); + if (!(resource instanceof MetadataResource)) { + throw new IllegalArgumentException( + "resource passed as resource argument is not a MetadataResource resource"); + } + adaptedResource = (MetadataResource) resource; + } + public KnowledgeArtifactAdapter(MetadataResource resource) { super(resource); - this.adaptedResource = resource; + adaptedResource = resource; } @Override public MetadataResource get() { - return this.adaptedResource; + return adaptedResource; } @Override public MetadataResource copy() { - return this.get().copy(); - } - - @Override - public boolean hasUrl() { - return this.get().hasUrl(); - } - - @Override - public String getUrl() { - return this.get().getUrl(); - } - - @Override - public void setUrl(String url) { - this.get().setUrl(url); - } - - @Override - public void setVersion(String version) { - this.get().setVersion(version); - } - - @Override - public String getVersion() { - return this.get().getVersion(); - } - - @Override - public boolean hasVersion() { - return this.get().hasVersion(); - } - - @Override - public String getName() { - return this.get().getName(); - } - - @Override - public String getPurpose() { - return null; - } - - @Override - public void setName(String name) { - this.get().setName(name); - } - - @Override - public Date getApprovalDate() { - return null; - } - - @Override - public Date getDate() { - return this.get().getDate(); - } - - @Override - public void setDate(Date date) { - this.get().setDate(date); + return get().copy(); } @Override @@ -107,27 +51,7 @@ public void setDateElement(IPrimitiveType date) { if (date != null && !(date instanceof DateTimeType)) { throw new UnprocessableEntityException("Date must be " + DateTimeType.class.getName()); } - this.get().setDateElement((DateTimeType) date); - } - - @Override - public void setApprovalDate(Date approvalDate) { - // do nothing - } - - @Override - public Period getEffectivePeriod() { - return new Period(); - } - - @Override - public List getDependencies() { - return new ArrayList<>(); - } - - @Override - public IBase accept(KnowledgeArtifactVisitor visitor, Repository repository, IBaseParameters operationParameters) { - return visitor.visit(this, repository, operationParameters); + org.opencds.cqf.fhir.utility.adapter.KnowledgeArtifactAdapter.super.setDateElement(date); } @Override @@ -135,18 +59,15 @@ public void setEffectivePeriod(ICompositeType effectivePeriod) { if (effectivePeriod != null && !(effectivePeriod instanceof Period)) { throw new UnprocessableEntityException("EffectivePeriod must be a valid " + Period.class.getName()); } - // does nothing + org.opencds.cqf.fhir.utility.adapter.KnowledgeArtifactAdapter.super.setEffectivePeriod(effectivePeriod); } @Override - public boolean hasRelatedArtifact() { - return false; - } - - @SuppressWarnings("unchecked") - @Override - public List getRelatedArtifact() { - return new ArrayList(); + public List getDependencies() { + List references = new ArrayList<>(); + final String referenceSource = getReferenceSource(); + addProfileReferences(references, referenceSource); + return references; } @SuppressWarnings("unchecked") @@ -158,14 +79,15 @@ public List getRelatedArtifactsOfType(String codeString) { } catch (FHIRException e) { throw new UnprocessableEntityException("Invalid related artifact code"); } - return this.getRelatedArtifact().stream() + return getRelatedArtifact().stream() + .map(ra -> (RelatedArtifact) ra) .filter(ra -> ra.getType() == type) .collect(Collectors.toList()); } @Override - public void setRelatedArtifact(List relatedArtifacts) { - // does nothing + public String getStatus() { + return get().getStatus() == null ? null : get().getStatus().toCode(); } @Override @@ -176,21 +98,21 @@ public void setStatus(String statusCodeString) { } catch (FHIRException e) { throw new UnprocessableEntityException("Invalid status code"); } - this.get().setStatus(status); - } - - @Override - public String getStatus() { - return this.get().getStatus() == null ? null : this.get().getStatus().toCode(); - } - - @Override - public boolean getExperimental() { - return this.get().getExperimental(); - } - - @Override - public void setExtension(List> extensions) { - this.get().setExtension(extensions.stream().map(e -> (Extension) e).collect(Collectors.toList())); + get().setStatus(status); + } + + @Override + public void setRelatedArtifact(List relatedArtifacts) + throws UnprocessableEntityException { + org.opencds.cqf.fhir.utility.adapter.KnowledgeArtifactAdapter.super.setRelatedArtifact(relatedArtifacts.stream() + .map(ra -> { + try { + return (RelatedArtifact) ra; + } catch (ClassCastException e) { + throw new UnprocessableEntityException( + "All related artifacts must be of type " + RelatedArtifact.class.getName()); + } + }) + .collect(Collectors.toList())); } } diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/LibraryAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/LibraryAdapter.java index d376efa1a..73989dcf0 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/LibraryAdapter.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/LibraryAdapter.java @@ -2,159 +2,82 @@ import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import java.util.ArrayList; -import java.util.Date; import java.util.List; import java.util.stream.Collectors; import org.hl7.fhir.dstu3.model.Attachment; -import org.hl7.fhir.dstu3.model.DateTimeType; -import org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus; -import org.hl7.fhir.dstu3.model.Extension; +import org.hl7.fhir.dstu3.model.CodeableConcept; +import org.hl7.fhir.dstu3.model.Coding; +import org.hl7.fhir.dstu3.model.DataRequirement; import org.hl7.fhir.dstu3.model.Library; -import org.hl7.fhir.dstu3.model.Period; import org.hl7.fhir.dstu3.model.RelatedArtifact; -import org.hl7.fhir.dstu3.model.RelatedArtifact.RelatedArtifactType; -import org.hl7.fhir.exceptions.FHIRException; -import org.hl7.fhir.instance.model.api.IBase; -import org.hl7.fhir.instance.model.api.IBaseExtension; -import org.hl7.fhir.instance.model.api.IBaseHasExtensions; -import org.hl7.fhir.instance.model.api.IBaseParameters; +import org.hl7.fhir.dstu3.model.UsageContext; import org.hl7.fhir.instance.model.api.ICompositeType; import org.hl7.fhir.instance.model.api.IDomainResource; -import org.hl7.fhir.instance.model.api.IIdType; -import org.hl7.fhir.instance.model.api.IPrimitiveType; -import org.opencds.cqf.fhir.api.Repository; import org.opencds.cqf.fhir.utility.adapter.DependencyInfo; import org.opencds.cqf.fhir.utility.adapter.IDependencyInfo; -import org.opencds.cqf.fhir.utility.visitor.KnowledgeArtifactVisitor; - -class LibraryAdapter extends ResourceAdapter implements org.opencds.cqf.fhir.utility.adapter.LibraryAdapter { - - private Library library; +public class LibraryAdapter extends KnowledgeArtifactAdapter + implements org.opencds.cqf.fhir.utility.adapter.LibraryAdapter { public LibraryAdapter(IDomainResource library) { super(library); - if (!(library instanceof Library)) { throw new IllegalArgumentException("resource passed as library argument is not a Library resource"); } - - this.library = (Library) library; } public LibraryAdapter(Library library) { super(library); - this.library = library; - } - - public IBase accept(KnowledgeArtifactVisitor visitor, Repository repository, IBaseParameters operationParameters) { - return visitor.visit(this, repository, operationParameters); } protected Library getLibrary() { - return this.library; + return (Library) resource; } @Override public Library get() { - return this.library; + return (Library) resource; } @Override public Library copy() { - return this.get().copy(); - } - - @Override - public IIdType getId() { - return this.getLibrary().getIdElement(); - } - - @Override - public void setId(IIdType id) { - this.getLibrary().setId(id); - } - - @Override - public String getName() { - return this.getLibrary().getName(); - } - - @Override - public String getPurpose() { - return this.getLibrary().getPurpose(); - } - - @Override - public void setName(String name) { - this.getLibrary().setName(name); - } - - @Override - public String getUrl() { - return this.getLibrary().getUrl(); - } - - @Override - public boolean hasUrl() { - return this.getLibrary().hasUrl(); - } - - @Override - public void setUrl(String url) { - this.getLibrary().setUrl(url); - } - - @Override - public boolean hasVersion() { - return this.getLibrary().hasVersion(); - } - - @Override - public String getVersion() { - return this.getLibrary().getVersion(); - } - - @Override - public void setVersion(String version) { - this.getLibrary().setVersion(version); + return get().copy(); } @Override public boolean hasContent() { - return this.getLibrary().hasContent(); + return getLibrary().hasContent(); } @Override public List getContent() { - return this.getLibrary().getContent().stream().collect(Collectors.toList()); + return getLibrary().getContent().stream().collect(Collectors.toList()); } @Override public void setContent(List attachments) { List castAttachments = attachments.stream().map(x -> (Attachment) x).collect(Collectors.toList()); - this.getLibrary().setContent(castAttachments); + getLibrary().setContent(castAttachments); } @Override public Attachment addContent() { - return this.getLibrary().addContent(); + return getLibrary().addContent(); } @Override public List getDependencies() { - List references = new ArrayList<>(); - final String referenceSource = - this.hasVersion() ? this.getUrl() + "|" + this.getLibrary().getVersion() : this.getUrl(); + List references = new ArrayList(); + final String referenceSource = getReferenceSource(); + addProfileReferences(references, referenceSource); // relatedArtifact[].resource - this.getRelatedArtifact().stream() + getRelatedArtifact().stream() + .map(ra -> (RelatedArtifact) ra) .filter(ra -> ra.hasResource()) .map(ra -> DependencyInfo.convertRelatedArtifact(ra, referenceSource)) .forEach(ra -> references.add(ra)); - - // dataRequirement - this.getLibrary().getDataRequirement().stream().forEach(dr -> { + getLibrary().getDataRequirement().stream().forEach(dr -> { dr.getProfile().stream() .filter(profile -> profile.hasValue()) .forEach(profile -> references.add(new DependencyInfo( @@ -173,113 +96,49 @@ public List getDependencies() { return references; } - @Override - public Date getApprovalDate() { - return this.getLibrary().getApprovalDate(); - } - - @Override - public void setApprovalDate(Date approvalDate) { - this.getLibrary().setApprovalDate(approvalDate); - } - - @Override - public Date getDate() { - return this.getLibrary().getDate(); - } - - @Override - public void setDate(Date date) { - this.getLibrary().setDate(date); - } - - @Override - public void setDateElement(IPrimitiveType date) { - if (date != null && !(date instanceof DateTimeType)) { - throw new UnprocessableEntityException("Date must be " + DateTimeType.class.getName()); - } - this.getLibrary().setDateElement((DateTimeType) date); - } - - @Override - public Period getEffectivePeriod() { - return this.getLibrary().getEffectivePeriod(); - } - - @Override - public boolean hasRelatedArtifact() { - return this.getLibrary().hasRelatedArtifact(); - } - - @SuppressWarnings("unchecked") - @Override - public List getRelatedArtifact() { - return this.getLibrary().getRelatedArtifact(); - } - @SuppressWarnings("unchecked") @Override - public List getRelatedArtifactsOfType(String codeString) { - RelatedArtifactType type; - try { - type = RelatedArtifactType.fromCode(codeString); - } catch (FHIRException e) { - throw new UnprocessableEntityException("Invalid related artifact code"); - } - return this.getRelatedArtifact().stream() - .filter(ra -> ra.getType() == type) - .collect(Collectors.toList()); + public List getComponents() { + return getRelatedArtifactsOfType("composed-of"); } @Override - public void setRelatedArtifact(List relatedArtifacts) - throws UnprocessableEntityException { - this.getLibrary() - .setRelatedArtifact(relatedArtifacts.stream() - .map(ra -> { - try { - return (RelatedArtifact) ra; - } catch (ClassCastException e) { - throw new UnprocessableEntityException( - "All related artifacts must be of type " + RelatedArtifact.class.getName()); - } - }) - .collect(Collectors.toList())); + public ICompositeType getType() { + return getLibrary().getType(); } @Override - public void setEffectivePeriod(ICompositeType effectivePeriod) { - if (effectivePeriod != null && !(effectivePeriod instanceof Period)) { - throw new UnprocessableEntityException("EffectivePeriod must be " + Period.class.getName()); + public LibraryAdapter setType(String type) { + if (LIBRARY_TYPES.contains(type)) { + getLibrary() + .setType(new CodeableConcept(new Coding("http://hl7.org/fhir/ValueSet/library-type", type, ""))); + } else { + throw new UnprocessableEntityException("Invalid type: {}", type); } - this.getLibrary().setEffectivePeriod((Period) effectivePeriod); + return this; } @Override - public void setStatus(String statusCodeString) { - PublicationStatus status; - try { - status = PublicationStatus.fromCode(statusCodeString); - } catch (FHIRException e) { - throw new UnprocessableEntityException("Invalid status code"); - } - this.getLibrary().setStatus(status); + public List getDataRequirement() { + return getLibrary().getDataRequirement(); } @Override - public String getStatus() { - return this.getLibrary().getStatus() == null - ? null - : this.getLibrary().getStatus().toCode(); + public LibraryAdapter addDataRequirement(ICompositeType dataRequirement) { + getLibrary().addDataRequirement((DataRequirement) dataRequirement); + return this; } @Override - public boolean getExperimental() { - return this.getLibrary().getExperimental(); + public LibraryAdapter setDataRequirement(List dataRequirement) { + getLibrary() + .setDataRequirement( + dataRequirement.stream().map(dr -> (DataRequirement) dr).collect(Collectors.toList())); + return this; } @Override - public void setExtension(List> extensions) { - this.get().setExtension(extensions.stream().map(e -> (Extension) e).collect(Collectors.toList())); + public List getUseContext() { + return getLibrary().getUseContext(); } } diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/MeasureAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/MeasureAdapter.java new file mode 100644 index 000000000..e266d4c22 --- /dev/null +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/MeasureAdapter.java @@ -0,0 +1,149 @@ +package org.opencds.cqf.fhir.utility.adapter.dstu3; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import org.hl7.fhir.dstu3.model.Library; +import org.hl7.fhir.dstu3.model.Measure; +import org.hl7.fhir.dstu3.model.Reference; +import org.hl7.fhir.dstu3.model.RelatedArtifact; +import org.hl7.fhir.instance.model.api.IDomainResource; +import org.opencds.cqf.fhir.utility.Constants; +import org.opencds.cqf.fhir.utility.adapter.DependencyInfo; +import org.opencds.cqf.fhir.utility.adapter.IDependencyInfo; + +public class MeasureAdapter extends KnowledgeArtifactAdapter + implements org.opencds.cqf.fhir.utility.adapter.MeasureAdapter { + + public MeasureAdapter(IDomainResource measure) { + super(measure); + if (!(measure instanceof Measure)) { + throw new IllegalArgumentException("resource passed as measure argument is not a Measure resource"); + } + } + + public MeasureAdapter(Measure measure) { + super(measure); + } + + protected Measure getMeasure() { + return (Measure) resource; + } + + @Override + public Measure get() { + return getMeasure(); + } + + @Override + public Measure copy() { + return get().copy(); + } + + private boolean checkedEffectiveDataRequirements; + private Library effectiveDataRequirements; + private LibraryAdapter effectiveDataRequirementsAdapter; + + private void findEffectiveDataRequirements() { + if (!checkedEffectiveDataRequirements) { + var edrExtensions = this.getMeasure().getExtension().stream() + .filter(ext -> ext.getUrl().endsWith("-effectiveDataRequirements")) + .filter(ext -> ext.hasValue()) + .collect(Collectors.toList()); + + var edrExtension = edrExtensions.size() == 1 ? edrExtensions.get(0) : null; + if (edrExtension != null) { + var edrReference = ((Reference) edrExtension.getValue()).getReference(); + for (var c : getMeasure().getContained()) { + if (c.hasId() && String.format("#%s", c.getId()).equals(edrReference) && c instanceof Library) { + effectiveDataRequirements = (Library) c; + effectiveDataRequirementsAdapter = new LibraryAdapter(effectiveDataRequirements); + } + } + } + checkedEffectiveDataRequirements = true; + } + } + + @Override + public List getDependencies() { + List references = new ArrayList<>(); + final String referenceSource = getReferenceSource(); + addProfileReferences(references, referenceSource); + + // If an effectiveDataRequirements library is present, use it exclusively + findEffectiveDataRequirements(); + if (effectiveDataRequirements != null) { + references.addAll(effectiveDataRequirementsAdapter.getDependencies()); + return references; + } + + // Otherwise, fall back to the relatedArtifact and library + + /* + relatedArtifact[].resource + library[] + group[].population[].criteria.reference - no path on dstu3 + group[].stratifier[].criteria.reference - no path on dstu3 + group[].stratifier[].component[].criteria.reference - no path on dstu3 + supplementalData[].criteria.reference - no path on dstu3 + extension[cqfm-inputParameters][] + extension[cqfm-expansionParameters][] + extension[cqfm-effectiveDataRequirements] + extension[cqfm-cqlOptions] + extension[cqfm-component][].resource + extension[crmi-effectiveDataRequirements] + */ + + // relatedArtifact[].resource + references.addAll(getRelatedArtifact().stream() + .map(ra -> DependencyInfo.convertRelatedArtifact(ra, referenceSource)) + .collect(Collectors.toList())); + + // library[] + for (var library : getMeasure().getLibrary()) { + DependencyInfo dependency = new DependencyInfo( + referenceSource, + library.getReference(), + library.getExtension(), + (reference) -> library.setReference(reference)); + references.add(dependency); + } + + // extension[cqfm-effectiveDataRequirements] + // extension[crmi-effectiveDataRequirements] + get().getExtension().stream() + .filter(e -> CANONICAL_EXTENSIONS.contains(e.getUrl())) + .forEach(referenceExt -> references.add(new DependencyInfo( + referenceSource, + ((Reference) referenceExt.getValue()).getReference(), + referenceExt.getExtension(), + (reference) -> referenceExt.setValue(new Reference(reference))))); + + // extension[cqfm-inputParameters][] + // extension[cqfm-expansionParameters][] + // extension[cqfm-cqlOptions] + get().getExtension().stream() + .filter(e -> REFERENCE_EXTENSIONS.contains(e.getUrl())) + .forEach(referenceExt -> references.add(new DependencyInfo( + referenceSource, + ((Reference) referenceExt.getValue()).getReference(), + referenceExt.getExtension(), + (reference) -> referenceExt.setValue(new Reference(reference))))); + + // extension[cqfm-component][].resource + get().getExtensionsByUrl(Constants.CQFM_COMPONENT).forEach(ext -> { + final var ref = (RelatedArtifact) ext.getValue(); + if (ref.hasResource() && ref.getResource().hasReference()) { + final var dep = new DependencyInfo( + referenceSource, + ref.getResource().getReference(), + ref.getExtension(), + (reference) -> ref.getResource().setReference(reference)); + references.add(dep); + } + }); + + return references; + } +} diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/ParametersAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/ParametersAdapter.java index 4af59fa6c..e55e85374 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/ParametersAdapter.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/ParametersAdapter.java @@ -5,6 +5,7 @@ import org.hl7.fhir.dstu3.model.Parameters; import org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent; import org.hl7.fhir.dstu3.model.Type; +import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseBackboneElement; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -39,6 +40,25 @@ public void setParameter(List parametersParameterComponent .collect(Collectors.toList())); } + @Override + public void addParameter(String name, IBase value) { + if (value instanceof Type) { + getParameters().addParameter(addParameter().setName(name).setValue((Type) value)); + } else { + throw new IllegalArgumentException("element passed as value argument is not a valid type"); + } + } + + @Override + public void addParameter(IBase parameter) { + if (parameter instanceof ParametersParameterComponent) { + getParameters().addParameter((ParametersParameterComponent) parameter); + } else { + throw new IllegalArgumentException( + "element passed as parameter argument is not a valid parameter component"); + } + } + @Override public ParametersParameterComponent addParameter() { return this.getParameters().addParameter(); diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/ParametersParameterComponentAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/ParametersParameterComponentAdapter.java index e02e5b097..0a2937f14 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/ParametersParameterComponentAdapter.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/ParametersParameterComponentAdapter.java @@ -1,5 +1,7 @@ package org.opencds.cqf.fhir.utility.adapter.dstu3; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.FhirVersionEnum; import java.util.List; import java.util.stream.Collectors; import org.hl7.fhir.dstu3.model.Parameters; @@ -8,13 +10,16 @@ import org.hl7.fhir.dstu3.model.Type; import org.hl7.fhir.instance.model.api.IBaseBackboneElement; import org.hl7.fhir.instance.model.api.IBaseDatatype; -import org.hl7.fhir.instance.model.api.IBaseExtension; import org.hl7.fhir.instance.model.api.IBaseResource; +import org.opencds.cqf.cql.engine.model.ModelResolver; +import org.opencds.cqf.fhir.utility.model.FhirModelResolverCache; class ParametersParameterComponentAdapter implements org.opencds.cqf.fhir.utility.adapter.ParametersParameterComponentAdapter { - private Parameters.ParametersParameterComponent parametersParametersComponent; + private final Parameters.ParametersParameterComponent parametersParametersComponent; + private final FhirContext fhirContext; + private final ModelResolver modelResolver; public ParametersParameterComponentAdapter(IBaseBackboneElement parametersParametersComponent) { if (parametersParametersComponent == null) { @@ -27,6 +32,9 @@ public ParametersParameterComponentAdapter(IBaseBackboneElement parametersParame } this.parametersParametersComponent = (ParametersParameterComponent) parametersParametersComponent; + fhirContext = FhirContext.forCached(FhirVersionEnum.R5); + modelResolver = FhirModelResolverCache.resolverForVersion( + fhirContext.getVersion().getVersion()); } @Override @@ -102,13 +110,12 @@ public IBaseDatatype getValue() { } @Override - public Boolean hasExtension() { - return this.parametersParametersComponent.hasExtension(); + public FhirContext fhirContext() { + return fhirContext; } @Override - @SuppressWarnings("unchecked") - public List> getExtension() { - return (List>) (List) this.parametersParametersComponent.getExtension(); + public ModelResolver getModelResolver() { + return modelResolver; } } diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/PlanDefinitionAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/PlanDefinitionAdapter.java index 9bf4b6b15..0365ee018 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/PlanDefinitionAdapter.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/PlanDefinitionAdapter.java @@ -1,127 +1,51 @@ package org.opencds.cqf.fhir.utility.adapter.dstu3; -import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import java.util.ArrayList; -import java.util.Date; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; import org.hl7.fhir.dstu3.model.DataRequirement.DataRequirementCodeFilterComponent; -import org.hl7.fhir.dstu3.model.DateTimeType; -import org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus; -import org.hl7.fhir.dstu3.model.Extension; -import org.hl7.fhir.dstu3.model.Period; import org.hl7.fhir.dstu3.model.PlanDefinition; import org.hl7.fhir.dstu3.model.Reference; -import org.hl7.fhir.dstu3.model.RelatedArtifact; -import org.hl7.fhir.dstu3.model.RelatedArtifact.RelatedArtifactType; import org.hl7.fhir.dstu3.model.StringType; import org.hl7.fhir.dstu3.model.UriType; -import org.hl7.fhir.exceptions.FHIRException; -import org.hl7.fhir.instance.model.api.IBase; -import org.hl7.fhir.instance.model.api.IBaseExtension; -import org.hl7.fhir.instance.model.api.IBaseHasExtensions; -import org.hl7.fhir.instance.model.api.IBaseParameters; -import org.hl7.fhir.instance.model.api.ICompositeType; import org.hl7.fhir.instance.model.api.IDomainResource; -import org.hl7.fhir.instance.model.api.IPrimitiveType; -import org.opencds.cqf.fhir.api.Repository; import org.opencds.cqf.fhir.utility.adapter.DependencyInfo; import org.opencds.cqf.fhir.utility.adapter.IDependencyInfo; -import org.opencds.cqf.fhir.utility.adapter.KnowledgeArtifactAdapter; -import org.opencds.cqf.fhir.utility.visitor.KnowledgeArtifactVisitor; - -class PlanDefinitionAdapter extends ResourceAdapter implements KnowledgeArtifactAdapter { - - private PlanDefinition planDefinition; +class PlanDefinitionAdapter extends KnowledgeArtifactAdapter { public PlanDefinitionAdapter(IDomainResource planDefinition) { super(planDefinition); - if (!(planDefinition instanceof PlanDefinition)) { throw new IllegalArgumentException( "resource passed as planDefinition argument is not a PlanDefinition resource"); } - - this.planDefinition = (PlanDefinition) planDefinition; } public PlanDefinitionAdapter(PlanDefinition planDefinition) { super(planDefinition); - this.planDefinition = planDefinition; - } - - @Override - public IBase accept(KnowledgeArtifactVisitor visitor, Repository repository, IBaseParameters operationParameters) { - return visitor.visit(this, repository, operationParameters); } protected PlanDefinition getPlanDefinition() { - return this.planDefinition; + return (PlanDefinition) resource; } @Override public PlanDefinition get() { - return this.planDefinition; + return getPlanDefinition(); } @Override public PlanDefinition copy() { - return this.get().copy(); - } - - @Override - public String getName() { - return this.getPlanDefinition().getName(); - } - - @Override - public String getPurpose() { - return this.getPlanDefinition().getPurpose(); - } - - @Override - public void setName(String name) { - this.getPlanDefinition().setName(name); - } - - @Override - public String getUrl() { - return this.getPlanDefinition().getUrl(); - } - - @Override - public boolean hasUrl() { - return this.getPlanDefinition().hasUrl(); - } - - @Override - public void setUrl(String url) { - this.getPlanDefinition().setUrl(url); - } - - @Override - public String getVersion() { - return this.getPlanDefinition().getVersion(); - } - - @Override - public boolean hasVersion() { - return this.getPlanDefinition().hasVersion(); - } - - @Override - public void setVersion(String version) { - this.getPlanDefinition().setVersion(version); + return get().copy(); } @Override public List getDependencies() { List references = new ArrayList<>(); - final String referenceSource = this.hasVersion() - ? this.getPlanDefinition().getUrl() + "|" - + this.getPlanDefinition().getVersion() - : this.getPlanDefinition().getUrl(); + final String referenceSource = getReferenceSource(); + addProfileReferences(references, referenceSource); + /* https://build.fhir.org/ig/HL7/crmi-ig/distribution.html#package-and-data-requirements relatedArtifact[].resource @@ -139,12 +63,12 @@ public List getDependencies() { */ // relatedArtifact[].resource - references.addAll(this.getRelatedArtifact().stream() + references.addAll(getRelatedArtifact().stream() .map(ra -> DependencyInfo.convertRelatedArtifact(ra, referenceSource)) .collect(Collectors.toList())); // library[] - List libraries = this.planDefinition.getLibrary(); + List libraries = getPlanDefinition().getLibrary(); for (Reference ref : libraries) { // TODO: Account for reference.identifier? DependencyInfo dependency = new DependencyInfo( @@ -155,8 +79,8 @@ public List getDependencies() { references.add(dependency); } // action[] - this.planDefinition.getAction().forEach(action -> getDependenciesOfAction(action, references, referenceSource)); - this.getPlanDefinition().getExtension().stream() + getPlanDefinition().getAction().forEach(action -> getDependenciesOfAction(action, references, referenceSource)); + getPlanDefinition().getExtension().stream() .filter(ext -> ext.getUrl().contains("cpg-partOf")) .filter(ext -> ext.hasValue()) .findAny() @@ -224,127 +148,17 @@ private DependencyInfo dependencyFromDataRequirementCodeFilter(DataRequirementCo var vs = cf.getValueSet(); if (vs instanceof StringType) { return new DependencyInfo( - this.planDefinition.getUrl(), + getPlanDefinition().getUrl(), ((StringType) vs).getValue(), vs.getExtension(), (reference) -> ((StringType) vs).setValue(reference)); } else if (vs instanceof Reference) { return new DependencyInfo( - this.planDefinition.getUrl(), + getPlanDefinition().getUrl(), ((Reference) vs).getReference(), vs.getExtension(), (reference) -> ((Reference) vs).setReference(reference)); } return null; } - - @Override - public Date getApprovalDate() { - return this.getPlanDefinition().getApprovalDate(); - } - - @Override - public void setApprovalDate(Date approvalDate) { - this.getPlanDefinition().setApprovalDate(approvalDate); - } - - @Override - public Date getDate() { - return this.getPlanDefinition().getDate(); - } - - @Override - public void setDate(Date date) { - this.getPlanDefinition().setDate(date); - } - - @Override - public void setDateElement(IPrimitiveType date) { - if (date != null && !(date instanceof DateTimeType)) { - throw new UnprocessableEntityException("Date must be " + DateTimeType.class.getName()); - } - this.getPlanDefinition().setDateElement((DateTimeType) date); - } - - @Override - public Period getEffectivePeriod() { - return this.getPlanDefinition().getEffectivePeriod(); - } - - @Override - public boolean hasRelatedArtifact() { - return this.getPlanDefinition().hasRelatedArtifact(); - } - - @SuppressWarnings("unchecked") - @Override - public List getRelatedArtifact() { - return this.getPlanDefinition().getRelatedArtifact(); - } - - @SuppressWarnings("unchecked") - @Override - public List getRelatedArtifactsOfType(String codeString) { - RelatedArtifactType type; - try { - type = RelatedArtifactType.fromCode(codeString); - } catch (FHIRException e) { - throw new UnprocessableEntityException("Invalid related artifact code"); - } - return this.getRelatedArtifact().stream() - .filter(ra -> ra.getType() == type) - .collect(Collectors.toList()); - } - - @Override - public void setRelatedArtifact(List relatedArtifacts) - throws UnprocessableEntityException { - this.getPlanDefinition() - .setRelatedArtifact(relatedArtifacts.stream() - .map(ra -> { - try { - return (RelatedArtifact) ra; - } catch (ClassCastException e) { - throw new UnprocessableEntityException( - "All related artifacts must be of type " + RelatedArtifact.class.getName()); - } - }) - .collect(Collectors.toList())); - } - - @Override - public void setEffectivePeriod(ICompositeType effectivePeriod) { - if (effectivePeriod != null && !(effectivePeriod instanceof Period)) { - throw new UnprocessableEntityException("EffectivePeriod must be " + Period.class.getName()); - } - this.getPlanDefinition().setEffectivePeriod((Period) effectivePeriod); - } - - @Override - public void setStatus(String statusCodeString) { - PublicationStatus status; - try { - status = PublicationStatus.fromCode(statusCodeString); - } catch (FHIRException e) { - throw new UnprocessableEntityException("Invalid status code"); - } - this.getPlanDefinition().setStatus(status); - } - - @Override - public String getStatus() { - return this.getPlanDefinition().getStatus() == null - ? null - : this.getPlanDefinition().getStatus().toCode(); - } - - @Override - public boolean getExperimental() { - return this.getPlanDefinition().getExperimental(); - } - - @Override - public void setExtension(List> extensions) { - this.get().setExtension(extensions.stream().map(e -> (Extension) e).collect(Collectors.toList())); - } } diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/QuestionnaireAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/QuestionnaireAdapter.java new file mode 100644 index 000000000..c88c2a2a8 --- /dev/null +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/QuestionnaireAdapter.java @@ -0,0 +1,121 @@ +package org.opencds.cqf.fhir.utility.adapter.dstu3; + +import java.util.ArrayList; +import java.util.List; +import org.hl7.fhir.dstu3.model.Questionnaire; +import org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemComponent; +import org.hl7.fhir.dstu3.model.Reference; +import org.hl7.fhir.dstu3.model.UriType; +import org.hl7.fhir.instance.model.api.IDomainResource; +import org.opencds.cqf.fhir.utility.Constants; +import org.opencds.cqf.fhir.utility.adapter.DependencyInfo; +import org.opencds.cqf.fhir.utility.adapter.IDependencyInfo; + +public class QuestionnaireAdapter extends KnowledgeArtifactAdapter + implements org.opencds.cqf.fhir.utility.adapter.QuestionnaireAdapter { + + public QuestionnaireAdapter(IDomainResource questionnaire) { + super(questionnaire); + + if (!(questionnaire instanceof Questionnaire)) { + throw new IllegalArgumentException( + "resource passed as questionnaire argument is not a Questionnaire resource"); + } + } + + public QuestionnaireAdapter(Questionnaire questionnaire) { + super(questionnaire); + } + + protected Questionnaire getQuestionnaire() { + return (Questionnaire) resource; + } + + @Override + public Questionnaire get() { + return (Questionnaire) resource; + } + + @Override + public Questionnaire copy() { + return get().copy(); + } + + @Override + public List getDependencies() { + List references = new ArrayList<>(); + final String referenceSource = getReferenceSource(); + addProfileReferences(references, referenceSource); + + /* + derivedFrom + extension[cqf-library] + extension[launchContext] + extension[variable].reference + item[]..definition // NOTE: This is not a simple canonical, it will have a fragment to identify the specific element + item[]..answerValueSet + item[]..extension[itemMedia] + item[]..extension[itemAnswerMedia] + item[]..extension[unitValueSet] + item[]..extension[referenceProfile] + item[]..extension[candidateExpression].reference + item[]..extension[lookupQuestionnaire] + item[]..extension[variable].reference + item[]..extension[initialExpression].reference + item[]..extension[calculatedExpression].reference + item[]..extension[cqf-calculatedValue].reference + item[]..extension[cqf-expression].reference + item[]..extension[sdc-questionnaire-subQuestionnaire] + */ + + // Not looking at launchContext as it references only base spec profiles and these are included implicitly as + // dependencies per the CRMI IG + + getQuestionnaire() + .getExtensionsByUrl(Constants.CQIF_LIBRARY) + .forEach(libraryExt -> references.add(new DependencyInfo( + referenceSource, + ((Reference) libraryExt.getValue()).getReference(), + libraryExt.getExtension(), + (reference) -> libraryExt.setValue(new Reference(reference))))); + + // Expression type does not exist in Stu3. + // var variableExtensions = getQuestionnaire().getExtensionsByUrl(Constants.VARIABLE_EXTENSION); + // for (var variableExt : variableExtensions) { + // var expression = CqfExpression.of(variableExt, null); + // if (!StringUtils.isEmpty(expression.getLibraryUrl())) { + // var dependency = new DependencyInfo( + // referenceSource, expression.getLibraryUrl(), variableExt.getExtension(), (reference) -> {}); + // references.add(dependency); + // } + // } + + getQuestionnaire().getItem().forEach(item -> getDependenciesOfItem(item, references, referenceSource)); + + return references; + } + + private void getDependenciesOfItem( + QuestionnaireItemComponent item, List references, String referenceSource) { + if (item.hasDefinition()) { + var definition = item.getDefinition().split("#")[0]; + // Not passing an updateReferenceConsumer here because the reference is not a simple canonical + references.add(new DependencyInfo(referenceSource, definition, item.getExtension(), null)); + } + if (item.hasOptions()) { + references.add(new DependencyInfo( + referenceSource, + item.getOptions().getReference(), + item.getExtension(), + (reference) -> item.setOptions(new Reference(reference)))); + } + item.getExtension().stream() + .filter(e -> REFERENCE_EXTENSIONS.contains(e.getUrl())) + .forEach(referenceExt -> references.add(new DependencyInfo( + referenceSource, + ((UriType) referenceExt.getValue()).asStringValue(), + referenceExt.getExtension(), + (reference) -> referenceExt.setValue(new UriType(reference))))); + item.getItem().forEach(childItem -> getDependenciesOfItem(childItem, references, referenceSource)); + } +} diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/ResourceAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/ResourceAdapter.java index a6d75fa60..04cd80bae 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/ResourceAdapter.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/ResourceAdapter.java @@ -1,15 +1,21 @@ package org.opencds.cqf.fhir.utility.adapter.dstu3; +import static java.util.Optional.ofNullable; + import ca.uhn.fhir.context.FhirVersionEnum; +import java.util.Optional; import org.hl7.fhir.dstu3.model.Base; +import org.hl7.fhir.dstu3.model.DomainResource; import org.hl7.fhir.dstu3.model.Resource; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseResource; +import org.opencds.cqf.fhir.utility.adapter.BaseResourceAdapter; -class ResourceAdapter implements org.opencds.cqf.fhir.utility.adapter.ResourceAdapter { +class ResourceAdapter extends BaseResourceAdapter { public ResourceAdapter(IBaseResource resource) { + super(resource); if (resource == null) { throw new IllegalArgumentException("resource can not be null"); } @@ -17,33 +23,33 @@ public ResourceAdapter(IBaseResource resource) { if (!resource.getStructureFhirVersionEnum().equals(FhirVersionEnum.DSTU3)) { throw new IllegalArgumentException("resource is incorrect fhir version for this adapter"); } - - this.resource = (Resource) resource; } - private Resource resource; - protected Resource getResource() { - return this.resource; + return (Resource) resource; + } + + protected boolean isDomainResource() { + return getDomainResource().isPresent(); } - public IBaseResource get() { - return this.resource; + protected Optional getDomainResource() { + return ofNullable(resource instanceof DomainResource ? (DomainResource) resource : null); } @Override public IBase setProperty(String name, IBase value) throws FHIRException { - return this.getResource().setProperty(name, (Base) value); + return getResource().setProperty(name, (Base) value); } @Override public IBase addChild(String name) throws FHIRException { - return this.getResource().addChild(name); + return getResource().addChild(name); } @Override public IBase getSingleProperty(String name) throws FHIRException { - IBase[] values = this.getProperty(name, true); + IBase[] values = getProperty(name, true); if (values == null || values.length == 0) { return null; @@ -58,41 +64,41 @@ public IBase getSingleProperty(String name) throws FHIRException { @Override public IBase[] getProperty(String name) throws FHIRException { - return this.getProperty(name, true); + return getProperty(name, true); } @Override public IBase[] getProperty(String name, boolean checkValid) throws FHIRException { - return this.getResource().getProperty(name.hashCode(), name, checkValid); + return getResource().getProperty(name.hashCode(), name, checkValid); } @Override public IBase makeProperty(String name) throws FHIRException { - return this.getResource().makeProperty(name.hashCode(), name); + return getResource().makeProperty(name.hashCode(), name); } @Override public String[] getTypesForProperty(String name) throws FHIRException { - return this.getResource().getTypesForProperty(name.hashCode(), name); + return getResource().getTypesForProperty(name.hashCode(), name); } @Override public IBaseResource copy() { - return this.getResource().copy(); + return getResource().copy(); } @Override public void copyValues(IBaseResource dst) { - this.getResource().copyValues((Resource) dst); + getResource().copyValues((Resource) dst); } @Override public boolean equalsDeep(IBase other) { - return this.getResource().equalsDeep((Base) other); + return getResource().equalsDeep((Base) other); } @Override public boolean equalsShallow(IBase other) { - return this.getResource().equalsShallow((Base) other); + return getResource().equalsShallow((Base) other); } } diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/StructureDefinitionAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/StructureDefinitionAdapter.java new file mode 100644 index 000000000..7659af4bb --- /dev/null +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/StructureDefinitionAdapter.java @@ -0,0 +1,102 @@ +package org.opencds.cqf.fhir.utility.adapter.dstu3; + +import java.util.ArrayList; +import java.util.List; +import org.hl7.fhir.dstu3.model.ElementDefinition; +import org.hl7.fhir.dstu3.model.StructureDefinition; +import org.hl7.fhir.dstu3.model.UriType; +import org.hl7.fhir.instance.model.api.IDomainResource; +import org.opencds.cqf.fhir.utility.adapter.DependencyInfo; +import org.opencds.cqf.fhir.utility.adapter.IDependencyInfo; + +public class StructureDefinitionAdapter extends KnowledgeArtifactAdapter { + + public StructureDefinitionAdapter(IDomainResource structureDefinition) { + super(structureDefinition); + if (!(structureDefinition instanceof StructureDefinition)) { + throw new IllegalArgumentException( + "resource passed as planDefinition argument is not a StructureDefinition resource"); + } + } + + public StructureDefinitionAdapter(StructureDefinition structureDefinition) { + super(structureDefinition); + } + + protected StructureDefinition getStructureDefinition() { + return (StructureDefinition) resource; + } + + @Override + public List getDependencies() { + List references = new ArrayList<>(); + final String referenceSource = getReferenceSource(); + addProfileReferences(references, referenceSource); + + /* + extension[].url + modifierExtension[].url + baseDefinition + differential.element[].type.code + differential.element[].type.profile[] + differential.element[].type.targetProfile[] + differential.element[].binding.valueSet + differential.element[].extension[].url + differential.element[].modifierExtension[].url + extension[cpg-inferenceExpression].reference + extension[cpg-assertionExpression].reference + extension[cpg-featureExpression].reference + */ + + if (get().hasBaseDefinition()) { + references.add(new DependencyInfo( + referenceSource, + get().getBaseDefinition(), + get().getBaseDefinitionElement().getExtension(), + (reference) -> get().setBaseDefinition(reference))); + } + + get().getDifferential() + .getElement() + .forEach(element -> getDependenciesOfDifferential(element, references, referenceSource)); + + return references; + } + + private void getDependenciesOfDifferential( + ElementDefinition element, List references, String referenceSource) { + element.getType().forEach(type -> { + if (type.hasProfile()) { + references.add(new DependencyInfo( + referenceSource, + type.getProfile(), + type.getProfileElement().getExtension(), + (reference) -> type.setProfile(reference))); + } + if (type.hasTargetProfile()) { + references.add(new DependencyInfo( + referenceSource, + type.getTargetProfile(), + type.getTargetProfileElement().getExtension(), + (reference) -> type.setTargetProfile(reference))); + } + }); + if (element.getBinding().hasValueSet()) { + references.add(new DependencyInfo( + referenceSource, + element.getBinding().getValueSet().primitiveValue(), + element.getBinding().getExtension(), + (reference) -> element.getBinding().setValueSet(new UriType(reference)))); + } + } + + @Override + public StructureDefinition get() { + return getStructureDefinition(); + } + + @Override + public StructureDefinition copy() { + return get().copy(); + } +} diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/ValueSetAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/ValueSetAdapter.java index 8e11f2d87..ce09616ee 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/ValueSetAdapter.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/ValueSetAdapter.java @@ -1,125 +1,55 @@ package org.opencds.cqf.fhir.utility.adapter.dstu3; -import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import static org.opencds.cqf.fhir.utility.ValueSets.getCodesInCompose; + +import java.time.Instant; import java.util.ArrayList; +import java.util.Collection; import java.util.Date; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; -import org.hl7.fhir.dstu3.model.DateTimeType; -import org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus; -import org.hl7.fhir.dstu3.model.Extension; -import org.hl7.fhir.dstu3.model.Period; -import org.hl7.fhir.dstu3.model.RelatedArtifact; -import org.hl7.fhir.dstu3.model.RelatedArtifact.RelatedArtifactType; +import org.hl7.fhir.dstu3.model.BooleanType; import org.hl7.fhir.dstu3.model.ValueSet; -import org.hl7.fhir.exceptions.FHIRException; -import org.hl7.fhir.instance.model.api.IBase; -import org.hl7.fhir.instance.model.api.IBaseExtension; -import org.hl7.fhir.instance.model.api.IBaseHasExtensions; -import org.hl7.fhir.instance.model.api.IBaseParameters; -import org.hl7.fhir.instance.model.api.ICompositeType; +import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionComponent; +import org.hl7.fhir.instance.model.api.IBaseBackboneElement; import org.hl7.fhir.instance.model.api.IDomainResource; -import org.hl7.fhir.instance.model.api.IIdType; -import org.hl7.fhir.instance.model.api.IPrimitiveType; -import org.opencds.cqf.fhir.api.Repository; import org.opencds.cqf.fhir.utility.adapter.DependencyInfo; import org.opencds.cqf.fhir.utility.adapter.IDependencyInfo; -import org.opencds.cqf.fhir.utility.visitor.KnowledgeArtifactVisitor; - -class ValueSetAdapter extends ResourceAdapter implements org.opencds.cqf.fhir.utility.adapter.ValueSetAdapter { - private ValueSet valueSet; +public class ValueSetAdapter extends KnowledgeArtifactAdapter + implements org.opencds.cqf.fhir.utility.adapter.ValueSetAdapter { public ValueSetAdapter(IDomainResource valueSet) { super(valueSet); - if (!(valueSet instanceof ValueSet)) { throw new IllegalArgumentException("resource passed as valueSet argument is not a ValueSet resource"); } - - this.valueSet = (ValueSet) valueSet; } public ValueSetAdapter(ValueSet valueSet) { super(valueSet); - this.valueSet = valueSet; - } - - public IBase accept(KnowledgeArtifactVisitor visitor, Repository repository, IBaseParameters operationParameters) { - return visitor.visit(this, repository, operationParameters); } protected ValueSet getValueSet() { - return this.valueSet; + return (ValueSet) resource; } @Override public ValueSet get() { - return this.valueSet; + return (ValueSet) resource; } @Override public ValueSet copy() { - return this.get().copy(); - } - - @Override - public void setId(IIdType id) { - this.getValueSet().setId(id); - } - - @Override - public String getName() { - return this.getValueSet().getName(); - } - - @Override - public String getPurpose() { - return this.getValueSet().getPurpose(); - } - - @Override - public void setName(String name) { - this.getValueSet().setName(name); - } - - @Override - public String getUrl() { - return this.getValueSet().getUrl(); - } - - @Override - public boolean hasUrl() { - return this.getValueSet().hasUrl(); - } - - @Override - public void setUrl(String url) { - this.getValueSet().setUrl(url); - } - - @Override - public String getVersion() { - return this.getValueSet().getVersion(); - } - - @Override - public boolean hasVersion() { - return this.getValueSet().hasVersion(); - } - - @Override - public void setVersion(String version) { - this.getValueSet().setVersion(version); + return get().copy(); } @Override public List getDependencies() { List references = new ArrayList<>(); - final String referenceSource = this.getValueSet().hasVersion() - ? this.getValueSet().getUrl() + "|" + this.getValueSet().getVersion() - : this.getValueSet().getUrl(); + final String referenceSource = getReferenceSource(); + addProfileReferences(references, referenceSource); /* compose.include[].valueSet @@ -128,16 +58,16 @@ public List getDependencies() { compose.exclude[].system */ Stream.concat( - this.valueSet.getCompose().getInclude().stream(), - this.valueSet.getCompose().getExclude().stream()) + getValueSet().getCompose().getInclude().stream(), + getValueSet().getCompose().getExclude().stream()) .forEach(component -> { if (component.hasValueSet()) { - component.getValueSet().forEach(uri -> { + component.getValueSet().forEach(ct -> { references.add(new DependencyInfo( referenceSource, - uri.getValue(), - uri.getExtension(), - (reference) -> uri.setValue(reference))); + ct.getValue(), + ct.getExtension(), + (reference) -> ct.setValue(reference))); }); } if (component.hasSystem()) { @@ -154,114 +84,77 @@ public List getDependencies() { } @Override - public Date getApprovalDate() { - return null; - } - - @Override - public void setApprovalDate(Date date) { - // do nothing; - } - - @Override - public Date getDate() { - return this.getValueSet().getDate(); - } - - @Override - public void setDate(Date date) { - this.getValueSet().setDate(date); - } - - @Override - public void setDateElement(IPrimitiveType date) { - if (date != null && !(date instanceof DateTimeType)) { - throw new UnprocessableEntityException("Date must be " + DateTimeType.class.getName()); - } - this.getValueSet().setDateElement((DateTimeType) date); - } - - @Override - public Period getEffectivePeriod() { - return new Period(); - } - - @Override - public boolean hasRelatedArtifact() { - return false; + public void setExpansion(T expansion) { + getValueSet().setExpansion((ValueSetExpansionComponent) expansion); } @SuppressWarnings("unchecked") @Override - public List getRelatedArtifact() { - return new ArrayList<>(); + public ValueSetExpansionComponent getExpansion() { + return getValueSet().getExpansion(); } @SuppressWarnings("unchecked") @Override - public List getComponents() { - return this.getRelatedArtifactsOfType("composed-of"); + public ValueSetExpansionComponent newExpansion() { + var expansion = new ValueSet.ValueSetExpansionComponent().setTimestamp(Date.from(Instant.now())); + expansion.getContains(); + return expansion; } - @SuppressWarnings("unchecked") @Override - public List getRelatedArtifactsOfType(String codeString) { - RelatedArtifactType type; - try { - type = RelatedArtifactType.fromCode(codeString); - } catch (FHIRException e) { - throw new UnprocessableEntityException("Invalid related artifact code"); - } - return this.getRelatedArtifact().stream() - .filter(ra -> ra.getType() == type) + public List getValueSetIncludes() { + return getValueSet().getCompose().getInclude().stream() + .map(i -> i.getValueSet()) + .flatMap(Collection::stream) + .map(c -> c.asStringValue()) + .distinct() .collect(Collectors.toList()); } @Override - public void setRelatedArtifact(List relatedArtifacts) - throws UnprocessableEntityException { - relatedArtifacts.stream().forEach(ra -> { - if (ra != null && !(ra instanceof RelatedArtifact)) { - throw new UnprocessableEntityException( - "All related artifacts must be of type " + RelatedArtifact.class.getName()); - } - }); - // do nothing + public boolean hasSimpleCompose() { + return getValueSet().hasCompose() + && !getValueSet().getCompose().hasExclude() + && getValueSet().getCompose().getInclude().stream() + .noneMatch( + csc -> csc.hasFilter() || csc.hasValueSet() || !csc.hasSystem() || !csc.hasConcept()); } @Override - public void setEffectivePeriod(ICompositeType effectivePeriod) { - if (effectivePeriod != null && !(effectivePeriod instanceof Period)) { - throw new UnprocessableEntityException("EffectivePeriod must be a valid " + Period.class.getName()); - } - // do nothing + public boolean hasGroupingCompose() { + return getValueSet().hasCompose() + && !getValueSet().getCompose().hasExclude() + && getValueSet().getCompose().getInclude().stream() + .noneMatch(csc -> !csc.hasValueSet() || csc.hasFilter()); } @Override - public void setStatus(String statusCodeString) { - PublicationStatus status; - try { - status = PublicationStatus.fromCode(statusCodeString); - } catch (FHIRException e) { - throw new UnprocessableEntityException("Invalid status code"); - } - this.getValueSet().setStatus(status); + public boolean hasNaiveParameter() { + return getValueSet().getExpansion().getParameter().stream() + .anyMatch(p -> p.getName().equals("naive")); } + @SuppressWarnings("unchecked") @Override - public String getStatus() { - return this.getValueSet().getStatus() == null - ? null - : this.getValueSet().getStatus().toCode(); + public ValueSet.ValueSetExpansionParameterComponent createNaiveParameter() { + return new ValueSet.ValueSetExpansionParameterComponent() + .setName("naive") + .setValue(new BooleanType(true)); } @Override - public boolean getExperimental() { - return this.getValueSet().getExperimental(); - } + public void naiveExpand() { + var expansion = newExpansion().addParameter(createNaiveParameter()); - @Override - public void setExtension(List> extensions) { - this.get().setExtension(extensions.stream().map(e -> (Extension) e).collect(Collectors.toList())); + for (var code : getCodesInCompose(fhirContext, getValueSet())) { + expansion + .addContains() + .setCode(code.getCode()) + .setSystem(code.getSystem()) + .setVersion(code.getVersion()) + .setDisplay(code.getDisplay()); + } + getValueSet().setExpansion(expansion); } } diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/AdapterFactory.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/AdapterFactory.java index 892aa20f8..ddbca71e8 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/AdapterFactory.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/AdapterFactory.java @@ -6,12 +6,15 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.ICompositeType; import org.hl7.fhir.instance.model.api.IDomainResource; +import org.hl7.fhir.r4.model.Endpoint; import org.hl7.fhir.r4.model.Library; +import org.hl7.fhir.r4.model.Measure; import org.hl7.fhir.r4.model.MetadataResource; +import org.hl7.fhir.r4.model.Parameters; import org.hl7.fhir.r4.model.PlanDefinition; +import org.hl7.fhir.r4.model.Questionnaire; +import org.hl7.fhir.r4.model.StructureDefinition; import org.hl7.fhir.r4.model.ValueSet; -import org.opencds.cqf.fhir.utility.adapter.KnowledgeArtifactAdapter; -import org.opencds.cqf.fhir.utility.adapter.LibraryAdapter; public class AdapterFactory implements org.opencds.cqf.fhir.utility.adapter.AdapterFactory { @@ -19,35 +22,45 @@ public class AdapterFactory implements org.opencds.cqf.fhir.utility.adapter.Adap public org.opencds.cqf.fhir.utility.adapter.ResourceAdapter createResource(IBaseResource resource) { if (resource instanceof MetadataResource) { return createKnowledgeArtifactAdapter((IDomainResource) resource); + } else if (resource instanceof Endpoint) { + return createEndpoint(resource); + } else if (resource instanceof Parameters) { + return createParameters((Parameters) resource); } else { return new ResourceAdapter(resource); } } @Override - public KnowledgeArtifactAdapter createKnowledgeArtifactAdapter(IDomainResource resource) { - KnowledgeArtifactAdapter retval; + public org.opencds.cqf.fhir.utility.adapter.KnowledgeArtifactAdapter createKnowledgeArtifactAdapter( + IDomainResource resource) { + org.opencds.cqf.fhir.utility.adapter.KnowledgeArtifactAdapter adapter; if (resource instanceof Library) { - retval = createLibrary(resource); + adapter = createLibrary(resource); + } else if (resource instanceof Measure) { + adapter = new MeasureAdapter((Measure) resource); } else if (resource instanceof PlanDefinition) { - retval = new org.opencds.cqf.fhir.utility.adapter.r4.PlanDefinitionAdapter((PlanDefinition) resource); + adapter = new PlanDefinitionAdapter((PlanDefinition) resource); + } else if (resource instanceof Questionnaire) { + adapter = new QuestionnaireAdapter((Questionnaire) resource); + } else if (resource instanceof StructureDefinition) { + adapter = new StructureDefinitionAdapter((StructureDefinition) resource); } else if (resource instanceof ValueSet) { - retval = new org.opencds.cqf.fhir.utility.adapter.r4.ValueSetAdapter((ValueSet) resource); + adapter = new ValueSetAdapter((ValueSet) resource); } else { if (resource != null && resource instanceof MetadataResource) { - retval = new org.opencds.cqf.fhir.utility.adapter.r4.KnowledgeArtifactAdapter( - (MetadataResource) resource); + adapter = new KnowledgeArtifactAdapter((MetadataResource) resource); } else { throw new UnprocessableEntityException( - String.format("Resouce must be instance of %s", MetadataResource.class.getName())); + String.format("Resource must be instance of %s", MetadataResource.class.getName())); } } - return retval; + return adapter; } @Override - public LibraryAdapter createLibrary(IBaseResource library) { - return new org.opencds.cqf.fhir.utility.adapter.r4.LibraryAdapter((IDomainResource) library); + public org.opencds.cqf.fhir.utility.adapter.LibraryAdapter createLibrary(IBaseResource library) { + return new LibraryAdapter((IDomainResource) library); } @Override @@ -65,4 +78,9 @@ public org.opencds.cqf.fhir.utility.adapter.ParametersParameterComponentAdapter IBaseBackboneElement parametersParametersComponent) { return new ParametersParameterComponentAdapter(parametersParametersComponent); } + + @Override + public org.opencds.cqf.fhir.utility.adapter.EndpointAdapter createEndpoint(IBaseResource endpoint) { + return new EndpointAdapter(endpoint); + } } diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/AttachmentAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/AttachmentAdapter.java index 181ae7812..cbd0e3584 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/AttachmentAdapter.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/AttachmentAdapter.java @@ -1,11 +1,16 @@ package org.opencds.cqf.fhir.utility.adapter.r4; +import ca.uhn.fhir.context.FhirContext; import org.hl7.fhir.instance.model.api.ICompositeType; import org.hl7.fhir.r4.model.Attachment; +import org.opencds.cqf.cql.engine.model.ModelResolver; +import org.opencds.cqf.fhir.utility.model.FhirModelResolverCache; class AttachmentAdapter implements org.opencds.cqf.fhir.utility.adapter.AttachmentAdapter { - private Attachment attachment; + private final Attachment attachment; + private final FhirContext fhirContext; + private final ModelResolver modelResolver; public AttachmentAdapter(ICompositeType attachment) { if (attachment == null) { @@ -21,6 +26,9 @@ public AttachmentAdapter(ICompositeType attachment) { } this.attachment = (Attachment) attachment; + fhirContext = FhirContext.forR4Cached(); + modelResolver = FhirModelResolverCache.resolverForVersion( + fhirContext.getVersion().getVersion()); } protected Attachment getAttachment() { @@ -51,4 +59,14 @@ public byte[] getData() { public void setData(byte[] data) { this.getAttachment().setData(data); } + + @Override + public FhirContext fhirContext() { + return fhirContext; + } + + @Override + public ModelResolver getModelResolver() { + return modelResolver; + } } diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/EndpointAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/EndpointAdapter.java new file mode 100644 index 000000000..76a283fed --- /dev/null +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/EndpointAdapter.java @@ -0,0 +1,13 @@ +package org.opencds.cqf.fhir.utility.adapter.r4; + +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r4.model.Endpoint; + +public class EndpointAdapter extends ResourceAdapter implements org.opencds.cqf.fhir.utility.adapter.EndpointAdapter { + public EndpointAdapter(IBaseResource endpoint) { + super(endpoint); + if (!(endpoint instanceof Endpoint)) { + throw new IllegalArgumentException("resource passed as endpoint argument is not an Endpoint resource"); + } + } +} diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/KnowledgeArtifactAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/KnowledgeArtifactAdapter.java index 8550f1d04..4fec9422b 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/KnowledgeArtifactAdapter.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/KnowledgeArtifactAdapter.java @@ -6,100 +6,44 @@ import java.util.List; import java.util.stream.Collectors; import org.hl7.fhir.exceptions.FHIRException; -import org.hl7.fhir.instance.model.api.IBase; -import org.hl7.fhir.instance.model.api.IBaseExtension; import org.hl7.fhir.instance.model.api.IBaseHasExtensions; -import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.ICompositeType; +import org.hl7.fhir.instance.model.api.IDomainResource; import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.r4.model.DateTimeType; import org.hl7.fhir.r4.model.Enumerations.PublicationStatus; -import org.hl7.fhir.r4.model.Extension; import org.hl7.fhir.r4.model.MetadataResource; import org.hl7.fhir.r4.model.Period; import org.hl7.fhir.r4.model.RelatedArtifact; import org.hl7.fhir.r4.model.RelatedArtifact.RelatedArtifactType; -import org.opencds.cqf.fhir.api.Repository; import org.opencds.cqf.fhir.utility.adapter.IDependencyInfo; -import org.opencds.cqf.fhir.utility.visitor.KnowledgeArtifactVisitor; public class KnowledgeArtifactAdapter extends ResourceAdapter implements org.opencds.cqf.fhir.utility.adapter.KnowledgeArtifactAdapter { MetadataResource adaptedResource; + public KnowledgeArtifactAdapter(IDomainResource resource) { + super(resource); + if (!(resource instanceof MetadataResource)) { + throw new IllegalArgumentException( + "resource passed as resource argument is not a MetadataResource resource"); + } + adaptedResource = (MetadataResource) resource; + } + public KnowledgeArtifactAdapter(MetadataResource resource) { super(resource); - this.adaptedResource = resource; + adaptedResource = resource; } @Override public MetadataResource get() { - return this.adaptedResource; + return adaptedResource; } @Override public MetadataResource copy() { - return this.get().copy(); - } - - @Override - public String getUrl() { - return this.get().getUrl(); - } - - @Override - public boolean hasUrl() { - return this.get().hasUrl(); - } - - @Override - public void setUrl(String url) { - this.get().setUrl(url); - } - - @Override - public void setVersion(String version) { - this.get().setVersion(version); - } - - @Override - public String getVersion() { - return this.get().getVersion(); - } - - @Override - public boolean hasVersion() { - return this.get().hasVersion(); - } - - @Override - public String getName() { - return this.get().getName(); - } - - @Override - public String getPurpose() { - return null; - } - - @Override - public void setName(String name) { - this.get().setName(name); - } - - @Override - public Date getApprovalDate() { - return null; - } - - @Override - public Date getDate() { - return this.get().getDate(); - } - - @Override - public void setDate(Date approvalDate) { - this.get().setDate(approvalDate); + return get().copy(); } @Override @@ -107,27 +51,7 @@ public void setDateElement(IPrimitiveType date) { if (date != null && !(date instanceof DateTimeType)) { throw new UnprocessableEntityException("Date must be " + DateTimeType.class.getName()); } - this.get().setDateElement((DateTimeType) date); - } - - @Override - public void setApprovalDate(Date approvalDate) { - // do nothing - } - - @Override - public Period getEffectivePeriod() { - return new Period(); - } - - @Override - public List getDependencies() { - return new ArrayList<>(); - } - - @Override - public IBase accept(KnowledgeArtifactVisitor visitor, Repository repository, IBaseParameters operationParameters) { - return visitor.visit(this, repository, operationParameters); + org.opencds.cqf.fhir.utility.adapter.KnowledgeArtifactAdapter.super.setDateElement(date); } @Override @@ -135,18 +59,15 @@ public void setEffectivePeriod(ICompositeType effectivePeriod) { if (effectivePeriod != null && !(effectivePeriod instanceof Period)) { throw new UnprocessableEntityException("EffectivePeriod must be a valid " + Period.class.getName()); } - // does nothing + org.opencds.cqf.fhir.utility.adapter.KnowledgeArtifactAdapter.super.setEffectivePeriod(effectivePeriod); } @Override - public boolean hasRelatedArtifact() { - return false; - } - - @SuppressWarnings("unchecked") - @Override - public List getRelatedArtifact() { - return new ArrayList(); + public List getDependencies() { + List references = new ArrayList<>(); + final String referenceSource = getReferenceSource(); + addProfileReferences(references, referenceSource); + return references; } @SuppressWarnings("unchecked") @@ -158,14 +79,15 @@ public List getRelatedArtifactsOfType(String codeString) { } catch (FHIRException e) { throw new UnprocessableEntityException("Invalid related artifact code"); } - return this.getRelatedArtifact().stream() + return getRelatedArtifact().stream() + .map(ra -> (RelatedArtifact) ra) .filter(ra -> ra.getType() == type) .collect(Collectors.toList()); } @Override - public void setRelatedArtifact(List relatedArtifacts) { - // does nothing + public String getStatus() { + return get().getStatus() == null ? null : get().getStatus().toCode(); } @Override @@ -176,21 +98,21 @@ public void setStatus(String statusCodeString) { } catch (FHIRException e) { throw new UnprocessableEntityException("Invalid status code"); } - this.get().setStatus(status); - } - - @Override - public String getStatus() { - return this.get().getStatus() == null ? null : this.get().getStatus().toCode(); - } - - @Override - public boolean getExperimental() { - return this.get().getExperimental(); - } - - @Override - public void setExtension(List> extensions) { - this.get().setExtension(extensions.stream().map(e -> (Extension) e).collect(Collectors.toList())); + get().setStatus(status); + } + + @Override + public void setRelatedArtifact(List relatedArtifacts) + throws UnprocessableEntityException { + org.opencds.cqf.fhir.utility.adapter.KnowledgeArtifactAdapter.super.setRelatedArtifact(relatedArtifacts.stream() + .map(ra -> { + try { + return (RelatedArtifact) ra; + } catch (ClassCastException e) { + throw new UnprocessableEntityException( + "All related artifacts must be of type " + RelatedArtifact.class.getName()); + } + }) + .collect(Collectors.toList())); } } diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/LibraryAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/LibraryAdapter.java index d362daf9e..a89feaccb 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/LibraryAdapter.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/LibraryAdapter.java @@ -2,276 +2,143 @@ import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import java.util.ArrayList; -import java.util.Date; import java.util.List; import java.util.stream.Collectors; -import org.hl7.fhir.exceptions.FHIRException; -import org.hl7.fhir.instance.model.api.IBase; -import org.hl7.fhir.instance.model.api.IBaseExtension; -import org.hl7.fhir.instance.model.api.IBaseHasExtensions; -import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.ICompositeType; import org.hl7.fhir.instance.model.api.IDomainResource; -import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.r4.model.Attachment; -import org.hl7.fhir.r4.model.DateTimeType; -import org.hl7.fhir.r4.model.Enumerations.PublicationStatus; -import org.hl7.fhir.r4.model.Extension; +import org.hl7.fhir.r4.model.CodeableConcept; +import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.DataRequirement; import org.hl7.fhir.r4.model.Library; -import org.hl7.fhir.r4.model.Period; import org.hl7.fhir.r4.model.RelatedArtifact; -import org.hl7.fhir.r4.model.RelatedArtifact.RelatedArtifactType; -import org.opencds.cqf.fhir.api.Repository; +import org.hl7.fhir.r4.model.UsageContext; import org.opencds.cqf.fhir.utility.adapter.DependencyInfo; import org.opencds.cqf.fhir.utility.adapter.IDependencyInfo; -import org.opencds.cqf.fhir.utility.visitor.KnowledgeArtifactVisitor; - -public class LibraryAdapter extends ResourceAdapter implements org.opencds.cqf.fhir.utility.adapter.LibraryAdapter { - - private Library library; +public class LibraryAdapter extends KnowledgeArtifactAdapter + implements org.opencds.cqf.fhir.utility.adapter.LibraryAdapter { public LibraryAdapter(IDomainResource library) { super(library); if (!(library instanceof Library)) { throw new IllegalArgumentException("resource passed as library argument is not a Library resource"); } - this.library = (Library) library; } public LibraryAdapter(Library library) { super(library); - this.library = library; } protected Library getLibrary() { - return this.library; + return (Library) resource; } @Override public Library get() { - return this.library; + return (Library) resource; } @Override public Library copy() { - return this.get().copy(); - } - - @Override - public String getName() { - return this.getLibrary().getName(); - } - - @Override - public String getPurpose() { - return this.getLibrary().getPurpose(); - } - - @Override - public void setName(String name) { - this.getLibrary().setName(name); - } - - @Override - public boolean hasUrl() { - return this.getLibrary().hasUrl(); - } - - @Override - public String getUrl() { - return this.getLibrary().getUrl(); - } - - @Override - public void setUrl(String url) { - this.getLibrary().setUrl(url); - } - - @Override - public String getVersion() { - return this.getLibrary().getVersion(); - } - - @Override - public boolean hasVersion() { - return this.getLibrary().hasVersion(); - } - - @Override - public void setVersion(String version) { - this.getLibrary().setVersion(version); + return get().copy(); } @Override public boolean hasContent() { - return this.getLibrary().hasContent(); + return getLibrary().hasContent(); } @Override public List getContent() { - return this.getLibrary().getContent().stream().collect(Collectors.toList()); + return getLibrary().getContent().stream().collect(Collectors.toList()); } @Override public void setContent(List attachments) { List castAttachments = attachments.stream().map(x -> (Attachment) x).collect(Collectors.toList()); - this.getLibrary().setContent(castAttachments); + getLibrary().setContent(castAttachments); } @Override public Attachment addContent() { - return this.getLibrary().addContent(); + return getLibrary().addContent(); } @Override public List getDependencies() { - List retval = new ArrayList(); - final String referenceSource = - this.hasVersion() ? this.getUrl() + "|" + this.getLibrary().getVersion() : this.getUrl(); + List references = new ArrayList(); + final String referenceSource = getReferenceSource(); + addProfileReferences(references, referenceSource); // relatedArtifact[].resource - this.getRelatedArtifact().stream() + getRelatedArtifact().stream() + .map(ra -> (RelatedArtifact) ra) .filter(ra -> ra.hasResource()) .map(ra -> DependencyInfo.convertRelatedArtifact(ra, referenceSource)) - .forEach(ra -> retval.add(ra)); - this.getLibrary().getDataRequirement().stream().forEach(dr -> { + .forEach(ra -> references.add(ra)); + getLibrary().getDataRequirement().stream().forEach(dr -> { dr.getProfile().stream() .filter(profile -> profile.hasValue()) - .forEach(profile -> retval.add(new DependencyInfo( + .forEach(profile -> references.add(new DependencyInfo( referenceSource, profile.getValue(), profile.getExtension(), (reference) -> profile.setValue(reference)))); dr.getCodeFilter().stream() .filter(cf -> cf.hasValueSet()) - .forEach(cf -> retval.add(new DependencyInfo( + .forEach(cf -> references.add(new DependencyInfo( referenceSource, cf.getValueSet(), cf.getExtension(), (reference) -> cf.setValueSet(reference)))); }); - return retval; - } - - @Override - public void setRelatedArtifact(List relatedArtifacts) - throws UnprocessableEntityException { - this.getLibrary() - .setRelatedArtifact(relatedArtifacts.stream() - .map(ra -> { - try { - return (RelatedArtifact) ra; - } catch (ClassCastException e) { - throw new UnprocessableEntityException( - "All related artifacts must be of type " + RelatedArtifact.class.getName()); - } - }) - .collect(Collectors.toList())); - } - - @Override - public IBase accept(KnowledgeArtifactVisitor visitor, Repository repository, IBaseParameters operationParameters) { - return visitor.visit(this, repository, operationParameters); - } - - @Override - public Date getApprovalDate() { - return this.getLibrary().getApprovalDate(); - } - - @Override - public Date getDate() { - return this.getLibrary().getDate(); - } - - @Override - public void setDate(Date date) { - this.getLibrary().setDate(date); - } - - @Override - public void setDateElement(IPrimitiveType date) { - if (date != null && !(date instanceof DateTimeType)) { - throw new UnprocessableEntityException("Date must be " + DateTimeType.class.getName()); - } - this.getLibrary().setDateElement((DateTimeType) date); - } - - @Override - public Period getEffectivePeriod() { - return this.getLibrary().getEffectivePeriod(); - } - - @Override - public void setEffectivePeriod(ICompositeType effectivePeriod) { - if (effectivePeriod != null && !(effectivePeriod instanceof Period)) { - throw new UnprocessableEntityException("EffectivePeriod must " + Period.class.getName()); - } - this.getLibrary().setEffectivePeriod((Period) effectivePeriod); - } - - @Override - public boolean hasRelatedArtifact() { - return this.getLibrary().hasRelatedArtifact(); + return references; } @SuppressWarnings("unchecked") @Override - public List getRelatedArtifact() { - return this.getLibrary().getRelatedArtifact(); + public List getComponents() { + return getRelatedArtifactsOfType("composed-of"); } @Override - public void setApprovalDate(Date approvalDate) { - this.getLibrary().setApprovalDate(approvalDate); + public ICompositeType getType() { + return getLibrary().getType(); } - @SuppressWarnings("unchecked") @Override - public List getRelatedArtifactsOfType(String codeString) { - RelatedArtifactType type; - try { - type = RelatedArtifactType.fromCode(codeString); - } catch (FHIRException e) { - throw new UnprocessableEntityException("Invalid related artifact code"); + public LibraryAdapter setType(String type) { + if (LIBRARY_TYPES.contains(type)) { + getLibrary() + .setType(new CodeableConcept(new Coding("http://hl7.org/fhir/ValueSet/library-type", type, ""))); + } else { + throw new UnprocessableEntityException("Invalid type: {}", type); } - return this.getRelatedArtifact().stream() - .filter(ra -> ra.getType() == type) - .collect(Collectors.toList()); - } - - @SuppressWarnings("unchecked") - @Override - public List getComponents() { - return this.getRelatedArtifactsOfType("composed-of"); + return this; } @Override - public String getStatus() { - return this.getLibrary().getStatus() == null - ? null - : this.getLibrary().getStatus().toCode(); + public List getDataRequirement() { + return getLibrary().getDataRequirement(); } @Override - public void setStatus(String statusCodeString) { - PublicationStatus status; - try { - status = PublicationStatus.fromCode(statusCodeString); - } catch (FHIRException e) { - throw new UnprocessableEntityException("Invalid status code: " + statusCodeString); - } - this.getLibrary().setStatus(status); + public LibraryAdapter addDataRequirement(ICompositeType dataRequirement) { + getLibrary().addDataRequirement((DataRequirement) dataRequirement); + return this; } @Override - public boolean getExperimental() { - return this.getLibrary().getExperimental(); + public LibraryAdapter setDataRequirement(List dataRequirement) { + getLibrary() + .setDataRequirement( + dataRequirement.stream().map(dr -> (DataRequirement) dr).collect(Collectors.toList())); + return this; } @Override - public void setExtension(List> extensions) { - this.get().setExtension(extensions.stream().map(e -> (Extension) e).collect(Collectors.toList())); + public List getUseContext() { + return getLibrary().getUseContext(); } } diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/MeasureAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/MeasureAdapter.java new file mode 100644 index 000000000..05cbfddaa --- /dev/null +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/MeasureAdapter.java @@ -0,0 +1,198 @@ +package org.opencds.cqf.fhir.utility.adapter.r4; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import org.hl7.fhir.instance.model.api.IDomainResource; +import org.hl7.fhir.r4.model.CanonicalType; +import org.hl7.fhir.r4.model.Library; +import org.hl7.fhir.r4.model.Measure; +import org.hl7.fhir.r4.model.Reference; +import org.hl7.fhir.r4.model.RelatedArtifact; +import org.opencds.cqf.fhir.utility.Constants; +import org.opencds.cqf.fhir.utility.adapter.DependencyInfo; +import org.opencds.cqf.fhir.utility.adapter.IDependencyInfo; + +public class MeasureAdapter extends KnowledgeArtifactAdapter + implements org.opencds.cqf.fhir.utility.adapter.MeasureAdapter { + + public MeasureAdapter(IDomainResource measure) { + super(measure); + if (!(measure instanceof Measure)) { + throw new IllegalArgumentException("resource passed as measure argument is not a Measure resource"); + } + } + + public MeasureAdapter(Measure measure) { + super(measure); + } + + protected Measure getMeasure() { + return (Measure) resource; + } + + @Override + public Measure get() { + return getMeasure(); + } + + @Override + public Measure copy() { + return get().copy(); + } + + private boolean checkedEffectiveDataRequirements; + private Library effectiveDataRequirements; + private LibraryAdapter effectiveDataRequirementsAdapter; + + private void findEffectiveDataRequirements() { + if (!checkedEffectiveDataRequirements) { + var edrExtensions = this.getMeasure().getExtension().stream() + .filter(ext -> ext.getUrl().endsWith("-effectiveDataRequirements")) + .filter(ext -> ext.hasValue()) + .collect(Collectors.toList()); + + var edrExtension = edrExtensions.size() == 1 ? edrExtensions.get(0) : null; + if (edrExtension != null) { + var edrReference = ((CanonicalType) edrExtension.getValue()).getValue(); + for (var c : getMeasure().getContained()) { + if (c.hasId() && String.format("#%s", c.getId()).equals(edrReference) && c instanceof Library) { + effectiveDataRequirements = (Library) c; + effectiveDataRequirementsAdapter = new LibraryAdapter(effectiveDataRequirements); + } + } + } + checkedEffectiveDataRequirements = true; + } + } + + @Override + public List getDependencies() { + List references = new ArrayList<>(); + final String referenceSource = getReferenceSource(); + addProfileReferences(references, referenceSource); + + // If an effectiveDataRequirements library is present, use it exclusively + findEffectiveDataRequirements(); + if (effectiveDataRequirements != null) { + references.addAll(effectiveDataRequirementsAdapter.getDependencies()); + return references; + } + + // Otherwise, fall back to the relatedArtifact and library + + /* + relatedArtifact[].resource + library[] + group[].population[].criteria.reference + group[].stratifier[].criteria.reference + group[].stratifier[].component[].criteria.reference + supplementalData[].criteria.reference + extension[cqfm-inputParameters][] + extension[cqfm-expansionParameters][] + extension[cqfm-effectiveDataRequirements] + extension[cqfm-cqlOptions] + extension[cqfm-component][].resource + extension[crmi-effectiveDataRequirements] + */ + + // relatedArtifact[].resource + references.addAll(getRelatedArtifact().stream() + .map(ra -> DependencyInfo.convertRelatedArtifact(ra, referenceSource)) + .collect(Collectors.toList())); + + // library[] + for (var library : getMeasure().getLibrary()) { + DependencyInfo dependency = new DependencyInfo( + referenceSource, + library.getValue(), + library.getExtension(), + (reference) -> library.setValue(reference)); + references.add(dependency); + } + + for (final var group : getMeasure().getGroup()) { + for (final var population : group.getPopulation()) { + // group[].population[].criteria.reference + if (population.getCriteria().hasReference()) { + final var dependency = new DependencyInfo( + referenceSource, + population.getCriteria().getReference(), + population.getCriteria().getExtension(), + (reference) -> population.getCriteria().setReference(reference)); + references.add(dependency); + } + } + for (final var stratifier : group.getStratifier()) { + // group[].stratifier[].criteria.reference + if (stratifier.getCriteria().hasReference()) { + final var dependency = new DependencyInfo( + referenceSource, + stratifier.getCriteria().getReference(), + stratifier.getCriteria().getExtension(), + (reference) -> stratifier.getCriteria().setReference(reference)); + references.add(dependency); + } + for (final var component : stratifier.getComponent()) { + // group[].stratifier[].component[].criteria.reference + if (component.getCriteria().hasReference()) { + final var stratifierComponentDep = new DependencyInfo( + referenceSource, + component.getCriteria().getReference(), + component.getCriteria().getExtension(), + (reference) -> component.getCriteria().setReference(reference)); + references.add(stratifierComponentDep); + } + } + } + } + + for (final var supplement : getMeasure().getSupplementalData()) { + // supplementalData[].criteria.reference + if (supplement.getCriteria().hasReference()) { + final var dependency = new DependencyInfo( + referenceSource, + supplement.getCriteria().getReference(), + supplement.getCriteria().getExtension(), + (reference) -> supplement.getCriteria().setReference(reference)); + references.add(dependency); + } + } + + // extension[cqfm-effectiveDataRequirements] + // extension[crmi-effectiveDataRequirements] + get().getExtension().stream() + .filter(e -> CANONICAL_EXTENSIONS.contains(e.getUrl())) + .forEach(referenceExt -> references.add(new DependencyInfo( + referenceSource, + ((CanonicalType) referenceExt.getValue()).getValue(), + referenceExt.getExtension(), + (reference) -> referenceExt.setValue(new CanonicalType(reference))))); + + // extension[cqfm-inputParameters][] + // extension[cqfm-expansionParameters][] + // extension[cqfm-cqlOptions] + get().getExtension().stream() + .filter(e -> REFERENCE_EXTENSIONS.contains(e.getUrl())) + .forEach(referenceExt -> references.add(new DependencyInfo( + referenceSource, + ((Reference) referenceExt.getValue()).getReference(), + referenceExt.getExtension(), + (reference) -> referenceExt.setValue(new Reference(reference))))); + + // extension[cqfm-component][].resource + get().getExtensionsByUrl(Constants.CQFM_COMPONENT).forEach(ext -> { + final var ref = (RelatedArtifact) ext.getValue(); + if (ref.hasResource()) { + final var dep = new DependencyInfo( + referenceSource, + ref.getResource(), + ref.getExtension(), + (reference) -> ref.setResource(reference)); + references.add(dep); + } + }); + + return references; + } +} diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/ParametersAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/ParametersAdapter.java index 58cffbd16..63cd6b506 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/ParametersAdapter.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/ParametersAdapter.java @@ -2,6 +2,7 @@ import java.util.List; import java.util.stream.Collectors; +import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseBackboneElement; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Parameters; @@ -49,6 +50,25 @@ public void setParameter(List parametersParameterComponent .collect(Collectors.toList())); } + @Override + public void addParameter(String name, IBase value) { + if (value instanceof Type) { + getParameters().addParameter(name, (Type) value); + } else { + throw new IllegalArgumentException("element passed as value argument is not a valid type"); + } + } + + @Override + public void addParameter(IBase parameter) { + if (parameter instanceof ParametersParameterComponent) { + getParameters().addParameter((ParametersParameterComponent) parameter); + } else { + throw new IllegalArgumentException( + "element passed as parameter argument is not a valid parameter component"); + } + } + @Override public ParametersParameterComponent addParameter() { return this.getParameters().addParameter(); diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/ParametersParameterComponentAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/ParametersParameterComponentAdapter.java index c15159396..b137fbe17 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/ParametersParameterComponentAdapter.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/ParametersParameterComponentAdapter.java @@ -1,20 +1,25 @@ package org.opencds.cqf.fhir.utility.adapter.r4; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.FhirVersionEnum; import java.util.List; import java.util.stream.Collectors; import org.hl7.fhir.instance.model.api.IBaseBackboneElement; import org.hl7.fhir.instance.model.api.IBaseDatatype; -import org.hl7.fhir.instance.model.api.IBaseExtension; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Parameters; import org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent; import org.hl7.fhir.r4.model.Resource; import org.hl7.fhir.r4.model.Type; +import org.opencds.cqf.cql.engine.model.ModelResolver; +import org.opencds.cqf.fhir.utility.model.FhirModelResolverCache; class ParametersParameterComponentAdapter implements org.opencds.cqf.fhir.utility.adapter.ParametersParameterComponentAdapter { - private Parameters.ParametersParameterComponent parametersParametersComponent; + private final Parameters.ParametersParameterComponent parametersParametersComponent; + private final FhirContext fhirContext; + private final ModelResolver modelResolver; protected Parameters.ParametersParameterComponent getParametersParameterComponent() { return this.parametersParametersComponent; @@ -31,6 +36,9 @@ public ParametersParameterComponentAdapter(IBaseBackboneElement parametersParame } this.parametersParametersComponent = (ParametersParameterComponent) parametersParametersComponent; + fhirContext = FhirContext.forCached(FhirVersionEnum.R5); + modelResolver = FhirModelResolverCache.resolverForVersion( + fhirContext.getVersion().getVersion()); } @Override @@ -107,13 +115,12 @@ public IBaseDatatype getValue() { } @Override - public Boolean hasExtension() { - return this.parametersParametersComponent.hasExtension(); + public FhirContext fhirContext() { + return fhirContext; } @Override - @SuppressWarnings("unchecked") - public List> getExtension() { - return (List>) (List) this.parametersParametersComponent.getExtension(); + public ModelResolver getModelResolver() { + return modelResolver; } } diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/PlanDefinitionAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/PlanDefinitionAdapter.java index 0088e07c2..3a1230539 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/PlanDefinitionAdapter.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/PlanDefinitionAdapter.java @@ -1,135 +1,49 @@ package org.opencds.cqf.fhir.utility.adapter.r4; -import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import java.util.ArrayList; -import java.util.Date; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; -import org.hl7.fhir.exceptions.FHIRException; -import org.hl7.fhir.instance.model.api.IBase; -import org.hl7.fhir.instance.model.api.IBaseExtension; -import org.hl7.fhir.instance.model.api.IBaseHasExtensions; -import org.hl7.fhir.instance.model.api.IBaseParameters; -import org.hl7.fhir.instance.model.api.ICompositeType; import org.hl7.fhir.instance.model.api.IDomainResource; -import org.hl7.fhir.instance.model.api.IIdType; -import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.r4.model.CanonicalType; -import org.hl7.fhir.r4.model.DateTimeType; -import org.hl7.fhir.r4.model.Enumerations.PublicationStatus; -import org.hl7.fhir.r4.model.Extension; -import org.hl7.fhir.r4.model.Period; import org.hl7.fhir.r4.model.PlanDefinition; -import org.hl7.fhir.r4.model.RelatedArtifact; -import org.hl7.fhir.r4.model.RelatedArtifact.RelatedArtifactType; -import org.opencds.cqf.fhir.api.Repository; import org.opencds.cqf.fhir.utility.adapter.DependencyInfo; import org.opencds.cqf.fhir.utility.adapter.IDependencyInfo; -import org.opencds.cqf.fhir.utility.adapter.KnowledgeArtifactAdapter; -import org.opencds.cqf.fhir.utility.visitor.KnowledgeArtifactVisitor; -public class PlanDefinitionAdapter extends ResourceAdapter implements KnowledgeArtifactAdapter { - - private PlanDefinition planDefinition; +public class PlanDefinitionAdapter extends KnowledgeArtifactAdapter { public PlanDefinitionAdapter(IDomainResource planDefinition) { super(planDefinition); - if (!(planDefinition instanceof PlanDefinition)) { throw new IllegalArgumentException( "resource passed as planDefinition argument is not a PlanDefinition resource"); } - - this.planDefinition = (PlanDefinition) planDefinition; } public PlanDefinitionAdapter(PlanDefinition planDefinition) { super(planDefinition); - this.planDefinition = planDefinition; - } - - @Override - public IBase accept(KnowledgeArtifactVisitor visitor, Repository repository, IBaseParameters operationParameters) { - return visitor.visit(this, repository, operationParameters); } protected PlanDefinition getPlanDefinition() { - return this.planDefinition; + return (PlanDefinition) resource; } @Override public PlanDefinition get() { - return this.planDefinition; + return getPlanDefinition(); } @Override public PlanDefinition copy() { - return this.get().copy(); - } - - @Override - public IIdType getId() { - return this.getPlanDefinition().getIdElement(); - } - - @Override - public void setId(IIdType id) { - this.getPlanDefinition().setId(id); - } - - @Override - public String getName() { - return this.getPlanDefinition().getName(); - } - - @Override - public String getPurpose() { - return this.getPlanDefinition().getPurpose(); - } - - @Override - public void setName(String name) { - this.getPlanDefinition().setName(name); - } - - @Override - public String getUrl() { - return this.getPlanDefinition().getUrl(); - } - - @Override - public boolean hasUrl() { - return this.getPlanDefinition().hasUrl(); - } - - @Override - public void setUrl(String url) { - this.getPlanDefinition().setUrl(url); - } - - @Override - public String getVersion() { - return this.getPlanDefinition().getVersion(); - } - - @Override - public boolean hasVersion() { - return this.getPlanDefinition().hasVersion(); - } - - @Override - public void setVersion(String version) { - this.getPlanDefinition().setVersion(version); + return get().copy(); } @Override public List getDependencies() { List references = new ArrayList<>(); - final String referenceSource = this.getPlanDefinition().hasVersion() - ? this.getPlanDefinition().getUrl() + "|" - + this.getPlanDefinition().getVersion() - : this.getPlanDefinition().getUrl(); + final String referenceSource = getReferenceSource(); + addProfileReferences(references, referenceSource); + /* relatedArtifact[].resource library[] @@ -146,20 +60,21 @@ public List getDependencies() { */ // relatedArtifact[].resource - references.addAll(this.getRelatedArtifact().stream() + references.addAll(getRelatedArtifact().stream() .map(ra -> DependencyInfo.convertRelatedArtifact(ra, referenceSource)) .collect(Collectors.toList())); // library[] - List libraries = this.getPlanDefinition().getLibrary(); + List libraries = getPlanDefinition().getLibrary(); for (CanonicalType ct : libraries) { DependencyInfo dependency = new DependencyInfo( referenceSource, ct.getValue(), ct.getExtension(), (reference) -> ct.setValue(reference)); references.add(dependency); } // action[] - this.planDefinition.getAction().forEach(action -> getDependenciesOfAction(action, references, referenceSource)); - this.getPlanDefinition().getExtension().stream() + getPlanDefinition().getAction().forEach(action -> getDependenciesOfAction(action, references, referenceSource)); + // extension[cpg-partOf] + getPlanDefinition().getExtension().stream() .filter(ext -> ext.getUrl().contains("cpg-partOf")) .filter(ext -> ext.hasValue()) .findAny() @@ -258,106 +173,4 @@ private void getDependenciesOfAction( } action.getAction().forEach(nestedAction -> getDependenciesOfAction(nestedAction, references, referenceSource)); } - - @Override - public Date getApprovalDate() { - return this.getPlanDefinition().getApprovalDate(); - } - - @Override - public Date getDate() { - return this.getPlanDefinition().getDate(); - } - - @Override - public void setDate(Date date) { - this.getPlanDefinition().setDate(date); - } - - @Override - public void setDateElement(IPrimitiveType date) { - if (date != null && !(date instanceof DateTimeType)) { - throw new UnprocessableEntityException("Date must be " + DateTimeType.class.getName()); - } - this.getPlanDefinition().setDateElement((DateTimeType) date); - } - - @Override - public Period getEffectivePeriod() { - return this.getPlanDefinition().getEffectivePeriod(); - } - - @Override - public void setApprovalDate(Date date) { - this.getPlanDefinition().setApprovalDate(date); - } - - @Override - public boolean hasRelatedArtifact() { - return this.getPlanDefinition().hasRelatedArtifact(); - } - - @SuppressWarnings("unchecked") - @Override - public List getRelatedArtifact() { - return this.getPlanDefinition().getRelatedArtifact(); - } - - @Override - public void setRelatedArtifact(List relatedArtifacts) { - this.getPlanDefinition() - .setRelatedArtifact(relatedArtifacts.stream() - .map(ra -> (RelatedArtifact) ra) - .collect(Collectors.toList())); - } - - @SuppressWarnings("unchecked") - @Override - public List getRelatedArtifactsOfType(String codeString) { - RelatedArtifactType type; - try { - type = RelatedArtifactType.fromCode(codeString); - } catch (FHIRException e) { - throw new UnprocessableEntityException("Invalid related artifact code"); - } - return this.getRelatedArtifact().stream() - .filter(ra -> ra.getType() == type) - .collect(Collectors.toList()); - } - - @Override - public void setEffectivePeriod(ICompositeType effectivePeriod) { - if (effectivePeriod != null && !(effectivePeriod instanceof Period)) { - throw new UnprocessableEntityException("EffectivePeriod must be " + Period.class.getName()); - } - this.getPlanDefinition().setEffectivePeriod((Period) effectivePeriod); - } - - @Override - public void setStatus(String statusCodeString) { - PublicationStatus status; - try { - status = PublicationStatus.fromCode(statusCodeString); - } catch (FHIRException e) { - throw new UnprocessableEntityException("Invalid status code"); - } - this.getPlanDefinition().setStatus(status); - } - - @Override - public String getStatus() { - return this.getPlanDefinition().getStatus() == null - ? null - : this.getPlanDefinition().getStatus().toCode(); - } - - @Override - public boolean getExperimental() { - return this.getPlanDefinition().getExperimental(); - } - - @Override - public void setExtension(List> extensions) { - this.get().setExtension(extensions.stream().map(e -> (Extension) e).collect(Collectors.toList())); - } } diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/QuestionnaireAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/QuestionnaireAdapter.java new file mode 100644 index 000000000..7056d3c01 --- /dev/null +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/QuestionnaireAdapter.java @@ -0,0 +1,135 @@ +package org.opencds.cqf.fhir.utility.adapter.r4; + +import java.util.ArrayList; +import java.util.List; +import org.hl7.fhir.instance.model.api.IDomainResource; +import org.hl7.fhir.r4.model.CanonicalType; +import org.hl7.fhir.r4.model.Expression; +import org.hl7.fhir.r4.model.Questionnaire; +import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemComponent; +import org.opencds.cqf.fhir.utility.Constants; +import org.opencds.cqf.fhir.utility.adapter.DependencyInfo; +import org.opencds.cqf.fhir.utility.adapter.IDependencyInfo; + +public class QuestionnaireAdapter extends KnowledgeArtifactAdapter + implements org.opencds.cqf.fhir.utility.adapter.QuestionnaireAdapter { + + public QuestionnaireAdapter(IDomainResource questionnaire) { + super(questionnaire); + if (!(questionnaire instanceof Questionnaire)) { + throw new IllegalArgumentException( + "resource passed as questionnaire argument is not a Questionnaire resource"); + } + } + + public QuestionnaireAdapter(Questionnaire questionnaire) { + super(questionnaire); + } + + protected Questionnaire getQuestionnaire() { + return (Questionnaire) resource; + } + + @Override + public Questionnaire get() { + return getQuestionnaire(); + } + + @Override + public Questionnaire copy() { + return get().copy(); + } + + @Override + public List getDependencies() { + List references = new ArrayList<>(); + final String referenceSource = getReferenceSource(); + addProfileReferences(references, referenceSource); + + /* + derivedFrom + extension[cqf-library] + extension[launchContext] + extension[variable].reference + item[]..definition // NOTE: This is not a simple canonical, it will have a fragment to identify the specific element + item[]..answerValueSet + item[]..extension[itemMedia] + item[]..extension[itemAnswerMedia] + item[]..extension[unitValueSet] + item[]..extension[referenceProfile] + item[]..extension[candidateExpression].reference + item[]..extension[lookupQuestionnaire] + item[]..extension[variable].reference + item[]..extension[initialExpression].reference + item[]..extension[calculatedExpression].reference + item[]..extension[cqf-calculatedValue].reference + item[]..extension[cqf-expression].reference + item[]..extension[sdc-questionnaire-subQuestionnaire] + */ + + // Not looking at launchContext as it references only base spec profiles and these are included implicitly as + // dependencies per the CRMI IG + + getQuestionnaire() + .getDerivedFrom() + .forEach(derivedRef -> references.add(new DependencyInfo( + referenceSource, + derivedRef.asStringValue(), + derivedRef.getExtension(), + (reference) -> derivedRef.setValue(reference)))); + + getQuestionnaire() + .getExtensionsByUrl(Constants.CQF_LIBRARY) + .forEach(libraryExt -> references.add(new DependencyInfo( + referenceSource, + ((CanonicalType) libraryExt.getValue()).asStringValue(), + libraryExt.getExtension(), + (reference) -> libraryExt.setValue(new CanonicalType(reference))))); + + getQuestionnaire().getExtensionsByUrl(Constants.VARIABLE_EXTENSION).stream() + .map(e -> (Expression) e.getValue()) + .filter(e -> e.hasReference()) + .forEach(expression -> references.add(new DependencyInfo( + referenceSource, + expression.getReference(), + expression.getExtension(), + (reference) -> expression.setReference(reference)))); + + getQuestionnaire().getItem().forEach(item -> getDependenciesOfItem(item, references, referenceSource)); + + return references; + } + + private void getDependenciesOfItem( + QuestionnaireItemComponent item, List references, String referenceSource) { + if (item.hasDefinition()) { + var definition = item.getDefinition().split("#")[0]; + // Not passing an updateReferenceConsumer here because the reference is not a simple canonical + references.add(new DependencyInfo(referenceSource, definition, item.getExtension(), null)); + } + if (item.hasAnswerValueSet()) { + references.add(new DependencyInfo( + referenceSource, + item.getAnswerValueSet(), + item.getExtension(), + (reference) -> item.setAnswerValueSet(reference))); + } + item.getExtension().stream() + .filter(e -> REFERENCE_EXTENSIONS.contains(e.getUrl())) + .forEach(referenceExt -> references.add(new DependencyInfo( + referenceSource, + ((CanonicalType) referenceExt.getValue()).asStringValue(), + referenceExt.getExtension(), + (reference) -> referenceExt.setValue(new CanonicalType(reference))))); + item.getExtension().stream() + .filter(e -> EXPRESSION_EXTENSIONS.contains(e.getUrl())) + .map(e -> (Expression) e.getValue()) + .filter(e -> e.hasReference()) + .forEach(expression -> references.add(new DependencyInfo( + referenceSource, + expression.getReference(), + expression.getExtension(), + (reference) -> expression.setReference(reference)))); + item.getItem().forEach(childItem -> getDependenciesOfItem(childItem, references, referenceSource)); + } +} diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/ResourceAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/ResourceAdapter.java index aa5ba049e..97efd14ba 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/ResourceAdapter.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/ResourceAdapter.java @@ -1,49 +1,51 @@ package org.opencds.cqf.fhir.utility.adapter.r4; +import static java.util.Optional.ofNullable; + import ca.uhn.fhir.context.FhirVersionEnum; +import java.util.Optional; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Base; +import org.hl7.fhir.r4.model.DomainResource; import org.hl7.fhir.r4.model.Resource; +import org.opencds.cqf.fhir.utility.adapter.BaseResourceAdapter; -class ResourceAdapter implements org.opencds.cqf.fhir.utility.adapter.ResourceAdapter { +class ResourceAdapter extends BaseResourceAdapter { ResourceAdapter(IBaseResource resource) { - if (resource == null) { - throw new IllegalArgumentException("resource can not be null"); - } - + super(resource); if (!resource.getStructureFhirVersionEnum().equals(FhirVersionEnum.R4)) { throw new IllegalArgumentException("resource is incorrect fhir version for this adapter"); } - - this.resource = (Resource) resource; } - private Resource resource; - protected Resource getResource() { - return this.resource; + return (Resource) resource; + } + + protected boolean isDomainResource() { + return getDomainResource().isPresent(); } - public IBaseResource get() { - return this.resource; + protected Optional getDomainResource() { + return ofNullable(resource instanceof DomainResource ? (DomainResource) resource : null); } @Override public IBase setProperty(String name, IBase value) throws FHIRException { - return this.getResource().setProperty(name, (Base) value); + return getResource().setProperty(name, (Base) value); } @Override public IBase addChild(String name) throws FHIRException { - return this.getResource().addChild(name); + return getResource().addChild(name); } @Override public IBase getSingleProperty(String name) throws FHIRException { - IBase[] values = this.getProperty(name, true); + IBase[] values = getProperty(name, true); if (values == null || values.length == 0) { return null; @@ -58,41 +60,41 @@ public IBase getSingleProperty(String name) throws FHIRException { @Override public IBase[] getProperty(String name) throws FHIRException { - return this.getProperty(name, true); + return getProperty(name, true); } @Override public IBase[] getProperty(String name, boolean checkValid) throws FHIRException { - return this.getResource().getProperty(name.hashCode(), name, checkValid); + return getResource().getProperty(name.hashCode(), name, checkValid); } @Override public IBase makeProperty(String name) throws FHIRException { - return this.getResource().makeProperty(name.hashCode(), name); + return getResource().makeProperty(name.hashCode(), name); } @Override public String[] getTypesForProperty(String name) throws FHIRException { - return this.getResource().getTypesForProperty(name.hashCode(), name); + return getResource().getTypesForProperty(name.hashCode(), name); } @Override public IBaseResource copy() { - return this.getResource().copy(); + return getResource().copy(); } @Override public void copyValues(IBaseResource dst) { - this.getResource().copyValues((Resource) dst); + getResource().copyValues((Resource) dst); } @Override public boolean equalsDeep(IBase other) { - return this.getResource().equalsDeep((Base) other); + return getResource().equalsDeep((Base) other); } @Override public boolean equalsShallow(IBase other) { - return this.getResource().equalsShallow((Base) other); + return getResource().equalsShallow((Base) other); } } diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/StructureDefinitionAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/StructureDefinitionAdapter.java new file mode 100644 index 000000000..8720bef5c --- /dev/null +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/StructureDefinitionAdapter.java @@ -0,0 +1,127 @@ +package org.opencds.cqf.fhir.utility.adapter.r4; + +import java.util.ArrayList; +import java.util.List; +import org.hl7.fhir.instance.model.api.IDomainResource; +import org.hl7.fhir.r4.model.ElementDefinition; +import org.hl7.fhir.r4.model.Expression; +import org.hl7.fhir.r4.model.StructureDefinition; +import org.opencds.cqf.fhir.utility.Constants; +import org.opencds.cqf.fhir.utility.adapter.DependencyInfo; +import org.opencds.cqf.fhir.utility.adapter.IDependencyInfo; + +public class StructureDefinitionAdapter extends KnowledgeArtifactAdapter { + + public StructureDefinitionAdapter(IDomainResource structureDefinition) { + super(structureDefinition); + if (!(structureDefinition instanceof StructureDefinition)) { + throw new IllegalArgumentException( + "resource passed as planDefinition argument is not a StructureDefinition resource"); + } + } + + public StructureDefinitionAdapter(StructureDefinition structureDefinition) { + super(structureDefinition); + } + + protected StructureDefinition getStructureDefinition() { + return (StructureDefinition) resource; + } + + @Override + public List getDependencies() { + List references = new ArrayList<>(); + final String referenceSource = getReferenceSource(); + addProfileReferences(references, referenceSource); + + /* + extension[].url + modifierExtension[].url + baseDefinition + differential.element[].type.code + differential.element[].type.profile[] + differential.element[].type.targetProfile[] + differential.element[].binding.valueSet + differential.element[].extension[].url + differential.element[].modifierExtension[].url + extension[cpg-inferenceExpression].reference + extension[cpg-assertionExpression].reference + extension[cpg-featureExpression].reference + */ + + if (get().hasBaseDefinition()) { + references.add(new DependencyInfo( + referenceSource, + get().getBaseDefinition(), + get().getBaseDefinitionElement().getExtension(), + (reference) -> get().setBaseDefinition(reference))); + } + get().getExtensionsByUrl(Constants.CPG_ASSERTION_EXPRESSION).stream() + .filter(e -> e.getValue() instanceof Expression) + .map(e -> (Expression) e.getValue()) + .filter(e -> e.hasReference()) + .forEach(expression -> references.add(new DependencyInfo( + referenceSource, + expression.getReference(), + expression.getExtension(), + (reference) -> expression.setReference(reference)))); + get().getExtensionsByUrl(Constants.CPG_FEATURE_EXPRESSION).stream() + .filter(e -> e.getValue() instanceof Expression) + .map(e -> (Expression) e.getValue()) + .filter(e -> e.hasReference()) + .forEach(expression -> references.add(new DependencyInfo( + referenceSource, + expression.getReference(), + expression.getExtension(), + (reference) -> expression.setReference(reference)))); + get().getExtensionsByUrl(Constants.CPG_INFERENCE_EXPRESSION).stream() + .filter(e -> e.getValue() instanceof Expression) + .map(e -> (Expression) e.getValue()) + .filter(e -> e.hasReference()) + .forEach(expression -> references.add(new DependencyInfo( + referenceSource, + expression.getReference(), + expression.getExtension(), + (reference) -> expression.setReference(reference)))); + get().getDifferential() + .getElement() + .forEach(element -> getDependenciesOfDifferential(element, references, referenceSource)); + + return references; + } + + private void getDependenciesOfDifferential( + ElementDefinition element, List references, String referenceSource) { + element.getType().forEach(type -> { + type.getProfile() + .forEach(profile -> references.add(new DependencyInfo( + referenceSource, + profile.getValueAsString(), + profile.getExtension(), + (reference) -> profile.setValue(reference)))); + type.getTargetProfile() + .forEach(profile -> references.add(new DependencyInfo( + referenceSource, + profile.getValueAsString(), + profile.getExtension(), + (reference) -> profile.setValue(reference)))); + }); + if (element.getBinding().hasValueSet()) { + references.add(new DependencyInfo( + referenceSource, + element.getBinding().getValueSet(), + element.getBinding().getExtension(), + (reference) -> element.getBinding().setValueSet(reference))); + } + } + + @Override + public StructureDefinition get() { + return getStructureDefinition(); + } + + @Override + public StructureDefinition copy() { + return get().copy(); + } +} diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/ValueSetAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/ValueSetAdapter.java index 0eea2c0d5..6165183be 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/ValueSetAdapter.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/ValueSetAdapter.java @@ -1,121 +1,56 @@ package org.opencds.cqf.fhir.utility.adapter.r4; -import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import static org.opencds.cqf.fhir.utility.ValueSets.getCodesInCompose; + +import java.time.Instant; import java.util.ArrayList; +import java.util.Collection; import java.util.Date; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; -import org.hl7.fhir.exceptions.FHIRException; -import org.hl7.fhir.instance.model.api.IBase; -import org.hl7.fhir.instance.model.api.IBaseExtension; -import org.hl7.fhir.instance.model.api.IBaseHasExtensions; -import org.hl7.fhir.instance.model.api.IBaseParameters; -import org.hl7.fhir.instance.model.api.ICompositeType; +import org.hl7.fhir.instance.model.api.IBaseBackboneElement; import org.hl7.fhir.instance.model.api.IDomainResource; -import org.hl7.fhir.instance.model.api.IIdType; -import org.hl7.fhir.instance.model.api.IPrimitiveType; +import org.hl7.fhir.r4.model.BooleanType; import org.hl7.fhir.r4.model.DateTimeType; -import org.hl7.fhir.r4.model.Enumerations.PublicationStatus; -import org.hl7.fhir.r4.model.Extension; -import org.hl7.fhir.r4.model.Period; -import org.hl7.fhir.r4.model.RelatedArtifact; -import org.hl7.fhir.r4.model.RelatedArtifact.RelatedArtifactType; import org.hl7.fhir.r4.model.ValueSet; -import org.opencds.cqf.fhir.api.Repository; +import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionComponent; import org.opencds.cqf.fhir.utility.adapter.DependencyInfo; import org.opencds.cqf.fhir.utility.adapter.IDependencyInfo; -import org.opencds.cqf.fhir.utility.visitor.KnowledgeArtifactVisitor; - -public class ValueSetAdapter extends ResourceAdapter implements org.opencds.cqf.fhir.utility.adapter.ValueSetAdapter { - private ValueSet valueSet; +public class ValueSetAdapter extends KnowledgeArtifactAdapter + implements org.opencds.cqf.fhir.utility.adapter.ValueSetAdapter { public ValueSetAdapter(IDomainResource valueSet) { super(valueSet); - if (!(valueSet instanceof ValueSet)) { throw new IllegalArgumentException("resource passed as valueSet argument is not a ValueSet resource"); } - - this.valueSet = (ValueSet) valueSet; } public ValueSetAdapter(ValueSet valueSet) { super(valueSet); - this.valueSet = valueSet; - } - - @Override - public IBase accept(KnowledgeArtifactVisitor visitor, Repository repository, IBaseParameters operationParameters) { - return visitor.visit(this, repository, operationParameters); } protected ValueSet getValueSet() { - return this.valueSet; + return (ValueSet) resource; } @Override public ValueSet get() { - return this.valueSet; + return (ValueSet) resource; } @Override public ValueSet copy() { - return this.get().copy(); - } - - @Override - public IIdType getId() { - return this.getValueSet().getIdElement(); - } - - @Override - public void setId(IIdType id) { - this.getValueSet().setId(id); - } - - @Override - public String getName() { - return this.getValueSet().getName(); - } - - @Override - public String getPurpose() { - return this.getValueSet().getPurpose(); - } - - @Override - public void setName(String name) { - this.getValueSet().setName(name); - } - - @Override - public String getUrl() { - return this.getValueSet().getUrl(); - } - - @Override - public void setUrl(String url) { - this.getValueSet().setUrl(url); - } - - @Override - public String getVersion() { - return this.getValueSet().getVersion(); - } - - @Override - public void setVersion(String version) { - this.getValueSet().setVersion(version); + return get().copy(); } @Override public List getDependencies() { List references = new ArrayList<>(); - final String referenceSource = this.getValueSet().hasVersion() - ? this.getValueSet().getUrl() + "|" + this.getValueSet().getVersion() - : this.getValueSet().getUrl(); + final String referenceSource = getReferenceSource(); + addProfileReferences(references, referenceSource); /* compose.include[].valueSet @@ -124,8 +59,8 @@ public List getDependencies() { compose.exclude[].system */ Stream.concat( - this.valueSet.getCompose().getInclude().stream(), - this.valueSet.getCompose().getExclude().stream()) + getValueSet().getCompose().getInclude().stream(), + getValueSet().getCompose().getExclude().stream()) .forEach(component -> { if (component.hasValueSet()) { component.getValueSet().forEach(ct -> { @@ -150,111 +85,77 @@ public List getDependencies() { } @Override - public Date getApprovalDate() { - return null; - } - - @Override - public Period getEffectivePeriod() { - return new Period(); - } - - @Override - public String getStatus() { - return this.getValueSet().getStatus() == null - ? null - : this.getValueSet().getStatus().toCode(); - } - - @Override - public void setStatus(String statusCodeString) { - PublicationStatus status; - try { - status = PublicationStatus.fromCode(statusCodeString); - } catch (FHIRException e) { - throw new UnprocessableEntityException("Invalid status code"); - } - this.getValueSet().setStatus(status); + public void setExpansion(T expansion) { + getValueSet().setExpansion((ValueSetExpansionComponent) expansion); } @SuppressWarnings("unchecked") @Override - public List getRelatedArtifactsOfType(String codeString) { - RelatedArtifactType type; - try { - type = RelatedArtifactType.fromCode(codeString); - } catch (FHIRException e) { - throw new UnprocessableEntityException("Invalid related artifact code"); - } - return this.getRelatedArtifact().stream() - .filter(ra -> ra.getType() == type) - .collect(Collectors.toList()); - } - - @Override - public boolean hasRelatedArtifact() { - return false; + public ValueSetExpansionComponent getExpansion() { + return getValueSet().getExpansion(); } @SuppressWarnings("unchecked") @Override - public List getRelatedArtifact() { - return new ArrayList<>(); - } - - @Override - public Date getDate() { - return this.getValueSet().getDate(); + public ValueSetExpansionComponent newExpansion() { + var expansion = new ValueSet.ValueSetExpansionComponent(new DateTimeType(Date.from(Instant.now()))); + expansion.getContains(); + return expansion; } @Override - public boolean getExperimental() { - return this.getValueSet().getExperimental(); - } - - @Override - public void setDateElement(IPrimitiveType date) throws UnprocessableEntityException { - if (date != null && !(date instanceof DateTimeType)) { - throw new UnprocessableEntityException("Date must be " + DateTimeType.class.getName()); - } - this.getValueSet().setDateElement((DateTimeType) date); + public List getValueSetIncludes() { + return getValueSet().getCompose().getInclude().stream() + .map(i -> i.getValueSet()) + .flatMap(Collection::stream) + .map(c -> c.asStringValue()) + .distinct() + .collect(Collectors.toList()); } @Override - public boolean hasUrl() { - return this.getValueSet().hasUrl(); + public boolean hasSimpleCompose() { + return getValueSet().hasCompose() + && !getValueSet().getCompose().hasExclude() + && getValueSet().getCompose().getInclude().stream() + .noneMatch( + csc -> csc.hasFilter() || csc.hasValueSet() || !csc.hasSystem() || !csc.hasConcept()); } @Override - public void setApprovalDate(Date approvalDate) { - // do nothing + public boolean hasGroupingCompose() { + return getValueSet().hasCompose() + && !getValueSet().getCompose().hasExclude() + && getValueSet().getCompose().getInclude().stream() + .noneMatch(csc -> !csc.hasValueSet() || csc.hasFilter()); } @Override - public boolean hasVersion() { - return this.getValueSet().hasVersion(); + public boolean hasNaiveParameter() { + return getValueSet().getExpansion().getParameter().stream() + .anyMatch(p -> p.getName().equals("naive")); } + @SuppressWarnings("unchecked") @Override - public void setDate(Date date) { - this.getValueSet().setDate(date); + public ValueSet.ValueSetExpansionParameterComponent createNaiveParameter() { + return new ValueSet.ValueSetExpansionParameterComponent() + .setName("naive") + .setValue(new BooleanType(true)); } @Override - public void setRelatedArtifact(List relatedArtifacts) { - // do nothing - } + public void naiveExpand() { + var expansion = newExpansion().addParameter(createNaiveParameter()); - @Override - public void setEffectivePeriod(ICompositeType effectivePeriod) { - if (effectivePeriod != null && !(effectivePeriod instanceof Period)) { - throw new UnprocessableEntityException("EffectivePeriod must be " + Period.class.getName()); + for (var code : getCodesInCompose(fhirContext, getValueSet())) { + expansion + .addContains() + .setCode(code.getCode()) + .setSystem(code.getSystem()) + .setVersion(code.getVersion()) + .setDisplay(code.getDisplay()); } - // do nothing - } - - @Override - public void setExtension(List> extensions) { - this.get().setExtension(extensions.stream().map(e -> (Extension) e).collect(Collectors.toList())); + getValueSet().setExpansion(expansion); } } diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/AdapterFactory.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/AdapterFactory.java index 9f65a26b0..3ae19dcb8 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/AdapterFactory.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/AdapterFactory.java @@ -6,11 +6,15 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.ICompositeType; import org.hl7.fhir.instance.model.api.IDomainResource; +import org.hl7.fhir.r5.model.Endpoint; import org.hl7.fhir.r5.model.Library; +import org.hl7.fhir.r5.model.Measure; import org.hl7.fhir.r5.model.MetadataResource; +import org.hl7.fhir.r5.model.Parameters; import org.hl7.fhir.r5.model.PlanDefinition; +import org.hl7.fhir.r5.model.Questionnaire; +import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.r5.model.ValueSet; -import org.opencds.cqf.fhir.utility.adapter.LibraryAdapter; public class AdapterFactory implements org.opencds.cqf.fhir.utility.adapter.AdapterFactory { @@ -18,6 +22,10 @@ public class AdapterFactory implements org.opencds.cqf.fhir.utility.adapter.Adap public org.opencds.cqf.fhir.utility.adapter.ResourceAdapter createResource(IBaseResource resource) { if (resource instanceof MetadataResource) { return createKnowledgeArtifactAdapter((MetadataResource) resource); + } else if (resource instanceof Endpoint) { + return createEndpoint(resource); + } else if (resource instanceof Parameters) { + return createParameters((Parameters) resource); } else { return new ResourceAdapter(resource); } @@ -26,28 +34,33 @@ public org.opencds.cqf.fhir.utility.adapter.ResourceAdapter createResource(IBase @Override public org.opencds.cqf.fhir.utility.adapter.KnowledgeArtifactAdapter createKnowledgeArtifactAdapter( IDomainResource resource) { - org.opencds.cqf.fhir.utility.adapter.KnowledgeArtifactAdapter retval; + org.opencds.cqf.fhir.utility.adapter.KnowledgeArtifactAdapter adapter; if (resource instanceof Library) { - retval = createLibrary(resource); + adapter = createLibrary(resource); + } else if (resource instanceof Measure) { + adapter = new MeasureAdapter((Measure) resource); } else if (resource instanceof PlanDefinition) { - retval = new org.opencds.cqf.fhir.utility.adapter.r5.PlanDefinitionAdapter((PlanDefinition) resource); + adapter = new PlanDefinitionAdapter((PlanDefinition) resource); + } else if (resource instanceof Questionnaire) { + adapter = new QuestionnaireAdapter((Questionnaire) resource); + } else if (resource instanceof StructureDefinition) { + adapter = new StructureDefinitionAdapter((StructureDefinition) resource); } else if (resource instanceof ValueSet) { - retval = new org.opencds.cqf.fhir.utility.adapter.r5.ValueSetAdapter((ValueSet) resource); + adapter = new ValueSetAdapter((ValueSet) resource); } else { if (resource instanceof MetadataResource) { - retval = new org.opencds.cqf.fhir.utility.adapter.r5.KnowledgeArtifactAdapter( - (MetadataResource) resource); + adapter = new KnowledgeArtifactAdapter((MetadataResource) resource); } else { throw new UnprocessableEntityException( - String.format("Resouce must be instance of %s", MetadataResource.class.getName())); + String.format("Resource must be instance of %s", MetadataResource.class.getName())); } } - return retval; + return adapter; } @Override - public LibraryAdapter createLibrary(IBaseResource library) { - return new org.opencds.cqf.fhir.utility.adapter.r5.LibraryAdapter((IDomainResource) library); + public org.opencds.cqf.fhir.utility.adapter.LibraryAdapter createLibrary(IBaseResource library) { + return new LibraryAdapter((IDomainResource) library); } @Override @@ -65,4 +78,9 @@ public org.opencds.cqf.fhir.utility.adapter.ParametersParameterComponentAdapter IBaseBackboneElement parametersParametersComponent) { return new ParametersParameterComponentAdapter(parametersParametersComponent); } + + @Override + public org.opencds.cqf.fhir.utility.adapter.EndpointAdapter createEndpoint(IBaseResource endpoint) { + return new EndpointAdapter(endpoint); + } } diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/AttachmentAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/AttachmentAdapter.java index 1d88f4254..266e33692 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/AttachmentAdapter.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/AttachmentAdapter.java @@ -1,11 +1,16 @@ package org.opencds.cqf.fhir.utility.adapter.r5; +import ca.uhn.fhir.context.FhirContext; import org.hl7.fhir.instance.model.api.ICompositeType; import org.hl7.fhir.r5.model.Attachment; +import org.opencds.cqf.cql.engine.model.ModelResolver; +import org.opencds.cqf.fhir.utility.model.FhirModelResolverCache; class AttachmentAdapter implements org.opencds.cqf.fhir.utility.adapter.AttachmentAdapter { - private Attachment attachment; + private final Attachment attachment; + private final FhirContext fhirContext; + private final ModelResolver modelResolver; public AttachmentAdapter(ICompositeType attachment) { if (attachment == null) { @@ -21,6 +26,9 @@ public AttachmentAdapter(ICompositeType attachment) { } this.attachment = (Attachment) attachment; + fhirContext = FhirContext.forR5Cached(); + modelResolver = FhirModelResolverCache.resolverForVersion( + fhirContext.getVersion().getVersion()); } protected Attachment getAttachment() { @@ -51,4 +59,14 @@ public byte[] getData() { public void setData(byte[] data) { this.getAttachment().setData(data); } + + @Override + public FhirContext fhirContext() { + return fhirContext; + } + + @Override + public ModelResolver getModelResolver() { + return modelResolver; + } } diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/EndpointAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/EndpointAdapter.java new file mode 100644 index 000000000..b986a786a --- /dev/null +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/EndpointAdapter.java @@ -0,0 +1,13 @@ +package org.opencds.cqf.fhir.utility.adapter.r5; + +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r5.model.Endpoint; + +public class EndpointAdapter extends ResourceAdapter implements org.opencds.cqf.fhir.utility.adapter.EndpointAdapter { + public EndpointAdapter(IBaseResource endpoint) { + super(endpoint); + if (!(endpoint instanceof Endpoint)) { + throw new IllegalArgumentException("resource passed as endpoint argument is not an Endpoint resource"); + } + } +} diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/KnowledgeArtifactAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/KnowledgeArtifactAdapter.java index 7a1ed87d1..bc6b98cd8 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/KnowledgeArtifactAdapter.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/KnowledgeArtifactAdapter.java @@ -6,23 +6,17 @@ import java.util.List; import java.util.stream.Collectors; import org.hl7.fhir.exceptions.FHIRException; -import org.hl7.fhir.instance.model.api.IBase; -import org.hl7.fhir.instance.model.api.IBaseExtension; import org.hl7.fhir.instance.model.api.IBaseHasExtensions; -import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.ICompositeType; import org.hl7.fhir.instance.model.api.IDomainResource; import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.r5.model.DateTimeType; import org.hl7.fhir.r5.model.Enumerations.PublicationStatus; -import org.hl7.fhir.r5.model.Extension; import org.hl7.fhir.r5.model.MetadataResource; import org.hl7.fhir.r5.model.Period; import org.hl7.fhir.r5.model.RelatedArtifact; import org.hl7.fhir.r5.model.RelatedArtifact.RelatedArtifactType; -import org.opencds.cqf.fhir.api.Repository; import org.opencds.cqf.fhir.utility.adapter.IDependencyInfo; -import org.opencds.cqf.fhir.utility.visitor.KnowledgeArtifactVisitor; public class KnowledgeArtifactAdapter extends ResourceAdapter implements org.opencds.cqf.fhir.utility.adapter.KnowledgeArtifactAdapter { @@ -31,79 +25,25 @@ public class KnowledgeArtifactAdapter extends ResourceAdapter public KnowledgeArtifactAdapter(IDomainResource resource) { super(resource); if (!(resource instanceof MetadataResource)) { - throw new IllegalArgumentException("resource argument is not a MetadataResource resource"); + throw new IllegalArgumentException( + "resource passed as resource argument is not a MetadataResource resource"); } - this.adaptedResource = (MetadataResource) resource; + adaptedResource = (MetadataResource) resource; } public KnowledgeArtifactAdapter(MetadataResource resource) { super(resource); - this.adaptedResource = resource; + adaptedResource = resource; } @Override public MetadataResource get() { - return this.adaptedResource; + return adaptedResource; } @Override public MetadataResource copy() { - return this.get().copy(); - } - - @Override - public String getUrl() { - return this.get().getUrl(); - } - - @Override - public boolean hasUrl() { - return this.get().hasUrl(); - } - - @Override - public void setUrl(String url) { - this.get().setUrl(url); - } - - @Override - public void setVersion(String version) { - this.get().setVersion(version); - } - - @Override - public String getVersion() { - return this.get().getVersion(); - } - - @Override - public boolean hasVersion() { - return this.get().hasVersion(); - } - - @Override - public String getName() { - return this.get().getName(); - } - - @Override - public void setName(String name) { - this.get().setName(name); - } - - @Override - public Date getApprovalDate() { - return this.get().getApprovalDate(); - } - - @Override - public Date getDate() { - return this.get().getDate(); - } - - @Override - public void setDate(Date approvalDate) { - this.get().setDate(approvalDate); + return get().copy(); } @Override @@ -111,32 +51,7 @@ public void setDateElement(IPrimitiveType date) { if (date != null && !(date instanceof DateTimeType)) { throw new UnprocessableEntityException("Date must be " + DateTimeType.class.getName()); } - this.get().setDateElement((DateTimeType) date); - } - - @Override - public void setApprovalDate(Date approvalDate) { - this.get().setApprovalDate(approvalDate); - } - - @Override - public Period getEffectivePeriod() { - return this.get().getEffectivePeriod(); - } - - @Override - public List getDependencies() { - return new ArrayList<>(); - } - - @Override - public String getPurpose() { - return this.get().getPurpose(); - } - - @Override - public IBase accept(KnowledgeArtifactVisitor visitor, Repository repository, IBaseParameters operationParameters) { - return visitor.visit(this, repository, operationParameters); + org.opencds.cqf.fhir.utility.adapter.KnowledgeArtifactAdapter.super.setDateElement(date); } @Override @@ -144,18 +59,15 @@ public void setEffectivePeriod(ICompositeType effectivePeriod) { if (effectivePeriod != null && !(effectivePeriod instanceof Period)) { throw new UnprocessableEntityException("EffectivePeriod must be a valid " + Period.class.getName()); } - this.get().setEffectivePeriod((Period) effectivePeriod); + org.opencds.cqf.fhir.utility.adapter.KnowledgeArtifactAdapter.super.setEffectivePeriod(effectivePeriod); } @Override - public boolean hasRelatedArtifact() { - return this.get().hasRelatedArtifact(); - } - - @SuppressWarnings("unchecked") - @Override - public List getRelatedArtifact() { - return this.get().getRelatedArtifact(); + public List getDependencies() { + List references = new ArrayList<>(); + final String referenceSource = getReferenceSource(); + addProfileReferences(references, referenceSource); + return references; } @SuppressWarnings("unchecked") @@ -167,25 +79,15 @@ public List getRelatedArtifactsOfType(String codeString) { } catch (FHIRException e) { throw new UnprocessableEntityException("Invalid related artifact code"); } - return this.getRelatedArtifact().stream() + return getRelatedArtifact().stream() + .map(ra -> (RelatedArtifact) ra) .filter(ra -> ra.getType() == type) .collect(Collectors.toList()); } @Override - public void setRelatedArtifact(List relatedArtifacts) - throws UnprocessableEntityException { - this.get() - .setRelatedArtifact(relatedArtifacts.stream() - .map(ra -> { - try { - return (RelatedArtifact) ra; - } catch (ClassCastException e) { - throw new UnprocessableEntityException( - "All related artifacts must be of type " + RelatedArtifact.class.getName()); - } - }) - .collect(Collectors.toList())); + public String getStatus() { + return get().getStatus() == null ? null : get().getStatus().toCode(); } @Override @@ -196,21 +98,21 @@ public void setStatus(String statusCodeString) { } catch (FHIRException e) { throw new UnprocessableEntityException("Invalid status code"); } - this.get().setStatus(status); - } - - @Override - public String getStatus() { - return this.get().getStatus() == null ? null : this.get().getStatus().toCode(); - } - - @Override - public boolean getExperimental() { - return this.get().getExperimental(); + get().setStatus(status); } @Override - public void setExtension(List> extensions) { - this.get().setExtension(extensions.stream().map(e -> (Extension) e).collect(Collectors.toList())); + public void setRelatedArtifact(List relatedArtifacts) + throws UnprocessableEntityException { + org.opencds.cqf.fhir.utility.adapter.KnowledgeArtifactAdapter.super.setRelatedArtifact(relatedArtifacts.stream() + .map(ra -> { + try { + return (RelatedArtifact) ra; + } catch (ClassCastException e) { + throw new UnprocessableEntityException( + "All related artifacts must be of type " + RelatedArtifact.class.getName()); + } + }) + .collect(Collectors.toList())); } } diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/LibraryAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/LibraryAdapter.java index be93dd368..e90371e6e 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/LibraryAdapter.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/LibraryAdapter.java @@ -2,270 +2,143 @@ import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import java.util.ArrayList; -import java.util.Date; import java.util.List; import java.util.stream.Collectors; -import org.hl7.fhir.exceptions.FHIRException; -import org.hl7.fhir.instance.model.api.IBase; -import org.hl7.fhir.instance.model.api.IBaseExtension; -import org.hl7.fhir.instance.model.api.IBaseHasExtensions; -import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.ICompositeType; import org.hl7.fhir.instance.model.api.IDomainResource; -import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.r5.model.Attachment; -import org.hl7.fhir.r5.model.DateTimeType; -import org.hl7.fhir.r5.model.Enumerations.PublicationStatus; -import org.hl7.fhir.r5.model.Extension; +import org.hl7.fhir.r5.model.CodeableConcept; +import org.hl7.fhir.r5.model.Coding; +import org.hl7.fhir.r5.model.DataRequirement; import org.hl7.fhir.r5.model.Library; -import org.hl7.fhir.r5.model.Period; import org.hl7.fhir.r5.model.RelatedArtifact; -import org.hl7.fhir.r5.model.RelatedArtifact.RelatedArtifactType; -import org.opencds.cqf.fhir.api.Repository; +import org.hl7.fhir.r5.model.UsageContext; import org.opencds.cqf.fhir.utility.adapter.DependencyInfo; import org.opencds.cqf.fhir.utility.adapter.IDependencyInfo; -import org.opencds.cqf.fhir.utility.visitor.KnowledgeArtifactVisitor; - -public class LibraryAdapter extends ResourceAdapter implements org.opencds.cqf.fhir.utility.adapter.LibraryAdapter { - - private Library library; +public class LibraryAdapter extends KnowledgeArtifactAdapter + implements org.opencds.cqf.fhir.utility.adapter.LibraryAdapter { public LibraryAdapter(IDomainResource library) { super(library); - if (!(library instanceof Library)) { throw new IllegalArgumentException("resource passed as library argument is not a Library resource"); } + } - this.library = (Library) library; + public LibraryAdapter(Library library) { + super(library); } protected Library getLibrary() { - return this.library; + return (Library) resource; } @Override public Library get() { - return this.library; + return (Library) resource; } @Override public Library copy() { - return this.get().copy(); - } - - @Override - public String getName() { - return this.getLibrary().getName(); - } - - @Override - public String getPurpose() { - return this.getLibrary().getPurpose(); - } - - @Override - public void setName(String name) { - this.getLibrary().setName(name); - } - - @Override - public String getUrl() { - return this.getLibrary().getUrl(); - } - - @Override - public boolean hasUrl() { - return this.getLibrary().hasUrl(); - } - - @Override - public void setUrl(String url) { - this.getLibrary().setUrl(url); - } - - @Override - public String getVersion() { - return this.getLibrary().getVersion(); - } - - @Override - public boolean hasVersion() { - return this.getLibrary().hasVersion(); - } - - @Override - public void setVersion(String version) { - this.getLibrary().setVersion(version); + return get().copy(); } @Override public boolean hasContent() { - return this.getLibrary().hasContent(); + return getLibrary().hasContent(); } @Override public List getContent() { - return this.getLibrary().getContent().stream().collect(Collectors.toList()); + return getLibrary().getContent().stream().collect(Collectors.toList()); } @Override public void setContent(List attachments) { - var castAttachments = attachments.stream().map(x -> (Attachment) x).collect(Collectors.toList()); - this.getLibrary().setContent(castAttachments); + List castAttachments = + attachments.stream().map(x -> (Attachment) x).collect(Collectors.toList()); + getLibrary().setContent(castAttachments); } @Override public Attachment addContent() { - return this.getLibrary().addContent(); + return getLibrary().addContent(); } @Override public List getDependencies() { - List retval = new ArrayList(); - final String referenceSource = - this.hasVersion() ? this.getUrl() + "|" + this.getLibrary().getVersion() : this.getUrl(); - this.getRelatedArtifact().stream() + List references = new ArrayList(); + final String referenceSource = getReferenceSource(); + addProfileReferences(references, referenceSource); + + // relatedArtifact[].resource + getRelatedArtifact().stream() + .map(ra -> (RelatedArtifact) ra) .filter(ra -> ra.hasResource()) .map(ra -> DependencyInfo.convertRelatedArtifact(ra, referenceSource)) - .forEach(dep -> retval.add(dep)); - this.getLibrary().getDataRequirement().stream().forEach(dr -> { + .forEach(ra -> references.add(ra)); + getLibrary().getDataRequirement().stream().forEach(dr -> { dr.getProfile().stream() .filter(profile -> profile.hasValue()) - .forEach(profile -> retval.add(new DependencyInfo( + .forEach(profile -> references.add(new DependencyInfo( referenceSource, profile.getValue(), profile.getExtension(), (reference) -> profile.setValue(reference)))); dr.getCodeFilter().stream() .filter(cf -> cf.hasValueSet()) - .forEach(cf -> retval.add(new DependencyInfo( + .forEach(cf -> references.add(new DependencyInfo( referenceSource, cf.getValueSet(), cf.getExtension(), (reference) -> cf.setValueSet(reference)))); }); - return retval; - } - - @Override - public IBase accept(KnowledgeArtifactVisitor visitor, Repository repository, IBaseParameters operationParameters) { - return visitor.visit(this, repository, operationParameters); - } - - @Override - public Date getApprovalDate() { - return this.library.getApprovalDate(); - } - - @Override - public Date getDate() { - return this.getLibrary().getDate(); - } - - @Override - public void setDate(Date date) { - this.getLibrary().setDate(date); - } - - @Override - public void setDateElement(IPrimitiveType date) { - if (date != null && !(date instanceof DateTimeType)) { - throw new UnprocessableEntityException("Date must be " + DateTimeType.class.getName()); - } - this.getLibrary().setDateElement((DateTimeType) date); - } - - @Override - public Period getEffectivePeriod() { - return this.getLibrary().getEffectivePeriod(); - } - - @Override - public boolean hasRelatedArtifact() { - return this.getLibrary().hasRelatedArtifact(); + return references; } @SuppressWarnings("unchecked") @Override - public List getRelatedArtifact() { - return this.getLibrary().getRelatedArtifact(); + public List getComponents() { + return getRelatedArtifactsOfType("composed-of"); } @Override - public void setEffectivePeriod(ICompositeType effectivePeriod) { - if (effectivePeriod != null && !(effectivePeriod instanceof Period)) { - throw new UnprocessableEntityException("EffectivePeriod must be org.hl7.fhir.r5.model.Period"); - } - this.getLibrary().setEffectivePeriod((Period) effectivePeriod); + public ICompositeType getType() { + return getLibrary().getType(); } - @SuppressWarnings("unchecked") @Override - public List getRelatedArtifactsOfType(String codeString) { - RelatedArtifactType type; - try { - type = RelatedArtifactType.fromCode(codeString); - } catch (FHIRException e) { - throw new UnprocessableEntityException("Invalid related artifact code"); + public LibraryAdapter setType(String type) { + if (LIBRARY_TYPES.contains(type)) { + getLibrary() + .setType(new CodeableConcept(new Coding("http://hl7.org/fhir/ValueSet/library-type", type, ""))); + } else { + throw new UnprocessableEntityException("Invalid type: {}", type); } - return this.getRelatedArtifact().stream() - .filter(ra -> ra.getType() == type) - .collect(Collectors.toList()); + return this; } @Override - public void setApprovalDate(Date approvalDate) { - this.getLibrary().setApprovalDate(approvalDate); - } - - @SuppressWarnings("unchecked") - @Override - public List getComponents() { - return this.getRelatedArtifactsOfType("composed-of"); - } - - @Override - public void setRelatedArtifact(List relatedArtifacts) - throws UnprocessableEntityException { - this.getLibrary() - .setRelatedArtifact(relatedArtifacts.stream() - .map(ra -> { - try { - return (RelatedArtifact) ra; - } catch (ClassCastException e) { - throw new UnprocessableEntityException( - "All related artifacts must be of type " + RelatedArtifact.class.getName()); - } - }) - .collect(Collectors.toList())); - } - - @Override - public void setStatus(String statusCodeString) { - PublicationStatus status; - try { - status = PublicationStatus.fromCode(statusCodeString); - } catch (FHIRException e) { - throw new UnprocessableEntityException("Invalid status code"); - } - this.getLibrary().setStatus(status); + public List getDataRequirement() { + return getLibrary().getDataRequirement(); } @Override - public String getStatus() { - return this.getLibrary().getStatus() == null - ? null - : this.getLibrary().getStatus().toCode(); + public LibraryAdapter addDataRequirement(ICompositeType dataRequirement) { + getLibrary().addDataRequirement((DataRequirement) dataRequirement); + return this; } @Override - public boolean getExperimental() { - return this.getLibrary().getExperimental(); + public LibraryAdapter setDataRequirement(List dataRequirement) { + getLibrary() + .setDataRequirement( + dataRequirement.stream().map(dr -> (DataRequirement) dr).collect(Collectors.toList())); + return this; } @Override - public void setExtension(List> extensions) { - this.get().setExtension(extensions.stream().map(e -> (Extension) e).collect(Collectors.toList())); + public List getUseContext() { + return getLibrary().getUseContext(); } } diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/MeasureAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/MeasureAdapter.java new file mode 100644 index 000000000..eef30e04b --- /dev/null +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/MeasureAdapter.java @@ -0,0 +1,198 @@ +package org.opencds.cqf.fhir.utility.adapter.r5; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import org.hl7.fhir.instance.model.api.IDomainResource; +import org.hl7.fhir.r5.model.CanonicalType; +import org.hl7.fhir.r5.model.Library; +import org.hl7.fhir.r5.model.Measure; +import org.hl7.fhir.r5.model.Reference; +import org.hl7.fhir.r5.model.RelatedArtifact; +import org.opencds.cqf.fhir.utility.Constants; +import org.opencds.cqf.fhir.utility.adapter.DependencyInfo; +import org.opencds.cqf.fhir.utility.adapter.IDependencyInfo; + +public class MeasureAdapter extends KnowledgeArtifactAdapter + implements org.opencds.cqf.fhir.utility.adapter.MeasureAdapter { + + public MeasureAdapter(IDomainResource measure) { + super(measure); + if (!(measure instanceof Measure)) { + throw new IllegalArgumentException("resource passed as measure argument is not a Measure resource"); + } + } + + public MeasureAdapter(Measure measure) { + super(measure); + } + + protected Measure getMeasure() { + return (Measure) resource; + } + + @Override + public Measure get() { + return getMeasure(); + } + + @Override + public Measure copy() { + return get().copy(); + } + + private boolean checkedEffectiveDataRequirements; + private Library effectiveDataRequirements; + private LibraryAdapter effectiveDataRequirementsAdapter; + + private void findEffectiveDataRequirements() { + if (!checkedEffectiveDataRequirements) { + var edrExtensions = this.getMeasure().getExtension().stream() + .filter(ext -> ext.getUrl().endsWith("-effectiveDataRequirements")) + .filter(ext -> ext.hasValue()) + .collect(Collectors.toList()); + + var edrExtension = edrExtensions.size() == 1 ? edrExtensions.get(0) : null; + if (edrExtension != null) { + var edrReference = ((CanonicalType) edrExtension.getValue()).getValue(); + for (var c : getMeasure().getContained()) { + if (c.hasId() && String.format("#%s", c.getId()).equals(edrReference) && c instanceof Library) { + effectiveDataRequirements = (Library) c; + effectiveDataRequirementsAdapter = new LibraryAdapter(effectiveDataRequirements); + } + } + } + checkedEffectiveDataRequirements = true; + } + } + + @Override + public List getDependencies() { + List references = new ArrayList<>(); + final String referenceSource = getReferenceSource(); + addProfileReferences(references, referenceSource); + + // If an effectiveDataRequirements library is present, use it exclusively + findEffectiveDataRequirements(); + if (effectiveDataRequirements != null) { + references.addAll(effectiveDataRequirementsAdapter.getDependencies()); + return references; + } + + // Otherwise, fall back to the relatedArtifact and library + + /* + relatedArtifact[].resource + library[] + group[].population[].criteria.reference + group[].stratifier[].criteria.reference + group[].stratifier[].component[].criteria.reference + supplementalData[].criteria.reference + extension[cqfm-inputParameters][] + extension[cqfm-expansionParameters][] + extension[cqfm-effectiveDataRequirements] + extension[cqfm-cqlOptions] + extension[cqfm-component][].resource + extension[crmi-effectiveDataRequirements] + */ + + // relatedArtifact[].resource + references.addAll(getRelatedArtifact().stream() + .map(ra -> DependencyInfo.convertRelatedArtifact(ra, referenceSource)) + .collect(Collectors.toList())); + + // library[] + for (var library : getMeasure().getLibrary()) { + DependencyInfo dependency = new DependencyInfo( + referenceSource, + library.getValue(), + library.getExtension(), + (reference) -> library.setValue(reference)); + references.add(dependency); + } + + for (final var group : getMeasure().getGroup()) { + for (final var population : group.getPopulation()) { + // group[].population[].criteria.reference + if (population.getCriteria().hasReference()) { + final var dependency = new DependencyInfo( + referenceSource, + population.getCriteria().getReference(), + population.getCriteria().getExtension(), + (reference) -> population.getCriteria().setReference(reference)); + references.add(dependency); + } + } + for (final var stratifier : group.getStratifier()) { + // group[].stratifier[].criteria.reference + if (stratifier.getCriteria().hasReference()) { + final var dependency = new DependencyInfo( + referenceSource, + stratifier.getCriteria().getReference(), + stratifier.getCriteria().getExtension(), + (reference) -> stratifier.getCriteria().setReference(reference)); + references.add(dependency); + } + for (final var component : stratifier.getComponent()) { + // group[].stratifier[].component[].criteria.reference + if (component.getCriteria().hasReference()) { + final var stratifierComponentDep = new DependencyInfo( + referenceSource, + component.getCriteria().getReference(), + component.getCriteria().getExtension(), + (reference) -> component.getCriteria().setReference(reference)); + references.add(stratifierComponentDep); + } + } + } + } + + for (final var supplement : getMeasure().getSupplementalData()) { + // supplementalData[].criteria.reference + if (supplement.getCriteria().hasReference()) { + final var dependency = new DependencyInfo( + referenceSource, + supplement.getCriteria().getReference(), + supplement.getCriteria().getExtension(), + (reference) -> supplement.getCriteria().setReference(reference)); + references.add(dependency); + } + } + + // extension[cqfm-effectiveDataRequirements] + // extension[crmi-effectiveDataRequirements] + get().getExtension().stream() + .filter(e -> CANONICAL_EXTENSIONS.contains(e.getUrl())) + .forEach(referenceExt -> references.add(new DependencyInfo( + referenceSource, + ((CanonicalType) referenceExt.getValue()).getValue(), + referenceExt.getExtension(), + (reference) -> referenceExt.setValue(new CanonicalType(reference))))); + + // extension[cqfm-inputParameters][] + // extension[cqfm-expansionParameters][] + // extension[cqfm-cqlOptions] + get().getExtension().stream() + .filter(e -> REFERENCE_EXTENSIONS.contains(e.getUrl())) + .forEach(referenceExt -> references.add(new DependencyInfo( + referenceSource, + ((Reference) referenceExt.getValue()).getReference(), + referenceExt.getExtension(), + (reference) -> referenceExt.setValue(new Reference(reference))))); + + // extension[cqfm-component][].resource + get().getExtensionsByUrl(Constants.CQFM_COMPONENT).forEach(ext -> { + final var ref = (RelatedArtifact) ext.getValue(); + if (ref.hasResource()) { + final var dep = new DependencyInfo( + referenceSource, + ref.getResource(), + ref.getExtension(), + (reference) -> ref.setResource(reference)); + references.add(dep); + } + }); + + return references; + } +} diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/ParametersAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/ParametersAdapter.java index ad34ed36c..08403986f 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/ParametersAdapter.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/ParametersAdapter.java @@ -2,6 +2,7 @@ import java.util.List; import java.util.stream.Collectors; +import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseBackboneElement; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r5.model.DataType; @@ -49,6 +50,25 @@ public void setParameter(List parametersParameterComponent .collect(Collectors.toList())); } + @Override + public void addParameter(String name, IBase value) { + if (value instanceof DataType) { + getParameters().addParameter(name, (DataType) value); + } else { + throw new IllegalArgumentException("element passed as value argument is not a valid data type"); + } + } + + @Override + public void addParameter(IBase parameter) { + if (parameter instanceof ParametersParameterComponent) { + getParameters().addParameter((ParametersParameterComponent) parameter); + } else { + throw new IllegalArgumentException( + "element passed as parameter argument is not a valid parameter component"); + } + } + @Override public ParametersParameterComponent addParameter() { return this.getParameters().addParameter(); diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/ParametersParameterComponentAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/ParametersParameterComponentAdapter.java index 5f843651c..ba47dfc62 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/ParametersParameterComponentAdapter.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/ParametersParameterComponentAdapter.java @@ -1,20 +1,25 @@ package org.opencds.cqf.fhir.utility.adapter.r5; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.FhirVersionEnum; import java.util.List; import java.util.stream.Collectors; import org.hl7.fhir.instance.model.api.IBaseBackboneElement; import org.hl7.fhir.instance.model.api.IBaseDatatype; -import org.hl7.fhir.instance.model.api.IBaseExtension; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r5.model.DataType; import org.hl7.fhir.r5.model.Parameters; import org.hl7.fhir.r5.model.Parameters.ParametersParameterComponent; import org.hl7.fhir.r5.model.Resource; +import org.opencds.cqf.cql.engine.model.ModelResolver; +import org.opencds.cqf.fhir.utility.model.FhirModelResolverCache; class ParametersParameterComponentAdapter implements org.opencds.cqf.fhir.utility.adapter.ParametersParameterComponentAdapter { - private Parameters.ParametersParameterComponent parametersParametersComponent; + private final Parameters.ParametersParameterComponent parametersParametersComponent; + private final FhirContext fhirContext; + private final ModelResolver modelResolver; protected Parameters.ParametersParameterComponent getParametersParameterComponent() { return this.parametersParametersComponent; @@ -31,6 +36,9 @@ public ParametersParameterComponentAdapter(IBaseBackboneElement parametersParame } this.parametersParametersComponent = (ParametersParameterComponent) parametersParametersComponent; + fhirContext = FhirContext.forCached(FhirVersionEnum.R5); + modelResolver = FhirModelResolverCache.resolverForVersion( + fhirContext.getVersion().getVersion()); } @Override @@ -107,13 +115,12 @@ public IBaseDatatype getValue() { } @Override - public Boolean hasExtension() { - return this.parametersParametersComponent.hasExtension(); + public FhirContext fhirContext() { + return fhirContext; } @Override - @SuppressWarnings("unchecked") - public List> getExtension() { - return (List>) (List) this.parametersParametersComponent.getExtension(); + public ModelResolver getModelResolver() { + return modelResolver; } } diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/PlanDefinitionAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/PlanDefinitionAdapter.java index 11772d385..0927d5ddb 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/PlanDefinitionAdapter.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/PlanDefinitionAdapter.java @@ -1,130 +1,49 @@ package org.opencds.cqf.fhir.utility.adapter.r5; -import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import java.util.ArrayList; -import java.util.Date; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; -import org.hl7.fhir.exceptions.FHIRException; -import org.hl7.fhir.instance.model.api.IBase; -import org.hl7.fhir.instance.model.api.IBaseExtension; -import org.hl7.fhir.instance.model.api.IBaseHasExtensions; -import org.hl7.fhir.instance.model.api.IBaseParameters; -import org.hl7.fhir.instance.model.api.ICompositeType; import org.hl7.fhir.instance.model.api.IDomainResource; -import org.hl7.fhir.instance.model.api.IIdType; -import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.r5.model.CanonicalType; -import org.hl7.fhir.r5.model.DateTimeType; -import org.hl7.fhir.r5.model.Enumerations.PublicationStatus; -import org.hl7.fhir.r5.model.Extension; -import org.hl7.fhir.r5.model.Period; import org.hl7.fhir.r5.model.PlanDefinition; -import org.hl7.fhir.r5.model.RelatedArtifact; -import org.hl7.fhir.r5.model.RelatedArtifact.RelatedArtifactType; -import org.opencds.cqf.fhir.api.Repository; import org.opencds.cqf.fhir.utility.adapter.DependencyInfo; import org.opencds.cqf.fhir.utility.adapter.IDependencyInfo; -import org.opencds.cqf.fhir.utility.adapter.KnowledgeArtifactAdapter; -import org.opencds.cqf.fhir.utility.visitor.KnowledgeArtifactVisitor; -public class PlanDefinitionAdapter extends ResourceAdapter implements KnowledgeArtifactAdapter { - - private PlanDefinition planDefinition; +public class PlanDefinitionAdapter extends KnowledgeArtifactAdapter { public PlanDefinitionAdapter(IDomainResource planDefinition) { super(planDefinition); - if (!(planDefinition instanceof PlanDefinition)) { throw new IllegalArgumentException( "resource passed as planDefinition argument is not a PlanDefinition resource"); } - - this.planDefinition = (PlanDefinition) planDefinition; } - @Override - public IBase accept(KnowledgeArtifactVisitor visitor, Repository repository, IBaseParameters operationParameters) { - return visitor.visit(this, repository, operationParameters); + public PlanDefinitionAdapter(PlanDefinition planDefinition) { + super(planDefinition); } protected PlanDefinition getPlanDefinition() { - return this.planDefinition; + return (PlanDefinition) resource; } @Override public PlanDefinition get() { - return this.planDefinition; + return getPlanDefinition(); } @Override public PlanDefinition copy() { - return this.get().copy(); - } - - @Override - public IIdType getId() { - return this.getPlanDefinition().getIdElement(); - } - - @Override - public void setId(IIdType id) { - this.getPlanDefinition().setId(id); - } - - @Override - public String getName() { - return this.getPlanDefinition().getName(); - } - - @Override - public String getPurpose() { - return this.getPlanDefinition().getPurpose(); - } - - @Override - public void setName(String name) { - this.getPlanDefinition().setName(name); - } - - @Override - public String getUrl() { - return this.getPlanDefinition().getUrl(); - } - - @Override - public boolean hasUrl() { - return this.getPlanDefinition().hasUrl(); - } - - @Override - public void setUrl(String url) { - this.getPlanDefinition().setUrl(url); - } - - @Override - public String getVersion() { - return this.getPlanDefinition().getVersion(); - } - - @Override - public boolean hasVersion() { - return this.getPlanDefinition().hasVersion(); - } - - @Override - public void setVersion(String version) { - this.getPlanDefinition().setVersion(version); + return get().copy(); } @Override public List getDependencies() { List references = new ArrayList<>(); - final String referenceSource = this.getPlanDefinition().hasVersion() - ? this.getPlanDefinition().getUrl() + "|" - + this.getPlanDefinition().getVersion() - : this.getPlanDefinition().getUrl(); + final String referenceSource = getReferenceSource(); + addProfileReferences(references, referenceSource); + /* relatedArtifact[].resource library[] @@ -141,20 +60,20 @@ public List getDependencies() { */ // relatedArtifact[].resource - references.addAll(this.getRelatedArtifact().stream() + references.addAll(getRelatedArtifact().stream() .map(ra -> DependencyInfo.convertRelatedArtifact(ra, referenceSource)) .collect(Collectors.toList())); // library[] - List libraries = this.getPlanDefinition().getLibrary(); + List libraries = getPlanDefinition().getLibrary(); for (CanonicalType ct : libraries) { DependencyInfo dependency = new DependencyInfo( referenceSource, ct.getValue(), ct.getExtension(), (reference) -> ct.setValue(reference)); references.add(dependency); } // action[] - this.planDefinition.getAction().forEach(action -> getDependenciesOfAction(action, references, referenceSource)); - this.getPlanDefinition().getExtension().stream() + getPlanDefinition().getAction().forEach(action -> getDependenciesOfAction(action, references, referenceSource)); + getPlanDefinition().getExtension().stream() .filter(ext -> ext.getUrl().contains("cpg-partOf")) .filter(ext -> ext.hasValue()) .findAny() @@ -256,106 +175,4 @@ private void getDependenciesOfAction( } action.getAction().forEach(nestedAction -> getDependenciesOfAction(nestedAction, references, referenceSource)); } - - @Override - public Date getApprovalDate() { - return this.getPlanDefinition().getApprovalDate(); - } - - @Override - public Date getDate() { - return this.getPlanDefinition().getDate(); - } - - @Override - public void setDate(Date date) { - this.getPlanDefinition().setDate(date); - } - - @Override - public void setDateElement(IPrimitiveType date) { - if (date != null && !(date instanceof DateTimeType)) { - throw new UnprocessableEntityException("Date must be " + DateTimeType.class.getName()); - } - this.getPlanDefinition().setDateElement((DateTimeType) date); - } - - @Override - public Period getEffectivePeriod() { - return this.getPlanDefinition().getEffectivePeriod(); - } - - @Override - public void setApprovalDate(Date date) { - this.getPlanDefinition().setApprovalDate(date); - } - - @Override - public boolean hasRelatedArtifact() { - return this.getPlanDefinition().hasRelatedArtifact(); - } - - @SuppressWarnings("unchecked") - @Override - public List getRelatedArtifact() { - return this.getPlanDefinition().getRelatedArtifact(); - } - - @Override - public void setRelatedArtifact(List relatedArtifacts) { - this.getPlanDefinition() - .setRelatedArtifact(relatedArtifacts.stream() - .map(ra -> (RelatedArtifact) ra) - .collect(Collectors.toList())); - } - - @SuppressWarnings("unchecked") - @Override - public List getRelatedArtifactsOfType(String codeString) { - RelatedArtifactType type; - try { - type = RelatedArtifactType.fromCode(codeString); - } catch (FHIRException e) { - throw new UnprocessableEntityException("Invalid related artifact code"); - } - return this.getRelatedArtifact().stream() - .filter(ra -> ra.getType() == type) - .collect(Collectors.toList()); - } - - @Override - public void setEffectivePeriod(ICompositeType effectivePeriod) { - if (effectivePeriod != null && !(effectivePeriod instanceof Period)) { - throw new UnprocessableEntityException("EffectivePeriod must be org.hl7.fhir.r5.model.Period"); - } - this.getPlanDefinition().setEffectivePeriod((Period) effectivePeriod); - } - - @Override - public void setStatus(String statusCodeString) { - PublicationStatus status; - try { - status = PublicationStatus.fromCode(statusCodeString); - } catch (FHIRException e) { - throw new UnprocessableEntityException("Invalid status code"); - } - this.getPlanDefinition().setStatus(status); - } - - @Override - public String getStatus() { - return this.getPlanDefinition().getStatus() == null - ? null - : this.getPlanDefinition().getStatus().toCode(); - } - - @Override - public boolean getExperimental() { - return this.getPlanDefinition().getExperimental(); - } - - @Override - public void setExtension(List> extensions) { - this.get().setExtension(extensions.stream().map(e -> (Extension) e).collect(Collectors.toList())); - } } diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/QuestionnaireAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/QuestionnaireAdapter.java new file mode 100644 index 000000000..e201cff85 --- /dev/null +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/QuestionnaireAdapter.java @@ -0,0 +1,135 @@ +package org.opencds.cqf.fhir.utility.adapter.r5; + +import java.util.ArrayList; +import java.util.List; +import org.hl7.fhir.instance.model.api.IDomainResource; +import org.hl7.fhir.r5.model.CanonicalType; +import org.hl7.fhir.r5.model.Expression; +import org.hl7.fhir.r5.model.Questionnaire; +import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireItemComponent; +import org.opencds.cqf.fhir.utility.Constants; +import org.opencds.cqf.fhir.utility.adapter.DependencyInfo; +import org.opencds.cqf.fhir.utility.adapter.IDependencyInfo; + +public class QuestionnaireAdapter extends KnowledgeArtifactAdapter + implements org.opencds.cqf.fhir.utility.adapter.QuestionnaireAdapter { + + public QuestionnaireAdapter(IDomainResource questionnaire) { + super(questionnaire); + if (!(questionnaire instanceof Questionnaire)) { + throw new IllegalArgumentException( + "resource passed as questionnaire argument is not a Questionnaire resource"); + } + } + + public QuestionnaireAdapter(Questionnaire questionnaire) { + super(questionnaire); + } + + protected Questionnaire getQuestionnaire() { + return (Questionnaire) resource; + } + + @Override + public Questionnaire get() { + return getQuestionnaire(); + } + + @Override + public Questionnaire copy() { + return get().copy(); + } + + @Override + public List getDependencies() { + List references = new ArrayList<>(); + final String referenceSource = getReferenceSource(); + addProfileReferences(references, referenceSource); + + /* + derivedFrom + extension[cqf-library] + extension[launchContext] + extension[variable].reference + item[]..definition // NOTE: This is not a simple canonical, it will have a fragment to identify the specific element + item[]..answerValueSet + item[]..extension[itemMedia] + item[]..extension[itemAnswerMedia] + item[]..extension[unitValueSet] + item[]..extension[referenceProfile] + item[]..extension[candidateExpression].reference + item[]..extension[lookupQuestionnaire] + item[]..extension[variable].reference + item[]..extension[initialExpression].reference + item[]..extension[calculatedExpression].reference + item[]..extension[cqf-calculatedValue].reference + item[]..extension[cqf-expression].reference + item[]..extension[sdc-questionnaire-subQuestionnaire] + */ + + // Not looking at launchContext as it references only base spec profiles and these are included implicitly as + // dependencies per the CRMI IG + + getQuestionnaire() + .getDerivedFrom() + .forEach(derivedRef -> references.add(new DependencyInfo( + referenceSource, + derivedRef.asStringValue(), + derivedRef.getExtension(), + (reference) -> derivedRef.setValue(reference)))); + + getQuestionnaire() + .getExtensionsByUrl(Constants.CQF_LIBRARY) + .forEach(libraryExt -> references.add(new DependencyInfo( + referenceSource, + ((CanonicalType) libraryExt.getValue()).asStringValue(), + libraryExt.getExtension(), + (reference) -> libraryExt.setValue(new CanonicalType(reference))))); + + getQuestionnaire().getExtensionsByUrl(Constants.VARIABLE_EXTENSION).stream() + .map(e -> (Expression) e.getValue()) + .filter(e -> e.hasReference()) + .forEach(expression -> references.add(new DependencyInfo( + referenceSource, + expression.getReference(), + expression.getExtension(), + (reference) -> expression.setReference(reference)))); + + getQuestionnaire().getItem().forEach(item -> getDependenciesOfItem(item, references, referenceSource)); + + return references; + } + + private void getDependenciesOfItem( + QuestionnaireItemComponent item, List references, String referenceSource) { + if (item.hasDefinition()) { + var definition = item.getDefinition().split("#")[0]; + // Not passing an updateReferenceConsumer here because the reference is not a simple canonical + references.add(new DependencyInfo(referenceSource, definition, item.getExtension(), null)); + } + if (item.hasAnswerValueSet()) { + references.add(new DependencyInfo( + referenceSource, + item.getAnswerValueSet(), + item.getExtension(), + (reference) -> item.setAnswerValueSet(reference))); + } + item.getExtension().stream() + .filter(e -> REFERENCE_EXTENSIONS.contains(e.getUrl())) + .forEach(referenceExt -> references.add(new DependencyInfo( + referenceSource, + ((CanonicalType) referenceExt.getValue()).asStringValue(), + referenceExt.getExtension(), + (reference) -> referenceExt.setValue(new CanonicalType(reference))))); + item.getExtension().stream() + .filter(e -> EXPRESSION_EXTENSIONS.contains(e.getUrl())) + .map(e -> (Expression) e.getValue()) + .filter(e -> e.hasReference()) + .forEach(expression -> references.add(new DependencyInfo( + referenceSource, + expression.getReference(), + expression.getExtension(), + (reference) -> expression.setReference(reference)))); + item.getItem().forEach(childItem -> getDependenciesOfItem(childItem, references, referenceSource)); + } +} diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/ResourceAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/ResourceAdapter.java index a7f957a87..b7fbddc2d 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/ResourceAdapter.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/ResourceAdapter.java @@ -1,15 +1,21 @@ package org.opencds.cqf.fhir.utility.adapter.r5; +import static java.util.Optional.ofNullable; + import ca.uhn.fhir.context.FhirVersionEnum; +import java.util.Optional; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r5.model.Base; +import org.hl7.fhir.r5.model.DomainResource; import org.hl7.fhir.r5.model.Resource; +import org.opencds.cqf.fhir.utility.adapter.BaseResourceAdapter; -class ResourceAdapter implements org.opencds.cqf.fhir.utility.adapter.ResourceAdapter { +class ResourceAdapter extends BaseResourceAdapter { public ResourceAdapter(IBaseResource resource) { + super(resource); if (resource == null) { throw new IllegalArgumentException("resource can not be null"); } @@ -17,33 +23,37 @@ public ResourceAdapter(IBaseResource resource) { if (!resource.getStructureFhirVersionEnum().equals(FhirVersionEnum.R5)) { throw new IllegalArgumentException("resource is incorrect fhir version for this adapter"); } + } - this.resource = (Resource) resource; + protected Resource getResource() { + return (Resource) resource; } - private Resource resource; + protected boolean isDomainResource() { + return getDomainResource().isPresent(); + } - protected Resource getResource() { - return this.resource; + protected Optional getDomainResource() { + return ofNullable(resource instanceof DomainResource ? (DomainResource) resource : null); } public IBaseResource get() { - return this.resource; + return resource; } @Override public IBase setProperty(String name, IBase value) throws FHIRException { - return this.getResource().setProperty(name, (Base) value); + return getResource().setProperty(name, (Base) value); } @Override public IBase addChild(String name) throws FHIRException { - return this.getResource().addChild(name); + return getResource().addChild(name); } @Override public IBase getSingleProperty(String name) throws FHIRException { - IBase[] values = this.getProperty(name, true); + IBase[] values = getProperty(name, true); if (values == null || values.length == 0) { return null; @@ -58,41 +68,41 @@ public IBase getSingleProperty(String name) throws FHIRException { @Override public IBase[] getProperty(String name) throws FHIRException { - return this.getProperty(name, true); + return getProperty(name, true); } @Override public IBase[] getProperty(String name, boolean checkValid) throws FHIRException { - return this.getResource().getProperty(name.hashCode(), name, checkValid); + return getResource().getProperty(name.hashCode(), name, checkValid); } @Override public IBase makeProperty(String name) throws FHIRException { - return this.getResource().makeProperty(name.hashCode(), name); + return getResource().makeProperty(name.hashCode(), name); } @Override public String[] getTypesForProperty(String name) throws FHIRException { - return this.getResource().getTypesForProperty(name.hashCode(), name); + return getResource().getTypesForProperty(name.hashCode(), name); } @Override public IBaseResource copy() { - return this.getResource().copy(); + return getResource().copy(); } @Override public void copyValues(IBaseResource dst) { - this.getResource().copyValues((Resource) dst); + getResource().copyValues((Resource) dst); } @Override public boolean equalsDeep(IBase other) { - return this.getResource().equalsDeep((Base) other); + return getResource().equalsDeep((Base) other); } @Override public boolean equalsShallow(IBase other) { - return this.getResource().equalsShallow((Base) other); + return getResource().equalsShallow((Base) other); } } diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/StructureDefinitionAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/StructureDefinitionAdapter.java new file mode 100644 index 000000000..152e4977e --- /dev/null +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/StructureDefinitionAdapter.java @@ -0,0 +1,265 @@ +package org.opencds.cqf.fhir.utility.adapter.r5; + +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.instance.model.api.IBaseHasExtensions; +import org.hl7.fhir.instance.model.api.ICompositeType; +import org.hl7.fhir.instance.model.api.IDomainResource; +import org.hl7.fhir.instance.model.api.IPrimitiveType; +import org.hl7.fhir.r5.model.DateTimeType; +import org.hl7.fhir.r5.model.ElementDefinition; +import org.hl7.fhir.r5.model.Enumerations.PublicationStatus; +import org.hl7.fhir.r5.model.Expression; +import org.hl7.fhir.r5.model.Period; +import org.hl7.fhir.r5.model.RelatedArtifact; +import org.hl7.fhir.r5.model.StructureDefinition; +import org.opencds.cqf.fhir.utility.Constants; +import org.opencds.cqf.fhir.utility.adapter.DependencyInfo; +import org.opencds.cqf.fhir.utility.adapter.IDependencyInfo; +import org.opencds.cqf.fhir.utility.adapter.KnowledgeArtifactAdapter; + +public class StructureDefinitionAdapter extends ResourceAdapter implements KnowledgeArtifactAdapter { + + public StructureDefinitionAdapter(IDomainResource structureDefinition) { + super(structureDefinition); + if (!(structureDefinition instanceof StructureDefinition)) { + throw new IllegalArgumentException( + "resource passed as planDefinition argument is not a StructureDefinition resource"); + } + } + + public StructureDefinitionAdapter(StructureDefinition structureDefinition) { + super(structureDefinition); + } + + protected StructureDefinition getStructureDefinition() { + return (StructureDefinition) resource; + } + + @Override + public List getDependencies() { + List references = new ArrayList<>(); + final String referenceSource = getReferenceSource(); + addProfileReferences(references, referenceSource); + + /* + extension[].url + modifierExtension[].url + baseDefinition + differential.element[].type.code + differential.element[].type.profile[] + differential.element[].type.targetProfile[] + differential.element[].binding.valueSet + differential.element[].extension[].url + differential.element[].modifierExtension[].url + extension[cpg-inferenceExpression].reference + extension[cpg-assertionExpression].reference + extension[cpg-featureExpression].reference + */ + + if (get().hasBaseDefinition()) { + references.add(new DependencyInfo( + referenceSource, + get().getBaseDefinition(), + get().getBaseDefinitionElement().getExtension(), + (reference) -> get().setBaseDefinition(reference))); + } + get().getExtensionsByUrl(Constants.CPG_ASSERTION_EXPRESSION).stream() + .filter(e -> e.getValue() instanceof Expression) + .map(e -> (Expression) e.getValue()) + .filter(e -> e.hasReference()) + .forEach(expression -> references.add(new DependencyInfo( + referenceSource, + expression.getReference(), + expression.getExtension(), + (reference) -> expression.setReference(reference)))); + get().getExtensionsByUrl(Constants.CPG_FEATURE_EXPRESSION).stream() + .filter(e -> e.getValue() instanceof Expression) + .map(e -> (Expression) e.getValue()) + .filter(e -> e.hasReference()) + .forEach(expression -> references.add(new DependencyInfo( + referenceSource, + expression.getReference(), + expression.getExtension(), + (reference) -> expression.setReference(reference)))); + get().getExtensionsByUrl(Constants.CPG_INFERENCE_EXPRESSION).stream() + .filter(e -> e.getValue() instanceof Expression) + .map(e -> (Expression) e.getValue()) + .filter(e -> e.hasReference()) + .forEach(expression -> references.add(new DependencyInfo( + referenceSource, + expression.getReference(), + expression.getExtension(), + (reference) -> expression.setReference(reference)))); + get().getDifferential() + .getElement() + .forEach(element -> getDependenciesOfDifferential(element, references, referenceSource)); + + return references; + } + + private void getDependenciesOfDifferential( + ElementDefinition element, List references, String referenceSource) { + element.getType().forEach(type -> { + type.getProfile() + .forEach(profile -> references.add(new DependencyInfo( + referenceSource, + profile.getValueAsString(), + profile.getExtension(), + (reference) -> profile.setValue(reference)))); + type.getTargetProfile() + .forEach(profile -> references.add(new DependencyInfo( + referenceSource, + profile.getValueAsString(), + profile.getExtension(), + (reference) -> profile.setValue(reference)))); + }); + if (element.getBinding().hasValueSet()) { + references.add(new DependencyInfo( + referenceSource, + element.getBinding().getValueSet(), + element.getBinding().getExtension(), + (reference) -> element.getBinding().setValueSet(reference))); + } + } + + @Override + public StructureDefinition get() { + return getStructureDefinition(); + } + + @Override + public StructureDefinition copy() { + return get().copy(); + } + + @Override + public String getUrl() { + return get().getUrl(); + } + + @Override + public boolean hasUrl() { + return get().hasUrl(); + } + + @Override + public void setUrl(String url) { + get().setUrl(url); + } + + @Override + public void setVersion(String version) { + get().setVersion(version); + } + + @Override + public String getVersion() { + return get().getVersion(); + } + + @Override + public boolean hasVersion() { + return get().hasVersion(); + } + + @Override + public String getName() { + return get().getName(); + } + + @Override + public void setName(String name) { + get().setName(name); + } + + @Override + public Date getApprovalDate() { + return null; + } + + @Override + public Date getDate() { + return get().getDate(); + } + + @Override + public void setDate(Date approvalDate) { + get().setDate(approvalDate); + } + + @Override + public void setDateElement(IPrimitiveType date) { + if (date != null && !(date instanceof DateTimeType)) { + throw new UnprocessableEntityException("Date must be " + DateTimeType.class.getName()); + } + get().setDateElement((DateTimeType) date); + } + + @Override + public void setApprovalDate(Date approvalDate) { + // do nothing + } + + @Override + public Period getEffectivePeriod() { + return null; + } + + @Override + public String getPurpose() { + return get().getPurpose(); + } + + @Override + public void setEffectivePeriod(ICompositeType effectivePeriod) { + // do nothing + } + + @Override + public boolean hasRelatedArtifact() { + return false; + } + + @SuppressWarnings("unchecked") + @Override + public List getRelatedArtifact() { + return new ArrayList<>(); + } + + @SuppressWarnings("unchecked") + @Override + public List getRelatedArtifactsOfType(String codeString) { + return new ArrayList<>(); + } + + @Override + public void setRelatedArtifact(List relatedArtifacts) + throws UnprocessableEntityException { + // do nothing + } + + @Override + public void setStatus(String statusCodeString) { + PublicationStatus status; + try { + status = PublicationStatus.fromCode(statusCodeString); + } catch (FHIRException e) { + throw new UnprocessableEntityException("Invalid status code"); + } + get().setStatus(status); + } + + @Override + public String getStatus() { + return get().getStatus() == null ? null : get().getStatus().toCode(); + } + + @Override + public boolean getExperimental() { + return get().getExperimental(); + } +} diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/ValueSetAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/ValueSetAdapter.java index 06bc4e55a..035362e91 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/ValueSetAdapter.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/ValueSetAdapter.java @@ -1,113 +1,55 @@ package org.opencds.cqf.fhir.utility.adapter.r5; -import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import static org.opencds.cqf.fhir.utility.ValueSets.getCodesInCompose; + +import java.time.Instant; import java.util.ArrayList; +import java.util.Collection; import java.util.Date; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; -import org.hl7.fhir.exceptions.FHIRException; -import org.hl7.fhir.instance.model.api.IBase; -import org.hl7.fhir.instance.model.api.IBaseExtension; -import org.hl7.fhir.instance.model.api.IBaseHasExtensions; -import org.hl7.fhir.instance.model.api.IBaseParameters; -import org.hl7.fhir.instance.model.api.ICompositeType; -import org.hl7.fhir.instance.model.api.IPrimitiveType; -import org.hl7.fhir.r5.model.DateTimeType; -import org.hl7.fhir.r5.model.Enumerations.PublicationStatus; -import org.hl7.fhir.r5.model.Extension; -import org.hl7.fhir.r5.model.Period; -import org.hl7.fhir.r5.model.RelatedArtifact; -import org.hl7.fhir.r5.model.RelatedArtifact.RelatedArtifactType; +import org.hl7.fhir.instance.model.api.IBaseBackboneElement; +import org.hl7.fhir.instance.model.api.IDomainResource; +import org.hl7.fhir.r5.model.BooleanType; import org.hl7.fhir.r5.model.ValueSet; -import org.opencds.cqf.fhir.api.Repository; +import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionComponent; import org.opencds.cqf.fhir.utility.adapter.DependencyInfo; import org.opencds.cqf.fhir.utility.adapter.IDependencyInfo; -import org.opencds.cqf.fhir.utility.visitor.KnowledgeArtifactVisitor; - -class ValueSetAdapter extends ResourceAdapter implements org.opencds.cqf.fhir.utility.adapter.ValueSetAdapter { - private ValueSet valueSet; +public class ValueSetAdapter extends KnowledgeArtifactAdapter + implements org.opencds.cqf.fhir.utility.adapter.ValueSetAdapter { - public ValueSetAdapter(ValueSet valueSet) { + public ValueSetAdapter(IDomainResource valueSet) { super(valueSet); - if (!(valueSet instanceof ValueSet)) { throw new IllegalArgumentException("resource passed as valueSet argument is not a ValueSet resource"); } - - this.valueSet = valueSet; } - public IBase accept(KnowledgeArtifactVisitor visitor, Repository repository, IBaseParameters operationParameters) { - return visitor.visit(this, repository, operationParameters); + public ValueSetAdapter(ValueSet valueSet) { + super(valueSet); } protected ValueSet getValueSet() { - return this.valueSet; + return (ValueSet) resource; } @Override public ValueSet get() { - return this.valueSet; + return (ValueSet) resource; } @Override public ValueSet copy() { - return this.get().copy(); - } - - @Override - public String getName() { - return this.getValueSet().getName(); - } - - @Override - public String getPurpose() { - return this.getValueSet().getPurpose(); - } - - @Override - public void setName(String name) { - this.getValueSet().setName(name); - } - - @Override - public boolean hasUrl() { - return this.getValueSet().hasUrl(); - } - - @Override - public String getUrl() { - return this.getValueSet().getUrl(); - } - - @Override - public void setUrl(String url) { - this.getValueSet().setUrl(url); - } - - @Override - public String getVersion() { - return this.getValueSet().getVersion(); - } - - @Override - public boolean hasVersion() { - return this.getValueSet().hasVersion(); - } - - @Override - public void setVersion(String version) { - this.getValueSet().setVersion(version); + return get().copy(); } @Override public List getDependencies() { List references = new ArrayList<>(); - final String referenceSource = this.getValueSet().hasVersion() - ? this.getValueSet().getUrl() + "|" + this.getValueSet().getVersion() - : this.getValueSet().getUrl(); + final String referenceSource = getReferenceSource(); + addProfileReferences(references, referenceSource); /* compose.include[].valueSet @@ -116,8 +58,8 @@ public List getDependencies() { compose.exclude[].system */ Stream.concat( - this.getValueSet().getCompose().getInclude().stream(), - this.getValueSet().getCompose().getExclude().stream()) + getValueSet().getCompose().getInclude().stream(), + getValueSet().getCompose().getExclude().stream()) .forEach(component -> { if (component.hasValueSet()) { component.getValueSet().forEach(ct -> { @@ -142,110 +84,77 @@ public List getDependencies() { } @Override - public Date getApprovalDate() { - return this.getValueSet().getApprovalDate(); - } - - @Override - public Date getDate() { - return this.getValueSet().getDate(); - } - - @Override - public void setDate(Date date) { - this.getValueSet().setDate(date); - } - - @Override - public void setDateElement(IPrimitiveType date) throws UnprocessableEntityException { - if (date != null && !(date instanceof DateTimeType)) { - throw new UnprocessableEntityException("Date must be " + DateTimeType.class.getName()); - } - this.getValueSet().setDateElement((DateTimeType) date); - } - - @Override - public Period getEffectivePeriod() { - return this.getValueSet().getEffectivePeriod(); - } - - @Override - public boolean hasRelatedArtifact() { - return this.getValueSet().hasRelatedArtifact(); + public void setExpansion(T expansion) { + getValueSet().setExpansion((ValueSetExpansionComponent) expansion); } @SuppressWarnings("unchecked") @Override - public List getRelatedArtifact() { - return this.getValueSet().getRelatedArtifact(); - } - - @Override - public void setRelatedArtifact(List relatedArtifacts) { - this.getValueSet() - .setRelatedArtifact(relatedArtifacts.stream() - .map(ra -> (RelatedArtifact) ra) - .collect(Collectors.toList())); + public ValueSetExpansionComponent getExpansion() { + return getValueSet().getExpansion(); } @SuppressWarnings("unchecked") @Override - public List getComponents() { - return this.getRelatedArtifactsOfType("composed-of"); + public ValueSetExpansionComponent newExpansion() { + var expansion = new ValueSet.ValueSetExpansionComponent(Date.from(Instant.now())); + expansion.getContains(); + return expansion; } @Override - public void setApprovalDate(Date approvalDate) { - this.getValueSet().setApprovalDate(approvalDate); + public List getValueSetIncludes() { + return getValueSet().getCompose().getInclude().stream() + .map(i -> i.getValueSet()) + .flatMap(Collection::stream) + .map(c -> c.asStringValue()) + .distinct() + .collect(Collectors.toList()); } @Override - public void setEffectivePeriod(ICompositeType effectivePeriod) { - if (effectivePeriod != null && !(effectivePeriod instanceof Period)) { - throw new UnprocessableEntityException("EffectivePeriod must be org.hl7.fhir.r5.model.Period"); - } - this.getValueSet().setEffectivePeriod((Period) effectivePeriod); + public boolean hasSimpleCompose() { + return getValueSet().hasCompose() + && !getValueSet().getCompose().hasExclude() + && getValueSet().getCompose().getInclude().stream() + .noneMatch( + csc -> csc.hasFilter() || csc.hasValueSet() || !csc.hasSystem() || !csc.hasConcept()); } - @SuppressWarnings("unchecked") @Override - public List getRelatedArtifactsOfType(String codeString) { - RelatedArtifactType type; - try { - type = RelatedArtifactType.fromCode(codeString); - } catch (FHIRException e) { - throw new UnprocessableEntityException("Invalid related artifact code"); - } - return this.getRelatedArtifact().stream() - .filter(ra -> ra.getType() == type) - .collect(Collectors.toList()); + public boolean hasGroupingCompose() { + return getValueSet().hasCompose() + && !getValueSet().getCompose().hasExclude() + && getValueSet().getCompose().getInclude().stream() + .noneMatch(csc -> !csc.hasValueSet() || csc.hasFilter()); } @Override - public void setStatus(String statusCodeString) { - PublicationStatus status; - try { - status = PublicationStatus.fromCode(statusCodeString); - } catch (FHIRException e) { - throw new UnprocessableEntityException("Invalid status code"); - } - this.getValueSet().setStatus(status); + public boolean hasNaiveParameter() { + return getValueSet().getExpansion().getParameter().stream() + .anyMatch(p -> p.getName().equals("naive")); } + @SuppressWarnings("unchecked") @Override - public String getStatus() { - return this.getValueSet().getStatus() == null - ? null - : this.getValueSet().getStatus().toCode(); + public ValueSet.ValueSetExpansionParameterComponent createNaiveParameter() { + return new ValueSet.ValueSetExpansionParameterComponent() + .setName("naive") + .setValue(new BooleanType(true)); } @Override - public boolean getExperimental() { - return this.getValueSet().getExperimental(); - } + public void naiveExpand() { + var expansion = newExpansion().addParameter(createNaiveParameter()); - @Override - public void setExtension(List> extensions) { - this.get().setExtension(extensions.stream().map(e -> (Extension) e).collect(Collectors.toList())); + for (var code : getCodesInCompose(fhirContext, getValueSet())) { + expansion + .addContains() + .setCode(code.getCode()) + .setSystem(code.getSystem()) + .setVersion(code.getVersion()) + .setDisplay(code.getDisplay()); + } + getValueSet().setExpansion(expansion); } } diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/client/TerminologyServerClient.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/client/TerminologyServerClient.java index ea1a70b9c..f001a4d52 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/client/TerminologyServerClient.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/client/TerminologyServerClient.java @@ -1,148 +1,145 @@ package org.opencds.cqf.fhir.utility.client; +import static com.google.common.base.Preconditions.checkNotNull; + import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import java.util.Objects; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.instance.model.api.IBaseEnumFactory; +import org.hl7.fhir.instance.model.api.IBaseParameters; +import org.hl7.fhir.instance.model.api.IBaseResource; import org.opencds.cqf.fhir.utility.Canonicals; +import org.opencds.cqf.fhir.utility.Constants; +import org.opencds.cqf.fhir.utility.Parameters; +import org.opencds.cqf.fhir.utility.adapter.EndpointAdapter; +import org.opencds.cqf.fhir.utility.adapter.ParametersAdapter; +import org.opencds.cqf.fhir.utility.adapter.ValueSetAdapter; +/** + * This class currently serves as a VSAC Terminology Server client as it expects the Endpoint provided to contain a VSAC username and api key. + * Future enhancements include adding support for multiple endpoints + */ public class TerminologyServerClient { private final FhirContext ctx; + public static final String versionParamName = "valueSetVersion"; + public static final String urlParamName = "url"; public TerminologyServerClient(FhirContext ctx) { this.ctx = ctx; } - public org.hl7.fhir.dstu3.model.ValueSet expand( - org.hl7.fhir.dstu3.model.ValueSet valueSet, - String authoritativeSource, - org.hl7.fhir.dstu3.model.Parameters expansionParameters, - String username, - String apiKey) { - IGenericClient fhirClient = ctx.newRestfulGenericClient(getAuthoritativeSourceBase(authoritativeSource)); - Clients.registerAdditionalRequestHeadersAuth(fhirClient, username, apiKey); - if (expansionParameters == null) { - expansionParameters = new org.hl7.fhir.dstu3.model.Parameters(); - } - if (!expansionParameters.getParameter().stream() - .anyMatch(p -> p.getName().equals("url"))) { - expansionParameters - .addParameter() - .setName("url") - .setValue(new org.hl7.fhir.dstu3.model.UriType(valueSet.getUrl())); - } - if (valueSet.hasVersion() - && !expansionParameters.getParameter().stream() - .anyMatch(p -> p.getName().equals("valueSetVersion"))) { - expansionParameters - .addParameter() - .setName("valueSetVersion") - .setValue(new org.hl7.fhir.dstu3.model.StringType(valueSet.getVersion())); - } - // Invoke on the type using the url parameter - return fhirClient - .operation() - .onType("ValueSet") - .named("$expand") - .withParameters(expansionParameters) - .returnResourceType(org.hl7.fhir.dstu3.model.ValueSet.class) - .execute(); + public IBaseResource expand(ValueSetAdapter valueSet, EndpointAdapter endpoint, ParametersAdapter parameters) { + checkNotNull(valueSet, "expected non-null value for valueSet"); + checkNotNull(endpoint, "expected non-null value for endpoint"); + checkNotNull(parameters, "expected non-null value for parameters"); + return expand( + endpoint, + parameters, + valueSet.getUrl(), + valueSet.getVersion(), + valueSet.get().getStructureFhirVersionEnum()); } - public org.hl7.fhir.r4.model.ValueSet expand( - org.hl7.fhir.r4.model.ValueSet valueSet, - String authoritativeSource, - org.hl7.fhir.r4.model.Parameters expansionParameters, - String username, - String apiKey) { - IGenericClient fhirClient = ctx.newRestfulGenericClient(getAuthoritativeSourceBase(authoritativeSource)); - Clients.registerAdditionalRequestHeadersAuth(fhirClient, username, apiKey); - if (expansionParameters == null) { - expansionParameters = new org.hl7.fhir.r4.model.Parameters(); - } - if (!expansionParameters.hasParameter("url")) { - expansionParameters.addParameter("url", new org.hl7.fhir.r4.model.UriType(valueSet.getUrl())); - } - if (valueSet.hasVersion() && !expansionParameters.hasParameter("valueSetVersion")) { - expansionParameters.addParameter( - "valueSetVersion", new org.hl7.fhir.r4.model.StringType(valueSet.getVersion())); - } - // Invoke on the type using the url parameter - return fhirClient - .operation() - .onType("ValueSet") - .named("$expand") - .withParameters(expansionParameters) - .returnResourceType(org.hl7.fhir.r4.model.ValueSet.class) - .execute(); + public IBaseResource expand(EndpointAdapter endpoint, ParametersAdapter parameters, FhirVersionEnum fhirVersion) { + checkNotNull(endpoint, "expected non-null value for endpoint"); + checkNotNull(parameters, "expected non-null value for parameters"); + checkNotNull(fhirVersion, "expected non-null value for fhirVersion"); + checkNotNull(parameters.getParameter(urlParamName), "expected non-null value for 'url' expansion parameter"); + return expand(endpoint, parameters, null, null, fhirVersion); } - public org.hl7.fhir.r5.model.ValueSet expand( - org.hl7.fhir.r5.model.ValueSet valueSet, - String authoritativeSource, - org.hl7.fhir.r5.model.Parameters expansionParameters, - String username, - String apiKey) { - IGenericClient fhirClient = ctx.newRestfulGenericClient(getAuthoritativeSourceBase(authoritativeSource)); + public IBaseResource expand( + EndpointAdapter endpoint, + ParametersAdapter parameters, + String url, + String valueSetVersion, + FhirVersionEnum fhirVersion) { + checkNotNull(endpoint, "expected non-null value for endpoint"); + checkNotNull(parameters, "expected non-null value for parameters"); + var username = endpoint.getExtensionsByUrl(Constants.VSAC_USERNAME).stream() + .findFirst() + .map(ext -> ext.getValue().toString()) + .orElseThrow(() -> new UnprocessableEntityException("Cannot expand ValueSet without VSAC Username.")); + var apiKey = endpoint.getExtensionsByUrl(Constants.APIKEY).stream() + .findFirst() + .map(ext -> ext.getValue().toString()) + .orElseThrow(() -> new UnprocessableEntityException("Cannot expand ValueSet without VSAC API Key.")); + IGenericClient fhirClient = ctx.newRestfulGenericClient(getAddressBase(endpoint.getAddress())); Clients.registerAdditionalRequestHeadersAuth(fhirClient, username, apiKey); - if (expansionParameters == null) { - expansionParameters = new org.hl7.fhir.r5.model.Parameters(); - } - if (!expansionParameters.hasParameter("url")) { - expansionParameters.addParameter("url", new org.hl7.fhir.r5.model.UriType(valueSet.getUrl())); + if (parameters.getParameter(urlParamName) == null) { + if (url == null) { + throw new UnprocessableEntityException("No '" + urlParamName + "' expansion parameter present"); + } + parameters.addParameter( + fhirVersion == FhirVersionEnum.DSTU3 + ? Parameters.newUriPart(ctx, urlParamName, url) + : Parameters.newUrlPart(ctx, urlParamName, url)); } - if (valueSet.hasVersion() && !expansionParameters.hasParameter("valueSetVersion")) { - expansionParameters.addParameter( - "valueSetVersion", new org.hl7.fhir.r5.model.StringType(valueSet.getVersion())); + if (parameters.getParameter(versionParamName) == null && valueSetVersion != null) { + parameters.addParameter(Parameters.newStringPart(ctx, versionParamName, valueSetVersion)); } // Invoke on the type using the url parameter return fhirClient .operation() .onType("ValueSet") .named("$expand") - .withParameters(expansionParameters) - .returnResourceType(org.hl7.fhir.r5.model.ValueSet.class) + .withParameters((IBaseParameters) parameters.get()) + .returnResourceType(getValueSetClass(fhirVersion)) .execute(); } - // Strips resource and id from the authoritative source URL, these are not needed as the client constructs the URL. + private Class getValueSetClass(FhirVersionEnum fhirVersion) { + switch (fhirVersion) { + case DSTU3: + return org.hl7.fhir.dstu3.model.ValueSet.class; + case R4: + return org.hl7.fhir.r4.model.ValueSet.class; + case R5: + return org.hl7.fhir.r5.model.ValueSet.class; + default: + throw new UnprocessableEntityException("Unknown ValueSet version"); + } + } + + // Strips resource and id from the endpoint address URL, these are not needed as the client constructs the URL. // Converts http URLs to https - public String getAuthoritativeSourceBase(String authoritativeSource) { - Objects.requireNonNull(authoritativeSource, "authoritativeSource must not be null"); - if (authoritativeSource.startsWith("http://")) { - authoritativeSource = authoritativeSource.replaceFirst("http://", "https://"); + public String getAddressBase(String address) { + Objects.requireNonNull(address, "address must not be null"); + if (address.startsWith("http://")) { + address = address.replaceFirst("http://", "https://"); } // remove trailing slashes - if (authoritativeSource.endsWith("/")) { - authoritativeSource = authoritativeSource.substring(0, authoritativeSource.length() - 1); + if (address.endsWith("/")) { + address = address.substring(0, address.length() - 1); } // check if URL is in the format [base URL]/[resource type]/[id] - var maybeFhirType = Canonicals.getResourceType(authoritativeSource); + var maybeFhirType = Canonicals.getResourceType(address); if (maybeFhirType != null && StringUtils.isNotBlank(maybeFhirType)) { IBaseEnumFactory factory = getEnumFactory(); try { factory.fromCode(maybeFhirType); } catch (IllegalArgumentException e) { // check if URL is in the format [base URL]/[resource type] - var lastSlashIndex = authoritativeSource.lastIndexOf("/"); + var lastSlashIndex = address.lastIndexOf("/"); if (lastSlashIndex > 0) { - maybeFhirType = authoritativeSource.substring(lastSlashIndex + 1, authoritativeSource.length()); + maybeFhirType = address.substring(lastSlashIndex + 1, address.length()); try { factory.fromCode(maybeFhirType); } catch (IllegalArgumentException e2) { - return authoritativeSource; + return address; } } else { - return authoritativeSource; + return address; } } - authoritativeSource = authoritativeSource.substring(0, authoritativeSource.indexOf(maybeFhirType) - 1); + address = address.substring(0, address.indexOf(maybeFhirType) - 1); } - return authoritativeSource; + return address; } private IBaseEnumFactory getEnumFactory() { diff --git a/cqf-fhir-cql/src/main/java/org/opencds/cqf/fhir/cql/engine/model/DynamicModelResolver.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/model/DynamicModelResolver.java similarity index 99% rename from cqf-fhir-cql/src/main/java/org/opencds/cqf/fhir/cql/engine/model/DynamicModelResolver.java rename to cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/model/DynamicModelResolver.java index 5fc3c3cc3..20277030f 100644 --- a/cqf-fhir-cql/src/main/java/org/opencds/cqf/fhir/cql/engine/model/DynamicModelResolver.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/model/DynamicModelResolver.java @@ -1,4 +1,4 @@ -package org.opencds.cqf.fhir.cql.engine.model; +package org.opencds.cqf.fhir.utility.model; import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition; import ca.uhn.fhir.context.FhirContext; diff --git a/cqf-fhir-cql/src/main/java/org/opencds/cqf/fhir/cql/engine/model/FhirModelResolverCache.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/model/FhirModelResolverCache.java similarity index 97% rename from cqf-fhir-cql/src/main/java/org/opencds/cqf/fhir/cql/engine/model/FhirModelResolverCache.java rename to cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/model/FhirModelResolverCache.java index eb2146937..66cd41590 100644 --- a/cqf-fhir-cql/src/main/java/org/opencds/cqf/fhir/cql/engine/model/FhirModelResolverCache.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/model/FhirModelResolverCache.java @@ -1,4 +1,4 @@ -package org.opencds.cqf.fhir.cql.engine.model; +package org.opencds.cqf.fhir.utility.model; import static java.util.Objects.requireNonNull; diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/KnowledgeArtifactApproveVisitor.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/ApproveVisitor.java similarity index 69% rename from cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/KnowledgeArtifactApproveVisitor.java rename to cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/ApproveVisitor.java index ad4eebfd2..1851ba156 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/KnowledgeArtifactApproveVisitor.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/ApproveVisitor.java @@ -2,7 +2,6 @@ import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.model.api.TemporalPrecisionEnum; -import ca.uhn.fhir.rest.server.exceptions.NotImplementedOperationException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import java.util.Date; import java.util.Optional; @@ -17,15 +16,12 @@ import org.opencds.cqf.fhir.utility.Canonicals; import org.opencds.cqf.fhir.utility.PackageHelper; import org.opencds.cqf.fhir.utility.adapter.KnowledgeArtifactAdapter; -import org.opencds.cqf.fhir.utility.adapter.LibraryAdapter; -import org.opencds.cqf.fhir.utility.adapter.PlanDefinitionAdapter; -import org.opencds.cqf.fhir.utility.adapter.ValueSetAdapter; -public class KnowledgeArtifactApproveVisitor implements KnowledgeArtifactVisitor { +public class ApproveVisitor implements KnowledgeArtifactVisitor { @Override - public IBaseResource visit(LibraryAdapter library, Repository repository, IBaseParameters approveParameters) { + public IBase visit(KnowledgeArtifactAdapter adapter, Repository repository, IBaseParameters approveParameters) { Date currentDate = new Date(); - var fhirVersion = library.get().getStructureFhirVersionEnum(); + var fhirVersion = adapter.get().getStructureFhirVersionEnum(); Date approvalDate = VisitorHelper.getParameter("approvalDate", approveParameters, IPrimitiveType.class) .map(d -> (Date) d.getValue()) .orElse(currentDate); @@ -40,12 +36,12 @@ public IBaseResource visit(LibraryAdapter library, Repository repository, IBaseP "artifactAssessmentTarget", approveParameters, IPrimitiveType.class) .map(t -> (String) t.getValue()); if (artifactAssessmentTarget.isPresent()) { - if (!Canonicals.getUrl(artifactAssessmentTarget.get()).equals(library.getUrl())) { + if (!Canonicals.getUrl(artifactAssessmentTarget.get()).equals(adapter.getUrl())) { throw new UnprocessableEntityException( "ArtifactCommentTarget URL does not match URL of resource being approved."); } - if (library.hasVersion()) { - if (!Canonicals.getVersion(artifactAssessmentTarget.get()).equals(library.getVersion())) { + if (adapter.hasVersion()) { + if (!Canonicals.getVersion(artifactAssessmentTarget.get()).equals(adapter.getVersion())) { throw new UnprocessableEntityException( "ArtifactCommentTarget version does not match version of resource being approved."); } @@ -59,18 +55,18 @@ public IBaseResource visit(LibraryAdapter library, Repository repository, IBaseP var returnBundle = BundleHelper.newBundle(fhirVersion, null, "transaction"); var assessment = createApprovalAssessment( - library.getId(), + adapter.getId(), artifactAssessmentType, artifactAssessmentSummary, artifactAssessmentTarget, artifactAssessmentRelatedArtifact, artifactAssessmentAuthor, - library.get().getIdElement(), + adapter.get().getIdElement(), fhirVersion); - library.setApprovalDate(approvalDate); - setDateElement(library, currentDate, fhirVersion); + adapter.setApprovalDate(approvalDate); + setDateElement(adapter, currentDate, fhirVersion); BundleHelper.addEntry(returnBundle, PackageHelper.createEntry(assessment, false)); - BundleHelper.addEntry(returnBundle, PackageHelper.createEntry(library.get(), true)); + BundleHelper.addEntry(returnBundle, PackageHelper.createEntry(adapter.get(), true)); return repository.transaction(returnBundle); } @@ -86,17 +82,16 @@ private IBaseResource createApprovalAssessment( throws UnprocessableEntityException { switch (fhirVersion) { case DSTU3: - return org.opencds.cqf.fhir.utility.visitor.dstu3.KnowledgeArtifactApproveVisitor - .createApprovalAssessment( - id, - new org.hl7.fhir.dstu3.model.CodeType(artifactAssessmentType), - artifactAssessmentSummary.map(t -> new org.hl7.fhir.dstu3.model.MarkdownType(t)), - artifactAssessmentTargetCanonical.map(t -> new org.hl7.fhir.dstu3.model.UriType(t)), - artifactAssessmentRelatedArtifact.map(t -> new org.hl7.fhir.dstu3.model.UriType(t)), - artifactAssessmentAuthor.map(t -> (org.hl7.fhir.dstu3.model.Reference) t), - new org.hl7.fhir.dstu3.model.Reference(artifactTargetReference)); + return org.opencds.cqf.fhir.utility.visitor.dstu3.ApproveVisitor.createApprovalAssessment( + id, + new org.hl7.fhir.dstu3.model.CodeType(artifactAssessmentType), + artifactAssessmentSummary.map(t -> new org.hl7.fhir.dstu3.model.MarkdownType(t)), + artifactAssessmentTargetCanonical.map(t -> new org.hl7.fhir.dstu3.model.UriType(t)), + artifactAssessmentRelatedArtifact.map(t -> new org.hl7.fhir.dstu3.model.UriType(t)), + artifactAssessmentAuthor.map(t -> (org.hl7.fhir.dstu3.model.Reference) t), + new org.hl7.fhir.dstu3.model.Reference(artifactTargetReference)); case R4: - return org.opencds.cqf.fhir.utility.visitor.r4.KnowledgeArtifactApproveVisitor.createApprovalAssessment( + return org.opencds.cqf.fhir.utility.visitor.r4.ApproveVisitor.createApprovalAssessment( id, new org.hl7.fhir.r4.model.CodeType(artifactAssessmentType), artifactAssessmentSummary.map(t -> new org.hl7.fhir.r4.model.MarkdownType(t)), @@ -105,7 +100,7 @@ private IBaseResource createApprovalAssessment( artifactAssessmentAuthor.map(t -> (org.hl7.fhir.r4.model.Reference) t), new org.hl7.fhir.r4.model.Reference(artifactTargetReference)); case R5: - return org.opencds.cqf.fhir.utility.visitor.r5.KnowledgeArtifactApproveVisitor.createApprovalAssessment( + return org.opencds.cqf.fhir.utility.visitor.r5.ApproveVisitor.createApprovalAssessment( id, artifactAssessmentType, artifactAssessmentSummary.map(t -> new org.hl7.fhir.r5.model.MarkdownType(t)), @@ -122,17 +117,17 @@ private IBaseResource createApprovalAssessment( } } - private void setDateElement(LibraryAdapter library, Date currentDate, FhirVersionEnum fhirVersion) { + private void setDateElement(KnowledgeArtifactAdapter adapter, Date currentDate, FhirVersionEnum fhirVersion) { switch (fhirVersion) { case DSTU3: - library.setDateElement( + adapter.setDateElement( new org.hl7.fhir.dstu3.model.DateTimeType(currentDate, TemporalPrecisionEnum.DAY)); break; case R4: - library.setDateElement(new org.hl7.fhir.r4.model.DateTimeType(currentDate, TemporalPrecisionEnum.DAY)); + adapter.setDateElement(new org.hl7.fhir.r4.model.DateTimeType(currentDate, TemporalPrecisionEnum.DAY)); break; case R5: - library.setDateElement(new org.hl7.fhir.r5.model.DateTimeType(currentDate, TemporalPrecisionEnum.DAY)); + adapter.setDateElement(new org.hl7.fhir.r5.model.DateTimeType(currentDate, TemporalPrecisionEnum.DAY)); break; case DSTU2: case DSTU2_1: @@ -142,20 +137,4 @@ private void setDateElement(LibraryAdapter library, Date currentDate, FhirVersio String.format("Unsupported version of FHIR: %s", fhirVersion.getFhirVersionString())); } } - - @Override - public IBase visit(KnowledgeArtifactAdapter library, Repository repository, IBaseParameters draftParameters) { - throw new NotImplementedOperationException("Not implemented"); - } - - @Override - public IBase visit( - PlanDefinitionAdapter planDefinition, Repository repository, IBaseParameters operationParameters) { - throw new NotImplementedOperationException("Not implemented"); - } - - @Override - public IBase visit(ValueSetAdapter valueSet, Repository repository, IBaseParameters operationParameters) { - throw new NotImplementedOperationException("Not implemented"); - } } diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/KnowledgeArtifactDraftVisitor.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/DraftVisitor.java similarity index 75% rename from cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/KnowledgeArtifactDraftVisitor.java rename to cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/DraftVisitor.java index fdd310df9..c38df830a 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/KnowledgeArtifactDraftVisitor.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/DraftVisitor.java @@ -1,7 +1,6 @@ package org.opencds.cqf.fhir.utility.visitor; import ca.uhn.fhir.context.FhirVersionEnum; -import ca.uhn.fhir.rest.server.exceptions.NotImplementedOperationException; import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import java.util.ArrayList; @@ -12,7 +11,6 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; import org.hl7.fhir.instance.model.api.IBase; -import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IBaseExtension; import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.ICompositeType; @@ -25,19 +23,16 @@ import org.opencds.cqf.fhir.utility.adapter.AdapterFactory; import org.opencds.cqf.fhir.utility.adapter.IDependencyInfo; import org.opencds.cqf.fhir.utility.adapter.KnowledgeArtifactAdapter; -import org.opencds.cqf.fhir.utility.adapter.LibraryAdapter; -import org.opencds.cqf.fhir.utility.adapter.PlanDefinitionAdapter; -import org.opencds.cqf.fhir.utility.adapter.ValueSetAdapter; import org.opencds.cqf.fhir.utility.r4.PackageHelper; -public class KnowledgeArtifactDraftVisitor implements KnowledgeArtifactVisitor { +public class DraftVisitor implements KnowledgeArtifactVisitor { @Override - public IBaseBundle visit(LibraryAdapter library, Repository repository, IBaseParameters draftParameters) { - var fhirVersion = library.get().getStructureFhirVersionEnum(); + public IBase visit(KnowledgeArtifactAdapter adapter, Repository repository, IBaseParameters draftParameters) { + var fhirVersion = adapter.get().getStructureFhirVersionEnum(); String version = VisitorHelper.getParameter("version", draftParameters, IPrimitiveType.class) .map(r -> (String) r.getValue()) .orElseThrow(() -> new UnprocessableEntityException("The version argument is required")); - var libRes = library.get(); + var libRes = adapter.get(); // check valid semverversion checkVersionValidSemver(version); @@ -46,28 +41,28 @@ public IBaseBundle visit(LibraryAdapter library, Repository repository, IBasePar .filter(ext -> !ext.getUrl().equals(KnowledgeArtifactAdapter.releaseDescriptionUrl) && !ext.getUrl().equals(KnowledgeArtifactAdapter.releaseLabelUrl)) .collect(Collectors.toList()); - library.setExtension(removeReleaseLabelAndDescription); + adapter.setExtension(removeReleaseLabelAndDescription); // remove approval date - library.setApprovalDate(null); + adapter.setApprovalDate(null); // new draft version String draftVersion = version + "-draft"; - String draftVersionUrl = Canonicals.getUrl(library.getUrl()) + "|" + draftVersion; + String draftVersionUrl = Canonicals.getUrl(adapter.getUrl()) + "|" + draftVersion; // Root artifact must NOT have status of 'Active'. Existing drafts of // reference artifacts with the right verison number will be adopted. // This check is performed here to facilitate that different treatment // for the root artifact and those referenced by it. - if (library.getStatus() != "active") { + if (adapter.getStatus() != "active") { throw new PreconditionFailedException(String.format( "Drafts can only be created from artifacts with status of 'active'. Resource '%s' has a status of: '%s'", - library.getUrl(), library.getStatus())); + adapter.getUrl(), adapter.getStatus())); } // Ensure only one resource exists with this URL var existingArtifactsForUrl = SearchHelper.searchRepositoryByCanonicalWithPaging(repository, draftVersionUrl); if (BundleHelper.getEntry(existingArtifactsForUrl).size() != 0) { throw new PreconditionFailedException(String.format( "A draft of Program '%s' already exists with version: '%s'. Only one draft of a program version can exist at a time.", - library.getUrl(), draftVersionUrl)); + adapter.getUrl(), draftVersionUrl)); } // create draft resources List resourcesToCreate = @@ -94,22 +89,6 @@ public IBaseBundle visit(LibraryAdapter library, Repository repository, IBasePar // what is dependency, where did it originate? potentially the package? } - @Override - public IBase visit(KnowledgeArtifactAdapter library, Repository repository, IBaseParameters draftParameters) { - throw new NotImplementedOperationException("Not implemented"); - } - - @Override - public IBase visit( - PlanDefinitionAdapter planDefinition, Repository repository, IBaseParameters operationParameters) { - throw new NotImplementedOperationException("Not implemented"); - } - - @Override - public IBase visit(ValueSetAdapter valueSet, Repository repository, IBaseParameters operationParameters) { - throw new NotImplementedOperationException("Not implemented"); - } - private List createDraftsOfArtifactAndRelated( IDomainResource resource, Repository repository, @@ -177,22 +156,19 @@ private void processReferencedResourceForDraft( switch (fhirVersion) { case DSTU3: referencedResource = - org.opencds.cqf.fhir.utility.visitor.dstu3.KnowledgeArtifactDraftVisitor - .processReferencedResourceForDraft( + org.opencds.cqf.fhir.utility.visitor.dstu3.DraftVisitor.processReferencedResourceForDraft( repository, (org.hl7.fhir.dstu3.model.RelatedArtifact) ra, version) .map(r -> (IDomainResource) r); break; case R4: referencedResource = - org.opencds.cqf.fhir.utility.visitor.r4.KnowledgeArtifactDraftVisitor - .processReferencedResourceForDraft( + org.opencds.cqf.fhir.utility.visitor.r4.DraftVisitor.processReferencedResourceForDraft( repository, (org.hl7.fhir.r4.model.RelatedArtifact) ra, version) .map(r -> (IDomainResource) r); break; case R5: referencedResource = - org.opencds.cqf.fhir.utility.visitor.r5.KnowledgeArtifactDraftVisitor - .processReferencedResourceForDraft( + org.opencds.cqf.fhir.utility.visitor.r5.DraftVisitor.processReferencedResourceForDraft( repository, (org.hl7.fhir.r5.model.RelatedArtifact) ra, version) .map(r -> (IDomainResource) r); break; @@ -245,37 +221,34 @@ private void updateUsageContextReferencesWithUrns( FhirVersionEnum fhirVersion) { switch (fhirVersion) { case DSTU3: - org.opencds.cqf.fhir.utility.visitor.dstu3.KnowledgeArtifactDraftVisitor - .updateUsageContextReferencesWithUrns( - (org.hl7.fhir.dstu3.model.MetadataResource) newResource, - resourceListWithOriginalIds.stream() - .map(ra -> (org.hl7.fhir.dstu3.model.MetadataResource) ra) - .collect(Collectors.toList()), - idListForTransactionBundle.stream() - .map(id -> new org.hl7.fhir.dstu3.model.IdType(id)) - .collect(Collectors.toList())); + org.opencds.cqf.fhir.utility.visitor.dstu3.DraftVisitor.updateUsageContextReferencesWithUrns( + (org.hl7.fhir.dstu3.model.MetadataResource) newResource, + resourceListWithOriginalIds.stream() + .map(ra -> (org.hl7.fhir.dstu3.model.MetadataResource) ra) + .collect(Collectors.toList()), + idListForTransactionBundle.stream() + .map(id -> new org.hl7.fhir.dstu3.model.IdType(id)) + .collect(Collectors.toList())); break; case R4: - org.opencds.cqf.fhir.utility.visitor.r4.KnowledgeArtifactDraftVisitor - .updateUsageContextReferencesWithUrns( - (org.hl7.fhir.r4.model.MetadataResource) newResource, - resourceListWithOriginalIds.stream() - .map(ra -> (org.hl7.fhir.r4.model.MetadataResource) ra) - .collect(Collectors.toList()), - idListForTransactionBundle.stream() - .map(id -> new org.hl7.fhir.r4.model.IdType(id)) - .collect(Collectors.toList())); + org.opencds.cqf.fhir.utility.visitor.r4.DraftVisitor.updateUsageContextReferencesWithUrns( + (org.hl7.fhir.r4.model.MetadataResource) newResource, + resourceListWithOriginalIds.stream() + .map(ra -> (org.hl7.fhir.r4.model.MetadataResource) ra) + .collect(Collectors.toList()), + idListForTransactionBundle.stream() + .map(id -> new org.hl7.fhir.r4.model.IdType(id)) + .collect(Collectors.toList())); break; case R5: - org.opencds.cqf.fhir.utility.visitor.r5.KnowledgeArtifactDraftVisitor - .updateUsageContextReferencesWithUrns( - (org.hl7.fhir.r5.model.MetadataResource) newResource, - resourceListWithOriginalIds.stream() - .map(ra -> (org.hl7.fhir.r5.model.MetadataResource) ra) - .collect(Collectors.toList()), - idListForTransactionBundle.stream() - .map(id -> new org.hl7.fhir.r5.model.IdType(id)) - .collect(Collectors.toList())); + org.opencds.cqf.fhir.utility.visitor.r5.DraftVisitor.updateUsageContextReferencesWithUrns( + (org.hl7.fhir.r5.model.MetadataResource) newResource, + resourceListWithOriginalIds.stream() + .map(ra -> (org.hl7.fhir.r5.model.MetadataResource) ra) + .collect(Collectors.toList()), + idListForTransactionBundle.stream() + .map(id -> new org.hl7.fhir.r5.model.IdType(id)) + .collect(Collectors.toList())); break; case DSTU2: diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/KnowledgeArtifactVisitor.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/KnowledgeArtifactVisitor.java index 4496ef6ca..256608680 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/KnowledgeArtifactVisitor.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/KnowledgeArtifactVisitor.java @@ -4,16 +4,7 @@ import org.hl7.fhir.instance.model.api.IBaseParameters; import org.opencds.cqf.fhir.api.Repository; import org.opencds.cqf.fhir.utility.adapter.KnowledgeArtifactAdapter; -import org.opencds.cqf.fhir.utility.adapter.LibraryAdapter; -import org.opencds.cqf.fhir.utility.adapter.PlanDefinitionAdapter; -import org.opencds.cqf.fhir.utility.adapter.ValueSetAdapter; public interface KnowledgeArtifactVisitor { IBase visit(KnowledgeArtifactAdapter knowledgeArtifact, Repository repository, IBaseParameters draftParameters); - - IBase visit(LibraryAdapter library, Repository repository, IBaseParameters draftParameters); - - IBase visit(PlanDefinitionAdapter planDefinition, Repository repository, IBaseParameters draftParameters); - - IBase visit(ValueSetAdapter valueSet, Repository repository, IBaseParameters draftParameters); } diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/KnowledgeArtifactPackageVisitor.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/PackageVisitor.java similarity index 56% rename from cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/KnowledgeArtifactPackageVisitor.java rename to cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/PackageVisitor.java index 7565a28e2..2e937088c 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/KnowledgeArtifactPackageVisitor.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/PackageVisitor.java @@ -1,7 +1,12 @@ package org.opencds.cqf.fhir.utility.visitor; +import static org.opencds.cqf.fhir.utility.Parameters.newParameters; +import static org.opencds.cqf.fhir.utility.adapter.AdapterFactory.createAdapterForResource; +import static org.opencds.cqf.fhir.utility.visitor.VisitorHelper.findUnsupportedCapability; +import static org.opencds.cqf.fhir.utility.visitor.VisitorHelper.processCanonicals; + +import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; -import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.rest.server.exceptions.NotImplementedOperationException; import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; @@ -15,28 +20,42 @@ import org.hl7.fhir.instance.model.api.IBaseBackboneElement; import org.hl7.fhir.instance.model.api.IBaseBooleanDatatype; import org.hl7.fhir.instance.model.api.IBaseBundle; -import org.hl7.fhir.instance.model.api.IBaseExtension; import org.hl7.fhir.instance.model.api.IBaseIntegerDatatype; import org.hl7.fhir.instance.model.api.IBaseParameters; +import org.hl7.fhir.instance.model.api.IBaseReference; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IDomainResource; import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.opencds.cqf.fhir.api.Repository; import org.opencds.cqf.fhir.utility.BundleHelper; import org.opencds.cqf.fhir.utility.Canonicals; +import org.opencds.cqf.fhir.utility.Constants; +import org.opencds.cqf.fhir.utility.ExpandHelper; +import org.opencds.cqf.fhir.utility.PackageHelper; import org.opencds.cqf.fhir.utility.SearchHelper; import org.opencds.cqf.fhir.utility.adapter.AdapterFactory; +import org.opencds.cqf.fhir.utility.adapter.EndpointAdapter; import org.opencds.cqf.fhir.utility.adapter.KnowledgeArtifactAdapter; import org.opencds.cqf.fhir.utility.adapter.LibraryAdapter; -import org.opencds.cqf.fhir.utility.adapter.PlanDefinitionAdapter; +import org.opencds.cqf.fhir.utility.adapter.ParametersAdapter; import org.opencds.cqf.fhir.utility.adapter.ValueSetAdapter; -import org.opencds.cqf.fhir.utility.r4.PackageHelper; +import org.opencds.cqf.fhir.utility.client.TerminologyServerClient; + +public class PackageVisitor implements KnowledgeArtifactVisitor { + protected final FhirContext fhirContext; + protected final TerminologyServerClient terminologyServerClient; + protected final ExpandHelper expandHelper; -public class KnowledgeArtifactPackageVisitor implements KnowledgeArtifactVisitor { + public PackageVisitor(FhirContext fhirContext) { + this.fhirContext = fhirContext; + this.terminologyServerClient = new TerminologyServerClient(this.fhirContext); + expandHelper = new ExpandHelper(fhirContext, terminologyServerClient); + } @Override - public IBaseResource visit(LibraryAdapter library, Repository repository, IBaseParameters packageParameters) { - var fhirVersion = library.get().getStructureFhirVersionEnum(); + public IBase visit(KnowledgeArtifactAdapter adapter, Repository repository, IBaseParameters packageParameters) { + var fhirVersion = adapter.get().getStructureFhirVersionEnum(); + Optional artifactRoute = VisitorHelper.getParameter( "artifactRoute", packageParameters, IPrimitiveType.class) .map(r -> (String) r.getValue()); @@ -72,6 +91,9 @@ public IBaseResource visit(LibraryAdapter library, Repository repository, IBaseP "forceArtifactVersion", packageParameters, IPrimitiveType.class) .map(l -> l.stream().map(t -> (String) t.getValue()).collect(Collectors.toList())) .orElseGet(() -> new ArrayList<>()); + boolean isPut = VisitorHelper.getParameter("isPut", packageParameters, IBaseBooleanDatatype.class) + .map(r -> r.getValue()) + .orElse(false); if ((artifactRoute.isPresent() && !StringUtils.isBlank(artifactRoute.get()) @@ -89,14 +111,14 @@ public IBaseResource visit(LibraryAdapter library, Repository repository, IBaseP if (count.isPresent() && count.get() < 0) { throw new UnprocessableEntityException("'count' must be non-negative"); } - var resource = library.get(); + var resource = adapter.get(); // TODO: In the case of a released (active) root Library we can depend on the relatedArtifacts as a // comprehensive manifest var packagedBundle = BundleHelper.newBundle(fhirVersion); if (include.size() == 1 && include.stream().anyMatch((includedType) -> includedType.equals("artifact"))) { - findUnsupportedCapability(library, capability); - processCanonicals(library, artifactVersion, checkArtifactVersion, forceArtifactVersion); - var entry = PackageHelper.createEntry(resource, false); + findUnsupportedCapability(adapter, capability); + processCanonicals(adapter, artifactVersion, checkArtifactVersion, forceArtifactVersion); + var entry = PackageHelper.createEntry(resource, isPut); BundleHelper.addEntry(packagedBundle, entry); } else { recursivePackage( @@ -107,13 +129,14 @@ public IBaseResource visit(LibraryAdapter library, Repository repository, IBaseP include, artifactVersion, checkArtifactVersion, - forceArtifactVersion); + forceArtifactVersion, + isPut); var included = findUnsupportedInclude(BundleHelper.getEntry(packagedBundle), include, fhirVersion); BundleHelper.setEntry(packagedBundle, included); } + handleValueSets(resource, packagedBundle, repository, terminologyEndpoint); setCorrectBundleType(count, offset, packagedBundle, fhirVersion); pageBundleBasedOnCountAndOffset(count, offset, packagedBundle); - handleValueSetReferenceExtensions(resource, packagedBundle, repository, terminologyEndpoint); return packagedBundle; // DependencyInfo --document here that there is a need for figuring out how to determine which package the @@ -121,47 +144,44 @@ public IBaseResource visit(LibraryAdapter library, Repository repository, IBaseP // what is dependency, where did it originate? potentially the package? } - protected void handleValueSetReferenceExtensions( + protected void handleValueSets( IDomainResource resource, IBaseBundle packagedBundle, Repository repository, Optional terminologyEndpoint) { - switch (resource.getStructureFhirVersionEnum()) { - case DSTU3: - org.opencds.cqf.fhir.utility.visitor.dstu3.KnowledgeArtifactPackageVisitor packageVisitorDstu3 = - new org.opencds.cqf.fhir.utility.visitor.dstu3.KnowledgeArtifactPackageVisitor(); - packageVisitorDstu3.handleValueSetReferenceExtensions( - (org.hl7.fhir.dstu3.model.MetadataResource) resource, - ((org.hl7.fhir.dstu3.model.Bundle) packagedBundle).getEntry(), - repository, - terminologyEndpoint.map(te -> (org.hl7.fhir.dstu3.model.Endpoint) te)); - break; - case R4: - org.opencds.cqf.fhir.utility.visitor.r4.KnowledgeArtifactPackageVisitor packageVisitorR4 = - new org.opencds.cqf.fhir.utility.visitor.r4.KnowledgeArtifactPackageVisitor(); - packageVisitorR4.handleValueSetReferenceExtensions( - (org.hl7.fhir.r4.model.MetadataResource) resource, - ((org.hl7.fhir.r4.model.Bundle) packagedBundle).getEntry(), - repository, - terminologyEndpoint.map(te -> (org.hl7.fhir.r4.model.Endpoint) te)); - break; - case R5: - org.opencds.cqf.fhir.utility.visitor.r5.KnowledgeArtifactPackageVisitor packageVisitorR5 = - new org.opencds.cqf.fhir.utility.visitor.r5.KnowledgeArtifactPackageVisitor(); - packageVisitorR5.handleValueSetReferenceExtensions( - (org.hl7.fhir.r5.model.MetadataResource) resource, - ((org.hl7.fhir.r5.model.Bundle) packagedBundle).getEntry(), - repository, - terminologyEndpoint.map(te -> (org.hl7.fhir.r5.model.Endpoint) te)); - break; - default: - throw new IllegalArgumentException(String.format( - "Unsupported FHIR version: %s", - resource.getStructureFhirVersionEnum().getFhirVersionString())); + var expansionParams = newParameters(repository.fhirContext()); + var rootSpecificationLibrary = getRootSpecificationLibrary(packagedBundle); + if (rootSpecificationLibrary != null) { + var expansionParamsExtension = + rootSpecificationLibrary.getExtensionByUrl(Constants.CQF_EXPANSION_PARAMETERS); + if (expansionParamsExtension != null && expansionParamsExtension.getValue() != null) { + // Reference expansionReference = (Reference) expansionParamsExtension.getValue(); + expansionParams = getExpansionParams( + rootSpecificationLibrary, + ((IBaseReference) expansionParamsExtension.getValue()) + .getReferenceElement() + .getValueAsString()); + } } + var params = (ParametersAdapter) createAdapterForResource(expansionParams); + var expandedList = new ArrayList(); + + var valueSets = BundleHelper.getEntryResources(packagedBundle).stream() + .filter(r -> r.fhirType().equals("ValueSet")) + .map(v -> (ValueSetAdapter) createAdapterForResource(v)) + .collect(Collectors.toList()); + + valueSets.stream().forEach(valueSet -> { + expandHelper.expandValueSet( + valueSet, + params, + terminologyEndpoint.map(e -> (EndpointAdapter) createAdapterForResource(e)), + valueSets, + expandedList); + }); } - void recursivePackage( + protected void recursivePackage( IDomainResource resource, IBaseBundle bundle, Repository repository, @@ -169,7 +189,8 @@ void recursivePackage( List include, List artifactVersion, List checkArtifactVersion, - List forceArtifactVersion) + List forceArtifactVersion, + boolean isPut) throws PreconditionFailedException { if (resource != null) { var fhirVersion = resource.getStructureFhirVersionEnum(); @@ -179,29 +200,25 @@ void recursivePackage( boolean entryExists = BundleHelper.getEntryResources(bundle).stream() .map(e -> AdapterFactory.forFhirVersion(fhirVersion) .createKnowledgeArtifactAdapter((IDomainResource) e)) - .filter(mr -> mr.getUrl() != null && mr.getVersion() != null) + .filter(mr -> mr.getUrl() != null) .anyMatch(mr -> mr.getUrl().equals(adapter.getUrl()) - && mr.getVersion().equals(adapter.getVersion())); + && (!mr.hasVersion() || mr.getVersion().equals(adapter.getVersion()))); if (!entryExists) { - var entry = PackageHelper.createEntry(resource, false); + var entry = PackageHelper.createEntry(resource, isPut); BundleHelper.addEntry(bundle, entry); } - adapter.combineComponentsAndDependencies().stream() - // sometimes VS dependencies aren't FHIR resources - .filter(ra -> !StringUtils.isBlank(Canonicals.getResourceType(ra.getReference()))) + var dependencies = adapter.combineComponentsAndDependencies(); + dependencies.stream() + // sometimes VS dependencies aren't FHIR resources, only include references that are .filter(ra -> { try { - var resourceDef = repository - .fhirContext() - .getResourceDefinition(Canonicals.getResourceType(ra.getReference())); - return resourceDef != null; - } catch (DataFormatException e) { - if (e.getMessage().contains("1684")) { - return false; - } else { - throw new DataFormatException(e.getMessage()); - } + return null + != repository + .fhirContext() + .getResourceDefinition(Canonicals.getResourceType(ra.getReference())); + } catch (Exception e) { + return false; } }) .map(ra -> SearchHelper.searchRepositoryByCanonicalWithPaging(repository, ra.getReference())) @@ -214,94 +231,24 @@ void recursivePackage( include, artifactVersion, checkArtifactVersion, - forceArtifactVersion)); + forceArtifactVersion, + isPut)); } } - @Override - public IBase visit(KnowledgeArtifactAdapter library, Repository repository, IBaseParameters draftParameters) { - throw new NotImplementedOperationException("Not implemented"); - } - - @Override - public IBase visit( - PlanDefinitionAdapter planDefinition, Repository repository, IBaseParameters operationParameters) { - throw new NotImplementedOperationException("Not implemented"); - } - - @Override - public IBase visit(ValueSetAdapter valueSet, Repository repository, IBaseParameters operationParameters) { - throw new NotImplementedOperationException("Not implemented"); - } - - private void findUnsupportedCapability(KnowledgeArtifactAdapter resource, List capability) - throws PreconditionFailedException { - if (capability != null && !capability.isEmpty()) { - List> knowledgeCapabilityExtension = resource.get().getExtension().stream() - .filter(ext -> ext.getUrl().contains("cqf-knowledgeCapability")) - .collect(Collectors.toList()); - if (knowledgeCapabilityExtension.isEmpty()) { - // consider resource unsupported if it's knowledgeCapability is undefined - throw new PreconditionFailedException( - String.format("Resource with url: '%s' does not specify capability.", resource.getUrl())); - } - knowledgeCapabilityExtension.stream() - .filter(ext -> !capability.contains(((IPrimitiveType) ext.getValue()).getValue())) - .findAny() - .ifPresent((ext) -> { - throw new PreconditionFailedException(String.format( - "Resource with url: '%s' is not one of '%s'.", - resource.getUrl(), String.join(", ", capability))); - }); - } - } - - private void processCanonicals( - KnowledgeArtifactAdapter resource, - List canonicalVersion, - List checkArtifactVersion, - List forceArtifactVersion) - throws PreconditionFailedException { - if (checkArtifactVersion != null && !checkArtifactVersion.isEmpty()) { - // check throws an error - findVersionInListMatchingResource(checkArtifactVersion, resource).ifPresent((version) -> { - if (!resource.getVersion().equals(version)) { - throw new PreconditionFailedException(String.format( - "Resource with url '%s' has version '%s' but checkVersion specifies '%s'", - resource.getUrl(), resource.getVersion(), version)); - } - }); - } else if (forceArtifactVersion != null && !forceArtifactVersion.isEmpty()) { - // force just does a silent override - findVersionInListMatchingResource(forceArtifactVersion, resource) - .ifPresent((version) -> resource.setVersion(version)); - } else if (canonicalVersion != null && !canonicalVersion.isEmpty() && !resource.hasVersion()) { - // canonicalVersion adds a version if it's missing - findVersionInListMatchingResource(canonicalVersion, resource) - .ifPresent((version) -> resource.setVersion(version)); - } - } - - private Optional findVersionInListMatchingResource(List list, KnowledgeArtifactAdapter resource) { - return list.stream() - .filter((canonical) -> Canonicals.getUrl(canonical).equals(resource.getUrl())) - .map((canonical) -> Canonicals.getVersion(canonical)) - .findAny(); - } - - private void setCorrectBundleType( + protected void setCorrectBundleType( Optional count, Optional offset, IBaseBundle bundle, FhirVersionEnum fhirVersion) { switch (fhirVersion) { case DSTU3: - org.opencds.cqf.fhir.utility.visitor.dstu3.KnowledgeArtifactPackageVisitor.setCorrectBundleType( + org.opencds.cqf.fhir.utility.visitor.dstu3.PackageVisitor.setCorrectBundleType( count, offset, (org.hl7.fhir.dstu3.model.Bundle) bundle); break; case R4: - org.opencds.cqf.fhir.utility.visitor.r4.KnowledgeArtifactPackageVisitor.setCorrectBundleType( + org.opencds.cqf.fhir.utility.visitor.r4.PackageVisitor.setCorrectBundleType( count, offset, (org.hl7.fhir.r4.model.Bundle) bundle); break; case R5: - org.opencds.cqf.fhir.utility.visitor.r5.KnowledgeArtifactPackageVisitor.setCorrectBundleType( + org.opencds.cqf.fhir.utility.visitor.r5.PackageVisitor.setCorrectBundleType( count, offset, (org.hl7.fhir.r5.model.Bundle) bundle); break; case DSTU2: @@ -319,7 +266,7 @@ private void setCorrectBundleType( * @param offset the number of resources to skip beginning from the start of the bundle (starts from 1) * @param bundle the bundle to page */ - private void pageBundleBasedOnCountAndOffset( + protected void pageBundleBasedOnCountAndOffset( Optional count, Optional offset, IBaseBundle bundle) { if (offset.isPresent()) { var entries = BundleHelper.getEntry(bundle); @@ -343,18 +290,17 @@ private void pageBundleBasedOnCountAndOffset( } @SuppressWarnings("unchecked") - private List findUnsupportedInclude( + protected List findUnsupportedInclude( List entries, List include, FhirVersionEnum fhirVersion) { switch (fhirVersion) { case DSTU3: - return org.opencds.cqf.fhir.utility.visitor.dstu3.KnowledgeArtifactPackageVisitor - .findUnsupportedInclude( - (List) entries, include); + return org.opencds.cqf.fhir.utility.visitor.dstu3.PackageVisitor.findUnsupportedInclude( + (List) entries, include); case R4: - return org.opencds.cqf.fhir.utility.visitor.r4.KnowledgeArtifactPackageVisitor.findUnsupportedInclude( + return org.opencds.cqf.fhir.utility.visitor.r4.PackageVisitor.findUnsupportedInclude( (List) entries, include); case R5: - return org.opencds.cqf.fhir.utility.visitor.r5.KnowledgeArtifactPackageVisitor.findUnsupportedInclude( + return org.opencds.cqf.fhir.utility.visitor.r5.PackageVisitor.findUnsupportedInclude( (List) entries, include); case DSTU2: case DSTU2_1: @@ -364,4 +310,44 @@ private List findUnsupportedInclude( String.format("Unsupported version of FHIR: %s", fhirVersion.getFhirVersionString())); } } + + protected static LibraryAdapter getRootSpecificationLibrary(IBaseBundle bundle) { + Optional rootSpecLibrary = BundleHelper.getEntryResources(bundle).stream() + .filter(r -> r.fhirType().equals("Library")) + .map(r -> AdapterFactory.forFhirVersion(r.getStructureFhirVersionEnum()) + .createLibrary(r)) + // .filter(a -> a.getType().hasCoding(Constants.LIBRARY_TYPE, Constants.ASSET_COLLECTION) + // && a.getUseContext().stream() + // .allMatch(useContext -> (useContext + // .getCode() + // .getSystem() + // .equals(KnowledgeArtifactAdapter.usPhContextTypeUrl) + // && useContext + // .getCode() + // .getCode() + // .equals("reporting") + // && useContext + // .getValueCodeableConcept() + // .hasCoding(Constants.US_PH_CONTEXT_URL, "triggering")) + // || (useContext + // .getCode() + // .getSystem() + // .equals(KnowledgeArtifactAdapter.usPhContextTypeUrl) + // && useContext + // .getCode() + // .getCode() + // .equals("specification-type") + // && useContext + // .getValueCodeableConcept() + // .hasCoding(Constants.US_PH_CONTEXT_URL, "program")))) + .findFirst(); + return rootSpecLibrary.orElse(null); + } + + protected static IBaseParameters getExpansionParams(LibraryAdapter rootSpecificationLibrary, String reference) { + Optional expansionParamResource = rootSpecificationLibrary.getContained().stream() + .filter(contained -> contained.getIdElement().getValue().equals(reference)) + .findFirst(); + return (IBaseParameters) expansionParamResource.orElse(null); + } } diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/KnowledgeArtifactReleaseVisitor.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/ReleaseVisitor.java similarity index 83% rename from cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/KnowledgeArtifactReleaseVisitor.java rename to cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/ReleaseVisitor.java index a33820255..a5b53c6b2 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/KnowledgeArtifactReleaseVisitor.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/ReleaseVisitor.java @@ -17,7 +17,6 @@ import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseBackboneElement; -import org.hl7.fhir.instance.model.api.IBaseExtension; import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.ICompositeType; @@ -31,18 +30,15 @@ import org.opencds.cqf.fhir.utility.adapter.AdapterFactory; import org.opencds.cqf.fhir.utility.adapter.IDependencyInfo; import org.opencds.cqf.fhir.utility.adapter.KnowledgeArtifactAdapter; -import org.opencds.cqf.fhir.utility.adapter.LibraryAdapter; -import org.opencds.cqf.fhir.utility.adapter.PlanDefinitionAdapter; -import org.opencds.cqf.fhir.utility.adapter.ValueSetAdapter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class KnowledgeArtifactReleaseVisitor implements KnowledgeArtifactVisitor { - private Logger log = LoggerFactory.getLogger(KnowledgeArtifactReleaseVisitor.class); +public class ReleaseVisitor implements KnowledgeArtifactVisitor { + private Logger log = LoggerFactory.getLogger(ReleaseVisitor.class); - @SuppressWarnings("unchecked") @Override - public IBase visit(LibraryAdapter rootLibraryAdapter, Repository repository, IBaseParameters operationParameters) { + public IBase visit( + KnowledgeArtifactAdapter rootAdapter, Repository repository, IBaseParameters operationParameters) { // boolean latestFromTxServer = operationParameters.getParameterBool("latestFromTxServer"); Optional latestFromTxServer = VisitorHelper.getParameter( "latestFromTxServer", operationParameters, IPrimitiveType.class) @@ -60,42 +56,41 @@ public IBase visit(LibraryAdapter rootLibraryAdapter, Repository repository, IBa Optional versionBehavior = VisitorHelper.getParameter( "versionBehavior", operationParameters, IPrimitiveType.class) .map(t -> (String) t.getValue()); - Optional requireNonExpermimental = VisitorHelper.getParameter( + Optional requireNonExperimental = VisitorHelper.getParameter( "requireNonExperimental", operationParameters, IPrimitiveType.class) .map(t -> (String) t.getValue()); checkReleaseVersion(version, versionBehavior); - var rootLibrary = rootLibraryAdapter.get(); + var rootLibrary = rootAdapter.get(); var fhirVersion = rootLibrary.getStructureFhirVersionEnum(); - var currentApprovalDate = rootLibraryAdapter.getApprovalDate(); - checkReleasePreconditions(rootLibraryAdapter, currentApprovalDate); + var currentApprovalDate = rootAdapter.getApprovalDate(); + checkReleasePreconditions(rootAdapter, currentApprovalDate); // Determine which version should be used. - var existingVersion = rootLibraryAdapter.hasVersion() - ? rootLibraryAdapter.getVersion().replace("-draft", "") - : null; + var existingVersion = + rootAdapter.hasVersion() ? rootAdapter.getVersion().replace("-draft", "") : null; var releaseVersion = getReleaseVersion(version, versionBehavior, existingVersion, fhirVersion) .orElseThrow( () -> new UnprocessableEntityException("Could not resolve a version for the root artifact.")); - var rootEffectivePeriod = rootLibraryAdapter.getEffectivePeriod(); + var rootEffectivePeriod = rootAdapter.getEffectivePeriod(); // if the root artifact is experimental then we don't need to check for experimental children - if (rootLibraryAdapter.getExperimental()) { - requireNonExpermimental = Optional.of("none"); + if (rootAdapter.getExperimental()) { + requireNonExperimental = Optional.of("none"); } var releasedResources = internalRelease( - rootLibraryAdapter, + rootAdapter, releaseVersion, rootEffectivePeriod, latestFromTxServer.orElse(false), - requireNonExpermimental, + requireNonExperimental, repository); updateReleaseLabel(rootLibrary, releaseLabel); - var rootArtifactOriginalDependencies = new ArrayList(rootLibraryAdapter.getDependencies()); + var rootArtifactOriginalDependencies = new ArrayList(rootAdapter.getDependencies()); // Get list of extensions which need to be preserved var originalDependenciesWithExtensions = rootArtifactOriginalDependencies.stream() .filter(dep -> dep.getExtension() != null && dep.getExtension().size() > 0) .collect(Collectors.toList()); // once iteration is complete, delete all depends-on RAs in the root artifact - rootLibraryAdapter.getRelatedArtifact().removeIf(ra -> KnowledgeArtifactAdapter.getRelatedArtifactType(ra) + rootAdapter.getRelatedArtifact().removeIf(ra -> KnowledgeArtifactAdapter.getRelatedArtifactType(ra) .equalsIgnoreCase("depends-on")); var transactionBundle = BundleHelper.newBundle(fhirVersion, null, "transaction"); @@ -117,17 +112,22 @@ public IBase visit(LibraryAdapter rootLibraryAdapter, Repository repository, IBa var adapter = AdapterFactory.forFhirVersion(resource.getStructureFhirVersionEnum()) .createKnowledgeArtifactAdapter(resource); var reference = String.format("%s|%s", adapter.getUrl(), adapter.getVersion()); - KnowledgeArtifactAdapter.setRelatedArtifactReference(component, reference); + KnowledgeArtifactAdapter.setRelatedArtifactReference(component, reference, null); } else if (Canonicals.getVersion(relatedArtifactReference) == null || Canonicals.getVersion(relatedArtifactReference).isEmpty()) { // if the not Owned component doesn't have a version, try to find the latest version String updatedReference = tryUpdateReferenceToLatestActiveVersion( relatedArtifactReference, repository, artifactAdapter.getUrl()); - KnowledgeArtifactAdapter.setRelatedArtifactReference(component, updatedReference); + KnowledgeArtifactAdapter.setRelatedArtifactReference(component, updatedReference, null); } var componentToDependency = KnowledgeArtifactAdapter.newRelatedArtifact( - fhirVersion, "depends-on", KnowledgeArtifactAdapter.getRelatedArtifactReference(component)); - rootLibraryAdapter.getRelatedArtifact().add(componentToDependency); + fhirVersion, + "depends-on", + KnowledgeArtifactAdapter.getRelatedArtifactReference(component), + null); + var relatedArtifacts = rootAdapter.getRelatedArtifact(); + relatedArtifacts.add(componentToDependency); + rootAdapter.setRelatedArtifact(relatedArtifacts); } var dependencies = artifactAdapter.getDependencies(); @@ -148,15 +148,17 @@ public IBase visit(LibraryAdapter rootLibraryAdapter, Repository repository, IBa dependency.setReference(updatedReference); } // only add the dependency to the manifest if it is from a leaf artifact - if (!artifactAdapter.getUrl().equals(rootLibraryAdapter.getUrl())) { + if (!artifactAdapter.getUrl().equals(rootAdapter.getUrl())) { var newDep = KnowledgeArtifactAdapter.newRelatedArtifact( - fhirVersion, "depends-on", dependency.getReference()); - rootLibraryAdapter.getRelatedArtifact().add(newDep); + fhirVersion, "depends-on", dependency.getReference(), null); + var relatedArtifacts = rootAdapter.getRelatedArtifact(); + relatedArtifacts.add(newDep); + rootAdapter.setRelatedArtifact(relatedArtifacts); } } } // removed duplicates and add - var relatedArtifacts = rootLibraryAdapter.getRelatedArtifact(); + var relatedArtifacts = rootAdapter.getRelatedArtifact(); var distinctResolvedRelatedArtifacts = new ArrayList<>(relatedArtifacts); distinctResolvedRelatedArtifacts.clear(); for (var resolvedRelatedArtifact : relatedArtifacts) { @@ -179,8 +181,8 @@ public IBase visit(LibraryAdapter rootLibraryAdapter, Repository repository, IBa .equalsIgnoreCase("depends-on")) .findFirst() .ifPresent(dep -> { - ((List>) resolvedRelatedArtifact.getExtension()) - .addAll((List>) dep.getExtension()); + // ((List>) resolvedRelatedArtifact.getExtension()) + // .addAll((List>) dep.getExtension()); originalDependenciesWithExtensions.removeIf( ra -> ra.getReference().equals(relatedArtifactReference)); }); @@ -190,7 +192,7 @@ public IBase visit(LibraryAdapter rootLibraryAdapter, Repository repository, IBa findArtifactCommentsToUpdate(rootLibrary, releaseVersion, repository).forEach(entry -> { BundleHelper.addEntry(transactionBundle, entry); }); - rootLibraryAdapter.setRelatedArtifact(distinctResolvedRelatedArtifacts); + rootAdapter.setRelatedArtifact(distinctResolvedRelatedArtifacts); return repository.transaction(transactionBundle); } @@ -211,7 +213,7 @@ private List internalRelease( artifactAdapter.setStatus("active"); artifactAdapter.setVersion(version); // Step 2: propagate effectivePeriod if it doesn't exist - propagageEffectivePeriod(rootEffectivePeriod, artifactAdapter); + propagateEffectivePeriod(rootEffectivePeriod, artifactAdapter); resourcesToUpdate.add(artifactAdapter.get()); var ownedRelatedArtifacts = artifactAdapter.getOwnedRelatedArtifacts(); @@ -268,7 +270,7 @@ private void checkNonExperimental( .CRMIReleaseExperimentalBehaviorCodes.fromCode(experimentalBehavior.get()) : org.opencds.cqf.fhir.utility.dstu3.CRMIReleaseExperimentalBehavior .CRMIReleaseExperimentalBehaviorCodes.NULL; - org.opencds.cqf.fhir.utility.visitor.dstu3.KnowledgeArtifactReleaseVisitor.checkNonExperimental( + org.opencds.cqf.fhir.utility.visitor.dstu3.ReleaseVisitor.checkNonExperimental( (org.hl7.fhir.dstu3.model.MetadataResource) resource, code, repository, log); } else if (resource instanceof org.hl7.fhir.r4.model.MetadataResource) { var code = experimentalBehavior.isPresent() @@ -276,7 +278,7 @@ private void checkNonExperimental( .CRMIReleaseExperimentalBehaviorCodes.fromCode(experimentalBehavior.get()) : org.opencds.cqf.fhir.utility.r4.CRMIReleaseExperimentalBehavior .CRMIReleaseExperimentalBehaviorCodes.NULL; - org.opencds.cqf.fhir.utility.visitor.r4.KnowledgeArtifactReleaseVisitor.checkNonExperimental( + org.opencds.cqf.fhir.utility.visitor.r4.ReleaseVisitor.checkNonExperimental( (org.hl7.fhir.r4.model.MetadataResource) resource, code, repository, log); } else if (resource instanceof org.hl7.fhir.r5.model.MetadataResource) { var code = experimentalBehavior.isPresent() @@ -284,23 +286,23 @@ private void checkNonExperimental( .CRMIReleaseExperimentalBehaviorCodes.fromCode(experimentalBehavior.get()) : org.opencds.cqf.fhir.utility.r5.CRMIReleaseExperimentalBehavior .CRMIReleaseExperimentalBehaviorCodes.NULL; - org.opencds.cqf.fhir.utility.visitor.r5.KnowledgeArtifactReleaseVisitor.checkNonExperimental( + org.opencds.cqf.fhir.utility.visitor.r5.ReleaseVisitor.checkNonExperimental( (org.hl7.fhir.r5.model.MetadataResource) resource, code, repository, log); } else { throw new UnprocessableEntityException(resource.getClass().getName() + " not supported"); } } - private void propagageEffectivePeriod( + private void propagateEffectivePeriod( ICompositeType rootEffectivePeriod, KnowledgeArtifactAdapter artifactAdapter) { if (rootEffectivePeriod instanceof org.hl7.fhir.dstu3.model.Period) { - org.opencds.cqf.fhir.utility.visitor.dstu3.KnowledgeArtifactReleaseVisitor.propagageEffectivePeriod( + org.opencds.cqf.fhir.utility.visitor.dstu3.ReleaseVisitor.propagateEffectivePeriod( (org.hl7.fhir.dstu3.model.Period) rootEffectivePeriod, artifactAdapter); } else if (rootEffectivePeriod instanceof org.hl7.fhir.r4.model.Period) { - org.opencds.cqf.fhir.utility.visitor.r4.KnowledgeArtifactReleaseVisitor.propagageEffectivePeriod( + org.opencds.cqf.fhir.utility.visitor.r4.ReleaseVisitor.propagateEffectivePeriod( (org.hl7.fhir.r4.model.Period) rootEffectivePeriod, artifactAdapter); } else if (rootEffectivePeriod instanceof org.hl7.fhir.r5.model.Period) { - org.opencds.cqf.fhir.utility.visitor.r5.KnowledgeArtifactReleaseVisitor.propagageEffectivePeriod( + org.opencds.cqf.fhir.utility.visitor.r5.ReleaseVisitor.propagateEffectivePeriod( (org.hl7.fhir.r5.model.Period) rootEffectivePeriod, artifactAdapter); } else { throw new UnprocessableEntityException( @@ -337,13 +339,13 @@ private Optional getReleaseVersion( throws UnprocessableEntityException { switch (fhirVersion) { case DSTU3: - return org.opencds.cqf.fhir.utility.visitor.dstu3.KnowledgeArtifactReleaseVisitor.getReleaseVersion( + return org.opencds.cqf.fhir.utility.visitor.dstu3.ReleaseVisitor.getReleaseVersion( version, versionBehavior, existingVersion); case R4: - return org.opencds.cqf.fhir.utility.visitor.r4.KnowledgeArtifactReleaseVisitor.getReleaseVersion( + return org.opencds.cqf.fhir.utility.visitor.r4.ReleaseVisitor.getReleaseVersion( version, versionBehavior, existingVersion); case R5: - return org.opencds.cqf.fhir.utility.visitor.r5.KnowledgeArtifactReleaseVisitor.getReleaseVersion( + return org.opencds.cqf.fhir.utility.visitor.r5.ReleaseVisitor.getReleaseVersion( version, versionBehavior, existingVersion); case DSTU2: case DSTU2_1: @@ -356,13 +358,13 @@ private Optional getReleaseVersion( private void updateReleaseLabel(IBaseResource artifact, String releaseLabel) throws IllegalArgumentException { if (artifact instanceof org.hl7.fhir.dstu3.model.MetadataResource) { - org.opencds.cqf.fhir.utility.visitor.dstu3.KnowledgeArtifactReleaseVisitor.updateReleaseLabel( + org.opencds.cqf.fhir.utility.visitor.dstu3.ReleaseVisitor.updateReleaseLabel( (org.hl7.fhir.dstu3.model.MetadataResource) artifact, releaseLabel); } else if (artifact instanceof org.hl7.fhir.r4.model.MetadataResource) { - org.opencds.cqf.fhir.utility.visitor.r4.KnowledgeArtifactReleaseVisitor.updateReleaseLabel( + org.opencds.cqf.fhir.utility.visitor.r4.ReleaseVisitor.updateReleaseLabel( (org.hl7.fhir.r4.model.MetadataResource) artifact, releaseLabel); } else if (artifact instanceof org.hl7.fhir.r5.model.MetadataResource) { - org.opencds.cqf.fhir.utility.visitor.r5.KnowledgeArtifactReleaseVisitor.updateReleaseLabel( + org.opencds.cqf.fhir.utility.visitor.r5.ReleaseVisitor.updateReleaseLabel( (org.hl7.fhir.r5.model.MetadataResource) artifact, releaseLabel); } else { throw new UnprocessableEntityException(artifact.getClass().getName() + " not supported"); @@ -427,27 +429,19 @@ private void checkReleasePreconditions(KnowledgeArtifactAdapter artifact, Date a private List findArtifactCommentsToUpdate( IBaseResource artifact, String releaseVersion, Repository repository) { if (artifact instanceof org.hl7.fhir.dstu3.model.MetadataResource) { - return org - .opencds - .cqf - .fhir - .utility - .visitor - .dstu3 - .KnowledgeArtifactReleaseVisitor - .findArtifactCommentsToUpdate( + return org.opencds.cqf.fhir.utility.visitor.dstu3.ReleaseVisitor.findArtifactCommentsToUpdate( (org.hl7.fhir.dstu3.model.MetadataResource) artifact, releaseVersion, repository) .stream() .map(r -> (IBaseBackboneElement) r) .collect(Collectors.toList()); } else if (artifact instanceof org.hl7.fhir.r4.model.MetadataResource) { - return org.opencds.cqf.fhir.utility.visitor.r4.KnowledgeArtifactReleaseVisitor.findArtifactCommentsToUpdate( + return org.opencds.cqf.fhir.utility.visitor.r4.ReleaseVisitor.findArtifactCommentsToUpdate( (org.hl7.fhir.r4.model.MetadataResource) artifact, releaseVersion, repository) .stream() .map(r -> (IBaseBackboneElement) r) .collect(Collectors.toList()); } else if (artifact instanceof org.hl7.fhir.r5.model.MetadataResource) { - return org.opencds.cqf.fhir.utility.visitor.r5.KnowledgeArtifactReleaseVisitor.findArtifactCommentsToUpdate( + return org.opencds.cqf.fhir.utility.visitor.r5.ReleaseVisitor.findArtifactCommentsToUpdate( (org.hl7.fhir.r5.model.MetadataResource) artifact, releaseVersion, repository) .stream() .map(r -> (IBaseBackboneElement) r) @@ -483,22 +477,4 @@ private void checkVersionValidSemver(String version) throws UnprocessableEntityE throw new UnprocessableEntityException("The version must be in the format MAJOR.MINOR.PATCH"); } } - - @Override - public IBase visit(PlanDefinitionAdapter valueSet, Repository repository, IBaseParameters operationParameters) { - throw new NotImplementedOperationException("Not implemented"); - } - - @Override - public IBase visit(ValueSetAdapter valueSet, Repository repository, IBaseParameters operationParameters) { - throw new NotImplementedOperationException("Not implemented"); - } - - @Override - public IBase visit( - KnowledgeArtifactAdapter knowledgeArtifactAdapter, - Repository repository, - IBaseParameters operationParameters) { - throw new NotImplementedOperationException("Not implemented"); - } } diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/VisitorHelper.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/VisitorHelper.java index d29032141..cc54beddc 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/VisitorHelper.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/VisitorHelper.java @@ -1,15 +1,21 @@ package org.opencds.cqf.fhir.utility.visitor; +import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IBaseDatatype; +import org.hl7.fhir.instance.model.api.IBaseExtension; import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.ICompositeType; +import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.opencds.cqf.fhir.utility.BundleHelper; +import org.opencds.cqf.fhir.utility.Canonicals; import org.opencds.cqf.fhir.utility.adapter.AdapterFactory; +import org.opencds.cqf.fhir.utility.adapter.KnowledgeArtifactAdapter; public class VisitorHelper { @@ -77,4 +83,64 @@ public static List getMetadataResourcesFromBundle(IBaseBundle bun return resourceList; } + + public static void findUnsupportedCapability(KnowledgeArtifactAdapter resource, List capability) + throws PreconditionFailedException { + if (capability != null && !capability.isEmpty()) { + List> knowledgeCapabilityExtension = resource.get().getExtension().stream() + .filter(ext -> ext.getUrl().contains("cqf-knowledgeCapability")) + .collect(Collectors.toList()); + if (knowledgeCapabilityExtension.isEmpty()) { + // consider resource unsupported if it's knowledgeCapability is undefined + throw new PreconditionFailedException( + String.format("Resource with url: '%s' does not specify capability.", resource.getUrl())); + } + knowledgeCapabilityExtension.stream() + .filter(ext -> !capability.contains(((IPrimitiveType) ext.getValue()).getValue())) + .findAny() + .ifPresent((ext) -> { + throw new PreconditionFailedException(String.format( + "Resource with url: '%s' is not one of '%s'.", + resource.getUrl(), String.join(", ", capability))); + }); + } + } + + public static void processCanonicals( + KnowledgeArtifactAdapter resource, + List canonicalVersion, + List checkArtifactVersion, + List forceArtifactVersion) + throws PreconditionFailedException { + if (checkArtifactVersion != null && !checkArtifactVersion.isEmpty()) { + // check throws an error + findVersionInListMatchingResource(checkArtifactVersion, resource).ifPresent((version) -> { + if (!resource.getVersion().equals(version)) { + throw new PreconditionFailedException(String.format( + "Resource with url '%s' has version '%s' but checkVersion specifies '%s'", + resource.getUrl(), resource.getVersion(), version)); + } + }); + } else if (forceArtifactVersion != null && !forceArtifactVersion.isEmpty()) { + // force just does a silent override + findVersionInListMatchingResource(forceArtifactVersion, resource) + .ifPresent((version) -> resource.setVersion(version)); + } else if (canonicalVersion != null && !canonicalVersion.isEmpty() && !resource.hasVersion()) { + // canonicalVersion adds a version if it's missing + findVersionInListMatchingResource(canonicalVersion, resource) + .ifPresent((version) -> resource.setVersion(version)); + } + } + + private static Optional findVersionInListMatchingResource( + List list, KnowledgeArtifactAdapter resource) { + return list.stream() + .filter((canonical) -> Canonicals.getUrl(canonical).equals(resource.getUrl())) + .map((canonical) -> Canonicals.getVersion(canonical)) + .findAny(); + } + + public static boolean typeHasCoding(ICompositeType type, String system, String code) { + return false; + } } diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/dstu3/KnowledgeArtifactApproveVisitor.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/dstu3/ApproveVisitor.java similarity index 97% rename from cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/dstu3/KnowledgeArtifactApproveVisitor.java rename to cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/dstu3/ApproveVisitor.java index eb6ffece5..5b3e953fb 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/dstu3/KnowledgeArtifactApproveVisitor.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/dstu3/ApproveVisitor.java @@ -10,7 +10,7 @@ import org.hl7.fhir.instance.model.api.IIdType; import org.opencds.cqf.fhir.utility.dstu3.ArtifactAssessment; -public class KnowledgeArtifactApproveVisitor { +public class ApproveVisitor { public static ArtifactAssessment createApprovalAssessment( IIdType id, diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/dstu3/KnowledgeArtifactDraftVisitor.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/dstu3/DraftVisitor.java similarity index 98% rename from cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/dstu3/KnowledgeArtifactDraftVisitor.java rename to cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/dstu3/DraftVisitor.java index 8bed77201..104d78e28 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/dstu3/KnowledgeArtifactDraftVisitor.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/dstu3/DraftVisitor.java @@ -11,7 +11,7 @@ import org.opencds.cqf.fhir.api.Repository; import org.opencds.cqf.fhir.utility.SearchHelper; -public class KnowledgeArtifactDraftVisitor { +public class DraftVisitor { public static Optional processReferencedResourceForDraft( Repository repository, RelatedArtifact ra, String version) { diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/dstu3/KnowledgeArtifactPackageVisitor.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/dstu3/KnowledgeArtifactPackageVisitor.java deleted file mode 100644 index 80df58cd2..000000000 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/dstu3/KnowledgeArtifactPackageVisitor.java +++ /dev/null @@ -1,464 +0,0 @@ -package org.opencds.cqf.fhir.utility.visitor.dstu3; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; -import java.time.Instant; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Date; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; -import org.hl7.fhir.dstu3.model.BooleanType; -import org.hl7.fhir.dstu3.model.Bundle; -import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent; -import org.hl7.fhir.dstu3.model.Bundle.BundleType; -import org.hl7.fhir.dstu3.model.CodeableConcept; -import org.hl7.fhir.dstu3.model.Coding; -import org.hl7.fhir.dstu3.model.Endpoint; -import org.hl7.fhir.dstu3.model.Extension; -import org.hl7.fhir.dstu3.model.Library; -import org.hl7.fhir.dstu3.model.MetadataResource; -import org.hl7.fhir.dstu3.model.Parameters; -import org.hl7.fhir.dstu3.model.Reference; -import org.hl7.fhir.dstu3.model.RelatedArtifact; -import org.hl7.fhir.dstu3.model.Resource; -import org.hl7.fhir.dstu3.model.ResourceType; -import org.hl7.fhir.dstu3.model.StructureDefinition; -import org.hl7.fhir.dstu3.model.UsageContext; -import org.hl7.fhir.dstu3.model.ValueSet; -import org.opencds.cqf.fhir.api.Repository; -import org.opencds.cqf.fhir.utility.Canonicals; -import org.opencds.cqf.fhir.utility.Constants; -import org.opencds.cqf.fhir.utility.adapter.AdapterFactory; -import org.opencds.cqf.fhir.utility.adapter.KnowledgeArtifactAdapter; -import org.opencds.cqf.fhir.utility.client.TerminologyServerClient; - -public class KnowledgeArtifactPackageVisitor { - - public KnowledgeArtifactPackageVisitor() { - this.terminologyServerClient = new TerminologyServerClient(FhirContext.forDstu3()); - } - - private TerminologyServerClient terminologyServerClient; - - // as per http://hl7.org/fhir/dstu3/resource.html#canonical - public static final List canonicalResourceTypes = - // can't use List.of for Android 26 compatibility - Collections.unmodifiableList(new ArrayList(Arrays.asList( - ResourceType.ActivityDefinition, - ResourceType.CapabilityStatement, - ResourceType.CompartmentDefinition, - ResourceType.ConceptMap, - ResourceType.GraphDefinition, - ResourceType.ImplementationGuide, - ResourceType.Library, - ResourceType.Measure, - ResourceType.MessageDefinition, - ResourceType.NamingSystem, - ResourceType.OperationDefinition, - ResourceType.PlanDefinition, - ResourceType.Questionnaire, - ResourceType.SearchParameter, - ResourceType.StructureDefinition, - ResourceType.StructureMap, - ResourceType.TestScript, - ResourceType.ValueSet))); - - public static final List conformanceResourceTypes = - // can't use List.of for Android 26 compatibility - Collections.unmodifiableList(new ArrayList(Arrays.asList( - ResourceType.CapabilityStatement, - ResourceType.StructureDefinition, - ResourceType.ImplementationGuide, - ResourceType.SearchParameter, - ResourceType.MessageDefinition, - ResourceType.OperationDefinition, - ResourceType.CompartmentDefinition, - ResourceType.StructureMap, - ResourceType.GraphDefinition))); - - public static final List knowledgeArtifactResourceTypes = - // can't use List.of for Android 26 compatibility - Collections.unmodifiableList(new ArrayList(Arrays.asList( - ResourceType.Library, - ResourceType.Measure, - ResourceType.ActivityDefinition, - ResourceType.PlanDefinition))); - - public static final List terminologyResourceTypes = - // can't use List.of for Android 26 compatibility - Collections.unmodifiableList(new ArrayList(Arrays.asList( - ResourceType.CodeSystem, - ResourceType.ValueSet, - ResourceType.ConceptMap, - ResourceType.NamingSystem))); - - public static void setCorrectBundleType(Optional count, Optional offset, Bundle bundle) { - // if the bundle is paged then it must be of type = collection and modified to follow bundle.type constraints - // if not, set type = transaction - // special case of count = 0 -> set type = searchset so we can display bundle.total - if (count.isPresent() && count.get() == 0) { - bundle.setType(BundleType.SEARCHSET); - bundle.setTotal(bundle.getEntry().size()); - } else if ((offset.isPresent() && offset.get() > 0) - || (count.isPresent() && count.get() < bundle.getEntry().size())) { - bundle.setType(BundleType.COLLECTION); - List removedRequest = bundle.getEntry().stream() - .map(entry -> { - entry.setRequest(null); - return entry; - }) - .collect(Collectors.toList()); - bundle.setEntry(removedRequest); - } else { - bundle.setType(BundleType.TRANSACTION); - } - } - - public static List findUnsupportedInclude( - List entries, List include) { - if (include == null - || include.isEmpty() - || include.stream().anyMatch((includedType) -> includedType.equals("all"))) { - return entries; - } - List filteredList = new ArrayList<>(); - entries.stream().forEach(entry -> { - if (include.stream().anyMatch((type) -> type.equals("knowledge"))) { - Boolean resourceIsKnowledgeType = knowledgeArtifactResourceTypes.contains( - entry.getResource().getResourceType()); - if (resourceIsKnowledgeType) { - filteredList.add(entry); - } - } - if (include.stream().anyMatch((type) -> type.equals("canonical"))) { - Boolean resourceIsCanonicalType = - canonicalResourceTypes.contains(entry.getResource().getResourceType()); - if (resourceIsCanonicalType) { - filteredList.add(entry); - } - } - if (include.stream().anyMatch((type) -> type.equals("terminology"))) { - Boolean resourceIsTerminologyType = - terminologyResourceTypes.contains(entry.getResource().getResourceType()); - if (resourceIsTerminologyType) { - filteredList.add(entry); - } - } - if (include.stream().anyMatch((type) -> type.equals("conformance"))) { - Boolean resourceIsConformanceType = - conformanceResourceTypes.contains(entry.getResource().getResourceType()); - if (resourceIsConformanceType) { - filteredList.add(entry); - } - } - if (include.stream().anyMatch((type) -> type.equals("extensions")) - && entry.getResource().getResourceType().equals(ResourceType.StructureDefinition) - && ((StructureDefinition) entry.getResource()).getType().equals("Extension")) { - filteredList.add(entry); - } - if (include.stream().anyMatch((type) -> type.equals("profiles")) - && entry.getResource().getResourceType().equals(ResourceType.StructureDefinition) - && !((StructureDefinition) entry.getResource()).getType().equals("Extension")) { - filteredList.add(entry); - } - if (include.stream().anyMatch((type) -> type.equals("tests"))) { - if (entry.getResource().getResourceType().equals(ResourceType.Library) - && ((Library) entry.getResource()) - .getType().getCoding().stream() - .anyMatch(coding -> coding.getCode().equals("test-case"))) { - filteredList.add(entry); - } else if (((MetadataResource) entry.getResource()) - .getExtension().stream() - .anyMatch(ext -> ext.getUrl().contains("isTestCase") - && ((BooleanType) ext.getValue()).getValue())) { - filteredList.add(entry); - } - } - if (include.stream().anyMatch((type) -> type.equals("examples"))) { - // TODO: idk if this is legit just a placeholder for now - if (((MetadataResource) entry.getResource()) - .getExtension().stream() - .anyMatch(ext -> ext.getUrl().contains("isExample") - && ((BooleanType) ext.getValue()).getValue())) { - filteredList.add(entry); - } - } - }); - List distinctFilteredEntries = new ArrayList<>(); - // remove duplicates - for (BundleEntryComponent entry : filteredList) { - if (!distinctFilteredEntries.stream() - .map((e) -> ((MetadataResource) e.getResource())) - .anyMatch(existingEntry -> - existingEntry.getUrl().equals(((MetadataResource) entry.getResource()).getUrl()) - && existingEntry - .getVersion() - .equals(((MetadataResource) entry.getResource()).getVersion()))) { - distinctFilteredEntries.add(entry); - } - } - return distinctFilteredEntries; - } - - /** - * ValueSets can be part of multiple artifacts at the same time. Certain properties are tracked/managed in the manifest to avoid conflicts with other artifacts. This function sets those properties on the ValueSets themselves at export / $package time - * @param manifest the resource containing all RelatedArtifact references - * @param bundleEntries the list of packaged resources to modify according to the extensions on the manifest relatedArtifact references - */ - public void handleValueSetReferenceExtensions( - MetadataResource manifest, - List bundleEntries, - Repository repository, - Optional terminologyEndpoint) - throws UnprocessableEntityException, IllegalArgumentException { - KnowledgeArtifactAdapter adapter = AdapterFactory.forFhirVersion(manifest.getStructureFhirVersionEnum()) - .createKnowledgeArtifactAdapter(manifest); - List relatedArtifactsWithPreservedExtension = - getRelatedArtifactsWithPreservedExtensions(adapter.getRelatedArtifact()); - Parameters expansionParams = new Parameters(); - Library rootSpecificationLibrary = getRootSpecificationLibrary(bundleEntries); - if (rootSpecificationLibrary != null) { - Extension expansionParamsExtension = - rootSpecificationLibrary.getExtensionByUrl(Constants.EXPANSION_PARAMETERS_URL); - if (expansionParamsExtension != null && expansionParamsExtension.hasValue()) { - Reference expansionReference = (Reference) expansionParamsExtension.getValue(); - expansionParams = getExpansionParams(rootSpecificationLibrary, expansionReference.getReference()); - } - } - Parameters params = expansionParams; - bundleEntries.stream().forEach(entry -> { - if (entry.getResource().getResourceType().equals(ResourceType.ValueSet)) { - ValueSet valueSet = (ValueSet) entry.getResource(); - // remove any existing Priority and Conditions - List usageContexts = removeExistingReferenceExtensionData(valueSet.getUseContext()); - valueSet.setUseContext(usageContexts); - Optional maybeVSRelatedArtifact = relatedArtifactsWithPreservedExtension.stream() - .filter(ra -> Canonicals.getUrl(ra.getResource().getReference()) - .equals(valueSet.getUrl())) - .findFirst(); - // If leaf valueset - if (!valueSet.hasCompose() - || (valueSet.hasCompose() - && valueSet.getCompose() - .getIncludeFirstRep() - .getValueSet() - .isEmpty())) { - expandValueSet(valueSet, params, terminologyEndpoint); - // If Condition extension is present - maybeVSRelatedArtifact - .map(ra -> ra.getExtension()) - .ifPresent( - // add Conditions - exts -> { - exts.stream() - .filter(ext -> ext.getUrl() - .equalsIgnoreCase(Constants.VALUE_SET_CONDITION_URL)) - .forEach(ext -> tryAddCondition( - usageContexts, (CodeableConcept) ext.getValue())); - }); - } - // update Priority - UsageContext priority = getOrCreateUsageContext( - usageContexts, KnowledgeArtifactAdapter.usPhContextTypeUrl, Constants.VALUE_SET_PRIORITY_CODE); - Optional ext = - maybeVSRelatedArtifact.map(ra -> ra.getExtensionByUrl(Constants.VALUE_SET_PRIORITY_URL)); - if (ext.isPresent()) { - priority.setValue(ext.get().getValue()); - } else { - CodeableConcept routine = new CodeableConcept( - new Coding(KnowledgeArtifactAdapter.contextUrl, "routine", null)) - .setText("Routine"); - priority.setValue(routine); - } - } - }); - } - - public void expandValueSet( - ValueSet valueSet, Parameters expansionParameters, Optional terminologyEndpoint) { - // Gather the Terminology Service from the valueSet's authoritativeSourceUrl. - Extension authoritativeSource = valueSet.getExtensionByUrl(Constants.AUTHORITATIVE_SOURCE_URL); - String authoritativeSourceUrl = authoritativeSource != null && authoritativeSource.hasValue() - ? authoritativeSource.getValue().primitiveValue() - : valueSet.getUrl(); - - ValueSet expandedValueSet; - if (isVSMAuthoredValueSet(valueSet) && hasSimpleCompose(valueSet)) { - // Perform naive expansion independent of terminology servers. Copy all codes from compose into expansion. - ValueSet.ValueSetExpansionComponent expansion = new ValueSet.ValueSetExpansionComponent(); - expansion.setTimestamp(Date.from(Instant.now())); - - ArrayList expansionParams = new ArrayList<>(); - ValueSet.ValueSetExpansionParameterComponent parameterNaive = - new ValueSet.ValueSetExpansionParameterComponent(); - parameterNaive.setName("naive"); - parameterNaive.setValue(new BooleanType(true)); - expansionParams.add(parameterNaive); - expansion.setParameter(expansionParams); - - for (ValueSet.ConceptSetComponent csc : valueSet.getCompose().getInclude()) { - for (ValueSet.ConceptReferenceComponent crc : csc.getConcept()) { - expansion - .addContains() - .setCode(crc.getCode()) - .setSystem(csc.getSystem()) - .setVersion(csc.getVersion()) - .setDisplay(crc.getDisplay()); - } - } - valueSet.setExpansion(expansion); - } else { - String username; - String apiKey; - if (terminologyEndpoint.isPresent()) { - username = terminologyEndpoint.get().getExtensionsByUrl(Constants.VSAC_USERNAME).stream() - .findFirst() - .map(ext -> ext.getValue().toString()) - .orElseThrow(() -> new UnprocessableEntityException( - "Cannot expand ValueSet without VSAC Username: " + valueSet.getId())); - apiKey = terminologyEndpoint.get().getExtensionsByUrl(Constants.APIKEY).stream() - .findFirst() - .map(ext -> ext.getValue().toString()) - .orElseThrow(() -> new UnprocessableEntityException( - "Cannot expand ValueSet without VSAC API Key: " + valueSet.getId())); - } else { - throw new UnprocessableEntityException( - "Cannot expand ValueSet without credentials: " + valueSet.getId()); - } - - try { - expandedValueSet = terminologyServerClient.expand( - valueSet, authoritativeSourceUrl, expansionParameters, username, apiKey); - valueSet.setExpansion(expandedValueSet.getExpansion()); - } catch (Exception ex) { - throw new UnprocessableEntityException( - "Terminology Server expansion failed for: " + valueSet.getId(), ex.getMessage()); - } - } - } - - protected boolean isVSMAuthoredValueSet(ValueSet valueSet) { - return valueSet.hasMeta() - && valueSet.getMeta().hasTag() - && valueSet.getMeta() - .getTag( - Constants.VSM_WORKFLOW_CODES_CODE_SYSTEM_URL, - Constants.VSM_VALUE_SET_TAG_VSM_AUTHORED_CODE) - != null; - } - - // A simple compose element of a ValueSet must have a compose without an exclude element. Each - // element of the include cannot reference a value set or have a filter, and must have a system - // and enumerate concepts - protected boolean hasSimpleCompose(ValueSet valueSet) { - return valueSet.hasCompose() - && !valueSet.getCompose().hasExclude() - && valueSet.getCompose().getInclude().stream() - .noneMatch( - csc -> csc.hasValueSet() || csc.hasFilter() || !csc.hasSystem() || !csc.hasConcept()); - } - - protected List getRelatedArtifactsWithPreservedExtensions(List deps) { - return deps.stream() - .filter(ra -> Constants.PRESERVED_EXTENSION_URLS.stream().anyMatch(url -> ra.getExtension().stream() - .anyMatch(ext -> ext.getUrl().equalsIgnoreCase(url)))) - .collect(Collectors.toList()); - } - - protected static Library getRootSpecificationLibrary(List bundleEntries) { - Optional rootSpecLibrary = bundleEntries.stream() - .filter(entry -> entry.getResource().getResourceType() == ResourceType.Library) - .map(entry -> ((Library) entry.getResource())) - .filter(entry -> entry.getType().hasCoding(Constants.LIBRARY_TYPE, Constants.ASSET_COLLECTION) - && entry.getUseContext().stream() - .allMatch(useContext -> (useContext - .getCode() - .getSystem() - .equals(KnowledgeArtifactAdapter.usPhContextTypeUrl) - && useContext - .getCode() - .getCode() - .equals("reporting") - && useContext - .getValueCodeableConcept() - .hasCoding(Constants.US_PH_CONTEXT_URL, "triggering")) - || (useContext - .getCode() - .getSystem() - .equals(KnowledgeArtifactAdapter.usPhContextTypeUrl) - && useContext - .getCode() - .getCode() - .equals("specification-type") - && useContext - .getValueCodeableConcept() - .hasCoding(Constants.US_PH_CONTEXT_URL, "program")))) - .findFirst(); - return rootSpecLibrary.orElse(null); - } - - protected static Parameters getExpansionParams(Library rootSpecificationLibrary, String reference) { - Optional expansionParamResource = rootSpecificationLibrary.getContained().stream() - .filter(contained -> contained.getId().equals(reference)) - .findFirst(); - return (Parameters) expansionParamResource.orElse(null); - } - - /** - * Removes any existing UsageContexts corresponding to the VSM specific extensions - * @param usageContexts the list of usage contexts to modify - */ - protected List removeExistingReferenceExtensionData(List usageContexts) { - List useContextCodesToReplace = Collections.unmodifiableList( - Arrays.asList(Constants.VALUE_SET_CONDITION_CODE, Constants.VALUE_SET_PRIORITY_CODE)); - return usageContexts.stream() - // remove any useContexts which need to be replaced - .filter(useContext -> !useContextCodesToReplace.stream() - .anyMatch(code -> useContext.getCode().getCode().equals(code))) - .collect(Collectors.toList()); - } - - protected void tryAddCondition(List usageContexts, CodeableConcept condition) { - boolean focusAlreadyExists = usageContexts.stream() - .anyMatch(u -> u.getCode().getSystem().equals(KnowledgeArtifactAdapter.contextTypeUrl) - && u.getCode().getCode().equals(Constants.VALUE_SET_CONDITION_CODE) - && u.getValueCodeableConcept() - .hasCoding( - condition.getCoding().get(0).getSystem(), - condition.getCoding().get(0).getCode())); - if (!focusAlreadyExists) { - UsageContext newFocus = new UsageContext( - new Coding(KnowledgeArtifactAdapter.contextUrl, Constants.VALUE_SET_CONDITION_CODE, null), - condition); - newFocus.setValue(condition); - usageContexts.add(newFocus); - } - } - /** - * - * Either finds a usageContext with the same system and code or creates an empty one - * and appends it - * - * @param usageContexts the list of usageContexts to search and/or append to - * @param system the usageContext.code.system to find / create - * @param code the usage.code.code to find / create - * @return the found / created usageContext - */ - protected UsageContext getOrCreateUsageContext(List usageContexts, String system, String code) { - return usageContexts.stream() - .filter(useContext -> useContext.getCode().getSystem().equals(system) - && useContext.getCode().getCode().equals(code)) - .findFirst() - .orElseGet(() -> { - // create the UseContext if it doesn't exist - Coding c = new Coding(system, code, null); - UsageContext n = new UsageContext(c, null); - // add it to the ValueSet before returning - usageContexts.add(n); - return n; - }); - } -} diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/dstu3/PackageVisitor.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/dstu3/PackageVisitor.java new file mode 100644 index 000000000..4e01a8e68 --- /dev/null +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/dstu3/PackageVisitor.java @@ -0,0 +1,178 @@ +package org.opencds.cqf.fhir.utility.visitor.dstu3; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import org.hl7.fhir.dstu3.model.BooleanType; +import org.hl7.fhir.dstu3.model.Bundle; +import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent; +import org.hl7.fhir.dstu3.model.Bundle.BundleType; +import org.hl7.fhir.dstu3.model.Library; +import org.hl7.fhir.dstu3.model.MetadataResource; +import org.hl7.fhir.dstu3.model.ResourceType; +import org.hl7.fhir.dstu3.model.StructureDefinition; + +public class PackageVisitor { + // as per http://hl7.org/fhir/dstu3/resource.html#canonical + public static final List canonicalResourceTypes = + // can't use List.of for Android 26 compatibility + Collections.unmodifiableList(new ArrayList(Arrays.asList( + ResourceType.ActivityDefinition, + ResourceType.CapabilityStatement, + ResourceType.CompartmentDefinition, + ResourceType.ConceptMap, + ResourceType.GraphDefinition, + ResourceType.ImplementationGuide, + ResourceType.Library, + ResourceType.Measure, + ResourceType.MessageDefinition, + ResourceType.NamingSystem, + ResourceType.OperationDefinition, + ResourceType.PlanDefinition, + ResourceType.Questionnaire, + ResourceType.SearchParameter, + ResourceType.StructureDefinition, + ResourceType.StructureMap, + ResourceType.TestScript, + ResourceType.ValueSet))); + + public static final List conformanceResourceTypes = + // can't use List.of for Android 26 compatibility + Collections.unmodifiableList(new ArrayList(Arrays.asList( + ResourceType.CapabilityStatement, + ResourceType.StructureDefinition, + ResourceType.ImplementationGuide, + ResourceType.SearchParameter, + ResourceType.MessageDefinition, + ResourceType.OperationDefinition, + ResourceType.CompartmentDefinition, + ResourceType.StructureMap, + ResourceType.GraphDefinition))); + + public static final List knowledgeArtifactResourceTypes = + // can't use List.of for Android 26 compatibility + Collections.unmodifiableList(new ArrayList(Arrays.asList( + ResourceType.Library, + ResourceType.Measure, + ResourceType.ActivityDefinition, + ResourceType.PlanDefinition))); + + public static final List terminologyResourceTypes = + // can't use List.of for Android 26 compatibility + Collections.unmodifiableList(new ArrayList(Arrays.asList( + ResourceType.CodeSystem, + ResourceType.ValueSet, + ResourceType.ConceptMap, + ResourceType.NamingSystem))); + + public static void setCorrectBundleType(Optional count, Optional offset, Bundle bundle) { + // if the bundle is paged then it must be of type = collection and modified to follow bundle.type constraints + // if not, set type = transaction + // special case of count = 0 -> set type = searchset so we can display bundle.total + if (count.isPresent() && count.get() == 0) { + bundle.setType(BundleType.SEARCHSET); + bundle.setTotal(bundle.getEntry().size()); + } else if ((offset.isPresent() && offset.get() > 0) + || (count.isPresent() && count.get() < bundle.getEntry().size())) { + bundle.setType(BundleType.COLLECTION); + List removedRequest = bundle.getEntry().stream() + .map(entry -> { + entry.setRequest(null); + return entry; + }) + .collect(Collectors.toList()); + bundle.setEntry(removedRequest); + } else { + bundle.setType(BundleType.TRANSACTION); + } + } + + public static List findUnsupportedInclude( + List entries, List include) { + if (include == null + || include.isEmpty() + || include.stream().anyMatch((includedType) -> includedType.equals("all"))) { + return entries; + } + List filteredList = new ArrayList<>(); + entries.stream().forEach(entry -> { + if (include.stream().anyMatch((type) -> type.equals("knowledge"))) { + Boolean resourceIsKnowledgeType = knowledgeArtifactResourceTypes.contains( + entry.getResource().getResourceType()); + if (resourceIsKnowledgeType) { + filteredList.add(entry); + } + } + if (include.stream().anyMatch((type) -> type.equals("canonical"))) { + Boolean resourceIsCanonicalType = + canonicalResourceTypes.contains(entry.getResource().getResourceType()); + if (resourceIsCanonicalType) { + filteredList.add(entry); + } + } + if (include.stream().anyMatch((type) -> type.equals("terminology"))) { + Boolean resourceIsTerminologyType = + terminologyResourceTypes.contains(entry.getResource().getResourceType()); + if (resourceIsTerminologyType) { + filteredList.add(entry); + } + } + if (include.stream().anyMatch((type) -> type.equals("conformance"))) { + Boolean resourceIsConformanceType = + conformanceResourceTypes.contains(entry.getResource().getResourceType()); + if (resourceIsConformanceType) { + filteredList.add(entry); + } + } + if (include.stream().anyMatch((type) -> type.equals("extensions")) + && entry.getResource().getResourceType().equals(ResourceType.StructureDefinition) + && ((StructureDefinition) entry.getResource()).getType().equals("Extension")) { + filteredList.add(entry); + } + if (include.stream().anyMatch((type) -> type.equals("profiles")) + && entry.getResource().getResourceType().equals(ResourceType.StructureDefinition) + && !((StructureDefinition) entry.getResource()).getType().equals("Extension")) { + filteredList.add(entry); + } + if (include.stream().anyMatch((type) -> type.equals("tests"))) { + if (entry.getResource().getResourceType().equals(ResourceType.Library) + && ((Library) entry.getResource()) + .getType().getCoding().stream() + .anyMatch(coding -> coding.getCode().equals("test-case"))) { + filteredList.add(entry); + } else if (((MetadataResource) entry.getResource()) + .getExtension().stream() + .anyMatch(ext -> ext.getUrl().contains("isTestCase") + && ((BooleanType) ext.getValue()).getValue())) { + filteredList.add(entry); + } + } + if (include.stream().anyMatch((type) -> type.equals("examples"))) { + // TODO: idk if this is legit just a placeholder for now + if (((MetadataResource) entry.getResource()) + .getExtension().stream() + .anyMatch(ext -> ext.getUrl().contains("isExample") + && ((BooleanType) ext.getValue()).getValue())) { + filteredList.add(entry); + } + } + }); + List distinctFilteredEntries = new ArrayList<>(); + // remove duplicates + for (BundleEntryComponent entry : filteredList) { + if (!distinctFilteredEntries.stream() + .map((e) -> ((MetadataResource) e.getResource())) + .anyMatch(existingEntry -> + existingEntry.getUrl().equals(((MetadataResource) entry.getResource()).getUrl()) + && existingEntry + .getVersion() + .equals(((MetadataResource) entry.getResource()).getVersion()))) { + distinctFilteredEntries.add(entry); + } + } + return distinctFilteredEntries; + } +} diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/dstu3/KnowledgeArtifactReleaseVisitor.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/dstu3/ReleaseVisitor.java similarity index 98% rename from cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/dstu3/KnowledgeArtifactReleaseVisitor.java rename to cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/dstu3/ReleaseVisitor.java index 944a91ee3..ccaf26047 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/dstu3/KnowledgeArtifactReleaseVisitor.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/dstu3/ReleaseVisitor.java @@ -30,7 +30,7 @@ import org.opencds.cqf.fhir.utility.dstu3.CRMIReleaseVersionBehavior.CRMIReleaseVersionBehaviorCodes; import org.slf4j.Logger; -public class KnowledgeArtifactReleaseVisitor { +public class ReleaseVisitor { public static void checkNonExperimental( MetadataResource resource, @@ -65,7 +65,7 @@ public static void checkNonExperimental( } } - public static void propagageEffectivePeriod(Period rootEffectivePeriod, KnowledgeArtifactAdapter artifactAdapter) { + public static void propagateEffectivePeriod(Period rootEffectivePeriod, KnowledgeArtifactAdapter artifactAdapter) { Period effectivePeriod = (Period) artifactAdapter.getEffectivePeriod(); // if the root artifact period is NOT null AND HAS a start or an end date if ((rootEffectivePeriod != null && (rootEffectivePeriod.hasStart() || rootEffectivePeriod.hasEnd())) diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r4/KnowledgeArtifactApproveVisitor.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r4/ApproveVisitor.java similarity index 97% rename from cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r4/KnowledgeArtifactApproveVisitor.java rename to cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r4/ApproveVisitor.java index a8d6bd35b..4a7a80d7a 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r4/KnowledgeArtifactApproveVisitor.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r4/ApproveVisitor.java @@ -10,7 +10,7 @@ import org.hl7.fhir.r4.model.Reference; import org.opencds.cqf.fhir.utility.r4.ArtifactAssessment; -public class KnowledgeArtifactApproveVisitor { +public class ApproveVisitor { public static ArtifactAssessment createApprovalAssessment( IIdType id, diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r4/KnowledgeArtifactDraftVisitor.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r4/DraftVisitor.java similarity index 98% rename from cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r4/KnowledgeArtifactDraftVisitor.java rename to cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r4/DraftVisitor.java index ec9274f97..2b86d1c51 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r4/KnowledgeArtifactDraftVisitor.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r4/DraftVisitor.java @@ -12,7 +12,7 @@ import org.opencds.cqf.fhir.api.Repository; import org.opencds.cqf.fhir.utility.SearchHelper; -public class KnowledgeArtifactDraftVisitor { +public class DraftVisitor { public static Optional processReferencedResourceForDraft( Repository repository, RelatedArtifact ra, String version) { diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r4/KnowledgeArtifactPackageVisitor.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r4/KnowledgeArtifactPackageVisitor.java deleted file mode 100644 index 982cd65fc..000000000 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r4/KnowledgeArtifactPackageVisitor.java +++ /dev/null @@ -1,477 +0,0 @@ -package org.opencds.cqf.fhir.utility.visitor.r4; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; -import java.time.Instant; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Date; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; -import org.hl7.fhir.r4.model.BooleanType; -import org.hl7.fhir.r4.model.Bundle; -import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent; -import org.hl7.fhir.r4.model.Bundle.BundleType; -import org.hl7.fhir.r4.model.CodeableConcept; -import org.hl7.fhir.r4.model.Coding; -import org.hl7.fhir.r4.model.Endpoint; -import org.hl7.fhir.r4.model.Extension; -import org.hl7.fhir.r4.model.Library; -import org.hl7.fhir.r4.model.MetadataResource; -import org.hl7.fhir.r4.model.Parameters; -import org.hl7.fhir.r4.model.Reference; -import org.hl7.fhir.r4.model.RelatedArtifact; -import org.hl7.fhir.r4.model.Resource; -import org.hl7.fhir.r4.model.ResourceType; -import org.hl7.fhir.r4.model.StructureDefinition; -import org.hl7.fhir.r4.model.UsageContext; -import org.hl7.fhir.r4.model.ValueSet; -import org.opencds.cqf.fhir.api.Repository; -import org.opencds.cqf.fhir.utility.Canonicals; -import org.opencds.cqf.fhir.utility.Constants; -import org.opencds.cqf.fhir.utility.adapter.AdapterFactory; -import org.opencds.cqf.fhir.utility.adapter.KnowledgeArtifactAdapter; -import org.opencds.cqf.fhir.utility.client.TerminologyServerClient; - -public class KnowledgeArtifactPackageVisitor { - - public KnowledgeArtifactPackageVisitor() { - this.terminologyServerClient = new TerminologyServerClient(FhirContext.forR4()); - } - - private TerminologyServerClient terminologyServerClient; - - // as per http://hl7.org/fhir/R4/resource.html#canonical - public static final List canonicalResourceTypes = - // can't use List.of for Android 26 compatibility - Collections.unmodifiableList(new ArrayList(Arrays.asList( - ResourceType.ActivityDefinition, - ResourceType.CapabilityStatement, - ResourceType.ChargeItemDefinition, - ResourceType.CompartmentDefinition, - ResourceType.ConceptMap, - ResourceType.EffectEvidenceSynthesis, - ResourceType.EventDefinition, - ResourceType.Evidence, - ResourceType.EvidenceVariable, - ResourceType.ExampleScenario, - ResourceType.GraphDefinition, - ResourceType.ImplementationGuide, - ResourceType.Library, - ResourceType.Measure, - ResourceType.MessageDefinition, - ResourceType.NamingSystem, - ResourceType.OperationDefinition, - ResourceType.PlanDefinition, - ResourceType.Questionnaire, - ResourceType.ResearchDefinition, - ResourceType.ResearchElementDefinition, - ResourceType.RiskEvidenceSynthesis, - ResourceType.SearchParameter, - ResourceType.StructureDefinition, - ResourceType.StructureMap, - ResourceType.TerminologyCapabilities, - ResourceType.TestScript, - ResourceType.ValueSet))); - - public static final List conformanceResourceTypes = - // can't use List.of for Android 26 compatibility - Collections.unmodifiableList(new ArrayList(Arrays.asList( - ResourceType.CapabilityStatement, - ResourceType.StructureDefinition, - ResourceType.ImplementationGuide, - ResourceType.SearchParameter, - ResourceType.MessageDefinition, - ResourceType.OperationDefinition, - ResourceType.CompartmentDefinition, - ResourceType.StructureMap, - ResourceType.GraphDefinition, - ResourceType.ExampleScenario))); - - public static final List knowledgeArtifactResourceTypes = - // can't use List.of for Android 26 compatibility - Collections.unmodifiableList(new ArrayList(Arrays.asList( - ResourceType.Library, - ResourceType.Measure, - ResourceType.ActivityDefinition, - ResourceType.PlanDefinition))); - - public static final List terminologyResourceTypes = - // can't use List.of for Android 26 compatibility - Collections.unmodifiableList(new ArrayList(Arrays.asList( - ResourceType.CodeSystem, - ResourceType.ValueSet, - ResourceType.ConceptMap, - ResourceType.NamingSystem, - ResourceType.TerminologyCapabilities))); - - public static void setCorrectBundleType(Optional count, Optional offset, Bundle bundle) { - // if the bundle is paged then it must be of type = collection and modified to follow bundle.type constraints - // if not, set type = transaction - // special case of count = 0 -> set type = searchset so we can display bundle.total - if (count.isPresent() && count.get() == 0) { - bundle.setType(BundleType.SEARCHSET); - bundle.setTotal(bundle.getEntry().size()); - } else if ((offset.isPresent() && offset.get() > 0) - || (count.isPresent() && count.get() < bundle.getEntry().size())) { - bundle.setType(BundleType.COLLECTION); - List removedRequest = bundle.getEntry().stream() - .map(entry -> { - entry.setRequest(null); - return entry; - }) - .collect(Collectors.toList()); - bundle.setEntry(removedRequest); - } else { - bundle.setType(BundleType.TRANSACTION); - } - } - - public static List findUnsupportedInclude( - List entries, List include) { - if (include == null - || include.isEmpty() - || include.stream().anyMatch((includedType) -> includedType.equals("all"))) { - return entries; - } - List filteredList = new ArrayList<>(); - entries.stream().forEach(entry -> { - if (include.stream().anyMatch((type) -> type.equals("knowledge"))) { - Boolean resourceIsKnowledgeType = knowledgeArtifactResourceTypes.contains( - entry.getResource().getResourceType()); - if (resourceIsKnowledgeType) { - filteredList.add(entry); - } - } - if (include.stream().anyMatch((type) -> type.equals("canonical"))) { - Boolean resourceIsCanonicalType = - canonicalResourceTypes.contains(entry.getResource().getResourceType()); - if (resourceIsCanonicalType) { - filteredList.add(entry); - } - } - if (include.stream().anyMatch((type) -> type.equals("terminology"))) { - Boolean resourceIsTerminologyType = - terminologyResourceTypes.contains(entry.getResource().getResourceType()); - if (resourceIsTerminologyType) { - filteredList.add(entry); - } - } - if (include.stream().anyMatch((type) -> type.equals("conformance"))) { - Boolean resourceIsConformanceType = - conformanceResourceTypes.contains(entry.getResource().getResourceType()); - if (resourceIsConformanceType) { - filteredList.add(entry); - } - } - if (include.stream().anyMatch((type) -> type.equals("extensions")) - && entry.getResource().getResourceType().equals(ResourceType.StructureDefinition) - && ((StructureDefinition) entry.getResource()).getType().equals("Extension")) { - filteredList.add(entry); - } - if (include.stream().anyMatch((type) -> type.equals("profiles")) - && entry.getResource().getResourceType().equals(ResourceType.StructureDefinition) - && !((StructureDefinition) entry.getResource()).getType().equals("Extension")) { - filteredList.add(entry); - } - if (include.stream().anyMatch((type) -> type.equals("tests"))) { - if (entry.getResource().getResourceType().equals(ResourceType.Library) - && ((Library) entry.getResource()) - .getType().getCoding().stream() - .anyMatch(coding -> coding.getCode().equals("test-case"))) { - filteredList.add(entry); - } else if (((MetadataResource) entry.getResource()) - .getExtension().stream() - .anyMatch(ext -> ext.getUrl().contains("isTestCase") - && ((BooleanType) ext.getValue()).getValue())) { - filteredList.add(entry); - } - } - if (include.stream().anyMatch((type) -> type.equals("examples"))) { - // TODO: idk if this is legit just a placeholder for now - if (((MetadataResource) entry.getResource()) - .getExtension().stream() - .anyMatch(ext -> ext.getUrl().contains("isExample") - && ((BooleanType) ext.getValue()).getValue())) { - filteredList.add(entry); - } - } - }); - List distinctFilteredEntries = new ArrayList<>(); - // remove duplicates - for (BundleEntryComponent entry : filteredList) { - if (!distinctFilteredEntries.stream() - .map((e) -> ((MetadataResource) e.getResource())) - .anyMatch(existingEntry -> - existingEntry.getUrl().equals(((MetadataResource) entry.getResource()).getUrl()) - && existingEntry - .getVersion() - .equals(((MetadataResource) entry.getResource()).getVersion()))) { - distinctFilteredEntries.add(entry); - } - } - return distinctFilteredEntries; - } - - /** - * ValueSets can be part of multiple artifacts at the same time. Certain properties are tracked/managed in the manifest to avoid conflicts with other artifacts. This function sets those properties on the ValueSets themselves at export / $package time - * @param manifest the resource containing all RelatedArtifact references - * @param bundleEntries the list of packaged resources to modify according to the extensions on the manifest relatedArtifact references - */ - public void handleValueSetReferenceExtensions( - MetadataResource manifest, - List bundleEntries, - Repository repository, - Optional terminologyEndpoint) - throws UnprocessableEntityException, IllegalArgumentException { - KnowledgeArtifactAdapter adapter = AdapterFactory.forFhirVersion(manifest.getStructureFhirVersionEnum()) - .createKnowledgeArtifactAdapter(manifest); - List relatedArtifactsWithPreservedExtension = - getRelatedArtifactsWithPreservedExtensions(adapter.getRelatedArtifact()); - Parameters expansionParams = new Parameters(); - Library rootSpecificationLibrary = getRootSpecificationLibrary(bundleEntries); - if (rootSpecificationLibrary != null) { - Extension expansionParamsExtension = - rootSpecificationLibrary.getExtensionByUrl(Constants.EXPANSION_PARAMETERS_URL); - if (expansionParamsExtension != null && expansionParamsExtension.hasValue()) { - Reference expansionReference = (Reference) expansionParamsExtension.getValue(); - expansionParams = getExpansionParams(rootSpecificationLibrary, expansionReference.getReference()); - } - } - Parameters params = expansionParams; - bundleEntries.stream().forEach(entry -> { - if (entry.getResource().getResourceType().equals(ResourceType.ValueSet)) { - ValueSet valueSet = (ValueSet) entry.getResource(); - // remove any existing Priority and Conditions - List usageContexts = removeExistingReferenceExtensionData(valueSet.getUseContext()); - valueSet.setUseContext(usageContexts); - Optional maybeVSRelatedArtifact = relatedArtifactsWithPreservedExtension.stream() - .filter(ra -> Canonicals.getUrl(ra.getResource()).equals(valueSet.getUrl())) - .findFirst(); - // If leaf valueset - if (!valueSet.hasCompose() - || (valueSet.hasCompose() - && valueSet.getCompose() - .getIncludeFirstRep() - .getValueSet() - .isEmpty())) { - expandValueSet(valueSet, params, terminologyEndpoint); - // If Condition extension is present - maybeVSRelatedArtifact - .map(ra -> ra.getExtension()) - .ifPresent( - // add Conditions - exts -> { - exts.stream() - .filter(ext -> ext.getUrl() - .equalsIgnoreCase(Constants.VALUE_SET_CONDITION_URL)) - .forEach(ext -> tryAddCondition( - usageContexts, (CodeableConcept) ext.getValue())); - }); - } - // update Priority - UsageContext priority = getOrCreateUsageContext( - usageContexts, KnowledgeArtifactAdapter.usPhContextTypeUrl, Constants.VALUE_SET_PRIORITY_CODE); - Optional ext = - maybeVSRelatedArtifact.map(ra -> ra.getExtensionByUrl(Constants.VALUE_SET_PRIORITY_URL)); - - if (ext.isPresent()) { - priority.setValue(ext.get().getValue()); - } else { - CodeableConcept routine = new CodeableConcept( - new Coding(KnowledgeArtifactAdapter.contextUrl, "routine", null)) - .setText("Routine"); - priority.setValue(routine); - } - } - }); - } - - public void expandValueSet( - ValueSet valueSet, Parameters expansionParameters, Optional terminologyEndpoint) { - // Gather the Terminology Service from the valueSet's authoritativeSourceUrl. - Extension authoritativeSource = valueSet.getExtensionByUrl(Constants.AUTHORITATIVE_SOURCE_URL); - String authoritativeSourceUrl = authoritativeSource != null && authoritativeSource.hasValue() - ? authoritativeSource.getValue().primitiveValue() - : valueSet.getUrl(); - - ValueSet expandedValueSet; - if (isVSMAuthoredValueSet(valueSet) && hasSimpleCompose(valueSet)) { - // Perform naive expansion independent of terminology servers. Copy all codes from compose into expansion. - ValueSet.ValueSetExpansionComponent expansion = new ValueSet.ValueSetExpansionComponent(); - expansion.setTimestamp(Date.from(Instant.now())); - - ArrayList expansionParams = new ArrayList<>(); - ValueSet.ValueSetExpansionParameterComponent parameterNaive = - new ValueSet.ValueSetExpansionParameterComponent(); - parameterNaive.setName("naive"); - parameterNaive.setValue(new BooleanType(true)); - expansionParams.add(parameterNaive); - expansion.setParameter(expansionParams); - - for (ValueSet.ConceptSetComponent csc : valueSet.getCompose().getInclude()) { - for (ValueSet.ConceptReferenceComponent crc : csc.getConcept()) { - expansion - .addContains() - .setCode(crc.getCode()) - .setSystem(csc.getSystem()) - .setVersion(csc.getVersion()) - .setDisplay(crc.getDisplay()); - } - } - valueSet.setExpansion(expansion); - } else { - String username; - String apiKey; - if (terminologyEndpoint.isPresent()) { - username = terminologyEndpoint.get().getExtensionsByUrl(Constants.VSAC_USERNAME).stream() - .findFirst() - .map(ext -> ext.getValue().toString()) - .orElseThrow(() -> new UnprocessableEntityException( - "Cannot expand ValueSet without VSAC Username: " + valueSet.getId())); - apiKey = terminologyEndpoint.get().getExtensionsByUrl(Constants.APIKEY).stream() - .findFirst() - .map(ext -> ext.getValue().toString()) - .orElseThrow(() -> new UnprocessableEntityException( - "Cannot expand ValueSet without VSAC API Key: " + valueSet.getId())); - } else { - throw new UnprocessableEntityException( - "Cannot expand ValueSet without credentials: " + valueSet.getId()); - } - - try { - expandedValueSet = terminologyServerClient.expand( - valueSet, authoritativeSourceUrl, expansionParameters, username, apiKey); - valueSet.setExpansion(expandedValueSet.getExpansion()); - } catch (Exception ex) { - throw new UnprocessableEntityException( - "Terminology Server expansion failed for: " + valueSet.getId(), ex.getMessage()); - } - } - } - - protected boolean isVSMAuthoredValueSet(ValueSet valueSet) { - return valueSet.hasMeta() - && valueSet.getMeta().hasTag() - && valueSet.getMeta() - .getTag( - Constants.VSM_WORKFLOW_CODES_CODE_SYSTEM_URL, - Constants.VSM_VALUE_SET_TAG_VSM_AUTHORED_CODE) - != null; - } - - // A simple compose element of a ValueSet must have a compose without an exclude element. Each - // element of the include cannot reference a value set or have a filter, and must have a system - // and enumerate concepts - protected boolean hasSimpleCompose(ValueSet valueSet) { - return valueSet.hasCompose() - && !valueSet.getCompose().hasExclude() - && valueSet.getCompose().getInclude().stream() - .noneMatch( - csc -> csc.hasValueSet() || csc.hasFilter() || !csc.hasSystem() || !csc.hasConcept()); - } - - protected List getRelatedArtifactsWithPreservedExtensions(List deps) { - return deps.stream() - .filter(ra -> Constants.PRESERVED_EXTENSION_URLS.stream().anyMatch(url -> ra.getExtension().stream() - .anyMatch(ext -> ext.getUrl().equalsIgnoreCase(url)))) - .collect(Collectors.toList()); - } - - protected static Library getRootSpecificationLibrary(List bundleEntries) { - Optional rootSpecLibrary = bundleEntries.stream() - .filter(entry -> entry.getResource().getResourceType() == ResourceType.Library) - .map(entry -> ((Library) entry.getResource())) - .filter(entry -> entry.getType().hasCoding(Constants.LIBRARY_TYPE, Constants.ASSET_COLLECTION) - && entry.getUseContext().stream() - .allMatch(useContext -> (useContext - .getCode() - .getSystem() - .equals(KnowledgeArtifactAdapter.usPhContextTypeUrl) - && useContext - .getCode() - .getCode() - .equals("reporting") - && useContext - .getValueCodeableConcept() - .hasCoding(Constants.US_PH_CONTEXT_URL, "triggering")) - || (useContext - .getCode() - .getSystem() - .equals(KnowledgeArtifactAdapter.usPhContextTypeUrl) - && useContext - .getCode() - .getCode() - .equals("specification-type") - && useContext - .getValueCodeableConcept() - .hasCoding(Constants.US_PH_CONTEXT_URL, "program")))) - .findFirst(); - return rootSpecLibrary.orElse(null); - } - - protected static Parameters getExpansionParams(Library rootSpecificationLibrary, String reference) { - Optional expansionParamResource = rootSpecificationLibrary.getContained().stream() - .filter(contained -> contained.getId().equals(reference)) - .findFirst(); - return (Parameters) expansionParamResource.orElse(null); - } - - /** - * Removes any existing UsageContexts corresponding to the VSM specific extensions - * @param usageContexts the list of usage contexts to modify - */ - protected List removeExistingReferenceExtensionData(List usageContexts) { - List useContextCodesToReplace = Collections.unmodifiableList( - Arrays.asList(Constants.VALUE_SET_CONDITION_CODE, Constants.VALUE_SET_PRIORITY_CODE)); - return usageContexts.stream() - // remove any useContexts which need to be replaced - .filter(useContext -> !useContextCodesToReplace.stream() - .anyMatch(code -> useContext.getCode().getCode().equals(code))) - .collect(Collectors.toList()); - } - - protected void tryAddCondition(List usageContexts, CodeableConcept condition) { - boolean focusAlreadyExists = usageContexts.stream() - .anyMatch(u -> u.getCode().getSystem().equals(KnowledgeArtifactAdapter.contextTypeUrl) - && u.getCode().getCode().equals(Constants.VALUE_SET_CONDITION_CODE) - && u.getValueCodeableConcept() - .hasCoding( - condition.getCoding().get(0).getSystem(), - condition.getCoding().get(0).getCode())); - if (!focusAlreadyExists) { - UsageContext newFocus = new UsageContext( - new Coding(KnowledgeArtifactAdapter.contextUrl, Constants.VALUE_SET_CONDITION_CODE, null), - condition); - newFocus.setValue(condition); - usageContexts.add(newFocus); - } - } - - /** - * - * Either finds a usageContext with the same system and code or creates an empty one - * and appends it - * - * @param usageContexts the list of usageContexts to search and/or append to - * @param system the usageContext.code.system to find / create - * @param code the usage.code.code to find / create - * @return the found / created usageContext - */ - protected UsageContext getOrCreateUsageContext(List usageContexts, String system, String code) { - return usageContexts.stream() - .filter(useContext -> useContext.getCode().getSystem().equals(system) - && useContext.getCode().getCode().equals(code)) - .findFirst() - .orElseGet(() -> { - // create the UseContext if it doesn't exist - Coding c = new Coding(system, code, null); - UsageContext n = new UsageContext(c, null); - // add it to the ValueSet before returning - usageContexts.add(n); - return n; - }); - } -} diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r4/PackageVisitor.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r4/PackageVisitor.java new file mode 100644 index 000000000..5afdc19fd --- /dev/null +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r4/PackageVisitor.java @@ -0,0 +1,190 @@ +package org.opencds.cqf.fhir.utility.visitor.r4; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import org.hl7.fhir.r4.model.BooleanType; +import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent; +import org.hl7.fhir.r4.model.Bundle.BundleType; +import org.hl7.fhir.r4.model.Library; +import org.hl7.fhir.r4.model.MetadataResource; +import org.hl7.fhir.r4.model.ResourceType; +import org.hl7.fhir.r4.model.StructureDefinition; + +public class PackageVisitor { + // as per http://hl7.org/fhir/R4/resource.html#canonical + public static final List canonicalResourceTypes = + // can't use List.of for Android 26 compatibility + Collections.unmodifiableList(new ArrayList(Arrays.asList( + ResourceType.ActivityDefinition, + ResourceType.CapabilityStatement, + ResourceType.ChargeItemDefinition, + ResourceType.CompartmentDefinition, + ResourceType.ConceptMap, + ResourceType.EffectEvidenceSynthesis, + ResourceType.EventDefinition, + ResourceType.Evidence, + ResourceType.EvidenceVariable, + ResourceType.ExampleScenario, + ResourceType.GraphDefinition, + ResourceType.ImplementationGuide, + ResourceType.Library, + ResourceType.Measure, + ResourceType.MessageDefinition, + ResourceType.NamingSystem, + ResourceType.OperationDefinition, + ResourceType.PlanDefinition, + ResourceType.Questionnaire, + ResourceType.ResearchDefinition, + ResourceType.ResearchElementDefinition, + ResourceType.RiskEvidenceSynthesis, + ResourceType.SearchParameter, + ResourceType.StructureDefinition, + ResourceType.StructureMap, + ResourceType.TerminologyCapabilities, + ResourceType.TestScript, + ResourceType.ValueSet))); + + public static final List conformanceResourceTypes = + // can't use List.of for Android 26 compatibility + Collections.unmodifiableList(new ArrayList(Arrays.asList( + ResourceType.CapabilityStatement, + ResourceType.StructureDefinition, + ResourceType.ImplementationGuide, + ResourceType.SearchParameter, + ResourceType.MessageDefinition, + ResourceType.OperationDefinition, + ResourceType.CompartmentDefinition, + ResourceType.StructureMap, + ResourceType.GraphDefinition, + ResourceType.ExampleScenario))); + + public static final List knowledgeArtifactResourceTypes = + // can't use List.of for Android 26 compatibility + Collections.unmodifiableList(new ArrayList(Arrays.asList( + ResourceType.Library, + ResourceType.Measure, + ResourceType.ActivityDefinition, + ResourceType.PlanDefinition))); + + public static final List terminologyResourceTypes = + // can't use List.of for Android 26 compatibility + Collections.unmodifiableList(new ArrayList(Arrays.asList( + ResourceType.CodeSystem, + ResourceType.ValueSet, + ResourceType.ConceptMap, + ResourceType.NamingSystem, + ResourceType.TerminologyCapabilities))); + + public static void setCorrectBundleType(Optional count, Optional offset, Bundle bundle) { + // if the bundle is paged then it must be of type = collection and modified to follow bundle.type constraints + // if not, set type = transaction + // special case of count = 0 -> set type = searchset so we can display bundle.total + if (count.isPresent() && count.get() == 0) { + bundle.setType(BundleType.SEARCHSET); + bundle.setTotal(bundle.getEntry().size()); + } else if ((offset.isPresent() && offset.get() > 0) + || (count.isPresent() && count.get() < bundle.getEntry().size())) { + bundle.setType(BundleType.COLLECTION); + List removedRequest = bundle.getEntry().stream() + .map(entry -> { + entry.setRequest(null); + return entry; + }) + .collect(Collectors.toList()); + bundle.setEntry(removedRequest); + } else { + bundle.setType(BundleType.TRANSACTION); + } + } + + public static List findUnsupportedInclude( + List entries, List include) { + if (include == null + || include.isEmpty() + || include.stream().anyMatch((includedType) -> includedType.equals("all"))) { + return entries; + } + List filteredList = new ArrayList<>(); + entries.stream().forEach(entry -> { + if (include.stream().anyMatch((type) -> type.equals("knowledge"))) { + Boolean resourceIsKnowledgeType = knowledgeArtifactResourceTypes.contains( + entry.getResource().getResourceType()); + if (resourceIsKnowledgeType) { + filteredList.add(entry); + } + } + if (include.stream().anyMatch((type) -> type.equals("canonical"))) { + Boolean resourceIsCanonicalType = + canonicalResourceTypes.contains(entry.getResource().getResourceType()); + if (resourceIsCanonicalType) { + filteredList.add(entry); + } + } + if (include.stream().anyMatch((type) -> type.equals("terminology"))) { + Boolean resourceIsTerminologyType = + terminologyResourceTypes.contains(entry.getResource().getResourceType()); + if (resourceIsTerminologyType) { + filteredList.add(entry); + } + } + if (include.stream().anyMatch((type) -> type.equals("conformance"))) { + Boolean resourceIsConformanceType = + conformanceResourceTypes.contains(entry.getResource().getResourceType()); + if (resourceIsConformanceType) { + filteredList.add(entry); + } + } + if (include.stream().anyMatch((type) -> type.equals("extensions")) + && entry.getResource().getResourceType().equals(ResourceType.StructureDefinition) + && ((StructureDefinition) entry.getResource()).getType().equals("Extension")) { + filteredList.add(entry); + } + if (include.stream().anyMatch((type) -> type.equals("profiles")) + && entry.getResource().getResourceType().equals(ResourceType.StructureDefinition) + && !((StructureDefinition) entry.getResource()).getType().equals("Extension")) { + filteredList.add(entry); + } + if (include.stream().anyMatch((type) -> type.equals("tests"))) { + if (entry.getResource().getResourceType().equals(ResourceType.Library) + && ((Library) entry.getResource()) + .getType().getCoding().stream() + .anyMatch(coding -> coding.getCode().equals("test-case"))) { + filteredList.add(entry); + } else if (((MetadataResource) entry.getResource()) + .getExtension().stream() + .anyMatch(ext -> ext.getUrl().contains("isTestCase") + && ((BooleanType) ext.getValue()).getValue())) { + filteredList.add(entry); + } + } + if (include.stream().anyMatch((type) -> type.equals("examples"))) { + // TODO: idk if this is legit just a placeholder for now + if (((MetadataResource) entry.getResource()) + .getExtension().stream() + .anyMatch(ext -> ext.getUrl().contains("isExample") + && ((BooleanType) ext.getValue()).getValue())) { + filteredList.add(entry); + } + } + }); + List distinctFilteredEntries = new ArrayList<>(); + // remove duplicates + for (BundleEntryComponent entry : filteredList) { + if (!distinctFilteredEntries.stream() + .map((e) -> ((MetadataResource) e.getResource())) + .anyMatch(existingEntry -> + existingEntry.getUrl().equals(((MetadataResource) entry.getResource()).getUrl()) + && existingEntry + .getVersion() + .equals(((MetadataResource) entry.getResource()).getVersion()))) { + distinctFilteredEntries.add(entry); + } + } + return distinctFilteredEntries; + } +} diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r4/KnowledgeArtifactReleaseVisitor.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r4/ReleaseVisitor.java similarity index 98% rename from cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r4/KnowledgeArtifactReleaseVisitor.java rename to cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r4/ReleaseVisitor.java index 4771cd6d9..a1bd7ffd8 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r4/KnowledgeArtifactReleaseVisitor.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r4/ReleaseVisitor.java @@ -30,7 +30,7 @@ import org.opencds.cqf.fhir.utility.r4.CRMIReleaseVersionBehavior.CRMIReleaseVersionBehaviorCodes; import org.slf4j.Logger; -public class KnowledgeArtifactReleaseVisitor { +public class ReleaseVisitor { public static void checkNonExperimental( MetadataResource resource, @@ -65,7 +65,7 @@ public static void checkNonExperimental( } } - public static void propagageEffectivePeriod(Period rootEffectivePeriod, KnowledgeArtifactAdapter artifactAdapter) { + public static void propagateEffectivePeriod(Period rootEffectivePeriod, KnowledgeArtifactAdapter artifactAdapter) { Period effectivePeriod = (Period) artifactAdapter.getEffectivePeriod(); // if the root artifact period is NOT null AND HAS a start or an end date if ((rootEffectivePeriod != null && (rootEffectivePeriod.hasStart() || rootEffectivePeriod.hasEnd())) diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r5/KnowledgeArtifactApproveVisitor.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r5/ApproveVisitor.java similarity index 98% rename from cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r5/KnowledgeArtifactApproveVisitor.java rename to cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r5/ApproveVisitor.java index 119f15450..b425a4c9e 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r5/KnowledgeArtifactApproveVisitor.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r5/ApproveVisitor.java @@ -12,7 +12,7 @@ import org.hl7.fhir.r5.model.Reference; import org.hl7.fhir.r5.model.RelatedArtifact.RelatedArtifactType; -public class KnowledgeArtifactApproveVisitor { +public class ApproveVisitor { public static ArtifactAssessment createApprovalAssessment( IIdType id, diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r5/KnowledgeArtifactDraftVisitor.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r5/DraftVisitor.java similarity index 98% rename from cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r5/KnowledgeArtifactDraftVisitor.java rename to cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r5/DraftVisitor.java index 077939263..5ee6210ea 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r5/KnowledgeArtifactDraftVisitor.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r5/DraftVisitor.java @@ -11,7 +11,7 @@ import org.opencds.cqf.fhir.api.Repository; import org.opencds.cqf.fhir.utility.SearchHelper; -public class KnowledgeArtifactDraftVisitor { +public class DraftVisitor { public static Optional processReferencedResourceForDraft( Repository repository, RelatedArtifact ra, String version) { diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r5/KnowledgeArtifactPackageVisitor.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r5/KnowledgeArtifactPackageVisitor.java deleted file mode 100644 index dfcf3a1da..000000000 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r5/KnowledgeArtifactPackageVisitor.java +++ /dev/null @@ -1,472 +0,0 @@ -package org.opencds.cqf.fhir.utility.visitor.r5; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; -import java.time.Instant; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Date; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; -import org.hl7.fhir.r5.model.BooleanType; -import org.hl7.fhir.r5.model.Bundle; -import org.hl7.fhir.r5.model.Bundle.BundleEntryComponent; -import org.hl7.fhir.r5.model.Bundle.BundleType; -import org.hl7.fhir.r5.model.CodeableConcept; -import org.hl7.fhir.r5.model.Coding; -import org.hl7.fhir.r5.model.Endpoint; -import org.hl7.fhir.r5.model.Extension; -import org.hl7.fhir.r5.model.Library; -import org.hl7.fhir.r5.model.MetadataResource; -import org.hl7.fhir.r5.model.Parameters; -import org.hl7.fhir.r5.model.Reference; -import org.hl7.fhir.r5.model.RelatedArtifact; -import org.hl7.fhir.r5.model.Resource; -import org.hl7.fhir.r5.model.ResourceType; -import org.hl7.fhir.r5.model.StructureDefinition; -import org.hl7.fhir.r5.model.UsageContext; -import org.hl7.fhir.r5.model.ValueSet; -import org.opencds.cqf.fhir.api.Repository; -import org.opencds.cqf.fhir.utility.Canonicals; -import org.opencds.cqf.fhir.utility.Constants; -import org.opencds.cqf.fhir.utility.adapter.AdapterFactory; -import org.opencds.cqf.fhir.utility.adapter.KnowledgeArtifactAdapter; -import org.opencds.cqf.fhir.utility.client.TerminologyServerClient; - -public class KnowledgeArtifactPackageVisitor { - - public KnowledgeArtifactPackageVisitor() { - this.terminologyServerClient = new TerminologyServerClient(FhirContext.forR5()); - } - - private TerminologyServerClient terminologyServerClient; - - // as per http://hl7.org/fhir/r5/resource.html#canonical - public static final List canonicalResourceTypes = - // can't use List.of for Android 26 compatibility - Collections.unmodifiableList(new ArrayList(Arrays.asList( - ResourceType.ActivityDefinition, - ResourceType.CapabilityStatement, - ResourceType.ChargeItemDefinition, - ResourceType.CompartmentDefinition, - ResourceType.ConceptMap, - ResourceType.EventDefinition, - ResourceType.Evidence, - ResourceType.EvidenceVariable, - ResourceType.ExampleScenario, - ResourceType.GraphDefinition, - ResourceType.ImplementationGuide, - ResourceType.Library, - ResourceType.Measure, - ResourceType.MessageDefinition, - ResourceType.NamingSystem, - ResourceType.OperationDefinition, - ResourceType.PlanDefinition, - ResourceType.Questionnaire, - ResourceType.SearchParameter, - ResourceType.StructureDefinition, - ResourceType.StructureMap, - ResourceType.TerminologyCapabilities, - ResourceType.TestScript, - ResourceType.ValueSet))); - - public static final List conformanceResourceTypes = - // can't use List.of for Android 26 compatibility - Collections.unmodifiableList(new ArrayList(Arrays.asList( - ResourceType.CapabilityStatement, - ResourceType.StructureDefinition, - ResourceType.ImplementationGuide, - ResourceType.SearchParameter, - ResourceType.MessageDefinition, - ResourceType.OperationDefinition, - ResourceType.CompartmentDefinition, - ResourceType.StructureMap, - ResourceType.GraphDefinition, - ResourceType.ExampleScenario))); - - public static final List knowledgeArtifactResourceTypes = - // can't use List.of for Android 26 compatibility - Collections.unmodifiableList(new ArrayList(Arrays.asList( - ResourceType.Library, - ResourceType.Measure, - ResourceType.ActivityDefinition, - ResourceType.PlanDefinition))); - - public static final List terminologyResourceTypes = - // can't use List.of for Android 26 compatibility - Collections.unmodifiableList(new ArrayList(Arrays.asList( - ResourceType.CodeSystem, - ResourceType.ValueSet, - ResourceType.ConceptMap, - ResourceType.NamingSystem, - ResourceType.TerminologyCapabilities))); - - public static void setCorrectBundleType(Optional count, Optional offset, Bundle bundle) { - // if the bundle is paged then it must be of type = collection and modified to follow bundle.type constraints - // if not, set type = transaction - // special case of count = 0 -> set type = searchset so we can display bundle.total - if (count.isPresent() && count.get() == 0) { - bundle.setType(BundleType.SEARCHSET); - bundle.setTotal(bundle.getEntry().size()); - } else if ((offset.isPresent() && offset.get() > 0) - || (count.isPresent() && count.get() < bundle.getEntry().size())) { - bundle.setType(BundleType.COLLECTION); - List removedRequest = bundle.getEntry().stream() - .map(entry -> { - entry.setRequest(null); - return entry; - }) - .collect(Collectors.toList()); - bundle.setEntry(removedRequest); - } else { - bundle.setType(BundleType.TRANSACTION); - } - } - - public static List findUnsupportedInclude( - List entries, List include) { - if (include == null - || include.isEmpty() - || include.stream().anyMatch((includedType) -> includedType.equals("all"))) { - return entries; - } - List filteredList = new ArrayList<>(); - entries.stream().forEach(entry -> { - if (include.stream().anyMatch((type) -> type.equals("knowledge"))) { - Boolean resourceIsKnowledgeType = knowledgeArtifactResourceTypes.contains( - entry.getResource().getResourceType()); - if (resourceIsKnowledgeType) { - filteredList.add(entry); - } - } - if (include.stream().anyMatch((type) -> type.equals("canonical"))) { - Boolean resourceIsCanonicalType = - canonicalResourceTypes.contains(entry.getResource().getResourceType()); - if (resourceIsCanonicalType) { - filteredList.add(entry); - } - } - if (include.stream().anyMatch((type) -> type.equals("terminology"))) { - Boolean resourceIsTerminologyType = - terminologyResourceTypes.contains(entry.getResource().getResourceType()); - if (resourceIsTerminologyType) { - filteredList.add(entry); - } - } - if (include.stream().anyMatch((type) -> type.equals("conformance"))) { - Boolean resourceIsConformanceType = - conformanceResourceTypes.contains(entry.getResource().getResourceType()); - if (resourceIsConformanceType) { - filteredList.add(entry); - } - } - if (include.stream().anyMatch((type) -> type.equals("extensions")) - && entry.getResource().getResourceType().equals(ResourceType.StructureDefinition) - && ((StructureDefinition) entry.getResource()).getType().equals("Extension")) { - filteredList.add(entry); - } - if (include.stream().anyMatch((type) -> type.equals("profiles")) - && entry.getResource().getResourceType().equals(ResourceType.StructureDefinition) - && !((StructureDefinition) entry.getResource()).getType().equals("Extension")) { - filteredList.add(entry); - } - if (include.stream().anyMatch((type) -> type.equals("tests"))) { - if (entry.getResource().getResourceType().equals(ResourceType.Library) - && ((Library) entry.getResource()) - .getType().getCoding().stream() - .anyMatch(coding -> coding.getCode().equals("test-case"))) { - filteredList.add(entry); - } else if (((MetadataResource) entry.getResource()) - .getExtension().stream() - .anyMatch(ext -> ext.getUrl().contains("isTestCase") - && ((BooleanType) ext.getValue()).getValue())) { - filteredList.add(entry); - } - } - if (include.stream().anyMatch((type) -> type.equals("examples"))) { - // TODO: idk if this is legit just a placeholder for now - if (((MetadataResource) entry.getResource()) - .getExtension().stream() - .anyMatch(ext -> ext.getUrl().contains("isExample") - && ((BooleanType) ext.getValue()).getValue())) { - filteredList.add(entry); - } - } - }); - List distinctFilteredEntries = new ArrayList<>(); - // remove duplicates - for (BundleEntryComponent entry : filteredList) { - if (!distinctFilteredEntries.stream() - .map((e) -> ((MetadataResource) e.getResource())) - .anyMatch(existingEntry -> - existingEntry.getUrl().equals(((MetadataResource) entry.getResource()).getUrl()) - && existingEntry - .getVersion() - .equals(((MetadataResource) entry.getResource()).getVersion()))) { - distinctFilteredEntries.add(entry); - } - } - return distinctFilteredEntries; - } - - /** - * ValueSets can be part of multiple artifacts at the same time. Certain properties are tracked/managed in the manifest to avoid conflicts with other artifacts. This function sets those properties on the ValueSets themselves at export / $package time - * @param manifest the resource containing all RelatedArtifact references - * @param bundleEntries the list of packaged resources to modify according to the extensions on the manifest relatedArtifact references - */ - public void handleValueSetReferenceExtensions( - MetadataResource manifest, - List bundleEntries, - Repository repository, - Optional terminologyEndpoint) - throws UnprocessableEntityException, IllegalArgumentException { - KnowledgeArtifactAdapter adapter = AdapterFactory.forFhirVersion(manifest.getStructureFhirVersionEnum()) - .createKnowledgeArtifactAdapter(manifest); - List relatedArtifactsWithPreservedExtension = - getRelatedArtifactsWithPreservedExtensions(adapter.getRelatedArtifact()); - Parameters expansionParams = new Parameters(); - Library rootSpecificationLibrary = getRootSpecificationLibrary(bundleEntries); - if (rootSpecificationLibrary != null) { - Extension expansionParamsExtension = - rootSpecificationLibrary.getExtensionByUrl(Constants.EXPANSION_PARAMETERS_URL); - if (expansionParamsExtension != null && expansionParamsExtension.hasValue()) { - Reference expansionReference = (Reference) expansionParamsExtension.getValue(); - expansionParams = getExpansionParams(rootSpecificationLibrary, expansionReference.getReference()); - } - } - Parameters params = expansionParams; - bundleEntries.stream().forEach(entry -> { - if (entry.getResource().getResourceType().equals(ResourceType.ValueSet)) { - ValueSet valueSet = (ValueSet) entry.getResource(); - // remove any existing Priority and Conditions - List usageContexts = removeExistingReferenceExtensionData(valueSet.getUseContext()); - valueSet.setUseContext(usageContexts); - Optional maybeVSRelatedArtifact = relatedArtifactsWithPreservedExtension.stream() - .filter(ra -> Canonicals.getUrl(ra.getResource()).equals(valueSet.getUrl())) - .findFirst(); - // If leaf valueset - if (!valueSet.hasCompose() - || (valueSet.hasCompose() - && valueSet.getCompose() - .getIncludeFirstRep() - .getValueSet() - .isEmpty())) { - expandValueSet(valueSet, params, terminologyEndpoint); - // If Condition extension is present - maybeVSRelatedArtifact - .map(ra -> ra.getExtension()) - .ifPresent( - // add Conditions - exts -> { - exts.stream() - .filter(ext -> ext.getUrl() - .equalsIgnoreCase(Constants.VALUE_SET_CONDITION_URL)) - .forEach(ext -> tryAddCondition( - usageContexts, (CodeableConcept) ext.getValue())); - }); - } - // update Priority - UsageContext priority = getOrCreateUsageContext( - usageContexts, KnowledgeArtifactAdapter.usPhContextTypeUrl, Constants.VALUE_SET_PRIORITY_CODE); - Optional ext = - maybeVSRelatedArtifact.map(ra -> ra.getExtensionByUrl(Constants.VALUE_SET_PRIORITY_URL)); - - if (ext.isPresent()) { - priority.setValue(ext.get().getValue()); - } else { - CodeableConcept routine = new CodeableConcept( - new Coding(KnowledgeArtifactAdapter.contextUrl, "routine", null)) - .setText("Routine"); - priority.setValue(routine); - } - } - }); - } - - public void expandValueSet( - ValueSet valueSet, Parameters expansionParameters, Optional terminologyEndpoint) { - // Gather the Terminology Service from the valueSet's authoritativeSourceUrl. - Extension authoritativeSource = valueSet.getExtensionByUrl(Constants.AUTHORITATIVE_SOURCE_URL); - String authoritativeSourceUrl = authoritativeSource != null && authoritativeSource.hasValue() - ? authoritativeSource.getValue().primitiveValue() - : valueSet.getUrl(); - - ValueSet expandedValueSet; - if (isVSMAuthoredValueSet(valueSet) && hasSimpleCompose(valueSet)) { - // Perform naive expansion independent of terminology servers. Copy all codes from compose into expansion. - ValueSet.ValueSetExpansionComponent expansion = new ValueSet.ValueSetExpansionComponent(); - expansion.setTimestamp(Date.from(Instant.now())); - - ArrayList expansionParams = new ArrayList<>(); - ValueSet.ValueSetExpansionParameterComponent parameterNaive = - new ValueSet.ValueSetExpansionParameterComponent(); - parameterNaive.setName("naive"); - parameterNaive.setValue(new BooleanType(true)); - expansionParams.add(parameterNaive); - expansion.setParameter(expansionParams); - - for (ValueSet.ConceptSetComponent csc : valueSet.getCompose().getInclude()) { - for (ValueSet.ConceptReferenceComponent crc : csc.getConcept()) { - expansion - .addContains() - .setCode(crc.getCode()) - .setSystem(csc.getSystem()) - .setVersion(csc.getVersion()) - .setDisplay(crc.getDisplay()); - } - } - valueSet.setExpansion(expansion); - } else { - String username; - String apiKey; - if (terminologyEndpoint.isPresent()) { - username = terminologyEndpoint.get().getExtensionsByUrl(Constants.VSAC_USERNAME).stream() - .findFirst() - .map(ext -> ext.getValue().toString()) - .orElseThrow(() -> new UnprocessableEntityException( - "Cannot expand ValueSet without VSAC Username: " + valueSet.getId())); - apiKey = terminologyEndpoint.get().getExtensionsByUrl(Constants.APIKEY).stream() - .findFirst() - .map(ext -> ext.getValue().toString()) - .orElseThrow(() -> new UnprocessableEntityException( - "Cannot expand ValueSet without VSAC API Key: " + valueSet.getId())); - } else { - throw new UnprocessableEntityException( - "Cannot expand ValueSet without credentials: " + valueSet.getId()); - } - - try { - expandedValueSet = terminologyServerClient.expand( - valueSet, authoritativeSourceUrl, expansionParameters, username, apiKey); - valueSet.setExpansion(expandedValueSet.getExpansion()); - } catch (Exception ex) { - throw new UnprocessableEntityException( - "Terminology Server expansion failed for: " + valueSet.getId(), ex.getMessage()); - } - } - } - - protected boolean isVSMAuthoredValueSet(ValueSet valueSet) { - return valueSet.hasMeta() - && valueSet.getMeta().hasTag() - && valueSet.getMeta() - .getTag( - Constants.VSM_WORKFLOW_CODES_CODE_SYSTEM_URL, - Constants.VSM_VALUE_SET_TAG_VSM_AUTHORED_CODE) - != null; - } - - // A simple compose element of a ValueSet must have a compose without an exclude element. Each - // element of the include cannot reference a value set or have a filter, and must have a system - // and enumerate concepts - protected boolean hasSimpleCompose(ValueSet valueSet) { - return valueSet.hasCompose() - && !valueSet.getCompose().hasExclude() - && valueSet.getCompose().getInclude().stream() - .noneMatch( - csc -> csc.hasValueSet() || csc.hasFilter() || !csc.hasSystem() || !csc.hasConcept()); - } - - protected List getRelatedArtifactsWithPreservedExtensions(List deps) { - return deps.stream() - .filter(ra -> Constants.PRESERVED_EXTENSION_URLS.stream().anyMatch(url -> ra.getExtension().stream() - .anyMatch(ext -> ext.getUrl().equalsIgnoreCase(url)))) - .collect(Collectors.toList()); - } - - protected static Library getRootSpecificationLibrary(List bundleEntries) { - Optional rootSpecLibrary = bundleEntries.stream() - .filter(entry -> entry.getResource().getResourceType() == ResourceType.Library) - .map(entry -> ((Library) entry.getResource())) - .filter(entry -> entry.getType().hasCoding(Constants.LIBRARY_TYPE, Constants.ASSET_COLLECTION) - && entry.getUseContext().stream() - .allMatch(useContext -> (useContext - .getCode() - .getSystem() - .equals(KnowledgeArtifactAdapter.usPhContextTypeUrl) - && useContext - .getCode() - .getCode() - .equals("reporting") - && useContext - .getValueCodeableConcept() - .hasCoding(Constants.US_PH_CONTEXT_URL, "triggering")) - || (useContext - .getCode() - .getSystem() - .equals(KnowledgeArtifactAdapter.usPhContextTypeUrl) - && useContext - .getCode() - .getCode() - .equals("specification-type") - && useContext - .getValueCodeableConcept() - .hasCoding(Constants.US_PH_CONTEXT_URL, "program")))) - .findFirst(); - return rootSpecLibrary.orElse(null); - } - - protected static Parameters getExpansionParams(Library rootSpecificationLibrary, String reference) { - Optional expansionParamResource = rootSpecificationLibrary.getContained().stream() - .filter(contained -> contained.getId().equals(reference)) - .findFirst(); - return (Parameters) expansionParamResource.orElse(null); - } - - /** - * Removes any existing UsageContexts corresponding to the VSM specific extensions - * @param usageContexts the list of usage contexts to modify - */ - protected List removeExistingReferenceExtensionData(List usageContexts) { - List useContextCodesToReplace = Collections.unmodifiableList( - Arrays.asList(Constants.VALUE_SET_CONDITION_CODE, Constants.VALUE_SET_PRIORITY_CODE)); - return usageContexts.stream() - // remove any useContexts which need to be replaced - .filter(useContext -> !useContextCodesToReplace.stream() - .anyMatch(code -> useContext.getCode().getCode().equals(code))) - .collect(Collectors.toList()); - } - - protected void tryAddCondition(List usageContexts, CodeableConcept condition) { - boolean focusAlreadyExists = usageContexts.stream() - .anyMatch(u -> u.getCode().getSystem().equals(KnowledgeArtifactAdapter.contextTypeUrl) - && u.getCode().getCode().equals(Constants.VALUE_SET_CONDITION_CODE) - && u.getValueCodeableConcept() - .hasCoding( - condition.getCoding().get(0).getSystem(), - condition.getCoding().get(0).getCode())); - if (!focusAlreadyExists) { - UsageContext newFocus = new UsageContext( - new Coding(KnowledgeArtifactAdapter.contextUrl, Constants.VALUE_SET_CONDITION_CODE, null), - condition); - newFocus.setValue(condition); - usageContexts.add(newFocus); - } - } - /** - * - * Either finds a usageContext with the same system and code or creates an empty one - * and appends it - * - * @param usageContexts the list of usageContexts to search and/or append to - * @param system the usageContext.code.system to find / create - * @param code the usage.code.code to find / create - * @return the found / created usageContext - */ - protected UsageContext getOrCreateUsageContext(List usageContexts, String system, String code) { - return usageContexts.stream() - .filter(useContext -> useContext.getCode().getSystem().equals(system) - && useContext.getCode().getCode().equals(code)) - .findFirst() - .orElseGet(() -> { - // create the UseContext if it doesn't exist - Coding c = new Coding(system, code, null); - UsageContext n = new UsageContext(c, null); - // add it to the ValueSet before returning - usageContexts.add(n); - return n; - }); - } -} diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r5/PackageVisitor.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r5/PackageVisitor.java new file mode 100644 index 000000000..0b51bb51c --- /dev/null +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r5/PackageVisitor.java @@ -0,0 +1,186 @@ +package org.opencds.cqf.fhir.utility.visitor.r5; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import org.hl7.fhir.r5.model.BooleanType; +import org.hl7.fhir.r5.model.Bundle; +import org.hl7.fhir.r5.model.Bundle.BundleEntryComponent; +import org.hl7.fhir.r5.model.Bundle.BundleType; +import org.hl7.fhir.r5.model.Library; +import org.hl7.fhir.r5.model.MetadataResource; +import org.hl7.fhir.r5.model.ResourceType; +import org.hl7.fhir.r5.model.StructureDefinition; + +public class PackageVisitor { + // as per http://hl7.org/fhir/r5/resource.html#canonical + public static final List canonicalResourceTypes = + // can't use List.of for Android 26 compatibility + Collections.unmodifiableList(new ArrayList(Arrays.asList( + ResourceType.ActivityDefinition, + ResourceType.CapabilityStatement, + ResourceType.ChargeItemDefinition, + ResourceType.CompartmentDefinition, + ResourceType.ConceptMap, + ResourceType.EventDefinition, + ResourceType.Evidence, + ResourceType.EvidenceVariable, + ResourceType.ExampleScenario, + ResourceType.GraphDefinition, + ResourceType.ImplementationGuide, + ResourceType.Library, + ResourceType.Measure, + ResourceType.MessageDefinition, + ResourceType.NamingSystem, + ResourceType.OperationDefinition, + ResourceType.PlanDefinition, + ResourceType.Questionnaire, + ResourceType.SearchParameter, + ResourceType.StructureDefinition, + ResourceType.StructureMap, + ResourceType.TerminologyCapabilities, + ResourceType.TestScript, + ResourceType.ValueSet))); + + public static final List conformanceResourceTypes = + // can't use List.of for Android 26 compatibility + Collections.unmodifiableList(new ArrayList(Arrays.asList( + ResourceType.CapabilityStatement, + ResourceType.StructureDefinition, + ResourceType.ImplementationGuide, + ResourceType.SearchParameter, + ResourceType.MessageDefinition, + ResourceType.OperationDefinition, + ResourceType.CompartmentDefinition, + ResourceType.StructureMap, + ResourceType.GraphDefinition, + ResourceType.ExampleScenario))); + + public static final List knowledgeArtifactResourceTypes = + // can't use List.of for Android 26 compatibility + Collections.unmodifiableList(new ArrayList(Arrays.asList( + ResourceType.Library, + ResourceType.Measure, + ResourceType.ActivityDefinition, + ResourceType.PlanDefinition))); + + public static final List terminologyResourceTypes = + // can't use List.of for Android 26 compatibility + Collections.unmodifiableList(new ArrayList(Arrays.asList( + ResourceType.CodeSystem, + ResourceType.ValueSet, + ResourceType.ConceptMap, + ResourceType.NamingSystem, + ResourceType.TerminologyCapabilities))); + + public static void setCorrectBundleType(Optional count, Optional offset, Bundle bundle) { + // if the bundle is paged then it must be of type = collection and modified to follow bundle.type constraints + // if not, set type = transaction + // special case of count = 0 -> set type = searchset so we can display bundle.total + if (count.isPresent() && count.get() == 0) { + bundle.setType(BundleType.SEARCHSET); + bundle.setTotal(bundle.getEntry().size()); + } else if ((offset.isPresent() && offset.get() > 0) + || (count.isPresent() && count.get() < bundle.getEntry().size())) { + bundle.setType(BundleType.COLLECTION); + List removedRequest = bundle.getEntry().stream() + .map(entry -> { + entry.setRequest(null); + return entry; + }) + .collect(Collectors.toList()); + bundle.setEntry(removedRequest); + } else { + bundle.setType(BundleType.TRANSACTION); + } + } + + public static List findUnsupportedInclude( + List entries, List include) { + if (include == null + || include.isEmpty() + || include.stream().anyMatch((includedType) -> includedType.equals("all"))) { + return entries; + } + List filteredList = new ArrayList<>(); + entries.stream().forEach(entry -> { + if (include.stream().anyMatch((type) -> type.equals("knowledge"))) { + Boolean resourceIsKnowledgeType = knowledgeArtifactResourceTypes.contains( + entry.getResource().getResourceType()); + if (resourceIsKnowledgeType) { + filteredList.add(entry); + } + } + if (include.stream().anyMatch((type) -> type.equals("canonical"))) { + Boolean resourceIsCanonicalType = + canonicalResourceTypes.contains(entry.getResource().getResourceType()); + if (resourceIsCanonicalType) { + filteredList.add(entry); + } + } + if (include.stream().anyMatch((type) -> type.equals("terminology"))) { + Boolean resourceIsTerminologyType = + terminologyResourceTypes.contains(entry.getResource().getResourceType()); + if (resourceIsTerminologyType) { + filteredList.add(entry); + } + } + if (include.stream().anyMatch((type) -> type.equals("conformance"))) { + Boolean resourceIsConformanceType = + conformanceResourceTypes.contains(entry.getResource().getResourceType()); + if (resourceIsConformanceType) { + filteredList.add(entry); + } + } + if (include.stream().anyMatch((type) -> type.equals("extensions")) + && entry.getResource().getResourceType().equals(ResourceType.StructureDefinition) + && ((StructureDefinition) entry.getResource()).getType().equals("Extension")) { + filteredList.add(entry); + } + if (include.stream().anyMatch((type) -> type.equals("profiles")) + && entry.getResource().getResourceType().equals(ResourceType.StructureDefinition) + && !((StructureDefinition) entry.getResource()).getType().equals("Extension")) { + filteredList.add(entry); + } + if (include.stream().anyMatch((type) -> type.equals("tests"))) { + if (entry.getResource().getResourceType().equals(ResourceType.Library) + && ((Library) entry.getResource()) + .getType().getCoding().stream() + .anyMatch(coding -> coding.getCode().equals("test-case"))) { + filteredList.add(entry); + } else if (((MetadataResource) entry.getResource()) + .getExtension().stream() + .anyMatch(ext -> ext.getUrl().contains("isTestCase") + && ((BooleanType) ext.getValue()).getValue())) { + filteredList.add(entry); + } + } + if (include.stream().anyMatch((type) -> type.equals("examples"))) { + // TODO: idk if this is legit just a placeholder for now + if (((MetadataResource) entry.getResource()) + .getExtension().stream() + .anyMatch(ext -> ext.getUrl().contains("isExample") + && ((BooleanType) ext.getValue()).getValue())) { + filteredList.add(entry); + } + } + }); + List distinctFilteredEntries = new ArrayList<>(); + // remove duplicates + for (BundleEntryComponent entry : filteredList) { + if (!distinctFilteredEntries.stream() + .map((e) -> ((MetadataResource) e.getResource())) + .anyMatch(existingEntry -> + existingEntry.getUrl().equals(((MetadataResource) entry.getResource()).getUrl()) + && existingEntry + .getVersion() + .equals(((MetadataResource) entry.getResource()).getVersion()))) { + distinctFilteredEntries.add(entry); + } + } + return distinctFilteredEntries; + } +} diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r5/KnowledgeArtifactReleaseVisitor.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r5/ReleaseVisitor.java similarity index 98% rename from cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r5/KnowledgeArtifactReleaseVisitor.java rename to cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r5/ReleaseVisitor.java index 6fda8a093..74dd08c41 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r5/KnowledgeArtifactReleaseVisitor.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/r5/ReleaseVisitor.java @@ -30,7 +30,7 @@ import org.opencds.cqf.fhir.utility.r5.CRMIReleaseVersionBehavior.CRMIReleaseVersionBehaviorCodes; import org.slf4j.Logger; -public class KnowledgeArtifactReleaseVisitor { +public class ReleaseVisitor { public static void checkNonExperimental( MetadataResource resource, @@ -65,7 +65,7 @@ public static void checkNonExperimental( } } - public static void propagageEffectivePeriod(Period rootEffectivePeriod, KnowledgeArtifactAdapter artifactAdapter) { + public static void propagateEffectivePeriod(Period rootEffectivePeriod, KnowledgeArtifactAdapter artifactAdapter) { Period effectivePeriod = (Period) artifactAdapter.getEffectivePeriod(); // if the root artifact period is NOT null AND HAS a start or an end date if ((rootEffectivePeriod != null && (rootEffectivePeriod.hasStart() || rootEffectivePeriod.hasEnd())) diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/CqfExpressionTests.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/CqfExpressionTests.java new file mode 100644 index 000000000..12f2570c6 --- /dev/null +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/CqfExpressionTests.java @@ -0,0 +1,122 @@ +package org.opencds.cqf.fhir.utility; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import org.hl7.fhir.instance.model.api.IBaseExtension; +import org.junit.jupiter.api.Test; + +class CqfExpressionTests { + @Test + void testInvalidExtension() { + var cqfExpression = CqfExpression.of((IBaseExtension) null, null); + assertNull(cqfExpression); + var dstu2Expression = CqfExpression.of(new org.hl7.fhir.dstu2.model.Extension(), null); + assertNull(dstu2Expression); + assertNull(CqfExpression.of(new org.hl7.fhir.r4.model.Extension(), null)); + assertNull(CqfExpression.of(new org.hl7.fhir.r5.model.Extension(), null)); + } + + @Test + void testCqfExpression() { + var language = "text/cql"; + var expression = "expression"; + var libraryUrl = "Library/lib"; + var altLanguage = "text/fhirpath"; + var altExpression = "altExpression"; + var altLibraryUrl = "Library/alt"; + var cqfExpression = new CqfExpression(); + cqfExpression.setLanguage(language); + cqfExpression.setExpression(expression); + cqfExpression.setLibraryUrl(libraryUrl); + cqfExpression.setAltLanguage(altLanguage); + cqfExpression.setAltExpression(altExpression); + cqfExpression.setAltLibraryUrl(altLibraryUrl); + assertEquals(language, cqfExpression.getLanguage()); + assertEquals(expression, cqfExpression.getExpression()); + assertEquals(libraryUrl, cqfExpression.getLibraryUrl()); + assertEquals(altLanguage, cqfExpression.getAltLanguage()); + assertEquals(altExpression, cqfExpression.getAltExpression()); + assertEquals(altLibraryUrl, cqfExpression.getAltLibraryUrl()); + } + + @Test + void testDstu3Extension() { + var expression = new org.hl7.fhir.dstu3.model.StringType("expression"); + var ext = new org.hl7.fhir.dstu3.model.Extension(Constants.CQIF_CQL_EXPRESSION, expression); + var cqfExpression = CqfExpression.of(ext, "http://test.com/Library/test"); + assertEquals(expression.getValue(), cqfExpression.getExpression()); + } + + @Test + void testR4Extension() { + var defaultLibraryUrl = "http://test.com/Library/test"; + var expression = new org.hl7.fhir.r4.model.Expression() + .setLanguage("text/cql.identifier") + .setExpression("expression"); + var ext = new org.hl7.fhir.r4.model.Extension(Constants.CQF_EXPRESSION, expression); + var cqfExpression = CqfExpression.of(ext, defaultLibraryUrl); + assertEquals(expression.getExpression(), cqfExpression.getExpression()); + assertEquals(expression.getLanguage(), cqfExpression.getLanguage()); + assertEquals(defaultLibraryUrl, cqfExpression.getLibraryUrl()); + assertNull(cqfExpression.getAltExpression()); + assertNull(cqfExpression.getAltLanguage()); + assertNull(cqfExpression.getAltLibraryUrl()); + } + + @Test + void testR4ExtensionWithAlternate() { + var defaultLibraryUrl = "http://test.com/Library/test"; + var expression = new org.hl7.fhir.r4.model.Expression() + .setLanguage("text/cql.identifier") + .setExpression("expression"); + var altExpression = new org.hl7.fhir.r4.model.Expression() + .setLanguage("text/fhirpath") + .setExpression("altExpression"); + expression.addExtension(Constants.ALT_EXPRESSION_EXT, altExpression); + var ext = new org.hl7.fhir.r4.model.Extension(Constants.CQF_EXPRESSION, expression); + var cqfExpression = CqfExpression.of(ext, defaultLibraryUrl); + assertEquals(expression.getExpression(), cqfExpression.getExpression()); + assertEquals(expression.getLanguage(), cqfExpression.getLanguage()); + assertEquals(defaultLibraryUrl, cqfExpression.getLibraryUrl()); + assertEquals(altExpression.getExpression(), cqfExpression.getAltExpression()); + assertEquals(altExpression.getLanguage(), cqfExpression.getAltLanguage()); + assertNull(cqfExpression.getAltLibraryUrl()); + } + + @Test + void testR5Extension() { + var defaultLibraryUrl = "http://test.com/Library/test"; + var expression = new org.hl7.fhir.r5.model.Expression() + .setLanguage("text/cql.identifier") + .setExpression("expression"); + var ext = new org.hl7.fhir.r5.model.Extension(Constants.CQF_EXPRESSION, expression); + var cqfExpression = CqfExpression.of(ext, defaultLibraryUrl); + assertEquals(expression.getExpression(), cqfExpression.getExpression()); + assertEquals(expression.getLanguage(), cqfExpression.getLanguage()); + assertEquals(defaultLibraryUrl, cqfExpression.getLibraryUrl()); + assertNull(cqfExpression.getAltExpression()); + assertNull(cqfExpression.getAltLanguage()); + assertNull(cqfExpression.getAltLibraryUrl()); + } + + @Test + void testR5ExtensionWithAlternate() { + var defaultLibraryUrl = "http://test.com/Library/test"; + var expression = new org.hl7.fhir.r5.model.Expression() + .setLanguage("text/cql.identifier") + .setExpression("expression"); + var altExpression = new org.hl7.fhir.r5.model.Expression() + .setLanguage("text/fhirpath") + .setExpression("altExpression"); + expression.addExtension(Constants.ALT_EXPRESSION_EXT, altExpression); + var ext = new org.hl7.fhir.r5.model.Extension(Constants.CQF_EXPRESSION, expression); + var cqfExpression = CqfExpression.of(ext, defaultLibraryUrl); + assertEquals(expression.getExpression(), cqfExpression.getExpression()); + assertEquals(expression.getLanguage(), cqfExpression.getLanguage()); + assertEquals(defaultLibraryUrl, cqfExpression.getLibraryUrl()); + assertEquals(altExpression.getExpression(), cqfExpression.getAltExpression()); + assertEquals(altExpression.getLanguage(), cqfExpression.getAltLanguage()); + assertNull(cqfExpression.getAltLibraryUrl()); + } +} diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/ValueSetsTest.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/ValueSetsTest.java new file mode 100644 index 000000000..da24e6256 --- /dev/null +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/ValueSetsTest.java @@ -0,0 +1,214 @@ +package org.opencds.cqf.fhir.utility; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import ca.uhn.fhir.context.FhirContext; +import java.lang.reflect.InvocationTargetException; +import java.util.Collections; +import org.hl7.fhir.r4.model.ValueSet; +import org.junit.jupiter.api.Test; +import org.opencds.cqf.cql.engine.runtime.Code; + +class ValueSetsTest { + private final FhirContext fhirContextR4 = FhirContext.forR4Cached(); + + @Test + void testGetCompose() { + var valueSet = new ValueSet(); + assertNull(ValueSets.getCompose(fhirContextR4, valueSet)); + var compose = new ValueSet.ValueSetComposeComponent(); + valueSet.setCompose(compose); + assertEquals(compose, ValueSets.getCompose(fhirContextR4, valueSet)); + } + + @Test + void testGetIncludes() { + var valueSet = new ValueSet(); + assertNull(ValueSets.getIncludes(fhirContextR4, valueSet)); + var include = new ValueSet.ConceptSetComponent(); + var compose = new ValueSet.ValueSetComposeComponent(); + valueSet.setCompose(compose); + assertNull(ValueSets.getIncludes(fhirContextR4, valueSet)); + compose.addInclude(include); + assertEquals(include, ValueSets.getIncludes(fhirContextR4, valueSet).get(0)); + } + + @Test + void testGetIncludeConcepts() { + var concept = new ValueSet.ConceptReferenceComponent(); + var include = new ValueSet.ConceptSetComponent().addConcept(concept); + var valueSet = new ValueSet(); + assertNull(ValueSets.getIncludeConcepts(fhirContextR4, valueSet)); + valueSet.setCompose(new ValueSet.ValueSetComposeComponent().addInclude(include)); + assertEquals( + concept, ValueSets.getIncludeConcepts(fhirContextR4, valueSet).get(0)); + } + + @Test + void testGetIncludeFilters() { + var filter = new ValueSet.ConceptSetFilterComponent(); + var include = new ValueSet.ConceptSetComponent().addFilter(filter); + var valueSet = new ValueSet(); + assertNull(ValueSets.getIncludeFilters(fhirContextR4, valueSet)); + valueSet.setCompose(new ValueSet.ValueSetComposeComponent().addInclude(include)); + assertEquals( + filter, ValueSets.getIncludeFilters(fhirContextR4, valueSet).get(0)); + } + + @Test + void testGetExcludes() { + var valueSet = new ValueSet(); + assertNull(ValueSets.getExcludes(fhirContextR4, valueSet)); + var exclude = new ValueSet.ConceptSetComponent(); + var compose = new ValueSet.ValueSetComposeComponent(); + valueSet.setCompose(compose); + assertNull(ValueSets.getExcludes(fhirContextR4, valueSet)); + compose.addExclude(exclude); + assertEquals(exclude, ValueSets.getExcludes(fhirContextR4, valueSet).get(0)); + } + + @Test + void testGetExcludeConcepts() { + var concept = new ValueSet.ConceptReferenceComponent(); + var exclude = new ValueSet.ConceptSetComponent().addConcept(concept); + var valueSet = new ValueSet(); + assertNull(ValueSets.getExcludeConcepts(fhirContextR4, valueSet)); + valueSet.setCompose(new ValueSet.ValueSetComposeComponent().addExclude(exclude)); + assertEquals( + concept, ValueSets.getExcludeConcepts(fhirContextR4, valueSet).get(0)); + } + + @Test + void testGetExcludeFilters() { + var filter = new ValueSet.ConceptSetFilterComponent(); + var exclude = new ValueSet.ConceptSetComponent().addFilter(filter); + var valueSet = new ValueSet(); + assertNull(ValueSets.getExcludeFilters(fhirContextR4, valueSet)); + valueSet.setCompose(new ValueSet.ValueSetComposeComponent().addExclude(exclude)); + assertEquals( + filter, ValueSets.getExcludeFilters(fhirContextR4, valueSet).get(0)); + } + + @Test + void testGetExpansion() { + var valueSet = new ValueSet(); + assertNull(ValueSets.getExpansion(fhirContextR4, valueSet)); + var expansion = new ValueSet.ValueSetExpansionComponent(); + valueSet.setExpansion(expansion); + assertEquals(expansion, ValueSets.getExpansion(fhirContextR4, valueSet)); + } + + @Test + void testGetContainsInExpansion() { + assertNull(ValueSets.getContainsInExpansion(fhirContextR4, null)); + var expansion = new ValueSet.ValueSetExpansionComponent(); + assertNull(ValueSets.getContainsInExpansion(fhirContextR4, expansion)); + var contains = new ValueSet.ValueSetExpansionContainsComponent(); + expansion.addContains(contains); + assertEquals( + contains, + ValueSets.getContainsInExpansion(fhirContextR4, expansion).get(0)); + } + + @Test + void testGetContains() { + var contains = new ValueSet.ValueSetExpansionContainsComponent(); + var valueSet = new ValueSet().setExpansion(new ValueSet.ValueSetExpansionComponent().addContains(contains)); + assertEquals(contains, ValueSets.getContains(fhirContextR4, valueSet).get(0)); + } + + @Test + void testGetCodesInCompose() { + var code = "test"; + var display = "Test"; + var version = "1.0.0"; + var system = "http://fhir.test"; + var valueSet = new ValueSet().setCompose(new ValueSet.ValueSetComposeComponent()); + assertNull(ValueSets.getCodesInCompose(fhirContextR4, valueSet)); + valueSet.getCompose() + .addInclude(new ValueSet.ConceptSetComponent() + .setVersion(version) + .setSystem(system) + .addConcept(new ValueSet.ConceptReferenceComponent() + .setCode(code) + .setDisplay(display))); + var actualCode = ValueSets.getCodesInCompose(fhirContextR4, valueSet).get(0); + assertEquals(code, actualCode.getCode()); + assertEquals(display, actualCode.getDisplay()); + assertEquals(version, actualCode.getVersion()); + assertEquals(system, actualCode.getSystem()); + } + + @Test + void testGetCodesInContains() { + var code = "test"; + var display = "Test"; + var version = "1.0.0"; + var system = "http://fhir.test"; + assertNull(ValueSets.getCodesInContains(fhirContextR4, null)); + var contains = new ValueSet.ValueSetExpansionContainsComponent() + .setCode(code) + .setDisplay(display) + .setVersion(version) + .setSystem(system); + var actualCode = ValueSets.getCodesInContains(fhirContextR4, Collections.singletonList(contains)) + .get(0); + assertEquals(code, actualCode.getCode()); + assertEquals(display, actualCode.getDisplay()); + assertEquals(version, actualCode.getVersion()); + assertEquals(system, actualCode.getSystem()); + } + + @Test + void testGetCodesInExpansion() { + var code = "test"; + var display = "Test"; + var version = "1.0.0"; + var system = "http://fhir.test"; + var expansion = new ValueSet.ValueSetExpansionComponent() + .addContains(new ValueSet.ValueSetExpansionContainsComponent() + .setCode(code) + .setDisplay(display) + .setVersion(version) + .setSystem(system)); + var valueSet = new ValueSet().setExpansion(expansion); + var expansionCode = + ValueSets.getCodesInExpansion(fhirContextR4, expansion).get(0); + assertEquals(code, expansionCode.getCode()); + assertEquals(display, expansionCode.getDisplay()); + assertEquals(version, expansionCode.getVersion()); + assertEquals(system, expansionCode.getSystem()); + var actualCode = ValueSets.getCodesInExpansion(fhirContextR4, valueSet).get(0); + assertEquals(code, actualCode.getCode()); + assertEquals(display, actualCode.getDisplay()); + assertEquals(version, actualCode.getVersion()); + assertEquals(system, actualCode.getSystem()); + } + + @Test + void testAddCodeToExpansion() { + var code = new Code(); + code.setCode("test"); + code.setDisplay("Test"); + code.setSystem("www.test.com"); + code.setVersion("1.0.0"); + var expansion = new ValueSet.ValueSetExpansionComponent(); + try { + ValueSets.addCodeToExpansion(fhirContextR4, expansion, code); + } catch (InstantiationException + | IllegalAccessException + | IllegalArgumentException + | InvocationTargetException + | NoSuchMethodException + | SecurityException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + var codes = ValueSets.getCodesInExpansion(fhirContextR4, expansion); + assertEquals(code.getCode(), codes.get(0).getCode()); + assertEquals(code.getDisplay(), codes.get(0).getDisplay()); + assertEquals(code.getSystem(), codes.get(0).getSystem()); + assertEquals(code.getVersion(), codes.get(0).getVersion()); + } +} diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/AdapterFactoryTest.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/AdapterFactoryTest.java new file mode 100644 index 000000000..a576bd2d0 --- /dev/null +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/AdapterFactoryTest.java @@ -0,0 +1,25 @@ +package org.opencds.cqf.fhir.utility.adapter; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.FhirVersionEnum; +import org.hl7.fhir.r4.model.Patient; +import org.junit.jupiter.api.Test; + +class AdapterFactoryTest { + @Test + void testUnsupportedVersion() { + assertThrows(IllegalArgumentException.class, () -> AdapterFactory.forFhirVersion(FhirVersionEnum.DSTU2)); + } + + @Test + void testFhirContext() { + var context = FhirContext.forR4Cached(); + var adapterFactory = AdapterFactory.forFhirContext(context); + assertNotNull(adapterFactory); + var adapter = adapterFactory.createResource(new Patient()); + assertNotNull(adapter); + } +} diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/AdapterTest.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/AdapterTest.java new file mode 100644 index 000000000..fd984bf81 --- /dev/null +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/AdapterTest.java @@ -0,0 +1,21 @@ +package org.opencds.cqf.fhir.utility.adapter; + +import static org.junit.Assert.assertThrows; + +import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import java.util.Date; +import org.junit.jupiter.api.Test; + +class AdapterTest { + @Test + void testUnsupportedVersion() { + var version = FhirVersionEnum.DSTU2; + assertThrows(UnprocessableEntityException.class, () -> Adapter.newPeriod(version)); + assertThrows(UnprocessableEntityException.class, () -> Adapter.newStringType(version, "string")); + assertThrows(UnprocessableEntityException.class, () -> Adapter.newUriType(version, "uri")); + assertThrows(UnprocessableEntityException.class, () -> Adapter.newUrlType(version, "url")); + assertThrows(UnprocessableEntityException.class, () -> Adapter.newDateType(version, new Date())); + assertThrows(UnprocessableEntityException.class, () -> Adapter.newDateTimeType(version, new Date())); + } +} diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/dstu3/EndpointAdapterTest.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/dstu3/EndpointAdapterTest.java new file mode 100644 index 000000000..fb7ed47b6 --- /dev/null +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/dstu3/EndpointAdapterTest.java @@ -0,0 +1,29 @@ +package org.opencds.cqf.fhir.utility.adapter.dstu3; + +import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.hl7.fhir.dstu3.model.Endpoint; +import org.hl7.fhir.dstu3.model.PlanDefinition; +import org.junit.jupiter.api.Test; + +public class EndpointAdapterTest { + private final org.opencds.cqf.fhir.utility.adapter.AdapterFactory adapterFactory = new AdapterFactory(); + + @Test + void invalid_object_fails() { + assertThrows(IllegalArgumentException.class, () -> new EndpointAdapter(new PlanDefinition())); + } + + @Test + void adapter_get_and_set_address() { + var endpoint = new Endpoint(); + var address = "123 Test Street"; + endpoint.setAddress(address); + var adapter = (EndpointAdapter) adapterFactory.createResource(endpoint); + assertEquals(address, adapter.getAddress()); + var newAddress = "456 Test Street"; + adapter.setAddress(newAddress); + assertEquals(newAddress, endpoint.getAddress()); + } +} diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/dstu3/KnowledgeArtifactAdapterTest.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/dstu3/KnowledgeArtifactAdapterTest.java new file mode 100644 index 000000000..9553223ec --- /dev/null +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/dstu3/KnowledgeArtifactAdapterTest.java @@ -0,0 +1,158 @@ +package org.opencds.cqf.fhir.utility.adapter.dstu3; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import org.hl7.fhir.dstu3.model.Bundle; +import org.hl7.fhir.dstu3.model.CompartmentDefinition; +import org.hl7.fhir.dstu3.model.DateTimeType; +import org.hl7.fhir.dstu3.model.DateType; +import org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus; +import org.hl7.fhir.dstu3.model.Extension; +import org.hl7.fhir.dstu3.model.Period; +import org.hl7.fhir.dstu3.model.RelatedArtifact; +import org.hl7.fhir.instance.model.api.IDomainResource; +import org.junit.jupiter.api.Test; +import org.opencds.cqf.fhir.utility.visitor.PackageVisitor; + +class KnowledgeArtifactAdapterTest { + private final FhirContext fhirContext = FhirContext.forDstu3Cached(); + private final org.opencds.cqf.fhir.utility.adapter.AdapterFactory adapterFactory = new AdapterFactory(); + + @Test + void invalid_object_fails() { + assertThrows( + IllegalArgumentException.class, + () -> new KnowledgeArtifactAdapter(new org.hl7.fhir.r4.model.Library())); + } + + @Test + void test() { + var resource = new CompartmentDefinition(); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(resource); + assertNotNull(adapter); + } + + @Test + void adapter_accepts_visitor() { + var spyVisitor = spy(new PackageVisitor(fhirContext)); + doReturn(new Bundle()).when(spyVisitor).visit(any(KnowledgeArtifactAdapter.class), any(), any()); + IDomainResource def = new CompartmentDefinition(); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(def); + adapter.accept(spyVisitor, null, null); + verify(spyVisitor, times(1)).visit(any(KnowledgeArtifactAdapter.class), any(), any()); + } + + @Test + void adapter_get_and_set_name() { + var def = new CompartmentDefinition(); + var name = "name"; + def.setName(name); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(def); + assertEquals(name, adapter.getName()); + var newName = "name2"; + adapter.setName(newName); + assertEquals(newName, def.getName()); + } + + @Test + void adapter_get_and_set_url() { + var def = new CompartmentDefinition(); + var url = "www.url.com"; + def.setUrl(url); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(def); + assertEquals(url, adapter.getUrl()); + var newUrl = "www.url2.com"; + adapter.setUrl(newUrl); + assertEquals(newUrl, def.getUrl()); + } + + @Test + void adapter_get_and_set_version() { + var def = new CompartmentDefinition(); + var version = "1.0.0"; + def.setVersion(version); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(def); + assertTrue(adapter.hasVersion()); + assertEquals(version, adapter.getVersion()); + var newVersion = "1.0.1"; + adapter.setVersion(newVersion); + assertEquals(newVersion, def.getVersion()); + } + + @Test + void adapter_get_and_set_status() { + var def = new CompartmentDefinition(); + var status = PublicationStatus.DRAFT; + def.setStatus(status); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(def); + assertEquals(status.toCode(), adapter.getStatus()); + assertThrows(UnprocessableEntityException.class, () -> adapter.setStatus("invalid-status")); + var newStatus = PublicationStatus.ACTIVE; + adapter.setStatus(newStatus.toCode()); + assertEquals(newStatus, PublicationStatus.fromCode(adapter.getStatus())); + } + + @Test + void adapter_get_and_set_dates() { + var def = new CompartmentDefinition(); + var date = new Date(); + def.setDate(date); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(def); + assertEquals(date, adapter.getDate()); + var newDate = new Date(); + newDate.setTime(100); + adapter.setDate(newDate); + assertEquals(newDate, def.getDate()); + assertThrows(UnprocessableEntityException.class, () -> adapter.setDateElement(new DateType())); + var newDateElement = new DateTimeType().setValue(new Date()); + adapter.setDateElement(newDateElement); + assertEquals(newDateElement, def.getDateElement()); + assertThrows(UnprocessableEntityException.class, () -> adapter.setEffectivePeriod(new Extension())); + } + + @Test + void adapter_get_experimental() { + var def = new CompartmentDefinition(); + var experimental = true; + def.setExperimental(experimental); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(def); + assertEquals(experimental, adapter.getExperimental()); + } + + @Test + void adapter_set_relatedArtifact() { + var def = new CompartmentDefinition(); + var relatedArtifactList = List.of(new RelatedArtifact()); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(def); + adapter.setRelatedArtifact(relatedArtifactList); + assertEquals(0, adapter.getRelatedArtifact().size()); + assertThrows(UnprocessableEntityException.class, () -> adapter.setRelatedArtifact(Arrays.asList(new Period()))); + assertThrows(UnprocessableEntityException.class, () -> adapter.getRelatedArtifactsOfType("depends")); + } + + @Test + void adapter_copy() { + var def = new CompartmentDefinition().setStatus(PublicationStatus.DRAFT); + def.setId("def-1"); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(def); + var copy = (CompartmentDefinition) adapter.copy(); + copy.setId("def-2"); + assertNotEquals(def.getId(), copy.getId()); + def.setStatus(PublicationStatus.ACTIVE); + assertNotEquals(adapter.getStatus(), copy.getStatus()); + } +} diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/dstu3/LibraryAdapterTest.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/dstu3/LibraryAdapterTest.java new file mode 100644 index 000000000..13a512447 --- /dev/null +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/dstu3/LibraryAdapterTest.java @@ -0,0 +1,237 @@ +package org.opencds.cqf.fhir.utility.adapter.dstu3; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.model.primitive.IdDt; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import org.hl7.fhir.dstu3.model.Attachment; +import org.hl7.fhir.dstu3.model.Bundle; +import org.hl7.fhir.dstu3.model.CodeableConcept; +import org.hl7.fhir.dstu3.model.Coding; +import org.hl7.fhir.dstu3.model.DataRequirement; +import org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus; +import org.hl7.fhir.dstu3.model.Library; +import org.hl7.fhir.dstu3.model.Period; +import org.hl7.fhir.dstu3.model.PlanDefinition; +import org.hl7.fhir.dstu3.model.Reference; +import org.hl7.fhir.dstu3.model.RelatedArtifact; +import org.hl7.fhir.dstu3.model.UsageContext; +import org.hl7.fhir.instance.model.api.IDomainResource; +import org.junit.jupiter.api.Test; +import org.opencds.cqf.fhir.utility.visitor.PackageVisitor; + +public class LibraryAdapterTest { + private final FhirContext fhirContext = FhirContext.forDstu3Cached(); + private final org.opencds.cqf.fhir.utility.adapter.AdapterFactory adapterFactory = new AdapterFactory(); + + @Test + void invalid_object_fails() { + assertThrows(IllegalArgumentException.class, () -> new LibraryAdapter(new PlanDefinition())); + } + + @Test + void adapter_accepts_visitor() { + var spyVisitor = spy(new PackageVisitor(fhirContext)); + doReturn(new Bundle()).when(spyVisitor).visit(any(LibraryAdapter.class), any(), any()); + IDomainResource library = new Library(); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(library); + adapter.accept(spyVisitor, null, null); + verify(spyVisitor, times(1)).visit(any(LibraryAdapter.class), any(), any()); + } + + @Test + void adapter_get_and_set_name() { + var library = new Library(); + var name = "name"; + library.setName(name); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(library); + assertEquals(name, adapter.getName()); + var newName = "name2"; + adapter.setName(newName); + assertEquals(newName, library.getName()); + } + + @Test + void adapter_get_and_set_url() { + var library = new Library(); + var url = "www.url.com"; + library.setUrl(url); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(library); + assertEquals(url, adapter.getUrl()); + var newUrl = "www.url2.com"; + adapter.setUrl(newUrl); + assertEquals(newUrl, library.getUrl()); + } + + @Test + void adapter_get_and_set_version() { + var library = new Library(); + var version = "1.0.0"; + library.setVersion(version); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(library); + assertTrue(adapter.hasVersion()); + assertEquals(version, adapter.getVersion()); + var newVersion = "1.0.1"; + adapter.setVersion(newVersion); + assertEquals(newVersion, library.getVersion()); + } + + @Test + void adapter_get_and_set_status() { + var library = new Library(); + var status = PublicationStatus.DRAFT; + library.setStatus(status); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(library); + assertEquals(status.toCode(), adapter.getStatus()); + assertThrows(UnprocessableEntityException.class, () -> adapter.setStatus("invalid-status")); + var newStatus = PublicationStatus.ACTIVE; + adapter.setStatus(newStatus.toCode()); + assertEquals(newStatus, PublicationStatus.fromCode(adapter.getStatus())); + } + + @Test + void adapter_get_and_set_dates() { + var library = new Library(); + var date = new Date(); + var approvalDate = new Date(); + var effectivePeriod = new Period() + .setStart(java.sql.Date.valueOf(LocalDate.parse("2020-01-01"))) + .setEnd(java.sql.Date.valueOf(LocalDate.parse("2020-12-31"))); + library.setDate(date); + library.setApprovalDate(approvalDate); + library.setEffectivePeriod(effectivePeriod); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(library); + assertEquals(date, adapter.getDate()); + assertEquals(approvalDate, adapter.getApprovalDate()); + assertEquals(effectivePeriod, adapter.getEffectivePeriod()); + var newDate = new Date(); + newDate.setTime(100); + adapter.setDate(newDate); + assertEquals(newDate, library.getDate()); + var newApprovalDate = new Date(); + newApprovalDate.setTime(100); + adapter.setApprovalDate(newApprovalDate); + assertEquals(newApprovalDate, library.getApprovalDate()); + var newEffectivePeriod = new Period() + .setStart(java.sql.Date.valueOf(LocalDate.parse("2021-01-01"))) + .setEnd(java.sql.Date.valueOf(LocalDate.parse("2021-12-31"))); + adapter.setEffectivePeriod(newEffectivePeriod); + assertEquals(newEffectivePeriod, adapter.getEffectivePeriod()); + } + + @Test + void adapter_get_experimental() { + var library = new Library(); + var experimental = true; + library.setExperimental(experimental); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(library); + assertEquals(experimental, adapter.getExperimental()); + } + + @Test + void adapter_set_relatedArtifact() { + var library = new Library(); + var relatedArtifactList = List.of(new RelatedArtifact()); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(library); + adapter.setRelatedArtifact(relatedArtifactList); + assertEquals(relatedArtifactList, library.getRelatedArtifact()); + assertEquals(relatedArtifactList, adapter.getRelatedArtifact()); + } + + @Test + void adapter_copy() { + var library = new Library().setStatus(PublicationStatus.DRAFT); + library.setId("library-1"); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(library); + var copy = (Library) adapter.copy(); + var adapterCopy = new LibraryAdapter(copy); + adapterCopy.setId(new IdDt("Library", "library-2")); + assertNotEquals(library.getId(), copy.getId()); + library.setStatus(PublicationStatus.ACTIVE); + assertNotEquals(adapter.getStatus(), copy.getStatus()); + } + + @Test + void adapter_get_all_dependencies() { + var dependencies = List.of( + "profileRef", "relatedArtifactRef", "dataRequirementProfileRef", "dataRequirementCodeFilterRef"); + var library = new Library(); + library.getMeta().addProfile(dependencies.get(0)); + library.getRelatedArtifactFirstRep().setResource(new Reference(dependencies.get(1))); + library.addDataRequirement().addProfile(dependencies.get(2)); + library.addDataRequirement().addCodeFilter().setValueSet(new Reference(dependencies.get(3))); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(library); + var extractedDependencies = adapter.getDependencies(); + assertEquals(extractedDependencies.size(), dependencies.size()); + extractedDependencies.forEach(dep -> { + assertTrue(dependencies.indexOf(dep.getReference()) >= 0); + }); + } + + @Test + void adapter_get_and_set_content() { + var library = new Library(); + var adapter = (LibraryAdapter) adapterFactory.createKnowledgeArtifactAdapter(library); + var contentList = new ArrayList(); + contentList.add(new Attachment().setContentType("text/cql").setData(new byte[10])); + adapter.setContent(contentList); + assertTrue(adapter.hasContent()); + assertEquals(contentList, adapter.getContent()); + adapter.addContent().setContentType("text/xml").setData(new byte[20]); + assertEquals(2, adapter.getContent().size()); + assertEquals("text/xml", adapter.getContent().get(1).getContentType()); + } + + @Test + void adapter_get_and_set_type() { + var type = new CodeableConcept(new Coding("www.test.com", "test", "Test")); + var library = new Library().setType(type); + var adapter = (LibraryAdapter) adapterFactory.createKnowledgeArtifactAdapter(library); + assertEquals(type, adapter.getType()); + assertThrows(UnprocessableEntityException.class, () -> adapter.setType("test")); + var newType = new CodeableConcept(new Coding("http://hl7.org/fhir/ValueSet/library-type", "logic-library", "")); + adapter.setType(newType.getCoding().get(0).getCode()); + assertEquals( + newType.getCoding().get(0).getCode(), + ((CodeableConcept) adapter.getType()).getCoding().get(0).getCode()); + assertEquals( + newType.getCoding().get(0).getSystem(), + ((CodeableConcept) adapter.getType()).getCoding().get(0).getSystem()); + } + + @Test + void adapter_get_and_set_dataRequirement() { + var library = new Library(); + var adapter = (LibraryAdapter) adapterFactory.createKnowledgeArtifactAdapter(library); + var dataRequirements = new ArrayList(); + dataRequirements.add(new DataRequirement().setType("Patient")); + adapter.setDataRequirement(dataRequirements); + assertEquals(dataRequirements, library.getDataRequirement()); + adapter.addDataRequirement(new DataRequirement().setType("Observation")); + assertEquals(library.getDataRequirement(), adapter.getDataRequirement()); + assertEquals(2, adapter.getDataRequirement().size()); + } + + @Test + void adapter_get_useContext() { + var library = new Library(); + var useContext = new UsageContext().setCode(new Coding("www.test.com", "test", "Test")); + library.setUseContext(Collections.singletonList(useContext)); + var adapter = (LibraryAdapter) adapterFactory.createKnowledgeArtifactAdapter(library); + assertEquals(useContext, adapter.getUseContext().get(0)); + } +} diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/dstu3/MeasureAdapterTest.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/dstu3/MeasureAdapterTest.java new file mode 100644 index 000000000..20c9b928e --- /dev/null +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/dstu3/MeasureAdapterTest.java @@ -0,0 +1,220 @@ +package org.opencds.cqf.fhir.utility.adapter.dstu3; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.model.primitive.IdDt; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import java.time.LocalDate; +import java.util.Date; +import java.util.List; +import org.hl7.fhir.dstu3.model.Bundle; +import org.hl7.fhir.dstu3.model.CodeableConcept; +import org.hl7.fhir.dstu3.model.Coding; +import org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus; +import org.hl7.fhir.dstu3.model.Library; +import org.hl7.fhir.dstu3.model.Measure; +import org.hl7.fhir.dstu3.model.Patient; +import org.hl7.fhir.dstu3.model.Period; +import org.hl7.fhir.dstu3.model.Reference; +import org.hl7.fhir.dstu3.model.RelatedArtifact; +import org.hl7.fhir.instance.model.api.IDomainResource; +import org.junit.jupiter.api.Test; +import org.opencds.cqf.fhir.utility.Constants; +import org.opencds.cqf.fhir.utility.visitor.PackageVisitor; + +public class MeasureAdapterTest { + private final FhirContext fhirContext = FhirContext.forDstu3Cached(); + private final org.opencds.cqf.fhir.utility.adapter.AdapterFactory adapterFactory = new AdapterFactory(); + + @Test + void invalid_object_fails() { + assertThrows(IllegalArgumentException.class, () -> new MeasureAdapter(new Library())); + } + + @Test + void adapter_accepts_visitor() { + var spyVisitor = spy(new PackageVisitor(fhirContext)); + doReturn(new Bundle()).when(spyVisitor).visit(any(MeasureAdapter.class), any(), any()); + IDomainResource measure = new Measure(); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(measure); + adapter.accept(spyVisitor, null, null); + verify(spyVisitor, times(1)).visit(any(MeasureAdapter.class), any(), any()); + } + + @Test + void adapter_get_and_set_name() { + var measure = new Measure(); + var name = "name"; + measure.setName(name); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(measure); + assertEquals(name, adapter.getName()); + var newName = "name2"; + adapter.setName(newName); + assertEquals(newName, measure.getName()); + } + + @Test + void adapter_get_and_set_url() { + var measure = new Measure(); + var url = "www.url.com"; + measure.setUrl(url); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(measure); + assertEquals(url, adapter.getUrl()); + var newUrl = "www.url2.com"; + adapter.setUrl(newUrl); + assertEquals(newUrl, measure.getUrl()); + } + + @Test + void adapter_get_and_set_version() { + var measure = new Measure(); + var version = "1.0.0"; + measure.setVersion(version); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(measure); + assertTrue(adapter.hasVersion()); + assertEquals(version, adapter.getVersion()); + var newVersion = "1.0.1"; + adapter.setVersion(newVersion); + assertEquals(newVersion, measure.getVersion()); + } + + @Test + void adapter_get_and_set_status() { + var measure = new Measure(); + var status = PublicationStatus.DRAFT; + measure.setStatus(status); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(measure); + assertEquals(status.toCode(), adapter.getStatus()); + assertThrows(UnprocessableEntityException.class, () -> adapter.setStatus("invalid-status")); + var newStatus = PublicationStatus.ACTIVE; + adapter.setStatus(newStatus.toCode()); + assertEquals(newStatus, PublicationStatus.fromCode(adapter.getStatus())); + } + + @Test + void adapter_get_and_set_dates() { + var measure = new Measure(); + var date = new Date(); + var approvalDate = new Date(); + var effectivePeriod = new Period() + .setStart(java.sql.Date.valueOf(LocalDate.parse("2020-01-01"))) + .setEnd(java.sql.Date.valueOf(LocalDate.parse("2020-12-31"))); + measure.setDate(date); + measure.setApprovalDate(approvalDate); + measure.setEffectivePeriod(effectivePeriod); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(measure); + assertEquals(date, adapter.getDate()); + assertEquals(approvalDate, adapter.getApprovalDate()); + assertEquals(effectivePeriod, adapter.getEffectivePeriod()); + var newDate = new Date(); + newDate.setTime(100); + adapter.setDate(newDate); + assertEquals(newDate, measure.getDate()); + var newApprovalDate = new Date(); + newApprovalDate.setTime(100); + adapter.setApprovalDate(newApprovalDate); + assertEquals(newApprovalDate, measure.getApprovalDate()); + var newEffectivePeriod = new Period() + .setStart(java.sql.Date.valueOf(LocalDate.parse("2021-01-01"))) + .setEnd(java.sql.Date.valueOf(LocalDate.parse("2021-12-31"))); + adapter.setEffectivePeriod(newEffectivePeriod); + assertEquals(newEffectivePeriod, adapter.getEffectivePeriod()); + } + + @Test + void adapter_get_experimental() { + var measure = new Measure(); + var experimental = true; + measure.setExperimental(experimental); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(measure); + assertEquals(experimental, adapter.getExperimental()); + } + + @Test + void adapter_set_relatedArtifact() { + var measure = new Measure(); + var relatedArtifactList = List.of(new RelatedArtifact()); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(measure); + adapter.setRelatedArtifact(relatedArtifactList); + assertEquals(relatedArtifactList, measure.getRelatedArtifact()); + assertEquals(relatedArtifactList, adapter.getRelatedArtifact()); + } + + @Test + void adapter_copy() { + var measure = new Measure().setStatus(PublicationStatus.DRAFT); + measure.setId("plan-1"); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(measure); + var copy = (Measure) adapter.copy(); + var adapterCopy = new MeasureAdapter(copy); + adapterCopy.setId(new IdDt("Measure", "plan-2")); + assertNotEquals(measure.getId(), copy.getId()); + measure.setStatus(PublicationStatus.ACTIVE); + assertNotEquals(adapter.getStatus(), copy.getStatus()); + } + + @Test + void adapter_get_all_dependencies() { + var dependencies = List.of( + "profileRef", + "relatedArtifactRef", + "libraryRef", + "inputParametersRef", + "expansionParametersRef", + "cqlOptionsRef", + "componentRef"); + var measure = new Measure(); + measure.getMeta().addProfile(dependencies.get(0)); + measure.getRelatedArtifactFirstRep().setResource(new Reference(dependencies.get(1))); + measure.getLibrary().add(new Reference(dependencies.get(2))); + measure.addExtension(Constants.CQFM_INPUT_PARAMETERS, new Reference(dependencies.get(3))); + measure.addExtension(Constants.CQF_EXPANSION_PARAMETERS, new Reference(dependencies.get(4))); + measure.addExtension(Constants.CQF_CQL_OPTIONS, new Reference(dependencies.get(5))); + measure.addExtension( + Constants.CQFM_COMPONENT, new RelatedArtifact().setResource(new Reference(dependencies.get(6)))); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(measure); + var extractedDependencies = adapter.getDependencies(); + assertEquals(extractedDependencies.size(), dependencies.size()); + extractedDependencies.forEach(dep -> { + assertTrue(dependencies.indexOf(dep.getReference()) >= 0); + }); + } + + @Test + void adapter_get_all_dependencies_with_effective_data_requirements() { + var dependencies = List.of( + "libraryProfileRef", + "relatedArtifactRef", + "dataRequirementProfileRef", + "dataRequirementCodeFilterRef", + "measureProfileRef"); + var library = new Library() + .setType(new CodeableConcept(new Coding( + "http://terminology.hl7.org/CodeSystem/library-type", + "module-definition", + "Module Definition"))); + library.setId("test"); + library.getMeta().addProfile(dependencies.get(0)); + library.getRelatedArtifactFirstRep().setResource(new Reference(dependencies.get(1))); + library.addDataRequirement().addProfile(dependencies.get(2)); + library.addDataRequirement().addCodeFilter().setValueSet(new Reference(dependencies.get(3))); + var measure = new Measure().addContained(new Patient()).addContained(library); + measure.getMeta().addProfile(dependencies.get(4)); + measure.addExtension(Constants.CQFM_EFFECTIVE_DATA_REQUIREMENTS, new Reference("#test")); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(measure); + var extractedDependencies = adapter.getDependencies(); + assertEquals(dependencies.size(), extractedDependencies.size()); + extractedDependencies.forEach(dep -> { + assertTrue(dependencies.indexOf(dep.getReference()) >= 0); + }); + } +} diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/dstu3/PlanDefinitionAdapterTest.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/dstu3/PlanDefinitionAdapterTest.java index 72ec340aa..4696f5cab 100644 --- a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/dstu3/PlanDefinitionAdapterTest.java +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/dstu3/PlanDefinitionAdapterTest.java @@ -1,6 +1,8 @@ package org.opencds.cqf.fhir.utility.adapter.dstu3; +import static org.junit.Assert.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; @@ -8,10 +10,17 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.model.primitive.IdDt; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import java.time.LocalDate; import java.util.Date; import java.util.List; import org.hl7.fhir.dstu3.model.Bundle; +import org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus; import org.hl7.fhir.dstu3.model.Extension; +import org.hl7.fhir.dstu3.model.Library; +import org.hl7.fhir.dstu3.model.Period; import org.hl7.fhir.dstu3.model.PlanDefinition; import org.hl7.fhir.dstu3.model.Reference; import org.hl7.fhir.dstu3.model.RelatedArtifact; @@ -19,15 +28,23 @@ import org.hl7.fhir.dstu3.model.UriType; import org.hl7.fhir.instance.model.api.IDomainResource; import org.junit.jupiter.api.Test; -import org.opencds.cqf.fhir.utility.visitor.KnowledgeArtifactPackageVisitor; +import org.opencds.cqf.fhir.utility.visitor.PackageVisitor; public class PlanDefinitionAdapterTest { + private final FhirContext fhirContext = FhirContext.forDstu3Cached(); + private final org.opencds.cqf.fhir.utility.adapter.AdapterFactory adapterFactory = new AdapterFactory(); + + @Test + void invalid_object_fails() { + assertThrows(IllegalArgumentException.class, () -> new PlanDefinitionAdapter(new Library())); + } + @Test void adapter_accepts_visitor() { - var spyVisitor = spy(new KnowledgeArtifactPackageVisitor()); + var spyVisitor = spy(new PackageVisitor(fhirContext)); doReturn(new Bundle()).when(spyVisitor).visit(any(PlanDefinitionAdapter.class), any(), any()); IDomainResource planDef = new PlanDefinition(); - var adapter = new PlanDefinitionAdapter(planDef); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(planDef); adapter.accept(spyVisitor, null, null); verify(spyVisitor, times(1)).visit(any(PlanDefinitionAdapter.class), any(), any()); } @@ -37,7 +54,7 @@ void adapter_get_and_set_name() { var planDef = new PlanDefinition(); var name = "name"; planDef.setName(name); - var adapter = new PlanDefinitionAdapter(planDef); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(planDef); assertEquals(name, adapter.getName()); var newName = "name2"; adapter.setName(newName); @@ -49,7 +66,7 @@ void adapter_get_and_set_url() { var planDef = new PlanDefinition(); var url = "www.url.com"; planDef.setUrl(url); - var adapter = new PlanDefinitionAdapter(planDef); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(planDef); assertEquals(url, adapter.getUrl()); var newUrl = "www.url2.com"; adapter.setUrl(newUrl); @@ -57,16 +74,59 @@ void adapter_get_and_set_url() { } @Test - void adapter_get_and_set_approvalDate() { + void adapter_get_and_set_version() { + var planDef = new PlanDefinition(); + var version = "1.0.0"; + planDef.setVersion(version); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(planDef); + assertTrue(adapter.hasVersion()); + assertEquals(version, adapter.getVersion()); + var newVersion = "1.0.1"; + adapter.setVersion(newVersion); + assertEquals(newVersion, planDef.getVersion()); + } + + @Test + void adapter_get_and_set_status() { var planDef = new PlanDefinition(); + var status = PublicationStatus.DRAFT; + planDef.setStatus(status); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(planDef); + assertEquals(status.toCode(), adapter.getStatus()); + assertThrows(UnprocessableEntityException.class, () -> adapter.setStatus("invalid-status")); + var newStatus = PublicationStatus.ACTIVE; + adapter.setStatus(newStatus.toCode()); + assertEquals(newStatus, PublicationStatus.fromCode(adapter.getStatus())); + } + + @Test + void adapter_get_and_set_dates() { + var planDef = new PlanDefinition(); + var date = new Date(); var approvalDate = new Date(); + var effectivePeriod = new Period() + .setStart(java.sql.Date.valueOf(LocalDate.parse("2020-01-01"))) + .setEnd(java.sql.Date.valueOf(LocalDate.parse("2020-12-31"))); + planDef.setDate(date); planDef.setApprovalDate(approvalDate); - var adapter = new PlanDefinitionAdapter(planDef); + planDef.setEffectivePeriod(effectivePeriod); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(planDef); + assertEquals(date, adapter.getDate()); assertEquals(approvalDate, adapter.getApprovalDate()); + assertEquals(effectivePeriod, adapter.getEffectivePeriod()); + var newDate = new Date(); + newDate.setTime(100); + adapter.setDate(newDate); + assertEquals(newDate, planDef.getDate()); var newApprovalDate = new Date(); newApprovalDate.setTime(100); adapter.setApprovalDate(newApprovalDate); assertEquals(newApprovalDate, planDef.getApprovalDate()); + var newEffectivePeriod = new Period() + .setStart(java.sql.Date.valueOf(LocalDate.parse("2021-01-01"))) + .setEnd(java.sql.Date.valueOf(LocalDate.parse("2021-12-31"))); + adapter.setEffectivePeriod(newEffectivePeriod); + assertEquals(newEffectivePeriod, adapter.getEffectivePeriod()); } @Test @@ -74,7 +134,7 @@ void adapter_get_experimental() { var planDef = new PlanDefinition(); var experimental = true; planDef.setExperimental(experimental); - var adapter = new PlanDefinitionAdapter(planDef); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(planDef); assertEquals(experimental, adapter.getExperimental()); } @@ -82,15 +142,29 @@ void adapter_get_experimental() { void adapter_set_relatedArtifact() { var planDef = new PlanDefinition(); var relatedArtifactList = List.of(new RelatedArtifact()); - var adapter = new PlanDefinitionAdapter(planDef); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(planDef); adapter.setRelatedArtifact(relatedArtifactList); assertEquals(relatedArtifactList, planDef.getRelatedArtifact()); assertEquals(relatedArtifactList, adapter.getRelatedArtifact()); } + @Test + void adapter_copy() { + var planDef = new PlanDefinition().setStatus(PublicationStatus.DRAFT); + planDef.setId("plan-1"); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(planDef); + var copy = (PlanDefinition) adapter.copy(); + var adapterCopy = new PlanDefinitionAdapter(copy); + adapterCopy.setId(new IdDt("PlanDefinition", "plan-2")); + assertNotEquals(planDef.getId(), copy.getId()); + planDef.setStatus(PublicationStatus.ACTIVE); + assertNotEquals(adapter.getStatus(), copy.getStatus()); + } + @Test void adapter_get_all_dependencies() { var dependencies = List.of( + "profileRef", "relatedArtifactRef", "libraryRef", "actionTriggerDataReqProfile", @@ -107,27 +181,28 @@ void adapter_get_all_dependencies() { "cpgPartOfExtRef", "nestedActionDefinitionReference"); var planDef = new PlanDefinition(); - planDef.getRelatedArtifactFirstRep().setResource(new Reference(dependencies.get(0))); - planDef.getLibraryFirstRep().setReference(dependencies.get(1)); + planDef.getMeta().addProfile(dependencies.get(0)); + planDef.getRelatedArtifactFirstRep().setResource(new Reference(dependencies.get(1))); + planDef.getLibraryFirstRep().setReference(dependencies.get(2)); var action = planDef.getActionFirstRep(); action.getTriggerDefinitionFirstRep() .getEventData() - .setProfile(List.of(new UriType(dependencies.get(2)))) + .setProfile(List.of(new UriType(dependencies.get(3)))) .getCodeFilterFirstRep() - .setValueSet(new StringType(dependencies.get(3))); + .setValueSet(new StringType(dependencies.get(4))); action.getInputFirstRep() - .setProfile(List.of(new UriType(dependencies.get(4)))) + .setProfile(List.of(new UriType(dependencies.get(5)))) .getCodeFilterFirstRep() - .setValueSet(new StringType(dependencies.get(5))); + .setValueSet(new StringType(dependencies.get(6))); action.getOutputFirstRep() - .setProfile(List.of(new UriType(dependencies.get(6)))) + .setProfile(List.of(new UriType(dependencies.get(7)))) .getCodeFilterFirstRep() - .setValueSet(new StringType(dependencies.get(7))); - action.getDefinition().setReference(dependencies.get(8)); + .setValueSet(new StringType(dependencies.get(8))); + action.getDefinition().setReference(dependencies.get(9)); planDef.addExtension(new Extension( - "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-partOf", new UriType(dependencies.get(9)))); - action.addAction().getDefinition().setReference(dependencies.get(10)); - var adapter = new PlanDefinitionAdapter(planDef); + "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-partOf", new UriType(dependencies.get(10)))); + action.addAction().getDefinition().setReference(dependencies.get(11)); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(planDef); var extractedDependencies = adapter.getDependencies(); assertEquals(extractedDependencies.size(), dependencies.size()); extractedDependencies.forEach(dep -> { diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/dstu3/QuestionnaireAdapterTest.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/dstu3/QuestionnaireAdapterTest.java new file mode 100644 index 000000000..5cc582b64 --- /dev/null +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/dstu3/QuestionnaireAdapterTest.java @@ -0,0 +1,205 @@ +package org.opencds.cqf.fhir.utility.adapter.dstu3; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import java.time.LocalDate; +import java.util.Date; +import java.util.List; +import org.hl7.fhir.dstu3.model.Bundle; +import org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus; +import org.hl7.fhir.dstu3.model.Library; +import org.hl7.fhir.dstu3.model.Period; +import org.hl7.fhir.dstu3.model.Questionnaire; +import org.hl7.fhir.dstu3.model.Reference; +import org.hl7.fhir.dstu3.model.RelatedArtifact; +import org.hl7.fhir.dstu3.model.UriType; +import org.hl7.fhir.instance.model.api.IDomainResource; +import org.junit.jupiter.api.Test; +import org.opencds.cqf.fhir.utility.Constants; +import org.opencds.cqf.fhir.utility.visitor.PackageVisitor; + +public class QuestionnaireAdapterTest { + private final FhirContext fhirContext = FhirContext.forDstu3Cached(); + private final org.opencds.cqf.fhir.utility.adapter.AdapterFactory adapterFactory = new AdapterFactory(); + + @Test + void invalid_object_fails() { + assertThrows(IllegalArgumentException.class, () -> new QuestionnaireAdapter(new Library())); + } + + @Test + void adapter_accepts_visitor() { + var spyVisitor = spy(new PackageVisitor(fhirContext)); + doReturn(new Bundle()).when(spyVisitor).visit(any(QuestionnaireAdapter.class), any(), any()); + IDomainResource questionnaire = new Questionnaire(); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(questionnaire); + adapter.accept(spyVisitor, null, null); + verify(spyVisitor, times(1)).visit(any(QuestionnaireAdapter.class), any(), any()); + } + + @Test + void adapter_get_and_set_name() { + var questionnaire = new Questionnaire(); + var name = "name"; + questionnaire.setName(name); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(questionnaire); + assertEquals(name, adapter.getName()); + var newName = "name2"; + adapter.setName(newName); + assertEquals(newName, questionnaire.getName()); + } + + @Test + void adapter_get_and_set_url() { + var questionnaire = new Questionnaire(); + var url = "www.url.com"; + questionnaire.setUrl(url); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(questionnaire); + assertEquals(url, adapter.getUrl()); + var newUrl = "www.url2.com"; + adapter.setUrl(newUrl); + assertEquals(newUrl, questionnaire.getUrl()); + } + + @Test + void adapter_get_and_set_version() { + var questionnaire = new Questionnaire(); + var version = "1.0.0"; + questionnaire.setVersion(version); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(questionnaire); + assertTrue(adapter.hasVersion()); + assertEquals(version, adapter.getVersion()); + var newVersion = "1.0.1"; + adapter.setVersion(newVersion); + assertEquals(newVersion, questionnaire.getVersion()); + } + + @Test + void adapter_get_and_set_status() { + var questionnaire = new Questionnaire(); + var status = PublicationStatus.DRAFT; + questionnaire.setStatus(status); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(questionnaire); + assertEquals(status.toCode(), adapter.getStatus()); + assertThrows(UnprocessableEntityException.class, () -> adapter.setStatus("invalid-status")); + var newStatus = PublicationStatus.ACTIVE; + adapter.setStatus(newStatus.toCode()); + assertEquals(newStatus, PublicationStatus.fromCode(adapter.getStatus())); + } + + @Test + void adapter_get_and_set_dates() { + var questionnaire = new Questionnaire(); + var date = new Date(); + var approvalDate = new Date(); + var effectivePeriod = new Period() + .setStart(java.sql.Date.valueOf(LocalDate.parse("2020-01-01"))) + .setEnd(java.sql.Date.valueOf(LocalDate.parse("2020-12-31"))); + questionnaire.setDate(date); + questionnaire.setApprovalDate(approvalDate); + questionnaire.setEffectivePeriod(effectivePeriod); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(questionnaire); + assertEquals(date, adapter.getDate()); + assertEquals(approvalDate, adapter.getApprovalDate()); + assertEquals(effectivePeriod, adapter.getEffectivePeriod()); + var newDate = new Date(); + newDate.setTime(100); + adapter.setDate(newDate); + assertEquals(newDate, questionnaire.getDate()); + var newApprovalDate = new Date(); + newApprovalDate.setTime(100); + adapter.setApprovalDate(newApprovalDate); + assertEquals(newApprovalDate, questionnaire.getApprovalDate()); + var newEffectivePeriod = new Period() + .setStart(java.sql.Date.valueOf(LocalDate.parse("2021-01-01"))) + .setEnd(java.sql.Date.valueOf(LocalDate.parse("2021-12-31"))); + adapter.setEffectivePeriod(newEffectivePeriod); + assertEquals(newEffectivePeriod, adapter.getEffectivePeriod()); + } + + @Test + void adapter_get_experimental() { + var questionnaire = new Questionnaire(); + var experimental = true; + questionnaire.setExperimental(experimental); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(questionnaire); + assertEquals(experimental, adapter.getExperimental()); + } + + @Test + void adapter_set_relatedArtifact() { + var questionnaire = new Questionnaire(); + var relatedArtifactList = List.of(new RelatedArtifact()); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(questionnaire); + adapter.setRelatedArtifact(relatedArtifactList); + assertEquals(0, adapter.getRelatedArtifact().size()); + } + + @Test + void adapter_copy() { + var questionnaire = new Questionnaire().setStatus(PublicationStatus.DRAFT); + questionnaire.setId("plan-1"); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(questionnaire); + var copy = (Questionnaire) adapter.copy(); + copy.setId("plan-2"); + assertNotEquals(questionnaire.getId(), copy.getId()); + questionnaire.setStatus(PublicationStatus.ACTIVE); + assertNotEquals(adapter.getStatus(), copy.getStatus()); + } + + @Test + void adapter_get_all_dependencies() { + var dependencies = List.of( + "profileRef", + "cqfLibraryRef", + // "variableRef", + "itemDefinitionRef", + "answerValueSetRef", + // "itemMediaRef", + // "itemAnswerMediaRef", + "unitValueSetRef", + "referenceProfileRef", + // "candidateExpressionRef", + "lookupQuestionnaireRef", + // "itemVariableRef", + // "initialExpressionRef", + // "calculatedExpressionRef", + // "calculatedValueRef", + // "expressionRef", + "subQuestionnaireRef"); + var questionnaire = new Questionnaire(); + questionnaire.getMeta().addProfile(dependencies.get(0)); + questionnaire.addExtension(Constants.CQIF_LIBRARY, new Reference(dependencies.get(1))); + // var variableExt = new Extension(Constants.VARIABLE_EXTENSION).setValue(new + // Expression().setReference(dependencies.get(2))); + // questionnaire.addExtension(variableExt); + questionnaire.addItem().setDefinition(dependencies.get(2) + "#Observation"); + questionnaire.addItem().setOptions(new Reference(dependencies.get(3))); + questionnaire.addItem().addExtension(Constants.QUESTIONNAIRE_UNIT_VALUE_SET, new UriType(dependencies.get(4))); + questionnaire + .addItem() + .addExtension(Constants.QUESTIONNAIRE_REFERENCE_PROFILE, new UriType(dependencies.get(5))); + questionnaire + .addItem() + .addExtension(Constants.SDC_QUESTIONNAIRE_LOOKUP_QUESTIONNAIRE, new UriType(dependencies.get(6))); + questionnaire + .addItem() + .addExtension(Constants.SDC_QUESTIONNAIRE_SUB_QUESTIONNAIRE, new UriType(dependencies.get(7))); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(questionnaire); + var extractedDependencies = adapter.getDependencies(); + assertEquals(dependencies.size(), extractedDependencies.size()); + extractedDependencies.forEach(dep -> { + assertTrue(dependencies.indexOf(dep.getReference()) >= 0); + }); + } +} diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/dstu3/ResourceAdapterTest.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/dstu3/ResourceAdapterTest.java new file mode 100644 index 000000000..c10fd3d1c --- /dev/null +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/dstu3/ResourceAdapterTest.java @@ -0,0 +1,118 @@ +package org.opencds.cqf.fhir.utility.adapter.dstu3; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import ca.uhn.fhir.model.primitive.IdDt; +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.read.ListAppender; +import java.util.Date; +import java.util.List; +import org.hl7.fhir.dstu3.model.BooleanType; +import org.hl7.fhir.dstu3.model.Bundle; +import org.hl7.fhir.dstu3.model.Extension; +import org.hl7.fhir.dstu3.model.HumanName; +import org.hl7.fhir.dstu3.model.IdType; +import org.hl7.fhir.dstu3.model.Library; +import org.hl7.fhir.dstu3.model.Meta; +import org.hl7.fhir.dstu3.model.Patient; +import org.hl7.fhir.dstu3.model.PlanDefinition; +import org.hl7.fhir.instance.model.api.IIdType; +import org.junit.jupiter.api.Test; +import org.opencds.cqf.fhir.utility.adapter.Adapter; +import org.slf4j.LoggerFactory; + +public class ResourceAdapterTest { + private final org.opencds.cqf.fhir.utility.adapter.AdapterFactory adapterFactory = new AdapterFactory(); + + @Test + void invalid_object_fails() { + assertThrows(IllegalArgumentException.class, () -> new ResourceAdapter(null)); + assertThrows(IllegalArgumentException.class, () -> new ResourceAdapter(new org.hl7.fhir.r5.model.Library())); + } + + @Test + void adapter_get_and_set_property() { + var resource = new Patient(); + var id = new IdDt("patient-1"); + resource.setId(id); + var adapter = adapterFactory.createResource(resource); + assertTrue(((ResourceAdapter) adapter).isDomainResource()); + assertEquals(resource, ((ResourceAdapter) adapter).getDomainResource().get()); + assertEquals(id.getValue(), ((IIdType) adapter.getSingleProperty("id")).getValue()); + var newId = new IdType("patient-2"); + adapter.setProperty("id", newId); + assertEquals(newId, resource.getIdElement()); + assertEquals("id", adapter.getTypesForProperty("id")[0]); + assertNotNull(adapter.makeProperty("language")); + assertNull(adapter.getSingleProperty("meta")); + var meta = (Meta) adapter.addChild("meta"); + var date = new Date(); + meta.setLastUpdated(date); + assertEquals(date, ((Meta) adapter.getProperty("meta")[0]).getLastUpdated()); + resource.addName(new HumanName().addGiven("name1")); + resource.addName(new HumanName().addGiven("name2")); + assertThrows(IllegalArgumentException.class, () -> adapter.getSingleProperty("name")); + } + + @Test + void adapter_copy() { + var resource = new Patient(); + resource.setId("patient-1"); + resource.setMeta(new Meta().setLastUpdated(new Date())); + var adapter = adapterFactory.createResource(resource); + var copy = (Patient) adapter.copy(); + assertTrue(adapter.equalsDeep(copy)); + var newDate = new Date(); + newDate.setTime(100); + copy.setMeta(new Meta().setLastUpdated(newDate)); + assertFalse(adapter.equalsDeep(copy)); + assertTrue(adapter.equalsShallow(copy)); + copy.setId("patient-2"); + assertFalse(adapter.equalsShallow(copy)); + resource.setLanguage("FR"); + adapter.copyValues(copy); + assertEquals("FR", copy.getLanguage()); + } + + @Test + void adapter_get_and_set_extension() { + var logger = (Logger) LoggerFactory.getLogger(Adapter.class); + var listAppender = new ListAppender(); + listAppender.start(); + logger.addAppender(listAppender); + var bundle = new Bundle(); + var bundleAdapter = new ResourceAdapter(bundle); + assertFalse(bundleAdapter.hasExtension()); + bundleAdapter.setExtension(List.of(new Extension())); + assertEquals(1, listAppender.list.size()); + assertEquals(Level.DEBUG, listAppender.list.get(0).getLevel()); + bundleAdapter.addExtension(new Extension()); + assertEquals(2, listAppender.list.size()); + assertEquals(Level.DEBUG, listAppender.list.get(1).getLevel()); + var resource = new Patient(); + var extensionList = List.of(new Extension().setUrl("test-extension-url").setValue(new BooleanType(true))); + var adapter = adapterFactory.createResource(resource); + adapter.setExtension(extensionList); + assertTrue(adapter.hasExtension()); + assertEquals(extensionList, resource.getExtension()); + assertEquals(extensionList, adapter.getExtension()); + assertTrue(adapter.hasExtension("test-extension-url")); + } + + @Test + void adapter_get_contained() { + var resource = new PlanDefinition(); + resource.addContained(new Library()); + var adapter = adapterFactory.createResource(resource); + assertTrue(adapter.hasContained()); + assertNotNull(adapter.getContained()); + assertFalse(adapter.hasContained(adapter.getContained().get(0))); + } +} diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/dstu3/StructureDefinitionAdapterTest.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/dstu3/StructureDefinitionAdapterTest.java new file mode 100644 index 000000000..9605aab35 --- /dev/null +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/dstu3/StructureDefinitionAdapterTest.java @@ -0,0 +1,184 @@ +package org.opencds.cqf.fhir.utility.adapter.dstu3; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import java.time.LocalDate; +import java.util.Date; +import java.util.List; +import org.hl7.fhir.dstu3.model.Bundle; +import org.hl7.fhir.dstu3.model.ElementDefinition.ElementDefinitionBindingComponent; +import org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus; +import org.hl7.fhir.dstu3.model.Library; +import org.hl7.fhir.dstu3.model.Period; +import org.hl7.fhir.dstu3.model.RelatedArtifact; +import org.hl7.fhir.dstu3.model.StructureDefinition; +import org.hl7.fhir.dstu3.model.UriType; +import org.hl7.fhir.instance.model.api.IDomainResource; +import org.junit.jupiter.api.Test; +import org.opencds.cqf.fhir.utility.visitor.PackageVisitor; + +public class StructureDefinitionAdapterTest { + private final FhirContext fhirContext = FhirContext.forDstu3Cached(); + private final org.opencds.cqf.fhir.utility.adapter.AdapterFactory adapterFactory = new AdapterFactory(); + + @Test + void invalid_object_fails() { + assertThrows(IllegalArgumentException.class, () -> new StructureDefinitionAdapter(new Library())); + } + + @Test + void adapter_accepts_visitor() { + var spyVisitor = spy(new PackageVisitor(fhirContext)); + doReturn(new Bundle()).when(spyVisitor).visit(any(StructureDefinitionAdapter.class), any(), any()); + IDomainResource structureDef = new StructureDefinition(); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(structureDef); + assertEquals(structureDef, adapter.get()); + adapter.accept(spyVisitor, null, null); + verify(spyVisitor, times(1)).visit(any(StructureDefinitionAdapter.class), any(), any()); + } + + @Test + void adapter_get_and_set_name() { + var structureDef = new StructureDefinition(); + var name = "name"; + structureDef.setName(name); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(structureDef); + assertEquals(name, adapter.getName()); + var newName = "name2"; + adapter.setName(newName); + assertEquals(newName, structureDef.getName()); + } + + @Test + void adapter_get_and_set_url() { + var structureDef = new StructureDefinition(); + var url = "www.url.com"; + structureDef.setUrl(url); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(structureDef); + assertTrue(adapter.hasUrl()); + assertEquals(url, adapter.getUrl()); + var newUrl = "www.url2.com"; + adapter.setUrl(newUrl); + assertTrue(adapter.hasUrl()); + assertEquals(newUrl, structureDef.getUrl()); + } + + @Test + void adapter_get_and_set_version() { + var structureDef = new StructureDefinition(); + var version = "1.0.0"; + structureDef.setVersion(version); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(structureDef); + assertTrue(adapter.hasVersion()); + assertEquals(version, adapter.getVersion()); + var newVersion = "1.0.1"; + adapter.setVersion(newVersion); + assertEquals(newVersion, structureDef.getVersion()); + } + + @Test + void adapter_get_and_set_status() { + var structureDef = new StructureDefinition(); + var status = PublicationStatus.DRAFT; + structureDef.setStatus(status); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(structureDef); + assertEquals(status.toCode(), adapter.getStatus()); + assertThrows(UnprocessableEntityException.class, () -> adapter.setStatus("invalid-status")); + var newStatus = PublicationStatus.ACTIVE; + adapter.setStatus(newStatus.toCode()); + assertEquals(newStatus, PublicationStatus.fromCode(adapter.getStatus())); + } + + @Test + void adapter_get_and_set_dates() { + // StructureDefinition does not have fields approvalDate and effectivePeriod + var structureDef = new StructureDefinition(); + var date = new Date(); + var effectivePeriod = new Period() + .setStart(java.sql.Date.valueOf(LocalDate.parse("2020-01-01"))) + .setEnd(java.sql.Date.valueOf(LocalDate.parse("2020-12-31"))); + structureDef.setDate(date); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(structureDef); + assertEquals(date, adapter.getDate()); + assertEquals(null, adapter.getApprovalDate()); + assertNotEquals(effectivePeriod, adapter.getEffectivePeriod()); + var newDate = new Date(); + newDate.setTime(100); + adapter.setDate(newDate); + assertEquals(newDate, structureDef.getDate()); + var newApprovalDate = new Date(); + newApprovalDate.setTime(100); + adapter.setApprovalDate(newApprovalDate); + assertEquals(null, adapter.getApprovalDate()); + var newEffectivePeriod = new Period() + .setStart(java.sql.Date.valueOf(LocalDate.parse("2021-01-01"))) + .setEnd(java.sql.Date.valueOf(LocalDate.parse("2021-12-31"))); + adapter.setEffectivePeriod(newEffectivePeriod); + assertNotEquals(newEffectivePeriod, adapter.getEffectivePeriod()); + } + + @Test + void adapter_get_experimental() { + var structureDef = new StructureDefinition(); + var experimental = true; + structureDef.setExperimental(experimental); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(structureDef); + assertEquals(experimental, adapter.getExperimental()); + } + + @Test + void adapter_set_relatedArtifact() { + var structureDef = new StructureDefinition(); + var relatedArtifactList = List.of(new RelatedArtifact()); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(structureDef); + adapter.setRelatedArtifact(relatedArtifactList); + assertEquals(0, adapter.getRelatedArtifact().size()); + } + + @Test + void adapter_copy() { + var structureDef = new StructureDefinition().setStatus(PublicationStatus.DRAFT); + structureDef.setId("plan-1"); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(structureDef); + var copy = (StructureDefinition) adapter.copy(); + copy.setId("plan-2"); + assertNotEquals(structureDef.getId(), copy.getId()); + structureDef.setStatus(PublicationStatus.ACTIVE); + assertNotEquals(adapter.getStatus(), copy.getStatus()); + } + + @Test + void adapter_get_all_dependencies() { + var dependencies = List.of( + "profileRef", + "baseDefinition", + "elementProfileRef", + "elementTargetProfileRef", + "elementValueSetBindingRef"); + var structureDef = new StructureDefinition(); + structureDef.getMeta().addProfile(dependencies.get(0)); + structureDef.setBaseDefinition(dependencies.get(1)); + structureDef.getDifferential().addElement().addType().setProfile(dependencies.get(2)); + structureDef.getDifferential().addElement().addType().setTargetProfile(dependencies.get(3)); + structureDef + .getDifferential() + .addElement() + .setBinding(new ElementDefinitionBindingComponent().setValueSet(new UriType(dependencies.get(4)))); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(structureDef); + var extractedDependencies = adapter.getDependencies(); + assertEquals(dependencies.size(), extractedDependencies.size()); + extractedDependencies.forEach(dep -> { + assertTrue(dependencies.indexOf(dep.getReference()) >= 0); + }); + } +} diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/dstu3/ValueSetAdapterTest.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/dstu3/ValueSetAdapterTest.java new file mode 100644 index 000000000..14a6d09ca --- /dev/null +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/dstu3/ValueSetAdapterTest.java @@ -0,0 +1,170 @@ +package org.opencds.cqf.fhir.utility.adapter.dstu3; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import java.time.LocalDate; +import java.util.Date; +import java.util.List; +import org.hl7.fhir.dstu3.model.Bundle; +import org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus; +import org.hl7.fhir.dstu3.model.Library; +import org.hl7.fhir.dstu3.model.Period; +import org.hl7.fhir.dstu3.model.RelatedArtifact; +import org.hl7.fhir.dstu3.model.ValueSet; +import org.hl7.fhir.instance.model.api.IDomainResource; +import org.junit.jupiter.api.Test; +import org.opencds.cqf.fhir.utility.visitor.PackageVisitor; + +public class ValueSetAdapterTest { + private final FhirContext fhirContext = FhirContext.forDstu3Cached(); + private final org.opencds.cqf.fhir.utility.adapter.AdapterFactory adapterFactory = new AdapterFactory(); + + @Test + void invalid_object_fails() { + assertThrows(IllegalArgumentException.class, () -> new ValueSetAdapter(new Library())); + } + + @Test + void adapter_accepts_visitor() { + var spyVisitor = spy(new PackageVisitor(fhirContext)); + doReturn(new Bundle()).when(spyVisitor).visit(any(ValueSetAdapter.class), any(), any()); + IDomainResource valueSet = new ValueSet(); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(valueSet); + assertEquals(valueSet, adapter.get()); + adapter.accept(spyVisitor, null, null); + verify(spyVisitor, times(1)).visit(any(ValueSetAdapter.class), any(), any()); + } + + @Test + void adapter_get_and_set_name() { + var valueSet = new ValueSet(); + var name = "name"; + valueSet.setName(name); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(valueSet); + assertEquals(name, adapter.getName()); + var newName = "name2"; + adapter.setName(newName); + assertEquals(newName, valueSet.getName()); + } + + @Test + void adapter_get_and_set_url() { + var valueSet = new ValueSet(); + var url = "www.url.com"; + valueSet.setUrl(url); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(valueSet); + assertTrue(adapter.hasUrl()); + assertEquals(url, adapter.getUrl()); + var newUrl = "www.url2.com"; + adapter.setUrl(newUrl); + assertTrue(adapter.hasUrl()); + assertEquals(newUrl, valueSet.getUrl()); + } + + @Test + void adapter_get_and_set_version() { + var valueSet = new ValueSet(); + var version = "1.0.0"; + valueSet.setVersion(version); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(valueSet); + assertTrue(adapter.hasVersion()); + assertEquals(version, adapter.getVersion()); + var newVersion = "1.0.1"; + adapter.setVersion(newVersion); + assertEquals(newVersion, valueSet.getVersion()); + } + + @Test + void adapter_get_and_set_status() { + var valueSet = new ValueSet(); + var status = PublicationStatus.DRAFT; + valueSet.setStatus(status); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(valueSet); + assertEquals(status.toCode(), adapter.getStatus()); + assertThrows(UnprocessableEntityException.class, () -> adapter.setStatus("invalid-status")); + var newStatus = PublicationStatus.ACTIVE; + adapter.setStatus(newStatus.toCode()); + assertEquals(newStatus, PublicationStatus.fromCode(adapter.getStatus())); + } + + @Test + void adapter_get_and_set_dates() { + // ValueSet does not have fields approvalDate and effectivePeriod + var valueSet = new ValueSet(); + var date = new Date(); + var effectivePeriod = new Period() + .setStart(java.sql.Date.valueOf(LocalDate.parse("2020-01-01"))) + .setEnd(java.sql.Date.valueOf(LocalDate.parse("2020-12-31"))); + valueSet.setDate(date); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(valueSet); + assertEquals(date, adapter.getDate()); + assertEquals(null, adapter.getApprovalDate()); + assertNotEquals(effectivePeriod, adapter.getEffectivePeriod()); + var newDate = new Date(); + newDate.setTime(100); + adapter.setDate(newDate); + assertEquals(newDate, valueSet.getDate()); + var newApprovalDate = new Date(); + newApprovalDate.setTime(100); + adapter.setApprovalDate(newApprovalDate); + assertEquals(null, adapter.getApprovalDate()); + var newEffectivePeriod = new Period() + .setStart(java.sql.Date.valueOf(LocalDate.parse("2021-01-01"))) + .setEnd(java.sql.Date.valueOf(LocalDate.parse("2021-12-31"))); + adapter.setEffectivePeriod(newEffectivePeriod); + assertNotEquals(newEffectivePeriod, adapter.getEffectivePeriod()); + } + + @Test + void adapter_get_experimental() { + var valueSet = new ValueSet(); + var experimental = true; + valueSet.setExperimental(experimental); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(valueSet); + assertEquals(experimental, adapter.getExperimental()); + } + + @Test + void adapter_set_relatedArtifact() { + var valueSet = new ValueSet(); + var relatedArtifactList = List.of(new RelatedArtifact()); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(valueSet); + adapter.setRelatedArtifact(relatedArtifactList); + assertEquals(0, adapter.getRelatedArtifact().size()); + } + + @Test + void adapter_copy() { + var valueSet = new ValueSet().setStatus(PublicationStatus.DRAFT); + valueSet.setId("valueset-1"); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(valueSet); + var copy = (ValueSet) adapter.copy(); + copy.setId("valueset-2"); + assertNotEquals(valueSet.getId(), copy.getId()); + valueSet.setStatus(PublicationStatus.ACTIVE); + assertNotEquals(adapter.getStatus(), copy.getStatus()); + } + + @Test + void adapter_get_all_dependencies() { + var dependencies = List.of("profileRef"); + var valueSet = new ValueSet(); + valueSet.getMeta().addProfile(dependencies.get(0)); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(valueSet); + var extractedDependencies = adapter.getDependencies(); + assertEquals(extractedDependencies.size(), dependencies.size()); + extractedDependencies.forEach(dep -> { + assertTrue(dependencies.indexOf(dep.getReference()) >= 0); + }); + } +} diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r4/EndpointAdapterTest.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r4/EndpointAdapterTest.java new file mode 100644 index 000000000..7e93e3cd4 --- /dev/null +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r4/EndpointAdapterTest.java @@ -0,0 +1,29 @@ +package org.opencds.cqf.fhir.utility.adapter.r4; + +import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.hl7.fhir.r4.model.Endpoint; +import org.hl7.fhir.r4.model.PlanDefinition; +import org.junit.jupiter.api.Test; + +public class EndpointAdapterTest { + private final org.opencds.cqf.fhir.utility.adapter.AdapterFactory adapterFactory = new AdapterFactory(); + + @Test + void invalid_object_fails() { + assertThrows(IllegalArgumentException.class, () -> new EndpointAdapter(new PlanDefinition())); + } + + @Test + void adapter_get_and_set_address() { + var endpoint = new Endpoint(); + var address = "123 Test Street"; + endpoint.setAddress(address); + var adapter = (EndpointAdapter) adapterFactory.createResource(endpoint); + assertEquals(address, adapter.getAddress()); + var newAddress = "456 Test Street"; + adapter.setAddress(newAddress); + assertEquals(newAddress, endpoint.getAddress()); + } +} diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r4/KnowledgeArtifactAdapterTest.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r4/KnowledgeArtifactAdapterTest.java new file mode 100644 index 000000000..cfb9dffed --- /dev/null +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r4/KnowledgeArtifactAdapterTest.java @@ -0,0 +1,158 @@ +package org.opencds.cqf.fhir.utility.adapter.r4; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import org.hl7.fhir.instance.model.api.IDomainResource; +import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.CompartmentDefinition; +import org.hl7.fhir.r4.model.DateTimeType; +import org.hl7.fhir.r4.model.DateType; +import org.hl7.fhir.r4.model.Enumerations.PublicationStatus; +import org.hl7.fhir.r4.model.Extension; +import org.hl7.fhir.r4.model.Period; +import org.hl7.fhir.r4.model.RelatedArtifact; +import org.junit.jupiter.api.Test; +import org.opencds.cqf.fhir.utility.visitor.PackageVisitor; + +class KnowledgeArtifactAdapterTest { + private final FhirContext fhirContext = FhirContext.forR4Cached(); + private final org.opencds.cqf.fhir.utility.adapter.AdapterFactory adapterFactory = new AdapterFactory(); + + @Test + void invalid_object_fails() { + assertThrows( + IllegalArgumentException.class, + () -> new KnowledgeArtifactAdapter(new org.hl7.fhir.r5.model.Library())); + } + + @Test + void test() { + var resource = new CompartmentDefinition(); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(resource); + assertNotNull(adapter); + } + + @Test + void adapter_accepts_visitor() { + var spyVisitor = spy(new PackageVisitor(fhirContext)); + doReturn(new Bundle()).when(spyVisitor).visit(any(KnowledgeArtifactAdapter.class), any(), any()); + IDomainResource def = new CompartmentDefinition(); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(def); + adapter.accept(spyVisitor, null, null); + verify(spyVisitor, times(1)).visit(any(KnowledgeArtifactAdapter.class), any(), any()); + } + + @Test + void adapter_get_and_set_name() { + var def = new CompartmentDefinition(); + var name = "name"; + def.setName(name); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(def); + assertEquals(name, adapter.getName()); + var newName = "name2"; + adapter.setName(newName); + assertEquals(newName, def.getName()); + } + + @Test + void adapter_get_and_set_url() { + var def = new CompartmentDefinition(); + var url = "www.url.com"; + def.setUrl(url); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(def); + assertEquals(url, adapter.getUrl()); + var newUrl = "www.url2.com"; + adapter.setUrl(newUrl); + assertEquals(newUrl, def.getUrl()); + } + + @Test + void adapter_get_and_set_version() { + var def = new CompartmentDefinition(); + var version = "1.0.0"; + def.setVersion(version); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(def); + assertTrue(adapter.hasVersion()); + assertEquals(version, adapter.getVersion()); + var newVersion = "1.0.1"; + adapter.setVersion(newVersion); + assertEquals(newVersion, def.getVersion()); + } + + @Test + void adapter_get_and_set_status() { + var def = new CompartmentDefinition(); + var status = PublicationStatus.DRAFT; + def.setStatus(status); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(def); + assertEquals(status.toCode(), adapter.getStatus()); + assertThrows(UnprocessableEntityException.class, () -> adapter.setStatus("invalid-status")); + var newStatus = PublicationStatus.ACTIVE; + adapter.setStatus(newStatus.toCode()); + assertEquals(newStatus, PublicationStatus.fromCode(adapter.getStatus())); + } + + @Test + void adapter_get_and_set_dates() { + var def = new CompartmentDefinition(); + var date = new Date(); + def.setDate(date); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(def); + assertEquals(date, adapter.getDate()); + var newDate = new Date(); + newDate.setTime(100); + adapter.setDate(newDate); + assertEquals(newDate, def.getDate()); + assertThrows(UnprocessableEntityException.class, () -> adapter.setDateElement(new DateType())); + var newDateElement = new DateTimeType().setValue(new Date()); + adapter.setDateElement(newDateElement); + assertEquals(newDateElement, def.getDateElement()); + assertThrows(UnprocessableEntityException.class, () -> adapter.setEffectivePeriod(new Extension())); + } + + @Test + void adapter_get_experimental() { + var def = new CompartmentDefinition(); + var experimental = true; + def.setExperimental(experimental); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(def); + assertEquals(experimental, adapter.getExperimental()); + } + + @Test + void adapter_set_relatedArtifact() { + var def = new CompartmentDefinition(); + var relatedArtifactList = List.of(new RelatedArtifact()); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(def); + adapter.setRelatedArtifact(relatedArtifactList); + assertEquals(0, adapter.getRelatedArtifact().size()); + assertThrows(UnprocessableEntityException.class, () -> adapter.setRelatedArtifact(Arrays.asList(new Period()))); + assertThrows(UnprocessableEntityException.class, () -> adapter.getRelatedArtifactsOfType("depends")); + } + + @Test + void adapter_copy() { + var def = new CompartmentDefinition().setStatus(PublicationStatus.DRAFT); + def.setId("def-1"); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(def); + var copy = (CompartmentDefinition) adapter.copy(); + copy.setId("def-2"); + assertNotEquals(def.getId(), copy.getId()); + def.setStatus(PublicationStatus.ACTIVE); + assertNotEquals(adapter.getStatus(), copy.getStatus()); + } +} diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r4/LibraryAdapterTest.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r4/LibraryAdapterTest.java new file mode 100644 index 000000000..47f8e6b95 --- /dev/null +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r4/LibraryAdapterTest.java @@ -0,0 +1,236 @@ +package org.opencds.cqf.fhir.utility.adapter.r4; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.model.primitive.IdDt; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import org.hl7.fhir.instance.model.api.IDomainResource; +import org.hl7.fhir.r4.model.Attachment; +import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.CodeableConcept; +import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.DataRequirement; +import org.hl7.fhir.r4.model.Enumerations.PublicationStatus; +import org.hl7.fhir.r4.model.Library; +import org.hl7.fhir.r4.model.Period; +import org.hl7.fhir.r4.model.PlanDefinition; +import org.hl7.fhir.r4.model.RelatedArtifact; +import org.hl7.fhir.r4.model.UsageContext; +import org.junit.jupiter.api.Test; +import org.opencds.cqf.fhir.utility.visitor.PackageVisitor; + +public class LibraryAdapterTest { + private final FhirContext fhirContext = FhirContext.forR4Cached(); + private final org.opencds.cqf.fhir.utility.adapter.AdapterFactory adapterFactory = new AdapterFactory(); + + @Test + void invalid_object_fails() { + assertThrows(IllegalArgumentException.class, () -> new LibraryAdapter(new PlanDefinition())); + } + + @Test + void adapter_accepts_visitor() { + var spyVisitor = spy(new PackageVisitor(fhirContext)); + doReturn(new Bundle()).when(spyVisitor).visit(any(LibraryAdapter.class), any(), any()); + IDomainResource library = new Library(); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(library); + adapter.accept(spyVisitor, null, null); + verify(spyVisitor, times(1)).visit(any(LibraryAdapter.class), any(), any()); + } + + @Test + void adapter_get_and_set_name() { + var library = new Library(); + var name = "name"; + library.setName(name); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(library); + assertEquals(name, adapter.getName()); + var newName = "name2"; + adapter.setName(newName); + assertEquals(newName, library.getName()); + } + + @Test + void adapter_get_and_set_url() { + var library = new Library(); + var url = "www.url.com"; + library.setUrl(url); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(library); + assertEquals(url, adapter.getUrl()); + var newUrl = "www.url2.com"; + adapter.setUrl(newUrl); + assertEquals(newUrl, library.getUrl()); + } + + @Test + void adapter_get_and_set_version() { + var library = new Library(); + var version = "1.0.0"; + library.setVersion(version); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(library); + assertTrue(adapter.hasVersion()); + assertEquals(version, adapter.getVersion()); + var newVersion = "1.0.1"; + adapter.setVersion(newVersion); + assertEquals(newVersion, library.getVersion()); + } + + @Test + void adapter_get_and_set_status() { + var library = new Library(); + var status = PublicationStatus.DRAFT; + library.setStatus(status); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(library); + assertEquals(status.toCode(), adapter.getStatus()); + assertThrows(UnprocessableEntityException.class, () -> adapter.setStatus("invalid-status")); + var newStatus = PublicationStatus.ACTIVE; + adapter.setStatus(newStatus.toCode()); + assertEquals(newStatus, PublicationStatus.fromCode(adapter.getStatus())); + } + + @Test + void adapter_get_and_set_dates() { + var library = new Library(); + var date = new Date(); + var approvalDate = new Date(); + var effectivePeriod = new Period() + .setStart(java.sql.Date.valueOf(LocalDate.parse("2020-01-01"))) + .setEnd(java.sql.Date.valueOf(LocalDate.parse("2020-12-31"))); + library.setDate(date); + library.setApprovalDate(approvalDate); + library.setEffectivePeriod(effectivePeriod); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(library); + assertEquals(date, adapter.getDate()); + assertEquals(approvalDate, adapter.getApprovalDate()); + assertEquals(effectivePeriod, adapter.getEffectivePeriod()); + var newDate = new Date(); + newDate.setTime(100); + adapter.setDate(newDate); + assertEquals(newDate, library.getDate()); + var newApprovalDate = new Date(); + newApprovalDate.setTime(100); + adapter.setApprovalDate(newApprovalDate); + assertEquals(newApprovalDate, library.getApprovalDate()); + var newEffectivePeriod = new Period() + .setStart(java.sql.Date.valueOf(LocalDate.parse("2021-01-01"))) + .setEnd(java.sql.Date.valueOf(LocalDate.parse("2021-12-31"))); + adapter.setEffectivePeriod(newEffectivePeriod); + assertEquals(newEffectivePeriod, adapter.getEffectivePeriod()); + } + + @Test + void adapter_get_experimental() { + var library = new Library(); + var experimental = true; + library.setExperimental(experimental); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(library); + assertEquals(experimental, adapter.getExperimental()); + } + + @Test + void adapter_set_relatedArtifact() { + var library = new Library(); + var relatedArtifactList = List.of(new RelatedArtifact()); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(library); + adapter.setRelatedArtifact(relatedArtifactList); + assertEquals(relatedArtifactList, library.getRelatedArtifact()); + assertEquals(relatedArtifactList, adapter.getRelatedArtifact()); + } + + @Test + void adapter_copy() { + var library = new Library().setStatus(PublicationStatus.DRAFT); + library.setId("library-1"); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(library); + var copy = (Library) adapter.copy(); + var adapterCopy = new LibraryAdapter(copy); + adapterCopy.setId(new IdDt("Library", "library-2")); + assertNotEquals(library.getId(), copy.getId()); + library.setStatus(PublicationStatus.ACTIVE); + assertNotEquals(adapter.getStatus(), copy.getStatus()); + } + + @Test + void adapter_get_all_dependencies() { + var dependencies = List.of( + "profileRef", "relatedArtifactRef", "dataRequirementProfileRef", "dataRequirementCodeFilterRef"); + var library = new Library(); + library.getMeta().addProfile(dependencies.get(0)); + library.getRelatedArtifactFirstRep().setResource(dependencies.get(1)); + library.addDataRequirement().addProfile(dependencies.get(2)); + library.addDataRequirement().addCodeFilter().setValueSet(dependencies.get(3)); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(library); + var extractedDependencies = adapter.getDependencies(); + assertEquals(extractedDependencies.size(), dependencies.size()); + extractedDependencies.forEach(dep -> { + assertTrue(dependencies.indexOf(dep.getReference()) >= 0); + }); + } + + @Test + void adapter_get_and_set_content() { + var library = new Library(); + var adapter = (LibraryAdapter) adapterFactory.createKnowledgeArtifactAdapter(library); + var contentList = new ArrayList(); + contentList.add(new Attachment().setContentType("text/cql").setData(new byte[10])); + adapter.setContent(contentList); + assertTrue(adapter.hasContent()); + assertEquals(contentList, adapter.getContent()); + adapter.addContent().setContentType("text/xml").setData(new byte[20]); + assertEquals(2, adapter.getContent().size()); + assertEquals("text/xml", adapter.getContent().get(1).getContentType()); + } + + @Test + void adapter_get_and_set_type() { + var type = new CodeableConcept(new Coding("www.test.com", "test", "Test")); + var library = new Library().setType(type); + var adapter = (LibraryAdapter) adapterFactory.createKnowledgeArtifactAdapter(library); + assertEquals(type, adapter.getType()); + assertThrows(UnprocessableEntityException.class, () -> adapter.setType("test")); + var newType = new CodeableConcept(new Coding("http://hl7.org/fhir/ValueSet/library-type", "logic-library", "")); + adapter.setType(newType.getCoding().get(0).getCode()); + assertEquals( + newType.getCoding().get(0).getCode(), + ((CodeableConcept) adapter.getType()).getCoding().get(0).getCode()); + assertEquals( + newType.getCoding().get(0).getSystem(), + ((CodeableConcept) adapter.getType()).getCoding().get(0).getSystem()); + } + + @Test + void adapter_get_and_set_dataRequirement() { + var library = new Library(); + var adapter = (LibraryAdapter) adapterFactory.createKnowledgeArtifactAdapter(library); + var dataRequirements = new ArrayList(); + dataRequirements.add(new DataRequirement().setType("Patient")); + adapter.setDataRequirement(dataRequirements); + assertEquals(dataRequirements, library.getDataRequirement()); + adapter.addDataRequirement(new DataRequirement().setType("Observation")); + assertEquals(library.getDataRequirement(), adapter.getDataRequirement()); + assertEquals(2, adapter.getDataRequirement().size()); + } + + @Test + void adapter_get_useContext() { + var library = new Library(); + var useContext = new UsageContext().setCode(new Coding("www.test.com", "test", "Test")); + library.setUseContext(Collections.singletonList(useContext)); + var adapter = (LibraryAdapter) adapterFactory.createKnowledgeArtifactAdapter(library); + assertEquals(useContext, adapter.getUseContext().get(0)); + } +} diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r4/MeasureAdapterTest.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r4/MeasureAdapterTest.java new file mode 100644 index 000000000..522c94889 --- /dev/null +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r4/MeasureAdapterTest.java @@ -0,0 +1,232 @@ +package org.opencds.cqf.fhir.utility.adapter.r4; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.model.primitive.IdDt; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import java.time.LocalDate; +import java.util.Date; +import java.util.List; +import org.hl7.fhir.instance.model.api.IDomainResource; +import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.CanonicalType; +import org.hl7.fhir.r4.model.CodeableConcept; +import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.Enumerations.PublicationStatus; +import org.hl7.fhir.r4.model.Expression; +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.Period; +import org.hl7.fhir.r4.model.Reference; +import org.hl7.fhir.r4.model.RelatedArtifact; +import org.junit.jupiter.api.Test; +import org.opencds.cqf.fhir.utility.Constants; +import org.opencds.cqf.fhir.utility.visitor.PackageVisitor; + +public class MeasureAdapterTest { + private final FhirContext fhirContext = FhirContext.forR4Cached(); + private final org.opencds.cqf.fhir.utility.adapter.AdapterFactory adapterFactory = new AdapterFactory(); + + @Test + void invalid_object_fails() { + assertThrows(IllegalArgumentException.class, () -> new MeasureAdapter(new Library())); + } + + @Test + void adapter_accepts_visitor() { + var spyVisitor = spy(new PackageVisitor(fhirContext)); + doReturn(new Bundle()).when(spyVisitor).visit(any(MeasureAdapter.class), any(), any()); + IDomainResource measure = new Measure(); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(measure); + adapter.accept(spyVisitor, null, null); + verify(spyVisitor, times(1)).visit(any(MeasureAdapter.class), any(), any()); + } + + @Test + void adapter_get_and_set_name() { + var measure = new Measure(); + var name = "name"; + measure.setName(name); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(measure); + assertEquals(name, adapter.getName()); + var newName = "name2"; + adapter.setName(newName); + assertEquals(newName, measure.getName()); + } + + @Test + void adapter_get_and_set_url() { + var measure = new Measure(); + var url = "www.url.com"; + measure.setUrl(url); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(measure); + assertEquals(url, adapter.getUrl()); + var newUrl = "www.url2.com"; + adapter.setUrl(newUrl); + assertEquals(newUrl, measure.getUrl()); + } + + @Test + void adapter_get_and_set_version() { + var measure = new Measure(); + var version = "1.0.0"; + measure.setVersion(version); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(measure); + assertTrue(adapter.hasVersion()); + assertEquals(version, adapter.getVersion()); + var newVersion = "1.0.1"; + adapter.setVersion(newVersion); + assertEquals(newVersion, measure.getVersion()); + } + + @Test + void adapter_get_and_set_status() { + var measure = new Measure(); + var status = PublicationStatus.DRAFT; + measure.setStatus(status); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(measure); + assertEquals(status.toCode(), adapter.getStatus()); + assertThrows(UnprocessableEntityException.class, () -> adapter.setStatus("invalid-status")); + var newStatus = PublicationStatus.ACTIVE; + adapter.setStatus(newStatus.toCode()); + assertEquals(newStatus, PublicationStatus.fromCode(adapter.getStatus())); + } + + @Test + void adapter_get_and_set_dates() { + var measure = new Measure(); + var date = new Date(); + var approvalDate = new Date(); + var effectivePeriod = new Period() + .setStart(java.sql.Date.valueOf(LocalDate.parse("2020-01-01"))) + .setEnd(java.sql.Date.valueOf(LocalDate.parse("2020-12-31"))); + measure.setDate(date); + measure.setApprovalDate(approvalDate); + measure.setEffectivePeriod(effectivePeriod); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(measure); + assertEquals(date, adapter.getDate()); + assertEquals(approvalDate, adapter.getApprovalDate()); + assertEquals(effectivePeriod, adapter.getEffectivePeriod()); + var newDate = new Date(); + newDate.setTime(100); + adapter.setDate(newDate); + assertEquals(newDate, measure.getDate()); + var newApprovalDate = new Date(); + newApprovalDate.setTime(100); + adapter.setApprovalDate(newApprovalDate); + assertEquals(newApprovalDate, measure.getApprovalDate()); + var newEffectivePeriod = new Period() + .setStart(java.sql.Date.valueOf(LocalDate.parse("2021-01-01"))) + .setEnd(java.sql.Date.valueOf(LocalDate.parse("2021-12-31"))); + adapter.setEffectivePeriod(newEffectivePeriod); + assertEquals(newEffectivePeriod, adapter.getEffectivePeriod()); + } + + @Test + void adapter_get_experimental() { + var measure = new Measure(); + var experimental = true; + measure.setExperimental(experimental); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(measure); + assertEquals(experimental, adapter.getExperimental()); + } + + @Test + void adapter_set_relatedArtifact() { + var measure = new Measure(); + var relatedArtifactList = List.of(new RelatedArtifact()); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(measure); + adapter.setRelatedArtifact(relatedArtifactList); + assertEquals(relatedArtifactList, measure.getRelatedArtifact()); + assertEquals(relatedArtifactList, adapter.getRelatedArtifact()); + } + + @Test + void adapter_copy() { + var measure = new Measure().setStatus(PublicationStatus.DRAFT); + measure.setId("plan-1"); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(measure); + var copy = (Measure) adapter.copy(); + var adapterCopy = new MeasureAdapter(copy); + adapterCopy.setId(new IdDt("Measure", "plan-2")); + assertNotEquals(measure.getId(), copy.getId()); + measure.setStatus(PublicationStatus.ACTIVE); + assertNotEquals(adapter.getStatus(), copy.getStatus()); + } + + @Test + void adapter_get_all_dependencies() { + var dependencies = List.of( + "profileRef", + "relatedArtifactRef", + "libraryRef", + "populationCriteriaRef", + "stratifierCriteriaRef", + "stratifierComponentCriteriaRef", + "supplementalDataCriteriaRef", + "inputParametersRef", + "expansionParametersRef", + "cqlOptionsRef", + "componentRef"); + var measure = new Measure(); + measure.getMeta().addProfile(dependencies.get(0)); + measure.getRelatedArtifactFirstRep().setResource(dependencies.get(1)); + measure.getLibrary().add(new CanonicalType(dependencies.get(2))); + measure.addGroup().addPopulation().setCriteria(new Expression().setReference(dependencies.get(3))); + measure.addGroup().addStratifier().setCriteria(new Expression().setReference(dependencies.get(4))); + measure.addGroup() + .addStratifier() + .addComponent() + .setCriteria(new Expression().setReference(dependencies.get(5))); + measure.addSupplementalData().setCriteria(new Expression().setReference(dependencies.get(6))); + measure.addExtension(Constants.CQFM_INPUT_PARAMETERS, new Reference(dependencies.get(7))); + measure.addExtension(Constants.CQF_EXPANSION_PARAMETERS, new Reference(dependencies.get(8))); + measure.addExtension(Constants.CQF_CQL_OPTIONS, new Reference(dependencies.get(9))); + measure.addExtension(Constants.CQFM_COMPONENT, new RelatedArtifact().setResource(dependencies.get(10))); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(measure); + var extractedDependencies = adapter.getDependencies(); + assertEquals(dependencies.size(), extractedDependencies.size()); + extractedDependencies.forEach(dep -> { + assertTrue(dependencies.indexOf(dep.getReference()) >= 0); + }); + } + + @Test + void adapter_get_all_dependencies_with_effective_data_requirements() { + var dependencies = List.of( + "libraryProfileRef", + "relatedArtifactRef", + "dataRequirementProfileRef", + "dataRequirementCodeFilterRef", + "measureProfileRef"); + var library = new Library() + .setType(new CodeableConcept(new Coding( + "http://terminology.hl7.org/CodeSystem/library-type", + "module-definition", + "Module Definition"))); + library.setId("test"); + library.getMeta().addProfile(dependencies.get(0)); + library.getRelatedArtifactFirstRep().setResource(dependencies.get(1)); + library.addDataRequirement().addProfile(dependencies.get(2)); + library.addDataRequirement().addCodeFilter().setValueSet(dependencies.get(3)); + var measure = new Measure().addContained(new Patient()).addContained(library); + measure.getMeta().addProfile(dependencies.get(4)); + measure.addExtension(Constants.CQFM_EFFECTIVE_DATA_REQUIREMENTS, new CanonicalType("#test")); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(measure); + var extractedDependencies = adapter.getDependencies(); + assertEquals(dependencies.size(), extractedDependencies.size()); + extractedDependencies.forEach(dep -> { + assertTrue(dependencies.indexOf(dep.getReference()) >= 0); + }); + } +} diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r4/PlanDefinitionAdapterTest.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r4/PlanDefinitionAdapterTest.java index 8a3bb55ce..7c869d849 100644 --- a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r4/PlanDefinitionAdapterTest.java +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r4/PlanDefinitionAdapterTest.java @@ -1,6 +1,8 @@ package org.opencds.cqf.fhir.utility.adapter.r4; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; @@ -8,24 +10,39 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.model.primitive.IdDt; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import java.time.LocalDate; import java.util.Date; import java.util.List; import org.hl7.fhir.instance.model.api.IDomainResource; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.CanonicalType; +import org.hl7.fhir.r4.model.Enumerations.PublicationStatus; import org.hl7.fhir.r4.model.Extension; +import org.hl7.fhir.r4.model.Library; +import org.hl7.fhir.r4.model.Period; import org.hl7.fhir.r4.model.PlanDefinition; import org.hl7.fhir.r4.model.RelatedArtifact; import org.junit.jupiter.api.Test; -import org.opencds.cqf.fhir.utility.visitor.KnowledgeArtifactPackageVisitor; +import org.opencds.cqf.fhir.utility.visitor.PackageVisitor; public class PlanDefinitionAdapterTest { + private final FhirContext fhirContext = FhirContext.forR4Cached(); + private final org.opencds.cqf.fhir.utility.adapter.AdapterFactory adapterFactory = new AdapterFactory(); + + @Test + void invalid_object_fails() { + assertThrows(IllegalArgumentException.class, () -> new PlanDefinitionAdapter(new Library())); + } + @Test void adapter_accepts_visitor() { - var spyVisitor = spy(new KnowledgeArtifactPackageVisitor()); + var spyVisitor = spy(new PackageVisitor(fhirContext)); doReturn(new Bundle()).when(spyVisitor).visit(any(PlanDefinitionAdapter.class), any(), any()); IDomainResource planDef = new PlanDefinition(); - var adapter = new PlanDefinitionAdapter(planDef); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(planDef); adapter.accept(spyVisitor, null, null); verify(spyVisitor, times(1)).visit(any(PlanDefinitionAdapter.class), any(), any()); } @@ -35,7 +52,7 @@ void adapter_get_and_set_name() { var planDef = new PlanDefinition(); var name = "name"; planDef.setName(name); - var adapter = new PlanDefinitionAdapter(planDef); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(planDef); assertEquals(name, adapter.getName()); var newName = "name2"; adapter.setName(newName); @@ -47,7 +64,7 @@ void adapter_get_and_set_url() { var planDef = new PlanDefinition(); var url = "www.url.com"; planDef.setUrl(url); - var adapter = new PlanDefinitionAdapter(planDef); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(planDef); assertEquals(url, adapter.getUrl()); var newUrl = "www.url2.com"; adapter.setUrl(newUrl); @@ -55,16 +72,59 @@ void adapter_get_and_set_url() { } @Test - void adapter_get_and_set_approvalDate() { + void adapter_get_and_set_version() { + var planDef = new PlanDefinition(); + var version = "1.0.0"; + planDef.setVersion(version); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(planDef); + assertTrue(adapter.hasVersion()); + assertEquals(version, adapter.getVersion()); + var newVersion = "1.0.1"; + adapter.setVersion(newVersion); + assertEquals(newVersion, planDef.getVersion()); + } + + @Test + void adapter_get_and_set_status() { var planDef = new PlanDefinition(); + var status = PublicationStatus.DRAFT; + planDef.setStatus(status); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(planDef); + assertEquals(status.toCode(), adapter.getStatus()); + assertThrows(UnprocessableEntityException.class, () -> adapter.setStatus("invalid-status")); + var newStatus = PublicationStatus.ACTIVE; + adapter.setStatus(newStatus.toCode()); + assertEquals(newStatus, PublicationStatus.fromCode(adapter.getStatus())); + } + + @Test + void adapter_get_and_set_dates() { + var planDef = new PlanDefinition(); + var date = new Date(); var approvalDate = new Date(); + var effectivePeriod = new Period() + .setStart(java.sql.Date.valueOf(LocalDate.parse("2020-01-01"))) + .setEnd(java.sql.Date.valueOf(LocalDate.parse("2020-12-31"))); + planDef.setDate(date); planDef.setApprovalDate(approvalDate); - var adapter = new PlanDefinitionAdapter(planDef); + planDef.setEffectivePeriod(effectivePeriod); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(planDef); + assertEquals(date, adapter.getDate()); assertEquals(approvalDate, adapter.getApprovalDate()); + assertEquals(effectivePeriod, adapter.getEffectivePeriod()); + var newDate = new Date(); + newDate.setTime(100); + adapter.setDate(newDate); + assertEquals(newDate, planDef.getDate()); var newApprovalDate = new Date(); newApprovalDate.setTime(100); adapter.setApprovalDate(newApprovalDate); assertEquals(newApprovalDate, planDef.getApprovalDate()); + var newEffectivePeriod = new Period() + .setStart(java.sql.Date.valueOf(LocalDate.parse("2021-01-01"))) + .setEnd(java.sql.Date.valueOf(LocalDate.parse("2021-12-31"))); + adapter.setEffectivePeriod(newEffectivePeriod); + assertEquals(newEffectivePeriod, adapter.getEffectivePeriod()); } @Test @@ -72,7 +132,7 @@ void adapter_get_experimental() { var planDef = new PlanDefinition(); var experimental = true; planDef.setExperimental(experimental); - var adapter = new PlanDefinitionAdapter(planDef); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(planDef); assertEquals(experimental, adapter.getExperimental()); } @@ -80,15 +140,29 @@ void adapter_get_experimental() { void adapter_set_relatedArtifact() { var planDef = new PlanDefinition(); var relatedArtifactList = List.of(new RelatedArtifact()); - var adapter = new PlanDefinitionAdapter(planDef); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(planDef); adapter.setRelatedArtifact(relatedArtifactList); assertEquals(relatedArtifactList, planDef.getRelatedArtifact()); assertEquals(relatedArtifactList, adapter.getRelatedArtifact()); } + @Test + void adapter_copy() { + var planDef = new PlanDefinition().setStatus(PublicationStatus.DRAFT); + planDef.setId("plan-1"); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(planDef); + var copy = (PlanDefinition) adapter.copy(); + var adapterCopy = new PlanDefinitionAdapter(copy); + adapterCopy.setId(new IdDt("PlanDefinition", "plan-2")); + assertNotEquals(planDef.getId(), copy.getId()); + planDef.setStatus(PublicationStatus.ACTIVE); + assertNotEquals(adapter.getStatus(), copy.getStatus()); + } + @Test void adapter_get_all_dependencies() { var dependencies = List.of( + "profileRef", "relatedArtifactRef", "libraryRef", "actionTriggerDataReqProfile", @@ -103,29 +177,30 @@ void adapter_get_all_dependencies() { "cpgPartOfExtRef", "nestedActionDefinitionRef"); var planDef = new PlanDefinition(); - planDef.getRelatedArtifactFirstRep().setResource(dependencies.get(0)); - planDef.getLibrary().add(new CanonicalType(dependencies.get(1))); + planDef.getMeta().addProfile(dependencies.get(0)); + planDef.getRelatedArtifactFirstRep().setResource(dependencies.get(1)); + planDef.getLibrary().add(new CanonicalType(dependencies.get(2))); var action = planDef.getActionFirstRep(); action.getTriggerFirstRep() .getDataFirstRep() - .setProfile(List.of(new CanonicalType(dependencies.get(2)))) + .setProfile(List.of(new CanonicalType(dependencies.get(3)))) .getCodeFilterFirstRep() - .setValueSet(dependencies.get(3)); + .setValueSet(dependencies.get(4)); action.getInputFirstRep() - .setProfile(List.of(new CanonicalType(dependencies.get(4)))) + .setProfile(List.of(new CanonicalType(dependencies.get(5)))) .getCodeFilterFirstRep() - .setValueSet(dependencies.get(5)); + .setValueSet(dependencies.get(6)); action.getOutputFirstRep() - .setProfile(List.of(new CanonicalType(dependencies.get(6)))) + .setProfile(List.of(new CanonicalType(dependencies.get(7)))) .getCodeFilterFirstRep() - .setValueSet(dependencies.get(7)); - action.setDefinition(new CanonicalType(dependencies.get(8))); - action.getConditionFirstRep().getExpression().setReference(dependencies.get(9)); - action.getDynamicValueFirstRep().getExpression().setReference(dependencies.get(10)); + .setValueSet(dependencies.get(8)); + action.setDefinition(new CanonicalType(dependencies.get(9))); + action.getConditionFirstRep().getExpression().setReference(dependencies.get(10)); + action.getDynamicValueFirstRep().getExpression().setReference(dependencies.get(11)); planDef.addExtension(new Extension( - "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-partOf", new CanonicalType(dependencies.get(11)))); - action.addAction().setDefinition(new CanonicalType(dependencies.get(12))); - var adapter = new PlanDefinitionAdapter(planDef); + "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-partOf", new CanonicalType(dependencies.get(12)))); + action.addAction().setDefinition(new CanonicalType(dependencies.get(13))); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(planDef); var extractedDependencies = adapter.getDependencies(); assertEquals(extractedDependencies.size(), dependencies.size()); extractedDependencies.forEach(dep -> { diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r4/QuestionnaireAdapterTest.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r4/QuestionnaireAdapterTest.java new file mode 100644 index 000000000..3c627faf3 --- /dev/null +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r4/QuestionnaireAdapterTest.java @@ -0,0 +1,227 @@ +package org.opencds.cqf.fhir.utility.adapter.r4; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import java.time.LocalDate; +import java.util.Date; +import java.util.List; +import org.hl7.fhir.instance.model.api.IDomainResource; +import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.CanonicalType; +import org.hl7.fhir.r4.model.Enumerations.PublicationStatus; +import org.hl7.fhir.r4.model.Expression; +import org.hl7.fhir.r4.model.Extension; +import org.hl7.fhir.r4.model.Library; +import org.hl7.fhir.r4.model.Period; +import org.hl7.fhir.r4.model.Questionnaire; +import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemType; +import org.hl7.fhir.r4.model.RelatedArtifact; +import org.junit.jupiter.api.Test; +import org.opencds.cqf.fhir.utility.Constants; +import org.opencds.cqf.fhir.utility.visitor.PackageVisitor; + +public class QuestionnaireAdapterTest { + private final FhirContext fhirContext = FhirContext.forR4Cached(); + private final org.opencds.cqf.fhir.utility.adapter.AdapterFactory adapterFactory = new AdapterFactory(); + + @Test + void invalid_object_fails() { + assertThrows(IllegalArgumentException.class, () -> new QuestionnaireAdapter(new Library())); + } + + @Test + void adapter_accepts_visitor() { + var spyVisitor = spy(new PackageVisitor(fhirContext)); + doReturn(new Bundle()).when(spyVisitor).visit(any(QuestionnaireAdapter.class), any(), any()); + IDomainResource questionnaire = new Questionnaire(); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(questionnaire); + adapter.accept(spyVisitor, null, null); + verify(spyVisitor, times(1)).visit(any(QuestionnaireAdapter.class), any(), any()); + } + + @Test + void adapter_get_and_set_name() { + var questionnaire = new Questionnaire(); + var name = "name"; + questionnaire.setName(name); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(questionnaire); + assertEquals(name, adapter.getName()); + var newName = "name2"; + adapter.setName(newName); + assertEquals(newName, questionnaire.getName()); + } + + @Test + void adapter_get_and_set_url() { + var questionnaire = new Questionnaire(); + var url = "www.url.com"; + questionnaire.setUrl(url); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(questionnaire); + assertEquals(url, adapter.getUrl()); + var newUrl = "www.url2.com"; + adapter.setUrl(newUrl); + assertEquals(newUrl, questionnaire.getUrl()); + } + + @Test + void adapter_get_and_set_version() { + var questionnaire = new Questionnaire(); + var version = "1.0.0"; + questionnaire.setVersion(version); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(questionnaire); + assertTrue(adapter.hasVersion()); + assertEquals(version, adapter.getVersion()); + var newVersion = "1.0.1"; + adapter.setVersion(newVersion); + assertEquals(newVersion, questionnaire.getVersion()); + } + + @Test + void adapter_get_and_set_status() { + var questionnaire = new Questionnaire(); + var status = PublicationStatus.DRAFT; + questionnaire.setStatus(status); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(questionnaire); + assertEquals(status.toCode(), adapter.getStatus()); + assertThrows(UnprocessableEntityException.class, () -> adapter.setStatus("invalid-status")); + var newStatus = PublicationStatus.ACTIVE; + adapter.setStatus(newStatus.toCode()); + assertEquals(newStatus, PublicationStatus.fromCode(adapter.getStatus())); + } + + @Test + void adapter_get_and_set_dates() { + var questionnaire = new Questionnaire(); + var date = new Date(); + var approvalDate = new Date(); + var effectivePeriod = new Period() + .setStart(java.sql.Date.valueOf(LocalDate.parse("2020-01-01"))) + .setEnd(java.sql.Date.valueOf(LocalDate.parse("2020-12-31"))); + questionnaire.setDate(date); + questionnaire.setApprovalDate(approvalDate); + questionnaire.setEffectivePeriod(effectivePeriod); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(questionnaire); + assertEquals(date, adapter.getDate()); + assertEquals(approvalDate, adapter.getApprovalDate()); + assertEquals(effectivePeriod, adapter.getEffectivePeriod()); + var newDate = new Date(); + newDate.setTime(100); + adapter.setDate(newDate); + assertEquals(newDate, questionnaire.getDate()); + var newApprovalDate = new Date(); + newApprovalDate.setTime(100); + adapter.setApprovalDate(newApprovalDate); + assertEquals(newApprovalDate, questionnaire.getApprovalDate()); + var newEffectivePeriod = new Period() + .setStart(java.sql.Date.valueOf(LocalDate.parse("2021-01-01"))) + .setEnd(java.sql.Date.valueOf(LocalDate.parse("2021-12-31"))); + adapter.setEffectivePeriod(newEffectivePeriod); + assertEquals(newEffectivePeriod, adapter.getEffectivePeriod()); + } + + @Test + void adapter_get_experimental() { + var questionnaire = new Questionnaire(); + var experimental = true; + questionnaire.setExperimental(experimental); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(questionnaire); + assertEquals(experimental, adapter.getExperimental()); + } + + @Test + void adapter_set_relatedArtifact() { + var questionnaire = new Questionnaire(); + var relatedArtifactList = List.of(new RelatedArtifact()); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(questionnaire); + adapter.setRelatedArtifact(relatedArtifactList); + assertEquals(0, adapter.getRelatedArtifact().size()); + } + + @Test + void adapter_copy() { + var questionnaire = new Questionnaire().setStatus(PublicationStatus.DRAFT); + questionnaire.setId("plan-1"); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(questionnaire); + var copy = (Questionnaire) adapter.copy(); + copy.setId("plan-2"); + assertNotEquals(questionnaire.getId(), copy.getId()); + questionnaire.setStatus(PublicationStatus.ACTIVE); + assertNotEquals(adapter.getStatus(), copy.getStatus()); + } + + @Test + void adapter_get_all_dependencies() { + var dependencies = List.of( + "profileRef", + "derivedRef", + "cqfLibraryRef", + "variableRef", + "itemDefinitionRef", + "answerValueSetRef", + // "itemMediaRef", + // "itemAnswerMediaRef", + "unitValueSetRef", + "referenceProfileRef", + "candidateExpressionRef", + "lookupQuestionnaireRef", + "itemVariableRef", + "initialExpressionRef", + "calculatedExpressionRef", + // "calculatedValueRef", + "expressionRef", + "subQuestionnaireRef"); + var questionnaire = new Questionnaire(); + questionnaire.getMeta().addProfile(dependencies.get(0)); + questionnaire.addDerivedFrom(dependencies.get(1)); + questionnaire.addExtension(Constants.CQF_LIBRARY, new CanonicalType(dependencies.get(2))); + var variableExt = new Extension(Constants.VARIABLE_EXTENSION) + .setValue(new Expression().setReference(dependencies.get(3))); + questionnaire.addExtension(variableExt); + questionnaire.addItem().setDefinition(dependencies.get(4) + "#Observation"); + questionnaire.addItem().setAnswerValueSet(dependencies.get(5)); + questionnaire + .addItem() + .setType(QuestionnaireItemType.QUANTITY) + .addExtension(Constants.QUESTIONNAIRE_UNIT_VALUE_SET, new CanonicalType(dependencies.get(6))); + questionnaire + .addItem() + .addExtension(Constants.QUESTIONNAIRE_REFERENCE_PROFILE, new CanonicalType(dependencies.get(7))); + var candidateExpressionExt = new Extension(Constants.SDC_QUESTIONNAIRE_CANDIDATE_EXPRESSION) + .setValue(new Expression().setReference(dependencies.get(8))); + questionnaire.addItem().addExtension(candidateExpressionExt); + var lookupQuestionnaireExt = new Extension(Constants.SDC_QUESTIONNAIRE_LOOKUP_QUESTIONNAIRE) + .setValue(new CanonicalType(dependencies.get(9))); + questionnaire.addItem().addExtension(lookupQuestionnaireExt); + var itemVariableExt = new Extension(Constants.VARIABLE_EXTENSION) + .setValue(new Expression().setReference(dependencies.get(10))); + questionnaire.addItem().addExtension(itemVariableExt); + var initialExpressionExt = new Extension(Constants.SDC_QUESTIONNAIRE_INITIAL_EXPRESSION) + .setValue(new Expression().setReference(dependencies.get(11))); + questionnaire.addItem().addExtension(initialExpressionExt); + var calculatedExpressionExt = new Extension(Constants.SDC_QUESTIONNAIRE_CALCULATED_EXPRESSION) + .setValue(new Expression().setReference(dependencies.get(12))); + questionnaire.addItem().addExtension(calculatedExpressionExt); + var expressionExt = + new Extension(Constants.CQF_EXPRESSION).setValue(new Expression().setReference(dependencies.get(13))); + questionnaire.addItem().addExtension(expressionExt); + questionnaire + .addItem() + .addExtension(Constants.SDC_QUESTIONNAIRE_SUB_QUESTIONNAIRE, new CanonicalType(dependencies.get(14))); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(questionnaire); + var extractedDependencies = adapter.getDependencies(); + assertEquals(dependencies.size(), extractedDependencies.size()); + extractedDependencies.forEach(dep -> { + assertTrue(dependencies.indexOf(dep.getReference()) >= 0); + }); + } +} diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r4/ResourceAdapterTest.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r4/ResourceAdapterTest.java new file mode 100644 index 000000000..d789de86f --- /dev/null +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r4/ResourceAdapterTest.java @@ -0,0 +1,118 @@ +package org.opencds.cqf.fhir.utility.adapter.r4; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import ca.uhn.fhir.model.primitive.IdDt; +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.read.ListAppender; +import java.util.Date; +import java.util.List; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.r4.model.BooleanType; +import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.Extension; +import org.hl7.fhir.r4.model.HumanName; +import org.hl7.fhir.r4.model.IdType; +import org.hl7.fhir.r4.model.Library; +import org.hl7.fhir.r4.model.Meta; +import org.hl7.fhir.r4.model.Patient; +import org.hl7.fhir.r4.model.PlanDefinition; +import org.junit.jupiter.api.Test; +import org.opencds.cqf.fhir.utility.adapter.Adapter; +import org.slf4j.LoggerFactory; + +public class ResourceAdapterTest { + private final org.opencds.cqf.fhir.utility.adapter.AdapterFactory adapterFactory = new AdapterFactory(); + + @Test + void invalid_object_fails() { + assertThrows(IllegalArgumentException.class, () -> new ResourceAdapter(null)); + assertThrows(IllegalArgumentException.class, () -> new ResourceAdapter(new org.hl7.fhir.r5.model.Library())); + } + + @Test + void adapter_get_and_set_property() { + var resource = new Patient(); + var id = new IdDt("patient-1"); + resource.setId(id); + var adapter = adapterFactory.createResource(resource); + assertTrue(((ResourceAdapter) adapter).isDomainResource()); + assertEquals(resource, ((ResourceAdapter) adapter).getDomainResource().get()); + assertEquals(id.getValue(), ((IIdType) adapter.getSingleProperty("id")).getValue()); + var newId = new IdType("patient-2"); + adapter.setProperty("id", newId); + assertEquals(newId, resource.getIdElement()); + assertEquals("id", adapter.getTypesForProperty("id")[0]); + assertNotNull(adapter.makeProperty("language")); + assertNull(adapter.getSingleProperty("meta")); + var meta = (Meta) adapter.addChild("meta"); + var date = new Date(); + meta.setLastUpdated(date); + assertEquals(date, ((Meta) adapter.getProperty("meta")[0]).getLastUpdated()); + resource.addName(new HumanName().addGiven("name1")); + resource.addName(new HumanName().addGiven("name2")); + assertThrows(IllegalArgumentException.class, () -> adapter.getSingleProperty("name")); + } + + @Test + void adapter_copy() { + var resource = new Patient(); + resource.setId("patient-1"); + resource.setMeta(new Meta().setLastUpdated(new Date())); + var adapter = adapterFactory.createResource(resource); + var copy = (Patient) adapter.copy(); + assertTrue(adapter.equalsDeep(copy)); + var newDate = new Date(); + newDate.setTime(100); + copy.setMeta(new Meta().setLastUpdated(newDate)); + assertFalse(adapter.equalsDeep(copy)); + assertTrue(adapter.equalsShallow(copy)); + copy.setId("patient-2"); + assertFalse(adapter.equalsShallow(copy)); + resource.setLanguage("FR"); + adapter.copyValues(copy); + assertEquals("FR", copy.getLanguage()); + } + + @Test + void adapter_get_and_set_extension() { + var logger = (Logger) LoggerFactory.getLogger(Adapter.class); + var listAppender = new ListAppender(); + listAppender.start(); + logger.addAppender(listAppender); + var bundle = new Bundle(); + var bundleAdapter = new ResourceAdapter(bundle); + assertFalse(bundleAdapter.hasExtension()); + bundleAdapter.setExtension(List.of(new Extension())); + assertEquals(1, listAppender.list.size()); + assertEquals(Level.DEBUG, listAppender.list.get(0).getLevel()); + bundleAdapter.addExtension(new Extension()); + assertEquals(2, listAppender.list.size()); + assertEquals(Level.DEBUG, listAppender.list.get(1).getLevel()); + var resource = new Patient(); + var extensionList = List.of(new Extension().setUrl("test-extension-url").setValue(new BooleanType(true))); + var adapter = adapterFactory.createResource(resource); + adapter.setExtension(extensionList); + assertTrue(adapter.hasExtension()); + assertEquals(extensionList, resource.getExtension()); + assertEquals(extensionList, adapter.getExtension()); + assertTrue(adapter.hasExtension("test-extension-url")); + } + + @Test + void adapter_get_contained() { + var resource = new PlanDefinition(); + resource.addContained(new Library()); + var adapter = adapterFactory.createResource(resource); + assertTrue(adapter.hasContained()); + assertNotNull(adapter.getContained()); + assertFalse(adapter.hasContained(adapter.getContained().get(0))); + } +} diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r4/StructureDefinitionAdapterTest.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r4/StructureDefinitionAdapterTest.java new file mode 100644 index 000000000..100795adb --- /dev/null +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r4/StructureDefinitionAdapterTest.java @@ -0,0 +1,195 @@ +package org.opencds.cqf.fhir.utility.adapter.r4; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import java.time.LocalDate; +import java.util.Date; +import java.util.List; +import org.hl7.fhir.instance.model.api.IDomainResource; +import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionBindingComponent; +import org.hl7.fhir.r4.model.Enumerations.PublicationStatus; +import org.hl7.fhir.r4.model.Expression; +import org.hl7.fhir.r4.model.Library; +import org.hl7.fhir.r4.model.Period; +import org.hl7.fhir.r4.model.RelatedArtifact; +import org.hl7.fhir.r4.model.StructureDefinition; +import org.junit.jupiter.api.Test; +import org.opencds.cqf.fhir.utility.Constants; +import org.opencds.cqf.fhir.utility.visitor.PackageVisitor; + +public class StructureDefinitionAdapterTest { + private final FhirContext fhirContext = FhirContext.forR4Cached(); + private final org.opencds.cqf.fhir.utility.adapter.AdapterFactory adapterFactory = new AdapterFactory(); + + @Test + void invalid_object_fails() { + assertThrows(IllegalArgumentException.class, () -> new StructureDefinitionAdapter(new Library())); + assertNotNull(new StructureDefinitionAdapter((IDomainResource) new StructureDefinition())); + } + + @Test + void adapter_accepts_visitor() { + var spyVisitor = spy(new PackageVisitor(fhirContext)); + doReturn(new Bundle()).when(spyVisitor).visit(any(StructureDefinitionAdapter.class), any(), any()); + IDomainResource structureDef = new StructureDefinition(); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(structureDef); + assertEquals(structureDef, adapter.get()); + adapter.accept(spyVisitor, null, null); + verify(spyVisitor, times(1)).visit(any(StructureDefinitionAdapter.class), any(), any()); + } + + @Test + void adapter_get_and_set_name() { + var structureDef = new StructureDefinition(); + var name = "name"; + structureDef.setName(name); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(structureDef); + assertEquals(name, adapter.getName()); + var newName = "name2"; + adapter.setName(newName); + assertEquals(newName, structureDef.getName()); + } + + @Test + void adapter_get_and_set_url() { + var structureDef = new StructureDefinition(); + var url = "www.url.com"; + structureDef.setUrl(url); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(structureDef); + assertTrue(adapter.hasUrl()); + assertEquals(url, adapter.getUrl()); + var newUrl = "www.url2.com"; + adapter.setUrl(newUrl); + assertTrue(adapter.hasUrl()); + assertEquals(newUrl, structureDef.getUrl()); + } + + @Test + void adapter_get_and_set_version() { + var structureDef = new StructureDefinition(); + var version = "1.0.0"; + structureDef.setVersion(version); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(structureDef); + assertTrue(adapter.hasVersion()); + assertEquals(version, adapter.getVersion()); + var newVersion = "1.0.1"; + adapter.setVersion(newVersion); + assertEquals(newVersion, structureDef.getVersion()); + } + + @Test + void adapter_get_and_set_status() { + var structureDef = new StructureDefinition(); + var status = PublicationStatus.DRAFT; + structureDef.setStatus(status); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(structureDef); + assertEquals(status.toCode(), adapter.getStatus()); + assertThrows(UnprocessableEntityException.class, () -> adapter.setStatus("invalid-status")); + var newStatus = PublicationStatus.ACTIVE; + adapter.setStatus(newStatus.toCode()); + assertEquals(newStatus, PublicationStatus.fromCode(adapter.getStatus())); + } + + @Test + void adapter_get_and_set_dates() { + // StructureDefinition does not have fields approvalDate and effectivePeriod + var structureDef = new StructureDefinition(); + var date = new Date(); + var effectivePeriod = new Period() + .setStart(java.sql.Date.valueOf(LocalDate.parse("2020-01-01"))) + .setEnd(java.sql.Date.valueOf(LocalDate.parse("2020-12-31"))); + structureDef.setDate(date); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(structureDef); + assertEquals(date, adapter.getDate()); + assertEquals(null, adapter.getApprovalDate()); + assertNotEquals(effectivePeriod, adapter.getEffectivePeriod()); + var newDate = new Date(); + newDate.setTime(100); + adapter.setDate(newDate); + assertEquals(newDate, structureDef.getDate()); + var newApprovalDate = new Date(); + newApprovalDate.setTime(100); + adapter.setApprovalDate(newApprovalDate); + assertEquals(null, adapter.getApprovalDate()); + var newEffectivePeriod = new Period() + .setStart(java.sql.Date.valueOf(LocalDate.parse("2021-01-01"))) + .setEnd(java.sql.Date.valueOf(LocalDate.parse("2021-12-31"))); + adapter.setEffectivePeriod(newEffectivePeriod); + assertNotEquals(newEffectivePeriod, adapter.getEffectivePeriod()); + } + + @Test + void adapter_get_experimental() { + var structureDef = new StructureDefinition(); + var experimental = true; + structureDef.setExperimental(experimental); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(structureDef); + assertEquals(experimental, adapter.getExperimental()); + } + + @Test + void adapter_set_relatedArtifact() { + var structureDef = new StructureDefinition(); + var relatedArtifactList = List.of(new RelatedArtifact()); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(structureDef); + adapter.setRelatedArtifact(relatedArtifactList); + assertEquals(0, adapter.getRelatedArtifact().size()); + } + + @Test + void adapter_copy() { + var structureDef = new StructureDefinition().setStatus(PublicationStatus.DRAFT); + structureDef.setId("plan-1"); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(structureDef); + var copy = (StructureDefinition) adapter.copy(); + copy.setId("plan-2"); + assertNotEquals(structureDef.getId(), copy.getId()); + structureDef.setStatus(PublicationStatus.ACTIVE); + assertNotEquals(adapter.getStatus(), copy.getStatus()); + } + + @Test + void adapter_get_all_dependencies() { + var dependencies = List.of( + "profileRef", + "baseDefinition", + "cpgAssertionRef", + "cpgFeatureRef", + "cpgInferenceRef", + "elementProfileRef", + "elementTargetProfileRef", + "elementValueSetBindingRef"); + var structureDef = new StructureDefinition(); + structureDef.getMeta().addProfile(dependencies.get(0)); + structureDef.setBaseDefinition(dependencies.get(1)); + structureDef.addExtension( + Constants.CPG_ASSERTION_EXPRESSION, new Expression().setReference(dependencies.get(2))); + structureDef.addExtension(Constants.CPG_FEATURE_EXPRESSION, new Expression().setReference(dependencies.get(3))); + structureDef.addExtension( + Constants.CPG_INFERENCE_EXPRESSION, new Expression().setReference(dependencies.get(4))); + structureDef.getDifferential().addElement().addType().addProfile(dependencies.get(5)); + structureDef.getDifferential().addElement().addType().addTargetProfile(dependencies.get(6)); + structureDef + .getDifferential() + .addElement() + .setBinding(new ElementDefinitionBindingComponent().setValueSet(dependencies.get(7))); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(structureDef); + var extractedDependencies = adapter.getDependencies(); + assertEquals(dependencies.size(), extractedDependencies.size()); + extractedDependencies.forEach(dep -> { + assertTrue(dependencies.indexOf(dep.getReference()) >= 0); + }); + } +} diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r4/ValueSetAdapterTest.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r4/ValueSetAdapterTest.java new file mode 100644 index 000000000..51dfbc2e5 --- /dev/null +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r4/ValueSetAdapterTest.java @@ -0,0 +1,170 @@ +package org.opencds.cqf.fhir.utility.adapter.r4; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import java.time.LocalDate; +import java.util.Date; +import java.util.List; +import org.hl7.fhir.instance.model.api.IDomainResource; +import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.Enumerations.PublicationStatus; +import org.hl7.fhir.r4.model.Library; +import org.hl7.fhir.r4.model.Period; +import org.hl7.fhir.r4.model.RelatedArtifact; +import org.hl7.fhir.r4.model.ValueSet; +import org.junit.jupiter.api.Test; +import org.opencds.cqf.fhir.utility.visitor.PackageVisitor; + +public class ValueSetAdapterTest { + private final FhirContext fhirContext = FhirContext.forR4Cached(); + private final org.opencds.cqf.fhir.utility.adapter.AdapterFactory adapterFactory = new AdapterFactory(); + + @Test + void invalid_object_fails() { + assertThrows(IllegalArgumentException.class, () -> new ValueSetAdapter(new Library())); + } + + @Test + void adapter_accepts_visitor() { + var spyVisitor = spy(new PackageVisitor(fhirContext)); + doReturn(new Bundle()).when(spyVisitor).visit(any(ValueSetAdapter.class), any(), any()); + IDomainResource valueSet = new ValueSet(); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(valueSet); + assertEquals(valueSet, adapter.get()); + adapter.accept(spyVisitor, null, null); + verify(spyVisitor, times(1)).visit(any(ValueSetAdapter.class), any(), any()); + } + + @Test + void adapter_get_and_set_name() { + var valueSet = new ValueSet(); + var name = "name"; + valueSet.setName(name); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(valueSet); + assertEquals(name, adapter.getName()); + var newName = "name2"; + adapter.setName(newName); + assertEquals(newName, valueSet.getName()); + } + + @Test + void adapter_get_and_set_url() { + var valueSet = new ValueSet(); + var url = "www.url.com"; + valueSet.setUrl(url); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(valueSet); + assertTrue(adapter.hasUrl()); + assertEquals(url, adapter.getUrl()); + var newUrl = "www.url2.com"; + adapter.setUrl(newUrl); + assertTrue(adapter.hasUrl()); + assertEquals(newUrl, valueSet.getUrl()); + } + + @Test + void adapter_get_and_set_version() { + var valueSet = new ValueSet(); + var version = "1.0.0"; + valueSet.setVersion(version); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(valueSet); + assertTrue(adapter.hasVersion()); + assertEquals(version, adapter.getVersion()); + var newVersion = "1.0.1"; + adapter.setVersion(newVersion); + assertEquals(newVersion, valueSet.getVersion()); + } + + @Test + void adapter_get_and_set_status() { + var valueSet = new ValueSet(); + var status = PublicationStatus.DRAFT; + valueSet.setStatus(status); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(valueSet); + assertEquals(status.toCode(), adapter.getStatus()); + assertThrows(UnprocessableEntityException.class, () -> adapter.setStatus("invalid-status")); + var newStatus = PublicationStatus.ACTIVE; + adapter.setStatus(newStatus.toCode()); + assertEquals(newStatus, PublicationStatus.fromCode(adapter.getStatus())); + } + + @Test + void adapter_get_and_set_dates() { + // ValueSet does not have fields approvalDate and effectivePeriod + var valueSet = new ValueSet(); + var date = new Date(); + var effectivePeriod = new Period() + .setStart(java.sql.Date.valueOf(LocalDate.parse("2020-01-01"))) + .setEnd(java.sql.Date.valueOf(LocalDate.parse("2020-12-31"))); + valueSet.setDate(date); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(valueSet); + assertEquals(date, adapter.getDate()); + assertEquals(null, adapter.getApprovalDate()); + assertNotEquals(effectivePeriod, adapter.getEffectivePeriod()); + var newDate = new Date(); + newDate.setTime(100); + adapter.setDate(newDate); + assertEquals(newDate, valueSet.getDate()); + var newApprovalDate = new Date(); + newApprovalDate.setTime(100); + adapter.setApprovalDate(newApprovalDate); + assertEquals(null, adapter.getApprovalDate()); + var newEffectivePeriod = new Period() + .setStart(java.sql.Date.valueOf(LocalDate.parse("2021-01-01"))) + .setEnd(java.sql.Date.valueOf(LocalDate.parse("2021-12-31"))); + adapter.setEffectivePeriod(newEffectivePeriod); + assertNotEquals(newEffectivePeriod, adapter.getEffectivePeriod()); + } + + @Test + void adapter_get_experimental() { + var valueSet = new ValueSet(); + var experimental = true; + valueSet.setExperimental(experimental); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(valueSet); + assertEquals(experimental, adapter.getExperimental()); + } + + @Test + void adapter_set_relatedArtifact() { + var valueSet = new ValueSet(); + var relatedArtifactList = List.of(new RelatedArtifact()); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(valueSet); + adapter.setRelatedArtifact(relatedArtifactList); + assertEquals(0, adapter.getRelatedArtifact().size()); + } + + @Test + void adapter_copy() { + var valueSet = new ValueSet().setStatus(PublicationStatus.DRAFT); + valueSet.setId("valueset-1"); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(valueSet); + var copy = (ValueSet) adapter.copy(); + copy.setId("valueset-2"); + assertNotEquals(valueSet.getId(), copy.getId()); + valueSet.setStatus(PublicationStatus.ACTIVE); + assertNotEquals(adapter.getStatus(), copy.getStatus()); + } + + @Test + void adapter_get_all_dependencies() { + var dependencies = List.of("profileRef"); + var valueSet = new ValueSet(); + valueSet.getMeta().addProfile(dependencies.get(0)); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(valueSet); + var extractedDependencies = adapter.getDependencies(); + assertEquals(dependencies.size(), extractedDependencies.size()); + extractedDependencies.forEach(dep -> { + assertTrue(dependencies.indexOf(dep.getReference()) >= 0); + }); + } +} diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r5/EndpointAdapterTest.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r5/EndpointAdapterTest.java new file mode 100644 index 000000000..0dee94209 --- /dev/null +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r5/EndpointAdapterTest.java @@ -0,0 +1,29 @@ +package org.opencds.cqf.fhir.utility.adapter.r5; + +import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.hl7.fhir.r5.model.Endpoint; +import org.hl7.fhir.r5.model.PlanDefinition; +import org.junit.jupiter.api.Test; + +class EndpointAdapterTest { + private final org.opencds.cqf.fhir.utility.adapter.AdapterFactory adapterFactory = new AdapterFactory(); + + @Test + void invalid_object_fails() { + assertThrows(IllegalArgumentException.class, () -> new EndpointAdapter(new PlanDefinition())); + } + + @Test + void adapter_get_and_set_address() { + var endpoint = new Endpoint(); + var address = "123 Test Street"; + endpoint.setAddress(address); + var adapter = (EndpointAdapter) adapterFactory.createResource(endpoint); + assertEquals(address, adapter.getAddress()); + var newAddress = "456 Test Street"; + adapter.setAddress(newAddress); + assertEquals(newAddress, endpoint.getAddress()); + } +} diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r5/KnowledgeArtifactAdapterTest.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r5/KnowledgeArtifactAdapterTest.java new file mode 100644 index 000000000..e96386f90 --- /dev/null +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r5/KnowledgeArtifactAdapterTest.java @@ -0,0 +1,170 @@ +package org.opencds.cqf.fhir.utility.adapter.r5; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import org.hl7.fhir.instance.model.api.IDomainResource; +import org.hl7.fhir.r5.model.Bundle; +import org.hl7.fhir.r5.model.ChargeItemDefinition; +import org.hl7.fhir.r5.model.DateTimeType; +import org.hl7.fhir.r5.model.DateType; +import org.hl7.fhir.r5.model.Enumerations.PublicationStatus; +import org.hl7.fhir.r5.model.Extension; +import org.hl7.fhir.r5.model.Period; +import org.hl7.fhir.r5.model.RelatedArtifact; +import org.junit.jupiter.api.Test; +import org.opencds.cqf.fhir.utility.visitor.PackageVisitor; + +class KnowledgeArtifactAdapterTest { + private final FhirContext fhirContext = FhirContext.forR5Cached(); + private final org.opencds.cqf.fhir.utility.adapter.AdapterFactory adapterFactory = new AdapterFactory(); + + @Test + void invalid_object_fails() { + assertThrows( + IllegalArgumentException.class, + () -> new KnowledgeArtifactAdapter(new org.hl7.fhir.r4.model.Library())); + } + + @Test + void test() { + var resource = new ChargeItemDefinition(); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(resource); + assertNotNull(adapter); + } + + @Test + void adapter_accepts_visitor() { + var spyVisitor = spy(new PackageVisitor(fhirContext)); + doReturn(new Bundle()).when(spyVisitor).visit(any(KnowledgeArtifactAdapter.class), any(), any()); + IDomainResource def = new ChargeItemDefinition(); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(def); + adapter.accept(spyVisitor, null, null); + verify(spyVisitor, times(1)).visit(any(KnowledgeArtifactAdapter.class), any(), any()); + } + + @Test + void adapter_get_and_set_name() { + var def = new ChargeItemDefinition(); + var name = "name"; + def.setName(name); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(def); + assertEquals(name, adapter.getName()); + var newName = "name2"; + adapter.setName(newName); + assertEquals(newName, def.getName()); + } + + @Test + void adapter_get_and_set_url() { + var def = new ChargeItemDefinition(); + var url = "www.url.com"; + def.setUrl(url); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(def); + assertEquals(url, adapter.getUrl()); + var newUrl = "www.url2.com"; + adapter.setUrl(newUrl); + assertEquals(newUrl, def.getUrl()); + } + + @Test + void adapter_get_and_set_version() { + var def = new ChargeItemDefinition(); + var version = "1.0.0"; + def.setVersion(version); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(def); + assertTrue(adapter.hasVersion()); + assertEquals(version, adapter.getVersion()); + var newVersion = "1.0.1"; + adapter.setVersion(newVersion); + assertEquals(newVersion, def.getVersion()); + } + + @Test + void adapter_get_and_set_status() { + var def = new ChargeItemDefinition(); + var status = PublicationStatus.DRAFT; + def.setStatus(status); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(def); + assertEquals(status.toCode(), adapter.getStatus()); + assertThrows(UnprocessableEntityException.class, () -> adapter.setStatus("invalid-status")); + var newStatus = PublicationStatus.ACTIVE; + adapter.setStatus(newStatus.toCode()); + assertEquals(newStatus, PublicationStatus.fromCode(adapter.getStatus())); + } + + @Test + void adapter_get_and_set_dates() { + var def = new ChargeItemDefinition(); + var date = new Date(); + var approvalDate = new Date(); + def.setDate(date); + def.setApprovalDate(approvalDate); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(def); + assertEquals(date, adapter.getDate()); + assertEquals(approvalDate, adapter.getApprovalDate()); + var newDate = new Date(); + newDate.setTime(100); + adapter.setDate(newDate); + assertEquals(newDate, def.getDate()); + assertThrows(UnprocessableEntityException.class, () -> adapter.setDateElement(new DateType())); + var newDateElement = new DateTimeType().setValue(new Date()); + adapter.setDateElement(newDateElement); + assertEquals(newDateElement, def.getDateElement()); + assertThrows(UnprocessableEntityException.class, () -> adapter.setEffectivePeriod(new Extension())); + var newEffectivePeriod = new Period(); + newEffectivePeriod.setStart(new Date()); + newEffectivePeriod.setEnd(new Date()); + adapter.setEffectivePeriod(newEffectivePeriod); + assertThrows(Error.class, () -> def.getEffectivePeriod()); + var newApprovalDate = new Date(); + newApprovalDate.setTime(100); + adapter.setApprovalDate(newApprovalDate); + assertEquals(newApprovalDate, def.getApprovalDate()); + } + + @Test + void adapter_get_experimental() { + var def = new ChargeItemDefinition(); + var experimental = true; + def.setExperimental(experimental); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(def); + assertEquals(experimental, adapter.getExperimental()); + } + + @Test + void adapter_set_relatedArtifact() { + var def = new ChargeItemDefinition(); + var relatedArtifactList = List.of(new RelatedArtifact()); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(def); + adapter.setRelatedArtifact(relatedArtifactList); + assertEquals(0, adapter.getRelatedArtifact().size()); + assertThrows(UnprocessableEntityException.class, () -> adapter.setRelatedArtifact(Arrays.asList(new Period()))); + assertThrows(UnprocessableEntityException.class, () -> adapter.getRelatedArtifactsOfType("depends")); + } + + @Test + void adapter_copy() { + var def = new ChargeItemDefinition().setStatus(PublicationStatus.DRAFT); + def.setId("def-1"); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(def); + var copy = (ChargeItemDefinition) adapter.copy(); + copy.setId("def-2"); + assertNotEquals(def.getId(), copy.getId()); + def.setStatus(PublicationStatus.ACTIVE); + assertNotEquals(adapter.getStatus(), copy.getStatus()); + } +} diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r5/LibraryAdapterTest.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r5/LibraryAdapterTest.java new file mode 100644 index 000000000..851d9fb59 --- /dev/null +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r5/LibraryAdapterTest.java @@ -0,0 +1,237 @@ +package org.opencds.cqf.fhir.utility.adapter.r5; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.model.primitive.IdDt; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import org.hl7.fhir.instance.model.api.IDomainResource; +import org.hl7.fhir.r5.model.Attachment; +import org.hl7.fhir.r5.model.Bundle; +import org.hl7.fhir.r5.model.CodeableConcept; +import org.hl7.fhir.r5.model.Coding; +import org.hl7.fhir.r5.model.DataRequirement; +import org.hl7.fhir.r5.model.Enumerations.FHIRTypes; +import org.hl7.fhir.r5.model.Enumerations.PublicationStatus; +import org.hl7.fhir.r5.model.Library; +import org.hl7.fhir.r5.model.Period; +import org.hl7.fhir.r5.model.PlanDefinition; +import org.hl7.fhir.r5.model.RelatedArtifact; +import org.hl7.fhir.r5.model.UsageContext; +import org.junit.jupiter.api.Test; +import org.opencds.cqf.fhir.utility.visitor.PackageVisitor; + +class LibraryAdapterTest { + private final FhirContext fhirContext = FhirContext.forR5Cached(); + private final org.opencds.cqf.fhir.utility.adapter.AdapterFactory adapterFactory = new AdapterFactory(); + + @Test + void invalid_object_fails() { + assertThrows(IllegalArgumentException.class, () -> new LibraryAdapter(new PlanDefinition())); + } + + @Test + void adapter_accepts_visitor() { + var spyVisitor = spy(new PackageVisitor(fhirContext)); + doReturn(new Bundle()).when(spyVisitor).visit(any(LibraryAdapter.class), any(), any()); + IDomainResource library = new Library(); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(library); + adapter.accept(spyVisitor, null, null); + verify(spyVisitor, times(1)).visit(any(LibraryAdapter.class), any(), any()); + } + + @Test + void adapter_get_and_set_name() { + var library = new Library(); + var name = "name"; + library.setName(name); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(library); + assertEquals(name, adapter.getName()); + var newName = "name2"; + adapter.setName(newName); + assertEquals(newName, library.getName()); + } + + @Test + void adapter_get_and_set_url() { + var library = new Library(); + var url = "www.url.com"; + library.setUrl(url); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(library); + assertEquals(url, adapter.getUrl()); + var newUrl = "www.url2.com"; + adapter.setUrl(newUrl); + assertEquals(newUrl, library.getUrl()); + } + + @Test + void adapter_get_and_set_version() { + var library = new Library(); + var version = "1.0.0"; + library.setVersion(version); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(library); + assertTrue(adapter.hasVersion()); + assertEquals(version, adapter.getVersion()); + var newVersion = "1.0.1"; + adapter.setVersion(newVersion); + assertEquals(newVersion, library.getVersion()); + } + + @Test + void adapter_get_and_set_status() { + var library = new Library(); + var status = PublicationStatus.DRAFT; + library.setStatus(status); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(library); + assertEquals(status.toCode(), adapter.getStatus()); + assertThrows(UnprocessableEntityException.class, () -> adapter.setStatus("invalid-status")); + var newStatus = PublicationStatus.ACTIVE; + adapter.setStatus(newStatus.toCode()); + assertEquals(newStatus, PublicationStatus.fromCode(adapter.getStatus())); + } + + @Test + void adapter_get_and_set_dates() { + var library = new Library(); + var date = new Date(); + var approvalDate = new Date(); + var effectivePeriod = new Period() + .setStart(java.sql.Date.valueOf(LocalDate.parse("2020-01-01"))) + .setEnd(java.sql.Date.valueOf(LocalDate.parse("2020-12-31"))); + library.setDate(date); + library.setApprovalDate(approvalDate); + library.setEffectivePeriod(effectivePeriod); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(library); + assertEquals(date, adapter.getDate()); + assertEquals(approvalDate, adapter.getApprovalDate()); + assertEquals(effectivePeriod, adapter.getEffectivePeriod()); + var newDate = new Date(); + newDate.setTime(100); + adapter.setDate(newDate); + assertEquals(newDate, library.getDate()); + var newApprovalDate = new Date(); + newApprovalDate.setTime(100); + adapter.setApprovalDate(newApprovalDate); + assertEquals(newApprovalDate, library.getApprovalDate()); + var newEffectivePeriod = new Period() + .setStart(java.sql.Date.valueOf(LocalDate.parse("2021-01-01"))) + .setEnd(java.sql.Date.valueOf(LocalDate.parse("2021-12-31"))); + adapter.setEffectivePeriod(newEffectivePeriod); + assertEquals(newEffectivePeriod, adapter.getEffectivePeriod()); + } + + @Test + void adapter_get_experimental() { + var library = new Library(); + var experimental = true; + library.setExperimental(experimental); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(library); + assertEquals(experimental, adapter.getExperimental()); + } + + @Test + void adapter_set_relatedArtifact() { + var library = new Library(); + var relatedArtifactList = List.of(new RelatedArtifact()); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(library); + adapter.setRelatedArtifact(relatedArtifactList); + assertEquals(relatedArtifactList, library.getRelatedArtifact()); + assertEquals(relatedArtifactList, adapter.getRelatedArtifact()); + } + + @Test + void adapter_copy() { + var library = new Library().setStatus(PublicationStatus.DRAFT); + library.setId("library-1"); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(library); + var copy = (Library) adapter.copy(); + var adapterCopy = new LibraryAdapter(copy); + adapterCopy.setId(new IdDt("Library", "library-2")); + assertNotEquals(library.getId(), copy.getId()); + library.setStatus(PublicationStatus.ACTIVE); + assertNotEquals(adapter.getStatus(), copy.getStatus()); + } + + @Test + void adapter_get_all_dependencies() { + var dependencies = List.of( + "profileRef", "relatedArtifactRef", "dataRequirementProfileRef", "dataRequirementCodeFilterRef"); + var library = new Library(); + library.getMeta().addProfile(dependencies.get(0)); + library.getRelatedArtifactFirstRep().setResource(dependencies.get(1)); + library.addDataRequirement().addProfile(dependencies.get(2)); + library.addDataRequirement().addCodeFilter().setValueSet(dependencies.get(3)); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(library); + var extractedDependencies = adapter.getDependencies(); + assertEquals(extractedDependencies.size(), dependencies.size()); + extractedDependencies.forEach(dep -> { + assertTrue(dependencies.indexOf(dep.getReference()) >= 0); + }); + } + + @Test + void adapter_get_and_set_content() { + var library = new Library(); + var adapter = new LibraryAdapter(library); + var contentList = new ArrayList(); + contentList.add(new Attachment().setContentType("text/cql").setData(new byte[10])); + adapter.setContent(contentList); + assertTrue(adapter.hasContent()); + assertEquals(contentList, adapter.getContent()); + adapter.addContent().setContentType("text/xml").setData(new byte[20]); + assertEquals(2, adapter.getContent().size()); + assertEquals("text/xml", adapter.getContent().get(1).getContentType()); + } + + @Test + void adapter_get_and_set_type() { + var type = new CodeableConcept(new Coding("www.test.com", "test", "Test")); + var library = new Library().setType(type); + var adapter = new LibraryAdapter(library); + assertEquals(type, adapter.getType()); + assertThrows(UnprocessableEntityException.class, () -> adapter.setType("test")); + var newType = new CodeableConcept(new Coding("http://hl7.org/fhir/ValueSet/library-type", "logic-library", "")); + adapter.setType(newType.getCoding().get(0).getCode()); + assertEquals( + newType.getCoding().get(0).getCode(), + ((CodeableConcept) adapter.getType()).getCoding().get(0).getCode()); + assertEquals( + newType.getCoding().get(0).getSystem(), + ((CodeableConcept) adapter.getType()).getCoding().get(0).getSystem()); + } + + @Test + void adapter_get_and_set_dataRequirement() { + var library = new Library(); + var adapter = new LibraryAdapter(library); + var dataRequirements = new ArrayList(); + dataRequirements.add(new DataRequirement().setType(FHIRTypes.PATIENT)); + adapter.setDataRequirement(dataRequirements); + assertEquals(dataRequirements, library.getDataRequirement()); + adapter.addDataRequirement(new DataRequirement().setType(FHIRTypes.OBSERVATION)); + assertEquals(library.getDataRequirement(), adapter.getDataRequirement()); + assertEquals(2, adapter.getDataRequirement().size()); + } + + @Test + void adapter_get_useContext() { + var library = new Library(); + var useContext = new UsageContext().setCode(new Coding("www.test.com", "test", "Test")); + library.setUseContext(Collections.singletonList(useContext)); + var adapter = new LibraryAdapter(library); + assertEquals(useContext, adapter.getUseContext().get(0)); + } +} diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r5/MeasureAdapterTest.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r5/MeasureAdapterTest.java new file mode 100644 index 000000000..51a745c8f --- /dev/null +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r5/MeasureAdapterTest.java @@ -0,0 +1,232 @@ +package org.opencds.cqf.fhir.utility.adapter.r5; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.model.primitive.IdDt; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import java.time.LocalDate; +import java.util.Date; +import java.util.List; +import org.hl7.fhir.instance.model.api.IDomainResource; +import org.hl7.fhir.r5.model.Bundle; +import org.hl7.fhir.r5.model.CanonicalType; +import org.hl7.fhir.r5.model.CodeableConcept; +import org.hl7.fhir.r5.model.Coding; +import org.hl7.fhir.r5.model.Enumerations.PublicationStatus; +import org.hl7.fhir.r5.model.Expression; +import org.hl7.fhir.r5.model.Library; +import org.hl7.fhir.r5.model.Measure; +import org.hl7.fhir.r5.model.Patient; +import org.hl7.fhir.r5.model.Period; +import org.hl7.fhir.r5.model.Reference; +import org.hl7.fhir.r5.model.RelatedArtifact; +import org.junit.jupiter.api.Test; +import org.opencds.cqf.fhir.utility.Constants; +import org.opencds.cqf.fhir.utility.visitor.PackageVisitor; + +public class MeasureAdapterTest { + private final FhirContext fhirContext = FhirContext.forR5Cached(); + private final org.opencds.cqf.fhir.utility.adapter.AdapterFactory adapterFactory = new AdapterFactory(); + + @Test + void invalid_object_fails() { + assertThrows(IllegalArgumentException.class, () -> new MeasureAdapter(new Library())); + } + + @Test + void adapter_accepts_visitor() { + var spyVisitor = spy(new PackageVisitor(fhirContext)); + doReturn(new Bundle()).when(spyVisitor).visit(any(MeasureAdapter.class), any(), any()); + IDomainResource measure = new Measure(); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(measure); + adapter.accept(spyVisitor, null, null); + verify(spyVisitor, times(1)).visit(any(MeasureAdapter.class), any(), any()); + } + + @Test + void adapter_get_and_set_name() { + var measure = new Measure(); + var name = "name"; + measure.setName(name); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(measure); + assertEquals(name, adapter.getName()); + var newName = "name2"; + adapter.setName(newName); + assertEquals(newName, measure.getName()); + } + + @Test + void adapter_get_and_set_url() { + var measure = new Measure(); + var url = "www.url.com"; + measure.setUrl(url); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(measure); + assertEquals(url, adapter.getUrl()); + var newUrl = "www.url2.com"; + adapter.setUrl(newUrl); + assertEquals(newUrl, measure.getUrl()); + } + + @Test + void adapter_get_and_set_version() { + var measure = new Measure(); + var version = "1.0.0"; + measure.setVersion(version); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(measure); + assertTrue(adapter.hasVersion()); + assertEquals(version, adapter.getVersion()); + var newVersion = "1.0.1"; + adapter.setVersion(newVersion); + assertEquals(newVersion, measure.getVersion()); + } + + @Test + void adapter_get_and_set_status() { + var measure = new Measure(); + var status = PublicationStatus.DRAFT; + measure.setStatus(status); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(measure); + assertEquals(status.toCode(), adapter.getStatus()); + assertThrows(UnprocessableEntityException.class, () -> adapter.setStatus("invalid-status")); + var newStatus = PublicationStatus.ACTIVE; + adapter.setStatus(newStatus.toCode()); + assertEquals(newStatus, PublicationStatus.fromCode(adapter.getStatus())); + } + + @Test + void adapter_get_and_set_dates() { + var measure = new Measure(); + var date = new Date(); + var approvalDate = new Date(); + var effectivePeriod = new Period() + .setStart(java.sql.Date.valueOf(LocalDate.parse("2020-01-01"))) + .setEnd(java.sql.Date.valueOf(LocalDate.parse("2020-12-31"))); + measure.setDate(date); + measure.setApprovalDate(approvalDate); + measure.setEffectivePeriod(effectivePeriod); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(measure); + assertEquals(date, adapter.getDate()); + assertEquals(approvalDate, adapter.getApprovalDate()); + assertEquals(effectivePeriod, adapter.getEffectivePeriod()); + var newDate = new Date(); + newDate.setTime(100); + adapter.setDate(newDate); + assertEquals(newDate, measure.getDate()); + var newApprovalDate = new Date(); + newApprovalDate.setTime(100); + adapter.setApprovalDate(newApprovalDate); + assertEquals(newApprovalDate, measure.getApprovalDate()); + var newEffectivePeriod = new Period() + .setStart(java.sql.Date.valueOf(LocalDate.parse("2021-01-01"))) + .setEnd(java.sql.Date.valueOf(LocalDate.parse("2021-12-31"))); + adapter.setEffectivePeriod(newEffectivePeriod); + assertEquals(newEffectivePeriod, adapter.getEffectivePeriod()); + } + + @Test + void adapter_get_experimental() { + var measure = new Measure(); + var experimental = true; + measure.setExperimental(experimental); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(measure); + assertEquals(experimental, adapter.getExperimental()); + } + + @Test + void adapter_set_relatedArtifact() { + var measure = new Measure(); + var relatedArtifactList = List.of(new RelatedArtifact()); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(measure); + adapter.setRelatedArtifact(relatedArtifactList); + assertEquals(relatedArtifactList, measure.getRelatedArtifact()); + assertEquals(relatedArtifactList, adapter.getRelatedArtifact()); + } + + @Test + void adapter_copy() { + var measure = new Measure().setStatus(PublicationStatus.DRAFT); + measure.setId("plan-1"); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(measure); + var copy = (Measure) adapter.copy(); + var adapterCopy = new MeasureAdapter(copy); + adapterCopy.setId(new IdDt("Measure", "plan-2")); + assertNotEquals(measure.getId(), copy.getId()); + measure.setStatus(PublicationStatus.ACTIVE); + assertNotEquals(adapter.getStatus(), copy.getStatus()); + } + + @Test + void adapter_get_all_dependencies() { + var dependencies = List.of( + "profileRef", + "relatedArtifactRef", + "libraryRef", + "populationCriteriaRef", + "stratifierCriteriaRef", + "stratifierComponentCriteriaRef", + "supplementalDataCriteriaRef", + "inputParametersRef", + "expansionParametersRef", + "cqlOptionsRef", + "componentRef"); + var measure = new Measure(); + measure.getMeta().addProfile(dependencies.get(0)); + measure.getRelatedArtifactFirstRep().setResource(dependencies.get(1)); + measure.getLibrary().add(new CanonicalType(dependencies.get(2))); + measure.addGroup().addPopulation().setCriteria(new Expression().setReference(dependencies.get(3))); + measure.addGroup().addStratifier().setCriteria(new Expression().setReference(dependencies.get(4))); + measure.addGroup() + .addStratifier() + .addComponent() + .setCriteria(new Expression().setReference(dependencies.get(5))); + measure.addSupplementalData().setCriteria(new Expression().setReference(dependencies.get(6))); + measure.addExtension(Constants.CQFM_INPUT_PARAMETERS, new Reference(dependencies.get(7))); + measure.addExtension(Constants.CQF_EXPANSION_PARAMETERS, new Reference(dependencies.get(8))); + measure.addExtension(Constants.CQF_CQL_OPTIONS, new Reference(dependencies.get(9))); + measure.addExtension(Constants.CQFM_COMPONENT, new RelatedArtifact().setResource(dependencies.get(10))); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(measure); + var extractedDependencies = adapter.getDependencies(); + assertEquals(extractedDependencies.size(), dependencies.size()); + extractedDependencies.forEach(dep -> { + assertTrue(dependencies.indexOf(dep.getReference()) >= 0); + }); + } + + @Test + void adapter_get_all_dependencies_with_effective_data_requirements() { + var dependencies = List.of( + "libraryProfileRef", + "relatedArtifactRef", + "dataRequirementProfileRef", + "dataRequirementCodeFilterRef", + "measureProfileRef"); + var library = new Library() + .setType(new CodeableConcept(new Coding( + "http://terminology.hl7.org/CodeSystem/library-type", + "module-definition", + "Module Definition"))); + library.setId("test"); + library.getMeta().addProfile(dependencies.get(0)); + library.getRelatedArtifactFirstRep().setResource(dependencies.get(1)); + library.addDataRequirement().addProfile(dependencies.get(2)); + library.addDataRequirement().addCodeFilter().setValueSet(dependencies.get(3)); + var measure = new Measure().addContained(new Patient()).addContained(library); + measure.getMeta().addProfile(dependencies.get(4)); + measure.addExtension(Constants.CQFM_EFFECTIVE_DATA_REQUIREMENTS, new CanonicalType("#test")); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(measure); + var extractedDependencies = adapter.getDependencies(); + assertEquals(dependencies.size(), extractedDependencies.size()); + extractedDependencies.forEach(dep -> { + assertTrue(dependencies.indexOf(dep.getReference()) >= 0); + }); + } +} diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r5/PlanDefinitionAdapterTest.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r5/PlanDefinitionAdapterTest.java index d03a64c4b..84e693783 100644 --- a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r5/PlanDefinitionAdapterTest.java +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r5/PlanDefinitionAdapterTest.java @@ -1,6 +1,8 @@ package org.opencds.cqf.fhir.utility.adapter.r5; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; @@ -8,24 +10,39 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.model.primitive.IdDt; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import java.time.LocalDate; import java.util.Date; import java.util.List; import org.hl7.fhir.instance.model.api.IDomainResource; import org.hl7.fhir.r5.model.Bundle; import org.hl7.fhir.r5.model.CanonicalType; +import org.hl7.fhir.r5.model.Enumerations.PublicationStatus; import org.hl7.fhir.r5.model.Extension; +import org.hl7.fhir.r5.model.Library; +import org.hl7.fhir.r5.model.Period; import org.hl7.fhir.r5.model.PlanDefinition; import org.hl7.fhir.r5.model.RelatedArtifact; import org.junit.jupiter.api.Test; -import org.opencds.cqf.fhir.utility.visitor.KnowledgeArtifactPackageVisitor; +import org.opencds.cqf.fhir.utility.visitor.PackageVisitor; + +class PlanDefinitionAdapterTest { + private final FhirContext fhirContext = FhirContext.forR5Cached(); + private final org.opencds.cqf.fhir.utility.adapter.AdapterFactory adapterFactory = new AdapterFactory(); + + @Test + void invalid_object_fails() { + assertThrows(IllegalArgumentException.class, () -> new PlanDefinitionAdapter(new Library())); + } -public class PlanDefinitionAdapterTest { @Test void adapter_accepts_visitor() { - var spyVisitor = spy(new KnowledgeArtifactPackageVisitor()); + var spyVisitor = spy(new PackageVisitor(fhirContext)); doReturn(new Bundle()).when(spyVisitor).visit(any(PlanDefinitionAdapter.class), any(), any()); IDomainResource planDef = new PlanDefinition(); - var adapter = new PlanDefinitionAdapter(planDef); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(planDef); adapter.accept(spyVisitor, null, null); verify(spyVisitor, times(1)).visit(any(PlanDefinitionAdapter.class), any(), any()); } @@ -35,7 +52,7 @@ void adapter_get_and_set_name() { var planDef = new PlanDefinition(); var name = "name"; planDef.setName(name); - var adapter = new PlanDefinitionAdapter(planDef); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(planDef); assertEquals(name, adapter.getName()); var newName = "name2"; adapter.setName(newName); @@ -47,7 +64,7 @@ void adapter_get_and_set_url() { var planDef = new PlanDefinition(); var url = "www.url.com"; planDef.setUrl(url); - var adapter = new PlanDefinitionAdapter(planDef); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(planDef); assertEquals(url, adapter.getUrl()); var newUrl = "www.url2.com"; adapter.setUrl(newUrl); @@ -55,16 +72,59 @@ void adapter_get_and_set_url() { } @Test - void adapter_get_and_set_approvalDate() { + void adapter_get_and_set_version() { + var planDef = new PlanDefinition(); + var version = "1.0.0"; + planDef.setVersion(version); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(planDef); + assertTrue(adapter.hasVersion()); + assertEquals(version, adapter.getVersion()); + var newVersion = "1.0.1"; + adapter.setVersion(newVersion); + assertEquals(newVersion, planDef.getVersion()); + } + + @Test + void adapter_get_and_set_status() { var planDef = new PlanDefinition(); + var status = PublicationStatus.DRAFT; + planDef.setStatus(status); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(planDef); + assertEquals(status.toCode(), adapter.getStatus()); + assertThrows(UnprocessableEntityException.class, () -> adapter.setStatus("invalid-status")); + var newStatus = PublicationStatus.ACTIVE; + adapter.setStatus(newStatus.toCode()); + assertEquals(newStatus, PublicationStatus.fromCode(adapter.getStatus())); + } + + @Test + void adapter_get_and_set_dates() { + var planDef = new PlanDefinition(); + var date = new Date(); var approvalDate = new Date(); + var effectivePeriod = new Period() + .setStart(java.sql.Date.valueOf(LocalDate.parse("2020-01-01"))) + .setEnd(java.sql.Date.valueOf(LocalDate.parse("2020-12-31"))); + planDef.setDate(date); planDef.setApprovalDate(approvalDate); - var adapter = new PlanDefinitionAdapter(planDef); + planDef.setEffectivePeriod(effectivePeriod); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(planDef); + assertEquals(date, adapter.getDate()); assertEquals(approvalDate, adapter.getApprovalDate()); + assertEquals(effectivePeriod, adapter.getEffectivePeriod()); + var newDate = new Date(); + newDate.setTime(100); + adapter.setDate(newDate); + assertEquals(newDate, planDef.getDate()); var newApprovalDate = new Date(); newApprovalDate.setTime(100); adapter.setApprovalDate(newApprovalDate); assertEquals(newApprovalDate, planDef.getApprovalDate()); + var newEffectivePeriod = new Period() + .setStart(java.sql.Date.valueOf(LocalDate.parse("2021-01-01"))) + .setEnd(java.sql.Date.valueOf(LocalDate.parse("2021-12-31"))); + adapter.setEffectivePeriod(newEffectivePeriod); + assertEquals(newEffectivePeriod, adapter.getEffectivePeriod()); } @Test @@ -72,7 +132,7 @@ void adapter_get_experimental() { var planDef = new PlanDefinition(); var experimental = true; planDef.setExperimental(experimental); - var adapter = new PlanDefinitionAdapter(planDef); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(planDef); assertEquals(experimental, adapter.getExperimental()); } @@ -80,15 +140,29 @@ void adapter_get_experimental() { void adapter_set_relatedArtifact() { var planDef = new PlanDefinition(); var relatedArtifactList = List.of(new RelatedArtifact()); - var adapter = new PlanDefinitionAdapter(planDef); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(planDef); adapter.setRelatedArtifact(relatedArtifactList); assertEquals(relatedArtifactList, planDef.getRelatedArtifact()); assertEquals(relatedArtifactList, adapter.getRelatedArtifact()); } + @Test + void adapter_copy() { + var planDef = new PlanDefinition().setStatus(PublicationStatus.DRAFT); + planDef.setId("plan-1"); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(planDef); + var copy = (PlanDefinition) adapter.copy(); + var adapterCopy = new PlanDefinitionAdapter(copy); + adapterCopy.setId(new IdDt("PlanDefinition", "plan-2")); + assertNotEquals(planDef.getId(), copy.getId()); + planDef.setStatus(PublicationStatus.ACTIVE); + assertNotEquals(adapter.getStatus(), copy.getStatus()); + } + @Test void adapter_get_all_dependencies() { var dependencies = List.of( + "profileRef", "relatedArtifactRef", "libraryRef", "actionTriggerDataReqProfile", @@ -103,31 +177,32 @@ void adapter_get_all_dependencies() { "cpgPartOfExtRef", "nestedActionDefinitionRef"); var planDef = new PlanDefinition(); - planDef.getRelatedArtifactFirstRep().setResource(dependencies.get(0)); - planDef.getLibrary().add(new CanonicalType(dependencies.get(1))); + planDef.getMeta().addProfile(dependencies.get(0)); + planDef.getRelatedArtifactFirstRep().setResource(dependencies.get(1)); + planDef.getLibrary().add(new CanonicalType(dependencies.get(2))); var action = planDef.getActionFirstRep(); action.getTriggerFirstRep() .getDataFirstRep() - .setProfile(List.of(new CanonicalType(dependencies.get(2)))) + .setProfile(List.of(new CanonicalType(dependencies.get(3)))) .getCodeFilterFirstRep() - .setValueSet(dependencies.get(3)); + .setValueSet(dependencies.get(4)); action.getInputFirstRep() .getRequirement() - .setProfile(List.of(new CanonicalType(dependencies.get(4)))) + .setProfile(List.of(new CanonicalType(dependencies.get(5)))) .getCodeFilterFirstRep() - .setValueSet(dependencies.get(5)); + .setValueSet(dependencies.get(6)); action.getOutputFirstRep() .getRequirement() - .setProfile(List.of(new CanonicalType(dependencies.get(6)))) + .setProfile(List.of(new CanonicalType(dependencies.get(7)))) .getCodeFilterFirstRep() - .setValueSet(dependencies.get(7)); - action.setDefinition(new CanonicalType(dependencies.get(8))); - action.getConditionFirstRep().getExpression().setReference(dependencies.get(9)); - action.getDynamicValueFirstRep().getExpression().setReference(dependencies.get(10)); + .setValueSet(dependencies.get(8)); + action.setDefinition(new CanonicalType(dependencies.get(9))); + action.getConditionFirstRep().getExpression().setReference(dependencies.get(10)); + action.getDynamicValueFirstRep().getExpression().setReference(dependencies.get(11)); planDef.addExtension(new Extension( - "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-partOf", new CanonicalType(dependencies.get(11)))); - action.addAction().setDefinition(new CanonicalType(dependencies.get(12))); - var adapter = new PlanDefinitionAdapter(planDef); + "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-partOf", new CanonicalType(dependencies.get(12)))); + action.addAction().setDefinition(new CanonicalType(dependencies.get(13))); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(planDef); var extractedDependencies = adapter.getDependencies(); assertEquals(extractedDependencies.size(), dependencies.size()); extractedDependencies.forEach(dep -> { diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r5/QuestionnaireAdapterTest.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r5/QuestionnaireAdapterTest.java new file mode 100644 index 000000000..c2c2963ec --- /dev/null +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r5/QuestionnaireAdapterTest.java @@ -0,0 +1,227 @@ +package org.opencds.cqf.fhir.utility.adapter.r5; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import java.time.LocalDate; +import java.util.Date; +import java.util.List; +import org.hl7.fhir.instance.model.api.IDomainResource; +import org.hl7.fhir.r5.model.Bundle; +import org.hl7.fhir.r5.model.CanonicalType; +import org.hl7.fhir.r5.model.Enumerations.PublicationStatus; +import org.hl7.fhir.r5.model.Expression; +import org.hl7.fhir.r5.model.Extension; +import org.hl7.fhir.r5.model.Library; +import org.hl7.fhir.r5.model.Period; +import org.hl7.fhir.r5.model.Questionnaire; +import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireItemType; +import org.hl7.fhir.r5.model.RelatedArtifact; +import org.junit.jupiter.api.Test; +import org.opencds.cqf.fhir.utility.Constants; +import org.opencds.cqf.fhir.utility.visitor.PackageVisitor; + +class QuestionnaireAdapterTest { + private final FhirContext fhirContext = FhirContext.forR5Cached(); + private final org.opencds.cqf.fhir.utility.adapter.AdapterFactory adapterFactory = new AdapterFactory(); + + @Test + void invalid_object_fails() { + assertThrows(IllegalArgumentException.class, () -> new QuestionnaireAdapter(new Library())); + } + + @Test + void adapter_accepts_visitor() { + var spyVisitor = spy(new PackageVisitor(fhirContext)); + doReturn(new Bundle()).when(spyVisitor).visit(any(QuestionnaireAdapter.class), any(), any()); + IDomainResource questionnaire = new Questionnaire(); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(questionnaire); + adapter.accept(spyVisitor, null, null); + verify(spyVisitor, times(1)).visit(any(QuestionnaireAdapter.class), any(), any()); + } + + @Test + void adapter_get_and_set_name() { + var questionnaire = new Questionnaire(); + var name = "name"; + questionnaire.setName(name); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(questionnaire); + assertEquals(name, adapter.getName()); + var newName = "name2"; + adapter.setName(newName); + assertEquals(newName, questionnaire.getName()); + } + + @Test + void adapter_get_and_set_url() { + var questionnaire = new Questionnaire(); + var url = "www.url.com"; + questionnaire.setUrl(url); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(questionnaire); + assertEquals(url, adapter.getUrl()); + var newUrl = "www.url2.com"; + adapter.setUrl(newUrl); + assertEquals(newUrl, questionnaire.getUrl()); + } + + @Test + void adapter_get_and_set_version() { + var questionnaire = new Questionnaire(); + var version = "1.0.0"; + questionnaire.setVersion(version); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(questionnaire); + assertTrue(adapter.hasVersion()); + assertEquals(version, adapter.getVersion()); + var newVersion = "1.0.1"; + adapter.setVersion(newVersion); + assertEquals(newVersion, questionnaire.getVersion()); + } + + @Test + void adapter_get_and_set_status() { + var questionnaire = new Questionnaire(); + var status = PublicationStatus.DRAFT; + questionnaire.setStatus(status); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(questionnaire); + assertEquals(status.toCode(), adapter.getStatus()); + assertThrows(UnprocessableEntityException.class, () -> adapter.setStatus("invalid-status")); + var newStatus = PublicationStatus.ACTIVE; + adapter.setStatus(newStatus.toCode()); + assertEquals(newStatus, PublicationStatus.fromCode(adapter.getStatus())); + } + + @Test + void adapter_get_and_set_dates() { + var questionnaire = new Questionnaire(); + var date = new Date(); + var approvalDate = new Date(); + var effectivePeriod = new Period() + .setStart(java.sql.Date.valueOf(LocalDate.parse("2020-01-01"))) + .setEnd(java.sql.Date.valueOf(LocalDate.parse("2020-12-31"))); + questionnaire.setDate(date); + questionnaire.setApprovalDate(approvalDate); + questionnaire.setEffectivePeriod(effectivePeriod); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(questionnaire); + assertEquals(date, adapter.getDate()); + assertEquals(approvalDate, adapter.getApprovalDate()); + assertEquals(effectivePeriod, adapter.getEffectivePeriod()); + var newDate = new Date(); + newDate.setTime(100); + adapter.setDate(newDate); + assertEquals(newDate, questionnaire.getDate()); + var newApprovalDate = new Date(); + newApprovalDate.setTime(100); + adapter.setApprovalDate(newApprovalDate); + assertEquals(newApprovalDate, questionnaire.getApprovalDate()); + var newEffectivePeriod = new Period() + .setStart(java.sql.Date.valueOf(LocalDate.parse("2021-01-01"))) + .setEnd(java.sql.Date.valueOf(LocalDate.parse("2021-12-31"))); + adapter.setEffectivePeriod(newEffectivePeriod); + assertEquals(newEffectivePeriod, adapter.getEffectivePeriod()); + } + + @Test + void adapter_get_experimental() { + var questionnaire = new Questionnaire(); + var experimental = true; + questionnaire.setExperimental(experimental); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(questionnaire); + assertEquals(experimental, adapter.getExperimental()); + } + + @Test + void adapter_set_relatedArtifact() { + var questionnaire = new Questionnaire(); + var relatedArtifactList = List.of(new RelatedArtifact()); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(questionnaire); + adapter.setRelatedArtifact(relatedArtifactList); + assertEquals(0, adapter.getRelatedArtifact().size()); + } + + @Test + void adapter_copy() { + var questionnaire = new Questionnaire().setStatus(PublicationStatus.DRAFT); + questionnaire.setId("question-1"); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(questionnaire); + var copy = (Questionnaire) adapter.copy(); + copy.setId("question-2"); + assertNotEquals(questionnaire.getId(), copy.getId()); + questionnaire.setStatus(PublicationStatus.ACTIVE); + assertNotEquals(adapter.getStatus(), copy.getStatus()); + } + + @Test + void adapter_get_all_dependencies() { + var dependencies = List.of( + "profileRef", + "derivedRef", + "cqfLibraryRef", + "variableRef", + "itemDefinitionRef", + "answerValueSetRef", + // "itemMediaRef", + // "itemAnswerMediaRef", + "unitValueSetRef", + "referenceProfileRef", + "candidateExpressionRef", + "lookupQuestionnaireRef", + "itemVariableRef", + "initialExpressionRef", + "calculatedExpressionRef", + // "calculatedValueRef", + "expressionRef", + "subQuestionnaireRef"); + var questionnaire = new Questionnaire(); + questionnaire.getMeta().addProfile(dependencies.get(0)); + questionnaire.addDerivedFrom(dependencies.get(1)); + questionnaire.addExtension(Constants.CQF_LIBRARY, new CanonicalType(dependencies.get(2))); + var variableExt = new Extension(Constants.VARIABLE_EXTENSION) + .setValue(new Expression().setReference(dependencies.get(3))); + questionnaire.addExtension(variableExt); + questionnaire.addItem().setDefinition(dependencies.get(4) + "#Observation"); + questionnaire.addItem().setAnswerValueSet(dependencies.get(5)); + questionnaire + .addItem() + .setType(QuestionnaireItemType.QUANTITY) + .addExtension(Constants.QUESTIONNAIRE_UNIT_VALUE_SET, new CanonicalType(dependencies.get(6))); + questionnaire + .addItem() + .addExtension(Constants.QUESTIONNAIRE_REFERENCE_PROFILE, new CanonicalType(dependencies.get(7))); + var candidateExpressionExt = new Extension(Constants.SDC_QUESTIONNAIRE_CANDIDATE_EXPRESSION) + .setValue(new Expression().setReference(dependencies.get(8))); + questionnaire.addItem().addExtension(candidateExpressionExt); + var lookupQuestionnaireExt = new Extension(Constants.SDC_QUESTIONNAIRE_LOOKUP_QUESTIONNAIRE) + .setValue(new CanonicalType(dependencies.get(9))); + questionnaire.addItem().addExtension(lookupQuestionnaireExt); + var itemVariableExt = new Extension(Constants.VARIABLE_EXTENSION) + .setValue(new Expression().setReference(dependencies.get(10))); + questionnaire.addItem().addExtension(itemVariableExt); + var initialExpressionExt = new Extension(Constants.SDC_QUESTIONNAIRE_INITIAL_EXPRESSION) + .setValue(new Expression().setReference(dependencies.get(11))); + questionnaire.addItem().addExtension(initialExpressionExt); + var calculatedExpressionExt = new Extension(Constants.SDC_QUESTIONNAIRE_CALCULATED_EXPRESSION) + .setValue(new Expression().setReference(dependencies.get(12))); + questionnaire.addItem().addExtension(calculatedExpressionExt); + var expressionExt = + new Extension(Constants.CQF_EXPRESSION).setValue(new Expression().setReference(dependencies.get(13))); + questionnaire.addItem().addExtension(expressionExt); + questionnaire + .addItem() + .addExtension(Constants.SDC_QUESTIONNAIRE_SUB_QUESTIONNAIRE, new CanonicalType(dependencies.get(14))); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(questionnaire); + var extractedDependencies = adapter.getDependencies(); + assertEquals(dependencies.size(), extractedDependencies.size()); + extractedDependencies.forEach(dep -> { + assertTrue(dependencies.indexOf(dep.getReference()) >= 0); + }); + } +} diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r5/ResourceAdapterTest.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r5/ResourceAdapterTest.java new file mode 100644 index 000000000..3ec0e260f --- /dev/null +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r5/ResourceAdapterTest.java @@ -0,0 +1,118 @@ +package org.opencds.cqf.fhir.utility.adapter.r5; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import ca.uhn.fhir.model.primitive.IdDt; +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.read.ListAppender; +import java.util.Date; +import java.util.List; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.r5.model.BooleanType; +import org.hl7.fhir.r5.model.Bundle; +import org.hl7.fhir.r5.model.Extension; +import org.hl7.fhir.r5.model.HumanName; +import org.hl7.fhir.r5.model.IdType; +import org.hl7.fhir.r5.model.Library; +import org.hl7.fhir.r5.model.Meta; +import org.hl7.fhir.r5.model.Patient; +import org.hl7.fhir.r5.model.PlanDefinition; +import org.junit.jupiter.api.Test; +import org.opencds.cqf.fhir.utility.adapter.Adapter; +import org.slf4j.LoggerFactory; + +class ResourceAdapterTest { + private final org.opencds.cqf.fhir.utility.adapter.AdapterFactory adapterFactory = new AdapterFactory(); + + @Test + void invalid_object_fails() { + assertThrows(IllegalArgumentException.class, () -> new ResourceAdapter(null)); + assertThrows(IllegalArgumentException.class, () -> new ResourceAdapter(new org.hl7.fhir.r4.model.Library())); + } + + @Test + void adapter_get_and_set_property() { + var resource = new Patient(); + var id = new IdDt("patient-1"); + resource.setId(id); + var adapter = adapterFactory.createResource(resource); + assertTrue(((ResourceAdapter) adapter).isDomainResource()); + assertEquals(resource, ((ResourceAdapter) adapter).getDomainResource().get()); + assertEquals(id.getValue(), ((IIdType) adapter.getSingleProperty("id")).getValue()); + var newId = new IdType("patient-2"); + adapter.setProperty("id", newId); + assertEquals(newId, resource.getIdElement()); + assertEquals("id", adapter.getTypesForProperty("id")[0]); + assertNotNull(adapter.makeProperty("language")); + assertNull(adapter.getSingleProperty("meta")); + var meta = (Meta) adapter.addChild("meta"); + var date = new Date(); + meta.setLastUpdated(date); + assertEquals(date, ((Meta) adapter.getProperty("meta")[0]).getLastUpdated()); + resource.addName(new HumanName().addGiven("name1")); + resource.addName(new HumanName().addGiven("name2")); + assertThrows(IllegalArgumentException.class, () -> adapter.getSingleProperty("name")); + } + + @Test + void adapter_copy() { + var resource = new Patient(); + resource.setId("patient-1"); + resource.setMeta(new Meta().setLastUpdated(new Date())); + var adapter = adapterFactory.createResource(resource); + var copy = (Patient) adapter.copy(); + assertTrue(adapter.equalsDeep(copy)); + var newDate = new Date(); + newDate.setTime(100); + copy.setMeta(new Meta().setLastUpdated(newDate)); + assertFalse(adapter.equalsDeep(copy)); + assertTrue(adapter.equalsShallow(copy)); + copy.setId("patient-2"); + assertFalse(adapter.equalsShallow(copy)); + resource.setLanguage("FR"); + adapter.copyValues(copy); + assertEquals("FR", copy.getLanguage()); + } + + @Test + void adapter_get_and_set_extension() { + var logger = (Logger) LoggerFactory.getLogger(Adapter.class); + var listAppender = new ListAppender(); + listAppender.start(); + logger.addAppender(listAppender); + var bundle = new Bundle(); + var bundleAdapter = adapterFactory.createResource(bundle); + assertFalse(bundleAdapter.hasExtension()); + bundleAdapter.setExtension(List.of(new Extension())); + assertEquals(1, listAppender.list.size()); + assertEquals(Level.DEBUG, listAppender.list.get(0).getLevel()); + bundleAdapter.addExtension(new Extension()); + assertEquals(2, listAppender.list.size()); + assertEquals(Level.DEBUG, listAppender.list.get(1).getLevel()); + var resource = new Patient(); + var extensionList = List.of(new Extension().setUrl("test-extension-url").setValue(new BooleanType(true))); + var adapter = adapterFactory.createResource(resource); + adapter.setExtension(extensionList); + assertTrue(adapter.hasExtension()); + assertEquals(extensionList, resource.getExtension()); + assertEquals(extensionList, adapter.getExtension()); + assertTrue(adapter.hasExtension("test-extension-url")); + } + + @Test + void adapter_get_contained() { + var resource = new PlanDefinition(); + resource.addContained(new Library()); + var adapter = adapterFactory.createResource(resource); + assertTrue(adapter.hasContained()); + assertNotNull(adapter.getContained()); + assertFalse(adapter.hasContained(adapter.getContained().get(0))); + } +} diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r5/StructureDefinitionAdapterTest.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r5/StructureDefinitionAdapterTest.java new file mode 100644 index 000000000..9fa1e3c6e --- /dev/null +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r5/StructureDefinitionAdapterTest.java @@ -0,0 +1,206 @@ +package org.opencds.cqf.fhir.utility.adapter.r5; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import java.time.LocalDate; +import java.util.Date; +import java.util.List; +import org.hl7.fhir.instance.model.api.IDomainResource; +import org.hl7.fhir.r5.model.Bundle; +import org.hl7.fhir.r5.model.DateTimeType; +import org.hl7.fhir.r5.model.DateType; +import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent; +import org.hl7.fhir.r5.model.Enumerations.PublicationStatus; +import org.hl7.fhir.r5.model.Expression; +import org.hl7.fhir.r5.model.Library; +import org.hl7.fhir.r5.model.Period; +import org.hl7.fhir.r5.model.RelatedArtifact; +import org.hl7.fhir.r5.model.RelatedArtifact.RelatedArtifactType; +import org.hl7.fhir.r5.model.StructureDefinition; +import org.junit.jupiter.api.Test; +import org.opencds.cqf.fhir.utility.Constants; +import org.opencds.cqf.fhir.utility.visitor.PackageVisitor; + +class StructureDefinitionAdapterTest { + private final FhirContext fhirContext = FhirContext.forR5Cached(); + private final org.opencds.cqf.fhir.utility.adapter.AdapterFactory adapterFactory = new AdapterFactory(); + + @Test + void invalid_object_fails() { + assertThrows(IllegalArgumentException.class, () -> new StructureDefinitionAdapter(new Library())); + assertNotNull(new StructureDefinitionAdapter((IDomainResource) new StructureDefinition())); + } + + @Test + void adapter_accepts_visitor() { + var spyVisitor = spy(new PackageVisitor(fhirContext)); + doReturn(new Bundle()).when(spyVisitor).visit(any(StructureDefinitionAdapter.class), any(), any()); + IDomainResource structureDef = new StructureDefinition(); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(structureDef); + assertEquals(structureDef, adapter.get()); + adapter.accept(spyVisitor, null, null); + verify(spyVisitor, times(1)).visit(any(StructureDefinitionAdapter.class), any(), any()); + } + + @Test + void adapter_get_and_set_name() { + var structureDef = new StructureDefinition(); + var name = "name"; + structureDef.setName(name); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(structureDef); + assertEquals(name, adapter.getName()); + var newName = "name2"; + adapter.setName(newName); + assertEquals(newName, structureDef.getName()); + } + + @Test + void adapter_get_and_set_url() { + var structureDef = new StructureDefinition(); + var url = "www.url.com"; + structureDef.setUrl(url); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(structureDef); + assertTrue(adapter.hasUrl()); + assertEquals(url, adapter.getUrl()); + var newUrl = "www.url2.com"; + adapter.setUrl(newUrl); + assertTrue(adapter.hasUrl()); + assertEquals(newUrl, structureDef.getUrl()); + } + + @Test + void adapter_get_and_set_version() { + var structureDef = new StructureDefinition(); + var version = "1.0.0"; + structureDef.setVersion(version); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(structureDef); + assertTrue(adapter.hasVersion()); + assertEquals(version, adapter.getVersion()); + var newVersion = "1.0.1"; + adapter.setVersion(newVersion); + assertEquals(newVersion, structureDef.getVersion()); + } + + @Test + void adapter_get_and_set_status() { + var structureDef = new StructureDefinition(); + var status = PublicationStatus.DRAFT; + structureDef.setStatus(status); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(structureDef); + assertEquals(status.toCode(), adapter.getStatus()); + assertThrows(UnprocessableEntityException.class, () -> adapter.setStatus("invalid-status")); + var newStatus = PublicationStatus.ACTIVE; + adapter.setStatus(newStatus.toCode()); + assertEquals(newStatus, PublicationStatus.fromCode(adapter.getStatus())); + } + + @Test + void adapter_get_and_set_dates() { + // StructureDefinition does not have fields approvalDate and effectivePeriod + var structureDef = new StructureDefinition(); + var date = new Date(); + var effectivePeriod = new Period() + .setStart(java.sql.Date.valueOf(LocalDate.parse("2020-01-01"))) + .setEnd(java.sql.Date.valueOf(LocalDate.parse("2020-12-31"))); + structureDef.setDate(date); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(structureDef); + assertEquals(date, adapter.getDate()); + assertEquals(null, adapter.getApprovalDate()); + assertNotEquals(effectivePeriod, adapter.getEffectivePeriod()); + var newDate = new Date(); + newDate.setTime(100); + adapter.setDate(newDate); + assertEquals(newDate, structureDef.getDate()); + assertThrows(UnprocessableEntityException.class, () -> adapter.setDateElement(new DateType())); + var newDateElement = new DateTimeType().setValue(new Date()); + adapter.setDateElement(newDateElement); + assertEquals(newDateElement, structureDef.getDateElement()); + var newApprovalDate = new Date(); + newApprovalDate.setTime(100); + adapter.setApprovalDate(newApprovalDate); + assertEquals(null, adapter.getApprovalDate()); + var newEffectivePeriod = new Period() + .setStart(java.sql.Date.valueOf(LocalDate.parse("2021-01-01"))) + .setEnd(java.sql.Date.valueOf(LocalDate.parse("2021-12-31"))); + adapter.setEffectivePeriod(newEffectivePeriod); + assertNotEquals(newEffectivePeriod, adapter.getEffectivePeriod()); + } + + @Test + void adapter_get_experimental() { + var structureDef = new StructureDefinition(); + var experimental = true; + structureDef.setExperimental(experimental); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(structureDef); + assertEquals(experimental, adapter.getExperimental()); + } + + @Test + void adapter_set_relatedArtifact() { + var structureDef = new StructureDefinition(); + var relatedArtifactList = List.of(new RelatedArtifact().setType(RelatedArtifactType.DEPENDSON)); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(structureDef); + assertFalse(adapter.hasRelatedArtifact()); + adapter.setRelatedArtifact(relatedArtifactList); + assertEquals(0, adapter.getRelatedArtifact().size()); + assertEquals(0, adapter.getRelatedArtifactsOfType("depends-on").size()); + } + + @Test + void adapter_copy() { + var structureDef = new StructureDefinition(); + structureDef.setId("plan-1"); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(structureDef); + var copy = (StructureDefinition) adapter.copy(); + copy.setId("plan-2"); + assertNotEquals(structureDef.getId(), copy.getId()); + assertEquals(copy.getStatus(), PublicationStatus.fromCode(adapter.getStatus())); + structureDef.setStatus(PublicationStatus.ACTIVE); + assertNotEquals(PublicationStatus.fromCode(adapter.getStatus()), copy.getStatus()); + } + + @Test + void adapter_get_all_dependencies() { + var dependencies = List.of( + "profileRef", + "baseDefinition", + "cpgAssertionRef", + "cpgFeatureRef", + "cpgInferenceRef", + "elementProfileRef", + "elementTargetProfileRef", + "elementValueSetBindingRef"); + var structureDef = new StructureDefinition(); + structureDef.getMeta().addProfile(dependencies.get(0)); + structureDef.setBaseDefinition(dependencies.get(1)); + structureDef.addExtension( + Constants.CPG_ASSERTION_EXPRESSION, new Expression().setReference(dependencies.get(2))); + structureDef.addExtension(Constants.CPG_FEATURE_EXPRESSION, new Expression().setReference(dependencies.get(3))); + structureDef.addExtension( + Constants.CPG_INFERENCE_EXPRESSION, new Expression().setReference(dependencies.get(4))); + structureDef.getDifferential().addElement().addType().addProfile(dependencies.get(5)); + structureDef.getDifferential().addElement().addType().addTargetProfile(dependencies.get(6)); + structureDef + .getDifferential() + .addElement() + .setBinding(new ElementDefinitionBindingComponent().setValueSet(dependencies.get(7))); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(structureDef); + var extractedDependencies = adapter.getDependencies(); + assertEquals(dependencies.size(), extractedDependencies.size()); + extractedDependencies.forEach(dep -> { + assertTrue(dependencies.indexOf(dep.getReference()) >= 0); + }); + } +} diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r5/ValueSetAdapterTest.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r5/ValueSetAdapterTest.java new file mode 100644 index 000000000..8e5b8efb2 --- /dev/null +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/adapter/r5/ValueSetAdapterTest.java @@ -0,0 +1,173 @@ +package org.opencds.cqf.fhir.utility.adapter.r5; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import java.time.LocalDate; +import java.util.Date; +import java.util.List; +import org.hl7.fhir.instance.model.api.IDomainResource; +import org.hl7.fhir.r5.model.Bundle; +import org.hl7.fhir.r5.model.Enumerations.PublicationStatus; +import org.hl7.fhir.r5.model.Library; +import org.hl7.fhir.r5.model.Period; +import org.hl7.fhir.r5.model.RelatedArtifact; +import org.hl7.fhir.r5.model.ValueSet; +import org.junit.jupiter.api.Test; +import org.opencds.cqf.fhir.utility.visitor.PackageVisitor; + +class ValueSetAdapterTest { + private final FhirContext fhirContext = FhirContext.forR5Cached(); + private final org.opencds.cqf.fhir.utility.adapter.AdapterFactory adapterFactory = new AdapterFactory(); + + @Test + void invalid_object_fails() { + assertThrows(IllegalArgumentException.class, () -> new ValueSetAdapter(new Library())); + } + + @Test + void adapter_accepts_visitor() { + var spyVisitor = spy(new PackageVisitor(fhirContext)); + doReturn(new Bundle()).when(spyVisitor).visit(any(ValueSetAdapter.class), any(), any()); + IDomainResource valueSet = new ValueSet(); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(valueSet); + assertEquals(valueSet, adapter.get()); + adapter.accept(spyVisitor, null, null); + verify(spyVisitor, times(1)).visit(any(ValueSetAdapter.class), any(), any()); + } + + @Test + void adapter_get_and_set_name() { + var valueSet = new ValueSet(); + var name = "name"; + valueSet.setName(name); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(valueSet); + assertEquals(name, adapter.getName()); + var newName = "name2"; + adapter.setName(newName); + assertEquals(newName, valueSet.getName()); + } + + @Test + void adapter_get_and_set_url() { + var valueSet = new ValueSet(); + var url = "www.url.com"; + valueSet.setUrl(url); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(valueSet); + assertTrue(adapter.hasUrl()); + assertEquals(url, adapter.getUrl()); + var newUrl = "www.url2.com"; + adapter.setUrl(newUrl); + assertTrue(adapter.hasUrl()); + assertEquals(newUrl, valueSet.getUrl()); + } + + @Test + void adapter_get_and_set_version() { + var valueSet = new ValueSet(); + var version = "1.0.0"; + valueSet.setVersion(version); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(valueSet); + assertTrue(adapter.hasVersion()); + assertEquals(version, adapter.getVersion()); + var newVersion = "1.0.1"; + adapter.setVersion(newVersion); + assertEquals(newVersion, valueSet.getVersion()); + } + + @Test + void adapter_get_and_set_status() { + var valueSet = new ValueSet(); + var status = PublicationStatus.DRAFT; + valueSet.setStatus(status); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(valueSet); + assertEquals(status.toCode(), adapter.getStatus()); + assertThrows(UnprocessableEntityException.class, () -> adapter.setStatus("invalid-status")); + var newStatus = PublicationStatus.ACTIVE; + adapter.setStatus(newStatus.toCode()); + assertEquals(newStatus, PublicationStatus.fromCode(adapter.getStatus())); + } + + @Test + void adapter_get_and_set_dates() { + var valueSet = new ValueSet(); + var date = new Date(); + var approvalDate = new Date(); + var effectivePeriod = new Period() + .setStart(java.sql.Date.valueOf(LocalDate.parse("2020-01-01"))) + .setEnd(java.sql.Date.valueOf(LocalDate.parse("2020-12-31"))); + valueSet.setDate(date); + valueSet.setApprovalDate(approvalDate); + valueSet.setEffectivePeriod(effectivePeriod); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(valueSet); + assertEquals(date, adapter.getDate()); + assertEquals(approvalDate, adapter.getApprovalDate()); + assertEquals(effectivePeriod, adapter.getEffectivePeriod()); + var newDate = new Date(); + newDate.setTime(100); + adapter.setDate(newDate); + assertEquals(newDate, valueSet.getDate()); + var newApprovalDate = new Date(); + newApprovalDate.setTime(100); + adapter.setApprovalDate(newApprovalDate); + assertEquals(newApprovalDate, valueSet.getApprovalDate()); + var newEffectivePeriod = new Period() + .setStart(java.sql.Date.valueOf(LocalDate.parse("2021-01-01"))) + .setEnd(java.sql.Date.valueOf(LocalDate.parse("2021-12-31"))); + adapter.setEffectivePeriod(newEffectivePeriod); + assertEquals(newEffectivePeriod, valueSet.getEffectivePeriod()); + } + + @Test + void adapter_get_experimental() { + var valueSet = new ValueSet(); + var experimental = true; + valueSet.setExperimental(experimental); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(valueSet); + assertEquals(experimental, adapter.getExperimental()); + } + + @Test + void adapter_set_relatedArtifact() { + var valueSet = new ValueSet(); + var relatedArtifactList = List.of(new RelatedArtifact()); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(valueSet); + adapter.setRelatedArtifact(relatedArtifactList); + assertEquals(relatedArtifactList, valueSet.getRelatedArtifact()); + assertEquals(relatedArtifactList, adapter.getRelatedArtifact()); + } + + @Test + void adapter_copy() { + var valueSet = new ValueSet().setStatus(PublicationStatus.DRAFT); + valueSet.setId("valueset-1"); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(valueSet); + var copy = (ValueSet) adapter.copy(); + copy.setId("valueset-2"); + assertNotEquals(valueSet.getId(), copy.getId()); + valueSet.setStatus(PublicationStatus.ACTIVE); + assertNotEquals(adapter.getStatus(), copy.getStatus()); + } + + @Test + void adapter_get_all_dependencies() { + var dependencies = List.of("profileRef"); + var valueSet = new ValueSet(); + valueSet.getMeta().addProfile(dependencies.get(0)); + var adapter = adapterFactory.createKnowledgeArtifactAdapter(valueSet); + var extractedDependencies = adapter.getDependencies(); + assertEquals(dependencies.size(), extractedDependencies.size()); + extractedDependencies.forEach(dep -> { + assertTrue(dependencies.indexOf(dep.getReference()) >= 0); + }); + } +} diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/client/TerminologyServerClientTest.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/client/TerminologyServerClientTest.java index b1458a574..702990a68 100644 --- a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/client/TerminologyServerClientTest.java +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/client/TerminologyServerClientTest.java @@ -6,7 +6,9 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import ca.uhn.fhir.context.FhirContext; @@ -16,6 +18,9 @@ import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs; +import org.opencds.cqf.fhir.utility.Constants; +import org.opencds.cqf.fhir.utility.adapter.AdapterFactory; +import org.opencds.cqf.fhir.utility.adapter.ValueSetAdapter; public class TerminologyServerClientTest { private static final String url = "www.test.com"; @@ -23,13 +28,25 @@ public class TerminologyServerClientTest { private static final String authoritativeSource = "www.source.com/ValueSet"; private static final String username = "username"; private static final String password = "password"; - private static final String urlParamName = "url"; - private static final String versionParamName = "valueSetVersion"; + private static final String urlParamName = TerminologyServerClient.urlParamName; + private static final String versionParamName = TerminologyServerClient.versionParamName; + ; + + private FhirContext fhirContextDstu3 = FhirContext.forDstu3Cached(); + private FhirContext fhirContextR4 = FhirContext.forR4Cached(); + private FhirContext fhirContextR5 = FhirContext.forR5Cached(); @Test void testR4UrlAndVersion() { - var vs = new org.hl7.fhir.r4.model.ValueSet(); - vs.setUrl(url); + var factory = AdapterFactory.forFhirVersion(FhirVersionEnum.R4); + var valueSet = (ValueSetAdapter) factory.createKnowledgeArtifactAdapter(new org.hl7.fhir.r4.model.ValueSet()); + valueSet.setUrl(url); + var endpoint = factory.createEndpoint(new org.hl7.fhir.r4.model.Endpoint()); + endpoint.setAddress(authoritativeSource); + endpoint.addExtension(new org.hl7.fhir.r4.model.Extension( + Constants.VSAC_USERNAME, new org.hl7.fhir.r4.model.StringType(username))); + endpoint.addExtension( + new org.hl7.fhir.r4.model.Extension(Constants.APIKEY, new org.hl7.fhir.r4.model.StringType(password))); var capt = ArgumentCaptor.forClass(org.hl7.fhir.r4.model.Parameters.class); var clientMock = mock(GenericClient.class, new ReturnsDeepStubs()); var contextMock = mock(FhirContext.class, new ReturnsDeepStubs()); @@ -43,10 +60,13 @@ void testR4UrlAndVersion() { .withParameters(capt.capture()) .returnResourceType(any()) .execute()) - .thenReturn(vs); + .thenReturn(valueSet.get()); + var contextSpy = spy(fhirContextR4); + doReturn(clientMock).when(contextSpy).newRestfulGenericClient(any()); - var client = new TerminologyServerClient(contextMock); - client.expand(vs, authoritativeSource, new org.hl7.fhir.r4.model.Parameters(), username, password); + var client = new TerminologyServerClient(contextSpy); + var parameters = factory.createParameters(new org.hl7.fhir.r4.model.Parameters()); + client.expand(valueSet, endpoint, parameters); assertNotNull(capt.getValue()); var params = capt.getValue(); assertEquals( @@ -57,20 +77,40 @@ void testR4UrlAndVersion() { assertNull(params.getParameter(versionParamName)); // when the valueset has a version it should be in params - vs.setVersion(version); - client.expand(vs, authoritativeSource, new org.hl7.fhir.r4.model.Parameters(), username, password); + valueSet.setVersion(version); + client.expand(valueSet, endpoint, parameters); params = capt.getAllValues().get(1); assertEquals( version, ((org.hl7.fhir.r4.model.StringType) params.getParameter(versionParamName).getValue()) .getValue()); + + // if url is provided we don't need to pass in a ValueSet + var urlAndVersionParams = factory.createParameters(new org.hl7.fhir.r4.model.Parameters()); + urlAndVersionParams.addParameter(urlParamName, new org.hl7.fhir.r4.model.UrlType(url)); + urlAndVersionParams.addParameter(versionParamName, new org.hl7.fhir.r4.model.StringType(version)); + Exception noException = null; + try { + var expanded = client.expand(endpoint, urlAndVersionParams, FhirVersionEnum.R4); + assertEquals(expanded.getClass(), valueSet.get().getClass()); + } catch (Exception e) { + noException = e; + } + assertNull(noException); } @Test void testR5UrlAndVersion() { - var vs = new org.hl7.fhir.r5.model.ValueSet(); - vs.setUrl(url); + var factory = AdapterFactory.forFhirVersion(FhirVersionEnum.R5); + var valueSet = (ValueSetAdapter) factory.createKnowledgeArtifactAdapter(new org.hl7.fhir.r5.model.ValueSet()); + valueSet.setUrl(url); + var endpoint = factory.createEndpoint(new org.hl7.fhir.r5.model.Endpoint()); + endpoint.setAddress(authoritativeSource); + endpoint.addExtension(new org.hl7.fhir.r5.model.Extension( + Constants.VSAC_USERNAME, new org.hl7.fhir.r5.model.StringType(username))); + endpoint.addExtension( + new org.hl7.fhir.r5.model.Extension(Constants.APIKEY, new org.hl7.fhir.r5.model.StringType(password))); var capt = ArgumentCaptor.forClass(org.hl7.fhir.r5.model.Parameters.class); var clientMock = mock(GenericClient.class, new ReturnsDeepStubs()); var contextMock = mock(FhirContext.class, new ReturnsDeepStubs()); @@ -83,10 +123,13 @@ void testR5UrlAndVersion() { .withParameters(capt.capture()) .returnResourceType(any()) .execute()) - .thenReturn(vs); + .thenReturn(valueSet.get()); + var contextSpy = spy(fhirContextR5); + doReturn(clientMock).when(contextSpy).newRestfulGenericClient(any()); - var client = new TerminologyServerClient(contextMock); - client.expand(vs, authoritativeSource, new org.hl7.fhir.r5.model.Parameters(), username, password); + var client = new TerminologyServerClient(contextSpy); + var parameters = factory.createParameters(new org.hl7.fhir.r5.model.Parameters()); + client.expand(valueSet, endpoint, parameters); assertNotNull(capt.getValue()); var params = capt.getValue(); assertEquals( @@ -97,20 +140,41 @@ void testR5UrlAndVersion() { assertNull(params.getParameter(versionParamName)); // when the valueset has a version it should be in params - vs.setVersion(version); - client.expand(vs, authoritativeSource, new org.hl7.fhir.r5.model.Parameters(), username, password); + valueSet.setVersion(version); + client.expand(valueSet, endpoint, parameters); params = capt.getAllValues().get(1); assertEquals( version, ((org.hl7.fhir.r5.model.StringType) params.getParameter(versionParamName).getValue()) .getValue()); + + // if url is provided we don't need to pass in a ValueSet + var urlAndVersionParams = factory.createParameters(new org.hl7.fhir.r5.model.Parameters()); + urlAndVersionParams.addParameter(urlParamName, new org.hl7.fhir.r5.model.UrlType(url)); + urlAndVersionParams.addParameter(versionParamName, new org.hl7.fhir.r5.model.StringType(version)); + Exception noException = null; + try { + var expanded = client.expand(endpoint, urlAndVersionParams, FhirVersionEnum.R5); + assertEquals(expanded.getClass(), valueSet.get().getClass()); + } catch (Exception e) { + noException = e; + } + assertNull(noException); } @Test void testDstu3UrlAndVersion() { - var vs = new org.hl7.fhir.dstu3.model.ValueSet(); - vs.setUrl(url); + var factory = AdapterFactory.forFhirVersion(FhirVersionEnum.DSTU3); + var valueSet = + (ValueSetAdapter) factory.createKnowledgeArtifactAdapter(new org.hl7.fhir.dstu3.model.ValueSet()); + valueSet.setUrl(url); + var endpoint = factory.createEndpoint(new org.hl7.fhir.dstu3.model.Endpoint()); + endpoint.setAddress(authoritativeSource); + endpoint.addExtension(new org.hl7.fhir.dstu3.model.Extension( + Constants.VSAC_USERNAME, new org.hl7.fhir.dstu3.model.StringType(username))); + endpoint.addExtension(new org.hl7.fhir.dstu3.model.Extension( + Constants.APIKEY, new org.hl7.fhir.dstu3.model.StringType(password))); var capt = ArgumentCaptor.forClass(org.hl7.fhir.dstu3.model.Parameters.class); var clientMock = mock(GenericClient.class, new ReturnsDeepStubs()); var contextMock = mock(FhirContext.class, new ReturnsDeepStubs()); @@ -123,10 +187,13 @@ void testDstu3UrlAndVersion() { .withParameters(capt.capture()) .returnResourceType(any()) .execute()) - .thenReturn(vs); + .thenReturn(valueSet.get()); + var contextSpy = spy(fhirContextDstu3); + doReturn(clientMock).when(contextSpy).newRestfulGenericClient(any()); - var client = new TerminologyServerClient(contextMock); - client.expand(vs, authoritativeSource, new org.hl7.fhir.dstu3.model.Parameters(), username, password); + var client = new TerminologyServerClient(contextSpy); + var parameters = factory.createParameters(new org.hl7.fhir.dstu3.model.Parameters()); + client.expand(valueSet, endpoint, parameters); assertNotNull(capt.getValue()); var params = capt.getValue(); assertEquals( @@ -140,8 +207,8 @@ void testDstu3UrlAndVersion() { assertTrue(!params.getParameter().stream().anyMatch(p -> p.getName().equals(versionParamName))); // when the valueset has a version it should be in params - vs.setVersion(version); - client.expand(vs, authoritativeSource, new org.hl7.fhir.dstu3.model.Parameters(), username, password); + valueSet.setVersion(version); + client.expand(valueSet, endpoint, parameters); params = capt.getAllValues().get(1); assertEquals( version, @@ -151,27 +218,37 @@ void testDstu3UrlAndVersion() { .orElseThrow() .getValue()) .getValue()); + + // if url is provided we don't need to pass in a ValueSet + var urlAndVersionParams = factory.createParameters(new org.hl7.fhir.dstu3.model.Parameters()); + urlAndVersionParams.addParameter(urlParamName, new org.hl7.fhir.dstu3.model.UriType(url)); + urlAndVersionParams.addParameter(versionParamName, new org.hl7.fhir.dstu3.model.StringType(version)); + Exception noException = null; + try { + var expanded = client.expand(endpoint, urlAndVersionParams, FhirVersionEnum.DSTU3); + assertEquals(expanded.getClass(), valueSet.get().getClass()); + } catch (Exception e) { + noException = e; + } + assertNull(noException); } @Test - void authoritativeSourceUrlParsing() { + void addressUrlParsing() { var supportedVersions = Arrays.asList(FhirVersionEnum.DSTU3, FhirVersionEnum.R4, FhirVersionEnum.R5); for (final var version : supportedVersions) { var ts = new TerminologyServerClient(new FhirContext(version)); var theCorrectBaseServerUrl = "https://cts.nlm.nih.gov/fhir"; // remove the FHIR type and the ID if included - assertEquals( - theCorrectBaseServerUrl, ts.getAuthoritativeSourceBase(theCorrectBaseServerUrl + "/ValueSet/1")); + assertEquals(theCorrectBaseServerUrl, ts.getAddressBase(theCorrectBaseServerUrl + "/ValueSet/1")); // remove a FHIR type if one was included - assertEquals(theCorrectBaseServerUrl, ts.getAuthoritativeSourceBase(theCorrectBaseServerUrl + "/ValueSet")); + assertEquals(theCorrectBaseServerUrl, ts.getAddressBase(theCorrectBaseServerUrl + "/ValueSet")); // don't break on the actual base url - assertEquals(theCorrectBaseServerUrl, ts.getAuthoritativeSourceBase(theCorrectBaseServerUrl)); + assertEquals(theCorrectBaseServerUrl, ts.getAddressBase(theCorrectBaseServerUrl)); // ensure it's forcing https - assertEquals( - theCorrectBaseServerUrl, - ts.getAuthoritativeSourceBase(theCorrectBaseServerUrl.replace("https", "http"))); + assertEquals(theCorrectBaseServerUrl, ts.getAddressBase(theCorrectBaseServerUrl.replace("https", "http"))); // remove trailing slashes - assertEquals(theCorrectBaseServerUrl, ts.getAuthoritativeSourceBase(theCorrectBaseServerUrl + "/")); + assertEquals(theCorrectBaseServerUrl, ts.getAddressBase(theCorrectBaseServerUrl + "/")); } } } diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/dstu3/KnowledgeArtifactApproveVisitorTest.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/dstu3/ApproveVisitorTest.java similarity index 91% rename from cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/dstu3/KnowledgeArtifactApproveVisitorTest.java rename to cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/dstu3/ApproveVisitorTest.java index 8a99686be..9aad392e9 100644 --- a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/dstu3/KnowledgeArtifactApproveVisitorTest.java +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/dstu3/ApproveVisitorTest.java @@ -37,17 +37,17 @@ import org.opencds.cqf.fhir.utility.adapter.dstu3.AdapterFactory; import org.opencds.cqf.fhir.utility.dstu3.ArtifactAssessment; import org.opencds.cqf.fhir.utility.repository.InMemoryFhirRepository; -import org.opencds.cqf.fhir.utility.visitor.KnowledgeArtifactApproveVisitor; +import org.opencds.cqf.fhir.utility.visitor.ApproveVisitor; -class KnowledgeArtifactApproveVisitorTest { +class ApproveVisitorTest { private final FhirContext fhirContext = FhirContext.forDstu3Cached(); private Repository spyRepository; private final IParser jsonParser = fhirContext.newJsonParser(); @BeforeEach void setup() { - var lib = (Library) jsonParser.parseResource( - KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream("Library-ersd-active.json")); + var lib = (Library) + jsonParser.parseResource(ReleaseVisitorTests.class.getResourceAsStream("Library-ersd-active.json")); spyRepository = spy(new InMemoryFhirRepository(fhirContext)); spyRepository.update(lib); doAnswer(new Answer() { @@ -66,7 +66,7 @@ void approveOperation_endpoint_id_should_match_target_parameter() { var artifactAssessmentTarget = "Library/This-Library-Does-Not-Exist|1.0.0"; var params = parameters(part("artifactAssessmentTarget", new UriType(artifactAssessmentTarget))); UnprocessableEntityException maybeException = null; - var releaseVisitor = new KnowledgeArtifactApproveVisitor(); + var releaseVisitor = new ApproveVisitor(); var lib = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -96,7 +96,7 @@ void approveOperation_should_respect_artifactAssessment_information_type_binding String artifactAssessmentType = "this-type-does-not-exist"; Parameters params = parameters(part("artifactAssessmentType", new CodeType(artifactAssessmentType))); UnprocessableEntityException maybeException = null; - KnowledgeArtifactApproveVisitor releaseVisitor = new KnowledgeArtifactApproveVisitor(); + ApproveVisitor releaseVisitor = new ApproveVisitor(); Library lib = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -112,8 +112,8 @@ void approveOperation_should_respect_artifactAssessment_information_type_binding @Test void approveOperation_test() { - var practitioner = (Practitioner) jsonParser.parseResource( - KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream("Practitioner-minimal.json")); + var practitioner = (Practitioner) + jsonParser.parseResource(ReleaseVisitorTests.class.getResourceAsStream("Practitioner-minimal.json")); spyRepository.update(practitioner); Date today = new Date(); // get today's date in the form "2023-05-11" @@ -130,7 +130,7 @@ void approveOperation_test() { part("artifactAssessmentTarget", new UriType(artifactAssessmentTarget)), part("artifactAssessmentRelatedArtifact", new UriType(artifactAssessmentRelatedArtifact)), part("artifactAssessmentAuthor", new Reference(artifactAssessmentAuthor))); - KnowledgeArtifactApproveVisitor approveVisitor = new KnowledgeArtifactApproveVisitor(); + ApproveVisitor approveVisitor = new ApproveVisitor(); Library lib = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/dstu3/KnowledgeArtifactDraftVisitorTests.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/dstu3/DraftVisitorTests.java similarity index 86% rename from cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/dstu3/KnowledgeArtifactDraftVisitorTests.java rename to cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/dstu3/DraftVisitorTests.java index 2a021385e..7cef08354 100644 --- a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/dstu3/KnowledgeArtifactDraftVisitorTests.java +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/dstu3/DraftVisitorTests.java @@ -41,10 +41,10 @@ import org.opencds.cqf.fhir.utility.adapter.dstu3.AdapterFactory; import org.opencds.cqf.fhir.utility.dstu3.MetadataResourceHelper; import org.opencds.cqf.fhir.utility.repository.InMemoryFhirRepository; -import org.opencds.cqf.fhir.utility.visitor.KnowledgeArtifactDraftVisitor; +import org.opencds.cqf.fhir.utility.visitor.DraftVisitor; import org.opencds.cqf.fhir.utility.visitor.KnowledgeArtifactVisitor; -class KnowledgeArtifactDraftVisitorTests { +class DraftVisitorTests { private final FhirContext fhirContext = FhirContext.forDstu3Cached(); private Repository spyRepository; private final IParser jsonParser = fhirContext.newJsonParser(); @@ -83,10 +83,10 @@ public Bundle answer(InvocationOnMock a) throws Throwable { @Test void library_draft_test() { - Bundle bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactDraftVisitorTests.class.getResourceAsStream("Bundle-ersd-example.json")); + Bundle bundle = (Bundle) + jsonParser.parseResource(DraftVisitorTests.class.getResourceAsStream("Bundle-ersd-example.json")); spyRepository.transaction(bundle); - KnowledgeArtifactVisitor draftVisitor = new KnowledgeArtifactDraftVisitor(); + KnowledgeArtifactVisitor draftVisitor = new DraftVisitor(); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -141,15 +141,15 @@ void library_draft_test() { @Test void draftOperation_no_effectivePeriod_test() { - Bundle bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactDraftVisitorTests.class.getResourceAsStream("Bundle-ersd-example.json")); + Bundle bundle = (Bundle) + jsonParser.parseResource(DraftVisitorTests.class.getResourceAsStream("Bundle-ersd-example.json")); spyRepository.transaction(bundle); Library baseLib = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); assertTrue(baseLib.hasEffectivePeriod()); LibraryAdapter libraryAdapter = new AdapterFactory().createLibrary(baseLib); - KnowledgeArtifactVisitor draftVisitor = new KnowledgeArtifactDraftVisitor(); + KnowledgeArtifactVisitor draftVisitor = new DraftVisitor(); PlanDefinition planDef = spyRepository .read(PlanDefinition.class, new IdType("PlanDefinition/plandefinition-ersd-instance-example")) .copy(); @@ -170,10 +170,10 @@ void draftOperation_no_effectivePeriod_test() { @Test void draftOperation_version_conflict_test() { - Bundle bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactDraftVisitorTests.class.getResourceAsStream("Bundle-ersd-example.json")); - Library versionConflictLibrary = (Library) jsonParser.parseResource( - KnowledgeArtifactDraftVisitorTests.class.getResourceAsStream("Library-version-conflict.json")); + Bundle bundle = (Bundle) + jsonParser.parseResource(DraftVisitorTests.class.getResourceAsStream("Bundle-ersd-example.json")); + Library versionConflictLibrary = (Library) + jsonParser.parseResource(DraftVisitorTests.class.getResourceAsStream("Library-version-conflict.json")); spyRepository.transaction(bundle); spyRepository.update(versionConflictLibrary); Parameters params = parameters(part("version", "1.0.0")); @@ -182,7 +182,7 @@ void draftOperation_version_conflict_test() { .read(Library.class, new IdType(specificationLibReference)) .copy(); LibraryAdapter libraryAdapter = new AdapterFactory().createLibrary(baseLib); - KnowledgeArtifactVisitor draftVisitor = new KnowledgeArtifactDraftVisitor(); + KnowledgeArtifactVisitor draftVisitor = new DraftVisitor(); try { libraryAdapter.accept(draftVisitor, spyRepository, params); @@ -196,8 +196,8 @@ void draftOperation_version_conflict_test() { @Test void draftOperation_cannot_create_draft_of_draft_test() { - Library versionConflictLibrary = (Library) jsonParser.parseResource( - KnowledgeArtifactDraftVisitorTests.class.getResourceAsStream("Library-version-conflict.json")); + Library versionConflictLibrary = (Library) + jsonParser.parseResource(DraftVisitorTests.class.getResourceAsStream("Library-version-conflict.json")); spyRepository.update(versionConflictLibrary); Parameters params = parameters(part("version", "1.2.1")); String maybeException = ""; @@ -205,7 +205,7 @@ void draftOperation_cannot_create_draft_of_draft_test() { .read(Library.class, new IdType("Library/SpecificationLibraryDraftVersion-1-0-0")) .copy(); LibraryAdapter libraryAdapter = new AdapterFactory().createLibrary(baseLib); - KnowledgeArtifactVisitor draftVisitor = new KnowledgeArtifactDraftVisitor(); + KnowledgeArtifactVisitor draftVisitor = new DraftVisitor(); try { libraryAdapter.accept(draftVisitor, spyRepository, params); } catch (PreconditionFailedException e) { @@ -217,14 +217,14 @@ void draftOperation_cannot_create_draft_of_draft_test() { @Test void draftOperation_version_format_test() { - Library versionConflictLibrary = (Library) jsonParser.parseResource( - KnowledgeArtifactDraftVisitorTests.class.getResourceAsStream("Library-version-conflict.json")); + Library versionConflictLibrary = (Library) + jsonParser.parseResource(DraftVisitorTests.class.getResourceAsStream("Library-version-conflict.json")); spyRepository.update(versionConflictLibrary); Library baseLib = spyRepository .read(Library.class, new IdType("Library/SpecificationLibraryDraftVersion-1-0-0")) .copy(); LibraryAdapter libraryAdapter = new AdapterFactory().createLibrary(baseLib); - KnowledgeArtifactVisitor draftVisitor = new KnowledgeArtifactDraftVisitor(); + KnowledgeArtifactVisitor draftVisitor = new DraftVisitor(); for (String version : badVersionList) { UnprocessableEntityException maybeException = null; diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/dstu3/KnowledgeArtifactPackageVisitorTests.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/dstu3/PackageVisitorTests.java similarity index 89% rename from cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/dstu3/KnowledgeArtifactPackageVisitorTests.java rename to cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/dstu3/PackageVisitorTests.java index 0b30ddf2a..69dbf6f1b 100644 --- a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/dstu3/KnowledgeArtifactPackageVisitorTests.java +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/dstu3/PackageVisitorTests.java @@ -36,6 +36,7 @@ import org.hl7.fhir.dstu3.model.UriType; import org.hl7.fhir.dstu3.model.ValueSet; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; @@ -44,9 +45,9 @@ import org.opencds.cqf.fhir.utility.adapter.LibraryAdapter; import org.opencds.cqf.fhir.utility.adapter.dstu3.AdapterFactory; import org.opencds.cqf.fhir.utility.repository.InMemoryFhirRepository; -import org.opencds.cqf.fhir.utility.visitor.KnowledgeArtifactPackageVisitor; +import org.opencds.cqf.fhir.utility.visitor.PackageVisitor; -class KnowledgeArtifactPackageVisitorTests { +class PackageVisitorTests { private final FhirContext fhirContext = FhirContext.forDstu3Cached(); private final IParser jsonParser = fhirContext.newJsonParser(); private Repository spyRepository; @@ -68,9 +69,9 @@ public Bundle answer(InvocationOnMock a) throws Throwable { @Test void visitLibraryTest() { Bundle loadedBundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactPackageVisitorTests.class.getResourceAsStream("Bundle-ersd-example-naive.json")); + PackageVisitorTests.class.getResourceAsStream("Bundle-ersd-example-naive.json")); spyRepository.transaction(loadedBundle); - KnowledgeArtifactPackageVisitor packageVisitor = new KnowledgeArtifactPackageVisitor(); + PackageVisitor packageVisitor = new PackageVisitor(fhirContext); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -98,11 +99,12 @@ void visitLibraryTest() { } @Test + @Disabled("This test needs a ValueSet that cannot be naively expanded") void packageOperation_should_fail_no_credentials() { - Bundle loadedBundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactPackageVisitorTests.class.getResourceAsStream("Bundle-ersd-example.json")); + Bundle loadedBundle = (Bundle) + jsonParser.parseResource(PackageVisitorTests.class.getResourceAsStream("Bundle-ersd-example.json")); spyRepository.transaction(loadedBundle); - KnowledgeArtifactPackageVisitor packageVisitor = new KnowledgeArtifactPackageVisitor(); + PackageVisitor packageVisitor = new PackageVisitor(fhirContext); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -119,11 +121,12 @@ void packageOperation_should_fail_no_credentials() { } @Test + @Disabled("This test needs a ValueSet that cannot be naively expanded") void packageOperation_should_fail_credentials_missing_username() { - Bundle loadedBundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactPackageVisitorTests.class.getResourceAsStream("Bundle-ersd-example.json")); + Bundle loadedBundle = (Bundle) + jsonParser.parseResource(PackageVisitorTests.class.getResourceAsStream("Bundle-ersd-example.json")); spyRepository.transaction(loadedBundle); - KnowledgeArtifactPackageVisitor packageVisitor = new KnowledgeArtifactPackageVisitor(); + PackageVisitor packageVisitor = new PackageVisitor(fhirContext); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -144,11 +147,12 @@ void packageOperation_should_fail_credentials_missing_username() { } @Test + @Disabled("This test needs a ValueSet that cannot be naively expanded") void packageOperation_should_fail_credentials_missing_apikey() { - Bundle loadedBundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactPackageVisitorTests.class.getResourceAsStream("Bundle-ersd-example.json")); + Bundle loadedBundle = (Bundle) + jsonParser.parseResource(PackageVisitorTests.class.getResourceAsStream("Bundle-ersd-example.json")); spyRepository.transaction(loadedBundle); - KnowledgeArtifactPackageVisitor packageVisitor = new KnowledgeArtifactPackageVisitor(); + PackageVisitor packageVisitor = new PackageVisitor(fhirContext); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -169,11 +173,12 @@ void packageOperation_should_fail_credentials_missing_apikey() { } @Test + @Disabled("This test needs a ValueSet that cannot be naively expanded") void packageOperation_should_fail_credentials_invalid() { - Bundle loadedBundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactPackageVisitorTests.class.getResourceAsStream("Bundle-ersd-example.json")); + Bundle loadedBundle = (Bundle) + jsonParser.parseResource(PackageVisitorTests.class.getResourceAsStream("Bundle-ersd-example.json")); spyRepository.transaction(loadedBundle); - KnowledgeArtifactPackageVisitor packageVisitor = new KnowledgeArtifactPackageVisitor(); + PackageVisitor packageVisitor = new PackageVisitor(fhirContext); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -196,12 +201,11 @@ void packageOperation_should_fail_credentials_invalid() { @Test void packageOperation_should_fail_non_matching_capability() { - Bundle bundle = - (Bundle) jsonParser.parseResource(KnowledgeArtifactPackageVisitorTests.class.getResourceAsStream( - "Bundle-ersd-package-capabilities.json")); + Bundle bundle = (Bundle) jsonParser.parseResource( + PackageVisitorTests.class.getResourceAsStream("Bundle-ersd-package-capabilities.json")); spyRepository.transaction(bundle); List capabilities = Arrays.asList("computable", "publishable", "executable"); - KnowledgeArtifactPackageVisitor packageVisitor = new KnowledgeArtifactPackageVisitor(); + PackageVisitor packageVisitor = new PackageVisitor(fhirContext); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -231,9 +235,9 @@ void packageOperation_should_fail_non_matching_capability() { @Test void packageOperation_should_apply_check_force_canonicalVersions() { Bundle bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactPackageVisitorTests.class.getResourceAsStream("Bundle-active-no-versions.json")); + PackageVisitorTests.class.getResourceAsStream("Bundle-active-no-versions.json")); spyRepository.transaction(bundle); - KnowledgeArtifactPackageVisitor packageVisitor = new KnowledgeArtifactPackageVisitor(); + PackageVisitor packageVisitor = new PackageVisitor(fhirContext); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -292,9 +296,9 @@ void packageOperation_should_apply_check_force_canonicalVersions() { @Test void packageOperation_should_respect_count_offset() { Bundle bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactPackageVisitorTests.class.getResourceAsStream("Bundle-ersd-small-active.json")); + PackageVisitorTests.class.getResourceAsStream("Bundle-ersd-small-active.json")); spyRepository.transaction(bundle); - KnowledgeArtifactPackageVisitor packageVisitor = new KnowledgeArtifactPackageVisitor(); + PackageVisitor packageVisitor = new PackageVisitor(fhirContext); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -330,9 +334,9 @@ void packageOperation_should_respect_count_offset() { @Test void packageOperation_different_bundle_types() { Bundle bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactPackageVisitorTests.class.getResourceAsStream("Bundle-ersd-small-active.json")); + PackageVisitorTests.class.getResourceAsStream("Bundle-ersd-small-active.json")); spyRepository.transaction(bundle); - KnowledgeArtifactPackageVisitor packageVisitor = new KnowledgeArtifactPackageVisitor(); + PackageVisitor packageVisitor = new PackageVisitor(fhirContext); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -369,9 +373,9 @@ void packageOperation_different_bundle_types() { @Test void packageOperation_should_conditionally_create() { Bundle bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactPackageVisitorTests.class.getResourceAsStream("Bundle-ersd-small-active.json")); + PackageVisitorTests.class.getResourceAsStream("Bundle-ersd-small-active.json")); spyRepository.transaction(bundle); - KnowledgeArtifactPackageVisitor packageVisitor = new KnowledgeArtifactPackageVisitor(); + PackageVisitor packageVisitor = new PackageVisitor(fhirContext); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -389,9 +393,9 @@ void packageOperation_should_conditionally_create() { @Test void packageOperation_should_respect_include() { Bundle bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactPackageVisitorTests.class.getResourceAsStream("Bundle-ersd-small-active.json")); + PackageVisitorTests.class.getResourceAsStream("Bundle-ersd-small-active.json")); spyRepository.transaction(bundle); - KnowledgeArtifactPackageVisitor packageVisitor = new KnowledgeArtifactPackageVisitor(); + PackageVisitor packageVisitor = new PackageVisitor(fhirContext); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/dstu3/KnowledgeArtifactReleaseVisitorTests.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/dstu3/ReleaseVisitorTests.java similarity index 88% rename from cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/dstu3/KnowledgeArtifactReleaseVisitorTests.java rename to cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/dstu3/ReleaseVisitorTests.java index 1ed3da510..410e579f0 100644 --- a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/dstu3/KnowledgeArtifactReleaseVisitorTests.java +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/dstu3/ReleaseVisitorTests.java @@ -48,10 +48,10 @@ import org.opencds.cqf.fhir.utility.adapter.dstu3.AdapterFactory; import org.opencds.cqf.fhir.utility.dstu3.MetadataResourceHelper; import org.opencds.cqf.fhir.utility.repository.InMemoryFhirRepository; -import org.opencds.cqf.fhir.utility.visitor.KnowledgeArtifactReleaseVisitor; +import org.opencds.cqf.fhir.utility.visitor.ReleaseVisitor; import org.slf4j.LoggerFactory; -class KnowledgeArtifactReleaseVisitorTests { +class ReleaseVisitorTests { private final FhirContext fhirContext = FhirContext.forDstu3Cached(); private Repository spyRepository; private final IParser jsonParser = fhirContext.newJsonParser(); @@ -75,9 +75,8 @@ class KnowledgeArtifactReleaseVisitorTests { @BeforeEach void setup() { - SearchParameter sp = (SearchParameter) - jsonParser.parseResource(KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream( - "SearchParameter-artifactAssessment.json")); + SearchParameter sp = (SearchParameter) jsonParser.parseResource( + ReleaseVisitorTests.class.getResourceAsStream("SearchParameter-artifactAssessment.json")); spyRepository = spy(new InMemoryFhirRepository(fhirContext)); spyRepository.update(sp); doAnswer(new Answer() { @@ -94,9 +93,9 @@ public Bundle answer(InvocationOnMock a) throws Throwable { @Test void visitLibraryTest() { Bundle bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream("Bundle-ersd-release-bundle.json")); + ReleaseVisitorTests.class.getResourceAsStream("Bundle-ersd-release-bundle.json")); spyRepository.transaction(bundle); - KnowledgeArtifactReleaseVisitor releaseVisitor = new KnowledgeArtifactReleaseVisitor(); + ReleaseVisitor releaseVisitor = new ReleaseVisitor(); Library library = spyRepository .read(Library.class, new IdType("Library/ReleaseSpecificationLibrary")) .copy(); @@ -169,7 +168,13 @@ void visitLibraryTest() { "http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-lab", "http://hl7.org/fhir/us/core/StructureDefinition/us-core-diagnosticreport-lab", "http://hl7.org/fhir/us/ecr/StructureDefinition/eicr-document-bundle", - "http://hl7.org/fhir/StructureDefinition/ServiceRequest"); + "http://hl7.org/fhir/StructureDefinition/ServiceRequest", + "http://hl7.org/fhir/us/ecr/StructureDefinition/ersd-valueset-library", + "http://hl7.org/fhir/us/ecr/StructureDefinition/us-ph-triggering-valueset-library", + "http://hl7.org/fhir/us/ecr/StructureDefinition/ersd-plandefinition", + "http://hl7.org/fhir/us/ecr/StructureDefinition/us-ph-plandefinition", + "http://hl7.org/fhir/us/ecr/StructureDefinition/ersd-valueset", + "http://hl7.org/fhir/us/ecr/StructureDefinition/us-ph-triggering-valueset"); var expectedErsdTestArtifactComponents = Arrays.asList( "http://ersd.aimsplatform.org/fhir/PlanDefinition/release-us-ecr-specification|" + existingVersion, "http://ersd.aimsplatform.org/fhir/Library/release-rctc|" + existingVersion, @@ -198,11 +203,11 @@ void visitLibraryTest() { @Test void releaseResource_force_version() { Bundle bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream("Bundle-small-approved-draft.json")); + ReleaseVisitorTests.class.getResourceAsStream("Bundle-small-approved-draft.json")); spyRepository.transaction(bundle); // Existing version should be "1.2.3"; String newVersionToForce = "1.2.7"; - KnowledgeArtifactReleaseVisitor releaseVisitor = new KnowledgeArtifactReleaseVisitor(); + ReleaseVisitor releaseVisitor = new ReleaseVisitor(); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -224,15 +229,13 @@ void releaseResource_force_version() { @Test void releaseResource_require_non_experimental_error() { - // SpecificationLibrary - root is experimentalbut HAS experimental children - Bundle bundle = - (Bundle) jsonParser.parseResource(KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream( - "Bundle-small-approved-draft-experimental.json")); + // SpecificationLibrary - root is experimental but HAS experimental children + Bundle bundle = (Bundle) jsonParser.parseResource( + ReleaseVisitorTests.class.getResourceAsStream("Bundle-small-approved-draft-experimental.json")); spyRepository.transaction(bundle); // SpecificationLibrary2 - root is NOT experimental but HAS experimental children - Bundle bundle2 = - (Bundle) jsonParser.parseResource(KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream( - "Bundle-small-approved-draft-experimental-children.json")); + Bundle bundle2 = (Bundle) jsonParser.parseResource(ReleaseVisitorTests.class.getResourceAsStream( + "Bundle-small-approved-draft-experimental-children.json")); spyRepository.transaction(bundle2); Parameters params = parameters( part("version", new StringType("1.2.3")), @@ -240,7 +243,7 @@ void releaseResource_require_non_experimental_error() { part("requireNonExperimental", new CodeType("error"))); Exception notExpectingAnyException = null; // no Exception if root is experimental - KnowledgeArtifactReleaseVisitor releaseVisitor = new KnowledgeArtifactReleaseVisitor(); + ReleaseVisitor releaseVisitor = new ReleaseVisitor(); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -268,18 +271,16 @@ void releaseResource_require_non_experimental_error() { @Test void releaseResource_require_non_experimental_warn() { - // SpecificationLibrary - root is experimentalbut HAS experimental children - Bundle bundle = - (Bundle) jsonParser.parseResource(KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream( - "Bundle-small-approved-draft-experimental.json")); + // SpecificationLibrary - root is experimental but HAS experimental children + Bundle bundle = (Bundle) jsonParser.parseResource( + ReleaseVisitorTests.class.getResourceAsStream("Bundle-small-approved-draft-experimental.json")); spyRepository.transaction(bundle); // SpecificationLibrary2 - root is NOT experimental but HAS experimental children - Bundle bundle2 = - (Bundle) jsonParser.parseResource(KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream( - "Bundle-small-approved-draft-experimental-children.json")); + Bundle bundle2 = (Bundle) jsonParser.parseResource(ReleaseVisitorTests.class.getResourceAsStream( + "Bundle-small-approved-draft-experimental-children.json")); spyRepository.transaction(bundle2); - KnowledgeArtifactReleaseVisitor releaseVisitor = new KnowledgeArtifactReleaseVisitor(); + ReleaseVisitor releaseVisitor = new ReleaseVisitor(); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -329,15 +330,14 @@ void releaseResource_require_non_experimental_warn() { @Test void releaseResource_propagate_effective_period() { - Bundle bundle = - (Bundle) jsonParser.parseResource(KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream( - "Bundle-ersd-no-child-effective-period.json")); + Bundle bundle = (Bundle) jsonParser.parseResource( + ReleaseVisitorTests.class.getResourceAsStream("Bundle-ersd-no-child-effective-period.json")); spyRepository.transaction(bundle); String effectivePeriodToPropagate = "2020-12-11"; Parameters params = parameters(part("version", new StringType("1.2.7")), part("versionBehavior", new CodeType("default"))); - KnowledgeArtifactReleaseVisitor releaseVisitor = new KnowledgeArtifactReleaseVisitor(); + ReleaseVisitor releaseVisitor = new ReleaseVisitor(); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -367,7 +367,7 @@ void releaseResource_propagate_effective_period() { @Test void releaseResource_latestFromTx_NotSupported_test() { Bundle bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream("Bundle-small-approved-draft.json")); + ReleaseVisitorTests.class.getResourceAsStream("Bundle-small-approved-draft.json")); spyRepository.transaction(bundle); String actualErrorMessage = ""; @@ -376,7 +376,7 @@ void releaseResource_latestFromTx_NotSupported_test() { part("version", "1.2.3"), part("versionBehavior", new CodeType("default")), part("latestFromTxServer", new BooleanType(true))); - KnowledgeArtifactReleaseVisitor releaseVisitor = new KnowledgeArtifactReleaseVisitor(); + ReleaseVisitor releaseVisitor = new ReleaseVisitor(); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -392,16 +392,15 @@ void releaseResource_latestFromTx_NotSupported_test() { @Test void release_missing_approvalDate_validation_test() { - Bundle bundle = - (Bundle) jsonParser.parseResource(KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream( - "Bundle-release-missing-approvalDate.json")); + Bundle bundle = (Bundle) jsonParser.parseResource( + ReleaseVisitorTests.class.getResourceAsStream("Bundle-release-missing-approvalDate.json")); spyRepository.transaction(bundle); String versionData = "1.2.3"; String actualErrorMessage = ""; Parameters params1 = parameters(part("version", versionData), part("versionBehavior", new CodeType("default"))); - KnowledgeArtifactReleaseVisitor releaseVisitor = new KnowledgeArtifactReleaseVisitor(); + ReleaseVisitor releaseVisitor = new ReleaseVisitor(); Library library = spyRepository .read(Library.class, new IdType("Library/ReleaseSpecificationLibrary")) .copy(); @@ -417,9 +416,9 @@ void release_missing_approvalDate_validation_test() { @Test void release_version_format_test() { Bundle bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream("Bundle-small-approved-draft.json")); + ReleaseVisitorTests.class.getResourceAsStream("Bundle-small-approved-draft.json")); spyRepository.transaction(bundle); - KnowledgeArtifactReleaseVisitor releaseVisitor = new KnowledgeArtifactReleaseVisitor(); + ReleaseVisitor releaseVisitor = new ReleaseVisitor(); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -441,10 +440,10 @@ void release_version_format_test() { @Test void release_releaseLabel_test() { Bundle bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream("Bundle-small-approved-draft.json")); + ReleaseVisitorTests.class.getResourceAsStream("Bundle-small-approved-draft.json")); spyRepository.transaction(bundle); String releaseLabel = "release label test"; - KnowledgeArtifactReleaseVisitor releaseVisitor = new KnowledgeArtifactReleaseVisitor(); + ReleaseVisitor releaseVisitor = new ReleaseVisitor(); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -471,9 +470,9 @@ void release_releaseLabel_test() { @Test void release_version_active_test() { Bundle bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream("Bundle-ersd-small-active.json")); + ReleaseVisitorTests.class.getResourceAsStream("Bundle-ersd-small-active.json")); spyRepository.transaction(bundle); - KnowledgeArtifactReleaseVisitor releaseVisitor = new KnowledgeArtifactReleaseVisitor(); + ReleaseVisitor releaseVisitor = new ReleaseVisitor(); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -493,9 +492,9 @@ void release_version_active_test() { @Test void release_versionBehaviour_format_test() { Bundle bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream("Bundle-small-approved-draft.json")); + ReleaseVisitorTests.class.getResourceAsStream("Bundle-small-approved-draft.json")); spyRepository.transaction(bundle); - KnowledgeArtifactReleaseVisitor releaseVisitor = new KnowledgeArtifactReleaseVisitor(); + ReleaseVisitor releaseVisitor = new ReleaseVisitor(); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r4/KnowledgeArtifactApproveVisitorTest.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r4/ApproveVisitorTest.java similarity index 91% rename from cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r4/KnowledgeArtifactApproveVisitorTest.java rename to cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r4/ApproveVisitorTest.java index 4e4e60856..9e620e8c0 100644 --- a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r4/KnowledgeArtifactApproveVisitorTest.java +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r4/ApproveVisitorTest.java @@ -37,17 +37,17 @@ import org.opencds.cqf.fhir.utility.adapter.r4.AdapterFactory; import org.opencds.cqf.fhir.utility.r4.ArtifactAssessment; import org.opencds.cqf.fhir.utility.repository.InMemoryFhirRepository; -import org.opencds.cqf.fhir.utility.visitor.KnowledgeArtifactApproveVisitor; +import org.opencds.cqf.fhir.utility.visitor.ApproveVisitor; -class KnowledgeArtifactApproveVisitorTest { +class ApproveVisitorTest { private final FhirContext fhirContext = FhirContext.forR4Cached(); private Repository spyRepository; private final IParser jsonParser = fhirContext.newJsonParser(); @BeforeEach void setup() { - var lib = (Library) jsonParser.parseResource( - KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream("Library-ersd-active.json")); + var lib = (Library) + jsonParser.parseResource(ReleaseVisitorTests.class.getResourceAsStream("Library-ersd-active.json")); spyRepository = spy(new InMemoryFhirRepository(fhirContext)); spyRepository.update(lib); doAnswer(new Answer() { @@ -66,7 +66,7 @@ void approveOperation_endpoint_id_should_match_target_parameter() { var artifactAssessmentTarget = "Library/This-Library-Does-Not-Exist|1.0.0"; var params = parameters(part("artifactAssessmentTarget", new CanonicalType(artifactAssessmentTarget))); UnprocessableEntityException maybeException = null; - var releaseVisitor = new KnowledgeArtifactApproveVisitor(); + var releaseVisitor = new ApproveVisitor(); var lib = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -96,7 +96,7 @@ void approveOperation_should_respect_artifactAssessment_information_type_binding String artifactAssessmentType = "this-type-does-not-exist"; Parameters params = parameters(part("artifactAssessmentType", new CodeType(artifactAssessmentType))); UnprocessableEntityException maybeException = null; - KnowledgeArtifactApproveVisitor releaseVisitor = new KnowledgeArtifactApproveVisitor(); + ApproveVisitor releaseVisitor = new ApproveVisitor(); Library lib = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -112,8 +112,8 @@ void approveOperation_should_respect_artifactAssessment_information_type_binding @Test void approveOperation_test() { - var practitioner = (Practitioner) jsonParser.parseResource( - KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream("Practitioner-minimal.json")); + var practitioner = (Practitioner) + jsonParser.parseResource(ReleaseVisitorTests.class.getResourceAsStream("Practitioner-minimal.json")); spyRepository.update(practitioner); Date today = new Date(); // get today's date in the form "2023-05-11" @@ -130,7 +130,7 @@ void approveOperation_test() { part("artifactAssessmentTarget", new CanonicalType(artifactAssessmentTarget)), part("artifactAssessmentRelatedArtifact", new CanonicalType(artifactAssessmentRelatedArtifact)), part("artifactAssessmentAuthor", new Reference(artifactAssessmentAuthor))); - KnowledgeArtifactApproveVisitor approveVisitor = new KnowledgeArtifactApproveVisitor(); + ApproveVisitor approveVisitor = new ApproveVisitor(); Library lib = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r4/KnowledgeArtifactDraftVisitorTests.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r4/DraftVisitorTests.java similarity index 85% rename from cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r4/KnowledgeArtifactDraftVisitorTests.java rename to cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r4/DraftVisitorTests.java index fe075304f..645460e37 100644 --- a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r4/KnowledgeArtifactDraftVisitorTests.java +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r4/DraftVisitorTests.java @@ -41,10 +41,10 @@ import org.opencds.cqf.fhir.utility.adapter.r4.AdapterFactory; import org.opencds.cqf.fhir.utility.r4.MetadataResourceHelper; import org.opencds.cqf.fhir.utility.repository.InMemoryFhirRepository; -import org.opencds.cqf.fhir.utility.visitor.KnowledgeArtifactDraftVisitor; +import org.opencds.cqf.fhir.utility.visitor.DraftVisitor; import org.opencds.cqf.fhir.utility.visitor.KnowledgeArtifactVisitor; -class KnowledgeArtifactDraftVisitorTests { +class DraftVisitorTests { private final FhirContext fhirContext = FhirContext.forR4Cached(); private Repository spyRepository; private final IParser jsonParser = fhirContext.newJsonParser(); @@ -83,10 +83,10 @@ public Bundle answer(InvocationOnMock a) throws Throwable { @Test void library_draft_test() { - Bundle bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactDraftVisitorTests.class.getResourceAsStream("Bundle-ersd-example.json")); + Bundle bundle = (Bundle) + jsonParser.parseResource(DraftVisitorTests.class.getResourceAsStream("Bundle-ersd-example.json")); spyRepository.transaction(bundle); - KnowledgeArtifactVisitor draftVisitor = new KnowledgeArtifactDraftVisitor(); + KnowledgeArtifactVisitor draftVisitor = new DraftVisitor(); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -137,15 +137,15 @@ void library_draft_test() { @Test void draftOperation_no_effectivePeriod_test() { - Bundle bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactDraftVisitorTests.class.getResourceAsStream("Bundle-ersd-example.json")); + Bundle bundle = (Bundle) + jsonParser.parseResource(DraftVisitorTests.class.getResourceAsStream("Bundle-ersd-example.json")); spyRepository.transaction(bundle); Library baseLib = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); assertTrue(baseLib.hasEffectivePeriod()); LibraryAdapter libraryAdapter = new AdapterFactory().createLibrary(baseLib); - KnowledgeArtifactVisitor draftVisitor = new KnowledgeArtifactDraftVisitor(); + KnowledgeArtifactVisitor draftVisitor = new DraftVisitor(); PlanDefinition planDef = spyRepository .read(PlanDefinition.class, new IdType("PlanDefinition/plandefinition-ersd-instance-example")) .copy(); @@ -166,10 +166,10 @@ void draftOperation_no_effectivePeriod_test() { @Test void draftOperation_version_conflict_test() { - Bundle bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactDraftVisitorTests.class.getResourceAsStream("Bundle-ersd-example.json")); - Library versionConflictLibrary = (Library) jsonParser.parseResource( - KnowledgeArtifactDraftVisitorTests.class.getResourceAsStream("Library-version-conflict.json")); + Bundle bundle = (Bundle) + jsonParser.parseResource(DraftVisitorTests.class.getResourceAsStream("Bundle-ersd-example.json")); + Library versionConflictLibrary = (Library) + jsonParser.parseResource(DraftVisitorTests.class.getResourceAsStream("Library-version-conflict.json")); spyRepository.transaction(bundle); spyRepository.update(versionConflictLibrary); Parameters params = parameters(part("version", "1.0.0")); @@ -178,7 +178,7 @@ void draftOperation_version_conflict_test() { .read(Library.class, new IdType(specificationLibReference)) .copy(); LibraryAdapter libraryAdapter = new AdapterFactory().createLibrary(baseLib); - KnowledgeArtifactVisitor draftVisitor = new KnowledgeArtifactDraftVisitor(); + KnowledgeArtifactVisitor draftVisitor = new DraftVisitor(); try { libraryAdapter.accept(draftVisitor, spyRepository, params); @@ -192,8 +192,8 @@ void draftOperation_version_conflict_test() { @Test void draftOperation_cannot_create_draft_of_draft_test() { - Library versionConflictLibrary = (Library) jsonParser.parseResource( - KnowledgeArtifactDraftVisitorTests.class.getResourceAsStream("Library-version-conflict.json")); + Library versionConflictLibrary = (Library) + jsonParser.parseResource(DraftVisitorTests.class.getResourceAsStream("Library-version-conflict.json")); spyRepository.update(versionConflictLibrary); Parameters params = parameters(part("version", "1.2.1")); String maybeException = ""; @@ -201,7 +201,7 @@ void draftOperation_cannot_create_draft_of_draft_test() { .read(Library.class, new IdType("Library/SpecificationLibraryDraftVersion-1-0-0")) .copy(); LibraryAdapter libraryAdapter = new AdapterFactory().createLibrary(baseLib); - KnowledgeArtifactVisitor draftVisitor = new KnowledgeArtifactDraftVisitor(); + KnowledgeArtifactVisitor draftVisitor = new DraftVisitor(); try { libraryAdapter.accept(draftVisitor, spyRepository, params); } catch (PreconditionFailedException e) { @@ -213,14 +213,14 @@ void draftOperation_cannot_create_draft_of_draft_test() { @Test void draftOperation_version_format_test() { - Library versionConflictLibrary = (Library) jsonParser.parseResource( - KnowledgeArtifactDraftVisitorTests.class.getResourceAsStream("Library-version-conflict.json")); + Library versionConflictLibrary = (Library) + jsonParser.parseResource(DraftVisitorTests.class.getResourceAsStream("Library-version-conflict.json")); spyRepository.update(versionConflictLibrary); Library baseLib = spyRepository .read(Library.class, new IdType("Library/SpecificationLibraryDraftVersion-1-0-0")) .copy(); LibraryAdapter libraryAdapter = new AdapterFactory().createLibrary(baseLib); - KnowledgeArtifactVisitor draftVisitor = new KnowledgeArtifactDraftVisitor(); + KnowledgeArtifactVisitor draftVisitor = new DraftVisitor(); for (String version : badVersionList) { UnprocessableEntityException maybeException = null; diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r4/KnowledgeArtifactPackageVisitorTests.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r4/PackageVisitorTests.java similarity index 86% rename from cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r4/KnowledgeArtifactPackageVisitorTests.java rename to cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r4/PackageVisitorTests.java index efb13d6e6..8b29714b7 100644 --- a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r4/KnowledgeArtifactPackageVisitorTests.java +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r4/PackageVisitorTests.java @@ -36,6 +36,7 @@ import org.hl7.fhir.r4.model.StringType; import org.hl7.fhir.r4.model.ValueSet; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; @@ -44,9 +45,9 @@ import org.opencds.cqf.fhir.utility.adapter.LibraryAdapter; import org.opencds.cqf.fhir.utility.adapter.r4.AdapterFactory; import org.opencds.cqf.fhir.utility.repository.InMemoryFhirRepository; -import org.opencds.cqf.fhir.utility.visitor.KnowledgeArtifactPackageVisitor; +import org.opencds.cqf.fhir.utility.visitor.PackageVisitor; -class KnowledgeArtifactPackageVisitorTests { +class PackageVisitorTests { private final FhirContext fhirContext = FhirContext.forR4Cached(); private final IParser jsonParser = fhirContext.newJsonParser(); private Repository spyRepository; @@ -68,9 +69,9 @@ public Bundle answer(InvocationOnMock a) throws Throwable { @Test void visitLibraryTest() { Bundle loadedBundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactPackageVisitorTests.class.getResourceAsStream("Bundle-ersd-example-naive.json")); + PackageVisitorTests.class.getResourceAsStream("Bundle-ersd-example-naive.json")); spyRepository.transaction(loadedBundle); - KnowledgeArtifactPackageVisitor packageVisitor = new KnowledgeArtifactPackageVisitor(); + PackageVisitor packageVisitor = new PackageVisitor(fhirContext); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -98,16 +99,17 @@ void visitLibraryTest() { } @Test + @Disabled("This test needs a ValueSet that cannot be naively expanded") void packageOperation_should_fail_no_credentials() { - Bundle loadedBundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactPackageVisitorTests.class.getResourceAsStream("Bundle-ersd-example.json")); + Bundle loadedBundle = (Bundle) + jsonParser.parseResource(PackageVisitorTests.class.getResourceAsStream("Bundle-ersd-example.json")); spyRepository.transaction(loadedBundle); - KnowledgeArtifactPackageVisitor packageVisitor = new KnowledgeArtifactPackageVisitor(); + PackageVisitor packageVisitor = new PackageVisitor(fhirContext); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); LibraryAdapter libraryAdapter = new AdapterFactory().createLibrary(library); - Parameters params = new Parameters(); + Parameters params = parameters(); UnprocessableEntityException maybeException = null; try { @@ -115,24 +117,23 @@ void packageOperation_should_fail_no_credentials() { } catch (UnprocessableEntityException e) { maybeException = e; } - assertTrue(maybeException.getMessage().contains("Cannot expand ValueSet without credentials: ")); + assertTrue(maybeException.getMessage().contains("Cannot expand ValueSet without a terminology server: ")); } @Test void packageOperation_should_fail_credentials_missing_username() { - Bundle loadedBundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactPackageVisitorTests.class.getResourceAsStream("Bundle-ersd-example.json")); + Bundle loadedBundle = (Bundle) + jsonParser.parseResource(PackageVisitorTests.class.getResourceAsStream("Bundle-ersd-example.json")); spyRepository.transaction(loadedBundle); - KnowledgeArtifactPackageVisitor packageVisitor = new KnowledgeArtifactPackageVisitor(); + PackageVisitor packageVisitor = new PackageVisitor(fhirContext); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); LibraryAdapter libraryAdapter = new AdapterFactory().createLibrary(library); - Parameters params = new Parameters(); Endpoint terminologyEndpoint = new Endpoint(); terminologyEndpoint.addExtension(Constants.VSAC_USERNAME, new StringType(null)); terminologyEndpoint.addExtension(Constants.APIKEY, new StringType("some-api-key")); - params.addParameter().setName("terminologyEndpoint").setResource(terminologyEndpoint); + Parameters params = parameters(part("terminologyEndpoint", terminologyEndpoint)); UnprocessableEntityException maybeException = null; try { @@ -140,24 +141,23 @@ void packageOperation_should_fail_credentials_missing_username() { } catch (UnprocessableEntityException e) { maybeException = e; } - assertTrue(maybeException.getMessage().contains("Cannot expand ValueSet without VSAC Username: ")); + assertTrue(maybeException.getMessage().contains("Cannot expand ValueSet without VSAC Username.")); } @Test void packageOperation_should_fail_credentials_missing_apikey() { - Bundle loadedBundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactPackageVisitorTests.class.getResourceAsStream("Bundle-ersd-example.json")); + Bundle loadedBundle = (Bundle) + jsonParser.parseResource(PackageVisitorTests.class.getResourceAsStream("Bundle-ersd-example.json")); spyRepository.transaction(loadedBundle); - KnowledgeArtifactPackageVisitor packageVisitor = new KnowledgeArtifactPackageVisitor(); + PackageVisitor packageVisitor = new PackageVisitor(fhirContext); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); LibraryAdapter libraryAdapter = new AdapterFactory().createLibrary(library); - Parameters params = new Parameters(); Endpoint terminologyEndpoint = new Endpoint(); terminologyEndpoint.addExtension(Constants.VSAC_USERNAME, new StringType("someUsername")); terminologyEndpoint.addExtension(Constants.APIKEY, new StringType(null)); - params.addParameter().setName("terminologyEndpoint").setResource(terminologyEndpoint); + Parameters params = parameters(part("terminologyEndpoint", terminologyEndpoint)); UnprocessableEntityException maybeException = null; try { @@ -165,24 +165,23 @@ void packageOperation_should_fail_credentials_missing_apikey() { } catch (UnprocessableEntityException e) { maybeException = e; } - assertTrue(maybeException.getMessage().contains("Cannot expand ValueSet without VSAC API Key: ")); + assertTrue(maybeException.getMessage().contains("Cannot expand ValueSet without VSAC API Key.")); } @Test void packageOperation_should_fail_credentials_invalid() { - Bundle loadedBundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactPackageVisitorTests.class.getResourceAsStream("Bundle-ersd-example.json")); + Bundle loadedBundle = (Bundle) + jsonParser.parseResource(PackageVisitorTests.class.getResourceAsStream("Bundle-ersd-example.json")); spyRepository.transaction(loadedBundle); - KnowledgeArtifactPackageVisitor packageVisitor = new KnowledgeArtifactPackageVisitor(); + PackageVisitor packageVisitor = new PackageVisitor(fhirContext); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); LibraryAdapter libraryAdapter = new AdapterFactory().createLibrary(library); - Parameters params = new Parameters(); Endpoint terminologyEndpoint = new Endpoint(); terminologyEndpoint.addExtension(Constants.VSAC_USERNAME, new StringType("someUsername")); terminologyEndpoint.addExtension(Constants.APIKEY, new StringType("some-api-key")); - params.addParameter().setName("terminologyEndpoint").setResource(terminologyEndpoint); + Parameters params = parameters(part("terminologyEndpoint", terminologyEndpoint)); UnprocessableEntityException maybeException = null; try { @@ -190,18 +189,16 @@ void packageOperation_should_fail_credentials_invalid() { } catch (UnprocessableEntityException e) { maybeException = e; } - assertTrue(maybeException.getMessage().contains("Terminology Server expansion failed for:")); - assertTrue(maybeException.getAdditionalMessages().stream().allMatch(msg -> msg.contains("HTTP 401"))); + assertTrue(maybeException.getMessage().contains("Terminology Server expansion failed for ValueSet ")); } @Test void packageOperation_should_fail_non_matching_capability() { - Bundle bundle = - (Bundle) jsonParser.parseResource(KnowledgeArtifactPackageVisitorTests.class.getResourceAsStream( - "Bundle-ersd-package-capabilities.json")); + Bundle bundle = (Bundle) jsonParser.parseResource( + PackageVisitorTests.class.getResourceAsStream("Bundle-ersd-package-capabilities.json")); spyRepository.transaction(bundle); List capabilities = Arrays.asList("computable", "publishable", "executable"); - KnowledgeArtifactPackageVisitor packageVisitor = new KnowledgeArtifactPackageVisitor(); + PackageVisitor packageVisitor = new PackageVisitor(fhirContext); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -231,9 +228,9 @@ void packageOperation_should_fail_non_matching_capability() { @Test void packageOperation_should_apply_check_force_canonicalVersions() { Bundle bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactPackageVisitorTests.class.getResourceAsStream("Bundle-active-no-versions.json")); + PackageVisitorTests.class.getResourceAsStream("Bundle-active-no-versions.json")); spyRepository.transaction(bundle); - KnowledgeArtifactPackageVisitor packageVisitor = new KnowledgeArtifactPackageVisitor(); + PackageVisitor packageVisitor = new PackageVisitor(fhirContext); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -293,9 +290,9 @@ void packageOperation_should_apply_check_force_canonicalVersions() { @Test void packageOperation_should_respect_count_offset() { Bundle bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactPackageVisitorTests.class.getResourceAsStream("Bundle-ersd-small-active.json")); + PackageVisitorTests.class.getResourceAsStream("Bundle-ersd-small-active.json")); spyRepository.transaction(bundle); - KnowledgeArtifactPackageVisitor packageVisitor = new KnowledgeArtifactPackageVisitor(); + PackageVisitor packageVisitor = new PackageVisitor(fhirContext); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -331,9 +328,9 @@ void packageOperation_should_respect_count_offset() { @Test void packageOperation_different_bundle_types() { Bundle bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactPackageVisitorTests.class.getResourceAsStream("Bundle-ersd-small-active.json")); + PackageVisitorTests.class.getResourceAsStream("Bundle-ersd-small-active.json")); spyRepository.transaction(bundle); - KnowledgeArtifactPackageVisitor packageVisitor = new KnowledgeArtifactPackageVisitor(); + PackageVisitor packageVisitor = new PackageVisitor(fhirContext); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -370,9 +367,9 @@ void packageOperation_different_bundle_types() { @Test void packageOperation_should_conditionally_create() { Bundle bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactPackageVisitorTests.class.getResourceAsStream("Bundle-ersd-small-active.json")); + PackageVisitorTests.class.getResourceAsStream("Bundle-ersd-small-active.json")); spyRepository.transaction(bundle); - KnowledgeArtifactPackageVisitor packageVisitor = new KnowledgeArtifactPackageVisitor(); + PackageVisitor packageVisitor = new PackageVisitor(fhirContext); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -390,9 +387,9 @@ void packageOperation_should_conditionally_create() { @Test void packageOperation_should_respect_include() { Bundle bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactPackageVisitorTests.class.getResourceAsStream("Bundle-ersd-small-active.json")); + PackageVisitorTests.class.getResourceAsStream("Bundle-ersd-small-active.json")); spyRepository.transaction(bundle); - KnowledgeArtifactPackageVisitor packageVisitor = new KnowledgeArtifactPackageVisitor(); + PackageVisitor packageVisitor = new PackageVisitor(fhirContext); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r4/KnowledgeArtifactReleaseVisitorTests.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r4/ReleaseVisitorTests.java similarity index 87% rename from cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r4/KnowledgeArtifactReleaseVisitorTests.java rename to cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r4/ReleaseVisitorTests.java index 888d7c7db..dabf6c6d5 100644 --- a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r4/KnowledgeArtifactReleaseVisitorTests.java +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r4/ReleaseVisitorTests.java @@ -49,10 +49,10 @@ import org.opencds.cqf.fhir.utility.adapter.r4.AdapterFactory; import org.opencds.cqf.fhir.utility.r4.MetadataResourceHelper; import org.opencds.cqf.fhir.utility.repository.InMemoryFhirRepository; -import org.opencds.cqf.fhir.utility.visitor.KnowledgeArtifactReleaseVisitor; +import org.opencds.cqf.fhir.utility.visitor.ReleaseVisitor; import org.slf4j.LoggerFactory; -class KnowledgeArtifactReleaseVisitorTests { +class ReleaseVisitorTests { private final FhirContext fhirContext = FhirContext.forR4Cached(); private Repository spyRepository; private final IParser jsonParser = fhirContext.newJsonParser(); @@ -76,9 +76,8 @@ class KnowledgeArtifactReleaseVisitorTests { @BeforeEach void setup() { - SearchParameter sp = (SearchParameter) - jsonParser.parseResource(KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream( - "SearchParameter-artifactAssessment.json")); + SearchParameter sp = (SearchParameter) jsonParser.parseResource( + ReleaseVisitorTests.class.getResourceAsStream("SearchParameter-artifactAssessment.json")); spyRepository = spy(new InMemoryFhirRepository(fhirContext)); spyRepository.update(sp); doAnswer(new Answer() { @@ -95,9 +94,9 @@ public Bundle answer(InvocationOnMock a) throws Throwable { @Test void visitLibraryTest() { Bundle bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream("Bundle-ersd-release-bundle.json")); + ReleaseVisitorTests.class.getResourceAsStream("Bundle-ersd-release-bundle.json")); spyRepository.transaction(bundle); - KnowledgeArtifactReleaseVisitor releaseVisitor = new KnowledgeArtifactReleaseVisitor(); + ReleaseVisitor releaseVisitor = new ReleaseVisitor(); Library library = spyRepository .read(Library.class, new IdType("Library/ReleaseSpecificationLibrary")) .copy(); @@ -170,7 +169,13 @@ void visitLibraryTest() { "http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-lab", "http://hl7.org/fhir/us/core/StructureDefinition/us-core-diagnosticreport-lab", "http://hl7.org/fhir/us/ecr/StructureDefinition/eicr-document-bundle", - "http://hl7.org/fhir/StructureDefinition/ServiceRequest"); + "http://hl7.org/fhir/StructureDefinition/ServiceRequest", + "http://hl7.org/fhir/us/ecr/StructureDefinition/ersd-valueset-library", + "http://hl7.org/fhir/us/ecr/StructureDefinition/us-ph-triggering-valueset-library", + "http://hl7.org/fhir/us/ecr/StructureDefinition/ersd-plandefinition", + "http://hl7.org/fhir/us/ecr/StructureDefinition/us-ph-plandefinition", + "http://hl7.org/fhir/us/ecr/StructureDefinition/ersd-valueset", + "http://hl7.org/fhir/us/ecr/StructureDefinition/us-ph-triggering-valueset"); var expectedErsdTestArtifactComponents = Arrays.asList( "http://ersd.aimsplatform.org/fhir/PlanDefinition/release-us-ecr-specification|" + existingVersion, "http://ersd.aimsplatform.org/fhir/Library/release-rctc|" + existingVersion, @@ -199,11 +204,11 @@ void visitLibraryTest() { @Test void releaseResource_force_version() { Bundle bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream("Bundle-small-approved-draft.json")); + ReleaseVisitorTests.class.getResourceAsStream("Bundle-small-approved-draft.json")); spyRepository.transaction(bundle); // Existing version should be "1.2.3"; String newVersionToForce = "1.2.7"; - KnowledgeArtifactReleaseVisitor releaseVisitor = new KnowledgeArtifactReleaseVisitor(); + ReleaseVisitor releaseVisitor = new ReleaseVisitor(); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -225,15 +230,13 @@ void releaseResource_force_version() { @Test void releaseResource_require_non_experimental_error() { - // SpecificationLibrary - root is experimentalbut HAS experimental children - Bundle bundle = - (Bundle) jsonParser.parseResource(KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream( - "Bundle-small-approved-draft-experimental.json")); + // SpecificationLibrary - root is experimental but HAS experimental children + Bundle bundle = (Bundle) jsonParser.parseResource( + ReleaseVisitorTests.class.getResourceAsStream("Bundle-small-approved-draft-experimental.json")); spyRepository.transaction(bundle); // SpecificationLibrary2 - root is NOT experimental but HAS experimental children - Bundle bundle2 = - (Bundle) jsonParser.parseResource(KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream( - "Bundle-small-approved-draft-experimental-children.json")); + Bundle bundle2 = (Bundle) jsonParser.parseResource(ReleaseVisitorTests.class.getResourceAsStream( + "Bundle-small-approved-draft-experimental-children.json")); spyRepository.transaction(bundle2); Parameters params = parameters( part("version", new StringType("1.2.3")), @@ -241,7 +244,7 @@ void releaseResource_require_non_experimental_error() { part("requireNonExperimental", new CodeType("error"))); Exception notExpectingAnyException = null; // no Exception if root is experimental - KnowledgeArtifactReleaseVisitor releaseVisitor = new KnowledgeArtifactReleaseVisitor(); + ReleaseVisitor releaseVisitor = new ReleaseVisitor(); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -270,17 +273,15 @@ void releaseResource_require_non_experimental_error() { @Test void releaseResource_require_non_experimental_warn() { // SpecificationLibrary - root is experimentalbut HAS experimental children - Bundle bundle = - (Bundle) jsonParser.parseResource(KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream( - "Bundle-small-approved-draft-experimental.json")); + Bundle bundle = (Bundle) jsonParser.parseResource( + ReleaseVisitorTests.class.getResourceAsStream("Bundle-small-approved-draft-experimental.json")); spyRepository.transaction(bundle); // SpecificationLibrary2 - root is NOT experimental but HAS experimental children - Bundle bundle2 = - (Bundle) jsonParser.parseResource(KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream( - "Bundle-small-approved-draft-experimental-children.json")); + Bundle bundle2 = (Bundle) jsonParser.parseResource(ReleaseVisitorTests.class.getResourceAsStream( + "Bundle-small-approved-draft-experimental-children.json")); spyRepository.transaction(bundle2); - KnowledgeArtifactReleaseVisitor releaseVisitor = new KnowledgeArtifactReleaseVisitor(); + ReleaseVisitor releaseVisitor = new ReleaseVisitor(); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -330,15 +331,14 @@ void releaseResource_require_non_experimental_warn() { @Test void releaseResource_propagate_effective_period() { - Bundle bundle = - (Bundle) jsonParser.parseResource(KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream( - "Bundle-ersd-no-child-effective-period.json")); + Bundle bundle = (Bundle) jsonParser.parseResource( + ReleaseVisitorTests.class.getResourceAsStream("Bundle-ersd-no-child-effective-period.json")); spyRepository.transaction(bundle); String effectivePeriodToPropagate = "2020-12-11"; Parameters params = parameters(part("version", new StringType("1.2.7")), part("versionBehavior", new CodeType("default"))); - KnowledgeArtifactReleaseVisitor releaseVisitor = new KnowledgeArtifactReleaseVisitor(); + ReleaseVisitor releaseVisitor = new ReleaseVisitor(); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -368,7 +368,7 @@ void releaseResource_propagate_effective_period() { @Test void releaseResource_latestFromTx_NotSupported_test() { Bundle bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream("Bundle-small-approved-draft.json")); + ReleaseVisitorTests.class.getResourceAsStream("Bundle-small-approved-draft.json")); spyRepository.transaction(bundle); String actualErrorMessage = ""; @@ -377,7 +377,7 @@ void releaseResource_latestFromTx_NotSupported_test() { part("version", "1.2.3"), part("versionBehavior", new CodeType("default")), part("latestFromTxServer", new BooleanType(true))); - KnowledgeArtifactReleaseVisitor releaseVisitor = new KnowledgeArtifactReleaseVisitor(); + ReleaseVisitor releaseVisitor = new ReleaseVisitor(); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -393,16 +393,15 @@ void releaseResource_latestFromTx_NotSupported_test() { @Test void release_missing_approvalDate_validation_test() { - Bundle bundle = - (Bundle) jsonParser.parseResource(KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream( - "Bundle-release-missing-approvalDate.json")); + Bundle bundle = (Bundle) jsonParser.parseResource( + ReleaseVisitorTests.class.getResourceAsStream("Bundle-release-missing-approvalDate.json")); spyRepository.transaction(bundle); String versionData = "1.2.3"; String actualErrorMessage = ""; Parameters params1 = parameters(part("version", versionData), part("versionBehavior", new CodeType("default"))); - KnowledgeArtifactReleaseVisitor releaseVisitor = new KnowledgeArtifactReleaseVisitor(); + ReleaseVisitor releaseVisitor = new ReleaseVisitor(); Library library = spyRepository .read(Library.class, new IdType("Library/ReleaseSpecificationLibrary")) .copy(); @@ -418,9 +417,9 @@ void release_missing_approvalDate_validation_test() { @Test void release_version_format_test() { Bundle bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream("Bundle-small-approved-draft.json")); + ReleaseVisitorTests.class.getResourceAsStream("Bundle-small-approved-draft.json")); spyRepository.transaction(bundle); - KnowledgeArtifactReleaseVisitor releaseVisitor = new KnowledgeArtifactReleaseVisitor(); + ReleaseVisitor releaseVisitor = new ReleaseVisitor(); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -442,10 +441,10 @@ void release_version_format_test() { @Test void release_releaseLabel_test() { Bundle bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream("Bundle-small-approved-draft.json")); + ReleaseVisitorTests.class.getResourceAsStream("Bundle-small-approved-draft.json")); spyRepository.transaction(bundle); String releaseLabel = "release label test"; - KnowledgeArtifactReleaseVisitor releaseVisitor = new KnowledgeArtifactReleaseVisitor(); + ReleaseVisitor releaseVisitor = new ReleaseVisitor(); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -472,9 +471,9 @@ void release_releaseLabel_test() { @Test void release_version_active_test() { Bundle bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream("Bundle-ersd-small-active.json")); + ReleaseVisitorTests.class.getResourceAsStream("Bundle-ersd-small-active.json")); spyRepository.transaction(bundle); - KnowledgeArtifactReleaseVisitor releaseVisitor = new KnowledgeArtifactReleaseVisitor(); + ReleaseVisitor releaseVisitor = new ReleaseVisitor(); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -494,9 +493,9 @@ void release_version_active_test() { @Test void release_versionBehaviour_format_test() { Bundle bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream("Bundle-small-approved-draft.json")); + ReleaseVisitorTests.class.getResourceAsStream("Bundle-small-approved-draft.json")); spyRepository.transaction(bundle); - KnowledgeArtifactReleaseVisitor releaseVisitor = new KnowledgeArtifactReleaseVisitor(); + ReleaseVisitor releaseVisitor = new ReleaseVisitor(); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -520,13 +519,13 @@ void release_versionBehaviour_format_test() { @Test void release_preserves_extensions() { var bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream("Bundle-small-approved-draft.json")); + ReleaseVisitorTests.class.getResourceAsStream("Bundle-small-approved-draft.json")); spyRepository.transaction(bundle); - var releaseVisitor = new KnowledgeArtifactReleaseVisitor(); - var orginalLibrary = spyRepository + var releaseVisitor = new ReleaseVisitor(); + var originalLibrary = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); - var testLibrary = orginalLibrary.copy(); + var testLibrary = originalLibrary.copy(); var libraryAdapter = new AdapterFactory().createLibrary(testLibrary); var params = parameters(part("version", new StringType("1.2.3")), part("versionBehavior", new CodeType("force"))); @@ -537,7 +536,7 @@ void release_preserves_extensions() { assertTrue(maybeLib.isPresent()); var releasedLibrary = spyRepository.read( Library.class, new IdType(maybeLib.get().getResponse().getLocation())); - for (final var originalRelatedArtifact : orginalLibrary.getRelatedArtifact()) { + for (final var originalRelatedArtifact : originalLibrary.getRelatedArtifact()) { releasedLibrary.getRelatedArtifact().forEach(releasedRelatedArtifact -> { if (Canonicals.getUrl(releasedRelatedArtifact.getResource()) .equals(Canonicals.getUrl(originalRelatedArtifact.getResource())) diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r5/KnowledgeArtifactApproveVisitorTest.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r5/ApproveVisitorTest.java similarity index 93% rename from cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r5/KnowledgeArtifactApproveVisitorTest.java rename to cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r5/ApproveVisitorTest.java index 0d4e8b0ed..629badd0e 100644 --- a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r5/KnowledgeArtifactApproveVisitorTest.java +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r5/ApproveVisitorTest.java @@ -37,17 +37,17 @@ import org.opencds.cqf.fhir.utility.adapter.LibraryAdapter; import org.opencds.cqf.fhir.utility.adapter.r5.AdapterFactory; import org.opencds.cqf.fhir.utility.repository.InMemoryFhirRepository; -import org.opencds.cqf.fhir.utility.visitor.KnowledgeArtifactApproveVisitor; +import org.opencds.cqf.fhir.utility.visitor.ApproveVisitor; -class KnowledgeArtifactApproveVisitorTest { +class ApproveVisitorTest { private final FhirContext fhirContext = FhirContext.forR5Cached(); private Repository spyRepository; private final IParser jsonParser = fhirContext.newJsonParser(); @BeforeEach void setup() { - var lib = (Library) jsonParser.parseResource( - KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream("Library-ersd-active.json")); + var lib = (Library) + jsonParser.parseResource(ReleaseVisitorTests.class.getResourceAsStream("Library-ersd-active.json")); spyRepository = spy(new InMemoryFhirRepository(fhirContext)); spyRepository.update(lib); doAnswer(new Answer() { @@ -66,7 +66,7 @@ void approveOperation_endpoint_id_should_match_target_parameter() { var artifactAssessmentTarget = "Library/This-Library-Does-Not-Exist|1.0.0"; var params = parameters(part("artifactAssessmentTarget", new CanonicalType(artifactAssessmentTarget))); UnprocessableEntityException maybeException = null; - var releaseVisitor = new KnowledgeArtifactApproveVisitor(); + var releaseVisitor = new ApproveVisitor(); var lib = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -96,7 +96,7 @@ void approveOperation_should_respect_artifactAssessment_information_type_binding String artifactAssessmentType = "this-type-does-not-exist"; Parameters params = parameters(part("artifactAssessmentType", new CodeType(artifactAssessmentType))); UnprocessableEntityException maybeException = null; - KnowledgeArtifactApproveVisitor releaseVisitor = new KnowledgeArtifactApproveVisitor(); + ApproveVisitor releaseVisitor = new ApproveVisitor(); Library lib = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -112,8 +112,8 @@ void approveOperation_should_respect_artifactAssessment_information_type_binding @Test void approveOperation_test() { - var practitioner = (Practitioner) jsonParser.parseResource( - KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream("Practitioner-minimal.json")); + var practitioner = (Practitioner) + jsonParser.parseResource(ReleaseVisitorTests.class.getResourceAsStream("Practitioner-minimal.json")); spyRepository.update(practitioner); Date today = new Date(); // get today's date in the form "2023-05-11" @@ -130,7 +130,7 @@ void approveOperation_test() { part("artifactAssessmentTarget", new CanonicalType(artifactAssessmentTarget)), part("artifactAssessmentRelatedArtifact", new CanonicalType(artifactAssessmentRelatedArtifact)), part("artifactAssessmentAuthor", new Reference(artifactAssessmentAuthor))); - KnowledgeArtifactApproveVisitor approveVisitor = new KnowledgeArtifactApproveVisitor(); + ApproveVisitor approveVisitor = new ApproveVisitor(); Library lib = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r5/KnowledgeArtifactDraftVisitorTests.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r5/DraftVisitorTests.java similarity index 85% rename from cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r5/KnowledgeArtifactDraftVisitorTests.java rename to cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r5/DraftVisitorTests.java index b4b422ccf..02c62c5e7 100644 --- a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r5/KnowledgeArtifactDraftVisitorTests.java +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r5/DraftVisitorTests.java @@ -41,10 +41,10 @@ import org.opencds.cqf.fhir.utility.adapter.r5.AdapterFactory; import org.opencds.cqf.fhir.utility.r5.MetadataResourceHelper; import org.opencds.cqf.fhir.utility.repository.InMemoryFhirRepository; -import org.opencds.cqf.fhir.utility.visitor.KnowledgeArtifactDraftVisitor; +import org.opencds.cqf.fhir.utility.visitor.DraftVisitor; import org.opencds.cqf.fhir.utility.visitor.KnowledgeArtifactVisitor; -class KnowledgeArtifactDraftVisitorTests { +class DraftVisitorTests { private final FhirContext fhirContext = FhirContext.forR5Cached(); private Repository spyRepository; private final IParser jsonParser = fhirContext.newJsonParser(); @@ -83,10 +83,10 @@ public Bundle answer(InvocationOnMock a) throws Throwable { @Test void library_draft_test() { - Bundle bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactDraftVisitorTests.class.getResourceAsStream("Bundle-ersd-example.json")); + Bundle bundle = (Bundle) + jsonParser.parseResource(DraftVisitorTests.class.getResourceAsStream("Bundle-ersd-example.json")); spyRepository.transaction(bundle); - KnowledgeArtifactVisitor draftVisitor = new KnowledgeArtifactDraftVisitor(); + KnowledgeArtifactVisitor draftVisitor = new DraftVisitor(); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -137,15 +137,15 @@ void library_draft_test() { @Test void draftOperation_no_effectivePeriod_test() { - Bundle bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactDraftVisitorTests.class.getResourceAsStream("Bundle-ersd-example.json")); + Bundle bundle = (Bundle) + jsonParser.parseResource(DraftVisitorTests.class.getResourceAsStream("Bundle-ersd-example.json")); spyRepository.transaction(bundle); Library baseLib = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); assertTrue(baseLib.hasEffectivePeriod()); LibraryAdapter libraryAdapter = new AdapterFactory().createLibrary(baseLib); - KnowledgeArtifactVisitor draftVisitor = new KnowledgeArtifactDraftVisitor(); + KnowledgeArtifactVisitor draftVisitor = new DraftVisitor(); PlanDefinition planDef = spyRepository .read(PlanDefinition.class, new IdType("PlanDefinition/plandefinition-ersd-instance-example")) .copy(); @@ -166,10 +166,10 @@ void draftOperation_no_effectivePeriod_test() { @Test void draftOperation_version_conflict_test() { - Bundle bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactDraftVisitorTests.class.getResourceAsStream("Bundle-ersd-example.json")); - Library versionConflictLibrary = (Library) jsonParser.parseResource( - KnowledgeArtifactDraftVisitorTests.class.getResourceAsStream("Library-version-conflict.json")); + Bundle bundle = (Bundle) + jsonParser.parseResource(DraftVisitorTests.class.getResourceAsStream("Bundle-ersd-example.json")); + Library versionConflictLibrary = (Library) + jsonParser.parseResource(DraftVisitorTests.class.getResourceAsStream("Library-version-conflict.json")); spyRepository.transaction(bundle); spyRepository.update(versionConflictLibrary); Parameters params = parameters(part("version", "1.0.0")); @@ -178,7 +178,7 @@ void draftOperation_version_conflict_test() { .read(Library.class, new IdType(specificationLibReference)) .copy(); LibraryAdapter libraryAdapter = new AdapterFactory().createLibrary(baseLib); - KnowledgeArtifactVisitor draftVisitor = new KnowledgeArtifactDraftVisitor(); + KnowledgeArtifactVisitor draftVisitor = new DraftVisitor(); try { libraryAdapter.accept(draftVisitor, spyRepository, params); @@ -192,8 +192,8 @@ void draftOperation_version_conflict_test() { @Test void draftOperation_cannot_create_draft_of_draft_test() { - Library versionConflictLibrary = (Library) jsonParser.parseResource( - KnowledgeArtifactDraftVisitorTests.class.getResourceAsStream("Library-version-conflict.json")); + Library versionConflictLibrary = (Library) + jsonParser.parseResource(DraftVisitorTests.class.getResourceAsStream("Library-version-conflict.json")); spyRepository.update(versionConflictLibrary); Parameters params = parameters(part("version", "1.2.1")); String maybeException = ""; @@ -201,7 +201,7 @@ void draftOperation_cannot_create_draft_of_draft_test() { .read(Library.class, new IdType("Library/SpecificationLibraryDraftVersion-1-0-0-23")) .copy(); LibraryAdapter libraryAdapter = new AdapterFactory().createLibrary(baseLib); - KnowledgeArtifactVisitor draftVisitor = new KnowledgeArtifactDraftVisitor(); + KnowledgeArtifactVisitor draftVisitor = new DraftVisitor(); try { libraryAdapter.accept(draftVisitor, spyRepository, params); } catch (PreconditionFailedException e) { @@ -213,14 +213,14 @@ void draftOperation_cannot_create_draft_of_draft_test() { @Test void draftOperation_version_format_test() { - Library versionConflictLibrary = (Library) jsonParser.parseResource( - KnowledgeArtifactDraftVisitorTests.class.getResourceAsStream("Library-version-conflict.json")); + Library versionConflictLibrary = (Library) + jsonParser.parseResource(DraftVisitorTests.class.getResourceAsStream("Library-version-conflict.json")); spyRepository.update(versionConflictLibrary); Library baseLib = spyRepository .read(Library.class, new IdType("Library/SpecificationLibraryDraftVersion-1-0-0-23")) .copy(); LibraryAdapter libraryAdapter = new AdapterFactory().createLibrary(baseLib); - KnowledgeArtifactVisitor draftVisitor = new KnowledgeArtifactDraftVisitor(); + KnowledgeArtifactVisitor draftVisitor = new DraftVisitor(); for (String version : badVersionList) { UnprocessableEntityException maybeException = null; diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r5/KnowledgeArtifactPackageVisitorTests.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r5/PackageVisitorTests.java similarity index 89% rename from cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r5/KnowledgeArtifactPackageVisitorTests.java rename to cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r5/PackageVisitorTests.java index f8545d981..15f0f7fe0 100644 --- a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r5/KnowledgeArtifactPackageVisitorTests.java +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r5/PackageVisitorTests.java @@ -36,6 +36,7 @@ import org.hl7.fhir.r5.model.StringType; import org.hl7.fhir.r5.model.ValueSet; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; @@ -44,9 +45,9 @@ import org.opencds.cqf.fhir.utility.adapter.LibraryAdapter; import org.opencds.cqf.fhir.utility.adapter.r5.AdapterFactory; import org.opencds.cqf.fhir.utility.repository.InMemoryFhirRepository; -import org.opencds.cqf.fhir.utility.visitor.KnowledgeArtifactPackageVisitor; +import org.opencds.cqf.fhir.utility.visitor.PackageVisitor; -class KnowledgeArtifactPackageVisitorTests { +class PackageVisitorTests { private final FhirContext fhirContext = FhirContext.forR5Cached(); private final IParser jsonParser = fhirContext.newJsonParser(); private Repository spyRepository; @@ -68,9 +69,9 @@ public Bundle answer(InvocationOnMock a) throws Throwable { @Test void visitLibraryTest() { Bundle loadedBundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactPackageVisitorTests.class.getResourceAsStream("Bundle-ersd-example-naive.json")); + PackageVisitorTests.class.getResourceAsStream("Bundle-ersd-example-naive.json")); spyRepository.transaction(loadedBundle); - KnowledgeArtifactPackageVisitor packageVisitor = new KnowledgeArtifactPackageVisitor(); + PackageVisitor packageVisitor = new PackageVisitor(fhirContext); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -98,11 +99,12 @@ void visitLibraryTest() { } @Test + @Disabled("This test needs a ValueSet that cannot be naively expanded") void packageOperation_should_fail_no_credentials() { - Bundle loadedBundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactPackageVisitorTests.class.getResourceAsStream("Bundle-ersd-example.json")); + Bundle loadedBundle = (Bundle) + jsonParser.parseResource(PackageVisitorTests.class.getResourceAsStream("Bundle-ersd-example.json")); spyRepository.transaction(loadedBundle); - KnowledgeArtifactPackageVisitor packageVisitor = new KnowledgeArtifactPackageVisitor(); + PackageVisitor packageVisitor = new PackageVisitor(fhirContext); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -119,11 +121,12 @@ void packageOperation_should_fail_no_credentials() { } @Test + @Disabled("This test needs a ValueSet that cannot be naively expanded") void packageOperation_should_fail_credentials_missing_username() { - Bundle loadedBundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactPackageVisitorTests.class.getResourceAsStream("Bundle-ersd-example.json")); + Bundle loadedBundle = (Bundle) + jsonParser.parseResource(PackageVisitorTests.class.getResourceAsStream("Bundle-ersd-example.json")); spyRepository.transaction(loadedBundle); - KnowledgeArtifactPackageVisitor packageVisitor = new KnowledgeArtifactPackageVisitor(); + PackageVisitor packageVisitor = new PackageVisitor(fhirContext); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -144,11 +147,12 @@ void packageOperation_should_fail_credentials_missing_username() { } @Test + @Disabled("This test needs a ValueSet that cannot be naively expanded") void packageOperation_should_fail_credentials_missing_apikey() { - Bundle loadedBundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactPackageVisitorTests.class.getResourceAsStream("Bundle-ersd-example.json")); + Bundle loadedBundle = (Bundle) + jsonParser.parseResource(PackageVisitorTests.class.getResourceAsStream("Bundle-ersd-example.json")); spyRepository.transaction(loadedBundle); - KnowledgeArtifactPackageVisitor packageVisitor = new KnowledgeArtifactPackageVisitor(); + PackageVisitor packageVisitor = new PackageVisitor(fhirContext); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -169,11 +173,12 @@ void packageOperation_should_fail_credentials_missing_apikey() { } @Test + @Disabled("This test needs a ValueSet that cannot be naively expanded") void packageOperation_should_fail_credentials_invalid() { - Bundle loadedBundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactPackageVisitorTests.class.getResourceAsStream("Bundle-ersd-example.json")); + Bundle loadedBundle = (Bundle) + jsonParser.parseResource(PackageVisitorTests.class.getResourceAsStream("Bundle-ersd-example.json")); spyRepository.transaction(loadedBundle); - KnowledgeArtifactPackageVisitor packageVisitor = new KnowledgeArtifactPackageVisitor(); + PackageVisitor packageVisitor = new PackageVisitor(fhirContext); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -196,12 +201,11 @@ void packageOperation_should_fail_credentials_invalid() { @Test void packageOperation_should_fail_non_matching_capability() { - Bundle bundle = - (Bundle) jsonParser.parseResource(KnowledgeArtifactPackageVisitorTests.class.getResourceAsStream( - "Bundle-ersd-package-capabilities.json")); + Bundle bundle = (Bundle) jsonParser.parseResource( + PackageVisitorTests.class.getResourceAsStream("Bundle-ersd-package-capabilities.json")); spyRepository.transaction(bundle); List capabilities = Arrays.asList("computable", "publishable", "executable"); - KnowledgeArtifactPackageVisitor packageVisitor = new KnowledgeArtifactPackageVisitor(); + PackageVisitor packageVisitor = new PackageVisitor(fhirContext); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -231,9 +235,9 @@ void packageOperation_should_fail_non_matching_capability() { @Test void packageOperation_should_apply_check_force_canonicalVersions() { Bundle bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactPackageVisitorTests.class.getResourceAsStream("Bundle-active-no-versions.json")); + PackageVisitorTests.class.getResourceAsStream("Bundle-active-no-versions.json")); spyRepository.transaction(bundle); - KnowledgeArtifactPackageVisitor packageVisitor = new KnowledgeArtifactPackageVisitor(); + PackageVisitor packageVisitor = new PackageVisitor(fhirContext); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -293,9 +297,9 @@ void packageOperation_should_apply_check_force_canonicalVersions() { @Test void packageOperation_should_respect_count_offset() { Bundle bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactPackageVisitorTests.class.getResourceAsStream("Bundle-ersd-small-active.json")); + PackageVisitorTests.class.getResourceAsStream("Bundle-ersd-small-active.json")); spyRepository.transaction(bundle); - KnowledgeArtifactPackageVisitor packageVisitor = new KnowledgeArtifactPackageVisitor(); + PackageVisitor packageVisitor = new PackageVisitor(fhirContext); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -331,9 +335,9 @@ void packageOperation_should_respect_count_offset() { @Test void packageOperation_different_bundle_types() { Bundle bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactPackageVisitorTests.class.getResourceAsStream("Bundle-ersd-small-active.json")); + PackageVisitorTests.class.getResourceAsStream("Bundle-ersd-small-active.json")); spyRepository.transaction(bundle); - KnowledgeArtifactPackageVisitor packageVisitor = new KnowledgeArtifactPackageVisitor(); + PackageVisitor packageVisitor = new PackageVisitor(fhirContext); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -370,9 +374,9 @@ void packageOperation_different_bundle_types() { @Test void packageOperation_should_conditionally_create() { Bundle bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactPackageVisitorTests.class.getResourceAsStream("Bundle-ersd-small-active.json")); + PackageVisitorTests.class.getResourceAsStream("Bundle-ersd-small-active.json")); spyRepository.transaction(bundle); - KnowledgeArtifactPackageVisitor packageVisitor = new KnowledgeArtifactPackageVisitor(); + PackageVisitor packageVisitor = new PackageVisitor(fhirContext); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -390,9 +394,9 @@ void packageOperation_should_conditionally_create() { @Test void packageOperation_should_respect_include() { Bundle bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactPackageVisitorTests.class.getResourceAsStream("Bundle-ersd-small-active.json")); + PackageVisitorTests.class.getResourceAsStream("Bundle-ersd-small-active.json")); spyRepository.transaction(bundle); - KnowledgeArtifactPackageVisitor packageVisitor = new KnowledgeArtifactPackageVisitor(); + PackageVisitor packageVisitor = new PackageVisitor(fhirContext); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r5/KnowledgeArtifactReleaseVisitorTests.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r5/ReleaseVisitorTests.java similarity index 88% rename from cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r5/KnowledgeArtifactReleaseVisitorTests.java rename to cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r5/ReleaseVisitorTests.java index f470d2aeb..a4d969257 100644 --- a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r5/KnowledgeArtifactReleaseVisitorTests.java +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r5/ReleaseVisitorTests.java @@ -48,10 +48,10 @@ import org.opencds.cqf.fhir.utility.adapter.r5.AdapterFactory; import org.opencds.cqf.fhir.utility.r5.MetadataResourceHelper; import org.opencds.cqf.fhir.utility.repository.InMemoryFhirRepository; -import org.opencds.cqf.fhir.utility.visitor.KnowledgeArtifactReleaseVisitor; +import org.opencds.cqf.fhir.utility.visitor.ReleaseVisitor; import org.slf4j.LoggerFactory; -class KnowledgeArtifactReleaseVisitorTests { +class ReleaseVisitorTests { private final FhirContext fhirContext = FhirContext.forR5Cached(); private Repository spyRepository; private final IParser jsonParser = fhirContext.newJsonParser(); @@ -75,9 +75,8 @@ class KnowledgeArtifactReleaseVisitorTests { @BeforeEach void setup() { - SearchParameter sp = (SearchParameter) - jsonParser.parseResource(KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream( - "SearchParameter-artifactAssessment.json")); + SearchParameter sp = (SearchParameter) jsonParser.parseResource( + ReleaseVisitorTests.class.getResourceAsStream("SearchParameter-artifactAssessment.json")); spyRepository = spy(new InMemoryFhirRepository(fhirContext)); spyRepository.update(sp); doAnswer(new Answer() { @@ -94,9 +93,9 @@ public Bundle answer(InvocationOnMock a) throws Throwable { @Test void visitLibraryTest() { Bundle bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream("Bundle-ersd-release-bundle.json")); + ReleaseVisitorTests.class.getResourceAsStream("Bundle-ersd-release-bundle.json")); spyRepository.transaction(bundle); - KnowledgeArtifactReleaseVisitor releaseVisitor = new KnowledgeArtifactReleaseVisitor(); + ReleaseVisitor releaseVisitor = new ReleaseVisitor(); Library library = spyRepository .read(Library.class, new IdType("Library/ReleaseSpecificationLibrary")) .copy(); @@ -167,7 +166,13 @@ void visitLibraryTest() { "http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-lab", "http://hl7.org/fhir/us/core/StructureDefinition/us-core-diagnosticreport-lab", "http://hl7.org/fhir/us/ecr/StructureDefinition/eicr-document-bundle", - "http://hl7.org/fhir/StructureDefinition/ServiceRequest"); + "http://hl7.org/fhir/StructureDefinition/ServiceRequest", + "http://hl7.org/fhir/us/ecr/StructureDefinition/ersd-valueset-library", + "http://hl7.org/fhir/us/ecr/StructureDefinition/us-ph-triggering-valueset-library", + "http://hl7.org/fhir/us/ecr/StructureDefinition/ersd-plandefinition", + "http://hl7.org/fhir/us/ecr/StructureDefinition/us-ph-plandefinition", + "http://hl7.org/fhir/us/ecr/StructureDefinition/ersd-valueset", + "http://hl7.org/fhir/us/ecr/StructureDefinition/us-ph-triggering-valueset"); var expectedErsdTestArtifactComponents = Arrays.asList( "http://ersd.aimsplatform.org/fhir/PlanDefinition/release-us-ecr-specification|" + existingVersion, "http://ersd.aimsplatform.org/fhir/Library/release-rctc|" + existingVersion, @@ -196,11 +201,11 @@ void visitLibraryTest() { @Test void releaseResource_force_version() { Bundle bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream("Bundle-small-approved-draft.json")); + ReleaseVisitorTests.class.getResourceAsStream("Bundle-small-approved-draft.json")); spyRepository.transaction(bundle); // Existing version should be "1.2.3"; String newVersionToForce = "1.2.7"; - KnowledgeArtifactReleaseVisitor releaseVisitor = new KnowledgeArtifactReleaseVisitor(); + ReleaseVisitor releaseVisitor = new ReleaseVisitor(); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -222,15 +227,13 @@ void releaseResource_force_version() { @Test void releaseResource_require_non_experimental_error() { - // SpecificationLibrary - root is experimentalbut HAS experimental children - Bundle bundle = - (Bundle) jsonParser.parseResource(KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream( - "Bundle-small-approved-draft-experimental.json")); + // SpecificationLibrary - root is experimental but HAS experimental children + Bundle bundle = (Bundle) jsonParser.parseResource( + ReleaseVisitorTests.class.getResourceAsStream("Bundle-small-approved-draft-experimental.json")); spyRepository.transaction(bundle); // SpecificationLibrary2 - root is NOT experimental but HAS experimental children - Bundle bundle2 = - (Bundle) jsonParser.parseResource(KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream( - "Bundle-small-approved-draft-experimental-children.json")); + Bundle bundle2 = (Bundle) jsonParser.parseResource(ReleaseVisitorTests.class.getResourceAsStream( + "Bundle-small-approved-draft-experimental-children.json")); spyRepository.transaction(bundle2); Parameters params = parameters( part("version", new StringType("1.2.3")), @@ -238,7 +241,7 @@ void releaseResource_require_non_experimental_error() { part("requireNonExperimental", new CodeType("error"))); Exception notExpectingAnyException = null; // no Exception if root is experimental - KnowledgeArtifactReleaseVisitor releaseVisitor = new KnowledgeArtifactReleaseVisitor(); + ReleaseVisitor releaseVisitor = new ReleaseVisitor(); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -267,17 +270,15 @@ void releaseResource_require_non_experimental_error() { @Test void releaseResource_require_non_experimental_warn() { // SpecificationLibrary - root is experimentalbut HAS experimental children - Bundle bundle = - (Bundle) jsonParser.parseResource(KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream( - "Bundle-small-approved-draft-experimental.json")); + Bundle bundle = (Bundle) jsonParser.parseResource( + ReleaseVisitorTests.class.getResourceAsStream("Bundle-small-approved-draft-experimental.json")); spyRepository.transaction(bundle); // SpecificationLibrary2 - root is NOT experimental but HAS experimental children - Bundle bundle2 = - (Bundle) jsonParser.parseResource(KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream( - "Bundle-small-approved-draft-experimental-children.json")); + Bundle bundle2 = (Bundle) jsonParser.parseResource(ReleaseVisitorTests.class.getResourceAsStream( + "Bundle-small-approved-draft-experimental-children.json")); spyRepository.transaction(bundle2); - KnowledgeArtifactReleaseVisitor releaseVisitor = new KnowledgeArtifactReleaseVisitor(); + ReleaseVisitor releaseVisitor = new ReleaseVisitor(); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -327,15 +328,14 @@ void releaseResource_require_non_experimental_warn() { @Test void releaseResource_propagate_effective_period() { - Bundle bundle = - (Bundle) jsonParser.parseResource(KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream( - "Bundle-ersd-no-child-effective-period.json")); + Bundle bundle = (Bundle) jsonParser.parseResource( + ReleaseVisitorTests.class.getResourceAsStream("Bundle-ersd-no-child-effective-period.json")); spyRepository.transaction(bundle); String effectivePeriodToPropagate = "2020-12-11"; Parameters params = parameters(part("version", new StringType("1.2.7")), part("versionBehavior", new CodeType("default"))); - KnowledgeArtifactReleaseVisitor releaseVisitor = new KnowledgeArtifactReleaseVisitor(); + ReleaseVisitor releaseVisitor = new ReleaseVisitor(); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -365,7 +365,7 @@ void releaseResource_propagate_effective_period() { @Test void releaseResource_latestFromTx_NotSupported_test() { Bundle bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream("Bundle-small-approved-draft.json")); + ReleaseVisitorTests.class.getResourceAsStream("Bundle-small-approved-draft.json")); spyRepository.transaction(bundle); String actualErrorMessage = ""; @@ -374,7 +374,7 @@ void releaseResource_latestFromTx_NotSupported_test() { part("version", "1.2.3"), part("versionBehavior", new CodeType("default")), part("latestFromTxServer", new BooleanType(true))); - KnowledgeArtifactReleaseVisitor releaseVisitor = new KnowledgeArtifactReleaseVisitor(); + ReleaseVisitor releaseVisitor = new ReleaseVisitor(); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -390,16 +390,15 @@ void releaseResource_latestFromTx_NotSupported_test() { @Test void release_missing_approvalDate_validation_test() { - Bundle bundle = - (Bundle) jsonParser.parseResource(KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream( - "Bundle-release-missing-approvalDate.json")); + Bundle bundle = (Bundle) jsonParser.parseResource( + ReleaseVisitorTests.class.getResourceAsStream("Bundle-release-missing-approvalDate.json")); spyRepository.transaction(bundle); String versionData = "1.2.3"; String actualErrorMessage = ""; Parameters params1 = parameters(part("version", versionData), part("versionBehavior", new CodeType("default"))); - KnowledgeArtifactReleaseVisitor releaseVisitor = new KnowledgeArtifactReleaseVisitor(); + ReleaseVisitor releaseVisitor = new ReleaseVisitor(); Library library = spyRepository .read(Library.class, new IdType("Library/ReleaseSpecificationLibrary")) .copy(); @@ -415,9 +414,9 @@ void release_missing_approvalDate_validation_test() { @Test void release_version_format_test() { Bundle bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream("Bundle-small-approved-draft.json")); + ReleaseVisitorTests.class.getResourceAsStream("Bundle-small-approved-draft.json")); spyRepository.transaction(bundle); - KnowledgeArtifactReleaseVisitor releaseVisitor = new KnowledgeArtifactReleaseVisitor(); + ReleaseVisitor releaseVisitor = new ReleaseVisitor(); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -439,10 +438,10 @@ void release_version_format_test() { @Test void release_releaseLabel_test() { Bundle bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream("Bundle-small-approved-draft.json")); + ReleaseVisitorTests.class.getResourceAsStream("Bundle-small-approved-draft.json")); spyRepository.transaction(bundle); String releaseLabel = "release label test"; - KnowledgeArtifactReleaseVisitor releaseVisitor = new KnowledgeArtifactReleaseVisitor(); + ReleaseVisitor releaseVisitor = new ReleaseVisitor(); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -469,9 +468,9 @@ void release_releaseLabel_test() { @Test void release_version_active_test() { Bundle bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream("Bundle-ersd-small-active.json")); + ReleaseVisitorTests.class.getResourceAsStream("Bundle-ersd-small-active.json")); spyRepository.transaction(bundle); - KnowledgeArtifactReleaseVisitor releaseVisitor = new KnowledgeArtifactReleaseVisitor(); + ReleaseVisitor releaseVisitor = new ReleaseVisitor(); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy(); @@ -491,9 +490,9 @@ void release_version_active_test() { @Test void release_versionBehaviour_format_test() { Bundle bundle = (Bundle) jsonParser.parseResource( - KnowledgeArtifactReleaseVisitorTests.class.getResourceAsStream("Bundle-small-approved-draft.json")); + ReleaseVisitorTests.class.getResourceAsStream("Bundle-small-approved-draft.json")); spyRepository.transaction(bundle); - KnowledgeArtifactReleaseVisitor releaseVisitor = new KnowledgeArtifactReleaseVisitor(); + ReleaseVisitor releaseVisitor = new ReleaseVisitor(); Library library = spyRepository .read(Library.class, new IdType("Library/SpecificationLibrary")) .copy();