diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/visitor/DataRequirementsVisitor.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/visitor/DataRequirementsVisitor.java index 9ed4121f3..359b1ac11 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/visitor/DataRequirementsVisitor.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/visitor/DataRequirementsVisitor.java @@ -11,8 +11,10 @@ import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.cqframework.cql.cql2elm.CqlTranslator; @@ -102,7 +104,7 @@ public IBase visit(KnowledgeArtifactAdapter adapter, Repository repository, IBas library.setStatus(adapter.getStatus()); library.setType("module-definition"); } - var gatheredResources = new ArrayList(); + var gatheredResources = new HashSet(); var relatedArtifacts = stripInvalid(library); recursiveGather( adapter.get(), @@ -203,7 +205,7 @@ protected LibraryManager createLibraryManager(Repository repository) { protected void recursiveGather( IDomainResource resource, - List gatheredResources, + Set gatheredResources, List relatedArtifacts, Repository repository, List capability, @@ -212,10 +214,13 @@ protected void recursiveGather( List checkArtifactVersion, List forceArtifactVersion) throws PreconditionFailedException { - if (resource != null && !gatheredResources.contains(resource.getId())) { - gatheredResources.add(resource.getId()); - var fhirVersion = resource.getStructureFhirVersionEnum(); - var adapter = AdapterFactory.forFhirVersion(fhirVersion).createKnowledgeArtifactAdapter(resource); + if (resource == null) { + return; + } + var fhirVersion = resource.getStructureFhirVersionEnum(); + var adapter = AdapterFactory.forFhirVersion(fhirVersion).createKnowledgeArtifactAdapter(resource); + if (!gatheredResources.contains(adapter.getCanonical())) { + gatheredResources.add(adapter.getCanonical()); findUnsupportedCapability(adapter, capability); processCanonicals(adapter, artifactVersion, checkArtifactVersion, forceArtifactVersion); var reference = adapter.hasVersion() diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/KnowledgeArtifactAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/KnowledgeArtifactAdapter.java index b118432dd..a64b1c5b3 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/KnowledgeArtifactAdapter.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/KnowledgeArtifactAdapter.java @@ -98,6 +98,17 @@ default void setVersion(String version) { getModelResolver().setValue(get(), "version", newStringType(get().getStructureFhirVersionEnum(), version)); } + /** + * Returns the url of the artifact appended with '|' version if the artifact has a version. + * @return + */ + default String getCanonical() { + if (!hasUrl()) { + return getId().getValueAsString(); + } + return getUrl().concat(hasVersion() ? String.format("|%s", getVersion()) : ""); + } + List getDependencies(); default String getReferenceSource() { diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/PackageVisitor.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/PackageVisitor.java index 7440109e3..511497d45 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/PackageVisitor.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/PackageVisitor.java @@ -13,8 +13,10 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Date; +import java.util.HashSet; import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.instance.model.api.IBase; @@ -46,7 +48,6 @@ public class PackageVisitor implements IKnowledgeArtifactVisitor { protected final FhirContext fhirContext; protected final TerminologyServerClient terminologyServerClient; protected final ExpandHelper expandHelper; - protected List packagedResources; public PackageVisitor(FhirContext fhirContext) { this.fhirContext = fhirContext; @@ -114,7 +115,7 @@ public IBase visit(KnowledgeArtifactAdapter adapter, Repository repository, IBas throw new UnprocessableEntityException("'count' must be non-negative"); } var resource = adapter.get(); - packagedResources = new ArrayList<>(); + var packagedResources = new HashSet(); // TODO: In the case of a released (active) root Library we can depend on the relatedArtifacts as a // comprehensive manifest var packagedBundle = BundleHelper.newBundle(fhirVersion); @@ -126,6 +127,7 @@ public IBase visit(KnowledgeArtifactAdapter adapter, Repository repository, IBas } else { recursivePackage( resource, + packagedResources, packagedBundle, repository, capability, @@ -188,6 +190,7 @@ protected void handleValueSets( protected void recursivePackage( IDomainResource resource, + Set packagedResources, IBaseBundle bundle, Repository repository, List capability, @@ -197,10 +200,13 @@ protected void recursivePackage( List forceArtifactVersion, boolean isPut) throws PreconditionFailedException { - if (resource != null && !packagedResources.contains(resource.getId())) { - packagedResources.add(resource.getId()); - var fhirVersion = resource.getStructureFhirVersionEnum(); - var adapter = AdapterFactory.forFhirVersion(fhirVersion).createKnowledgeArtifactAdapter(resource); + if (resource == null) { + return; + } + var fhirVersion = resource.getStructureFhirVersionEnum(); + var adapter = AdapterFactory.forFhirVersion(fhirVersion).createKnowledgeArtifactAdapter(resource); + if (!packagedResources.contains(adapter.getCanonical())) { + packagedResources.add(adapter.getCanonical()); findUnsupportedCapability(adapter, capability); processCanonicals(adapter, artifactVersion, checkArtifactVersion, forceArtifactVersion); boolean entryExists = BundleHelper.getEntryResources(bundle).stream() @@ -231,6 +237,7 @@ protected void recursivePackage( .map(searchBundle -> (IDomainResource) BundleHelper.getEntryResourceFirstRep(searchBundle)) .forEach(component -> recursivePackage( component, + packagedResources, bundle, repository, capability, diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/ReleaseVisitor.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/ReleaseVisitor.java index 86db8b110..834ef628b 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/ReleaseVisitor.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/ReleaseVisitor.java @@ -9,9 +9,11 @@ import java.util.ArrayList; import java.util.Date; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.regex.Pattern; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; @@ -85,7 +87,8 @@ public IBase visit( latestFromTxServer.orElse(false), requireNonExperimental, repository, - new Date()); + new Date(), + new HashSet()); var rootArtifactOriginalDependencies = new ArrayList(rootAdapter.getDependencies()); // Get list of extensions which need to be preserved var originalDependenciesWithExtensions = rootArtifactOriginalDependencies.stream() @@ -111,9 +114,11 @@ public IBase visit( .orElse(new ArrayList()); // Report all dependencies, resolving unversioned dependencies to the latest known version, recursively + var gatheredResources = new HashSet(); gatherDependencies( rootAdapter, rootAdapter, + gatheredResources, releasedResources, fhirVersion, repository, @@ -191,9 +196,14 @@ private List internalRelease( boolean latestFromTxServer, Optional experimentalBehavior, Repository repository, - Date current) + Date current, + Set releasedResources) throws NotImplementedOperationException, ResourceNotFoundException { var resourcesToUpdate = new ArrayList(); + if (releasedResources.contains(artifactAdapter.getCanonical())) { + return resourcesToUpdate; + } + releasedResources.add(artifactAdapter.getCanonical()); // Step 1: Update the Date, version and propagate effectivePeriod if it doesn't exist updateMetadata(artifactAdapter, version, rootEffectivePeriod, current); // Step 2: add the resource to the list of released resources @@ -221,7 +231,8 @@ private List internalRelease( latestFromTxServer, experimentalBehavior, repository, - current)); + current, + releasedResources)); } else { // if missing throw because it's an owned resource throw new ResourceNotFoundException(String.format( @@ -243,139 +254,153 @@ private List internalRelease( private void gatherDependencies( KnowledgeArtifactAdapter rootAdapter, KnowledgeArtifactAdapter artifactAdapter, + Set gatheredResources, List releasedResources, FhirVersionEnum fhirVersion, Repository repository, Map alreadyUpdatedDependencies, List systemVersionExpansionParameters, List canonicalVersionExpansionParameters) { - - // Step 1: Check components, add them to the cache and convert to dependencies - for (var component : artifactAdapter.getComponents()) { - // all components are already updated to latest as part of release - var preReleaseReference = KnowledgeArtifactAdapter.getRelatedArtifactReference(component); - var updatedReference = preReleaseReference; - Optional res = Optional.empty(); - if (KnowledgeArtifactAdapter.checkIfRelatedArtifactIsOwned(component)) { - res = checkIfReferenceInList(preReleaseReference, releasedResources); - if (!res.isPresent()) { - // should never happen since we check all references as part of `internalRelease` - throw new InternalErrorException( - "Owned resource reference not found during release: " + preReleaseReference); - } - } else { - res = VisitorHelper.tryGetLatestVersion(preReleaseReference, repository); - } - if (res.isPresent()) { - // add to cache if resolvable - if (!alreadyUpdatedDependencies.containsKey(Canonicals.getUrl(preReleaseReference))) { - alreadyUpdatedDependencies.put( - Canonicals.getUrl(preReleaseReference), res.get().get()); - } - // update the reference to latest - updatedReference = res.get().hasVersion() - ? String.format("%s|%s", res.get().getUrl(), res.get().getVersion()) - : res.get().getUrl(); - KnowledgeArtifactAdapter.setRelatedArtifactReference( - component, updatedReference, res.get().getDescriptor()); - } - var componentToDependency = KnowledgeArtifactAdapter.newRelatedArtifact( - fhirVersion, - DEPENDSON, - updatedReference, - res.map(a -> a.getDescriptor()).orElse(null)); - var updatedRelatedArtifacts = artifactAdapter.getRelatedArtifact(); - updatedRelatedArtifacts.add(componentToDependency); - artifactAdapter.setRelatedArtifact(updatedRelatedArtifacts); + if (artifactAdapter == null) { + return; } - var dependencies = artifactAdapter.getDependencies(); - // Step 2: update dependencies recursively - for (var dependency : dependencies) { - KnowledgeArtifactAdapter dependencyAdapter = null; - if (alreadyUpdatedDependencies.containsKey(Canonicals.getUrl(dependency.getReference()))) { - dependencyAdapter = AdapterFactory.forFhirVersion(fhirVersion) - .createKnowledgeArtifactAdapter( - alreadyUpdatedDependencies.get(Canonicals.getUrl(dependency.getReference()))); - String versionedReference = addVersionToReference(dependency.getReference(), dependencyAdapter); - dependency.setReference(versionedReference); + if (!gatheredResources.contains(artifactAdapter.getCanonical())) { + gatheredResources.add(artifactAdapter.getCanonical()); - // the dependency is already updated we just need to recurse into it - gatherDependencies( - rootAdapter, - dependencyAdapter, - releasedResources, - fhirVersion, - repository, - alreadyUpdatedDependencies, - systemVersionExpansionParameters, - canonicalVersionExpansionParameters); - } else { - // try to get versions from expansion parameters if they are available - var resourceType = Canonicals.getResourceType(dependency.getReference()) == null - ? null - : SearchHelper.getResourceType(repository, dependency); - if (StringUtils.isBlank(Canonicals.getVersion(dependency.getReference()))) { - // TODO: update when we support requireVersionedDependencies - Optional expansionParametersVersion = Optional.empty(); - // assume if we can't figure out the resource type it's a CodeSystem - if (resourceType == null || resourceType.getSimpleName().equals("CodeSystem")) { - expansionParametersVersion = systemVersionExpansionParameters.stream() - .filter(canonical -> !StringUtils.isBlank(Canonicals.getUrl(canonical))) - .filter(canonical -> - Canonicals.getUrl(canonical).equals(dependency.getReference())) - .findAny(); - } else if (resourceType.getSimpleName().equals("ValueSet")) { - expansionParametersVersion = canonicalVersionExpansionParameters.stream() - .filter(canonical -> - Canonicals.getUrl(canonical).equals(dependency.getReference())) - .findAny(); + // Step 1: Check components, add them to the cache and convert to dependencies + for (var component : artifactAdapter.getComponents()) { + // all components are already updated to latest as part of release + var preReleaseReference = KnowledgeArtifactAdapter.getRelatedArtifactReference(component); + var updatedReference = preReleaseReference; + Optional res; + if (KnowledgeArtifactAdapter.checkIfRelatedArtifactIsOwned(component)) { + res = checkIfReferenceInList(preReleaseReference, releasedResources); + if (!res.isPresent()) { + // should never happen since we check all references as part of `internalRelease` + throw new InternalErrorException( + "Owned resource reference not found during release: " + preReleaseReference); } - expansionParametersVersion - .map(canonical -> Canonicals.getVersion(canonical)) - .ifPresent(version -> dependency.setReference(dependency.getReference() + "|" + version)); - } - - Optional maybeAdapter = Optional.empty(); - // if not available in expansion parameters then try to find the latest version and update the - // dependency - if (StringUtils.isBlank(Canonicals.getVersion(dependency.getReference()))) { - maybeAdapter = VisitorHelper.tryGetLatestVersionWithStatus( - dependency.getReference(), repository, "active") - .map(adapter -> { - String versionedReference = addVersionToReference(dependency.getReference(), adapter); - dependency.setReference(versionedReference); - alreadyUpdatedDependencies.put( - Canonicals.getUrl(dependency.getReference()), adapter.get()); - return adapter; - }); } else { - // This is a versioned reference, just get the dependency - maybeAdapter = Optional.ofNullable(getArtifactByCanonical(dependency.getReference(), repository)); + res = VisitorHelper.tryGetLatestVersion(preReleaseReference, repository); } - // if the dependency is resolvable then recurse into it - if (maybeAdapter.isPresent()) { - dependencyAdapter = maybeAdapter.get(); + if (res.isPresent()) { + // add to cache if resolvable + if (!alreadyUpdatedDependencies.containsKey(Canonicals.getUrl(preReleaseReference))) { + alreadyUpdatedDependencies.put( + Canonicals.getUrl(preReleaseReference), + res.get().get()); + } + // update the reference to latest + updatedReference = res.get().hasVersion() + ? String.format( + "%s|%s", res.get().getUrl(), res.get().getVersion()) + : res.get().getUrl(); + KnowledgeArtifactAdapter.setRelatedArtifactReference( + component, updatedReference, res.get().getDescriptor()); + } + var componentToDependency = KnowledgeArtifactAdapter.newRelatedArtifact( + fhirVersion, + DEPENDSON, + updatedReference, + res.map(a -> a.getDescriptor()).orElse(null)); + var updatedRelatedArtifacts = artifactAdapter.getRelatedArtifact(); + updatedRelatedArtifacts.add(componentToDependency); + artifactAdapter.setRelatedArtifact(updatedRelatedArtifacts); + } + var dependencies = artifactAdapter.getDependencies(); + // Step 2: update dependencies recursively + for (var dependency : dependencies) { + KnowledgeArtifactAdapter dependencyAdapter = null; + if (alreadyUpdatedDependencies.containsKey(Canonicals.getUrl(dependency.getReference()))) { + dependencyAdapter = AdapterFactory.forFhirVersion(fhirVersion) + .createKnowledgeArtifactAdapter( + alreadyUpdatedDependencies.get(Canonicals.getUrl(dependency.getReference()))); + String versionedReference = addVersionToReference(dependency.getReference(), dependencyAdapter); + dependency.setReference(versionedReference); + + // the dependency is already updated we just need to recurse into it gatherDependencies( rootAdapter, dependencyAdapter, + gatheredResources, releasedResources, fhirVersion, repository, alreadyUpdatedDependencies, systemVersionExpansionParameters, canonicalVersionExpansionParameters); + } else { + // try to get versions from expansion parameters if they are available + var resourceType = Canonicals.getResourceType(dependency.getReference()) == null + ? null + : SearchHelper.getResourceType(repository, dependency); + if (StringUtils.isBlank(Canonicals.getVersion(dependency.getReference()))) { + // TODO: update when we support requireVersionedDependencies + Optional expansionParametersVersion = Optional.empty(); + // assume if we can't figure out the resource type it's a CodeSystem + if (resourceType == null || resourceType.getSimpleName().equals("CodeSystem")) { + expansionParametersVersion = systemVersionExpansionParameters.stream() + .filter(canonical -> !StringUtils.isBlank(Canonicals.getUrl(canonical))) + .filter(canonical -> + Canonicals.getUrl(canonical).equals(dependency.getReference())) + .findAny(); + } else if (resourceType.getSimpleName().equals("ValueSet")) { + expansionParametersVersion = canonicalVersionExpansionParameters.stream() + .filter(canonical -> + Canonicals.getUrl(canonical).equals(dependency.getReference())) + .findAny(); + } + expansionParametersVersion + .map(canonical -> Canonicals.getVersion(canonical)) + .ifPresent( + version -> dependency.setReference(dependency.getReference() + "|" + version)); + } + + Optional maybeAdapter; + // if not available in expansion parameters then try to find the latest version and update the + // dependency + if (StringUtils.isBlank(Canonicals.getVersion(dependency.getReference()))) { + maybeAdapter = VisitorHelper.tryGetLatestVersionWithStatus( + dependency.getReference(), repository, "active") + .map(adapter -> { + String versionedReference = + addVersionToReference(dependency.getReference(), adapter); + dependency.setReference(versionedReference); + alreadyUpdatedDependencies.put( + Canonicals.getUrl(dependency.getReference()), adapter.get()); + return adapter; + }); + } else { + // This is a versioned reference, just get the dependency + maybeAdapter = + Optional.ofNullable(getArtifactByCanonical(dependency.getReference(), repository)); + } + // if the dependency is resolvable then recurse into it + if (maybeAdapter.isPresent()) { + dependencyAdapter = maybeAdapter.get(); + gatherDependencies( + rootAdapter, + dependencyAdapter, + gatheredResources, + releasedResources, + fhirVersion, + repository, + alreadyUpdatedDependencies, + systemVersionExpansionParameters, + canonicalVersionExpansionParameters); + } + } + // only add the dependency to the manifest if it is from a leaf artifact + if (!artifactAdapter.getUrl().equals(rootAdapter.getUrl())) { + var newDep = KnowledgeArtifactAdapter.newRelatedArtifact( + fhirVersion, + DEPENDSON, + dependency.getReference(), + dependencyAdapter != null ? dependencyAdapter.getDescriptor() : null); + var updatedRelatedArtifacts = rootAdapter.getRelatedArtifact(); + updatedRelatedArtifacts.add(newDep); + rootAdapter.setRelatedArtifact(updatedRelatedArtifacts); } - } - // only add the dependency to the manifest if it is from a leaf artifact - if (!artifactAdapter.getUrl().equals(rootAdapter.getUrl())) { - var newDep = KnowledgeArtifactAdapter.newRelatedArtifact( - fhirVersion, - DEPENDSON, - dependency.getReference(), - dependencyAdapter != null ? dependencyAdapter.getDescriptor() : null); - var updatedRelatedArtifacts = rootAdapter.getRelatedArtifact(); - updatedRelatedArtifacts.add(newDep); - rootAdapter.setRelatedArtifact(updatedRelatedArtifacts); } } } diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/dstu3/ReleaseVisitorTests.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/dstu3/ReleaseVisitorTests.java index 567f12ce3..93abc4325 100644 --- a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/dstu3/ReleaseVisitorTests.java +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/dstu3/ReleaseVisitorTests.java @@ -719,4 +719,32 @@ void release_preserves_extensions() { }); } } + + @Test + void release_should_not_duplicate_components_as_dependencies() { + var bundle = (Bundle) jsonParser.parseResource( + ReleaseVisitorTests.class.getResourceAsStream("Bundle-small-approved-draft.json")); + repo.transaction(bundle); + var releaseVisitor = new ReleaseVisitor(); + var originalLibrary = repo.read(Library.class, new IdType("Library/SpecificationLibrary")) + .copy(); + var testLibrary = originalLibrary.copy(); + var libraryAdapter = new AdapterFactory().createLibrary(testLibrary); + var params = + parameters(part("version", new StringType("1.2.3")), part("versionBehavior", new CodeType("force"))); + var returnResource = (Bundle) libraryAdapter.accept(releaseVisitor, repo, params); + Optional maybeRCTCLib = returnResource.getEntry().stream() + .filter(entry -> entry.getResponse().getLocation().contains("Library/rctc")) + .findFirst(); + assertTrue(maybeRCTCLib.isPresent()); + var releasedRCTCLibrary = repo.read( + Library.class, new IdType(maybeRCTCLib.get().getResponse().getLocation())); + assertEquals(2, releasedRCTCLibrary.getRelatedArtifact().size()); + // 1 component + assertTrue(releasedRCTCLibrary.getRelatedArtifact().stream() + .anyMatch(ra -> ra.getType() == RelatedArtifactType.DEPENDSON)); + // 1 dependency + assertTrue(releasedRCTCLibrary.getRelatedArtifact().stream() + .anyMatch(ra -> ra.getType() == RelatedArtifactType.COMPOSEDOF)); + } } diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r4/ReleaseVisitorTests.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r4/ReleaseVisitorTests.java index eb2b3a8c1..8719b4195 100644 --- a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r4/ReleaseVisitorTests.java +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r4/ReleaseVisitorTests.java @@ -698,4 +698,32 @@ void release_preserves_extensions() { }); } } + + @Test + void release_should_not_duplicate_components_as_dependencies() { + var bundle = (Bundle) jsonParser.parseResource( + ReleaseVisitorTests.class.getResourceAsStream("Bundle-small-approved-draft.json")); + repo.transaction(bundle); + var releaseVisitor = new ReleaseVisitor(); + var originalLibrary = repo.read(Library.class, new IdType("Library/SpecificationLibrary")) + .copy(); + var testLibrary = originalLibrary.copy(); + var libraryAdapter = new AdapterFactory().createLibrary(testLibrary); + var params = + parameters(part("version", new StringType("1.2.3")), part("versionBehavior", new CodeType("force"))); + var returnResource = (Bundle) libraryAdapter.accept(releaseVisitor, repo, params); + Optional maybeRCTCLib = returnResource.getEntry().stream() + .filter(entry -> entry.getResponse().getLocation().contains("Library/rctc")) + .findFirst(); + assertTrue(maybeRCTCLib.isPresent()); + var releasedRCTCLibrary = repo.read( + Library.class, new IdType(maybeRCTCLib.get().getResponse().getLocation())); + assertEquals(2, releasedRCTCLibrary.getRelatedArtifact().size()); + // 1 component + assertTrue(releasedRCTCLibrary.getRelatedArtifact().stream() + .anyMatch(ra -> ra.getType() == RelatedArtifactType.DEPENDSON)); + // 1 dependency + assertTrue(releasedRCTCLibrary.getRelatedArtifact().stream() + .anyMatch(ra -> ra.getType() == RelatedArtifactType.COMPOSEDOF)); + } } diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r5/ReleaseVisitorTests.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r5/ReleaseVisitorTests.java index 03e00dddf..18c98d57a 100644 --- a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r5/ReleaseVisitorTests.java +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r5/ReleaseVisitorTests.java @@ -698,4 +698,32 @@ void release_preserves_extensions() { }); } } + + @Test + void release_should_not_duplicate_components_as_dependencies() { + var bundle = (Bundle) jsonParser.parseResource( + ReleaseVisitorTests.class.getResourceAsStream("Bundle-small-approved-draft.json")); + repo.transaction(bundle); + var releaseVisitor = new ReleaseVisitor(); + var originalLibrary = repo.read(Library.class, new IdType("Library/SpecificationLibrary")) + .copy(); + var testLibrary = originalLibrary.copy(); + var libraryAdapter = new AdapterFactory().createLibrary(testLibrary); + var params = + parameters(part("version", new StringType("1.2.3")), part("versionBehavior", new CodeType("force"))); + var returnResource = (Bundle) libraryAdapter.accept(releaseVisitor, repo, params); + Optional maybeRCTCLib = returnResource.getEntry().stream() + .filter(entry -> entry.getResponse().getLocation().contains("Library/rctc")) + .findFirst(); + assertTrue(maybeRCTCLib.isPresent()); + var releasedRCTCLibrary = repo.read( + Library.class, new IdType(maybeRCTCLib.get().getResponse().getLocation())); + assertEquals(2, releasedRCTCLibrary.getRelatedArtifact().size()); + // 1 component + assertTrue(releasedRCTCLibrary.getRelatedArtifact().stream() + .anyMatch(ra -> ra.getType() == RelatedArtifactType.DEPENDSON)); + // 1 dependency + assertTrue(releasedRCTCLibrary.getRelatedArtifact().stream() + .anyMatch(ra -> ra.getType() == RelatedArtifactType.COMPOSEDOF)); + } }