From 30f9d47c532c98a91b5ec2ffc3579930f2ec0071 Mon Sep 17 00:00:00 2001 From: Luke deGruchy Date: Mon, 6 Oct 2025 17:02:14 -0400 Subject: [PATCH 1/5] First commit. --- .../npm/NpmConfigDependencySubstitutor.java | 24 + .../fhir/utility/npm/NpmNamespaceManager.java | 16 + .../fhir/utility/npm/NpmPackageLoader.java | 266 +++++++++++ .../npm/R4NpmPackageLoaderInMemory.java | 377 +++++++++++++++ .../npm/BaseNpmResourceInfoForCqlTest.java | 440 ++++++++++++++++++ .../NpmConfigDependencySubstitutorTest.java | 29 ++ .../npm/r4/NpmResourceHolderR4Test.java | 385 +++++++++++++++ .../npm/r5/NpmResourceHolderR5Test.java | 268 +++++++++++ .../utility/npm/r4/crosspackagesource.tgz | Bin 0 -> 1614 bytes .../utility/npm/r4/crosspackagetarget.tgz | Bin 0 -> 1364 bytes .../cqf/fhir/utility/npm/r4/simplealpha.tgz | Bin 0 -> 1564 bytes .../cqf/fhir/utility/npm/r4/simplebravo.tgz | Bin 0 -> 1577 bytes .../utility/npm/r4/withderivedlibrary.tgz | Bin 0 -> 1821 bytes .../npm/r4/withtwolayersderivedlibraries.tgz | Bin 0 -> 2599 bytes .../utility/npm/r5/crosspackagesource.tgz | Bin 0 -> 1616 bytes .../utility/npm/r5/crosspackagetarget.tgz | Bin 0 -> 1361 bytes .../cqf/fhir/utility/npm/r5/simplealpha.tgz | Bin 0 -> 1546 bytes .../cqf/fhir/utility/npm/r5/simplebravo.tgz | Bin 0 -> 1576 bytes .../utility/npm/r5/withderivedlibrary.tgz | Bin 0 -> 1817 bytes .../npm/r5/withtwolayersderivedlibraries.tgz | Bin 0 -> 2598 bytes 20 files changed, 1805 insertions(+) create mode 100644 cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/NpmConfigDependencySubstitutor.java create mode 100644 cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/NpmNamespaceManager.java create mode 100644 cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/NpmPackageLoader.java create mode 100644 cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/R4NpmPackageLoaderInMemory.java create mode 100644 cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/BaseNpmResourceInfoForCqlTest.java create mode 100644 cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/NpmConfigDependencySubstitutorTest.java create mode 100644 cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/r4/NpmResourceHolderR4Test.java create mode 100644 cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/r5/NpmResourceHolderR5Test.java create mode 100644 cqf-fhir-utility/src/test/resources/org/opencds/cqf/fhir/utility/npm/r4/crosspackagesource.tgz create mode 100644 cqf-fhir-utility/src/test/resources/org/opencds/cqf/fhir/utility/npm/r4/crosspackagetarget.tgz create mode 100644 cqf-fhir-utility/src/test/resources/org/opencds/cqf/fhir/utility/npm/r4/simplealpha.tgz create mode 100644 cqf-fhir-utility/src/test/resources/org/opencds/cqf/fhir/utility/npm/r4/simplebravo.tgz create mode 100644 cqf-fhir-utility/src/test/resources/org/opencds/cqf/fhir/utility/npm/r4/withderivedlibrary.tgz create mode 100644 cqf-fhir-utility/src/test/resources/org/opencds/cqf/fhir/utility/npm/r4/withtwolayersderivedlibraries.tgz create mode 100644 cqf-fhir-utility/src/test/resources/org/opencds/cqf/fhir/utility/npm/r5/crosspackagesource.tgz create mode 100644 cqf-fhir-utility/src/test/resources/org/opencds/cqf/fhir/utility/npm/r5/crosspackagetarget.tgz create mode 100644 cqf-fhir-utility/src/test/resources/org/opencds/cqf/fhir/utility/npm/r5/simplealpha.tgz create mode 100644 cqf-fhir-utility/src/test/resources/org/opencds/cqf/fhir/utility/npm/r5/simplebravo.tgz create mode 100644 cqf-fhir-utility/src/test/resources/org/opencds/cqf/fhir/utility/npm/r5/withderivedlibrary.tgz create mode 100644 cqf-fhir-utility/src/test/resources/org/opencds/cqf/fhir/utility/npm/r5/withtwolayersderivedlibraries.tgz diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/NpmConfigDependencySubstitutor.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/NpmConfigDependencySubstitutor.java new file mode 100644 index 0000000000..b7197d46aa --- /dev/null +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/NpmConfigDependencySubstitutor.java @@ -0,0 +1,24 @@ +package org.opencds.cqf.fhir.utility.npm; + +import java.util.Optional; + +/** + * This class is meant to be used from Spring configuration classes, in the case of any missing + * NpmPackageLoader bean definitions, which Spring will inject as empty Optionals. + *

+ * Helps implement a migration from the old world of FHIR/Repository based resources for Libraries, + * Measures and eventually other clinical intelligence resources (such as PlanDefinitions or + * ValueSets), and the new world where they're derived from NPM packages. + * If Spring config is missing an instance of {@link NpmPackageLoader}, then * return the default + * instance. + */ +public class NpmConfigDependencySubstitutor { + + private NpmConfigDependencySubstitutor() { + // static utility class + } + + public static NpmPackageLoader substituteNpmPackageLoaderIfEmpty(Optional optNpmPackageLoader) { + return NpmPackageLoader.getDefaultIfEmpty(optNpmPackageLoader.orElse(null)); + } +} diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/NpmNamespaceManager.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/NpmNamespaceManager.java new file mode 100644 index 0000000000..c983a2d076 --- /dev/null +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/NpmNamespaceManager.java @@ -0,0 +1,16 @@ +package org.opencds.cqf.fhir.utility.npm; + +import java.util.List; +import org.hl7.cql.model.NamespaceInfo; + +/** + * Load all {@link NamespaceInfo}s capturing package ID to URL mappings associated with the NPM + * packages maintained for clinical-reasoning NPM package users to be used to resolve cross-package + * Library/CQL dependencies. See {@link NpmPackageLoader}. + */ +public interface NpmNamespaceManager { + + NpmNamespaceManager DEFAULT = List::of; + + List getAllNamespaceInfos(); +} diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/NpmPackageLoader.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/NpmPackageLoader.java new file mode 100644 index 0000000000..4ea89d92dc --- /dev/null +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/NpmPackageLoader.java @@ -0,0 +1,266 @@ +package org.opencds.cqf.fhir.utility.npm; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.repository.IRepository; +import jakarta.annotation.Nullable; +import java.util.List; +import java.util.Optional; +import org.cqframework.cql.cql2elm.LibraryManager; +import org.cqframework.cql.cql2elm.LibrarySourceProvider; +import org.hl7.cql.model.ModelIdentifier; +import org.hl7.cql.model.NamespaceInfo; +import org.hl7.cql.model.NamespaceManager; +import org.hl7.elm.r1.VersionedIdentifier; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IPrimitiveType; +import org.opencds.cqf.fhir.utility.adapter.ILibraryAdapter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +// LUKETODO: redo java +/** + * FHIR version agnostic Interface for loading NPM resources including Measures, Libraries and + * NpmPackages as captured within {@link NpmResourceHolder}. + *

+ * This javadoc documents the entire NPM package feature in the clinical-reasoning project. Please + * read below: + *

+ * A downstream app from clinical-reasoning will be able to maintain Measures and Libraries loaded + * from NPM packages. Such Measures and Libraries will, for those clients implementing this + * feature, no longer be maintained in {@link IRepository} storage, unlike all other FHIR resources, + * such as Patients. + *

+ * Downstream apps are responsible for loading and retrieving such packages from implementations + * of the below interface. Additionally, they must map all package IDs to package URLs via a + * List of {@link NamespaceInfo}s. This is done via the + * {@link #initNamespaceMappings(LibraryManager)}, as due to how CQL libraries are loaded, it + * won't work automatically. + *

+ * The {@link NpmResourceHolder} class is used to capture the results of query the NPM + * package with a given measure URL. It's effectively a container for the Measure, its directly + * associated Library, and its NPM package information. In theory, there could be more than one + * NPM package for a given Measure. When CQL runs and calls a custom {@link LibrarySourceProvider}, + * it will first check to see if the directly associated Library matches the provided + * {@link VersionedIdentifier}. If not, it will query all NPM packages within the + * R4NpmResourceInfoForCql to find the Library. And if there is still no match, it will pass the + * VersionedIdentifier, and build a URL from the system and ID before calling + * {@link #loadLibraryByUrl(String)} to load that Library from another package, with the + * VersionedIdentifier already resolved correctly with the help of the NamespaceInfos provided above. + * The implementor is responsible for implementing loadLibraryByUrl to properly return the Library + * from any packages maintained by the application. + *

+ * The above should also work with multiple layers of includes across packages. + *

+ * This workflow is meant to be triggered by a new Measure operation provider: + * $evaluate-measure-by-url, which takes a canonical measure URL instead of a measure ID like + * $evaluate-measure. + *

+ * Example: Package with ID X and URL ... contains Measure ABC + * is associated with Library 123, which contains CQL that includes Library 456 from NPM Package + * with ID Y and URL ..., which contains both the Library and + * its CQL. When resolve the CQL include pointing to Package ID Y, the CQL engine must be able + * to read the namespace info and resolve ID Y to URL .... This + * can only be accomplished via an explicit mapping. + *

+ * Note that there is the real possibility of Measures corresponding to the same canonical URL + * among multiple NPM packages. As such, clients who unintentionally add Measures with the same + * URL in at least two different packages may see the Measure they're not expecting during an + * $evaluate-measure-by-url, and may file production issues accordingly. This may be mitigated + * by new APIs in IHapiPackageCacheManager. + */ +public interface NpmPackageLoader { + Logger logger = LoggerFactory.getLogger(NpmPackageLoader.class); + String LIBRARY_URL_TEMPLATE = "%s/Library/%s"; + + // effectively a no-op implementation + NpmPackageLoader R4_DEFAULT = new NpmPackageLoader() { + + @Override + public Optional loadNpmResource(IPrimitiveType canonicalUrl) { + return Optional.empty(); + } + + @Override + public NpmNamespaceManager getNamespaceManager() { + return NpmNamespaceManager.DEFAULT; + } + + @Override + public FhirContext getFhirContext() { + return FhirContext.forR4Cached(); + } + }; + + // LUKETODO: redo java + /** + * @param resourceUrl The Measure URL provided by the caller, corresponding to a Measure contained + * withing one of the stored NPM packages. + * @return The Measure corresponding to the URL. + */ + default Optional loadNpmResource( + Class resourceClass, IPrimitiveType canonicalResourceUrl) { + final Optional optResource = loadNpmResource(canonicalResourceUrl); + + if (optResource.isEmpty()) { + return Optional.empty(); + } + + final IBaseResource resource = optResource.get(); + + if (!resourceClass.isInstance(resource)) { + throw new IllegalArgumentException("Expected resource to be a %s, but was a %s" + .formatted(resourceClass.getSimpleName(), resource.fhirType())); + } + + return Optional.of(resourceClass.cast(resource)); + } + + /** + * Obtain the resource corresponding to the provided type-enclosed String URL from the NPM + * package repo. Implementors must figure out how to retrieve the resource by querying one + * or more NPM packages maintained by the application. + * + * @param canonicalResourceUrl The type-enclosed String canonical URL of the resource to load. + * @return The resource corresponding to the URL, if it exists. + */ + Optional loadNpmResource(IPrimitiveType canonicalResourceUrl); + + /** + * Implementors must commit to supporting a specific FHIR version, in order to ensure that + * the correct version of a given resource is used for both the FHIR canonical URL and the + * returned resource. + * + * @return The FhirContext corresponding to the FHIR version of the NPM packages maintained + * by the application. + */ + FhirContext getFhirContext(); + + /** + * It's up to implementors to maintain the NamespaceManager that maintains the NamespaceInfos. + */ + NpmNamespaceManager getNamespaceManager(); + + /** + * Hackish: Either the downstream app injected this or we default to a NO-OP implementation. + * + * @param npmPackageLoader The NpmPackageLoader, if injected by the downstream app, + * otherwise null. + * @return Either the downstream app's NpmPackageLoader a no-op implementation. + */ + static NpmPackageLoader getDefaultIfEmpty(@Nullable NpmPackageLoader npmPackageLoader) { + return Optional.ofNullable(npmPackageLoader).orElse(NpmPackageLoader.R4_DEFAULT); + } + + /** + * Ensure the passed Library gets initialized with the NPM namespace mappings belonging + * to this instance of NpmPackageLoader. + * + * @param libraryManager from the CQL Engine being used for an evaluation + */ + default void initNamespaceMappings(LibraryManager libraryManager) { + final List allNamespaceInfos = getAllNamespaceInfos(); + final NamespaceManager namespaceManager = libraryManager.getNamespaceManager(); + + for (NamespaceInfo namespaceInfo : allNamespaceInfos) { + // if we do this more than one time it won't error out subsequent times + namespaceManager.ensureNamespaceRegistered(namespaceInfo); + } + } + + /** + * @return All NamespaceInfos to map package IDs to package URLs for all NPM Packages maintained + * for clinical-reasoning NPM package to be used to resolve cross-package Library/CQL + * dependencies. + */ + default List getAllNamespaceInfos() { + return getNamespaceManager().getAllNamespaceInfos(); + } + + default Optional findMatchingLibrary(VersionedIdentifier versionedIdentifier) { + return findLibraryFromUnrelatedNpmPackage(versionedIdentifier); + } + + default Optional findMatchingLibrary(ModelIdentifier modelIdentifier) { + return findLibraryFromUnrelatedNpmPackage(modelIdentifier); + } + + default Optional findLibraryFromUnrelatedNpmPackage(VersionedIdentifier versionedIdentifier) { + return loadLibraryByUrl(getUrl(versionedIdentifier)); + } + + default Optional findLibraryFromUnrelatedNpmPackage(ModelIdentifier modelIdentifier) { + return loadLibraryByUrl(getUrl(modelIdentifier)); + } + + /** + * @param libraryUrl The Library URL converted from a given + * withing one of the stored NPM packages. + * @return The Measure corresponding to the URL. + */ + default Optional loadLibraryByUrl(String libraryUrl) { + + return toLibraryAdapter( + loadNpmResource(getLibraryClass(), toPrimitiveType(libraryUrl)).orElse(null)); + } + + default Optional toLibraryAdapter(IBaseResource resource) { + if (resource == null) { + return Optional.empty(); + } + switch (getFhirContext().getVersion().getVersion()) { + case R4 -> { + if (!(resource instanceof org.hl7.fhir.r4.model.Library r4Library)) { + throw new IllegalArgumentException( + "Expected resource to be a Library, but was a " + resource.fhirType()); + } + return Optional.of(new org.opencds.cqf.fhir.utility.adapter.r4.LibraryAdapter(r4Library)); + } + case R5 -> { + if (!(resource instanceof org.hl7.fhir.r5.model.Library r5Library)) { + ; + throw new IllegalArgumentException( + "Expected resource to be a Library, but was a " + resource.fhirType()); + } + return Optional.of(new org.opencds.cqf.fhir.utility.adapter.r5.LibraryAdapter(r5Library)); + } + default -> throw new IllegalStateException( + "Unsupported FHIR version: " + getFhirContext().getVersion().getVersion()); + } + } + + default IPrimitiveType toPrimitiveType(String libraryUrl) { + switch (getFhirContext().getVersion().getVersion()) { + case R4 -> { + return new org.hl7.fhir.r4.model.StringType(libraryUrl); + } + case R5 -> { + return new org.hl7.fhir.r5.model.StringType(libraryUrl); + } + default -> throw new IllegalStateException( + "Unsupported FHIR version: " + getFhirContext().getVersion().getVersion()); + } + } + + default Class getLibraryClass() { + switch (getFhirContext().getVersion().getVersion()) { + case R4 -> { + return org.hl7.fhir.r4.model.Library.class; + } + case R5 -> { + return org.hl7.fhir.r5.model.Library.class; + } + default -> throw new IllegalStateException( + "Unsupported FHIR version: " + getFhirContext().getVersion().getVersion()); + } + } + + private String getUrl(VersionedIdentifier versionedIdentifier) { + // We need this case because the CQL engine will do the right thing and populate the system + // in the cross-package target case + return LIBRARY_URL_TEMPLATE.formatted(versionedIdentifier.getSystem(), versionedIdentifier.getId()); + } + + private String getUrl(ModelIdentifier modelIdentifier) { + return LIBRARY_URL_TEMPLATE.formatted(modelIdentifier.getSystem(), modelIdentifier.getId()); + } +} diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/R4NpmPackageLoaderInMemory.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/R4NpmPackageLoaderInMemory.java new file mode 100644 index 0000000000..4c1566e208 --- /dev/null +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/R4NpmPackageLoaderInMemory.java @@ -0,0 +1,377 @@ +package org.opencds.cqf.fhir.utility.npm; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.StringJoiner; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import org.hl7.cql.model.NamespaceInfo; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IPrimitiveType; +import org.hl7.fhir.utilities.npm.NpmPackage; +import org.opencds.cqf.fhir.utility.adapter.ILibraryAdapter; +import org.opencds.cqf.fhir.utility.adapter.IResourceAdapter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +// LUKETODO: redo java +/** + * Simplistic implementation of {@link NpmPackageLoader} that loads NpmPackages from the classpath + * and stores {@link NpmResourceHolder}s in a Map. This class is recommended for testing + * and NOT for production. + *

npmPackages; + // private final Map resourceUrlToResource = new HashMap<>(); + // private final Map libraryUrlToPackage = new HashMap<>(); + private final NpmNamespaceManager npmNamespaceManager; + + public static R4NpmPackageLoaderInMemory fromNpmPackageAbsolutePath(List tgzPaths) { + return fromNpmPackageAbsolutePath(null, tgzPaths); + } + + public static R4NpmPackageLoaderInMemory fromNpmPackageAbsolutePath( + NpmNamespaceManager npmNamespaceManager, List tgzPaths) { + final Set npmPackages = buildNpmPackagesFromAbsolutePath(tgzPaths); + + return new R4NpmPackageLoaderInMemory(npmPackages, npmNamespaceManager); + } + + public static R4NpmPackageLoaderInMemory fromNpmPackageClasspath(Class clazz, Path... tgzPaths) { + return fromNpmPackageClasspath(null, clazz, tgzPaths); + } + + public static R4NpmPackageLoaderInMemory fromNpmPackageClasspath( + @Nullable NpmNamespaceManager npmNamespaceManager, Class clazz, Path... tgzPaths) { + return fromNpmPackageClasspath(npmNamespaceManager, clazz, Arrays.asList(tgzPaths)); + } + + public static R4NpmPackageLoaderInMemory fromNpmPackageClasspath(Class clazz, List tgzPaths) { + return fromNpmPackageClasspath(null, clazz, tgzPaths); + } + + public static R4NpmPackageLoaderInMemory fromNpmPackageClasspath( + @Nullable NpmNamespaceManager npmNamespaceManager, Class clazz, List tgzPaths) { + final Set npmPackages = buildNpmPackageFromClasspath(clazz, tgzPaths); + + return new R4NpmPackageLoaderInMemory(npmPackages, npmNamespaceManager); + } + + record UrlAndVersion(String url, @Nullable String version) { + + static UrlAndVersion fromCanonical(String canonical) { + final String[] parts = PATTERN_PIPE.split(canonical); + if (parts.length > 2) { + throw new IllegalArgumentException("Invalid canonical URL: " + canonical); + } + if (parts.length == 1) { + return new UrlAndVersion(parts[0], null); + } + return new UrlAndVersion(parts[0], parts[1]); + } + + static UrlAndVersion fromCanonicalAndVersion(String canonical, @Nullable String version) { + if (version == null) { + return new UrlAndVersion(canonical, null); + } + + return new UrlAndVersion(canonical, version); + } + + @Override + @Nonnull + public String toString() { + return url + "|" + version; + } + } + + @Override + public Optional loadNpmResource(IPrimitiveType resourceUrl) { + return npmPackages.stream() + .filter(npmPackage -> doesPackageMatch(resourceUrl, npmPackage)) + .map(npmPackage -> getResource(npmPackage, resourceUrl)) + .findFirst(); + } + + private IBaseResource getResource(NpmPackage npmPackage, IPrimitiveType resourceUrl) { + try { + return tryGetResource(npmPackage, resourceUrl); + } catch (IOException exception) { + throw new RuntimeException(FAILED_TO_LOAD_RESOURCE_TEMPLATE.formatted(resourceUrl.getValue()), exception); + } + } + + private IBaseResource tryGetResource(NpmPackage npmPackage, IPrimitiveType resourceUrl) throws IOException { + + final FhirContext fhirContext = getFhirContext(npmPackage); + final String resourceUrlString = resourceUrl.getValue(); + + final String[] split = resourceUrlString.split("\\|"); + + try (InputStream libraryInputStream = npmPackage.loadByCanonical(split[0])) { + return fhirContext.newJsonParser().parseResource(libraryInputStream); + } + } + + private boolean doesPackageMatch(IPrimitiveType resourceUrl, NpmPackage npmPackage) { + try { + return npmPackage.hasCanonical(resourceUrl.getValue()); + } catch (IOException exception) { + throw new RuntimeException(FAILED_TO_LOAD_RESOURCE_TEMPLATE, exception); + } + } + + @Override + public FhirContext getFhirContext() { + return FhirContext.forR4Cached(); + } + + // @Override + // public Optional loadLibraryByUrl(String url) { + // for (NpmPackage npmPackage : libraryUrlToPackage.values()) { + // final FhirContext fhirContext = getFhirContext(npmPackage); + // try (InputStream libraryInputStream = npmPackage.loadByCanonical(url)) { + // if (libraryInputStream != null) { + // final IResourceAdapter resourceAdapter = IAdapterFactory.createAdapterForResource( + // fhirContext.newJsonParser().parseResource(libraryInputStream)); + // if (resourceAdapter instanceof ILibraryAdapter libraryAdapter) { + // return Optional.of(libraryAdapter); + // } + // } + // } catch (IOException exception) { + // throw new InternalErrorException(exception); + // } + // } + // return Optional.empty(); + // } + + @Override + public NpmNamespaceManager getNamespaceManager() { + return npmNamespaceManager; + } + + @Nonnull + private static Set buildNpmPackagesFromAbsolutePath(List tgzPaths) { + return tgzPaths.stream() + .map(R4NpmPackageLoaderInMemory::getNpmPackageFromAbsolutePaths) + .collect(Collectors.toUnmodifiableSet()); + } + + @Nonnull + private static Set buildNpmPackageFromClasspath(Class clazz, List tgzPaths) { + return tgzPaths.stream() + .map(path -> getNpmPackageFromClasspath(clazz, path)) + .collect(Collectors.toUnmodifiableSet()); + } + + @Nonnull + private static NpmPackage getNpmPackageFromAbsolutePaths(Path tgzPath) { + try (final InputStream npmStream = Files.newInputStream(tgzPath)) { + return NpmPackage.fromPackage(npmStream); + } catch (IOException exception) { + throw new InvalidRequestException(FAILED_TO_LOAD_RESOURCE_TEMPLATE.formatted(tgzPath), exception); + } + } + + @Nonnull + private static NpmPackage getNpmPackageFromClasspath(Class clazz, Path tgzClasspathPath) { + try (final InputStream simpleAlphaStream = clazz.getResourceAsStream(tgzClasspathPath.toString())) { + if (simpleAlphaStream == null) { + throw new InvalidRequestException(FAILED_TO_LOAD_RESOURCE_TEMPLATE.formatted(tgzClasspathPath)); + } + + return NpmPackage.fromPackage(simpleAlphaStream); + } catch (IOException exception) { + throw new InvalidRequestException(FAILED_TO_LOAD_RESOURCE_TEMPLATE.formatted(tgzClasspathPath), exception); + } + } + + private R4NpmPackageLoaderInMemory(Set npmPackages, @Nullable NpmNamespaceManager npmNamespaceManager) { + + if (npmNamespaceManager == null) { + var namespaceInfos = npmPackages.stream() + .map(npmPackage -> new NamespaceInfo(npmPackage.name(), npmPackage.canonical())) + .toList(); + + this.npmNamespaceManager = new NpmNamespaceManagerFromList(namespaceInfos); + } else { + this.npmNamespaceManager = npmNamespaceManager; + } + + this.npmPackages = npmPackages; + } + + private void setup(NpmPackage npmPackage) { + try { + trySetup(npmPackage); + } catch (Exception e) { + throw new InternalErrorException("Failed to setup NpmPackage: " + npmPackage.name(), e); + } + } + + private void trySetup(NpmPackage npmPackage) throws IOException { + final FhirContext fhirContext = getFhirContext(npmPackage); + + final Optional optPackageFolder = npmPackage.getFolders().entrySet().stream() + .filter(entry -> "package".equals(entry.getKey())) + .map(Map.Entry::getValue) + .findFirst(); + + if (optPackageFolder.isPresent()) { + setupNpmPackageInfo(npmPackage, optPackageFolder.get(), fhirContext); + } + } + + private void setupNpmPackageInfo( + NpmPackage npmPackage, NpmPackage.NpmPackageFolder packageFolder, FhirContext fhirContext) + throws IOException { + + // LUKETODO: store all resources + + final List resources = findResources(packageFolder, fhirContext); + + // final Optional optMeasure = findMeasure(resources); + // final List libraries = findLibraries(resources); + // + // storeResources(npmPackage, optMeasure.orElse(null), libraries); + } + + private List findResources(NpmPackage.NpmPackageFolder packageFolder, FhirContext fhirContext) + throws IOException { + + final Map> types = packageFolder.getTypes(); + final List resources = new ArrayList<>(); + + for (Map.Entry> typeToFiles : types.entrySet()) { + for (String nextFile : typeToFiles.getValue()) { + final String fileContents = new String(packageFolder.fetchFile(nextFile), StandardCharsets.UTF_8); + + if (nextFile.toLowerCase().endsWith(".json")) { + final IBaseResource resource = fhirContext.newJsonParser().parseResource(fileContents); + + resources.add(resource); + } + } + } + + return resources; + } + + // private Optional findMeasure(List resources) { + // return resources.stream() + // .filter(Measure.class::isInstance) + // .map(IMeasureAdapter.class::cast) + // .findFirst(); + // } + + private List findLibraries(List resources) { + return resources.stream() + .filter(ILibraryAdapter.class::isInstance) + .map(ILibraryAdapter.class::cast) + .toList(); + } + + // private void storeResources( + // NpmPackage npmPackage, @Nullable IMeasureAdapter measure, List libraries) { + // if (measure != null) { + // resourceUrlToResource.put( + // UrlAndVersion.fromCanonicalAndVersion(measure.getUrl(), measure.getVersion()), measure.get()); + // } + // + // for (ILibraryAdapter library : libraries) { + // libraryUrlToPackage.put( + // UrlAndVersion.fromCanonicalAndVersion(library.getUrl(), library.getVersion()), npmPackage); + // } + // } + + private static boolean doUrlAndVersionMatch( + IPrimitiveType measureUrl, Map.Entry entry) { + + if (entry.getKey().equals(UrlAndVersion.fromCanonical(measureUrl.getValueAsString()))) { + return true; + } + + return entry.getKey().url.equals(measureUrl.getValueAsString()); + } + + private static boolean doMeasureUrlAndLibraryMatch(String measureLibraryUrl, ILibraryAdapter library) { + final String[] split = PATTERN_PIPE.split(measureLibraryUrl); + + if (split.length == 1) { + return library.getUrl().equals(measureLibraryUrl); + } + + if (split.length == 2) { + return library.getUrl().equals(split[0]) && library.getVersion().equals(split[1]); + } + + throw new InternalErrorException("bad measureUrl: " + measureLibraryUrl); + } + + private FhirContext getFhirContext(NpmPackage npmPackage) { + return FhirContext.forCached(FhirVersionEnum.forVersionString(npmPackage.fhirVersion())); + } + + /** + * Meant to test various scenarios involving missing of faulty NamespaceInfo data. + */ + public static class NpmNamespaceManagerFromList implements NpmNamespaceManager { + + private final List namespaceInfos; + + public NpmNamespaceManagerFromList(List namespaceInfos) { + this.namespaceInfos = List.copyOf(namespaceInfos); + } + + @Override + public List getAllNamespaceInfos() { + return namespaceInfos; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) { + return false; + } + NpmNamespaceManagerFromList that = (NpmNamespaceManagerFromList) o; + return Objects.equals(namespaceInfos, that.namespaceInfos); + } + + @Override + public int hashCode() { + return Objects.hashCode(namespaceInfos); + } + + @Override + public String toString() { + return new StringJoiner(", ", NpmNamespaceManagerFromList.class.getSimpleName() + "[", "]") + .add("namespaceInfos=" + namespaceInfos) + .toString(); + } + } +} diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/BaseNpmResourceInfoForCqlTest.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/BaseNpmResourceInfoForCqlTest.java new file mode 100644 index 0000000000..40cd29487c --- /dev/null +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/BaseNpmResourceInfoForCqlTest.java @@ -0,0 +1,440 @@ +package org.opencds.cqf.fhir.utility.npm; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import ca.uhn.fhir.context.FhirVersionEnum; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import org.hl7.fhir.instance.model.api.ICompositeType; +import org.opencds.cqf.fhir.utility.adapter.IAdapterFactory; +import org.opencds.cqf.fhir.utility.adapter.IAttachmentAdapter; +import org.opencds.cqf.fhir.utility.adapter.ILibraryAdapter; + +public abstract class BaseNpmResourceInfoForCqlTest { + + protected static final String DOT_TGZ = ".tgz"; + + protected static final String SIMPLE_ALPHA_LOWER = "simplealpha"; + protected static final String SIMPLE_ALPHA_MIXED = "SimpleAlpha"; + protected static final String SIMPLE_BRAVO_LOWER = "simplebravo"; + protected static final String SIMPLE_BRAVO_MIXED = "SimpleBravo"; + protected static final String WITH_DERIVED_LIBRARY_LOWER = "withderivedlibrary"; + protected static final String WITH_DERIVED_LIBRARY_MIXED = "WithDerivedLibrary"; + protected static final String DERIVED_LIBRARY_ID = "DerivedLibrary"; + protected static final String DERIVED_LIBRARY = DERIVED_LIBRARY_ID; + + protected static final String NAMESPACE_PREFIX = "opencds."; + + protected static final String WITH_TWO_LAYERS_DERIVED_LIBRARIES = "withtwolayersderivedlibraries"; + protected static final String WITH_TWO_LAYERS_DERIVED_LIBRARIES_UPPER = "WithTwoLayersDerivedLibraries"; + + protected static final String SIMPLE_ALPHA_NAMESPACE = NAMESPACE_PREFIX + SIMPLE_ALPHA_LOWER; + protected static final String SIMPLE_BRAVO_NAMESPACE = NAMESPACE_PREFIX + SIMPLE_BRAVO_LOWER; + protected static final String WITH_DERIVED_NAMESPACE = NAMESPACE_PREFIX + WITH_DERIVED_LIBRARY_LOWER; + protected static final String WITH_TWO_LAYERS_NAMESPACE = NAMESPACE_PREFIX + WITH_TWO_LAYERS_DERIVED_LIBRARIES; + + protected static final String DERIVED_LAYER_1_A = "DerivedLayer1a"; + protected static final String DERIVED_LAYER_1_B = "DerivedLayer1b"; + protected static final String DERIVED_LAYER_2_A = "DerivedLayer2a"; + protected static final String DERIVED_LAYER_2_B = "DerivedLayer2b"; + protected static final String CROSS_PACKAGE_SOURCE = "crosspackagesource"; + protected static final String CROSS_PACKAGE_SOURCE_ID = "CrossPackageSource"; + protected static final String CROSS_PACKAGE_TARGET = "crosspackagetarget"; + protected static final String CROSS_PACKAGE_TARGET_ID = "CrossPackageTarget"; + + protected static final String SIMPLE_ALPHA_TGZ = SIMPLE_ALPHA_LOWER + DOT_TGZ; + protected static final String SIMPLE_BRAVO_TGZ = SIMPLE_BRAVO_LOWER + DOT_TGZ; + protected static final Path WITH_DERIVED_LIBRARY_TGZ = Paths.get(WITH_DERIVED_LIBRARY_LOWER + DOT_TGZ); + protected static final Path WITH_TWO_LAYERS_DERIVED_LIBRARIES_TGZ = + Paths.get(WITH_TWO_LAYERS_DERIVED_LIBRARIES + DOT_TGZ); + protected static final Path CROSS_PACKAGE_SOURCE_TGZ = Paths.get(CROSS_PACKAGE_SOURCE + DOT_TGZ); + protected static final Path CROSS_PACKAGE_TARGET_TGZ = Paths.get(CROSS_PACKAGE_TARGET + DOT_TGZ); + + protected static final String SLASH_MEASURE_SLASH = "/Measure/"; + protected static final String SLASH_LIBRARY_SLASH = "/Library/"; + + private static final String PIPE = "|"; + private static final String VERSION_0_1 = "0.1"; + private static final String VERSION_0_2 = "0.2"; + private static final String VERSION_0_4 = "0.4"; + private static final String VERSION_0_5 = "0.5"; + + protected static final String SIMPLE_ALPHA_NAMESPACE_URL = "http://simplealpha.npm.opencds.org"; + protected static final String SIMPLE_BRAVO_NAMESPACE_URL = "http://simplebravo.npm.opencds.org"; + protected static final String WITH_DERIVED_URL = "http://withderivedlibrary.npm.opencds.org"; + protected static final String WITH_DERIVED_TWO_LAYERS_URL = "http://withtwolayersderivedlibraries.npm.opencds.org"; + protected static final String CROSS_PACKAGE_SOURCE_URL = "http://crosspackagesource.npm.opencds.org"; + protected static final String CROSS_PACKAGE_TARGET_URL = "http://crosspackagetarget.npm.opencds.org"; + + protected static final String MEASURE_URL_ALPHA = + SIMPLE_ALPHA_NAMESPACE_URL + SLASH_MEASURE_SLASH + SIMPLE_ALPHA_MIXED; + protected static final String MEASURE_URL_BRAVO = + SIMPLE_BRAVO_NAMESPACE_URL + SLASH_MEASURE_SLASH + SIMPLE_BRAVO_MIXED; + protected static final String MEASURE_URL_WITH_DERIVED_LIBRARY = + WITH_DERIVED_URL + SLASH_MEASURE_SLASH + WITH_DERIVED_LIBRARY_MIXED; + protected static final String MEASURE_URL_WITH_DERIVED_LIBRARY_WITH_VERSION = + MEASURE_URL_WITH_DERIVED_LIBRARY + PIPE + VERSION_0_4; + protected static final String MEASURE_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES = + WITH_DERIVED_TWO_LAYERS_URL + SLASH_MEASURE_SLASH + WITH_TWO_LAYERS_DERIVED_LIBRARIES_UPPER; + protected static final String MEASURE_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES_WITH_VERSION = + MEASURE_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES + PIPE + VERSION_0_5; + + protected static final String MEASURE_URL_CROSS_PACKAGE_SOURCE = + CROSS_PACKAGE_SOURCE_URL + SLASH_MEASURE_SLASH + CROSS_PACKAGE_SOURCE_ID; + protected static final String MEASURE_URL_CROSS_PACKAGE_SOURCE_WITH_VERSION = + MEASURE_URL_CROSS_PACKAGE_SOURCE + PIPE + VERSION_0_2; + + protected static final String LIBRARY_URL_ALPHA_NO_VERSION = + SIMPLE_ALPHA_NAMESPACE_URL + SLASH_LIBRARY_SLASH + SIMPLE_ALPHA_MIXED; + protected static final String LIBRARY_URL_ALPHA_WITH_VERSION = LIBRARY_URL_ALPHA_NO_VERSION + PIPE + VERSION_0_1; + protected static final String LIBRARY_URL_BRAVO_NO_VERSION = + SIMPLE_BRAVO_NAMESPACE_URL + SLASH_LIBRARY_SLASH + SIMPLE_BRAVO_MIXED; + protected static final String LIBRARY_URL_BRAVO_WITH_VERSION = LIBRARY_URL_BRAVO_NO_VERSION + PIPE + VERSION_0_1; + + protected static final String LIBRARY_URL_WITH_DERIVED_LIBRARY_NO_VERSION = + WITH_DERIVED_URL + SLASH_LIBRARY_SLASH + WITH_DERIVED_LIBRARY_MIXED; + protected static final String LIBRARY_URL_WITH_DERIVED_LIBRARY_WITH_VERSION = + WITH_DERIVED_URL + SLASH_LIBRARY_SLASH + WITH_DERIVED_LIBRARY_MIXED + PIPE + VERSION_0_4; + protected static final String LIBRARY_URL_DERIVED_LIBRARY = + WITH_DERIVED_URL + SLASH_LIBRARY_SLASH + DERIVED_LIBRARY; + + protected static final String LIBRARY_URL_CROSS_PACKAGE_SOURCE = + CROSS_PACKAGE_SOURCE_URL + SLASH_LIBRARY_SLASH + CROSS_PACKAGE_SOURCE_ID; + protected static final String LIBRARY_URL_CROSS_PACKAGE_SOURCE_WITH_VERSION = + LIBRARY_URL_CROSS_PACKAGE_SOURCE + PIPE + VERSION_0_2; + protected static final String LIBRARY_URL_CROSS_PACKAGE_TARGET = + CROSS_PACKAGE_TARGET_URL + SLASH_LIBRARY_SLASH + CROSS_PACKAGE_TARGET_ID; + + protected static final String LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES_NO_VERSION = + WITH_DERIVED_TWO_LAYERS_URL + SLASH_LIBRARY_SLASH + WITH_TWO_LAYERS_DERIVED_LIBRARIES_UPPER; + protected static final String LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES_WITH_VERSION = + LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES_NO_VERSION + PIPE + VERSION_0_5; + + protected static final String LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_1A = + WITH_DERIVED_TWO_LAYERS_URL + SLASH_LIBRARY_SLASH + DERIVED_LAYER_1_A; + protected static final String LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_1B = + WITH_DERIVED_TWO_LAYERS_URL + SLASH_LIBRARY_SLASH + DERIVED_LAYER_1_B; + protected static final String LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_2A = + WITH_DERIVED_TWO_LAYERS_URL + SLASH_LIBRARY_SLASH + DERIVED_LAYER_2_A; + protected static final String LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_2B = + WITH_DERIVED_TWO_LAYERS_URL + SLASH_LIBRARY_SLASH + DERIVED_LAYER_2_B; + + // LUKETODO: + protected abstract FhirVersionEnum getExpectedFhirVersion(); + + // protected void simpleAlpha( + // Path tgzPath, + // String measureUrl, + // String expectedLibraryUrlFromMeasure, + // String expectedLibraryUrlWithinLibrary, + // String expectedCql) { + // + // simpleCommon( + // tgzPath, + // List.of(new NamespaceInfo(SIMPLE_ALPHA_NAMESPACE, SIMPLE_ALPHA_NAMESPACE_URL)), + // measureUrl, + // expectedLibraryUrlFromMeasure, + // expectedLibraryUrlWithinLibrary, + // expectedCql); + // } + // + // protected void simpleBravo( + // Path tgzPath, + // String measureUrl, + // String expectedLibraryUrlFromMeasure, + // String expectedLibraryUrlWithinLibrary, + // String expectedCql) { + // + // simpleCommon( + // tgzPath, + // List.of(new NamespaceInfo(SIMPLE_BRAVO_NAMESPACE, SIMPLE_BRAVO_NAMESPACE_URL)), + // measureUrl, + // expectedLibraryUrlFromMeasure, + // expectedLibraryUrlWithinLibrary, + // expectedCql); + // } + + // protected void simpleCommon( + // Path tgzPath, + // List expectedNamespaceInfos, + // String measureUrl, + // String expectedLibraryUrlFromMeasure, + // String expectedLibraryUrlWithinLibrary, + // String expectedCql) { + // final R4NpmPackageLoaderInMemory loader = setup(tgzPath); + // + // final Optional optMeasure = + // loader.loadNpmResource(Measure.class, new CanonicalType(measureUrl)); + // + // verifyMeasure(measureUrl, expectedLibraryUrlFromMeasure, optMeasure); + // verifyLibrary( + // expectedLibraryUrlWithinLibrary, + // expectedCql, + // npmResourceHolder.getOptMainLibrary().orElse(null)); + // } + // + // protected void multiplePackages(String expectedCqlAlpha, String expectedCqlBravo) { + // final R4NpmPackageLoaderInMemory loader = setup( + // Stream.of(SIMPLE_ALPHA_TGZ, SIMPLE_BRAVO_TGZ).map(Paths::get).toArray(Path[]::new)); + // + // final Optional optMeasureAlpha = + // loader.loadNpmResource( + // Measure.class, + // new CanonicalType(MEASURE_URL_ALPHA)); + // final Optional optMeasureBravo = + // loader.loadNpmResource( + // Measure.class, + // new CanonicalType(MEASURE_URL_BRAVO)); + // + // assertEquals( + // List.of(new NamespaceInfo(SIMPLE_ALPHA_NAMESPACE, SIMPLE_ALPHA_NAMESPACE_URL)), + // resourceInfoAlpha.getNamespaceInfos()); + // assertEquals( + // List.of(new NamespaceInfo(SIMPLE_BRAVO_NAMESPACE, SIMPLE_BRAVO_NAMESPACE_URL)), + // resourceInfoBravo.getNamespaceInfos()); + // + // verifyMeasure(MEASURE_URL_ALPHA, LIBRARY_URL_ALPHA_WITH_VERSION, resourceInfoAlpha); + // verifyLibrary( + // LIBRARY_URL_ALPHA_NO_VERSION, + // expectedCqlAlpha, + // resourceInfoAlpha.getOptMainLibrary().orElse(null)); + // + // verifyMeasure(MEASURE_URL_BRAVO, LIBRARY_URL_BRAVO_WITH_VERSION, resourceInfoBravo); + // verifyLibrary( + // LIBRARY_URL_BRAVO_NO_VERSION, + // expectedCqlBravo, + // resourceInfoBravo.getOptMainLibrary().orElse(null)); + // } + + // protected void derivedLibrary(String expectedCql, String expectedCqlDerived) { + // + // final R4NpmPackageLoaderInMemory loader = setup(WITH_DERIVED_LIBRARY_TGZ); + // + // final NpmResourceHolder resourceInfoWithNoVersion = + // loader.loadNpmResources(new CanonicalType(MEASURE_URL_WITH_DERIVED_LIBRARY)); + // + // sanityCheckNpmResourceHolder(resourceInfoWithNoVersion); + // + // var expectedNamespaceInfos = List.of(new NamespaceInfo(WITH_DERIVED_NAMESPACE, WITH_DERIVED_URL)); + // + // assertEquals(expectedNamespaceInfos, resourceInfoWithNoVersion.getNamespaceInfos()); + // + // verifyMeasure( + // MEASURE_URL_WITH_DERIVED_LIBRARY, + // LIBRARY_URL_WITH_DERIVED_LIBRARY_WITH_VERSION, + // resourceInfoWithNoVersion); + // + // final NpmResourceHolder resourceInfoWithVersion = + // loader.loadNpmResources(new CanonicalType(MEASURE_URL_WITH_DERIVED_LIBRARY_WITH_VERSION)); + // sanityCheckNpmResourceHolder(resourceInfoWithVersion); + // assertEquals(expectedNamespaceInfos, resourceInfoWithVersion.getNamespaceInfos()); + // + // verifyMeasure( + // MEASURE_URL_WITH_DERIVED_LIBRARY, + // LIBRARY_URL_WITH_DERIVED_LIBRARY_WITH_VERSION, + // resourceInfoWithVersion); + // verifyLibrary( + // LIBRARY_URL_WITH_DERIVED_LIBRARY_NO_VERSION, + // expectedCql, + // resourceInfoWithVersion.getOptMainLibrary().orElse(null)); + // + // final ILibraryAdapter derivedLibraryFromNoVersion = resourceInfoWithVersion + // .findMatchingLibrary(new VersionedIdentifier().withId(DERIVED_LIBRARY_ID)) + // .orElse(null); + // + // verifyLibrary(LIBRARY_URL_DERIVED_LIBRARY, expectedCqlDerived, derivedLibraryFromNoVersion); + // + // final ILibraryAdapter derivedLibraryFromVersion = resourceInfoWithVersion + // .findMatchingLibrary( + // new VersionedIdentifier().withId(DERIVED_LIBRARY_ID).withVersion("0.4")) + // .orElse(null); + // + // verifyLibrary(LIBRARY_URL_DERIVED_LIBRARY, expectedCqlDerived, derivedLibraryFromVersion); + // + // final ILibraryAdapter derivedLibraryFromBadVersion = resourceInfoWithVersion + // .findMatchingLibrary( + // new VersionedIdentifier().withId(DERIVED_LIBRARY_ID).withVersion("bad")) + // .orElse(null); + // + // assertNull(derivedLibraryFromBadVersion); + // } + + // protected void derivedLibraryTwoLayers( + // String expectedCql, + // String expectedCqlDerived1a, + // String expectedCqlDerived1b, + // String expectedCqlDerived2a, + // String expectedCqlDerived2b) { + // + // final R4NpmPackageLoaderInMemory loader = setup(WITH_TWO_LAYERS_DERIVED_LIBRARIES_TGZ); + // + // var expectedNamespaceInfos = List.of(new NamespaceInfo(WITH_TWO_LAYERS_NAMESPACE, + // WITH_DERIVED_TWO_LAYERS_URL)); + // + // final NpmResourceHolder resourceInfoNoVersion = + // loader.loadNpmResources(new CanonicalType(MEASURE_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES)); + // sanityCheckNpmResourceHolder(resourceInfoNoVersion); + // assertEquals(expectedNamespaceInfos, resourceInfoNoVersion.getNamespaceInfos()); + // verifyMeasure( + // MEASURE_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES, + // LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES_WITH_VERSION, + // resourceInfoNoVersion); + // verifyLibrary( + // LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES_NO_VERSION, + // expectedCql, + // resourceInfoNoVersion.getOptMainLibrary().orElse(null)); + // + // final NpmResourceHolder resourceInfoWithVersion = + // loader.loadNpmResources(new + // CanonicalType(MEASURE_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES_WITH_VERSION)); + // sanityCheckNpmResourceHolder(resourceInfoWithVersion); + // assertEquals(expectedNamespaceInfos, resourceInfoWithVersion.getNamespaceInfos()); + // verifyMeasure( + // MEASURE_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES, + // LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES_WITH_VERSION, + // resourceInfoWithVersion); + // verifyLibrary( + // LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES_NO_VERSION, + // expectedCql, + // resourceInfoWithVersion.getOptMainLibrary().orElse(null)); + // + // final ILibraryAdapter derivedLibrary1a = resourceInfoWithVersion + // .findMatchingLibrary(new VersionedIdentifier() + // .withId(DERIVED_LAYER_1_A) + // .withVersion(VERSION_0_5) + // .withSystem(WITH_DERIVED_TWO_LAYERS_URL)) + // .orElse(null); + // + // verifyLibrary(LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_1A, expectedCqlDerived1a, derivedLibrary1a); + // + // final ILibraryAdapter derivedLibrary1b = resourceInfoWithVersion + // .findMatchingLibrary( + // new VersionedIdentifier().withId(DERIVED_LAYER_1_B).withVersion(VERSION_0_5)) + // .orElse(null); + // + // verifyLibrary(LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_1B, expectedCqlDerived1b, derivedLibrary1b); + // + // final ILibraryAdapter derivedLibrary2a = resourceInfoWithVersion + // .findMatchingLibrary( + // new VersionedIdentifier().withId(DERIVED_LAYER_2_A).withVersion(VERSION_0_5)) + // .orElse(null); + // + // verifyLibrary(LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_2A, expectedCqlDerived2a, derivedLibrary2a); + // + // final ILibraryAdapter derivedLibrary2b = resourceInfoWithVersion + // .findMatchingLibrary( + // new VersionedIdentifier().withId(DERIVED_LAYER_2_B).withVersion(VERSION_0_5)) + // .orElse(null); + // + // verifyLibrary(LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_2B, expectedCqlDerived2b, derivedLibrary2b); + // } + + // protected void crossPackage(String expectedCqlSource, String expectedCqlTarget) { + // + // final R4NpmPackageLoaderInMemory loader = setup(CROSS_PACKAGE_SOURCE_TGZ, CROSS_PACKAGE_TARGET_TGZ); + // + // final NpmResourceHolder resourceInfoWithNoVersion = + // loader.loadNpmResources(new CanonicalType(MEASURE_URL_CROSS_PACKAGE_SOURCE)); + // sanityCheckNpmResourceHolder(resourceInfoWithNoVersion); + // verifyMeasure( + // MEASURE_URL_CROSS_PACKAGE_SOURCE, + // LIBRARY_URL_CROSS_PACKAGE_SOURCE_WITH_VERSION, + // resourceInfoWithNoVersion); + // final NpmResourceHolder resourceInfoWithVersion = + // loader.loadNpmResources(new CanonicalType(MEASURE_URL_CROSS_PACKAGE_SOURCE_WITH_VERSION)); + // sanityCheckNpmResourceHolder(resourceInfoWithVersion); + // verifyMeasure( + // MEASURE_URL_CROSS_PACKAGE_SOURCE, + // LIBRARY_URL_CROSS_PACKAGE_SOURCE_WITH_VERSION, + // resourceInfoWithVersion); + // + // verifyLibrary( + // LIBRARY_URL_CROSS_PACKAGE_SOURCE, + // expectedCqlSource, + // resourceInfoWithVersion.getOptMainLibrary().orElse(null)); + // + // final Optional matchingLibraryWithSourceResult = + // resourceInfoWithVersion.findMatchingLibrary(new + // VersionedIdentifier().withId(CROSS_PACKAGE_TARGET_ID)); + // + // // We expect NOT to find the target Library here since it's not in the source package at all + // assertTrue(matchingLibraryWithSourceResult.isEmpty()); + // + // // On the other hand, we can load the Library directly from the loader by URL: + // final Optional optLibraryTarget = + // loader.loadLibraryByUrl(LIBRARY_URL_CROSS_PACKAGE_TARGET); + // + // assertTrue(optLibraryTarget.isPresent()); + // verifyLibrary(LIBRARY_URL_CROSS_PACKAGE_TARGET, expectedCqlTarget, optLibraryTarget.get()); + // } + + private void verifyLibrary(String expectedLibraryUrl, String expectedCql, @Nullable ILibraryAdapter library) { + assertNotNull(library); + + assertEquals( + getExpectedFhirVersion(), library.fhirContext().getVersion().getVersion()); + + assertEquals(expectedLibraryUrl, library.getUrl()); + + final List attachments = library.getContent(); + + assertEquals(1, attachments.size()); + + final ICompositeType attachment = attachments.get(0); + + final IAdapterFactory adapterFactory = IAdapterFactory.forFhirVersion( + library.fhirContext().getVersion().getVersion()); + + final IAttachmentAdapter adaptedAttachment = adapterFactory.createAttachment(attachment); + + assertEquals("text/cql", adaptedAttachment.getContentType()); + final byte[] attachmentData = adaptedAttachment.getData(); + final String cql = new String(attachmentData, StandardCharsets.UTF_8); + + assertEquals(expectedCql, cql); + } + // + // protected void verifyMeasure(String measureUrl, String expectedLibraryUrl, Optional + // measureAdapter) { + // + // final Optional optMeasure = npmResourceHolder.getMeasure(); + // assertTrue(optMeasure.isPresent(), "Could not find measure with url: %s".formatted(measureUrl)); + // + // final IMeasureAdapter measure = optMeasure.get(); + // assertEquals( + // getExpectedFhirVersion(), measure.fhirContext().getVersion().getVersion()); + // assertEquals(measureUrl, measure.getUrl()); + // + // final List libraryUrls = measure.getLibrary(); + // assertEquals(1, libraryUrls.size()); + // final String libraryUrl = libraryUrls.get(0); + // assertEquals(expectedLibraryUrl, libraryUrl); + // } + // + // protected void verifyMeasure(String measureUrl, String expectedLibraryUrl, Optional + // measureAdapter) { + // + // final Optional optMeasure = npmResourceHolder.getMeasure(); + // assertTrue(optMeasure.isPresent(), "Could not find measure with url: %s".formatted(measureUrl)); + // + // final IMeasureAdapter measure = optMeasure.get(); + // assertEquals( + // getExpectedFhirVersion(), measure.fhirContext().getVersion().getVersion()); + // assertEquals(measureUrl, measure.getUrl()); + // + // final List libraryUrls = measure.getLibrary(); + // assertEquals(1, libraryUrls.size()); + // final String libraryUrl = libraryUrls.get(0); + // assertEquals(expectedLibraryUrl, libraryUrl); + // } + + @Nonnull + protected R4NpmPackageLoaderInMemory setup(Path... tgzPaths) { + return R4NpmPackageLoaderInMemory.fromNpmPackageClasspath(getClass(), tgzPaths); + } +} diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/NpmConfigDependencySubstitutorTest.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/NpmConfigDependencySubstitutorTest.java new file mode 100644 index 0000000000..60e1016558 --- /dev/null +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/NpmConfigDependencySubstitutorTest.java @@ -0,0 +1,29 @@ +package org.opencds.cqf.fhir.utility.npm; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.mock; + +import java.util.Optional; +import org.junit.jupiter.api.Test; + +class NpmConfigDependencySubstitutorTest { + + @Test + void present() { + var npmPackageLoader = mock(NpmPackageLoader.class); + + var npmPackageLoaderFromSubstitutor = + NpmConfigDependencySubstitutor.substituteNpmPackageLoaderIfEmpty(Optional.of(npmPackageLoader)); + + assertNotEquals(NpmPackageLoader.R4_DEFAULT, npmPackageLoaderFromSubstitutor); + assertEquals(npmPackageLoader, npmPackageLoaderFromSubstitutor); + } + + @Test + void empty() { + var npmPackageLoaderFromSubstitutor = + NpmConfigDependencySubstitutor.substituteNpmPackageLoaderIfEmpty(Optional.empty()); + + assertEquals(NpmPackageLoader.R4_DEFAULT, npmPackageLoaderFromSubstitutor); + } +} diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/r4/NpmResourceHolderR4Test.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/r4/NpmResourceHolderR4Test.java new file mode 100644 index 0000000000..f80c21646c --- /dev/null +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/r4/NpmResourceHolderR4Test.java @@ -0,0 +1,385 @@ +package org.opencds.cqf.fhir.utility.npm.r4; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import ca.uhn.fhir.context.FhirVersionEnum; +import jakarta.annotation.Nullable; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; +import org.hl7.cql.model.NamespaceInfo; +import org.hl7.fhir.r4.model.Attachment; +import org.hl7.fhir.r4.model.CanonicalType; +import org.hl7.fhir.r4.model.Library; +import org.hl7.fhir.r4.model.Measure; +import org.junit.jupiter.api.Test; +import org.opencds.cqf.fhir.utility.npm.BaseNpmResourceInfoForCqlTest; +import org.opencds.cqf.fhir.utility.npm.R4NpmPackageLoaderInMemory; + +@SuppressWarnings("squid:S2699") +class NpmResourceHolderR4Test extends BaseNpmResourceInfoForCqlTest { + + protected FhirVersionEnum fhirVersion = FhirVersionEnum.R4; + + private static final String EXPECTED_CQL_ALPHA = + """ + library opencds.simplealpha.SimpleAlpha + + using FHIR version '4.0.1' + + include FHIRHelpers version '4.0.1' called FHIRHelpers + + parameter "Measurement Period" Interval + default Interval[@2021-01-01T00:00:00.0-06:00, @2022-01-01T00:00:00.0-06:00) + + context Patient + + define "Initial Population": + exists ("Encounter Finished") + + define "Encounter Finished": + [Encounter] E + where E.status = 'finished' + """; + + private static final String EXPECTED_CQL_BRAVO = + """ + library opencds.simplealpha.SimpleBravo + + using FHIR version '4.0.1' + + include FHIRHelpers version '4.0.1' called FHIRHelpers + + parameter "Measurement Period" Interval + default Interval[@2024-01-01T00:00:00.0-06:00, @2025-01-01T00:00:00.0-06:00) + + context Patient + + define "Initial Population": + exists ("Encounter Planned") + + define "Encounter Planned": + [Encounter] E + where E.status = 'planned' + """; + + private static final String EXPECTED_CQL_WITH_DERIVED = + """ + library opencds.withderivedlibrary WithDerivedLibrary version '0.4' + + using FHIR version '4.0.1' + + include FHIRHelpers version '4.0.1' called FHIRHelpers + include DerivedLibrary version '0.4' + + parameter "Measurement Period" Interval + default Interval[@2021-01-01T00:00:00.0-06:00, @2022-01-01T00:00:00.0-06:00) + + context Patient + + define "Initial Population": + exists (DerivedLibrary."Encounter Finished") + """; + + private static final String EXPECTED_CQL_DERIVED = + """ + library opencds.withderivedlibrary.DerivedLibrary version '0.4' + + using FHIR version '4.0.1' + + include FHIRHelpers version '4.0.1' called FHIRHelpers + + context Patient + + define "Encounter Finished": + [Encounter] E + where E.status = 'finished' + """; + + private static final String EXPECTED_CQL_DERIVED_TWO_LAYERS = + """ + library opencds.withtwolayersderivedlibraries.WithTwoLayersDerivedLibraries version '0.5' + + using FHIR version '4.0.1' + + include DerivedLayer1a version '0.5' + include DerivedLayer1b version '0.5' + + parameter "Measurement Period" Interval + default Interval[@2022-01-01T00:00:00.0-06:00, @2023-01-01T00:00:00.0-06:00) + + context Patient + + define "Initial Population": + DerivedLayer1a."Initial Population" + + define "Denominator": + DerivedLayer1b."Denominator" + + define "Numerator": + DerivedLayer1b."Numerator" + """; + + private static final String EXPECTED_CQL_DERIVED_1_A = + """ + library opencds.withtwolayersderivedlibraries.DerivedLayer1a version '0.5' + + using FHIR version '4.0.1' + + include DerivedLayer2a version '0.5' + include DerivedLayer2b version '0.5' + + context Patient + + define "Initial Population": + DerivedLayer2a."Initial Population" + """; + + private static final String EXPECTED_CQL_DERIVED_1_B = + """ + library opencds.withtwolayersderivedlibraries.DerivedLayer1b version '0.5' + + using FHIR version '4.0.1' + + include DerivedLayer2a version '0.5' + include DerivedLayer2b version '0.5' + + context Patient + + define "Denominator": + DerivedLayer2a."Denominator" + + define "Numerator": + DerivedLayer2b."Numerator" + """; + + private static final String EXPECTED_CQL_DERIVED_2_A = + """ + library opencds.withtwolayersderivedlibraries.DerivedLayer2a version '0.5' + + using FHIR version '4.0.1' + + include FHIRHelpers version '4.0.1' called FHIRHelpers + + context Patient + + define "Initial Population": + exists ("Encounter Finished") + + define "Denominator": + exists ("Encounter Planned") + + define "Encounter Finished": + [Encounter] E + where E.status = 'finished' + + define "Encounter Planned": + [Encounter] E + where E.status = 'planned' + """; + + private static final String EXPECTED_CQL_DERIVED_2_B = + """ + library opencds.withtwolayersderivedlibraries.DerivedLayer2b version '0.5' + + using FHIR version '4.0.1' + + include FHIRHelpers version '4.0.1' called FHIRHelpers + + parameter "Measurement Period" Interval + default Interval[@2021-01-01T00:00:00.0-06:00, @2022-01-01T00:00:00.0-06:00) + + context Patient + + define "Numerator": + exists ("Encounter Triaged") + + define "Encounter Triaged": + [Encounter] E + where E.status = 'triaged' + """; + + private static final String EXPECTED_CQL_CROSS_SOURCE = + """ + library opencds.crosspackagesource.CrossPackageSource version '0.2' + + using FHIR version '4.0.1' + + include FHIRHelpers version '4.0.1' called FHIRHelpers + include opencds.crosspackagetarget.CrossPackageTarget version '0.3' called CrossPackageTarget + + parameter "Measurement Period" Interval + default Interval[@2020-01-01T00:00:00.0-06:00, @2021-01-01T00:00:00.0-06:00) + + context Patient + + define "Initial Population": + exists (CrossPackageTarget."Encounter Finished") + """; + + private static final String EXPECTED_CQL_CROSS_TARGET = + """ + library opencds.crosspackagetarget.CrossPackageTarget version '0.3' + + using FHIR version '4.0.1' + + include FHIRHelpers version '4.0.1' called FHIRHelpers + + context Patient + + define "Encounter Finished": + [Encounter] E + where E.status = 'finished' + """; + + @Override + protected FhirVersionEnum getExpectedFhirVersion() { + return FhirVersionEnum.R4; + } + + @Test + void simpleAlpha() { + final R4NpmPackageLoaderInMemory loader = setup(Path.of(SIMPLE_ALPHA_TGZ)); + + final Optional optMeasure = + loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_ALPHA)); + + verifyMeasure(optMeasure.orElse(null), MEASURE_URL_ALPHA, LIBRARY_URL_ALPHA_WITH_VERSION); + + final Optional optLibrary = + loader.loadNpmResource(Library.class, new CanonicalType(LIBRARY_URL_ALPHA_WITH_VERSION)); + + verifyLibrary(optLibrary.orElse(null), LIBRARY_URL_ALPHA_NO_VERSION, EXPECTED_CQL_ALPHA); + + final List allNamespaceInfos = + loader.getNamespaceManager().getAllNamespaceInfos(); + + assertEquals(1, allNamespaceInfos.size()); + + final NamespaceInfo namespaceInfo = allNamespaceInfos.get(0); + + assertEquals(SIMPLE_ALPHA_NAMESPACE, namespaceInfo.getName()); + assertEquals(SIMPLE_ALPHA_NAMESPACE_URL, namespaceInfo.getUri()); + } + + @Test + void simpleBravo() { + final R4NpmPackageLoaderInMemory loader = setup(Path.of(SIMPLE_BRAVO_TGZ)); + + final Optional optMeasure = + loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_BRAVO)); + + verifyMeasure(optMeasure.orElse(null), MEASURE_URL_BRAVO, LIBRARY_URL_BRAVO_WITH_VERSION); + + final Optional optLibrary = + loader.loadNpmResource(Library.class, new CanonicalType(LIBRARY_URL_BRAVO_WITH_VERSION)); + + verifyLibrary(optLibrary.orElse(null), LIBRARY_URL_BRAVO_NO_VERSION, EXPECTED_CQL_BRAVO); + + final List allNamespaceInfos = + loader.getNamespaceManager().getAllNamespaceInfos(); + + assertEquals(1, allNamespaceInfos.size()); + + final NamespaceInfo namespaceInfo = allNamespaceInfos.get(0); + + assertEquals(SIMPLE_BRAVO_NAMESPACE, namespaceInfo.getName()); + assertEquals(SIMPLE_BRAVO_NAMESPACE_URL, namespaceInfo.getUri()); + } + + @Test + void multiplePackages() { + final R4NpmPackageLoaderInMemory loader = R4NpmPackageLoaderInMemory.fromNpmPackageClasspath( + getClass(), + Stream.of(SIMPLE_ALPHA_TGZ, SIMPLE_BRAVO_TGZ).map(Paths::get).toList()); + + final List allNamespaceInfos = + loader.getNamespaceManager().getAllNamespaceInfos(); + + assertEquals(2, allNamespaceInfos.size()); + + assertTrue(allNamespaceInfos.contains(new NamespaceInfo(SIMPLE_ALPHA_NAMESPACE, SIMPLE_ALPHA_NAMESPACE_URL))); + assertTrue(allNamespaceInfos.contains(new NamespaceInfo(SIMPLE_BRAVO_NAMESPACE, SIMPLE_BRAVO_NAMESPACE_URL))); + + final Optional optMeasureAlpha = + loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_ALPHA)); + + verifyMeasure(optMeasureAlpha.orElse(null), MEASURE_URL_ALPHA, LIBRARY_URL_ALPHA_WITH_VERSION); + + final Optional optLibraryAlpha = + loader.loadNpmResource(Library.class, new CanonicalType(LIBRARY_URL_ALPHA_WITH_VERSION)); + + verifyLibrary(optLibraryAlpha.orElse(null), LIBRARY_URL_ALPHA_NO_VERSION, EXPECTED_CQL_ALPHA); + + final Optional optMeasureBravo = + loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_ALPHA)); + + verifyMeasure(optMeasureBravo.orElse(null), MEASURE_URL_ALPHA, LIBRARY_URL_ALPHA_WITH_VERSION); + + final Optional optLibraryBravo = + loader.loadNpmResource(Library.class, new CanonicalType(LIBRARY_URL_BRAVO_WITH_VERSION)); + + verifyLibrary(optLibraryBravo.orElse(null), LIBRARY_URL_BRAVO_NO_VERSION, EXPECTED_CQL_BRAVO); + } + + protected void verifyMeasure(@Nullable Measure measure, String measureUrl, String expectedLibraryUrl) { + + assertNotNull(measure, "Could not find measure with url: %s".formatted(measureUrl)); + + assertEquals(measureUrl, measure.getUrl()); + + final List libraryUrls = measure.getLibrary(); + assertEquals(1, libraryUrls.size()); + final CanonicalType libraryUrl = libraryUrls.get(0); + assertEquals(expectedLibraryUrl, libraryUrl.asStringValue()); + } + + private void verifyLibrary(@Nullable Library library, String expectedLibraryUrl, String expectedCql) { + + assertNotNull(library, "Could not find library with url: %s".formatted(expectedLibraryUrl)); + + assertEquals(expectedLibraryUrl, library.getUrl()); + + final List attachments = library.getContent(); + + assertEquals(1, attachments.size()); + + final Attachment attachment = attachments.get(0); + + assertEquals("text/cql", attachment.getContentType()); + final byte[] attachmentData = attachment.getData(); + final String cql = new String(attachmentData, StandardCharsets.UTF_8); + + assertEquals(expectedCql, cql); + } + + // @Test + // void multiplePackages() { + // multiplePackages(EXPECTED_CQL_ALPHA, EXPECTED_CQL_BRAVO); + // } + // + // @Test + // void derivedLibrary() { + // derivedLibrary(EXPECTED_CQL_WITH_DERIVED, EXPECTED_CQL_DERIVED); + // } + // + // @Test + // void derivedLibraryTwoLayers() { + // derivedLibraryTwoLayers( + // EXPECTED_CQL_DERIVED_TWO_LAYERS, + // EXPECTED_CQL_DERIVED_1_A, + // EXPECTED_CQL_DERIVED_1_B, + // EXPECTED_CQL_DERIVED_2_A, + // EXPECTED_CQL_DERIVED_2_B); + // } + // + // @Test + // void crossPackage() { + // crossPackage(EXPECTED_CQL_CROSS_SOURCE, EXPECTED_CQL_CROSS_TARGET); + // } +} diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/r5/NpmResourceHolderR5Test.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/r5/NpmResourceHolderR5Test.java new file mode 100644 index 0000000000..a54cd2e6cf --- /dev/null +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/r5/NpmResourceHolderR5Test.java @@ -0,0 +1,268 @@ +package org.opencds.cqf.fhir.utility.npm.r5; + +import ca.uhn.fhir.context.FhirVersionEnum; +import org.opencds.cqf.fhir.utility.npm.BaseNpmResourceInfoForCqlTest; + +@SuppressWarnings("squid:S2699") +class NpmResourceHolderR5Test extends BaseNpmResourceInfoForCqlTest { + + protected FhirVersionEnum fhirVersion = FhirVersionEnum.R5; + + private static final String EXPECTED_CQL_ALPHA = + """ + library opencds.simplealpha.SimpleAlpha + + using FHIR version '5.0.1' + + include FHIRHelpers version '5.0.1' called FHIRHelpers + + parameter "Measurement Period" Interval + default Interval[@2021-01-01T00:00:00.0-06:00, @2022-01-01T00:00:00.0-06:00) + + context Patient + + define "Initial Population": + exists ("Encounter Finished") + + define "Encounter Finished": + [Encounter] E + where E.status = 'finished' + """; + private static final String EXPECTED_CQL_BRAVO = + """ + library opencds.simplealpha.SimpleBravo + + using FHIR version '5.0.1' + + include FHIRHelpers version '4.0.1' called FHIRHelpers + + parameter "Measurement Period" Interval + default Interval[@2024-01-01T00:00:00.0-06:00, @2025-01-01T00:00:00.0-06:00) + + context Patient + + define "Initial Population": + exists ("Encounter Planned") + + define "Encounter Planned": + [Encounter] E + where E.status = 'planned' + """; + private static final String EXPECTED_CQL_WITH_DERIVED = + """ + library opencds.withderivedlibrary WithDerivedLibrary version '0.4' + + using FHIR version '5.0.1' + + include FHIRHelpers version '5.0.1' called FHIRHelpers + include DerivedLibrary version '0.4' + + parameter "Measurement Period" Interval + default Interval[@2021-01-01T00:00:00.0-06:00, @2022-01-01T00:00:00.0-06:00) + + context Patient + + define "Initial Population": + exists (DerivedLibrary."Encounter Finished") + """; + private static final String EXPECTED_CQL_DERIVED = + """ + library opencds.withderivedlibrary.DerivedLibrary version '0.4' + + using FHIR version '5.0.1' + + include FHIRHelpers version '5.0.1' called FHIRHelpers + + context Patient + + define "Encounter Finished": + [Encounter] E + where E.status = 'finished' + """; + + private static final String EXPECTED_CQL_DERIVED_TWO_LAYERS = + """ + library opencds.withtwolayersderivedlibraries.WithTwoLayersDerivedLibraries version '0.5' + + using FHIR version '5.0.1' + + include DerivedLayer1a version '0.5' + include DerivedLayer1b version '0.5' + + parameter "Measurement Period" Interval + default Interval[@2022-01-01T00:00:00.0-06:00, @2023-01-01T00:00:00.0-06:00) + + context Patient + + define "Initial Population": + DerivedLayer1a."Initial Population" + + define "Denominator": + DerivedLayer1b."Denominator" + + define "Numerator": + DerivedLayer1b."Numerator" + """; + + private static final String EXPECTED_CQL_DERIVED_1_A = + """ + library opencds.withtwolayersderivedlibraries.DerivedLayer1a version '0.5' + + using FHIR version '5.0.1' + + include DerivedLayer2a version '0.5' + include DerivedLayer2b version '0.5' + + context Patient + + define "Initial Population": + DerivedLayer2a."Initial Population" + """; + + private static final String EXPECTED_CQL_DERIVED_1_B = + """ + library opencds.withtwolayersderivedlibraries.DerivedLayer1b version '0.5' + + using FHIR version '5.0.1' + + include DerivedLayer2a version '0.5' + include DerivedLayer2b version '0.5' + + context Patient + + define "Denominator": + DerivedLayer2a."Denominator" + + define "Numerator": + DerivedLayer2b."Numerator" + """; + + private static final String EXPECTED_CQL_DERIVED_2_A = + """ + library opencds.withtwolayersderivedlibraries.DerivedLayer2a version '0.5' + + using FHIR version '5.0.1' + + include FHIRHelpers version '5.0.1' called FHIRHelpers + + context Patient + + define "Initial Population": + exists ("Encounter Finished") + + define "Denominator": + exists ("Encounter Planned") + + define "Encounter Finished": + [Encounter] E + where E.status = 'finished' + + define "Encounter Planned": + [Encounter] E + where E.status = 'planned' + """; + + private static final String EXPECTED_CQL_DERIVED_2_B = + """ + library opencds.withtwolayersderivedlibraries.DerivedLayer2b version '0.5' + + using FHIR version '5.0.1' + + include FHIRHelpers version '5.0.1' called FHIRHelpers + + parameter "Measurement Period" Interval + default Interval[@2021-01-01T00:00:00.0-06:00, @2022-01-01T00:00:00.0-06:00) + + context Patient + + define "Numerator": + exists ("Encounter Triaged") + + define "Encounter Triaged": + [Encounter] E + where E.status = 'triaged' + """; + + private static final String EXPECTED_CQL_CROSS_SOURCE = + """ + library opencds.crosspackagesource.CrossPackageSource version '0.2' + + using FHIR version '5.0.1' + + include FHIRHelpers version '4.0.1' called FHIRHelpers + include opencds.crosspackagetarget.CrossPackageTarget version '0.3' called CrossPackageTarget + + parameter "Measurement Period" Interval + default Interval[@2020-01-01T00:00:00.0-06:00, @2021-01-01T00:00:00.0-06:00) + + context Patient + + define "Initial Population": + exists (CrossPackageTarget."Encounter Finished") + """; + + private static final String EXPECTED_CQL_CROSS_TARGET = + """ + library opencds.crosspackagetarget.CrossPackageTarget version '0.3' + + using FHIR version '5.0.1' + + include FHIRHelpers version '5.0.1' called FHIRHelpers + + context Patient + + define "Encounter Finished": + [Encounter] E + where E.status = 'finished' + """; + + @Override + protected FhirVersionEnum getExpectedFhirVersion() { + return FhirVersionEnum.R5; + } + // + // @Test + // void simpleAlpha() { + // simpleAlpha( + // Path.of(SIMPLE_ALPHA_TGZ), + // MEASURE_URL_ALPHA, + // LIBRARY_URL_ALPHA_WITH_VERSION, + // LIBRARY_URL_ALPHA_NO_VERSION, + // EXPECTED_CQL_ALPHA); + // } + // + // @Test + // void simpleBravo() { + // simpleBravo( + // Path.of(SIMPLE_BRAVO_TGZ), + // MEASURE_URL_BRAVO, + // LIBRARY_URL_BRAVO_WITH_VERSION, + // LIBRARY_URL_BRAVO_NO_VERSION, + // EXPECTED_CQL_BRAVO); + // } + // + // @Test + // void multiplePackages() { + // multiplePackages(EXPECTED_CQL_ALPHA, EXPECTED_CQL_BRAVO); + // } + // + // @Test + // void derivedLibrary() { + // derivedLibrary(EXPECTED_CQL_WITH_DERIVED, EXPECTED_CQL_DERIVED); + // } + // + // @Test + // void derivedLibraryTwoLayers() { + // derivedLibraryTwoLayers( + // EXPECTED_CQL_DERIVED_TWO_LAYERS, + // EXPECTED_CQL_DERIVED_1_A, + // EXPECTED_CQL_DERIVED_1_B, + // EXPECTED_CQL_DERIVED_2_A, + // EXPECTED_CQL_DERIVED_2_B); + // } + // + // @Test + // void crossPackage() { + // crossPackage(EXPECTED_CQL_CROSS_SOURCE, EXPECTED_CQL_CROSS_TARGET); + // } +} diff --git a/cqf-fhir-utility/src/test/resources/org/opencds/cqf/fhir/utility/npm/r4/crosspackagesource.tgz b/cqf-fhir-utility/src/test/resources/org/opencds/cqf/fhir/utility/npm/r4/crosspackagesource.tgz new file mode 100644 index 0000000000000000000000000000000000000000..79954af46aa7fd53c5e05b2789db665a03e89045 GIT binary patch literal 1614 zcmV-U2C?}ciwFos;<0D~|6_7*b8~QEV{2h&Wpi(Ja${vKbZ2@1?OI!N+Bg)>Grz*a zOSdzPv5gH$X1mj1!YzSKAb~(953;P-0{ODOa7j1+edSxqz3H}1JB_}9*w#6g&XJC! z?@sIf+R`@-2C7L5c&b|X!8fJ6Vef91=vFG z#j;Y8kB+30$d?qMbR^5U1BTxoE`SqYpu&%ABbwA@MMPvVasBta4m1(ANP1>q$A@U);rukW}>D(;ZJ&E=p&tC`bAL4axFxg4z? z+lb3IZ5&`gxJ$W6&9NW&+#j1B@%b|wI$>lNSHm{;O{C}FK4S%E`#;Jj zbew|Fk80IDa35sFLE>2AZd?K#ow62M_VKAK!l#v|q{@XKAZy7`y31PFc8Fsvc$1W4 z%Tz~4y;!l3a;n3)&3Bvi(U#+Wq3>gOm}=)&^d6s{?&7=0)Qy-k*ujd`s}^S}*+@nd zqd=+rnjv`|1R#=Yb%LQ)TcBQL3hVuFD0Lq$tjM2!d z9X~*^Vh*Li97+9at=L}-r5i)H`U@zLsfJJOwkj`LGv{2t?YNo+jqb#3-QbEex@|px z!ExABp>Q!i4ck3KSDh6A1G_1c2kEnG|zA8#@r z`&|=SRIaYrN!x5Mx4G{B&G5EG`{(LhwJY@fS}?lp5{OUn^`LJ;+Bc(tNHqJ(xS=-E zIwwtBX=ygqu}7v>ou3VF(O+>x26k=-G`=X?&_DYIf-KGSd4wfl{&54 zg_248vgxYE`}d#YK8g9e+#}nS+4&#i|38)gmyZ@J;?uKBRtkEM{u$yw`C zA8#>x+4SwT?d!+q+4D^qXV&vy^kVB^Ji8B86G-g41Vj^U+ky4o_aApk@3u-FcMXY$ zX~$r&e^>~xjgU^`oiBk_ZWG5e5Etor86^IxSs3grhBU;cfU}|^K0<`;wJwW z8UKF~{$Jk0|K%fDVaNZkivOqAZzua-&;JEQSfBq&QIr|~e?k7A5%}Fd1K3aGA+Q}A zL-2KU{(q+d)#zwn%w7j!K-pw`qD}`fD~%{=KQY!{%?9FM9jsD$9nsn;C4dLh zTTxZKC-aoo#=W#u!6Q4aWME)mU|?WiU|?WiU|?WiU|?WiU|?WiU|?WiU|?WiVEBpn M54Bb&0RT_{0Q1K|p#T5? literal 0 HcmV?d00001 diff --git a/cqf-fhir-utility/src/test/resources/org/opencds/cqf/fhir/utility/npm/r4/crosspackagetarget.tgz b/cqf-fhir-utility/src/test/resources/org/opencds/cqf/fhir/utility/npm/r4/crosspackagetarget.tgz new file mode 100644 index 0000000000000000000000000000000000000000..080274ad5fb9917c5644f89faf6c4a6a7b3392a1 GIT binary patch literal 1364 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 LbD;&X08jt`AxFdC literal 0 HcmV?d00001 diff --git a/cqf-fhir-utility/src/test/resources/org/opencds/cqf/fhir/utility/npm/r4/withtwolayersderivedlibraries.tgz b/cqf-fhir-utility/src/test/resources/org/opencds/cqf/fhir/utility/npm/r4/withtwolayersderivedlibraries.tgz new file mode 100644 index 0000000000000000000000000000000000000000..8d91473cc4857aab58cf580e86732906fd248ff3 GIT binary patch literal 2599 zcmV+?3fT1@iwFpd?XhSA|95G0XmodPY+-q2a&u&5a%pyDWNc|-a$$05WpgfcXL|c??7w4U6V^)wp({mmmU~p*#A(}q@A)84_w#kCv8~^vYNr)0QkyeJ+ z)NO4fb#rS;QulYSMM)l!M}J<)!&6<>bbs$tgi2-6D@C!~>(B9Vnqp{Xj~MQ4K%yX& zeW-qKH-$MS>=4=dH@d_ZxiU?2C6eLUa*1d5_pto-;>l`XHuR&ca^7*%-zOLzpAF1YOo*t;(-~WEspWKT1pPilmX*~aLWd1MZ=6|Zp z(ImxF#}tdF|6Q8@Pjnv*bnRRo={_x!>VK?{eg2Caw{ZUF;9<`{|3#K5?h(``pZ^`5 z|1G5WvOgkRPj^%;ARkd^hL5fl-v@z~-V~`0(gQMomkSlu&i@ySB*T`940iuJ z>;IVz$NwdYneqQDgZ=+@`+sJw_&+W^)h!RorTt2XGws;dF5z_A}M$71;__bxR#3PRnU zXfJqa8#%7!8Y9xQN=d6~uBLYq#iKD&9VT^tnY9NsCqo10BlU2sUC8M@zr6dI{PaBT zM#3Uam}_;u#hFTWA!Bqo0-B#{NI{cBIWAY47EwNAt9%Nohzp|E(x8UV1S*G%2)#hRh-Kj_jvwLcWQ>`DfVTciET@$Pup$HwF@>Ml+#{c>Qw7l z`)-;IUb+=Vcc{w1F{rm32{je!=*D$CC=5?Yw5A)wF|55O!g!ymR`A^vl{}nmRe}6mvQ`X==<$@h>(2uw52w;b&75EvLaiAjMY?Hi**Z<)4KXX3+$>5KFV*X<`lmBCAo}J}C3iIDK z^B=QD{*U>z{2zn!e_O$Stq;oo#doQIQM)Xv+{Up>3)Q&lm1slL_&^BnN z^&~fYeF61XV#kHH8r9%VqKoKQxKd2Ms+adAD}=Fn7Z-jZrUv4$#!NX7RJ}}Q)PQ2Nu=X^FK})T z+Fu5KueK7lbtF?c8P*{NuzF1^lx{fS>1tmRB~ufJlu)-4Uf1kKAX1gl#fdOHzo{hO zy_3r0_lNZR{Gk$FoZJ;JPAY*=PxMA_=y|K;q;H%a^re;Nn6q^V%^n9a0i}*AuF#+0 zbX4uaH8CY-PJEJ!J=!n^4QK6`f_*vP+4eOKSTO%%{(leuGb~5rPXO&A|BJw4H;-;Y zRY8VWFD$lRIPez#v!$v19~l-xP|W|^#{Wz2jn%?|l}vm@bg)uII9S+VuwW&6UC_H2 z>rE`$c-8xy1tg94E+82$SU~c42OzG9y(D2S^l9tv_L5d@0CWIn0Qk7xfm9QWP72b@ zo%nd%ZZ;^X*Y*^#`_DMB{zh;yS~0kI`iS7-{R^HOZ!CEB;=aMP=o&K%WER^*V6zTm zI8a0@$`-=B83fI@lUsO$gA>gEnE&6)|9lbe0I*a1KmKs_|Aqfc)BFF%{EyfFY?A-q z0r=CQ!uZZvi}dFQ3%|nu%Thrulv#TajKHM;eOjTDt7a zFjE#Ysji>KLLUl7L8cP1g@si-Zi{jl5+;>`iPUHPwgt{g`-YP`VM- z#2C+M_GHuNTU$1ctF`Bs;?Qj5-|WTQi?Q+;4-07U`hUFs ze{%m%n&wHK;#iu;_`gg1znuGjZ9A~HKK}{-rud)cIQ;iNHX`ra|7+=8@~hi{)2nO; zKD+DqIy;0d*dvTtxV*Xn%|I3w2 zSg>Hhf&~i}ELgB$!GZ+~7A#n>V8Ma~3l=O`uwcQ01q&7|Sg?GK{10fY Jy&wRf000K3MN9m zH~oI=zg#Nq-2W?5d2RhyL{Q`Smt6m^bpNMB^HguaIWD%5?iWxr57J5DC-Y!)YH(BR z`NXlgH+(^SlgouZu??=)Xm+0@a)B=hAR{)Sp^jr|15+2|PfDd&i9;%-SYK9od~BOt zFktX!#jfkPXxT35vD!_&T+UTJ)xrVxxO^L{e&}Hf+W~ipJ>uwju4#j4s?tAB)c{|^ z_V{Ox1L*ip4QcQw{`djtGJnVm@VVysm6f-+p+a*Ht|iY=YN034$jVh zcK&a~fAZ10vLvwc{}rA87s)wrxP=FdeaHTh=f5bf$AY53%Mv^PUjm=wgC*~!hyC@! z#rb0{pH7GT+eA$CsBm*`Wb*Xfjg){9WTiG7vHPD3JsK&PLEs)2i(9yd?OKJ50yv(L zP1EA;Dq)<0(2r`>J@qcgii5CucXR_A7E?AP&j2RVms6^ z76p@5V#^dq$Gupwm~yJaxXrhl^+C&VzrgbZ9j4m(6}^YYU!UT;ht!RjGuXk3)vFd~ zD%nUz6r%`Ke$9})t_Er(*XooYtG2+s>J-)c;ZW?}Us#=mCUx#ny*5_GnmxMhO!eE= zc+`_mk#%&BgsM3d19K$yuQjQ^7>YNBZuJ*Pq*INY*lne>Xw95+{kG$37BadMvvotN z;^?+@uMUpFrhxHe(B@ni)o&8LiAl9al_I;nQfEgg%`V1gcC+3wwfdZn zlxn?qMti+_-v_%FXy!s&9bX@GX|sMb8x7>~sL>9GQfrE&&TKg7c*B8C^?GdqcJmjL z#K)V=$9~sD7RZ$qJE6_?a+~Y^-wbbC&_7q_id}{0Yr*KYOA$FC*Mq)^pl?P4fok@Z zaRW9&os%Z1wlo`b?2)OR&d-LR->=hPbX_`cDkny>-lZd}CTNW-qc@OwutgjW=GxV1 zbJiZ81ntu^_||UPrzhp~8@3gG))_mqwlXsky|XXqO`zX9)5s3oh-|Cz>YyL$C(ik( z-nZ0&Oq)uprCAipYkso^bEDDs!B$rjOXM%ZF(O+>x2B7v?tN1+clAryoJ4c#NQiwV z)jHJeBGH7tY`Ti^{{7RuPv-om+ygt6Ut9i9+T{NNC(v&Aju63!8x0t-jHwv2`$>-3O}yB=KFUMhk6Q!g}xX z4?Crwwn`p$4T(pfV~DzcSg2|nAuQwNX!wxm5F?X2g1tSy!U}g`Wo8@KbKTr@Z?)vp z|0v{P`G1!G-^~BNlcfrK|LZmJ|MdFpWdG~=KQHs^@xLqyCC2|>kpE`{e)nqt`-wbK zZO0}^{W?1TpTmIDD77zSkE>w-Y%#`>$#K>TXLDwWqU)DDyY5$vy$ z@<2I>XEo7eXj7kQ4~%@Swd<>>$R@bXh);Z8G`&`d|Jf)_;9*{nudq-^lvEQ(pg7y(Mdg+SoB#umQZG_1_G< zFg(j1KBB-Tk&ORhKW_dvb$#Xf-)Oex=6_2w8Wl;|p!F800mRY^9jdRDC1-t z=Kil5 z`rP?XQx#o>{eKhsl5Ul?fP`KgVDfS5liE9}mJf&8ADNk2N#f$zDD3%$o@5e4P^5C? zlJ)P!0ZTGCQRKg?*O#yd_g%R_6ECofVOn49GTZGFuU3F2&&g31SQc)Yf;~%6i%9$Q zoR;WI=_zwL4I|>tR8&BQOS#89dn!AumDbGkEHX$l)^n!XPRsmb(Vr+eZ5Q!A#oKxA zS}EVt^V3aw_mqc`QW1|zwJz7V$WruV0#S8=@^clbStvq@ecWXfy9ZM;=-lJ(Wqhsm zACKMB>9}$Fh`R^3NIT%8Vc)^7#Vpf!kKOhoRy)qM7C9sB^5S}UB<$z5- zkDr+OObY=KhbA+#6)1-U$l`>;zirqi=?4G%2TOtn@ z@An7Bqt!i7tl^1$HrV4MQFk;v^+s3c+_ZOhU-~fXznkh4@E`Es|B(N5qY3=?miX_4 zpfC=|f5CrRQ)w;oAAJAWB>#O4@aFk2A1<#az2$`X&HR^_yN3S?oSG%19|D+vC-u&@ z^}HxSR)ptKt;Df%hy^W992(T)P=MLQ%|Z(6gGda%NZh_pzI4$@I8N=`zURjbCC?oc z(r}*S?h%WLnKR|s^B6&VfkX;A#$J$7?~ftjQapJi+eIO3&=-4-7eudPUOVt&e_a#5 zw9i5e%j1|C@@YdOHq*vSsy$ov<=)n{?vxhBE*EKck`Q!2cWM{{rAw z{|vky19^guzVUwe|0XSyhVg} zfdrV6unfluZ6&VcK$ivArmZyN;OW=@#0LU_Kp+qZ1OkCTAP@)y0)apv5C{YUfj}S- T2m}IwepCGegE5et08jt`vgOCt literal 0 HcmV?d00001 diff --git a/cqf-fhir-utility/src/test/resources/org/opencds/cqf/fhir/utility/npm/r5/simplealpha.tgz b/cqf-fhir-utility/src/test/resources/org/opencds/cqf/fhir/utility/npm/r5/simplealpha.tgz new file mode 100644 index 0000000000000000000000000000000000000000..a31cb42b492f3c592f4334b3444661ad433b2789 GIT binary patch literal 1546 zcmV+l2KD(LiwFoj__1gL|8r?=aBO8^Y;b5{E_7#l0PR^>bJ|D{_A|di#fvwUAcR0* zyEa<^Y(UsJG1vl>^1~nvlI+p3It)1L|Guru=3cVvu)Fbm1trb&9NlyE*CRI1=0ANw z(-uZ5b`O3CxEPzB99Jy8yodP(jxX>B%=BOn2s|IT(EY*N1b@VszG32zg^Exv9T$&| zN~~~HIV$pe=78d-hl7-Rq~ZLJ>?7RN6+wuf<@m|*OM8A{{};I;cfbhy-2WuF$12v` zK+$LG%0aR5_~)B{mY)Cg{9ir)%R-?@&;K`c{$Cg)7rC=s&oCW=tHjX}d*|8KYwiDX zskG_*=eg4I`Ck!uIRCkQ?*BJ>{(s9bfUJu>J8%_zJ#%pOgvln$A^RZ~8)}$2w>DD2 z19m;bwn8b#7Sdb`nRr8mz$KwX_kHIipZB5yC|YfnW3sk`Ek*TM+ttz)nP1Zg%!MC# zVLPge#(tXZ$I8bhMLCMC8kPomU!U_rd|ODxP0lqOs>ny7eAFa{Vm4;DS9?&JQZVGZj~CXZGaA%9x!>@VrmkO*Bz+U7>zY$gcoN97 zTC;aXdSXi^N?3<4Y3;|Gpi?hR+Y|d7^@Y1pOS+ePak*Yog8I@-nbmlC(0)Yy<3Ow{ zg^Rmt(CI1KkZ*XSrgSf!$zo4xi*kQD3Clr$li{GPkNOfP4@6D8FOcEjvY?ofFZTsb zY}ANik_WjiYCT9p9e&uK9Ce!wPL{gPsDJamZd5h7DVf88IgR6 z%YE4ynKz(62@vFij@nK0c%t^Fy4oZUBSS2QdW!WL!A{sE7QyLHR%B|7-aFaj`(}f8G-R?_lHwF8){ezfcrb`G1-6 z|2^{mjRb!=Fi6(P4orW2Xs|o~PkLI%|5G|W&yfFSNbyIgTTFJ6#(_&?E75!-0bevD zjSt4U;pPJmsQB27wfFKpAC{C4ucvryScVS`NAjFwI{`sqx`sT%TjuT$hy=KvL=hv~ z2AixEewM~VitWa6&g;ZJ3-%m+@J#VZ9x53ppBS zMCQhLjoS07uWxPJIPRv;KZ-Hak^f68)=tLGW3U)98lFQ?xV5rk+yL|CrGc&WpEgQw z*Gir?O^Iv3z!+%zv=C%z0eG(Y_%~;$5EUC^1G`&ng&pp|&P)%dUCmrIZ!zcge;GVc z{!jV;0{<@-*-D{UDRX7Y|KAY*pXdE>lD_)>-@^a7@(TYKDsUuF{=Ww#RGLX?@;rT> zTN9Z~-~kI_8ZEKQB+Q;U6j)%-hnb9FDI`#FByC~hfPA?SqaZ@CI$tD{agYn~133HV zxWWYg&3pmxZ>w2Gv_J$r_}5e9<7+siegns<;xP({|1A9ai3e|y(sp0t^^Go*3QtTj9f@uKN@^q|b z-@VjdBMs|+ea^pS&Z71vI(9L0#>U??n2*fiIH_>Bj{}(cf9n5N@Bf4XUlOSQe?$I1 zdA{16-}m_cl2Bgu|3$t?{r?{C|I&NBH8f!ReV&3W+cLnp?{=&A{nvn};TQhr?oi-8HFtE3HV-{Y)giWz-iB)Vj8j!gLp6J(5 w3JMAe3JMAe3JMAe3JMAe3JMAe3JMAe3JMAe3JMAeivJq^1l-a8vj9*40IlH}QUCw| literal 0 HcmV?d00001 diff --git a/cqf-fhir-utility/src/test/resources/org/opencds/cqf/fhir/utility/npm/r5/simplebravo.tgz b/cqf-fhir-utility/src/test/resources/org/opencds/cqf/fhir/utility/npm/r5/simplebravo.tgz new file mode 100644 index 0000000000000000000000000000000000000000..ca650e7fb5ab65ee153813fb9d6fb3759d2da96a GIT binary patch literal 1576 zcmV+@2G{u?iwFpL^s#6H|8r?=aBO8_a$$CFE_7#l0PR^>Q`<-o=GnhO|i5vEMSmrrFa-gV_7gdR)-I={P%5LVBsTVmw2;bzJii9-P6-^_SYk}!d5<> zqv;Vw2KKkU3%C@ko*Y-LKK+i%c~0Q@EoQp40YpKFd}w~_Z9=Fsc4*o717DYF;-0W4 z@T|BiaW$c|Me*~)L;4*uapilq5pU|cB&E+<`s4($xaSM|zsgm)Ek@ep{wL8LHn8bO z`e?RH57dUkpKtzIdj8Y%f8qQWckA^0e^clGiKY3-pY3!l+atK)qp{202Cnm3`@bfN ztImIct3HbbuolJo7Q=0F|G&}m|4WGhlzkkyk+0*cnTN~!OgUQ)<@c%BGUCFywUG

3Q;WA+QicB)(m$WW4*0J1L5-i#5wb{ej`wOtw3*&MkgeDzwXQieRbS4Jzf_` zq|O@c?jh;QM=H_dI)Z3+?ysZMra0|P++);}ZnY!jPVJ_8-L?Yul$kzj2x`A`k9vEN z+|;>~+k@z|tD6I%6=-ecPCiuSuF{d!-gFY{L3@)yzcbQ$3a9pEQ@-QLpnt~ewi2p6 ziIZCmqT6JwHf6I5?@&h=^d`HP?G~phm!8(U{TpF{}C!AJADy*Dv>(~;38W6hFlv7d6YL2#Sg8%L++akJCX?6a|v z^h*Z)=}5CB0)6$0HsiV5aiZmO>^42{Dd)j3@7gZ(pYu4{JT3I_)1n)3E zEtqo-eC74o!JgbSCV73B#}7|yGhLTAr3V@ve|}K@Px=3n`yZiRrTqUb@&8kdg2>1J z3ICU>(gOdN1j_$6$p2Ro{O5^5woYEa^p~dwFX#VROUw9wPN(M?@;^)|{)&A|2~TDm zxHNV$&sP%gVmI>qU^ue;N)!MUAKIz+L8TkQk_zGHCp@$qD+I%de8+RWh@kkrfdVUd z&fUj|M7Wt{5t{1~jGP6Imghsd>!*3n>*PL#{$xGb$s1Xd?C9>u^~0BCIi~MM-nt=v z8lQzeEbqtMLQV!6k-0Hm;`Y4j%dM>&$Cvr@uX4_SN z8eq+o zf38*)p6!1r{#;D?{|1mzX(p%1DSbx78zFXXO_WMe04#`U9m$tW#_XAGi3Rq&T`F0Q zP9g&*(j!bfkUuGji4vnDLa;HfQYv}Khx`G8{bOEXL;PmWA^N*UnUNh30T2G|6AJMa z98$kSU^Va%MI?L_e*P%HTH4`w_`Bk`{p643cDNcm-!rjPS|NQ`DkT@`Q)m(bKQvt` z88J)8OIg8w64vq53c`T-rF`hZRDDqx*Z%xie8U_j)+QtDW9E=e zzi2QYm~AiB-`>#aOXEL{|BLs3yjZQ(Y5adf@jrXM+MVCG#D7t$J&*rYp-SWb2Jip! zd%PkI(7t}3rz6L8EC}u|yVd*lYrs$O7yifQP!x__Kl!i@^Ocvz_f$&iUQoOgOc+Y> ze$T*Q-GBxx929TN;wzZg6zhwu?)u4q%o%u~UqmS=C@3f>C@3f>C@3f>C@3f>C@3f> aC@3f>C@3f>C@3g?H2e+mC7hlBPyhf@9x(<0 literal 0 HcmV?d00001 diff --git a/cqf-fhir-utility/src/test/resources/org/opencds/cqf/fhir/utility/npm/r5/withderivedlibrary.tgz b/cqf-fhir-utility/src/test/resources/org/opencds/cqf/fhir/utility/npm/r5/withderivedlibrary.tgz new file mode 100644 index 0000000000000000000000000000000000000000..201a3c71b18a67f559ac41efef3f91e181dfb13b GIT binary patch literal 1817 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 IHvpgj0M9;LaR2}S literal 0 HcmV?d00001 From d8da8d278f8f221ecd503e65e6638752ac813fc3 Mon Sep 17 00:00:00 2001 From: Luke deGruchy Date: Tue, 7 Oct 2025 13:04:35 -0400 Subject: [PATCH 2/5] Implement all tests. Leave open the question of whether to retrieve by resource class or adapter. --- ...java => BaseNpmPackageLoaderInMemory.java} | 71 +- .../fhir/utility/npm/NpmPackageLoader.java | 81 +- .../npm/r4/R4NpmPackageLoaderInMemory.java | 65 ++ .../npm/r5/R5NpmPackageLoaderInMemory.java | 58 ++ .../npm/BaseNpmResourceInfoForCqlTest.java | 265 +------ .../npm/r4/NpmResourceHolderR4Test.java | 381 +++++++-- .../npm/r5/NpmResourceHolderR5Test.java | 734 +++++++++++++----- .../utility/npm/r4/crosspackagetarget.tgz | Bin 1364 -> 1464 bytes 8 files changed, 1133 insertions(+), 522 deletions(-) rename cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/{R4NpmPackageLoaderInMemory.java => BaseNpmPackageLoaderInMemory.java} (83%) create mode 100644 cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/r4/R4NpmPackageLoaderInMemory.java create mode 100644 cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/r5/R5NpmPackageLoaderInMemory.java diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/R4NpmPackageLoaderInMemory.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/BaseNpmPackageLoaderInMemory.java similarity index 83% rename from cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/R4NpmPackageLoaderInMemory.java rename to cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/BaseNpmPackageLoaderInMemory.java index 4c1566e208..b2beca3126 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/R4NpmPackageLoaderInMemory.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/BaseNpmPackageLoaderInMemory.java @@ -6,6 +6,14 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; +import org.hl7.cql.model.NamespaceInfo; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IPrimitiveType; +import org.hl7.fhir.utilities.npm.NpmPackage; +import org.opencds.cqf.fhir.utility.adapter.ILibraryAdapter; +import org.opencds.cqf.fhir.utility.adapter.IResourceAdapter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; @@ -21,16 +29,9 @@ import java.util.StringJoiner; import java.util.regex.Pattern; import java.util.stream.Collectors; -import org.hl7.cql.model.NamespaceInfo; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.hl7.fhir.instance.model.api.IPrimitiveType; -import org.hl7.fhir.utilities.npm.NpmPackage; -import org.opencds.cqf.fhir.utility.adapter.ILibraryAdapter; -import org.opencds.cqf.fhir.utility.adapter.IResourceAdapter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; // LUKETODO: redo java + /** * Simplistic implementation of {@link NpmPackageLoader} that loads NpmPackages from the classpath * and stores {@link NpmResourceHolder}s in a Map. This class is recommended for testing @@ -39,47 +40,45 @@ * Optionally uses a custom {@link NpmNamespaceManager} but can also resolve all NamespaceInfos * by extracting them from all loaded packages at construction time. */ -public class R4NpmPackageLoaderInMemory implements NpmPackageLoader { +abstract public class BaseNpmPackageLoaderInMemory implements NpmPackageLoader { - private static final Logger logger = LoggerFactory.getLogger(R4NpmPackageLoaderInMemory.class); + private static final Logger logger = LoggerFactory.getLogger(BaseNpmPackageLoaderInMemory.class); private static final Pattern PATTERN_PIPE = Pattern.compile("\\|"); public static final String FAILED_TO_LOAD_RESOURCE_TEMPLATE = "Failed to load resource: %s"; private final Set npmPackages; - // private final Map resourceUrlToResource = new HashMap<>(); - // private final Map libraryUrlToPackage = new HashMap<>(); private final NpmNamespaceManager npmNamespaceManager; - public static R4NpmPackageLoaderInMemory fromNpmPackageAbsolutePath(List tgzPaths) { + public static BaseNpmPackageLoaderInMemory fromNpmPackageAbsolutePath(List tgzPaths) { return fromNpmPackageAbsolutePath(null, tgzPaths); } - public static R4NpmPackageLoaderInMemory fromNpmPackageAbsolutePath( + public static BaseNpmPackageLoaderInMemory fromNpmPackageAbsolutePath( NpmNamespaceManager npmNamespaceManager, List tgzPaths) { final Set npmPackages = buildNpmPackagesFromAbsolutePath(tgzPaths); - return new R4NpmPackageLoaderInMemory(npmPackages, npmNamespaceManager); + return null; } - public static R4NpmPackageLoaderInMemory fromNpmPackageClasspath(Class clazz, Path... tgzPaths) { + public static BaseNpmPackageLoaderInMemory fromNpmPackageClasspath(Class clazz, Path... tgzPaths) { return fromNpmPackageClasspath(null, clazz, tgzPaths); } - public static R4NpmPackageLoaderInMemory fromNpmPackageClasspath( + public static BaseNpmPackageLoaderInMemory fromNpmPackageClasspath( @Nullable NpmNamespaceManager npmNamespaceManager, Class clazz, Path... tgzPaths) { return fromNpmPackageClasspath(npmNamespaceManager, clazz, Arrays.asList(tgzPaths)); } - public static R4NpmPackageLoaderInMemory fromNpmPackageClasspath(Class clazz, List tgzPaths) { + public static BaseNpmPackageLoaderInMemory fromNpmPackageClasspath(Class clazz, List tgzPaths) { return fromNpmPackageClasspath(null, clazz, tgzPaths); } - public static R4NpmPackageLoaderInMemory fromNpmPackageClasspath( + public static BaseNpmPackageLoaderInMemory fromNpmPackageClasspath( @Nullable NpmNamespaceManager npmNamespaceManager, Class clazz, List tgzPaths) { final Set npmPackages = buildNpmPackageFromClasspath(clazz, tgzPaths); - return new R4NpmPackageLoaderInMemory(npmPackages, npmNamespaceManager); + return null; } record UrlAndVersion(String url, @Nullable String version) { @@ -146,29 +145,7 @@ private boolean doesPackageMatch(IPrimitiveType resourceUrl, NpmPackage } } - @Override - public FhirContext getFhirContext() { - return FhirContext.forR4Cached(); - } - - // @Override - // public Optional loadLibraryByUrl(String url) { - // for (NpmPackage npmPackage : libraryUrlToPackage.values()) { - // final FhirContext fhirContext = getFhirContext(npmPackage); - // try (InputStream libraryInputStream = npmPackage.loadByCanonical(url)) { - // if (libraryInputStream != null) { - // final IResourceAdapter resourceAdapter = IAdapterFactory.createAdapterForResource( - // fhirContext.newJsonParser().parseResource(libraryInputStream)); - // if (resourceAdapter instanceof ILibraryAdapter libraryAdapter) { - // return Optional.of(libraryAdapter); - // } - // } - // } catch (IOException exception) { - // throw new InternalErrorException(exception); - // } - // } - // return Optional.empty(); - // } + abstract public FhirContext getFhirContext(); @Override public NpmNamespaceManager getNamespaceManager() { @@ -176,14 +153,14 @@ public NpmNamespaceManager getNamespaceManager() { } @Nonnull - private static Set buildNpmPackagesFromAbsolutePath(List tgzPaths) { + protected static Set buildNpmPackagesFromAbsolutePath(List tgzPaths) { return tgzPaths.stream() - .map(R4NpmPackageLoaderInMemory::getNpmPackageFromAbsolutePaths) + .map(BaseNpmPackageLoaderInMemory::getNpmPackageFromAbsolutePaths) .collect(Collectors.toUnmodifiableSet()); } @Nonnull - private static Set buildNpmPackageFromClasspath(Class clazz, List tgzPaths) { + protected static Set buildNpmPackageFromClasspath(Class clazz, List tgzPaths) { return tgzPaths.stream() .map(path -> getNpmPackageFromClasspath(clazz, path)) .collect(Collectors.toUnmodifiableSet()); @@ -211,7 +188,7 @@ private static NpmPackage getNpmPackageFromClasspath(Class clazz, Path tgzCla } } - private R4NpmPackageLoaderInMemory(Set npmPackages, @Nullable NpmNamespaceManager npmNamespaceManager) { + protected BaseNpmPackageLoaderInMemory(Set npmPackages, @Nullable NpmNamespaceManager npmNamespaceManager) { if (npmNamespaceManager == null) { var namespaceInfos = npmPackages.stream() diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/NpmPackageLoader.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/NpmPackageLoader.java index 4ea89d92dc..4b21c095b5 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/NpmPackageLoader.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/NpmPackageLoader.java @@ -1,7 +1,9 @@ package org.opencds.cqf.fhir.utility.npm; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.repository.IRepository; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import jakarta.annotation.Nullable; import java.util.List; import java.util.Optional; @@ -13,7 +15,10 @@ import org.hl7.elm.r1.VersionedIdentifier; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IPrimitiveType; +import org.opencds.cqf.fhir.utility.FhirVersions; +import org.opencds.cqf.fhir.utility.adapter.IAdapterFactory; import org.opencds.cqf.fhir.utility.adapter.ILibraryAdapter; +import org.opencds.cqf.fhir.utility.adapter.IResourceAdapter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -90,6 +95,8 @@ public FhirContext getFhirContext() { return FhirContext.forR4Cached(); } }; + String LIBRARY = "Library"; + String MEASURE = "Measure"; // LUKETODO: redo java /** @@ -99,6 +106,7 @@ public FhirContext getFhirContext() { */ default Optional loadNpmResource( Class resourceClass, IPrimitiveType canonicalResourceUrl) { + final Optional optResource = loadNpmResource(canonicalResourceUrl); if (optResource.isEmpty()) { @@ -115,6 +123,38 @@ default Optional loadNpmResource( return Optional.of(resourceClass.cast(resource)); } + // LUKETODO: which is the better API of the two? + default Optional loadNpmResourceAsAdapter( + IPrimitiveType canonicalResourceUrl) { + + return loadNpmResource(canonicalResourceUrl) + .map(this::toResourceAdapter); + + } + +// default Optional loadNpmResource( +// Class resourceClass, IPrimitiveType canonicalResourceUrl) { +// final Optional optResource = loadNpmResource(canonicalResourceUrl); +// +// if (optResource.isEmpty()) { +// return Optional.empty(); +// } +// +// final IBaseResource resource = optResource.get(); +// +// if (!resourceClass.isInstance(resource)) { +// throw new IllegalArgumentException("Expected resource to be a %s, but was a %s" +// .formatted(resourceClass.getSimpleName(), resource.fhirType())); +// } +// +// return Optional.of(resourceClass.cast(resource)); +// } + + + default IAdapterFactory getAdapterFactory() { + return IAdapterFactory.forFhirVersion(getFhirContext().getVersion().getVersion()); + } + /** * Obtain the resource corresponding to the provided type-enclosed String URL from the NPM * package repo. Implementors must figure out how to retrieve the resource by querying one @@ -200,7 +240,8 @@ default Optional findLibraryFromUnrelatedNpmPackage(ModelIdenti default Optional loadLibraryByUrl(String libraryUrl) { return toLibraryAdapter( - loadNpmResource(getLibraryClass(), toPrimitiveType(libraryUrl)).orElse(null)); + loadNpmResource(getLibraryClass(), toPrimitiveType(libraryUrl)) + .orElse(null)); } default Optional toLibraryAdapter(IBaseResource resource) { @@ -254,6 +295,44 @@ default Class getLibraryClass() { } } + default IResourceAdapter toResourceAdapter(IBaseResource resource) { + if (resource == null) { + return null; + } + if (FhirVersionEnum.R4 == getFhirContext().getVersion().getVersion()) { + switch (resource.fhirType()) { + case LIBRARY -> { + return new org.opencds.cqf.fhir.utility.adapter.r4.LibraryAdapter( + (org.hl7.fhir.r4.model.Library) resource); + } + case MEASURE -> { + return new org.opencds.cqf.fhir.utility.adapter.r4.MeasureAdapter( + (org.hl7.fhir.r4.model.Measure) resource); + } + default -> throw new IllegalArgumentException( + "Expected resource to be a Library or Measure, but was a " + resource.fhirType()); + } + } + + if (FhirVersionEnum.R5 == getFhirContext().getVersion().getVersion()) { + switch (resource.fhirType()) { + case LIBRARY -> { + return new org.opencds.cqf.fhir.utility.adapter.r5.LibraryAdapter( + (org.hl7.fhir.r5.model.Library) resource); + } + case MEASURE -> { + return new org.opencds.cqf.fhir.utility.adapter.r5.MeasureAdapter( + (org.hl7.fhir.r5.model.Measure) resource); + } + default -> throw new IllegalArgumentException( + "Expected resource to be a Library or Measure, but was a " + resource.fhirType()); + } + } + + throw new InvalidRequestException("Unsupported FHIR version: %s" + .formatted(getFhirContext().getVersion().getVersion().toString())); + } + private String getUrl(VersionedIdentifier versionedIdentifier) { // We need this case because the CQL engine will do the right thing and populate the system // in the cross-package target case diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/r4/R4NpmPackageLoaderInMemory.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/r4/R4NpmPackageLoaderInMemory.java new file mode 100644 index 0000000000..113478aecd --- /dev/null +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/r4/R4NpmPackageLoaderInMemory.java @@ -0,0 +1,65 @@ +package org.opencds.cqf.fhir.utility.npm.r4; + +import ca.uhn.fhir.context.FhirContext; +import jakarta.annotation.Nullable; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import org.hl7.fhir.utilities.npm.NpmPackage; +import org.opencds.cqf.fhir.utility.npm.BaseNpmPackageLoaderInMemory; +import org.opencds.cqf.fhir.utility.npm.NpmNamespaceManager; +import org.opencds.cqf.fhir.utility.npm.NpmPackageLoader; + +// LUKETODO: redo java +/** + * Simplistic implementation of {@link NpmPackageLoader} that loads NpmPackages from the classpath + * and stores {@link NpmResourceHolder}s in a Map. This class is recommended for testing + * and NOT for production. + *

tgzPaths) { + return fromNpmPackageAbsolutePath(null, tgzPaths); + } + + public static R4NpmPackageLoaderInMemory fromNpmPackageAbsolutePath( + NpmNamespaceManager npmNamespaceManager, List tgzPaths) { + final Set npmPackages = buildNpmPackagesFromAbsolutePath(tgzPaths); + + return new R4NpmPackageLoaderInMemory(npmPackages, npmNamespaceManager); + } + + public static R4NpmPackageLoaderInMemory fromNpmPackageClasspath(Class clazz, Path... tgzPaths) { + return fromNpmPackageClasspath(null, clazz, tgzPaths); + } + + public static R4NpmPackageLoaderInMemory fromNpmPackageClasspath( + @Nullable NpmNamespaceManager npmNamespaceManager, Class clazz, Path... tgzPaths) { + return fromNpmPackageClasspath(npmNamespaceManager, clazz, Arrays.asList(tgzPaths)); + } + + public static R4NpmPackageLoaderInMemory fromNpmPackageClasspath(Class clazz, List tgzPaths) { + return fromNpmPackageClasspath(null, clazz, tgzPaths); + } + + public static R4NpmPackageLoaderInMemory fromNpmPackageClasspath( + @Nullable NpmNamespaceManager npmNamespaceManager, Class clazz, List tgzPaths) { + final Set npmPackages = buildNpmPackageFromClasspath(clazz, tgzPaths); + + return new R4NpmPackageLoaderInMemory(npmPackages, npmNamespaceManager); + } + + public R4NpmPackageLoaderInMemory( + Set npmPackages, @Nullable NpmNamespaceManager npmNamespaceManager) { + super(npmPackages, npmNamespaceManager); + } + + @Override + public FhirContext getFhirContext() { + return FhirContext.forR4Cached(); + } +} diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/r5/R5NpmPackageLoaderInMemory.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/r5/R5NpmPackageLoaderInMemory.java new file mode 100644 index 0000000000..c2c3b6d56d --- /dev/null +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/r5/R5NpmPackageLoaderInMemory.java @@ -0,0 +1,58 @@ +package org.opencds.cqf.fhir.utility.npm.r5; + +import ca.uhn.fhir.context.FhirContext; +import jakarta.annotation.Nullable; +import org.hl7.fhir.utilities.npm.NpmPackage; +import org.opencds.cqf.fhir.utility.npm.BaseNpmPackageLoaderInMemory; +import org.opencds.cqf.fhir.utility.npm.NpmNamespaceManager; +import org.opencds.cqf.fhir.utility.npm.NpmPackageLoader; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.List; +import java.util.Set; + +// LUKETODO: redo java + +public class R5NpmPackageLoaderInMemory extends BaseNpmPackageLoaderInMemory implements NpmPackageLoader { + + public static R5NpmPackageLoaderInMemory fromNpmPackageAbsolutePath(List tgzPaths) { + return fromNpmPackageAbsolutePath(null, tgzPaths); + } + + public static R5NpmPackageLoaderInMemory fromNpmPackageAbsolutePath( + NpmNamespaceManager npmNamespaceManager, List tgzPaths) { + final Set npmPackages = buildNpmPackagesFromAbsolutePath(tgzPaths); + + return new R5NpmPackageLoaderInMemory(npmPackages, npmNamespaceManager); + } + + public static R5NpmPackageLoaderInMemory fromNpmPackageClasspath(Class clazz, Path... tgzPaths) { + return fromNpmPackageClasspath(null, clazz, tgzPaths); + } + + public static R5NpmPackageLoaderInMemory fromNpmPackageClasspath( + @Nullable NpmNamespaceManager npmNamespaceManager, Class clazz, Path... tgzPaths) { + return fromNpmPackageClasspath(npmNamespaceManager, clazz, Arrays.asList(tgzPaths)); + } + + public static R5NpmPackageLoaderInMemory fromNpmPackageClasspath(Class clazz, List tgzPaths) { + return fromNpmPackageClasspath(null, clazz, tgzPaths); + } + + public static R5NpmPackageLoaderInMemory fromNpmPackageClasspath( + @Nullable NpmNamespaceManager npmNamespaceManager, Class clazz, List tgzPaths) { + final Set npmPackages = buildNpmPackageFromClasspath(clazz, tgzPaths); + + return new R5NpmPackageLoaderInMemory(npmPackages, npmNamespaceManager); + } + + public R5NpmPackageLoaderInMemory( + Set npmPackages, @Nullable NpmNamespaceManager npmNamespaceManager) { + super(npmPackages, npmNamespaceManager); + } + + @Override + public FhirContext getFhirContext() { + return FhirContext.forR5Cached(); + } +} diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/BaseNpmResourceInfoForCqlTest.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/BaseNpmResourceInfoForCqlTest.java index 40cd29487c..7dbc52a3ef 100644 --- a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/BaseNpmResourceInfoForCqlTest.java +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/BaseNpmResourceInfoForCqlTest.java @@ -4,16 +4,8 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import ca.uhn.fhir.context.FhirVersionEnum; -import jakarta.annotation.Nonnull; -import jakarta.annotation.Nullable; -import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.List; -import org.hl7.fhir.instance.model.api.ICompositeType; -import org.opencds.cqf.fhir.utility.adapter.IAdapterFactory; -import org.opencds.cqf.fhir.utility.adapter.IAttachmentAdapter; -import org.opencds.cqf.fhir.utility.adapter.ILibraryAdapter; public abstract class BaseNpmResourceInfoForCqlTest { @@ -28,6 +20,15 @@ public abstract class BaseNpmResourceInfoForCqlTest { protected static final String DERIVED_LIBRARY_ID = "DerivedLibrary"; protected static final String DERIVED_LIBRARY = DERIVED_LIBRARY_ID; + protected static final String DERIVED_LAYER_1_A = "DerivedLayer1a"; + protected static final String DERIVED_LAYER_1_B = "DerivedLayer1b"; + protected static final String DERIVED_LAYER_2_A = "DerivedLayer2a"; + protected static final String DERIVED_LAYER_2_B = "DerivedLayer2b"; + protected static final String CROSS_PACKAGE_SOURCE = "crosspackagesource"; + protected static final String CROSS_PACKAGE_SOURCE_ID = "CrossPackageSource"; + protected static final String CROSS_PACKAGE_TARGET = "crosspackagetarget"; + protected static final String CROSS_PACKAGE_TARGET_ID = "CrossPackageTarget"; + protected static final String NAMESPACE_PREFIX = "opencds."; protected static final String WITH_TWO_LAYERS_DERIVED_LIBRARIES = "withtwolayersderivedlibraries"; @@ -37,15 +38,9 @@ public abstract class BaseNpmResourceInfoForCqlTest { protected static final String SIMPLE_BRAVO_NAMESPACE = NAMESPACE_PREFIX + SIMPLE_BRAVO_LOWER; protected static final String WITH_DERIVED_NAMESPACE = NAMESPACE_PREFIX + WITH_DERIVED_LIBRARY_LOWER; protected static final String WITH_TWO_LAYERS_NAMESPACE = NAMESPACE_PREFIX + WITH_TWO_LAYERS_DERIVED_LIBRARIES; + protected static final String CROSS_PACKAGE_SOURCE_NAMESPACE = NAMESPACE_PREFIX + CROSS_PACKAGE_SOURCE; + protected static final String CROSS_PACKAGE_TARGET_NAMESPACE = NAMESPACE_PREFIX + CROSS_PACKAGE_TARGET; - protected static final String DERIVED_LAYER_1_A = "DerivedLayer1a"; - protected static final String DERIVED_LAYER_1_B = "DerivedLayer1b"; - protected static final String DERIVED_LAYER_2_A = "DerivedLayer2a"; - protected static final String DERIVED_LAYER_2_B = "DerivedLayer2b"; - protected static final String CROSS_PACKAGE_SOURCE = "crosspackagesource"; - protected static final String CROSS_PACKAGE_SOURCE_ID = "CrossPackageSource"; - protected static final String CROSS_PACKAGE_TARGET = "crosspackagetarget"; - protected static final String CROSS_PACKAGE_TARGET_ID = "CrossPackageTarget"; protected static final String SIMPLE_ALPHA_TGZ = SIMPLE_ALPHA_LOWER + DOT_TGZ; protected static final String SIMPLE_BRAVO_TGZ = SIMPLE_BRAVO_LOWER + DOT_TGZ; @@ -59,15 +54,16 @@ public abstract class BaseNpmResourceInfoForCqlTest { protected static final String SLASH_LIBRARY_SLASH = "/Library/"; private static final String PIPE = "|"; - private static final String VERSION_0_1 = "0.1"; - private static final String VERSION_0_2 = "0.2"; - private static final String VERSION_0_4 = "0.4"; - private static final String VERSION_0_5 = "0.5"; + protected static final String VERSION_0_1 = "0.1"; + protected static final String VERSION_0_2 = "0.2"; + protected static final String VERSION_0_3 = "0.3"; + protected static final String VERSION_0_4 = "0.4"; + protected static final String VERSION_0_5 = "0.5"; protected static final String SIMPLE_ALPHA_NAMESPACE_URL = "http://simplealpha.npm.opencds.org"; protected static final String SIMPLE_BRAVO_NAMESPACE_URL = "http://simplebravo.npm.opencds.org"; protected static final String WITH_DERIVED_URL = "http://withderivedlibrary.npm.opencds.org"; - protected static final String WITH_DERIVED_TWO_LAYERS_URL = "http://withtwolayersderivedlibraries.npm.opencds.org"; + protected static final String WITH_TWO_LAYERS_DERIVED_URL = "http://withtwolayersderivedlibraries.npm.opencds.org"; protected static final String CROSS_PACKAGE_SOURCE_URL = "http://crosspackagesource.npm.opencds.org"; protected static final String CROSS_PACKAGE_TARGET_URL = "http://crosspackagetarget.npm.opencds.org"; @@ -80,14 +76,15 @@ public abstract class BaseNpmResourceInfoForCqlTest { protected static final String MEASURE_URL_WITH_DERIVED_LIBRARY_WITH_VERSION = MEASURE_URL_WITH_DERIVED_LIBRARY + PIPE + VERSION_0_4; protected static final String MEASURE_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES = - WITH_DERIVED_TWO_LAYERS_URL + SLASH_MEASURE_SLASH + WITH_TWO_LAYERS_DERIVED_LIBRARIES_UPPER; + WITH_TWO_LAYERS_DERIVED_URL + SLASH_MEASURE_SLASH + WITH_TWO_LAYERS_DERIVED_LIBRARIES_UPPER; protected static final String MEASURE_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES_WITH_VERSION = MEASURE_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES + PIPE + VERSION_0_5; protected static final String MEASURE_URL_CROSS_PACKAGE_SOURCE = CROSS_PACKAGE_SOURCE_URL + SLASH_MEASURE_SLASH + CROSS_PACKAGE_SOURCE_ID; - protected static final String MEASURE_URL_CROSS_PACKAGE_SOURCE_WITH_VERSION = - MEASURE_URL_CROSS_PACKAGE_SOURCE + PIPE + VERSION_0_2; + + protected static final String MEASURE_URL_CROSS_PACKAGE_TARGET = + CROSS_PACKAGE_TARGET_URL + SLASH_MEASURE_SLASH + CROSS_PACKAGE_TARGET_ID; protected static final String LIBRARY_URL_ALPHA_NO_VERSION = SIMPLE_ALPHA_NAMESPACE_URL + SLASH_LIBRARY_SLASH + SIMPLE_ALPHA_MIXED; @@ -103,167 +100,31 @@ public abstract class BaseNpmResourceInfoForCqlTest { protected static final String LIBRARY_URL_DERIVED_LIBRARY = WITH_DERIVED_URL + SLASH_LIBRARY_SLASH + DERIVED_LIBRARY; - protected static final String LIBRARY_URL_CROSS_PACKAGE_SOURCE = + protected static final String LIBRARY_URL_CROSS_PACKAGE_SOURCE_NO_VERSION = CROSS_PACKAGE_SOURCE_URL + SLASH_LIBRARY_SLASH + CROSS_PACKAGE_SOURCE_ID; protected static final String LIBRARY_URL_CROSS_PACKAGE_SOURCE_WITH_VERSION = - LIBRARY_URL_CROSS_PACKAGE_SOURCE + PIPE + VERSION_0_2; - protected static final String LIBRARY_URL_CROSS_PACKAGE_TARGET = + LIBRARY_URL_CROSS_PACKAGE_SOURCE_NO_VERSION + PIPE + VERSION_0_2; + protected static final String LIBRARY_URL_CROSS_PACKAGE_TARGET_NO_VERSION = CROSS_PACKAGE_TARGET_URL + SLASH_LIBRARY_SLASH + CROSS_PACKAGE_TARGET_ID; + protected static final String LIBRARY_URL_CROSS_PACKAGE_TARGET_WITH_VERSION = + LIBRARY_URL_CROSS_PACKAGE_TARGET_NO_VERSION + PIPE + VERSION_0_3; protected static final String LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES_NO_VERSION = - WITH_DERIVED_TWO_LAYERS_URL + SLASH_LIBRARY_SLASH + WITH_TWO_LAYERS_DERIVED_LIBRARIES_UPPER; + WITH_TWO_LAYERS_DERIVED_URL + SLASH_LIBRARY_SLASH + WITH_TWO_LAYERS_DERIVED_LIBRARIES_UPPER; protected static final String LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES_WITH_VERSION = LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES_NO_VERSION + PIPE + VERSION_0_5; protected static final String LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_1A = - WITH_DERIVED_TWO_LAYERS_URL + SLASH_LIBRARY_SLASH + DERIVED_LAYER_1_A; + WITH_TWO_LAYERS_DERIVED_URL + SLASH_LIBRARY_SLASH + DERIVED_LAYER_1_A; protected static final String LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_1B = - WITH_DERIVED_TWO_LAYERS_URL + SLASH_LIBRARY_SLASH + DERIVED_LAYER_1_B; + WITH_TWO_LAYERS_DERIVED_URL + SLASH_LIBRARY_SLASH + DERIVED_LAYER_1_B; protected static final String LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_2A = - WITH_DERIVED_TWO_LAYERS_URL + SLASH_LIBRARY_SLASH + DERIVED_LAYER_2_A; + WITH_TWO_LAYERS_DERIVED_URL + SLASH_LIBRARY_SLASH + DERIVED_LAYER_2_A; protected static final String LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_2B = - WITH_DERIVED_TWO_LAYERS_URL + SLASH_LIBRARY_SLASH + DERIVED_LAYER_2_B; + WITH_TWO_LAYERS_DERIVED_URL + SLASH_LIBRARY_SLASH + DERIVED_LAYER_2_B; - // LUKETODO: protected abstract FhirVersionEnum getExpectedFhirVersion(); - // protected void simpleAlpha( - // Path tgzPath, - // String measureUrl, - // String expectedLibraryUrlFromMeasure, - // String expectedLibraryUrlWithinLibrary, - // String expectedCql) { - // - // simpleCommon( - // tgzPath, - // List.of(new NamespaceInfo(SIMPLE_ALPHA_NAMESPACE, SIMPLE_ALPHA_NAMESPACE_URL)), - // measureUrl, - // expectedLibraryUrlFromMeasure, - // expectedLibraryUrlWithinLibrary, - // expectedCql); - // } - // - // protected void simpleBravo( - // Path tgzPath, - // String measureUrl, - // String expectedLibraryUrlFromMeasure, - // String expectedLibraryUrlWithinLibrary, - // String expectedCql) { - // - // simpleCommon( - // tgzPath, - // List.of(new NamespaceInfo(SIMPLE_BRAVO_NAMESPACE, SIMPLE_BRAVO_NAMESPACE_URL)), - // measureUrl, - // expectedLibraryUrlFromMeasure, - // expectedLibraryUrlWithinLibrary, - // expectedCql); - // } - - // protected void simpleCommon( - // Path tgzPath, - // List expectedNamespaceInfos, - // String measureUrl, - // String expectedLibraryUrlFromMeasure, - // String expectedLibraryUrlWithinLibrary, - // String expectedCql) { - // final R4NpmPackageLoaderInMemory loader = setup(tgzPath); - // - // final Optional optMeasure = - // loader.loadNpmResource(Measure.class, new CanonicalType(measureUrl)); - // - // verifyMeasure(measureUrl, expectedLibraryUrlFromMeasure, optMeasure); - // verifyLibrary( - // expectedLibraryUrlWithinLibrary, - // expectedCql, - // npmResourceHolder.getOptMainLibrary().orElse(null)); - // } - // - // protected void multiplePackages(String expectedCqlAlpha, String expectedCqlBravo) { - // final R4NpmPackageLoaderInMemory loader = setup( - // Stream.of(SIMPLE_ALPHA_TGZ, SIMPLE_BRAVO_TGZ).map(Paths::get).toArray(Path[]::new)); - // - // final Optional optMeasureAlpha = - // loader.loadNpmResource( - // Measure.class, - // new CanonicalType(MEASURE_URL_ALPHA)); - // final Optional optMeasureBravo = - // loader.loadNpmResource( - // Measure.class, - // new CanonicalType(MEASURE_URL_BRAVO)); - // - // assertEquals( - // List.of(new NamespaceInfo(SIMPLE_ALPHA_NAMESPACE, SIMPLE_ALPHA_NAMESPACE_URL)), - // resourceInfoAlpha.getNamespaceInfos()); - // assertEquals( - // List.of(new NamespaceInfo(SIMPLE_BRAVO_NAMESPACE, SIMPLE_BRAVO_NAMESPACE_URL)), - // resourceInfoBravo.getNamespaceInfos()); - // - // verifyMeasure(MEASURE_URL_ALPHA, LIBRARY_URL_ALPHA_WITH_VERSION, resourceInfoAlpha); - // verifyLibrary( - // LIBRARY_URL_ALPHA_NO_VERSION, - // expectedCqlAlpha, - // resourceInfoAlpha.getOptMainLibrary().orElse(null)); - // - // verifyMeasure(MEASURE_URL_BRAVO, LIBRARY_URL_BRAVO_WITH_VERSION, resourceInfoBravo); - // verifyLibrary( - // LIBRARY_URL_BRAVO_NO_VERSION, - // expectedCqlBravo, - // resourceInfoBravo.getOptMainLibrary().orElse(null)); - // } - - // protected void derivedLibrary(String expectedCql, String expectedCqlDerived) { - // - // final R4NpmPackageLoaderInMemory loader = setup(WITH_DERIVED_LIBRARY_TGZ); - // - // final NpmResourceHolder resourceInfoWithNoVersion = - // loader.loadNpmResources(new CanonicalType(MEASURE_URL_WITH_DERIVED_LIBRARY)); - // - // sanityCheckNpmResourceHolder(resourceInfoWithNoVersion); - // - // var expectedNamespaceInfos = List.of(new NamespaceInfo(WITH_DERIVED_NAMESPACE, WITH_DERIVED_URL)); - // - // assertEquals(expectedNamespaceInfos, resourceInfoWithNoVersion.getNamespaceInfos()); - // - // verifyMeasure( - // MEASURE_URL_WITH_DERIVED_LIBRARY, - // LIBRARY_URL_WITH_DERIVED_LIBRARY_WITH_VERSION, - // resourceInfoWithNoVersion); - // - // final NpmResourceHolder resourceInfoWithVersion = - // loader.loadNpmResources(new CanonicalType(MEASURE_URL_WITH_DERIVED_LIBRARY_WITH_VERSION)); - // sanityCheckNpmResourceHolder(resourceInfoWithVersion); - // assertEquals(expectedNamespaceInfos, resourceInfoWithVersion.getNamespaceInfos()); - // - // verifyMeasure( - // MEASURE_URL_WITH_DERIVED_LIBRARY, - // LIBRARY_URL_WITH_DERIVED_LIBRARY_WITH_VERSION, - // resourceInfoWithVersion); - // verifyLibrary( - // LIBRARY_URL_WITH_DERIVED_LIBRARY_NO_VERSION, - // expectedCql, - // resourceInfoWithVersion.getOptMainLibrary().orElse(null)); - // - // final ILibraryAdapter derivedLibraryFromNoVersion = resourceInfoWithVersion - // .findMatchingLibrary(new VersionedIdentifier().withId(DERIVED_LIBRARY_ID)) - // .orElse(null); - // - // verifyLibrary(LIBRARY_URL_DERIVED_LIBRARY, expectedCqlDerived, derivedLibraryFromNoVersion); - // - // final ILibraryAdapter derivedLibraryFromVersion = resourceInfoWithVersion - // .findMatchingLibrary( - // new VersionedIdentifier().withId(DERIVED_LIBRARY_ID).withVersion("0.4")) - // .orElse(null); - // - // verifyLibrary(LIBRARY_URL_DERIVED_LIBRARY, expectedCqlDerived, derivedLibraryFromVersion); - // - // final ILibraryAdapter derivedLibraryFromBadVersion = resourceInfoWithVersion - // .findMatchingLibrary( - // new VersionedIdentifier().withId(DERIVED_LIBRARY_ID).withVersion("bad")) - // .orElse(null); - // - // assertNull(derivedLibraryFromBadVersion); - // } - // protected void derivedLibraryTwoLayers( // String expectedCql, // String expectedCqlDerived1a, @@ -373,68 +234,4 @@ public abstract class BaseNpmResourceInfoForCqlTest { // verifyLibrary(LIBRARY_URL_CROSS_PACKAGE_TARGET, expectedCqlTarget, optLibraryTarget.get()); // } - private void verifyLibrary(String expectedLibraryUrl, String expectedCql, @Nullable ILibraryAdapter library) { - assertNotNull(library); - - assertEquals( - getExpectedFhirVersion(), library.fhirContext().getVersion().getVersion()); - - assertEquals(expectedLibraryUrl, library.getUrl()); - - final List attachments = library.getContent(); - - assertEquals(1, attachments.size()); - - final ICompositeType attachment = attachments.get(0); - - final IAdapterFactory adapterFactory = IAdapterFactory.forFhirVersion( - library.fhirContext().getVersion().getVersion()); - - final IAttachmentAdapter adaptedAttachment = adapterFactory.createAttachment(attachment); - - assertEquals("text/cql", adaptedAttachment.getContentType()); - final byte[] attachmentData = adaptedAttachment.getData(); - final String cql = new String(attachmentData, StandardCharsets.UTF_8); - - assertEquals(expectedCql, cql); - } - // - // protected void verifyMeasure(String measureUrl, String expectedLibraryUrl, Optional - // measureAdapter) { - // - // final Optional optMeasure = npmResourceHolder.getMeasure(); - // assertTrue(optMeasure.isPresent(), "Could not find measure with url: %s".formatted(measureUrl)); - // - // final IMeasureAdapter measure = optMeasure.get(); - // assertEquals( - // getExpectedFhirVersion(), measure.fhirContext().getVersion().getVersion()); - // assertEquals(measureUrl, measure.getUrl()); - // - // final List libraryUrls = measure.getLibrary(); - // assertEquals(1, libraryUrls.size()); - // final String libraryUrl = libraryUrls.get(0); - // assertEquals(expectedLibraryUrl, libraryUrl); - // } - // - // protected void verifyMeasure(String measureUrl, String expectedLibraryUrl, Optional - // measureAdapter) { - // - // final Optional optMeasure = npmResourceHolder.getMeasure(); - // assertTrue(optMeasure.isPresent(), "Could not find measure with url: %s".formatted(measureUrl)); - // - // final IMeasureAdapter measure = optMeasure.get(); - // assertEquals( - // getExpectedFhirVersion(), measure.fhirContext().getVersion().getVersion()); - // assertEquals(measureUrl, measure.getUrl()); - // - // final List libraryUrls = measure.getLibrary(); - // assertEquals(1, libraryUrls.size()); - // final String libraryUrl = libraryUrls.get(0); - // assertEquals(expectedLibraryUrl, libraryUrl); - // } - - @Nonnull - protected R4NpmPackageLoaderInMemory setup(Path... tgzPaths) { - return R4NpmPackageLoaderInMemory.fromNpmPackageClasspath(getClass(), tgzPaths); - } } diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/r4/NpmResourceHolderR4Test.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/r4/NpmResourceHolderR4Test.java index f80c21646c..f6c26fe4bd 100644 --- a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/r4/NpmResourceHolderR4Test.java +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/r4/NpmResourceHolderR4Test.java @@ -1,10 +1,12 @@ package org.opencds.cqf.fhir.utility.npm.r4; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import ca.uhn.fhir.context.FhirVersionEnum; +import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; import java.nio.charset.StandardCharsets; import java.nio.file.Path; @@ -13,13 +15,16 @@ import java.util.Optional; import java.util.stream.Stream; import org.hl7.cql.model.NamespaceInfo; +import org.hl7.elm.r1.VersionedIdentifier; import org.hl7.fhir.r4.model.Attachment; import org.hl7.fhir.r4.model.CanonicalType; import org.hl7.fhir.r4.model.Library; import org.hl7.fhir.r4.model.Measure; import org.junit.jupiter.api.Test; +import org.opencds.cqf.fhir.utility.adapter.ILibraryAdapter; +import org.opencds.cqf.fhir.utility.adapter.r4.LibraryAdapter; +import org.opencds.cqf.fhir.utility.npm.BaseNpmPackageLoaderInMemory; import org.opencds.cqf.fhir.utility.npm.BaseNpmResourceInfoForCqlTest; -import org.opencds.cqf.fhir.utility.npm.R4NpmPackageLoaderInMemory; @SuppressWarnings("squid:S2699") class NpmResourceHolderR4Test extends BaseNpmResourceInfoForCqlTest { @@ -241,23 +246,25 @@ class NpmResourceHolderR4Test extends BaseNpmResourceInfoForCqlTest { protected FhirVersionEnum getExpectedFhirVersion() { return FhirVersionEnum.R4; } - @Test void simpleAlpha() { - final R4NpmPackageLoaderInMemory loader = setup(Path.of(SIMPLE_ALPHA_TGZ)); + final BaseNpmPackageLoaderInMemory loader = setup(Path.of(SIMPLE_ALPHA_TGZ)); final Optional optMeasure = - loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_ALPHA)); + loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_ALPHA)); - verifyMeasure(optMeasure.orElse(null), MEASURE_URL_ALPHA, LIBRARY_URL_ALPHA_WITH_VERSION); + verifyMeasure(optMeasure.orElse(null), MEASURE_URL_ALPHA, VERSION_0_1, + LIBRARY_URL_ALPHA_WITH_VERSION); final Optional optLibrary = - loader.loadNpmResource(Library.class, new CanonicalType(LIBRARY_URL_ALPHA_WITH_VERSION)); + loader.loadNpmResource(Library.class, + new CanonicalType(LIBRARY_URL_ALPHA_WITH_VERSION)); - verifyLibrary(optLibrary.orElse(null), LIBRARY_URL_ALPHA_NO_VERSION, EXPECTED_CQL_ALPHA); + verifyLibrary(optLibrary.orElse(null), LIBRARY_URL_ALPHA_NO_VERSION, VERSION_0_1, + EXPECTED_CQL_ALPHA); final List allNamespaceInfos = - loader.getNamespaceManager().getAllNamespaceInfos(); + loader.getNamespaceManager().getAllNamespaceInfos(); assertEquals(1, allNamespaceInfos.size()); @@ -269,20 +276,23 @@ void simpleAlpha() { @Test void simpleBravo() { - final R4NpmPackageLoaderInMemory loader = setup(Path.of(SIMPLE_BRAVO_TGZ)); + final BaseNpmPackageLoaderInMemory loader = setup(Path.of(SIMPLE_BRAVO_TGZ)); final Optional optMeasure = - loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_BRAVO)); + loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_BRAVO)); - verifyMeasure(optMeasure.orElse(null), MEASURE_URL_BRAVO, LIBRARY_URL_BRAVO_WITH_VERSION); + verifyMeasure(optMeasure.orElse(null), MEASURE_URL_BRAVO, VERSION_0_1, + LIBRARY_URL_BRAVO_WITH_VERSION); final Optional optLibrary = - loader.loadNpmResource(Library.class, new CanonicalType(LIBRARY_URL_BRAVO_WITH_VERSION)); + loader.loadNpmResource(Library.class, + new CanonicalType(LIBRARY_URL_BRAVO_WITH_VERSION)); - verifyLibrary(optLibrary.orElse(null), LIBRARY_URL_BRAVO_NO_VERSION, EXPECTED_CQL_BRAVO); + verifyLibrary(optLibrary.orElse(null), LIBRARY_URL_BRAVO_NO_VERSION, VERSION_0_1, + EXPECTED_CQL_BRAVO); final List allNamespaceInfos = - loader.getNamespaceManager().getAllNamespaceInfos(); + loader.getNamespaceManager().getAllNamespaceInfos(); assertEquals(1, allNamespaceInfos.size()); @@ -294,37 +304,319 @@ void simpleBravo() { @Test void multiplePackages() { - final R4NpmPackageLoaderInMemory loader = R4NpmPackageLoaderInMemory.fromNpmPackageClasspath( - getClass(), - Stream.of(SIMPLE_ALPHA_TGZ, SIMPLE_BRAVO_TGZ).map(Paths::get).toList()); + final BaseNpmPackageLoaderInMemory loader = setup(Stream.of(SIMPLE_ALPHA_TGZ, SIMPLE_BRAVO_TGZ).map(Paths::get).toList()); final List allNamespaceInfos = - loader.getNamespaceManager().getAllNamespaceInfos(); + loader.getNamespaceManager().getAllNamespaceInfos(); assertEquals(2, allNamespaceInfos.size()); - assertTrue(allNamespaceInfos.contains(new NamespaceInfo(SIMPLE_ALPHA_NAMESPACE, SIMPLE_ALPHA_NAMESPACE_URL))); - assertTrue(allNamespaceInfos.contains(new NamespaceInfo(SIMPLE_BRAVO_NAMESPACE, SIMPLE_BRAVO_NAMESPACE_URL))); + assertTrue(allNamespaceInfos.contains( + new NamespaceInfo(SIMPLE_ALPHA_NAMESPACE, SIMPLE_ALPHA_NAMESPACE_URL))); + assertTrue(allNamespaceInfos.contains( + new NamespaceInfo(SIMPLE_BRAVO_NAMESPACE, SIMPLE_BRAVO_NAMESPACE_URL))); final Optional optMeasureAlpha = - loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_ALPHA)); + loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_ALPHA)); - verifyMeasure(optMeasureAlpha.orElse(null), MEASURE_URL_ALPHA, LIBRARY_URL_ALPHA_WITH_VERSION); + verifyMeasure(optMeasureAlpha.orElse(null), MEASURE_URL_ALPHA, VERSION_0_1, + LIBRARY_URL_ALPHA_WITH_VERSION); final Optional optLibraryAlpha = - loader.loadNpmResource(Library.class, new CanonicalType(LIBRARY_URL_ALPHA_WITH_VERSION)); + loader.loadNpmResource(Library.class, + new CanonicalType(LIBRARY_URL_ALPHA_WITH_VERSION)); - verifyLibrary(optLibraryAlpha.orElse(null), LIBRARY_URL_ALPHA_NO_VERSION, EXPECTED_CQL_ALPHA); + verifyLibrary(optLibraryAlpha.orElse(null), LIBRARY_URL_ALPHA_NO_VERSION, VERSION_0_1, + EXPECTED_CQL_ALPHA); final Optional optMeasureBravo = - loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_ALPHA)); + loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_ALPHA)); - verifyMeasure(optMeasureBravo.orElse(null), MEASURE_URL_ALPHA, LIBRARY_URL_ALPHA_WITH_VERSION); + verifyMeasure(optMeasureBravo.orElse(null), MEASURE_URL_ALPHA, VERSION_0_1, + LIBRARY_URL_ALPHA_WITH_VERSION); final Optional optLibraryBravo = - loader.loadNpmResource(Library.class, new CanonicalType(LIBRARY_URL_BRAVO_WITH_VERSION)); + loader.loadNpmResource(Library.class, + new CanonicalType(LIBRARY_URL_BRAVO_WITH_VERSION)); + + verifyLibrary(optLibraryBravo.orElse(null), LIBRARY_URL_BRAVO_NO_VERSION, VERSION_0_1, + EXPECTED_CQL_BRAVO); + } + + @Test + void derivedLibrary() { + + final BaseNpmPackageLoaderInMemory loader = setup(WITH_DERIVED_LIBRARY_TGZ); + + final List allNamespaceInfos = + loader.getNamespaceManager().getAllNamespaceInfos(); + + assertEquals(1, allNamespaceInfos.size()); + + assertTrue(allNamespaceInfos.contains( + new NamespaceInfo(WITH_DERIVED_NAMESPACE, WITH_DERIVED_URL))); + + final Optional optMeasureWithDerived = + loader.loadNpmResource(Measure.class, + new CanonicalType(MEASURE_URL_WITH_DERIVED_LIBRARY)); + + verifyMeasure(optMeasureWithDerived.orElse(null), MEASURE_URL_WITH_DERIVED_LIBRARY, + VERSION_0_4, LIBRARY_URL_WITH_DERIVED_LIBRARY_WITH_VERSION); + + final Optional optLibraryWithDerived = + loader.loadNpmResource(Library.class, + new CanonicalType(LIBRARY_URL_WITH_DERIVED_LIBRARY_WITH_VERSION)); + + verifyLibrary( + optLibraryWithDerived.orElse(null), + LIBRARY_URL_WITH_DERIVED_LIBRARY_NO_VERSION, + VERSION_0_4, + EXPECTED_CQL_WITH_DERIVED); + + final Optional optLibraryDerived = + loader.loadNpmResource(Library.class, new CanonicalType(LIBRARY_URL_DERIVED_LIBRARY)); + + verifyLibrary( + optLibraryDerived.orElse(null), + LIBRARY_URL_DERIVED_LIBRARY, + VERSION_0_4, + EXPECTED_CQL_DERIVED); + } + + @Test + void derivedLibraryTwoLayers() { + + final BaseNpmPackageLoaderInMemory loader = setup(WITH_TWO_LAYERS_DERIVED_LIBRARIES_TGZ); + + final List allNamespaceInfos = + loader.getNamespaceManager().getAllNamespaceInfos(); + + assertEquals(1, allNamespaceInfos.size()); + + assertTrue(allNamespaceInfos.contains(new NamespaceInfo(WITH_TWO_LAYERS_NAMESPACE, WITH_TWO_LAYERS_DERIVED_URL))); + + final Optional optMeasureWithTwoDerived = + loader.loadNpmResource(Measure.class, + new CanonicalType(MEASURE_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES)); + + verifyMeasure( + optMeasureWithTwoDerived.orElse(null), + MEASURE_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES, + VERSION_0_5, + LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES_WITH_VERSION); + + final Optional optLibraryWithTwoDerived = + loader.loadNpmResource(Library.class, + new CanonicalType(LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES_WITH_VERSION)); + + verifyLibrary( + optLibraryWithTwoDerived.orElse(null), + LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES_NO_VERSION, + VERSION_0_5, + EXPECTED_CQL_DERIVED_TWO_LAYERS); + + final Optional optLibraryWithTwoDerived1a = + loader.loadNpmResource(Library.class, + new CanonicalType(LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_1A)); + + verifyLibrary( + optLibraryWithTwoDerived1a.orElse(null), + LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_1A, + VERSION_0_5, + EXPECTED_CQL_DERIVED_1_A); + + final Optional optLibraryWithTwoDerived1b = + loader.loadNpmResource(Library.class, + new CanonicalType(LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_1B)); + + verifyLibrary( + optLibraryWithTwoDerived1b.orElse(null), + LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_1B, + VERSION_0_5, + EXPECTED_CQL_DERIVED_1_B); + + final Optional optLibraryWithTwoDerived2a = + loader.loadNpmResource(Library.class, + new CanonicalType(LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_2A)); + + verifyLibrary( + optLibraryWithTwoDerived2a.orElse(null), + LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_2A, + VERSION_0_5, + EXPECTED_CQL_DERIVED_2_A); + + final Optional optLibraryWithTwoDerived2b = + loader.loadNpmResource(Library.class, + new CanonicalType(LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_2B)); + + verifyLibrary( + optLibraryWithTwoDerived2b.orElse(null), + LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_2B, + VERSION_0_5, + EXPECTED_CQL_DERIVED_2_B); + + final ILibraryAdapter libraryAdapter1a = loader.findMatchingLibrary(new VersionedIdentifier() + .withId(DERIVED_LAYER_1_A) + .withVersion(VERSION_0_5) + .withSystem(WITH_TWO_LAYERS_DERIVED_URL)) + .orElse(null); + + verifyLibrary( + libraryAdapter1a, + LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_1A, + VERSION_0_5, + EXPECTED_CQL_DERIVED_1_A); + + final ILibraryAdapter libraryAdapter1b = loader.findMatchingLibrary(new VersionedIdentifier() + .withId(DERIVED_LAYER_1_B) + .withVersion(VERSION_0_5) + .withSystem(WITH_TWO_LAYERS_DERIVED_URL)) + .orElse(null); + + verifyLibrary( + libraryAdapter1b, + LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_1B, + VERSION_0_5, + EXPECTED_CQL_DERIVED_1_B); + + final ILibraryAdapter libraryAdapter2a = loader.findMatchingLibrary(new VersionedIdentifier() + .withId(DERIVED_LAYER_2_A) + .withVersion(VERSION_0_5) + .withSystem(WITH_TWO_LAYERS_DERIVED_URL)) + .orElse(null); + + verifyLibrary( + libraryAdapter2a, + LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_2A, + VERSION_0_5, + EXPECTED_CQL_DERIVED_2_A); + + final ILibraryAdapter libraryAdapter2b = loader.findMatchingLibrary(new VersionedIdentifier() + .withId(DERIVED_LAYER_2_B) + .withVersion(VERSION_0_5) + .withSystem(WITH_TWO_LAYERS_DERIVED_URL)) + .orElse(null); + + verifyLibrary( + libraryAdapter2b, + LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_2B, + VERSION_0_5, + EXPECTED_CQL_DERIVED_2_B); + } + + @Test + void crossPackage() { + + final BaseNpmPackageLoaderInMemory loader = setup(CROSS_PACKAGE_SOURCE_TGZ, CROSS_PACKAGE_TARGET_TGZ); + + final List allNamespaceInfos = + loader.getNamespaceManager().getAllNamespaceInfos(); + + assertEquals(2, allNamespaceInfos.size()); + + assertTrue(allNamespaceInfos.contains(new NamespaceInfo(CROSS_PACKAGE_SOURCE_NAMESPACE, CROSS_PACKAGE_SOURCE_URL))); + assertTrue(allNamespaceInfos.contains(new NamespaceInfo(CROSS_PACKAGE_TARGET_NAMESPACE, CROSS_PACKAGE_TARGET_URL))); + + final Optional optMeasureCrossSource = + loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_CROSS_PACKAGE_SOURCE)); + + verifyMeasure( + optMeasureCrossSource.orElse(null), + MEASURE_URL_CROSS_PACKAGE_SOURCE, + VERSION_0_2, + LIBRARY_URL_CROSS_PACKAGE_SOURCE_WITH_VERSION); + + final Optional optLibraryCrossSource = + loader.loadNpmResource(Library.class, new CanonicalType(LIBRARY_URL_CROSS_PACKAGE_SOURCE_WITH_VERSION)); + + verifyLibrary( + optLibraryCrossSource.orElse(null), + LIBRARY_URL_CROSS_PACKAGE_SOURCE_NO_VERSION, + VERSION_0_2, + EXPECTED_CQL_CROSS_SOURCE); + + final Optional optMeasureCrossTarget = + loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_CROSS_PACKAGE_TARGET)); + + verifyMeasure( + optMeasureCrossTarget.orElse(null), + MEASURE_URL_CROSS_PACKAGE_TARGET, + VERSION_0_3, + LIBRARY_URL_CROSS_PACKAGE_TARGET_WITH_VERSION); + + final Optional optLibraryCrossTarget = + loader.loadNpmResource(Library.class, new CanonicalType(LIBRARY_URL_CROSS_PACKAGE_TARGET_WITH_VERSION)); + + verifyLibrary( + optLibraryCrossTarget.orElse(null), + LIBRARY_URL_CROSS_PACKAGE_TARGET_NO_VERSION, + VERSION_0_3, + EXPECTED_CQL_CROSS_TARGET); + + final ILibraryAdapter libraryAdapterSource = loader.findMatchingLibrary(new VersionedIdentifier() + .withId(CROSS_PACKAGE_SOURCE_ID) + .withVersion(VERSION_0_2) + .withSystem(CROSS_PACKAGE_SOURCE_URL)) + .orElse(null); + + verifyLibrary( + libraryAdapterSource, + LIBRARY_URL_CROSS_PACKAGE_SOURCE_NO_VERSION, + VERSION_0_2, + EXPECTED_CQL_CROSS_SOURCE); + + final ILibraryAdapter libraryAdapterTarget = loader.findMatchingLibrary(new VersionedIdentifier() + .withId(CROSS_PACKAGE_TARGET_ID) + .withVersion(VERSION_0_3) + .withSystem(CROSS_PACKAGE_TARGET_URL)) + .orElse(null); + + verifyLibrary( + libraryAdapterTarget, + LIBRARY_URL_CROSS_PACKAGE_TARGET_NO_VERSION, + VERSION_0_3, + EXPECTED_CQL_CROSS_TARGET); + } + + protected void verifyMeasure(@Nullable Measure measure, String measureUrl, String version, String expectedLibraryUrl) { + + assertNotNull(measure, "Could not find measure with url: %s".formatted(measureUrl)); + + assertEquals(measureUrl, measure.getUrl()); + + assertEquals(version, measure.getVersion()); + + final List libraryUrls = measure.getLibrary(); + assertEquals(1, libraryUrls.size()); + final CanonicalType libraryUrl = libraryUrls.get(0); + assertEquals(expectedLibraryUrl, libraryUrl.asStringValue()); + } + + private void verifyLibrary(@Nullable ILibraryAdapter libraryAdapter, String expectedLibraryUrl, String version, String expectedCql) { - verifyLibrary(optLibraryBravo.orElse(null), LIBRARY_URL_BRAVO_NO_VERSION, EXPECTED_CQL_BRAVO); + assertNotNull(libraryAdapter, "Could not find library with url: %s".formatted(expectedLibraryUrl)); + + assertInstanceOf(LibraryAdapter.class, libraryAdapter); + + verifyLibrary(((LibraryAdapter)libraryAdapter).get(), expectedLibraryUrl, version, expectedCql); + } + + private void verifyLibrary(@Nullable Library library, String expectedLibraryUrl, String version, String expectedCql) { + + assertNotNull(library, "Could not find library with url: %s".formatted(expectedLibraryUrl)); + + assertEquals(expectedLibraryUrl, library.getUrl()); + + assertEquals(version, library.getVersion()); + + final List attachments = library.getContent(); + assertEquals(1, attachments.size()); + final Attachment attachment = attachments.get(0); + assertEquals("text/cql", attachment.getContentType()); + final byte[] attachmentData = attachment.getData(); + final String cql = new String(attachmentData, StandardCharsets.UTF_8); + + assertEquals(expectedCql, cql); } protected void verifyMeasure(@Nullable Measure measure, String measureUrl, String expectedLibraryUrl) { @@ -358,28 +650,13 @@ private void verifyLibrary(@Nullable Library library, String expectedLibraryUrl, assertEquals(expectedCql, cql); } - // @Test - // void multiplePackages() { - // multiplePackages(EXPECTED_CQL_ALPHA, EXPECTED_CQL_BRAVO); - // } - // - // @Test - // void derivedLibrary() { - // derivedLibrary(EXPECTED_CQL_WITH_DERIVED, EXPECTED_CQL_DERIVED); - // } - // - // @Test - // void derivedLibraryTwoLayers() { - // derivedLibraryTwoLayers( - // EXPECTED_CQL_DERIVED_TWO_LAYERS, - // EXPECTED_CQL_DERIVED_1_A, - // EXPECTED_CQL_DERIVED_1_B, - // EXPECTED_CQL_DERIVED_2_A, - // EXPECTED_CQL_DERIVED_2_B); - // } - // - // @Test - // void crossPackage() { - // crossPackage(EXPECTED_CQL_CROSS_SOURCE, EXPECTED_CQL_CROSS_TARGET); - // } + @Nonnull + protected R4NpmPackageLoaderInMemory setup(List tgzPaths) { + return R4NpmPackageLoaderInMemory.fromNpmPackageClasspath(getClass(), tgzPaths); + } + + @Nonnull + protected R4NpmPackageLoaderInMemory setup(Path... tgzPaths) { + return R4NpmPackageLoaderInMemory.fromNpmPackageClasspath(getClass(), tgzPaths); + } } diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/r5/NpmResourceHolderR5Test.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/r5/NpmResourceHolderR5Test.java index a54cd2e6cf..0bb03fe4de 100644 --- a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/r5/NpmResourceHolderR5Test.java +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/r5/NpmResourceHolderR5Test.java @@ -1,6 +1,29 @@ package org.opencds.cqf.fhir.utility.npm.r5; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + import ca.uhn.fhir.context.FhirVersionEnum; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; +import org.hl7.cql.model.NamespaceInfo; +import org.hl7.elm.r1.VersionedIdentifier; +import org.hl7.fhir.r5.model.Attachment; +import org.hl7.fhir.r5.model.CanonicalType; +import org.hl7.fhir.r5.model.Library; +import org.hl7.fhir.r5.model.Measure; +import org.junit.jupiter.api.Test; +import org.opencds.cqf.fhir.utility.adapter.ILibraryAdapter; +import org.opencds.cqf.fhir.utility.adapter.r5.LibraryAdapter; +import org.opencds.cqf.fhir.utility.npm.BaseNpmPackageLoaderInMemory; import org.opencds.cqf.fhir.utility.npm.BaseNpmResourceInfoForCqlTest; @SuppressWarnings("squid:S2699") @@ -9,260 +32,595 @@ class NpmResourceHolderR5Test extends BaseNpmResourceInfoForCqlTest { protected FhirVersionEnum fhirVersion = FhirVersionEnum.R5; private static final String EXPECTED_CQL_ALPHA = - """ - library opencds.simplealpha.SimpleAlpha + """ + library opencds.simplealpha.SimpleAlpha + + using FHIR version '5.0.1' + + include FHIRHelpers version '5.0.1' called FHIRHelpers + + parameter "Measurement Period" Interval + default Interval[@2021-01-01T00:00:00.0-06:00, @2022-01-01T00:00:00.0-06:00) + + context Patient + + define "Initial Population": + exists ("Encounter Finished") + + define "Encounter Finished": + [Encounter] E + where E.status = 'finished' + """; + private static final String EXPECTED_CQL_BRAVO = + """ + library opencds.simplealpha.SimpleBravo + + using FHIR version '5.0.1' + + include FHIRHelpers version '4.0.1' called FHIRHelpers + + parameter "Measurement Period" Interval + default Interval[@2024-01-01T00:00:00.0-06:00, @2025-01-01T00:00:00.0-06:00) + + context Patient + + define "Initial Population": + exists ("Encounter Planned") + + define "Encounter Planned": + [Encounter] E + where E.status = 'planned' + """; + private static final String EXPECTED_CQL_WITH_DERIVED = + """ + library opencds.withderivedlibrary WithDerivedLibrary version '0.4' + + using FHIR version '5.0.1' + + include FHIRHelpers version '5.0.1' called FHIRHelpers + include DerivedLibrary version '0.4' + + parameter "Measurement Period" Interval + default Interval[@2021-01-01T00:00:00.0-06:00, @2022-01-01T00:00:00.0-06:00) + + context Patient + + define "Initial Population": + exists (DerivedLibrary."Encounter Finished") + """; + private static final String EXPECTED_CQL_DERIVED = + """ + library opencds.withderivedlibrary.DerivedLibrary version '0.4' + + using FHIR version '5.0.1' + + include FHIRHelpers version '5.0.1' called FHIRHelpers + + context Patient + + define "Encounter Finished": + [Encounter] E + where E.status = 'finished' + """; - using FHIR version '5.0.1' + private static final String EXPECTED_CQL_DERIVED_TWO_LAYERS = + """ + library opencds.withtwolayersderivedlibraries.WithTwoLayersDerivedLibraries version '0.5' + + using FHIR version '5.0.1' + + include DerivedLayer1a version '0.5' + include DerivedLayer1b version '0.5' + + parameter "Measurement Period" Interval + default Interval[@2022-01-01T00:00:00.0-06:00, @2023-01-01T00:00:00.0-06:00) + + context Patient + + define "Initial Population": + DerivedLayer1a."Initial Population" + + define "Denominator": + DerivedLayer1b."Denominator" + + define "Numerator": + DerivedLayer1b."Numerator" + """; - include FHIRHelpers version '5.0.1' called FHIRHelpers + private static final String EXPECTED_CQL_DERIVED_1_A = + """ + library opencds.withtwolayersderivedlibraries.DerivedLayer1a version '0.5' + + using FHIR version '5.0.1' + + include DerivedLayer2a version '0.5' + include DerivedLayer2b version '0.5' + + context Patient + + define "Initial Population": + DerivedLayer2a."Initial Population" + """; - parameter "Measurement Period" Interval - default Interval[@2021-01-01T00:00:00.0-06:00, @2022-01-01T00:00:00.0-06:00) + private static final String EXPECTED_CQL_DERIVED_1_B = + """ + library opencds.withtwolayersderivedlibraries.DerivedLayer1b version '0.5' + + using FHIR version '5.0.1' + + include DerivedLayer2a version '0.5' + include DerivedLayer2b version '0.5' + + context Patient + + define "Denominator": + DerivedLayer2a."Denominator" + + define "Numerator": + DerivedLayer2b."Numerator" + """; - context Patient + private static final String EXPECTED_CQL_DERIVED_2_A = + """ + library opencds.withtwolayersderivedlibraries.DerivedLayer2a version '0.5' + + using FHIR version '5.0.1' + + include FHIRHelpers version '5.0.1' called FHIRHelpers + + context Patient + + define "Initial Population": + exists ("Encounter Finished") + + define "Denominator": + exists ("Encounter Planned") + + define "Encounter Finished": + [Encounter] E + where E.status = 'finished' + + define "Encounter Planned": + [Encounter] E + where E.status = 'planned' + """; - define "Initial Population": - exists ("Encounter Finished") + private static final String EXPECTED_CQL_DERIVED_2_B = + """ + library opencds.withtwolayersderivedlibraries.DerivedLayer2b version '0.5' + + using FHIR version '5.0.1' + + include FHIRHelpers version '5.0.1' called FHIRHelpers + + parameter "Measurement Period" Interval + default Interval[@2021-01-01T00:00:00.0-06:00, @2022-01-01T00:00:00.0-06:00) + + context Patient + + define "Numerator": + exists ("Encounter Triaged") + + define "Encounter Triaged": + [Encounter] E + where E.status = 'triaged' + """; - define "Encounter Finished": - [Encounter] E - where E.status = 'finished' - """; - private static final String EXPECTED_CQL_BRAVO = - """ - library opencds.simplealpha.SimpleBravo + private static final String EXPECTED_CQL_CROSS_SOURCE = + """ + library opencds.crosspackagesource.CrossPackageSource version '0.2' + + using FHIR version '5.0.1' + + include FHIRHelpers version '4.0.1' called FHIRHelpers + include opencds.crosspackagetarget.CrossPackageTarget version '0.3' called CrossPackageTarget + + parameter "Measurement Period" Interval + default Interval[@2020-01-01T00:00:00.0-06:00, @2021-01-01T00:00:00.0-06:00) + + context Patient + + define "Initial Population": + exists (CrossPackageTarget."Encounter Finished") + """; - using FHIR version '5.0.1' + private static final String EXPECTED_CQL_CROSS_TARGET = + """ + library opencds.crosspackagetarget.CrossPackageTarget version '0.3' + + using FHIR version '5.0.1' + + include FHIRHelpers version '5.0.1' called FHIRHelpers + + context Patient + + define "Encounter Finished": + [Encounter] E + where E.status = 'finished' + """; - include FHIRHelpers version '4.0.1' called FHIRHelpers + @Override + protected FhirVersionEnum getExpectedFhirVersion() { + return FhirVersionEnum.R5; + } - parameter "Measurement Period" Interval - default Interval[@2024-01-01T00:00:00.0-06:00, @2025-01-01T00:00:00.0-06:00) + @Test + void simpleAlpha() { + final BaseNpmPackageLoaderInMemory loader = setup(Path.of(SIMPLE_ALPHA_TGZ)); - context Patient + final Optional optMeasure = + loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_ALPHA)); - define "Initial Population": - exists ("Encounter Planned") + verifyMeasure(optMeasure.orElse(null), MEASURE_URL_ALPHA, VERSION_0_1, + LIBRARY_URL_ALPHA_WITH_VERSION); - define "Encounter Planned": - [Encounter] E - where E.status = 'planned' - """; - private static final String EXPECTED_CQL_WITH_DERIVED = - """ - library opencds.withderivedlibrary WithDerivedLibrary version '0.4' + final Optional optLibrary = + loader.loadNpmResource(Library.class, + new CanonicalType(LIBRARY_URL_ALPHA_WITH_VERSION)); - using FHIR version '5.0.1' + verifyLibrary(optLibrary.orElse(null), LIBRARY_URL_ALPHA_NO_VERSION, VERSION_0_1, + EXPECTED_CQL_ALPHA); - include FHIRHelpers version '5.0.1' called FHIRHelpers - include DerivedLibrary version '0.4' + final List allNamespaceInfos = + loader.getNamespaceManager().getAllNamespaceInfos(); - parameter "Measurement Period" Interval - default Interval[@2021-01-01T00:00:00.0-06:00, @2022-01-01T00:00:00.0-06:00) + assertEquals(1, allNamespaceInfos.size()); - context Patient + final NamespaceInfo namespaceInfo = allNamespaceInfos.get(0); - define "Initial Population": - exists (DerivedLibrary."Encounter Finished") - """; - private static final String EXPECTED_CQL_DERIVED = - """ - library opencds.withderivedlibrary.DerivedLibrary version '0.4' + assertEquals(SIMPLE_ALPHA_NAMESPACE, namespaceInfo.getName()); + assertEquals(SIMPLE_ALPHA_NAMESPACE_URL, namespaceInfo.getUri()); + } - using FHIR version '5.0.1' + @Test + void simpleBravo() { + final BaseNpmPackageLoaderInMemory loader = setup(Path.of(SIMPLE_BRAVO_TGZ)); - include FHIRHelpers version '5.0.1' called FHIRHelpers + final Optional optMeasure = + loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_BRAVO)); - context Patient + verifyMeasure(optMeasure.orElse(null), MEASURE_URL_BRAVO, VERSION_0_1, + LIBRARY_URL_BRAVO_WITH_VERSION); - define "Encounter Finished": - [Encounter] E - where E.status = 'finished' - """; + final Optional optLibrary = + loader.loadNpmResource(Library.class, + new CanonicalType(LIBRARY_URL_BRAVO_WITH_VERSION)); - private static final String EXPECTED_CQL_DERIVED_TWO_LAYERS = - """ - library opencds.withtwolayersderivedlibraries.WithTwoLayersDerivedLibraries version '0.5' + verifyLibrary(optLibrary.orElse(null), LIBRARY_URL_BRAVO_NO_VERSION, VERSION_0_1, + EXPECTED_CQL_BRAVO); - using FHIR version '5.0.1' + final List allNamespaceInfos = + loader.getNamespaceManager().getAllNamespaceInfos(); - include DerivedLayer1a version '0.5' - include DerivedLayer1b version '0.5' + assertEquals(1, allNamespaceInfos.size()); - parameter "Measurement Period" Interval - default Interval[@2022-01-01T00:00:00.0-06:00, @2023-01-01T00:00:00.0-06:00) + final NamespaceInfo namespaceInfo = allNamespaceInfos.get(0); - context Patient + assertEquals(SIMPLE_BRAVO_NAMESPACE, namespaceInfo.getName()); + assertEquals(SIMPLE_BRAVO_NAMESPACE_URL, namespaceInfo.getUri()); + } - define "Initial Population": - DerivedLayer1a."Initial Population" + @Test + void multiplePackages() { + final BaseNpmPackageLoaderInMemory loader = R5NpmPackageLoaderInMemory.fromNpmPackageClasspath( + getClass(), + Stream.of(SIMPLE_ALPHA_TGZ, SIMPLE_BRAVO_TGZ).map(Paths::get).toList()); - define "Denominator": - DerivedLayer1b."Denominator" + final List allNamespaceInfos = + loader.getNamespaceManager().getAllNamespaceInfos(); - define "Numerator": - DerivedLayer1b."Numerator" - """; + assertEquals(2, allNamespaceInfos.size()); - private static final String EXPECTED_CQL_DERIVED_1_A = - """ - library opencds.withtwolayersderivedlibraries.DerivedLayer1a version '0.5' + assertTrue(allNamespaceInfos.contains( + new NamespaceInfo(SIMPLE_ALPHA_NAMESPACE, SIMPLE_ALPHA_NAMESPACE_URL))); + assertTrue(allNamespaceInfos.contains( + new NamespaceInfo(SIMPLE_BRAVO_NAMESPACE, SIMPLE_BRAVO_NAMESPACE_URL))); - using FHIR version '5.0.1' + final Optional optMeasureAlpha = + loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_ALPHA)); - include DerivedLayer2a version '0.5' - include DerivedLayer2b version '0.5' + verifyMeasure(optMeasureAlpha.orElse(null), MEASURE_URL_ALPHA, VERSION_0_1, + LIBRARY_URL_ALPHA_WITH_VERSION); - context Patient + final Optional optLibraryAlpha = + loader.loadNpmResource(Library.class, + new CanonicalType(LIBRARY_URL_ALPHA_WITH_VERSION)); - define "Initial Population": - DerivedLayer2a."Initial Population" - """; + verifyLibrary(optLibraryAlpha.orElse(null), LIBRARY_URL_ALPHA_NO_VERSION, VERSION_0_1, + EXPECTED_CQL_ALPHA); - private static final String EXPECTED_CQL_DERIVED_1_B = - """ - library opencds.withtwolayersderivedlibraries.DerivedLayer1b version '0.5' + final Optional optMeasureBravo = + loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_ALPHA)); - using FHIR version '5.0.1' + verifyMeasure(optMeasureBravo.orElse(null), MEASURE_URL_ALPHA, VERSION_0_1, + LIBRARY_URL_ALPHA_WITH_VERSION); - include DerivedLayer2a version '0.5' - include DerivedLayer2b version '0.5' + final Optional optLibraryBravo = + loader.loadNpmResource(Library.class, + new CanonicalType(LIBRARY_URL_BRAVO_WITH_VERSION)); - context Patient + verifyLibrary(optLibraryBravo.orElse(null), LIBRARY_URL_BRAVO_NO_VERSION, VERSION_0_1, + EXPECTED_CQL_BRAVO); + } - define "Denominator": - DerivedLayer2a."Denominator" + @Test + void derivedLibrary() { - define "Numerator": - DerivedLayer2b."Numerator" - """; + final BaseNpmPackageLoaderInMemory loader = setup(WITH_DERIVED_LIBRARY_TGZ); - private static final String EXPECTED_CQL_DERIVED_2_A = - """ - library opencds.withtwolayersderivedlibraries.DerivedLayer2a version '0.5' + final List allNamespaceInfos = + loader.getNamespaceManager().getAllNamespaceInfos(); - using FHIR version '5.0.1' + assertEquals(1, allNamespaceInfos.size()); - include FHIRHelpers version '5.0.1' called FHIRHelpers + assertTrue(allNamespaceInfos.contains( + new NamespaceInfo(WITH_DERIVED_NAMESPACE, WITH_DERIVED_URL))); - context Patient + final Optional optMeasureWithDerived = + loader.loadNpmResource(Measure.class, + new CanonicalType(MEASURE_URL_WITH_DERIVED_LIBRARY)); - define "Initial Population": - exists ("Encounter Finished") + verifyMeasure(optMeasureWithDerived.orElse(null), MEASURE_URL_WITH_DERIVED_LIBRARY, + VERSION_0_4, LIBRARY_URL_WITH_DERIVED_LIBRARY_WITH_VERSION); - define "Denominator": - exists ("Encounter Planned") + final Optional optLibraryWithDerived = + loader.loadNpmResource(Library.class, + new CanonicalType(LIBRARY_URL_WITH_DERIVED_LIBRARY_WITH_VERSION)); - define "Encounter Finished": - [Encounter] E - where E.status = 'finished' + verifyLibrary( + optLibraryWithDerived.orElse(null), + LIBRARY_URL_WITH_DERIVED_LIBRARY_NO_VERSION, + VERSION_0_4, + EXPECTED_CQL_WITH_DERIVED); - define "Encounter Planned": - [Encounter] E - where E.status = 'planned' - """; + final Optional optLibraryDerived = + loader.loadNpmResource(Library.class, new CanonicalType(LIBRARY_URL_DERIVED_LIBRARY)); - private static final String EXPECTED_CQL_DERIVED_2_B = - """ - library opencds.withtwolayersderivedlibraries.DerivedLayer2b version '0.5' + verifyLibrary( + optLibraryDerived.orElse(null), + LIBRARY_URL_DERIVED_LIBRARY, + VERSION_0_4, + EXPECTED_CQL_DERIVED); + } - using FHIR version '5.0.1' + @Test + void derivedLibraryTwoLayers() { + + final BaseNpmPackageLoaderInMemory loader = setup(WITH_TWO_LAYERS_DERIVED_LIBRARIES_TGZ); + + final List allNamespaceInfos = + loader.getNamespaceManager().getAllNamespaceInfos(); + + assertEquals(1, allNamespaceInfos.size()); + + assertTrue(allNamespaceInfos.contains(new NamespaceInfo(WITH_TWO_LAYERS_NAMESPACE, WITH_TWO_LAYERS_DERIVED_URL))); + + final Optional optMeasureWithTwoDerived = + loader.loadNpmResource(Measure.class, + new CanonicalType(MEASURE_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES)); + + verifyMeasure( + optMeasureWithTwoDerived.orElse(null), + MEASURE_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES, + VERSION_0_5, + LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES_WITH_VERSION); + + final Optional optLibraryWithTwoDerived = + loader.loadNpmResource(Library.class, + new CanonicalType(LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES_WITH_VERSION)); + + verifyLibrary( + optLibraryWithTwoDerived.orElse(null), + LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES_NO_VERSION, + VERSION_0_5, + EXPECTED_CQL_DERIVED_TWO_LAYERS); + + final Optional optLibraryWithTwoDerived1a = + loader.loadNpmResource(Library.class, + new CanonicalType(LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_1A)); + + verifyLibrary( + optLibraryWithTwoDerived1a.orElse(null), + LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_1A, + VERSION_0_5, + EXPECTED_CQL_DERIVED_1_A); + + final Optional optLibraryWithTwoDerived1b = + loader.loadNpmResource(Library.class, + new CanonicalType(LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_1B)); + + verifyLibrary( + optLibraryWithTwoDerived1b.orElse(null), + LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_1B, + VERSION_0_5, + EXPECTED_CQL_DERIVED_1_B); + + final Optional optLibraryWithTwoDerived2a = + loader.loadNpmResource(Library.class, + new CanonicalType(LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_2A)); + + verifyLibrary( + optLibraryWithTwoDerived2a.orElse(null), + LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_2A, + VERSION_0_5, + EXPECTED_CQL_DERIVED_2_A); + + final Optional optLibraryWithTwoDerived2b = + loader.loadNpmResource(Library.class, + new CanonicalType(LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_2B)); + + verifyLibrary( + optLibraryWithTwoDerived2b.orElse(null), + LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_2B, + VERSION_0_5, + EXPECTED_CQL_DERIVED_2_B); + + final ILibraryAdapter libraryAdapter1a = loader.findMatchingLibrary(new VersionedIdentifier() + .withId(DERIVED_LAYER_1_A) + .withVersion(VERSION_0_5) + .withSystem(WITH_TWO_LAYERS_DERIVED_URL)) + .orElse(null); + + verifyLibrary( + libraryAdapter1a, + LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_1A, + VERSION_0_5, + EXPECTED_CQL_DERIVED_1_A); + + final ILibraryAdapter libraryAdapter1b = loader.findMatchingLibrary(new VersionedIdentifier() + .withId(DERIVED_LAYER_1_B) + .withVersion(VERSION_0_5) + .withSystem(WITH_TWO_LAYERS_DERIVED_URL)) + .orElse(null); + + verifyLibrary( + libraryAdapter1b, + LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_1B, + VERSION_0_5, + EXPECTED_CQL_DERIVED_1_B); + + final ILibraryAdapter libraryAdapter2a = loader.findMatchingLibrary(new VersionedIdentifier() + .withId(DERIVED_LAYER_2_A) + .withVersion(VERSION_0_5) + .withSystem(WITH_TWO_LAYERS_DERIVED_URL)) + .orElse(null); + + verifyLibrary( + libraryAdapter2a, + LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_2A, + VERSION_0_5, + EXPECTED_CQL_DERIVED_2_A); + + final ILibraryAdapter libraryAdapter2b = loader.findMatchingLibrary(new VersionedIdentifier() + .withId(DERIVED_LAYER_2_B) + .withVersion(VERSION_0_5) + .withSystem(WITH_TWO_LAYERS_DERIVED_URL)) + .orElse(null); + + verifyLibrary( + libraryAdapter2b, + LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_2B, + VERSION_0_5, + EXPECTED_CQL_DERIVED_2_B); + } - include FHIRHelpers version '5.0.1' called FHIRHelpers + @Test + void crossPackage() { + + final BaseNpmPackageLoaderInMemory loader = setup(CROSS_PACKAGE_SOURCE_TGZ, CROSS_PACKAGE_TARGET_TGZ); + + final List allNamespaceInfos = + loader.getNamespaceManager().getAllNamespaceInfos(); + + assertEquals(2, allNamespaceInfos.size()); + + assertTrue(allNamespaceInfos.contains(new NamespaceInfo(CROSS_PACKAGE_SOURCE_NAMESPACE, CROSS_PACKAGE_SOURCE_URL))); + assertTrue(allNamespaceInfos.contains(new NamespaceInfo(CROSS_PACKAGE_TARGET_NAMESPACE, CROSS_PACKAGE_TARGET_URL))); + + final Optional optMeasureCrossSource = + loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_CROSS_PACKAGE_SOURCE)); + + verifyMeasure( + optMeasureCrossSource.orElse(null), + MEASURE_URL_CROSS_PACKAGE_SOURCE, + VERSION_0_2, + LIBRARY_URL_CROSS_PACKAGE_SOURCE_WITH_VERSION); + + final Optional optLibraryCrossSource = + loader.loadNpmResource(Library.class, new CanonicalType(LIBRARY_URL_CROSS_PACKAGE_SOURCE_WITH_VERSION)); + + verifyLibrary( + optLibraryCrossSource.orElse(null), + LIBRARY_URL_CROSS_PACKAGE_SOURCE_NO_VERSION, + VERSION_0_2, + EXPECTED_CQL_CROSS_SOURCE); + + final Optional optMeasureCrossTarget = + loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_CROSS_PACKAGE_TARGET)); + + verifyMeasure( + optMeasureCrossTarget.orElse(null), + MEASURE_URL_CROSS_PACKAGE_TARGET, + VERSION_0_3, + LIBRARY_URL_CROSS_PACKAGE_TARGET_WITH_VERSION); + + final Optional optLibraryCrossTarget = + loader.loadNpmResource(Library.class, new CanonicalType(LIBRARY_URL_CROSS_PACKAGE_TARGET_WITH_VERSION)); + + verifyLibrary( + optLibraryCrossTarget.orElse(null), + LIBRARY_URL_CROSS_PACKAGE_TARGET_NO_VERSION, + VERSION_0_3, + EXPECTED_CQL_CROSS_TARGET); + + final ILibraryAdapter libraryAdapterSource = loader.findMatchingLibrary(new VersionedIdentifier() + .withId(CROSS_PACKAGE_SOURCE_ID) + .withVersion(VERSION_0_2) + .withSystem(CROSS_PACKAGE_SOURCE_URL)) + .orElse(null); + + verifyLibrary( + libraryAdapterSource, + LIBRARY_URL_CROSS_PACKAGE_SOURCE_NO_VERSION, + VERSION_0_2, + EXPECTED_CQL_CROSS_SOURCE); + + final ILibraryAdapter libraryAdapterTarget = loader.findMatchingLibrary(new VersionedIdentifier() + .withId(CROSS_PACKAGE_TARGET_ID) + .withVersion(VERSION_0_3) + .withSystem(CROSS_PACKAGE_TARGET_URL)) + .orElse(null); + + verifyLibrary( + libraryAdapterTarget, + LIBRARY_URL_CROSS_PACKAGE_TARGET_NO_VERSION, + VERSION_0_3, + EXPECTED_CQL_CROSS_TARGET); + } - parameter "Measurement Period" Interval - default Interval[@2021-01-01T00:00:00.0-06:00, @2022-01-01T00:00:00.0-06:00) + protected void verifyMeasure(@Nullable Measure measure, String measureUrl, String version, String expectedLibraryUrl) { - context Patient + assertNotNull(measure, "Could not find measure with url: %s".formatted(measureUrl)); - define "Numerator": - exists ("Encounter Triaged") + assertEquals(measureUrl, measure.getUrl()); - define "Encounter Triaged": - [Encounter] E - where E.status = 'triaged' - """; + assertEquals(version, measure.getVersion()); - private static final String EXPECTED_CQL_CROSS_SOURCE = - """ - library opencds.crosspackagesource.CrossPackageSource version '0.2' + final List libraryUrls = measure.getLibrary(); + assertEquals(1, libraryUrls.size()); + final CanonicalType libraryUrl = libraryUrls.get(0); + assertEquals(expectedLibraryUrl, libraryUrl.asStringValue()); + } - using FHIR version '5.0.1' + private void verifyLibrary(@Nullable ILibraryAdapter libraryAdapter, String expectedLibraryUrl, String version, String expectedCql) { - include FHIRHelpers version '4.0.1' called FHIRHelpers - include opencds.crosspackagetarget.CrossPackageTarget version '0.3' called CrossPackageTarget + assertNotNull(libraryAdapter, "Could not find library with url: %s".formatted(expectedLibraryUrl)); - parameter "Measurement Period" Interval - default Interval[@2020-01-01T00:00:00.0-06:00, @2021-01-01T00:00:00.0-06:00) + assertInstanceOf(LibraryAdapter.class, libraryAdapter); - context Patient + verifyLibrary(((LibraryAdapter)libraryAdapter).get(), expectedLibraryUrl, version, expectedCql); + } - define "Initial Population": - exists (CrossPackageTarget."Encounter Finished") - """; + private void verifyLibrary(@Nullable Library library, String expectedLibraryUrl, String version, String expectedCql) { - private static final String EXPECTED_CQL_CROSS_TARGET = - """ - library opencds.crosspackagetarget.CrossPackageTarget version '0.3' + assertNotNull(library, "Could not find library with url: %s".formatted(expectedLibraryUrl)); - using FHIR version '5.0.1' + assertEquals(expectedLibraryUrl, library.getUrl()); - include FHIRHelpers version '5.0.1' called FHIRHelpers + assertEquals(version, library.getVersion()); - context Patient + final List attachments = library.getContent(); + assertEquals(1, attachments.size()); + final Attachment attachment = attachments.get(0); + assertEquals("text/cql", attachment.getContentType()); + final byte[] attachmentData = attachment.getData(); + final String cql = new String(attachmentData, StandardCharsets.UTF_8); - define "Encounter Finished": - [Encounter] E - where E.status = 'finished' - """; + assertEquals(expectedCql, cql); + } - @Override - protected FhirVersionEnum getExpectedFhirVersion() { - return FhirVersionEnum.R5; + @Nonnull + protected R5NpmPackageLoaderInMemory setup(Path... tgzPaths) { + return R5NpmPackageLoaderInMemory.fromNpmPackageClasspath(getClass(), tgzPaths); } - // - // @Test - // void simpleAlpha() { - // simpleAlpha( - // Path.of(SIMPLE_ALPHA_TGZ), - // MEASURE_URL_ALPHA, - // LIBRARY_URL_ALPHA_WITH_VERSION, - // LIBRARY_URL_ALPHA_NO_VERSION, - // EXPECTED_CQL_ALPHA); - // } - // - // @Test - // void simpleBravo() { - // simpleBravo( - // Path.of(SIMPLE_BRAVO_TGZ), - // MEASURE_URL_BRAVO, - // LIBRARY_URL_BRAVO_WITH_VERSION, - // LIBRARY_URL_BRAVO_NO_VERSION, - // EXPECTED_CQL_BRAVO); - // } - // - // @Test - // void multiplePackages() { - // multiplePackages(EXPECTED_CQL_ALPHA, EXPECTED_CQL_BRAVO); - // } - // - // @Test - // void derivedLibrary() { - // derivedLibrary(EXPECTED_CQL_WITH_DERIVED, EXPECTED_CQL_DERIVED); - // } - // - // @Test - // void derivedLibraryTwoLayers() { - // derivedLibraryTwoLayers( - // EXPECTED_CQL_DERIVED_TWO_LAYERS, - // EXPECTED_CQL_DERIVED_1_A, - // EXPECTED_CQL_DERIVED_1_B, - // EXPECTED_CQL_DERIVED_2_A, - // EXPECTED_CQL_DERIVED_2_B); - // } - // - // @Test - // void crossPackage() { - // crossPackage(EXPECTED_CQL_CROSS_SOURCE, EXPECTED_CQL_CROSS_TARGET); - // } } diff --git a/cqf-fhir-utility/src/test/resources/org/opencds/cqf/fhir/utility/npm/r4/crosspackagetarget.tgz b/cqf-fhir-utility/src/test/resources/org/opencds/cqf/fhir/utility/npm/r4/crosspackagetarget.tgz index 080274ad5fb9917c5644f89faf6c4a6a7b3392a1..a5627babcf287a05380463b550aeaebb5c155d6e 100644 GIT binary patch literal 1464 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 Date: Tue, 7 Oct 2025 16:53:24 -0400 Subject: [PATCH 3/5] Cleanup and spotless. --- .../npm/BaseNpmPackageLoaderInMemory.java | 204 +------- .../fhir/utility/npm/NpmPackageLoader.java | 85 +-- .../npm/r4/R4NpmPackageLoaderInMemory.java | 14 +- .../npm/r5/R5NpmPackageLoaderInMemory.java | 23 +- .../npm/BaseNpmResourceInfoForCqlTest.java | 129 +---- .../npm/r4/NpmResourceHolderR4Test.java | 374 ++++++-------- .../npm/r5/NpmResourceHolderR5Test.java | 482 +++++++++--------- 7 files changed, 457 insertions(+), 854 deletions(-) diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/BaseNpmPackageLoaderInMemory.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/BaseNpmPackageLoaderInMemory.java index b2beca3126..e2d74c74c3 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/BaseNpmPackageLoaderInMemory.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/BaseNpmPackageLoaderInMemory.java @@ -2,113 +2,31 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; -import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; -import org.hl7.cql.model.NamespaceInfo; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.hl7.fhir.instance.model.api.IPrimitiveType; -import org.hl7.fhir.utilities.npm.NpmPackage; -import org.opencds.cqf.fhir.utility.adapter.ILibraryAdapter; -import org.opencds.cqf.fhir.utility.adapter.IResourceAdapter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.InputStream; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Arrays; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.StringJoiner; -import java.util.regex.Pattern; import java.util.stream.Collectors; +import org.hl7.cql.model.NamespaceInfo; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IPrimitiveType; +import org.hl7.fhir.utilities.npm.NpmPackage; -// LUKETODO: redo java - -/** - * Simplistic implementation of {@link NpmPackageLoader} that loads NpmPackages from the classpath - * and stores {@link NpmResourceHolder}s in a Map. This class is recommended for testing - * and NOT for production. - *

npmPackages; private final NpmNamespaceManager npmNamespaceManager; - public static BaseNpmPackageLoaderInMemory fromNpmPackageAbsolutePath(List tgzPaths) { - return fromNpmPackageAbsolutePath(null, tgzPaths); - } - - public static BaseNpmPackageLoaderInMemory fromNpmPackageAbsolutePath( - NpmNamespaceManager npmNamespaceManager, List tgzPaths) { - final Set npmPackages = buildNpmPackagesFromAbsolutePath(tgzPaths); - - return null; - } - - public static BaseNpmPackageLoaderInMemory fromNpmPackageClasspath(Class clazz, Path... tgzPaths) { - return fromNpmPackageClasspath(null, clazz, tgzPaths); - } - - public static BaseNpmPackageLoaderInMemory fromNpmPackageClasspath( - @Nullable NpmNamespaceManager npmNamespaceManager, Class clazz, Path... tgzPaths) { - return fromNpmPackageClasspath(npmNamespaceManager, clazz, Arrays.asList(tgzPaths)); - } - - public static BaseNpmPackageLoaderInMemory fromNpmPackageClasspath(Class clazz, List tgzPaths) { - return fromNpmPackageClasspath(null, clazz, tgzPaths); - } - - public static BaseNpmPackageLoaderInMemory fromNpmPackageClasspath( - @Nullable NpmNamespaceManager npmNamespaceManager, Class clazz, List tgzPaths) { - final Set npmPackages = buildNpmPackageFromClasspath(clazz, tgzPaths); - - return null; - } - - record UrlAndVersion(String url, @Nullable String version) { - - static UrlAndVersion fromCanonical(String canonical) { - final String[] parts = PATTERN_PIPE.split(canonical); - if (parts.length > 2) { - throw new IllegalArgumentException("Invalid canonical URL: " + canonical); - } - if (parts.length == 1) { - return new UrlAndVersion(parts[0], null); - } - return new UrlAndVersion(parts[0], parts[1]); - } - - static UrlAndVersion fromCanonicalAndVersion(String canonical, @Nullable String version) { - if (version == null) { - return new UrlAndVersion(canonical, null); - } - - return new UrlAndVersion(canonical, version); - } - - @Override - @Nonnull - public String toString() { - return url + "|" + version; - } - } - @Override public Optional loadNpmResource(IPrimitiveType resourceUrl) { return npmPackages.stream() @@ -145,7 +63,7 @@ private boolean doesPackageMatch(IPrimitiveType resourceUrl, NpmPackage } } - abstract public FhirContext getFhirContext(); + public abstract FhirContext getFhirContext(); @Override public NpmNamespaceManager getNamespaceManager() { @@ -188,7 +106,8 @@ private static NpmPackage getNpmPackageFromClasspath(Class clazz, Path tgzCla } } - protected BaseNpmPackageLoaderInMemory(Set npmPackages, @Nullable NpmNamespaceManager npmNamespaceManager) { + protected BaseNpmPackageLoaderInMemory( + Set npmPackages, @Nullable NpmNamespaceManager npmNamespaceManager) { if (npmNamespaceManager == null) { var namespaceInfos = npmPackages.stream() @@ -203,113 +122,6 @@ protected BaseNpmPackageLoaderInMemory(Set npmPackages, @Nullable Np this.npmPackages = npmPackages; } - private void setup(NpmPackage npmPackage) { - try { - trySetup(npmPackage); - } catch (Exception e) { - throw new InternalErrorException("Failed to setup NpmPackage: " + npmPackage.name(), e); - } - } - - private void trySetup(NpmPackage npmPackage) throws IOException { - final FhirContext fhirContext = getFhirContext(npmPackage); - - final Optional optPackageFolder = npmPackage.getFolders().entrySet().stream() - .filter(entry -> "package".equals(entry.getKey())) - .map(Map.Entry::getValue) - .findFirst(); - - if (optPackageFolder.isPresent()) { - setupNpmPackageInfo(npmPackage, optPackageFolder.get(), fhirContext); - } - } - - private void setupNpmPackageInfo( - NpmPackage npmPackage, NpmPackage.NpmPackageFolder packageFolder, FhirContext fhirContext) - throws IOException { - - // LUKETODO: store all resources - - final List resources = findResources(packageFolder, fhirContext); - - // final Optional optMeasure = findMeasure(resources); - // final List libraries = findLibraries(resources); - // - // storeResources(npmPackage, optMeasure.orElse(null), libraries); - } - - private List findResources(NpmPackage.NpmPackageFolder packageFolder, FhirContext fhirContext) - throws IOException { - - final Map> types = packageFolder.getTypes(); - final List resources = new ArrayList<>(); - - for (Map.Entry> typeToFiles : types.entrySet()) { - for (String nextFile : typeToFiles.getValue()) { - final String fileContents = new String(packageFolder.fetchFile(nextFile), StandardCharsets.UTF_8); - - if (nextFile.toLowerCase().endsWith(".json")) { - final IBaseResource resource = fhirContext.newJsonParser().parseResource(fileContents); - - resources.add(resource); - } - } - } - - return resources; - } - - // private Optional findMeasure(List resources) { - // return resources.stream() - // .filter(Measure.class::isInstance) - // .map(IMeasureAdapter.class::cast) - // .findFirst(); - // } - - private List findLibraries(List resources) { - return resources.stream() - .filter(ILibraryAdapter.class::isInstance) - .map(ILibraryAdapter.class::cast) - .toList(); - } - - // private void storeResources( - // NpmPackage npmPackage, @Nullable IMeasureAdapter measure, List libraries) { - // if (measure != null) { - // resourceUrlToResource.put( - // UrlAndVersion.fromCanonicalAndVersion(measure.getUrl(), measure.getVersion()), measure.get()); - // } - // - // for (ILibraryAdapter library : libraries) { - // libraryUrlToPackage.put( - // UrlAndVersion.fromCanonicalAndVersion(library.getUrl(), library.getVersion()), npmPackage); - // } - // } - - private static boolean doUrlAndVersionMatch( - IPrimitiveType measureUrl, Map.Entry entry) { - - if (entry.getKey().equals(UrlAndVersion.fromCanonical(measureUrl.getValueAsString()))) { - return true; - } - - return entry.getKey().url.equals(measureUrl.getValueAsString()); - } - - private static boolean doMeasureUrlAndLibraryMatch(String measureLibraryUrl, ILibraryAdapter library) { - final String[] split = PATTERN_PIPE.split(measureLibraryUrl); - - if (split.length == 1) { - return library.getUrl().equals(measureLibraryUrl); - } - - if (split.length == 2) { - return library.getUrl().equals(split[0]) && library.getVersion().equals(split[1]); - } - - throw new InternalErrorException("bad measureUrl: " + measureLibraryUrl); - } - private FhirContext getFhirContext(NpmPackage npmPackage) { return FhirContext.forCached(FhirVersionEnum.forVersionString(npmPackage.fhirVersion())); } diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/NpmPackageLoader.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/NpmPackageLoader.java index 4b21c095b5..9124d3ddf1 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/NpmPackageLoader.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/NpmPackageLoader.java @@ -15,17 +15,15 @@ import org.hl7.elm.r1.VersionedIdentifier; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IPrimitiveType; -import org.opencds.cqf.fhir.utility.FhirVersions; import org.opencds.cqf.fhir.utility.adapter.IAdapterFactory; import org.opencds.cqf.fhir.utility.adapter.ILibraryAdapter; import org.opencds.cqf.fhir.utility.adapter.IResourceAdapter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -// LUKETODO: redo java /** * FHIR version agnostic Interface for loading NPM resources including Measures, Libraries and - * NpmPackages as captured within {@link NpmResourceHolder}. + * potentially other qualifying resources. *

* This javadoc documents the entire NPM package feature in the clinical-reasoning project. Please * read below: @@ -41,18 +39,8 @@ * {@link #initNamespaceMappings(LibraryManager)}, as due to how CQL libraries are loaded, it * won't work automatically. *

- * The {@link NpmResourceHolder} class is used to capture the results of query the NPM - * package with a given measure URL. It's effectively a container for the Measure, its directly - * associated Library, and its NPM package information. In theory, there could be more than one - * NPM package for a given Measure. When CQL runs and calls a custom {@link LibrarySourceProvider}, - * it will first check to see if the directly associated Library matches the provided - * {@link VersionedIdentifier}. If not, it will query all NPM packages within the - * R4NpmResourceInfoForCql to find the Library. And if there is still no match, it will pass the - * VersionedIdentifier, and build a URL from the system and ID before calling - * {@link #loadLibraryByUrl(String)} to load that Library from another package, with the - * VersionedIdentifier already resolved correctly with the help of the NamespaceInfos provided above. - * The implementor is responsible for implementing loadLibraryByUrl to properly return the Library - * from any packages maintained by the application. + * When CQL runs and calls a custom {@link LibrarySourceProvider}, it will query all NPM packages + * accessible by the backing implementation. *

* The above should also work with multiple layers of includes across packages. *

@@ -67,11 +55,11 @@ * to read the namespace info and resolve ID Y to URL .... This * can only be accomplished via an explicit mapping. *

- * Note that there is the real possibility of Measures corresponding to the same canonical URL - * among multiple NPM packages. As such, clients who unintentionally add Measures with the same - * URL in at least two different packages may see the Measure they're not expecting during an - * $evaluate-measure-by-url, and may file production issues accordingly. This may be mitigated - * by new APIs in IHapiPackageCacheManager. + * Note that, depending on the implementation, there is the real possibility of Measures + * corresponding to the same canonical URL among multiple NPM packages. As such, clients who + * unintentionally add Measures with the same URL in at least two different packages may see the + * Measure they're not expecting during an $evaluate-measure-by-url, and may file production + * issues accordingly. */ public interface NpmPackageLoader { Logger logger = LoggerFactory.getLogger(NpmPackageLoader.class); @@ -98,11 +86,15 @@ public FhirContext getFhirContext() { String LIBRARY = "Library"; String MEASURE = "Measure"; - // LUKETODO: redo java /** - * @param resourceUrl The Measure URL provided by the caller, corresponding to a Measure contained - * withing one of the stored NPM packages. - * @return The Measure corresponding to the URL. + * Query the NPM package repo for the resource corresponding to the provided resource class and + * canonical URL corresponding to the NPM package repo. + * + * @param resourceClass The expected class of the resource to be returned, which will + * be checked via instanceof before being cast and returned. + * @param canonicalResourceUrl The resource URL provided by the caller, corresponding to a + * resource contained within one of the stored NPM packages. + * @return The resource (any {@link IBaseResource}) corresponding to the URL. */ default Optional loadNpmResource( Class resourceClass, IPrimitiveType canonicalResourceUrl) { @@ -123,34 +115,6 @@ default Optional loadNpmResource( return Optional.of(resourceClass.cast(resource)); } - // LUKETODO: which is the better API of the two? - default Optional loadNpmResourceAsAdapter( - IPrimitiveType canonicalResourceUrl) { - - return loadNpmResource(canonicalResourceUrl) - .map(this::toResourceAdapter); - - } - -// default Optional loadNpmResource( -// Class resourceClass, IPrimitiveType canonicalResourceUrl) { -// final Optional optResource = loadNpmResource(canonicalResourceUrl); -// -// if (optResource.isEmpty()) { -// return Optional.empty(); -// } -// -// final IBaseResource resource = optResource.get(); -// -// if (!resourceClass.isInstance(resource)) { -// throw new IllegalArgumentException("Expected resource to be a %s, but was a %s" -// .formatted(resourceClass.getSimpleName(), resource.fhirType())); -// } -// -// return Optional.of(resourceClass.cast(resource)); -// } - - default IAdapterFactory getAdapterFactory() { return IAdapterFactory.forFhirVersion(getFhirContext().getVersion().getVersion()); } @@ -240,8 +204,7 @@ default Optional findLibraryFromUnrelatedNpmPackage(ModelIdenti default Optional loadLibraryByUrl(String libraryUrl) { return toLibraryAdapter( - loadNpmResource(getLibraryClass(), toPrimitiveType(libraryUrl)) - .orElse(null)); + loadNpmResource(getLibraryClass(), toPrimitiveType(libraryUrl)).orElse(null)); } default Optional toLibraryAdapter(IBaseResource resource) { @@ -303,14 +266,14 @@ default IResourceAdapter toResourceAdapter(IBaseResource resource) { switch (resource.fhirType()) { case LIBRARY -> { return new org.opencds.cqf.fhir.utility.adapter.r4.LibraryAdapter( - (org.hl7.fhir.r4.model.Library) resource); + (org.hl7.fhir.r4.model.Library) resource); } case MEASURE -> { return new org.opencds.cqf.fhir.utility.adapter.r4.MeasureAdapter( - (org.hl7.fhir.r4.model.Measure) resource); + (org.hl7.fhir.r4.model.Measure) resource); } default -> throw new IllegalArgumentException( - "Expected resource to be a Library or Measure, but was a " + resource.fhirType()); + "Expected resource to be a Library or Measure, but was a " + resource.fhirType()); } } @@ -318,19 +281,19 @@ default IResourceAdapter toResourceAdapter(IBaseResource resource) { switch (resource.fhirType()) { case LIBRARY -> { return new org.opencds.cqf.fhir.utility.adapter.r5.LibraryAdapter( - (org.hl7.fhir.r5.model.Library) resource); + (org.hl7.fhir.r5.model.Library) resource); } case MEASURE -> { return new org.opencds.cqf.fhir.utility.adapter.r5.MeasureAdapter( - (org.hl7.fhir.r5.model.Measure) resource); + (org.hl7.fhir.r5.model.Measure) resource); } default -> throw new IllegalArgumentException( - "Expected resource to be a Library or Measure, but was a " + resource.fhirType()); + "Expected resource to be a Library or Measure, but was a " + resource.fhirType()); } } throw new InvalidRequestException("Unsupported FHIR version: %s" - .formatted(getFhirContext().getVersion().getVersion().toString())); + .formatted(getFhirContext().getVersion().getVersion().toString())); } private String getUrl(VersionedIdentifier versionedIdentifier) { diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/r4/R4NpmPackageLoaderInMemory.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/r4/R4NpmPackageLoaderInMemory.java index 113478aecd..bea2e32903 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/r4/R4NpmPackageLoaderInMemory.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/r4/R4NpmPackageLoaderInMemory.java @@ -11,23 +11,24 @@ import org.opencds.cqf.fhir.utility.npm.NpmNamespaceManager; import org.opencds.cqf.fhir.utility.npm.NpmPackageLoader; -// LUKETODO: redo java /** * Simplistic implementation of {@link NpmPackageLoader} that loads NpmPackages from the classpath - * and stores {@link NpmResourceHolder}s in a Map. This class is recommended for testing + * and stores {@link NpmPackage}s in a Set. This class is recommended for testing * and NOT for production. - *

* Optionally uses a custom {@link NpmNamespaceManager} but can also resolve all NamespaceInfos * by extracting them from all loaded packages at construction time. + *

+ * This is for R4 only. */ public class R4NpmPackageLoaderInMemory extends BaseNpmPackageLoaderInMemory implements NpmPackageLoader { - + public static R4NpmPackageLoaderInMemory fromNpmPackageAbsolutePath(List tgzPaths) { return fromNpmPackageAbsolutePath(null, tgzPaths); } public static R4NpmPackageLoaderInMemory fromNpmPackageAbsolutePath( - NpmNamespaceManager npmNamespaceManager, List tgzPaths) { + NpmNamespaceManager npmNamespaceManager, List tgzPaths) { final Set npmPackages = buildNpmPackagesFromAbsolutePath(tgzPaths); return new R4NpmPackageLoaderInMemory(npmPackages, npmNamespaceManager); @@ -53,8 +54,7 @@ public static R4NpmPackageLoaderInMemory fromNpmPackageClasspath( return new R4NpmPackageLoaderInMemory(npmPackages, npmNamespaceManager); } - public R4NpmPackageLoaderInMemory( - Set npmPackages, @Nullable NpmNamespaceManager npmNamespaceManager) { + public R4NpmPackageLoaderInMemory(Set npmPackages, @Nullable NpmNamespaceManager npmNamespaceManager) { super(npmPackages, npmNamespaceManager); } diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/r5/R5NpmPackageLoaderInMemory.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/r5/R5NpmPackageLoaderInMemory.java index c2c3b6d56d..801e67eb94 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/r5/R5NpmPackageLoaderInMemory.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/r5/R5NpmPackageLoaderInMemory.java @@ -2,17 +2,25 @@ import ca.uhn.fhir.context.FhirContext; import jakarta.annotation.Nullable; -import org.hl7.fhir.utilities.npm.NpmPackage; -import org.opencds.cqf.fhir.utility.npm.BaseNpmPackageLoaderInMemory; -import org.opencds.cqf.fhir.utility.npm.NpmNamespaceManager; -import org.opencds.cqf.fhir.utility.npm.NpmPackageLoader; import java.nio.file.Path; import java.util.Arrays; import java.util.List; import java.util.Set; +import org.hl7.fhir.utilities.npm.NpmPackage; +import org.opencds.cqf.fhir.utility.npm.BaseNpmPackageLoaderInMemory; +import org.opencds.cqf.fhir.utility.npm.NpmNamespaceManager; +import org.opencds.cqf.fhir.utility.npm.NpmPackageLoader; -// LUKETODO: redo java - +/** + * Simplistic implementation of {@link NpmPackageLoader} that loads NpmPackages from the classpath + * and stores {@link NpmPackage}s in a Set. This class is recommended for testing + * and NOT for production. + *

+ * Optionally uses a custom {@link NpmNamespaceManager} but can also resolve all NamespaceInfos + * by extracting them from all loaded packages at construction time. + *

+ * This is for R5 only. + */ public class R5NpmPackageLoaderInMemory extends BaseNpmPackageLoaderInMemory implements NpmPackageLoader { public static R5NpmPackageLoaderInMemory fromNpmPackageAbsolutePath(List tgzPaths) { @@ -46,8 +54,7 @@ public static R5NpmPackageLoaderInMemory fromNpmPackageClasspath( return new R5NpmPackageLoaderInMemory(npmPackages, npmNamespaceManager); } - public R5NpmPackageLoaderInMemory( - Set npmPackages, @Nullable NpmNamespaceManager npmNamespaceManager) { + public R5NpmPackageLoaderInMemory(Set npmPackages, @Nullable NpmNamespaceManager npmNamespaceManager) { super(npmPackages, npmNamespaceManager); } diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/BaseNpmResourceInfoForCqlTest.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/BaseNpmResourceInfoForCqlTest.java index 7dbc52a3ef..347df23048 100644 --- a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/BaseNpmResourceInfoForCqlTest.java +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/BaseNpmResourceInfoForCqlTest.java @@ -1,11 +1,9 @@ package org.opencds.cqf.fhir.utility.npm; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -import ca.uhn.fhir.context.FhirVersionEnum; +import jakarta.annotation.Nonnull; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.List; public abstract class BaseNpmResourceInfoForCqlTest { @@ -41,7 +39,6 @@ public abstract class BaseNpmResourceInfoForCqlTest { protected static final String CROSS_PACKAGE_SOURCE_NAMESPACE = NAMESPACE_PREFIX + CROSS_PACKAGE_SOURCE; protected static final String CROSS_PACKAGE_TARGET_NAMESPACE = NAMESPACE_PREFIX + CROSS_PACKAGE_TARGET; - protected static final String SIMPLE_ALPHA_TGZ = SIMPLE_ALPHA_LOWER + DOT_TGZ; protected static final String SIMPLE_BRAVO_TGZ = SIMPLE_BRAVO_LOWER + DOT_TGZ; protected static final Path WITH_DERIVED_LIBRARY_TGZ = Paths.get(WITH_DERIVED_LIBRARY_LOWER + DOT_TGZ); @@ -73,18 +70,14 @@ public abstract class BaseNpmResourceInfoForCqlTest { SIMPLE_BRAVO_NAMESPACE_URL + SLASH_MEASURE_SLASH + SIMPLE_BRAVO_MIXED; protected static final String MEASURE_URL_WITH_DERIVED_LIBRARY = WITH_DERIVED_URL + SLASH_MEASURE_SLASH + WITH_DERIVED_LIBRARY_MIXED; - protected static final String MEASURE_URL_WITH_DERIVED_LIBRARY_WITH_VERSION = - MEASURE_URL_WITH_DERIVED_LIBRARY + PIPE + VERSION_0_4; protected static final String MEASURE_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES = WITH_TWO_LAYERS_DERIVED_URL + SLASH_MEASURE_SLASH + WITH_TWO_LAYERS_DERIVED_LIBRARIES_UPPER; - protected static final String MEASURE_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES_WITH_VERSION = - MEASURE_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES + PIPE + VERSION_0_5; protected static final String MEASURE_URL_CROSS_PACKAGE_SOURCE = CROSS_PACKAGE_SOURCE_URL + SLASH_MEASURE_SLASH + CROSS_PACKAGE_SOURCE_ID; protected static final String MEASURE_URL_CROSS_PACKAGE_TARGET = - CROSS_PACKAGE_TARGET_URL + SLASH_MEASURE_SLASH + CROSS_PACKAGE_TARGET_ID; + CROSS_PACKAGE_TARGET_URL + SLASH_MEASURE_SLASH + CROSS_PACKAGE_TARGET_ID; protected static final String LIBRARY_URL_ALPHA_NO_VERSION = SIMPLE_ALPHA_NAMESPACE_URL + SLASH_LIBRARY_SLASH + SIMPLE_ALPHA_MIXED; @@ -107,7 +100,7 @@ public abstract class BaseNpmResourceInfoForCqlTest { protected static final String LIBRARY_URL_CROSS_PACKAGE_TARGET_NO_VERSION = CROSS_PACKAGE_TARGET_URL + SLASH_LIBRARY_SLASH + CROSS_PACKAGE_TARGET_ID; protected static final String LIBRARY_URL_CROSS_PACKAGE_TARGET_WITH_VERSION = - LIBRARY_URL_CROSS_PACKAGE_TARGET_NO_VERSION + PIPE + VERSION_0_3; + LIBRARY_URL_CROSS_PACKAGE_TARGET_NO_VERSION + PIPE + VERSION_0_3; protected static final String LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES_NO_VERSION = WITH_TWO_LAYERS_DERIVED_URL + SLASH_LIBRARY_SLASH + WITH_TWO_LAYERS_DERIVED_LIBRARIES_UPPER; @@ -123,115 +116,9 @@ public abstract class BaseNpmResourceInfoForCqlTest { protected static final String LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_2B = WITH_TWO_LAYERS_DERIVED_URL + SLASH_LIBRARY_SLASH + DERIVED_LAYER_2_B; - protected abstract FhirVersionEnum getExpectedFhirVersion(); - - // protected void derivedLibraryTwoLayers( - // String expectedCql, - // String expectedCqlDerived1a, - // String expectedCqlDerived1b, - // String expectedCqlDerived2a, - // String expectedCqlDerived2b) { - // - // final R4NpmPackageLoaderInMemory loader = setup(WITH_TWO_LAYERS_DERIVED_LIBRARIES_TGZ); - // - // var expectedNamespaceInfos = List.of(new NamespaceInfo(WITH_TWO_LAYERS_NAMESPACE, - // WITH_DERIVED_TWO_LAYERS_URL)); - // - // final NpmResourceHolder resourceInfoNoVersion = - // loader.loadNpmResources(new CanonicalType(MEASURE_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES)); - // sanityCheckNpmResourceHolder(resourceInfoNoVersion); - // assertEquals(expectedNamespaceInfos, resourceInfoNoVersion.getNamespaceInfos()); - // verifyMeasure( - // MEASURE_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES, - // LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES_WITH_VERSION, - // resourceInfoNoVersion); - // verifyLibrary( - // LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES_NO_VERSION, - // expectedCql, - // resourceInfoNoVersion.getOptMainLibrary().orElse(null)); - // - // final NpmResourceHolder resourceInfoWithVersion = - // loader.loadNpmResources(new - // CanonicalType(MEASURE_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES_WITH_VERSION)); - // sanityCheckNpmResourceHolder(resourceInfoWithVersion); - // assertEquals(expectedNamespaceInfos, resourceInfoWithVersion.getNamespaceInfos()); - // verifyMeasure( - // MEASURE_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES, - // LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES_WITH_VERSION, - // resourceInfoWithVersion); - // verifyLibrary( - // LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES_NO_VERSION, - // expectedCql, - // resourceInfoWithVersion.getOptMainLibrary().orElse(null)); - // - // final ILibraryAdapter derivedLibrary1a = resourceInfoWithVersion - // .findMatchingLibrary(new VersionedIdentifier() - // .withId(DERIVED_LAYER_1_A) - // .withVersion(VERSION_0_5) - // .withSystem(WITH_DERIVED_TWO_LAYERS_URL)) - // .orElse(null); - // - // verifyLibrary(LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_1A, expectedCqlDerived1a, derivedLibrary1a); - // - // final ILibraryAdapter derivedLibrary1b = resourceInfoWithVersion - // .findMatchingLibrary( - // new VersionedIdentifier().withId(DERIVED_LAYER_1_B).withVersion(VERSION_0_5)) - // .orElse(null); - // - // verifyLibrary(LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_1B, expectedCqlDerived1b, derivedLibrary1b); - // - // final ILibraryAdapter derivedLibrary2a = resourceInfoWithVersion - // .findMatchingLibrary( - // new VersionedIdentifier().withId(DERIVED_LAYER_2_A).withVersion(VERSION_0_5)) - // .orElse(null); - // - // verifyLibrary(LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_2A, expectedCqlDerived2a, derivedLibrary2a); - // - // final ILibraryAdapter derivedLibrary2b = resourceInfoWithVersion - // .findMatchingLibrary( - // new VersionedIdentifier().withId(DERIVED_LAYER_2_B).withVersion(VERSION_0_5)) - // .orElse(null); - // - // verifyLibrary(LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_2B, expectedCqlDerived2b, derivedLibrary2b); - // } - - // protected void crossPackage(String expectedCqlSource, String expectedCqlTarget) { - // - // final R4NpmPackageLoaderInMemory loader = setup(CROSS_PACKAGE_SOURCE_TGZ, CROSS_PACKAGE_TARGET_TGZ); - // - // final NpmResourceHolder resourceInfoWithNoVersion = - // loader.loadNpmResources(new CanonicalType(MEASURE_URL_CROSS_PACKAGE_SOURCE)); - // sanityCheckNpmResourceHolder(resourceInfoWithNoVersion); - // verifyMeasure( - // MEASURE_URL_CROSS_PACKAGE_SOURCE, - // LIBRARY_URL_CROSS_PACKAGE_SOURCE_WITH_VERSION, - // resourceInfoWithNoVersion); - // final NpmResourceHolder resourceInfoWithVersion = - // loader.loadNpmResources(new CanonicalType(MEASURE_URL_CROSS_PACKAGE_SOURCE_WITH_VERSION)); - // sanityCheckNpmResourceHolder(resourceInfoWithVersion); - // verifyMeasure( - // MEASURE_URL_CROSS_PACKAGE_SOURCE, - // LIBRARY_URL_CROSS_PACKAGE_SOURCE_WITH_VERSION, - // resourceInfoWithVersion); - // - // verifyLibrary( - // LIBRARY_URL_CROSS_PACKAGE_SOURCE, - // expectedCqlSource, - // resourceInfoWithVersion.getOptMainLibrary().orElse(null)); - // - // final Optional matchingLibraryWithSourceResult = - // resourceInfoWithVersion.findMatchingLibrary(new - // VersionedIdentifier().withId(CROSS_PACKAGE_TARGET_ID)); - // - // // We expect NOT to find the target Library here since it's not in the source package at all - // assertTrue(matchingLibraryWithSourceResult.isEmpty()); - // - // // On the other hand, we can load the Library directly from the loader by URL: - // final Optional optLibraryTarget = - // loader.loadLibraryByUrl(LIBRARY_URL_CROSS_PACKAGE_TARGET); - // - // assertTrue(optLibraryTarget.isPresent()); - // verifyLibrary(LIBRARY_URL_CROSS_PACKAGE_TARGET, expectedCqlTarget, optLibraryTarget.get()); - // } + @Nonnull + protected abstract BaseNpmPackageLoaderInMemory setup(Path... npmPackagePaths); + @Nonnull + protected abstract BaseNpmPackageLoaderInMemory setup(List npmPackagePaths); } diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/r4/NpmResourceHolderR4Test.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/r4/NpmResourceHolderR4Test.java index f6c26fe4bd..01cd16740f 100644 --- a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/r4/NpmResourceHolderR4Test.java +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/r4/NpmResourceHolderR4Test.java @@ -242,29 +242,22 @@ class NpmResourceHolderR4Test extends BaseNpmResourceInfoForCqlTest { where E.status = 'finished' """; - @Override - protected FhirVersionEnum getExpectedFhirVersion() { - return FhirVersionEnum.R4; - } @Test void simpleAlpha() { final BaseNpmPackageLoaderInMemory loader = setup(Path.of(SIMPLE_ALPHA_TGZ)); final Optional optMeasure = - loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_ALPHA)); + loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_ALPHA)); - verifyMeasure(optMeasure.orElse(null), MEASURE_URL_ALPHA, VERSION_0_1, - LIBRARY_URL_ALPHA_WITH_VERSION); + verifyMeasure(optMeasure.orElse(null), MEASURE_URL_ALPHA, VERSION_0_1, LIBRARY_URL_ALPHA_WITH_VERSION); final Optional optLibrary = - loader.loadNpmResource(Library.class, - new CanonicalType(LIBRARY_URL_ALPHA_WITH_VERSION)); + loader.loadNpmResource(Library.class, new CanonicalType(LIBRARY_URL_ALPHA_WITH_VERSION)); - verifyLibrary(optLibrary.orElse(null), LIBRARY_URL_ALPHA_NO_VERSION, VERSION_0_1, - EXPECTED_CQL_ALPHA); + verifyLibrary(optLibrary.orElse(null), LIBRARY_URL_ALPHA_NO_VERSION, VERSION_0_1, EXPECTED_CQL_ALPHA); final List allNamespaceInfos = - loader.getNamespaceManager().getAllNamespaceInfos(); + loader.getNamespaceManager().getAllNamespaceInfos(); assertEquals(1, allNamespaceInfos.size()); @@ -279,20 +272,17 @@ void simpleBravo() { final BaseNpmPackageLoaderInMemory loader = setup(Path.of(SIMPLE_BRAVO_TGZ)); final Optional optMeasure = - loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_BRAVO)); + loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_BRAVO)); - verifyMeasure(optMeasure.orElse(null), MEASURE_URL_BRAVO, VERSION_0_1, - LIBRARY_URL_BRAVO_WITH_VERSION); + verifyMeasure(optMeasure.orElse(null), MEASURE_URL_BRAVO, VERSION_0_1, LIBRARY_URL_BRAVO_WITH_VERSION); final Optional optLibrary = - loader.loadNpmResource(Library.class, - new CanonicalType(LIBRARY_URL_BRAVO_WITH_VERSION)); + loader.loadNpmResource(Library.class, new CanonicalType(LIBRARY_URL_BRAVO_WITH_VERSION)); - verifyLibrary(optLibrary.orElse(null), LIBRARY_URL_BRAVO_NO_VERSION, VERSION_0_1, - EXPECTED_CQL_BRAVO); + verifyLibrary(optLibrary.orElse(null), LIBRARY_URL_BRAVO_NO_VERSION, VERSION_0_1, EXPECTED_CQL_BRAVO); final List allNamespaceInfos = - loader.getNamespaceManager().getAllNamespaceInfos(); + loader.getNamespaceManager().getAllNamespaceInfos(); assertEquals(1, allNamespaceInfos.size()); @@ -304,43 +294,36 @@ void simpleBravo() { @Test void multiplePackages() { - final BaseNpmPackageLoaderInMemory loader = setup(Stream.of(SIMPLE_ALPHA_TGZ, SIMPLE_BRAVO_TGZ).map(Paths::get).toList()); + final BaseNpmPackageLoaderInMemory loader = setup( + Stream.of(SIMPLE_ALPHA_TGZ, SIMPLE_BRAVO_TGZ).map(Paths::get).toList()); final List allNamespaceInfos = - loader.getNamespaceManager().getAllNamespaceInfos(); + loader.getNamespaceManager().getAllNamespaceInfos(); assertEquals(2, allNamespaceInfos.size()); - assertTrue(allNamespaceInfos.contains( - new NamespaceInfo(SIMPLE_ALPHA_NAMESPACE, SIMPLE_ALPHA_NAMESPACE_URL))); - assertTrue(allNamespaceInfos.contains( - new NamespaceInfo(SIMPLE_BRAVO_NAMESPACE, SIMPLE_BRAVO_NAMESPACE_URL))); + assertTrue(allNamespaceInfos.contains(new NamespaceInfo(SIMPLE_ALPHA_NAMESPACE, SIMPLE_ALPHA_NAMESPACE_URL))); + assertTrue(allNamespaceInfos.contains(new NamespaceInfo(SIMPLE_BRAVO_NAMESPACE, SIMPLE_BRAVO_NAMESPACE_URL))); final Optional optMeasureAlpha = - loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_ALPHA)); + loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_ALPHA)); - verifyMeasure(optMeasureAlpha.orElse(null), MEASURE_URL_ALPHA, VERSION_0_1, - LIBRARY_URL_ALPHA_WITH_VERSION); + verifyMeasure(optMeasureAlpha.orElse(null), MEASURE_URL_ALPHA, VERSION_0_1, LIBRARY_URL_ALPHA_WITH_VERSION); final Optional optLibraryAlpha = - loader.loadNpmResource(Library.class, - new CanonicalType(LIBRARY_URL_ALPHA_WITH_VERSION)); + loader.loadNpmResource(Library.class, new CanonicalType(LIBRARY_URL_ALPHA_WITH_VERSION)); - verifyLibrary(optLibraryAlpha.orElse(null), LIBRARY_URL_ALPHA_NO_VERSION, VERSION_0_1, - EXPECTED_CQL_ALPHA); + verifyLibrary(optLibraryAlpha.orElse(null), LIBRARY_URL_ALPHA_NO_VERSION, VERSION_0_1, EXPECTED_CQL_ALPHA); final Optional optMeasureBravo = - loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_ALPHA)); + loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_ALPHA)); - verifyMeasure(optMeasureBravo.orElse(null), MEASURE_URL_ALPHA, VERSION_0_1, - LIBRARY_URL_ALPHA_WITH_VERSION); + verifyMeasure(optMeasureBravo.orElse(null), MEASURE_URL_ALPHA, VERSION_0_1, LIBRARY_URL_ALPHA_WITH_VERSION); final Optional optLibraryBravo = - loader.loadNpmResource(Library.class, - new CanonicalType(LIBRARY_URL_BRAVO_WITH_VERSION)); + loader.loadNpmResource(Library.class, new CanonicalType(LIBRARY_URL_BRAVO_WITH_VERSION)); - verifyLibrary(optLibraryBravo.orElse(null), LIBRARY_URL_BRAVO_NO_VERSION, VERSION_0_1, - EXPECTED_CQL_BRAVO); + verifyLibrary(optLibraryBravo.orElse(null), LIBRARY_URL_BRAVO_NO_VERSION, VERSION_0_1, EXPECTED_CQL_BRAVO); } @Test @@ -349,38 +332,34 @@ void derivedLibrary() { final BaseNpmPackageLoaderInMemory loader = setup(WITH_DERIVED_LIBRARY_TGZ); final List allNamespaceInfos = - loader.getNamespaceManager().getAllNamespaceInfos(); + loader.getNamespaceManager().getAllNamespaceInfos(); assertEquals(1, allNamespaceInfos.size()); - assertTrue(allNamespaceInfos.contains( - new NamespaceInfo(WITH_DERIVED_NAMESPACE, WITH_DERIVED_URL))); + assertTrue(allNamespaceInfos.contains(new NamespaceInfo(WITH_DERIVED_NAMESPACE, WITH_DERIVED_URL))); final Optional optMeasureWithDerived = - loader.loadNpmResource(Measure.class, - new CanonicalType(MEASURE_URL_WITH_DERIVED_LIBRARY)); + loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_WITH_DERIVED_LIBRARY)); - verifyMeasure(optMeasureWithDerived.orElse(null), MEASURE_URL_WITH_DERIVED_LIBRARY, - VERSION_0_4, LIBRARY_URL_WITH_DERIVED_LIBRARY_WITH_VERSION); + verifyMeasure( + optMeasureWithDerived.orElse(null), + MEASURE_URL_WITH_DERIVED_LIBRARY, + VERSION_0_4, + LIBRARY_URL_WITH_DERIVED_LIBRARY_WITH_VERSION); final Optional optLibraryWithDerived = - loader.loadNpmResource(Library.class, - new CanonicalType(LIBRARY_URL_WITH_DERIVED_LIBRARY_WITH_VERSION)); + loader.loadNpmResource(Library.class, new CanonicalType(LIBRARY_URL_WITH_DERIVED_LIBRARY_WITH_VERSION)); verifyLibrary( - optLibraryWithDerived.orElse(null), - LIBRARY_URL_WITH_DERIVED_LIBRARY_NO_VERSION, - VERSION_0_4, - EXPECTED_CQL_WITH_DERIVED); + optLibraryWithDerived.orElse(null), + LIBRARY_URL_WITH_DERIVED_LIBRARY_NO_VERSION, + VERSION_0_4, + EXPECTED_CQL_WITH_DERIVED); final Optional optLibraryDerived = - loader.loadNpmResource(Library.class, new CanonicalType(LIBRARY_URL_DERIVED_LIBRARY)); + loader.loadNpmResource(Library.class, new CanonicalType(LIBRARY_URL_DERIVED_LIBRARY)); - verifyLibrary( - optLibraryDerived.orElse(null), - LIBRARY_URL_DERIVED_LIBRARY, - VERSION_0_4, - EXPECTED_CQL_DERIVED); + verifyLibrary(optLibraryDerived.orElse(null), LIBRARY_URL_DERIVED_LIBRARY, VERSION_0_4, EXPECTED_CQL_DERIVED); } @Test @@ -389,119 +368,114 @@ void derivedLibraryTwoLayers() { final BaseNpmPackageLoaderInMemory loader = setup(WITH_TWO_LAYERS_DERIVED_LIBRARIES_TGZ); final List allNamespaceInfos = - loader.getNamespaceManager().getAllNamespaceInfos(); + loader.getNamespaceManager().getAllNamespaceInfos(); assertEquals(1, allNamespaceInfos.size()); - assertTrue(allNamespaceInfos.contains(new NamespaceInfo(WITH_TWO_LAYERS_NAMESPACE, WITH_TWO_LAYERS_DERIVED_URL))); + assertTrue( + allNamespaceInfos.contains(new NamespaceInfo(WITH_TWO_LAYERS_NAMESPACE, WITH_TWO_LAYERS_DERIVED_URL))); final Optional optMeasureWithTwoDerived = - loader.loadNpmResource(Measure.class, - new CanonicalType(MEASURE_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES)); + loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES)); verifyMeasure( - optMeasureWithTwoDerived.orElse(null), - MEASURE_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES, - VERSION_0_5, - LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES_WITH_VERSION); + optMeasureWithTwoDerived.orElse(null), + MEASURE_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES, + VERSION_0_5, + LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES_WITH_VERSION); - final Optional optLibraryWithTwoDerived = - loader.loadNpmResource(Library.class, - new CanonicalType(LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES_WITH_VERSION)); + final Optional optLibraryWithTwoDerived = loader.loadNpmResource( + Library.class, new CanonicalType(LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES_WITH_VERSION)); verifyLibrary( - optLibraryWithTwoDerived.orElse(null), - LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES_NO_VERSION, - VERSION_0_5, - EXPECTED_CQL_DERIVED_TWO_LAYERS); + optLibraryWithTwoDerived.orElse(null), + LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES_NO_VERSION, + VERSION_0_5, + EXPECTED_CQL_DERIVED_TWO_LAYERS); - final Optional optLibraryWithTwoDerived1a = - loader.loadNpmResource(Library.class, - new CanonicalType(LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_1A)); + final Optional optLibraryWithTwoDerived1a = loader.loadNpmResource( + Library.class, new CanonicalType(LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_1A)); verifyLibrary( - optLibraryWithTwoDerived1a.orElse(null), - LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_1A, - VERSION_0_5, - EXPECTED_CQL_DERIVED_1_A); + optLibraryWithTwoDerived1a.orElse(null), + LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_1A, + VERSION_0_5, + EXPECTED_CQL_DERIVED_1_A); - final Optional optLibraryWithTwoDerived1b = - loader.loadNpmResource(Library.class, - new CanonicalType(LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_1B)); + final Optional optLibraryWithTwoDerived1b = loader.loadNpmResource( + Library.class, new CanonicalType(LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_1B)); verifyLibrary( - optLibraryWithTwoDerived1b.orElse(null), - LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_1B, - VERSION_0_5, - EXPECTED_CQL_DERIVED_1_B); + optLibraryWithTwoDerived1b.orElse(null), + LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_1B, + VERSION_0_5, + EXPECTED_CQL_DERIVED_1_B); - final Optional optLibraryWithTwoDerived2a = - loader.loadNpmResource(Library.class, - new CanonicalType(LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_2A)); + final Optional optLibraryWithTwoDerived2a = loader.loadNpmResource( + Library.class, new CanonicalType(LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_2A)); verifyLibrary( - optLibraryWithTwoDerived2a.orElse(null), - LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_2A, - VERSION_0_5, - EXPECTED_CQL_DERIVED_2_A); + optLibraryWithTwoDerived2a.orElse(null), + LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_2A, + VERSION_0_5, + EXPECTED_CQL_DERIVED_2_A); - final Optional optLibraryWithTwoDerived2b = - loader.loadNpmResource(Library.class, - new CanonicalType(LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_2B)); + final Optional optLibraryWithTwoDerived2b = loader.loadNpmResource( + Library.class, new CanonicalType(LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_2B)); verifyLibrary( - optLibraryWithTwoDerived2b.orElse(null), - LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_2B, - VERSION_0_5, - EXPECTED_CQL_DERIVED_2_B); + optLibraryWithTwoDerived2b.orElse(null), + LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_2B, + VERSION_0_5, + EXPECTED_CQL_DERIVED_2_B); final ILibraryAdapter libraryAdapter1a = loader.findMatchingLibrary(new VersionedIdentifier() - .withId(DERIVED_LAYER_1_A) - .withVersion(VERSION_0_5) - .withSystem(WITH_TWO_LAYERS_DERIVED_URL)) - .orElse(null); + .withId(DERIVED_LAYER_1_A) + .withVersion(VERSION_0_5) + .withSystem(WITH_TWO_LAYERS_DERIVED_URL)) + .orElse(null); verifyLibrary( - libraryAdapter1a, - LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_1A, - VERSION_0_5, - EXPECTED_CQL_DERIVED_1_A); + libraryAdapter1a, + LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_1A, + VERSION_0_5, + EXPECTED_CQL_DERIVED_1_A); final ILibraryAdapter libraryAdapter1b = loader.findMatchingLibrary(new VersionedIdentifier() - .withId(DERIVED_LAYER_1_B) - .withVersion(VERSION_0_5) - .withSystem(WITH_TWO_LAYERS_DERIVED_URL)) - .orElse(null); + .withId(DERIVED_LAYER_1_B) + .withVersion(VERSION_0_5) + .withSystem(WITH_TWO_LAYERS_DERIVED_URL)) + .orElse(null); verifyLibrary( - libraryAdapter1b, - LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_1B, - VERSION_0_5, - EXPECTED_CQL_DERIVED_1_B); + libraryAdapter1b, + LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_1B, + VERSION_0_5, + EXPECTED_CQL_DERIVED_1_B); final ILibraryAdapter libraryAdapter2a = loader.findMatchingLibrary(new VersionedIdentifier() - .withId(DERIVED_LAYER_2_A) - .withVersion(VERSION_0_5) - .withSystem(WITH_TWO_LAYERS_DERIVED_URL)) - .orElse(null); + .withId(DERIVED_LAYER_2_A) + .withVersion(VERSION_0_5) + .withSystem(WITH_TWO_LAYERS_DERIVED_URL)) + .orElse(null); verifyLibrary( - libraryAdapter2a, - LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_2A, - VERSION_0_5, - EXPECTED_CQL_DERIVED_2_A); + libraryAdapter2a, + LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_2A, + VERSION_0_5, + EXPECTED_CQL_DERIVED_2_A); final ILibraryAdapter libraryAdapter2b = loader.findMatchingLibrary(new VersionedIdentifier() - .withId(DERIVED_LAYER_2_B) - .withVersion(VERSION_0_5) - .withSystem(WITH_TWO_LAYERS_DERIVED_URL)) - .orElse(null); + .withId(DERIVED_LAYER_2_B) + .withVersion(VERSION_0_5) + .withSystem(WITH_TWO_LAYERS_DERIVED_URL)) + .orElse(null); verifyLibrary( - libraryAdapter2b, - LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_2B, - VERSION_0_5, - EXPECTED_CQL_DERIVED_2_B); + libraryAdapter2b, + LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_2B, + VERSION_0_5, + EXPECTED_CQL_DERIVED_2_B); } @Test @@ -510,75 +484,78 @@ void crossPackage() { final BaseNpmPackageLoaderInMemory loader = setup(CROSS_PACKAGE_SOURCE_TGZ, CROSS_PACKAGE_TARGET_TGZ); final List allNamespaceInfos = - loader.getNamespaceManager().getAllNamespaceInfos(); + loader.getNamespaceManager().getAllNamespaceInfos(); assertEquals(2, allNamespaceInfos.size()); - assertTrue(allNamespaceInfos.contains(new NamespaceInfo(CROSS_PACKAGE_SOURCE_NAMESPACE, CROSS_PACKAGE_SOURCE_URL))); - assertTrue(allNamespaceInfos.contains(new NamespaceInfo(CROSS_PACKAGE_TARGET_NAMESPACE, CROSS_PACKAGE_TARGET_URL))); + assertTrue(allNamespaceInfos.contains( + new NamespaceInfo(CROSS_PACKAGE_SOURCE_NAMESPACE, CROSS_PACKAGE_SOURCE_URL))); + assertTrue(allNamespaceInfos.contains( + new NamespaceInfo(CROSS_PACKAGE_TARGET_NAMESPACE, CROSS_PACKAGE_TARGET_URL))); final Optional optMeasureCrossSource = - loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_CROSS_PACKAGE_SOURCE)); + loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_CROSS_PACKAGE_SOURCE)); verifyMeasure( - optMeasureCrossSource.orElse(null), - MEASURE_URL_CROSS_PACKAGE_SOURCE, - VERSION_0_2, - LIBRARY_URL_CROSS_PACKAGE_SOURCE_WITH_VERSION); + optMeasureCrossSource.orElse(null), + MEASURE_URL_CROSS_PACKAGE_SOURCE, + VERSION_0_2, + LIBRARY_URL_CROSS_PACKAGE_SOURCE_WITH_VERSION); final Optional optLibraryCrossSource = - loader.loadNpmResource(Library.class, new CanonicalType(LIBRARY_URL_CROSS_PACKAGE_SOURCE_WITH_VERSION)); + loader.loadNpmResource(Library.class, new CanonicalType(LIBRARY_URL_CROSS_PACKAGE_SOURCE_WITH_VERSION)); verifyLibrary( - optLibraryCrossSource.orElse(null), - LIBRARY_URL_CROSS_PACKAGE_SOURCE_NO_VERSION, - VERSION_0_2, - EXPECTED_CQL_CROSS_SOURCE); + optLibraryCrossSource.orElse(null), + LIBRARY_URL_CROSS_PACKAGE_SOURCE_NO_VERSION, + VERSION_0_2, + EXPECTED_CQL_CROSS_SOURCE); final Optional optMeasureCrossTarget = - loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_CROSS_PACKAGE_TARGET)); + loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_CROSS_PACKAGE_TARGET)); verifyMeasure( - optMeasureCrossTarget.orElse(null), - MEASURE_URL_CROSS_PACKAGE_TARGET, - VERSION_0_3, - LIBRARY_URL_CROSS_PACKAGE_TARGET_WITH_VERSION); + optMeasureCrossTarget.orElse(null), + MEASURE_URL_CROSS_PACKAGE_TARGET, + VERSION_0_3, + LIBRARY_URL_CROSS_PACKAGE_TARGET_WITH_VERSION); final Optional optLibraryCrossTarget = - loader.loadNpmResource(Library.class, new CanonicalType(LIBRARY_URL_CROSS_PACKAGE_TARGET_WITH_VERSION)); + loader.loadNpmResource(Library.class, new CanonicalType(LIBRARY_URL_CROSS_PACKAGE_TARGET_WITH_VERSION)); verifyLibrary( - optLibraryCrossTarget.orElse(null), - LIBRARY_URL_CROSS_PACKAGE_TARGET_NO_VERSION, - VERSION_0_3, - EXPECTED_CQL_CROSS_TARGET); + optLibraryCrossTarget.orElse(null), + LIBRARY_URL_CROSS_PACKAGE_TARGET_NO_VERSION, + VERSION_0_3, + EXPECTED_CQL_CROSS_TARGET); final ILibraryAdapter libraryAdapterSource = loader.findMatchingLibrary(new VersionedIdentifier() - .withId(CROSS_PACKAGE_SOURCE_ID) - .withVersion(VERSION_0_2) - .withSystem(CROSS_PACKAGE_SOURCE_URL)) - .orElse(null); + .withId(CROSS_PACKAGE_SOURCE_ID) + .withVersion(VERSION_0_2) + .withSystem(CROSS_PACKAGE_SOURCE_URL)) + .orElse(null); verifyLibrary( - libraryAdapterSource, - LIBRARY_URL_CROSS_PACKAGE_SOURCE_NO_VERSION, - VERSION_0_2, - EXPECTED_CQL_CROSS_SOURCE); + libraryAdapterSource, + LIBRARY_URL_CROSS_PACKAGE_SOURCE_NO_VERSION, + VERSION_0_2, + EXPECTED_CQL_CROSS_SOURCE); final ILibraryAdapter libraryAdapterTarget = loader.findMatchingLibrary(new VersionedIdentifier() - .withId(CROSS_PACKAGE_TARGET_ID) - .withVersion(VERSION_0_3) - .withSystem(CROSS_PACKAGE_TARGET_URL)) - .orElse(null); + .withId(CROSS_PACKAGE_TARGET_ID) + .withVersion(VERSION_0_3) + .withSystem(CROSS_PACKAGE_TARGET_URL)) + .orElse(null); verifyLibrary( - libraryAdapterTarget, - LIBRARY_URL_CROSS_PACKAGE_TARGET_NO_VERSION, - VERSION_0_3, - EXPECTED_CQL_CROSS_TARGET); + libraryAdapterTarget, + LIBRARY_URL_CROSS_PACKAGE_TARGET_NO_VERSION, + VERSION_0_3, + EXPECTED_CQL_CROSS_TARGET); } - protected void verifyMeasure(@Nullable Measure measure, String measureUrl, String version, String expectedLibraryUrl) { + protected void verifyMeasure( + @Nullable Measure measure, String measureUrl, String version, String expectedLibraryUrl) { assertNotNull(measure, "Could not find measure with url: %s".formatted(measureUrl)); @@ -592,16 +569,18 @@ protected void verifyMeasure(@Nullable Measure measure, String measureUrl, Strin assertEquals(expectedLibraryUrl, libraryUrl.asStringValue()); } - private void verifyLibrary(@Nullable ILibraryAdapter libraryAdapter, String expectedLibraryUrl, String version, String expectedCql) { + private void verifyLibrary( + @Nullable ILibraryAdapter libraryAdapter, String expectedLibraryUrl, String version, String expectedCql) { assertNotNull(libraryAdapter, "Could not find library with url: %s".formatted(expectedLibraryUrl)); assertInstanceOf(LibraryAdapter.class, libraryAdapter); - verifyLibrary(((LibraryAdapter)libraryAdapter).get(), expectedLibraryUrl, version, expectedCql); + verifyLibrary(((LibraryAdapter) libraryAdapter).get(), expectedLibraryUrl, version, expectedCql); } - private void verifyLibrary(@Nullable Library library, String expectedLibraryUrl, String version, String expectedCql) { + private void verifyLibrary( + @Nullable Library library, String expectedLibraryUrl, String version, String expectedCql) { assertNotNull(library, "Could not find library with url: %s".formatted(expectedLibraryUrl)); @@ -619,44 +598,15 @@ private void verifyLibrary(@Nullable Library library, String expectedLibraryUrl, assertEquals(expectedCql, cql); } - protected void verifyMeasure(@Nullable Measure measure, String measureUrl, String expectedLibraryUrl) { - - assertNotNull(measure, "Could not find measure with url: %s".formatted(measureUrl)); - - assertEquals(measureUrl, measure.getUrl()); - - final List libraryUrls = measure.getLibrary(); - assertEquals(1, libraryUrls.size()); - final CanonicalType libraryUrl = libraryUrls.get(0); - assertEquals(expectedLibraryUrl, libraryUrl.asStringValue()); - } - - private void verifyLibrary(@Nullable Library library, String expectedLibraryUrl, String expectedCql) { - - assertNotNull(library, "Could not find library with url: %s".formatted(expectedLibraryUrl)); - - assertEquals(expectedLibraryUrl, library.getUrl()); - - final List attachments = library.getContent(); - - assertEquals(1, attachments.size()); - - final Attachment attachment = attachments.get(0); - - assertEquals("text/cql", attachment.getContentType()); - final byte[] attachmentData = attachment.getData(); - final String cql = new String(attachmentData, StandardCharsets.UTF_8); - - assertEquals(expectedCql, cql); - } - @Nonnull - protected R4NpmPackageLoaderInMemory setup(List tgzPaths) { - return R4NpmPackageLoaderInMemory.fromNpmPackageClasspath(getClass(), tgzPaths); + @Override + protected BaseNpmPackageLoaderInMemory setup(Path... npmPackagePaths) { + return R4NpmPackageLoaderInMemory.fromNpmPackageClasspath(getClass(), npmPackagePaths); } @Nonnull - protected R4NpmPackageLoaderInMemory setup(Path... tgzPaths) { - return R4NpmPackageLoaderInMemory.fromNpmPackageClasspath(getClass(), tgzPaths); + @Override + protected BaseNpmPackageLoaderInMemory setup(List npmPackagePaths) { + return R4NpmPackageLoaderInMemory.fromNpmPackageClasspath(getClass(), npmPackagePaths); } } diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/r5/NpmResourceHolderR5Test.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/r5/NpmResourceHolderR5Test.java index 0bb03fe4de..b4ee4918e4 100644 --- a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/r5/NpmResourceHolderR5Test.java +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/r5/NpmResourceHolderR5Test.java @@ -32,237 +32,229 @@ class NpmResourceHolderR5Test extends BaseNpmResourceInfoForCqlTest { protected FhirVersionEnum fhirVersion = FhirVersionEnum.R5; private static final String EXPECTED_CQL_ALPHA = - """ + """ library opencds.simplealpha.SimpleAlpha - + using FHIR version '5.0.1' - + include FHIRHelpers version '5.0.1' called FHIRHelpers - + parameter "Measurement Period" Interval default Interval[@2021-01-01T00:00:00.0-06:00, @2022-01-01T00:00:00.0-06:00) - + context Patient - + define "Initial Population": exists ("Encounter Finished") - + define "Encounter Finished": [Encounter] E where E.status = 'finished' """; private static final String EXPECTED_CQL_BRAVO = - """ + """ library opencds.simplealpha.SimpleBravo - + using FHIR version '5.0.1' - + include FHIRHelpers version '4.0.1' called FHIRHelpers - + parameter "Measurement Period" Interval default Interval[@2024-01-01T00:00:00.0-06:00, @2025-01-01T00:00:00.0-06:00) - + context Patient - + define "Initial Population": exists ("Encounter Planned") - + define "Encounter Planned": [Encounter] E where E.status = 'planned' """; private static final String EXPECTED_CQL_WITH_DERIVED = - """ + """ library opencds.withderivedlibrary WithDerivedLibrary version '0.4' - + using FHIR version '5.0.1' - + include FHIRHelpers version '5.0.1' called FHIRHelpers include DerivedLibrary version '0.4' - + parameter "Measurement Period" Interval default Interval[@2021-01-01T00:00:00.0-06:00, @2022-01-01T00:00:00.0-06:00) - + context Patient - + define "Initial Population": exists (DerivedLibrary."Encounter Finished") """; private static final String EXPECTED_CQL_DERIVED = - """ + """ library opencds.withderivedlibrary.DerivedLibrary version '0.4' - + using FHIR version '5.0.1' - + include FHIRHelpers version '5.0.1' called FHIRHelpers - + context Patient - + define "Encounter Finished": [Encounter] E where E.status = 'finished' """; private static final String EXPECTED_CQL_DERIVED_TWO_LAYERS = - """ + """ library opencds.withtwolayersderivedlibraries.WithTwoLayersDerivedLibraries version '0.5' - + using FHIR version '5.0.1' - + include DerivedLayer1a version '0.5' include DerivedLayer1b version '0.5' - + parameter "Measurement Period" Interval default Interval[@2022-01-01T00:00:00.0-06:00, @2023-01-01T00:00:00.0-06:00) - + context Patient - + define "Initial Population": DerivedLayer1a."Initial Population" - + define "Denominator": DerivedLayer1b."Denominator" - + define "Numerator": DerivedLayer1b."Numerator" """; private static final String EXPECTED_CQL_DERIVED_1_A = - """ + """ library opencds.withtwolayersderivedlibraries.DerivedLayer1a version '0.5' - + using FHIR version '5.0.1' - + include DerivedLayer2a version '0.5' include DerivedLayer2b version '0.5' - + context Patient - + define "Initial Population": DerivedLayer2a."Initial Population" """; private static final String EXPECTED_CQL_DERIVED_1_B = - """ + """ library opencds.withtwolayersderivedlibraries.DerivedLayer1b version '0.5' - + using FHIR version '5.0.1' - + include DerivedLayer2a version '0.5' include DerivedLayer2b version '0.5' - + context Patient - + define "Denominator": DerivedLayer2a."Denominator" - + define "Numerator": DerivedLayer2b."Numerator" """; private static final String EXPECTED_CQL_DERIVED_2_A = - """ + """ library opencds.withtwolayersderivedlibraries.DerivedLayer2a version '0.5' - + using FHIR version '5.0.1' - + include FHIRHelpers version '5.0.1' called FHIRHelpers - + context Patient - + define "Initial Population": exists ("Encounter Finished") - + define "Denominator": exists ("Encounter Planned") - + define "Encounter Finished": [Encounter] E where E.status = 'finished' - + define "Encounter Planned": [Encounter] E where E.status = 'planned' """; private static final String EXPECTED_CQL_DERIVED_2_B = - """ + """ library opencds.withtwolayersderivedlibraries.DerivedLayer2b version '0.5' - + using FHIR version '5.0.1' - + include FHIRHelpers version '5.0.1' called FHIRHelpers - + parameter "Measurement Period" Interval default Interval[@2021-01-01T00:00:00.0-06:00, @2022-01-01T00:00:00.0-06:00) - + context Patient - + define "Numerator": exists ("Encounter Triaged") - + define "Encounter Triaged": [Encounter] E where E.status = 'triaged' """; private static final String EXPECTED_CQL_CROSS_SOURCE = - """ + """ library opencds.crosspackagesource.CrossPackageSource version '0.2' - + using FHIR version '5.0.1' - + include FHIRHelpers version '4.0.1' called FHIRHelpers include opencds.crosspackagetarget.CrossPackageTarget version '0.3' called CrossPackageTarget - + parameter "Measurement Period" Interval default Interval[@2020-01-01T00:00:00.0-06:00, @2021-01-01T00:00:00.0-06:00) - + context Patient - + define "Initial Population": exists (CrossPackageTarget."Encounter Finished") """; private static final String EXPECTED_CQL_CROSS_TARGET = - """ + """ library opencds.crosspackagetarget.CrossPackageTarget version '0.3' - + using FHIR version '5.0.1' - + include FHIRHelpers version '5.0.1' called FHIRHelpers - + context Patient - + define "Encounter Finished": [Encounter] E where E.status = 'finished' """; - @Override - protected FhirVersionEnum getExpectedFhirVersion() { - return FhirVersionEnum.R5; - } - @Test void simpleAlpha() { final BaseNpmPackageLoaderInMemory loader = setup(Path.of(SIMPLE_ALPHA_TGZ)); final Optional optMeasure = - loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_ALPHA)); + loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_ALPHA)); - verifyMeasure(optMeasure.orElse(null), MEASURE_URL_ALPHA, VERSION_0_1, - LIBRARY_URL_ALPHA_WITH_VERSION); + verifyMeasure(optMeasure.orElse(null), MEASURE_URL_ALPHA, VERSION_0_1, LIBRARY_URL_ALPHA_WITH_VERSION); final Optional optLibrary = - loader.loadNpmResource(Library.class, - new CanonicalType(LIBRARY_URL_ALPHA_WITH_VERSION)); + loader.loadNpmResource(Library.class, new CanonicalType(LIBRARY_URL_ALPHA_WITH_VERSION)); - verifyLibrary(optLibrary.orElse(null), LIBRARY_URL_ALPHA_NO_VERSION, VERSION_0_1, - EXPECTED_CQL_ALPHA); + verifyLibrary(optLibrary.orElse(null), LIBRARY_URL_ALPHA_NO_VERSION, VERSION_0_1, EXPECTED_CQL_ALPHA); final List allNamespaceInfos = - loader.getNamespaceManager().getAllNamespaceInfos(); + loader.getNamespaceManager().getAllNamespaceInfos(); assertEquals(1, allNamespaceInfos.size()); @@ -277,20 +269,17 @@ void simpleBravo() { final BaseNpmPackageLoaderInMemory loader = setup(Path.of(SIMPLE_BRAVO_TGZ)); final Optional optMeasure = - loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_BRAVO)); + loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_BRAVO)); - verifyMeasure(optMeasure.orElse(null), MEASURE_URL_BRAVO, VERSION_0_1, - LIBRARY_URL_BRAVO_WITH_VERSION); + verifyMeasure(optMeasure.orElse(null), MEASURE_URL_BRAVO, VERSION_0_1, LIBRARY_URL_BRAVO_WITH_VERSION); final Optional optLibrary = - loader.loadNpmResource(Library.class, - new CanonicalType(LIBRARY_URL_BRAVO_WITH_VERSION)); + loader.loadNpmResource(Library.class, new CanonicalType(LIBRARY_URL_BRAVO_WITH_VERSION)); - verifyLibrary(optLibrary.orElse(null), LIBRARY_URL_BRAVO_NO_VERSION, VERSION_0_1, - EXPECTED_CQL_BRAVO); + verifyLibrary(optLibrary.orElse(null), LIBRARY_URL_BRAVO_NO_VERSION, VERSION_0_1, EXPECTED_CQL_BRAVO); final List allNamespaceInfos = - loader.getNamespaceManager().getAllNamespaceInfos(); + loader.getNamespaceManager().getAllNamespaceInfos(); assertEquals(1, allNamespaceInfos.size()); @@ -303,44 +292,36 @@ void simpleBravo() { @Test void multiplePackages() { final BaseNpmPackageLoaderInMemory loader = R5NpmPackageLoaderInMemory.fromNpmPackageClasspath( - getClass(), - Stream.of(SIMPLE_ALPHA_TGZ, SIMPLE_BRAVO_TGZ).map(Paths::get).toList()); + getClass(), + Stream.of(SIMPLE_ALPHA_TGZ, SIMPLE_BRAVO_TGZ).map(Paths::get).toList()); final List allNamespaceInfos = - loader.getNamespaceManager().getAllNamespaceInfos(); + loader.getNamespaceManager().getAllNamespaceInfos(); assertEquals(2, allNamespaceInfos.size()); - assertTrue(allNamespaceInfos.contains( - new NamespaceInfo(SIMPLE_ALPHA_NAMESPACE, SIMPLE_ALPHA_NAMESPACE_URL))); - assertTrue(allNamespaceInfos.contains( - new NamespaceInfo(SIMPLE_BRAVO_NAMESPACE, SIMPLE_BRAVO_NAMESPACE_URL))); + assertTrue(allNamespaceInfos.contains(new NamespaceInfo(SIMPLE_ALPHA_NAMESPACE, SIMPLE_ALPHA_NAMESPACE_URL))); + assertTrue(allNamespaceInfos.contains(new NamespaceInfo(SIMPLE_BRAVO_NAMESPACE, SIMPLE_BRAVO_NAMESPACE_URL))); final Optional optMeasureAlpha = - loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_ALPHA)); + loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_ALPHA)); - verifyMeasure(optMeasureAlpha.orElse(null), MEASURE_URL_ALPHA, VERSION_0_1, - LIBRARY_URL_ALPHA_WITH_VERSION); + verifyMeasure(optMeasureAlpha.orElse(null), MEASURE_URL_ALPHA, VERSION_0_1, LIBRARY_URL_ALPHA_WITH_VERSION); final Optional optLibraryAlpha = - loader.loadNpmResource(Library.class, - new CanonicalType(LIBRARY_URL_ALPHA_WITH_VERSION)); + loader.loadNpmResource(Library.class, new CanonicalType(LIBRARY_URL_ALPHA_WITH_VERSION)); - verifyLibrary(optLibraryAlpha.orElse(null), LIBRARY_URL_ALPHA_NO_VERSION, VERSION_0_1, - EXPECTED_CQL_ALPHA); + verifyLibrary(optLibraryAlpha.orElse(null), LIBRARY_URL_ALPHA_NO_VERSION, VERSION_0_1, EXPECTED_CQL_ALPHA); final Optional optMeasureBravo = - loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_ALPHA)); + loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_ALPHA)); - verifyMeasure(optMeasureBravo.orElse(null), MEASURE_URL_ALPHA, VERSION_0_1, - LIBRARY_URL_ALPHA_WITH_VERSION); + verifyMeasure(optMeasureBravo.orElse(null), MEASURE_URL_ALPHA, VERSION_0_1, LIBRARY_URL_ALPHA_WITH_VERSION); final Optional optLibraryBravo = - loader.loadNpmResource(Library.class, - new CanonicalType(LIBRARY_URL_BRAVO_WITH_VERSION)); + loader.loadNpmResource(Library.class, new CanonicalType(LIBRARY_URL_BRAVO_WITH_VERSION)); - verifyLibrary(optLibraryBravo.orElse(null), LIBRARY_URL_BRAVO_NO_VERSION, VERSION_0_1, - EXPECTED_CQL_BRAVO); + verifyLibrary(optLibraryBravo.orElse(null), LIBRARY_URL_BRAVO_NO_VERSION, VERSION_0_1, EXPECTED_CQL_BRAVO); } @Test @@ -349,38 +330,34 @@ void derivedLibrary() { final BaseNpmPackageLoaderInMemory loader = setup(WITH_DERIVED_LIBRARY_TGZ); final List allNamespaceInfos = - loader.getNamespaceManager().getAllNamespaceInfos(); + loader.getNamespaceManager().getAllNamespaceInfos(); assertEquals(1, allNamespaceInfos.size()); - assertTrue(allNamespaceInfos.contains( - new NamespaceInfo(WITH_DERIVED_NAMESPACE, WITH_DERIVED_URL))); + assertTrue(allNamespaceInfos.contains(new NamespaceInfo(WITH_DERIVED_NAMESPACE, WITH_DERIVED_URL))); final Optional optMeasureWithDerived = - loader.loadNpmResource(Measure.class, - new CanonicalType(MEASURE_URL_WITH_DERIVED_LIBRARY)); + loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_WITH_DERIVED_LIBRARY)); - verifyMeasure(optMeasureWithDerived.orElse(null), MEASURE_URL_WITH_DERIVED_LIBRARY, - VERSION_0_4, LIBRARY_URL_WITH_DERIVED_LIBRARY_WITH_VERSION); + verifyMeasure( + optMeasureWithDerived.orElse(null), + MEASURE_URL_WITH_DERIVED_LIBRARY, + VERSION_0_4, + LIBRARY_URL_WITH_DERIVED_LIBRARY_WITH_VERSION); final Optional optLibraryWithDerived = - loader.loadNpmResource(Library.class, - new CanonicalType(LIBRARY_URL_WITH_DERIVED_LIBRARY_WITH_VERSION)); + loader.loadNpmResource(Library.class, new CanonicalType(LIBRARY_URL_WITH_DERIVED_LIBRARY_WITH_VERSION)); verifyLibrary( - optLibraryWithDerived.orElse(null), - LIBRARY_URL_WITH_DERIVED_LIBRARY_NO_VERSION, - VERSION_0_4, - EXPECTED_CQL_WITH_DERIVED); + optLibraryWithDerived.orElse(null), + LIBRARY_URL_WITH_DERIVED_LIBRARY_NO_VERSION, + VERSION_0_4, + EXPECTED_CQL_WITH_DERIVED); final Optional optLibraryDerived = - loader.loadNpmResource(Library.class, new CanonicalType(LIBRARY_URL_DERIVED_LIBRARY)); + loader.loadNpmResource(Library.class, new CanonicalType(LIBRARY_URL_DERIVED_LIBRARY)); - verifyLibrary( - optLibraryDerived.orElse(null), - LIBRARY_URL_DERIVED_LIBRARY, - VERSION_0_4, - EXPECTED_CQL_DERIVED); + verifyLibrary(optLibraryDerived.orElse(null), LIBRARY_URL_DERIVED_LIBRARY, VERSION_0_4, EXPECTED_CQL_DERIVED); } @Test @@ -389,119 +366,114 @@ void derivedLibraryTwoLayers() { final BaseNpmPackageLoaderInMemory loader = setup(WITH_TWO_LAYERS_DERIVED_LIBRARIES_TGZ); final List allNamespaceInfos = - loader.getNamespaceManager().getAllNamespaceInfos(); + loader.getNamespaceManager().getAllNamespaceInfos(); assertEquals(1, allNamespaceInfos.size()); - assertTrue(allNamespaceInfos.contains(new NamespaceInfo(WITH_TWO_LAYERS_NAMESPACE, WITH_TWO_LAYERS_DERIVED_URL))); + assertTrue( + allNamespaceInfos.contains(new NamespaceInfo(WITH_TWO_LAYERS_NAMESPACE, WITH_TWO_LAYERS_DERIVED_URL))); final Optional optMeasureWithTwoDerived = - loader.loadNpmResource(Measure.class, - new CanonicalType(MEASURE_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES)); + loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES)); verifyMeasure( - optMeasureWithTwoDerived.orElse(null), - MEASURE_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES, - VERSION_0_5, - LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES_WITH_VERSION); + optMeasureWithTwoDerived.orElse(null), + MEASURE_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES, + VERSION_0_5, + LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES_WITH_VERSION); - final Optional optLibraryWithTwoDerived = - loader.loadNpmResource(Library.class, - new CanonicalType(LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES_WITH_VERSION)); + final Optional optLibraryWithTwoDerived = loader.loadNpmResource( + Library.class, new CanonicalType(LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES_WITH_VERSION)); verifyLibrary( - optLibraryWithTwoDerived.orElse(null), - LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES_NO_VERSION, - VERSION_0_5, - EXPECTED_CQL_DERIVED_TWO_LAYERS); + optLibraryWithTwoDerived.orElse(null), + LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARIES_NO_VERSION, + VERSION_0_5, + EXPECTED_CQL_DERIVED_TWO_LAYERS); - final Optional optLibraryWithTwoDerived1a = - loader.loadNpmResource(Library.class, - new CanonicalType(LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_1A)); + final Optional optLibraryWithTwoDerived1a = loader.loadNpmResource( + Library.class, new CanonicalType(LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_1A)); verifyLibrary( - optLibraryWithTwoDerived1a.orElse(null), - LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_1A, - VERSION_0_5, - EXPECTED_CQL_DERIVED_1_A); + optLibraryWithTwoDerived1a.orElse(null), + LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_1A, + VERSION_0_5, + EXPECTED_CQL_DERIVED_1_A); - final Optional optLibraryWithTwoDerived1b = - loader.loadNpmResource(Library.class, - new CanonicalType(LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_1B)); + final Optional optLibraryWithTwoDerived1b = loader.loadNpmResource( + Library.class, new CanonicalType(LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_1B)); verifyLibrary( - optLibraryWithTwoDerived1b.orElse(null), - LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_1B, - VERSION_0_5, - EXPECTED_CQL_DERIVED_1_B); + optLibraryWithTwoDerived1b.orElse(null), + LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_1B, + VERSION_0_5, + EXPECTED_CQL_DERIVED_1_B); - final Optional optLibraryWithTwoDerived2a = - loader.loadNpmResource(Library.class, - new CanonicalType(LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_2A)); + final Optional optLibraryWithTwoDerived2a = loader.loadNpmResource( + Library.class, new CanonicalType(LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_2A)); verifyLibrary( - optLibraryWithTwoDerived2a.orElse(null), - LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_2A, - VERSION_0_5, - EXPECTED_CQL_DERIVED_2_A); + optLibraryWithTwoDerived2a.orElse(null), + LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_2A, + VERSION_0_5, + EXPECTED_CQL_DERIVED_2_A); - final Optional optLibraryWithTwoDerived2b = - loader.loadNpmResource(Library.class, - new CanonicalType(LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_2B)); + final Optional optLibraryWithTwoDerived2b = loader.loadNpmResource( + Library.class, new CanonicalType(LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_2B)); verifyLibrary( - optLibraryWithTwoDerived2b.orElse(null), - LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_2B, - VERSION_0_5, - EXPECTED_CQL_DERIVED_2_B); + optLibraryWithTwoDerived2b.orElse(null), + LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_2B, + VERSION_0_5, + EXPECTED_CQL_DERIVED_2_B); final ILibraryAdapter libraryAdapter1a = loader.findMatchingLibrary(new VersionedIdentifier() - .withId(DERIVED_LAYER_1_A) - .withVersion(VERSION_0_5) - .withSystem(WITH_TWO_LAYERS_DERIVED_URL)) - .orElse(null); + .withId(DERIVED_LAYER_1_A) + .withVersion(VERSION_0_5) + .withSystem(WITH_TWO_LAYERS_DERIVED_URL)) + .orElse(null); verifyLibrary( - libraryAdapter1a, - LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_1A, - VERSION_0_5, - EXPECTED_CQL_DERIVED_1_A); + libraryAdapter1a, + LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_1A, + VERSION_0_5, + EXPECTED_CQL_DERIVED_1_A); final ILibraryAdapter libraryAdapter1b = loader.findMatchingLibrary(new VersionedIdentifier() - .withId(DERIVED_LAYER_1_B) - .withVersion(VERSION_0_5) - .withSystem(WITH_TWO_LAYERS_DERIVED_URL)) - .orElse(null); + .withId(DERIVED_LAYER_1_B) + .withVersion(VERSION_0_5) + .withSystem(WITH_TWO_LAYERS_DERIVED_URL)) + .orElse(null); verifyLibrary( - libraryAdapter1b, - LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_1B, - VERSION_0_5, - EXPECTED_CQL_DERIVED_1_B); + libraryAdapter1b, + LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_1B, + VERSION_0_5, + EXPECTED_CQL_DERIVED_1_B); final ILibraryAdapter libraryAdapter2a = loader.findMatchingLibrary(new VersionedIdentifier() - .withId(DERIVED_LAYER_2_A) - .withVersion(VERSION_0_5) - .withSystem(WITH_TWO_LAYERS_DERIVED_URL)) - .orElse(null); + .withId(DERIVED_LAYER_2_A) + .withVersion(VERSION_0_5) + .withSystem(WITH_TWO_LAYERS_DERIVED_URL)) + .orElse(null); verifyLibrary( - libraryAdapter2a, - LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_2A, - VERSION_0_5, - EXPECTED_CQL_DERIVED_2_A); + libraryAdapter2a, + LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_2A, + VERSION_0_5, + EXPECTED_CQL_DERIVED_2_A); final ILibraryAdapter libraryAdapter2b = loader.findMatchingLibrary(new VersionedIdentifier() - .withId(DERIVED_LAYER_2_B) - .withVersion(VERSION_0_5) - .withSystem(WITH_TWO_LAYERS_DERIVED_URL)) - .orElse(null); + .withId(DERIVED_LAYER_2_B) + .withVersion(VERSION_0_5) + .withSystem(WITH_TWO_LAYERS_DERIVED_URL)) + .orElse(null); verifyLibrary( - libraryAdapter2b, - LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_2B, - VERSION_0_5, - EXPECTED_CQL_DERIVED_2_B); + libraryAdapter2b, + LIBRARY_URL_WITH_TWO_LAYERS_DERIVED_LIBRARY_2B, + VERSION_0_5, + EXPECTED_CQL_DERIVED_2_B); } @Test @@ -510,75 +482,78 @@ void crossPackage() { final BaseNpmPackageLoaderInMemory loader = setup(CROSS_PACKAGE_SOURCE_TGZ, CROSS_PACKAGE_TARGET_TGZ); final List allNamespaceInfos = - loader.getNamespaceManager().getAllNamespaceInfos(); + loader.getNamespaceManager().getAllNamespaceInfos(); assertEquals(2, allNamespaceInfos.size()); - assertTrue(allNamespaceInfos.contains(new NamespaceInfo(CROSS_PACKAGE_SOURCE_NAMESPACE, CROSS_PACKAGE_SOURCE_URL))); - assertTrue(allNamespaceInfos.contains(new NamespaceInfo(CROSS_PACKAGE_TARGET_NAMESPACE, CROSS_PACKAGE_TARGET_URL))); + assertTrue(allNamespaceInfos.contains( + new NamespaceInfo(CROSS_PACKAGE_SOURCE_NAMESPACE, CROSS_PACKAGE_SOURCE_URL))); + assertTrue(allNamespaceInfos.contains( + new NamespaceInfo(CROSS_PACKAGE_TARGET_NAMESPACE, CROSS_PACKAGE_TARGET_URL))); final Optional optMeasureCrossSource = - loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_CROSS_PACKAGE_SOURCE)); + loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_CROSS_PACKAGE_SOURCE)); verifyMeasure( - optMeasureCrossSource.orElse(null), - MEASURE_URL_CROSS_PACKAGE_SOURCE, - VERSION_0_2, - LIBRARY_URL_CROSS_PACKAGE_SOURCE_WITH_VERSION); + optMeasureCrossSource.orElse(null), + MEASURE_URL_CROSS_PACKAGE_SOURCE, + VERSION_0_2, + LIBRARY_URL_CROSS_PACKAGE_SOURCE_WITH_VERSION); final Optional optLibraryCrossSource = - loader.loadNpmResource(Library.class, new CanonicalType(LIBRARY_URL_CROSS_PACKAGE_SOURCE_WITH_VERSION)); + loader.loadNpmResource(Library.class, new CanonicalType(LIBRARY_URL_CROSS_PACKAGE_SOURCE_WITH_VERSION)); verifyLibrary( - optLibraryCrossSource.orElse(null), - LIBRARY_URL_CROSS_PACKAGE_SOURCE_NO_VERSION, - VERSION_0_2, - EXPECTED_CQL_CROSS_SOURCE); + optLibraryCrossSource.orElse(null), + LIBRARY_URL_CROSS_PACKAGE_SOURCE_NO_VERSION, + VERSION_0_2, + EXPECTED_CQL_CROSS_SOURCE); final Optional optMeasureCrossTarget = - loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_CROSS_PACKAGE_TARGET)); + loader.loadNpmResource(Measure.class, new CanonicalType(MEASURE_URL_CROSS_PACKAGE_TARGET)); verifyMeasure( - optMeasureCrossTarget.orElse(null), - MEASURE_URL_CROSS_PACKAGE_TARGET, - VERSION_0_3, - LIBRARY_URL_CROSS_PACKAGE_TARGET_WITH_VERSION); + optMeasureCrossTarget.orElse(null), + MEASURE_URL_CROSS_PACKAGE_TARGET, + VERSION_0_3, + LIBRARY_URL_CROSS_PACKAGE_TARGET_WITH_VERSION); final Optional optLibraryCrossTarget = - loader.loadNpmResource(Library.class, new CanonicalType(LIBRARY_URL_CROSS_PACKAGE_TARGET_WITH_VERSION)); + loader.loadNpmResource(Library.class, new CanonicalType(LIBRARY_URL_CROSS_PACKAGE_TARGET_WITH_VERSION)); verifyLibrary( - optLibraryCrossTarget.orElse(null), - LIBRARY_URL_CROSS_PACKAGE_TARGET_NO_VERSION, - VERSION_0_3, - EXPECTED_CQL_CROSS_TARGET); + optLibraryCrossTarget.orElse(null), + LIBRARY_URL_CROSS_PACKAGE_TARGET_NO_VERSION, + VERSION_0_3, + EXPECTED_CQL_CROSS_TARGET); final ILibraryAdapter libraryAdapterSource = loader.findMatchingLibrary(new VersionedIdentifier() - .withId(CROSS_PACKAGE_SOURCE_ID) - .withVersion(VERSION_0_2) - .withSystem(CROSS_PACKAGE_SOURCE_URL)) - .orElse(null); + .withId(CROSS_PACKAGE_SOURCE_ID) + .withVersion(VERSION_0_2) + .withSystem(CROSS_PACKAGE_SOURCE_URL)) + .orElse(null); verifyLibrary( - libraryAdapterSource, - LIBRARY_URL_CROSS_PACKAGE_SOURCE_NO_VERSION, - VERSION_0_2, - EXPECTED_CQL_CROSS_SOURCE); + libraryAdapterSource, + LIBRARY_URL_CROSS_PACKAGE_SOURCE_NO_VERSION, + VERSION_0_2, + EXPECTED_CQL_CROSS_SOURCE); final ILibraryAdapter libraryAdapterTarget = loader.findMatchingLibrary(new VersionedIdentifier() - .withId(CROSS_PACKAGE_TARGET_ID) - .withVersion(VERSION_0_3) - .withSystem(CROSS_PACKAGE_TARGET_URL)) - .orElse(null); + .withId(CROSS_PACKAGE_TARGET_ID) + .withVersion(VERSION_0_3) + .withSystem(CROSS_PACKAGE_TARGET_URL)) + .orElse(null); verifyLibrary( - libraryAdapterTarget, - LIBRARY_URL_CROSS_PACKAGE_TARGET_NO_VERSION, - VERSION_0_3, - EXPECTED_CQL_CROSS_TARGET); + libraryAdapterTarget, + LIBRARY_URL_CROSS_PACKAGE_TARGET_NO_VERSION, + VERSION_0_3, + EXPECTED_CQL_CROSS_TARGET); } - protected void verifyMeasure(@Nullable Measure measure, String measureUrl, String version, String expectedLibraryUrl) { + protected void verifyMeasure( + @Nullable Measure measure, String measureUrl, String version, String expectedLibraryUrl) { assertNotNull(measure, "Could not find measure with url: %s".formatted(measureUrl)); @@ -592,16 +567,18 @@ protected void verifyMeasure(@Nullable Measure measure, String measureUrl, Strin assertEquals(expectedLibraryUrl, libraryUrl.asStringValue()); } - private void verifyLibrary(@Nullable ILibraryAdapter libraryAdapter, String expectedLibraryUrl, String version, String expectedCql) { + private void verifyLibrary( + @Nullable ILibraryAdapter libraryAdapter, String expectedLibraryUrl, String version, String expectedCql) { assertNotNull(libraryAdapter, "Could not find library with url: %s".formatted(expectedLibraryUrl)); assertInstanceOf(LibraryAdapter.class, libraryAdapter); - verifyLibrary(((LibraryAdapter)libraryAdapter).get(), expectedLibraryUrl, version, expectedCql); + verifyLibrary(((LibraryAdapter) libraryAdapter).get(), expectedLibraryUrl, version, expectedCql); } - private void verifyLibrary(@Nullable Library library, String expectedLibraryUrl, String version, String expectedCql) { + private void verifyLibrary( + @Nullable Library library, String expectedLibraryUrl, String version, String expectedCql) { assertNotNull(library, "Could not find library with url: %s".formatted(expectedLibraryUrl)); @@ -620,7 +597,14 @@ private void verifyLibrary(@Nullable Library library, String expectedLibraryUrl, } @Nonnull + @Override protected R5NpmPackageLoaderInMemory setup(Path... tgzPaths) { return R5NpmPackageLoaderInMemory.fromNpmPackageClasspath(getClass(), tgzPaths); } + + @Nonnull + @Override + protected R5NpmPackageLoaderInMemory setup(List tgzPaths) { + return R5NpmPackageLoaderInMemory.fromNpmPackageClasspath(getClass(), tgzPaths); + } } From cbed2ed6ab9e413573d9194b0074793df66fabc4 Mon Sep 17 00:00:00 2001 From: Luke deGruchy Date: Wed, 8 Oct 2025 16:33:11 -0400 Subject: [PATCH 4/5] Small tweaks and sonar. --- .../fhir/utility/npm/BaseNpmPackageLoaderInMemory.java | 10 +++++----- .../opencds/cqf/fhir/utility/npm/NpmPackageLoader.java | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/BaseNpmPackageLoaderInMemory.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/BaseNpmPackageLoaderInMemory.java index e2d74c74c3..ed4892ffae 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/BaseNpmPackageLoaderInMemory.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/BaseNpmPackageLoaderInMemory.java @@ -2,6 +2,7 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; @@ -28,7 +29,7 @@ public abstract class BaseNpmPackageLoaderInMemory implements NpmPackageLoader { private final NpmNamespaceManager npmNamespaceManager; @Override - public Optional loadNpmResource(IPrimitiveType resourceUrl) { + public Optional loadNpmResource(IPrimitiveType resourceUrl) { return npmPackages.stream() .filter(npmPackage -> doesPackageMatch(resourceUrl, npmPackage)) .map(npmPackage -> getResource(npmPackage, resourceUrl)) @@ -39,7 +40,8 @@ private IBaseResource getResource(NpmPackage npmPackage, IPrimitiveType try { return tryGetResource(npmPackage, resourceUrl); } catch (IOException exception) { - throw new RuntimeException(FAILED_TO_LOAD_RESOURCE_TEMPLATE.formatted(resourceUrl.getValue()), exception); + throw new InternalErrorException( + FAILED_TO_LOAD_RESOURCE_TEMPLATE.formatted(resourceUrl.getValue()), exception); } } @@ -59,12 +61,10 @@ private boolean doesPackageMatch(IPrimitiveType resourceUrl, NpmPackage try { return npmPackage.hasCanonical(resourceUrl.getValue()); } catch (IOException exception) { - throw new RuntimeException(FAILED_TO_LOAD_RESOURCE_TEMPLATE, exception); + throw new InternalErrorException(FAILED_TO_LOAD_RESOURCE_TEMPLATE, exception); } } - public abstract FhirContext getFhirContext(); - @Override public NpmNamespaceManager getNamespaceManager() { return npmNamespaceManager; diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/NpmPackageLoader.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/NpmPackageLoader.java index 9124d3ddf1..87dadd7b0f 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/NpmPackageLoader.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/npm/NpmPackageLoader.java @@ -63,13 +63,16 @@ */ public interface NpmPackageLoader { Logger logger = LoggerFactory.getLogger(NpmPackageLoader.class); + String LIBRARY_URL_TEMPLATE = "%s/Library/%s"; + String LIBRARY = "Library"; + String MEASURE = "Measure"; // effectively a no-op implementation NpmPackageLoader R4_DEFAULT = new NpmPackageLoader() { @Override - public Optional loadNpmResource(IPrimitiveType canonicalUrl) { + public Optional loadNpmResource(IPrimitiveType canonicalUrl) { return Optional.empty(); } @@ -83,8 +86,6 @@ public FhirContext getFhirContext() { return FhirContext.forR4Cached(); } }; - String LIBRARY = "Library"; - String MEASURE = "Measure"; /** * Query the NPM package repo for the resource corresponding to the provided resource class and @@ -127,7 +128,7 @@ default IAdapterFactory getAdapterFactory() { * @param canonicalResourceUrl The type-enclosed String canonical URL of the resource to load. * @return The resource corresponding to the URL, if it exists. */ - Optional loadNpmResource(IPrimitiveType canonicalResourceUrl); + Optional loadNpmResource(IPrimitiveType canonicalResourceUrl); /** * Implementors must commit to supporting a specific FHIR version, in order to ensure that @@ -221,7 +222,6 @@ default Optional toLibraryAdapter(IBaseResource resource) { } case R5 -> { if (!(resource instanceof org.hl7.fhir.r5.model.Library r5Library)) { - ; throw new IllegalArgumentException( "Expected resource to be a Library, but was a " + resource.fhirType()); } From 783a48026c8f97be8c7130df822d442c0f3b3351 Mon Sep 17 00:00:00 2001 From: Luke deGruchy Date: Wed, 22 Oct 2025 11:36:08 -0400 Subject: [PATCH 5/5] Rename tests. --- ...ourceInfoForCqlTest.java => BaseNpmPackageLoaderTest.java} | 2 +- ...mResourceHolderR4Test.java => NpmPackageLoaderR4Test.java} | 4 ++-- ...mResourceHolderR5Test.java => NpmPackageLoaderR5Test.java} | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) rename cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/{BaseNpmResourceInfoForCqlTest.java => BaseNpmPackageLoaderTest.java} (99%) rename cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/r4/{NpmResourceHolderR4Test.java => NpmPackageLoaderR4Test.java} (99%) rename cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/r5/{NpmResourceHolderR5Test.java => NpmPackageLoaderR5Test.java} (99%) diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/BaseNpmResourceInfoForCqlTest.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/BaseNpmPackageLoaderTest.java similarity index 99% rename from cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/BaseNpmResourceInfoForCqlTest.java rename to cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/BaseNpmPackageLoaderTest.java index 347df23048..c57af47cd5 100644 --- a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/BaseNpmResourceInfoForCqlTest.java +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/BaseNpmPackageLoaderTest.java @@ -5,7 +5,7 @@ import java.nio.file.Paths; import java.util.List; -public abstract class BaseNpmResourceInfoForCqlTest { +public abstract class BaseNpmPackageLoaderTest { protected static final String DOT_TGZ = ".tgz"; diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/r4/NpmResourceHolderR4Test.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/r4/NpmPackageLoaderR4Test.java similarity index 99% rename from cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/r4/NpmResourceHolderR4Test.java rename to cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/r4/NpmPackageLoaderR4Test.java index 01cd16740f..f036cda55b 100644 --- a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/r4/NpmResourceHolderR4Test.java +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/r4/NpmPackageLoaderR4Test.java @@ -24,10 +24,10 @@ import org.opencds.cqf.fhir.utility.adapter.ILibraryAdapter; import org.opencds.cqf.fhir.utility.adapter.r4.LibraryAdapter; import org.opencds.cqf.fhir.utility.npm.BaseNpmPackageLoaderInMemory; -import org.opencds.cqf.fhir.utility.npm.BaseNpmResourceInfoForCqlTest; +import org.opencds.cqf.fhir.utility.npm.BaseNpmPackageLoaderTest; @SuppressWarnings("squid:S2699") -class NpmResourceHolderR4Test extends BaseNpmResourceInfoForCqlTest { +class NpmPackageLoaderR4Test extends BaseNpmPackageLoaderTest { protected FhirVersionEnum fhirVersion = FhirVersionEnum.R4; diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/r5/NpmResourceHolderR5Test.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/r5/NpmPackageLoaderR5Test.java similarity index 99% rename from cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/r5/NpmResourceHolderR5Test.java rename to cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/r5/NpmPackageLoaderR5Test.java index b4ee4918e4..0bc7dc15cd 100644 --- a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/r5/NpmResourceHolderR5Test.java +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/npm/r5/NpmPackageLoaderR5Test.java @@ -24,10 +24,10 @@ import org.opencds.cqf.fhir.utility.adapter.ILibraryAdapter; import org.opencds.cqf.fhir.utility.adapter.r5.LibraryAdapter; import org.opencds.cqf.fhir.utility.npm.BaseNpmPackageLoaderInMemory; -import org.opencds.cqf.fhir.utility.npm.BaseNpmResourceInfoForCqlTest; +import org.opencds.cqf.fhir.utility.npm.BaseNpmPackageLoaderTest; @SuppressWarnings("squid:S2699") -class NpmResourceHolderR5Test extends BaseNpmResourceInfoForCqlTest { +class NpmPackageLoaderR5Test extends BaseNpmPackageLoaderTest { protected FhirVersionEnum fhirVersion = FhirVersionEnum.R5;