Skip to content

Commit 987833a

Browse files
TahaAttaritaha.attari@smilecdr.com
andauthored
Fallback to latest non draft version if an active version is unavailable during $release (cqframework#630)
* [APHL-1326] - fallback to latest non draft version if an active version is unavailable during $release * [APHL-1326] - update tests for $release non-active fallback * [APHL-1326] - update terminology server lookup * spotless * [APHL-1326] - update mocks * cleanup --------- Co-authored-by: taha.attari@smilecdr.com <taha.attari@smilecdr.com>
1 parent 99397a5 commit 987833a

File tree

12 files changed

+354
-85
lines changed

12 files changed

+354
-85
lines changed

cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/visitor/ReleaseVisitor.java

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
public class ReleaseVisitor extends BaseKnowledgeArtifactVisitor {
4545
private static final String NOT_SUPPORTED = " not supported";
4646
private static final String ACTIVE = "active";
47+
private static final String DRAFT = "draft";
4748
private Logger logger = LoggerFactory.getLogger(ReleaseVisitor.class);
4849
private static final String DEPENDSON = "depends-on";
4950
private static final String VALUESET = "ValueSet";
@@ -263,11 +264,12 @@ private Optional<IKnowledgeArtifactAdapter> latestComponentRespectingVersions(
263264
// we trust in this case that the Endpoint URL matches up with the Authoritative Source in the ValueSet
264265
// if this assumption is faulty the only consequence is that the VSet doesn't get resolved
265266
latest = terminologyServerClient
266-
.getResource(endpoint, preReleaseReference, this.fhirVersion())
267+
.getLatestNonDraftResource(endpoint, preReleaseReference, this.fhirVersion())
267268
.map(r -> (IKnowledgeArtifactAdapter) createAdapterForResource(r));
268269
} else {
269-
// get the latest ACTIVE version only because it's NOT owned
270-
latest = VisitorHelper.tryGetLatestVersionWithStatus(preReleaseReference, repository, ACTIVE);
270+
// get the latest ACTIVE version, if not fallback to the latest non-DRAFT version
271+
latest = VisitorHelper.tryGetLatestVersionWithStatus(preReleaseReference, repository, ACTIVE)
272+
.or(() -> VisitorHelper.tryGetLatestVersionExceptStatus(preReleaseReference, repository, DRAFT));
271273
}
272274
return latest;
273275
}
@@ -390,22 +392,25 @@ private Optional<IKnowledgeArtifactAdapter> tryResolveDependency(
390392
if (!StringUtils.isBlank(Canonicals.getVersion(dependency.getReference()))) {
391393
maybeAdapter = Optional.ofNullable(getArtifactByCanonical(dependency.getReference(), repository));
392394
} else {
393-
maybeAdapter = tryFindLatestDependencyVersion(dependency, resourceType, latestFromTxServer, endpoint);
395+
maybeAdapter =
396+
tryFindLatestDependency(dependency.getReference(), resourceType, latestFromTxServer, endpoint);
394397
}
395398
return maybeAdapter;
396399
}
397400

398-
private Optional<IKnowledgeArtifactAdapter> tryFindLatestDependencyVersion(
399-
IDependencyInfo dependency, String resourceType, boolean latestFromTxServer, IEndpointAdapter endpoint) {
401+
private Optional<IKnowledgeArtifactAdapter> tryFindLatestDependency(
402+
String reference, String resourceType, boolean latestFromTxServer, IEndpointAdapter endpoint) {
400403
Optional<IKnowledgeArtifactAdapter> maybeAdapter = Optional.empty();
401404
// we trust in this case that the Endpoint URL matches up with the Authoritative Source in the ValueSet
402405
// if this assumption is faulty the only consequence is that the VSet doesn't get resolved
403406
if (resourceType != null && resourceType.equals(VALUESET) && latestFromTxServer) {
404407
maybeAdapter = terminologyServerClient
405-
.getResource(endpoint, dependency.getReference(), this.fhirVersion())
408+
.getLatestNonDraftResource(endpoint, reference, this.fhirVersion())
406409
.map(r -> (IKnowledgeArtifactAdapter) createAdapterForResource(r));
407410
} else {
408-
maybeAdapter = VisitorHelper.tryGetLatestVersionWithStatus(dependency.getReference(), repository, ACTIVE);
411+
// get the latest ACTIVE version, if not fallback to the latest non-DRAFT version
412+
maybeAdapter = VisitorHelper.tryGetLatestVersionWithStatus(reference, repository, ACTIVE)
413+
.or(() -> VisitorHelper.tryGetLatestVersionExceptStatus(reference, repository, DRAFT));
409414
}
410415
return maybeAdapter;
411416
}
@@ -595,7 +600,7 @@ private void checkReleasePreconditions(IKnowledgeArtifactAdapter artifact, Date
595600
throw new ResourceNotFoundException("Resource not found.");
596601
}
597602

598-
if (!artifact.getStatus().equals("draft")) {
603+
if (!artifact.getStatus().equals(DRAFT)) {
599604
throw new PreconditionFailedException(String.format(
600605
"Resource with ID: '%s' does not have a status of 'draft'.",
601606
artifact.get().getIdElement().getIdPart()));
@@ -624,7 +629,7 @@ private void checkVersionValidSemver(String version) throws UnprocessableEntityE
624629
if (version == null || version.isEmpty()) {
625630
throw new UnprocessableEntityException("The version argument is required");
626631
}
627-
if (version.contains("draft")) {
632+
if (version.contains(DRAFT)) {
628633
throw new UnprocessableEntityException("The version cannot contain 'draft'");
629634
}
630635
if (version.contains("/") || version.contains("\\") || version.contains("|")) {

cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/visitor/VisitorHelper.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,4 +208,12 @@ public static Optional<IKnowledgeArtifactAdapter> tryGetLatestVersionWithStatus(
208208
.map(res -> IAdapterFactory.forFhirVersion(res.getStructureFhirVersionEnum())
209209
.createKnowledgeArtifactAdapter(res));
210210
}
211+
212+
public static Optional<IKnowledgeArtifactAdapter> tryGetLatestVersionExceptStatus(
213+
String inputReference, Repository repository, String status) {
214+
return IKnowledgeArtifactAdapter.findLatestVersion(SearchHelper.searchRepositoryByCanonicalWithPagingWithParams(
215+
repository, inputReference, Searches.exceptStatus(status)))
216+
.map(res -> IAdapterFactory.forFhirVersion(res.getStructureFhirVersionEnum())
217+
.createKnowledgeArtifactAdapter(res));
218+
}
211219
}

cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/visitor/dstu3/ReleaseVisitorTests.java

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.opencds.cqf.fhir.cr.visitor.dstu3;
22

33
import static org.junit.Assert.assertNull;
4+
import static org.junit.Assert.assertSame;
45
import static org.junit.jupiter.api.Assertions.assertEquals;
56
import static org.junit.jupiter.api.Assertions.assertNotNull;
67
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -29,6 +30,7 @@
2930
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
3031
import org.hl7.fhir.dstu3.model.CodeType;
3132
import org.hl7.fhir.dstu3.model.DateType;
33+
import org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus;
3234
import org.hl7.fhir.dstu3.model.Extension;
3335
import org.hl7.fhir.dstu3.model.IdType;
3436
import org.hl7.fhir.dstu3.model.Library;
@@ -509,7 +511,7 @@ void release_latest_from_tx_server_sets_versions() {
509511
var endpoint = createEndpoint(authoritativeSource);
510512

511513
var clientMock = mock(TerminologyServerClient.class, new ReturnsDeepStubs());
512-
when(clientMock.getResource(any(), any(), any())).thenReturn(Optional.of(latestVSet));
514+
when(clientMock.getLatestNonDraftResource(any(), any(), any())).thenReturn(Optional.of(latestVSet));
513515
var releaseVisitor = new ReleaseVisitor(repo, clientMock);
514516
var libraryAdapter = new AdapterFactory().createLibrary(library);
515517
var params = parameters(
@@ -521,17 +523,19 @@ void release_latest_from_tx_server_sets_versions() {
521523
var grouper = repo.read(ValueSet.class, new IdType("ValueSet/dxtc"));
522524
var include = grouper.getCompose().getIncludeFirstRep();
523525
assertNotNull(Canonicals.getVersion(include.getValueSet().get(0).getValue()));
524-
assertTrue(
525-
Canonicals.getVersion(include.getValueSet().get(0).getValue()).equals(latestVSet.getVersion()));
526+
assertEquals(
527+
latestVSet.getVersion(),
528+
Canonicals.getVersion(include.getValueSet().get(0).getValue()));
526529
var updatedLibrary = repo.read(Library.class, new IdType("Library/SpecificationLibrary"));
527530
var leafRelatedArtifact = updatedLibrary.getRelatedArtifact().stream()
528531
.filter(ra -> ra.getResource().getReference().contains(leafOid))
529532
.findAny();
530533
assertTrue(leafRelatedArtifact.isPresent());
531534
assertNotNull(
532535
Canonicals.getVersion(leafRelatedArtifact.get().getResource().getReference()));
533-
assertTrue(Canonicals.getVersion(leafRelatedArtifact.get().getResource().getReference())
534-
.equals(latestVSet.getVersion()));
536+
assertEquals(
537+
latestVSet.getVersion(),
538+
Canonicals.getVersion(leafRelatedArtifact.get().getResource().getReference()));
535539
}
536540

537541
private IEndpointAdapter createEndpoint(String authoritativeSource) {
@@ -762,6 +766,8 @@ void release_should_pin_the_latest_version_of_dependencies() {
762766
var releaseVisitor = new ReleaseVisitor(repo);
763767
var originalLibrary = repo.read(Library.class, new IdType("Library/SpecificationLibrary"))
764768
.copy();
769+
var retiredLeaf = repo.read(ValueSet.class, new IdType("ValueSet/2.16.840.1.113762.1.4.1146.77-old"))
770+
.copy();
765771
var testLibrary = originalLibrary.copy();
766772
var libraryAdapter = new AdapterFactory().createLibrary(testLibrary);
767773
var params =
@@ -773,11 +779,23 @@ void release_should_pin_the_latest_version_of_dependencies() {
773779
assertTrue(maybeLib.isPresent());
774780
var releasedLibrary =
775781
repo.read(Library.class, new IdType(maybeLib.get().getResponse().getLocation()));
776-
var maybeLeafRA = releasedLibrary.getRelatedArtifact().stream()
782+
var maybeActiveLeafRA = releasedLibrary.getRelatedArtifact().stream()
777783
.filter(ra -> ra.getResource().getReference().contains("2.16.840.1.113762.1.4.1146.6"))
778784
.findFirst();
779-
assertTrue(maybeLeafRA.isPresent());
780-
assertTrue(Canonicals.getVersion(maybeLeafRA.get().getResource().getReference())
781-
.equals("1.0.1"));
785+
assertTrue(maybeActiveLeafRA.isPresent());
786+
assertEquals(
787+
"1.0.1",
788+
Canonicals.getVersion(maybeActiveLeafRA.get().getResource().getReference()));
789+
var maybeRetiredLeafRA = releasedLibrary.getRelatedArtifact().stream()
790+
.filter(ra -> ra.getResource().getReference().contains("2.16.840.1.113762.1.4.1146.77"))
791+
.findFirst();
792+
assertTrue(maybeRetiredLeafRA.isPresent());
793+
assertEquals(
794+
retiredLeaf.getUrl() + "|" + retiredLeaf.getVersion(),
795+
maybeRetiredLeafRA.get().getResource().getReference());
796+
assertSame(PublicationStatus.RETIRED, retiredLeaf.getStatus());
797+
assertEquals(
798+
"3.2.0",
799+
Canonicals.getVersion(maybeRetiredLeafRA.get().getResource().getReference()));
782800
}
783801
}

cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/visitor/r4/ReleaseVisitorTests.java

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.opencds.cqf.fhir.cr.visitor.r4;
22

33
import static org.junit.Assert.assertNull;
4+
import static org.junit.Assert.assertSame;
45
import static org.junit.jupiter.api.Assertions.assertEquals;
56
import static org.junit.jupiter.api.Assertions.assertNotNull;
67
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -31,6 +32,7 @@
3132
import org.hl7.fhir.r4.model.CanonicalType;
3233
import org.hl7.fhir.r4.model.CodeType;
3334
import org.hl7.fhir.r4.model.DateType;
35+
import org.hl7.fhir.r4.model.Enumerations.PublicationStatus;
3436
import org.hl7.fhir.r4.model.Extension;
3537
import org.hl7.fhir.r4.model.IdType;
3638
import org.hl7.fhir.r4.model.Library;
@@ -484,7 +486,7 @@ void release_latest_from_tx_server_sets_versions() {
484486
var endpoint = createEndpoint(authoritativeSource);
485487

486488
var clientMock = mock(TerminologyServerClient.class, new ReturnsDeepStubs());
487-
when(clientMock.getResource(any(), any(), any())).thenReturn(Optional.of(latestVSet));
489+
when(clientMock.getLatestNonDraftResource(any(), any(), any())).thenReturn(Optional.of(latestVSet));
488490
var releaseVisitor = new ReleaseVisitor(repo, clientMock);
489491
var libraryAdapter = new AdapterFactory().createLibrary(library);
490492
var params = parameters(
@@ -496,16 +498,18 @@ void release_latest_from_tx_server_sets_versions() {
496498
var grouper = repo.read(ValueSet.class, new IdType("ValueSet/dxtc"));
497499
var include = grouper.getCompose().getIncludeFirstRep();
498500
assertNotNull(Canonicals.getVersion(include.getValueSet().get(0).getValue()));
499-
assertTrue(
500-
Canonicals.getVersion(include.getValueSet().get(0).getValue()).equals(latestVSet.getVersion()));
501+
assertEquals(
502+
latestVSet.getVersion(),
503+
Canonicals.getVersion(include.getValueSet().get(0).getValue()));
501504
var updatedLibrary = repo.read(Library.class, new IdType("Library/SpecificationLibrary"));
502505
var leafRelatedArtifact = updatedLibrary.getRelatedArtifact().stream()
503506
.filter(ra -> ra.getResource().contains(leafOid))
504507
.findAny();
505508
assertTrue(leafRelatedArtifact.isPresent());
506509
assertNotNull(Canonicals.getVersion(leafRelatedArtifact.get().getResource()));
507-
assertTrue(
508-
Canonicals.getVersion(leafRelatedArtifact.get().getResource()).equals(latestVSet.getVersion()));
510+
assertEquals(
511+
latestVSet.getVersion(),
512+
Canonicals.getVersion(leafRelatedArtifact.get().getResource()));
509513
}
510514

511515
private IEndpointAdapter createEndpoint(String authoritativeSource) {
@@ -735,6 +739,8 @@ void release_should_pin_the_latest_version_of_dependencies() {
735739
var releaseVisitor = new ReleaseVisitor(repo);
736740
var originalLibrary = repo.read(Library.class, new IdType("Library/SpecificationLibrary"))
737741
.copy();
742+
var retiredLeaf = repo.read(ValueSet.class, new IdType("ValueSet/2.16.840.1.113762.1.4.1146.77-old"))
743+
.copy();
738744
var testLibrary = originalLibrary.copy();
739745
var libraryAdapter = new AdapterFactory().createLibrary(testLibrary);
740746
var params =
@@ -746,10 +752,19 @@ void release_should_pin_the_latest_version_of_dependencies() {
746752
assertTrue(maybeLib.isPresent());
747753
var releasedLibrary =
748754
repo.read(Library.class, new IdType(maybeLib.get().getResponse().getLocation()));
749-
var maybeLeafRA = releasedLibrary.getRelatedArtifact().stream()
755+
var maybeActiveLeafRA = releasedLibrary.getRelatedArtifact().stream()
750756
.filter(ra -> ra.getResource().contains("2.16.840.1.113762.1.4.1146.6"))
751757
.findFirst();
752-
assertTrue(maybeLeafRA.isPresent());
753-
assertTrue(Canonicals.getVersion(maybeLeafRA.get().getResource()).equals("1.0.1"));
758+
assertTrue(maybeActiveLeafRA.isPresent());
759+
assertEquals("1.0.1", Canonicals.getVersion(maybeActiveLeafRA.get().getResource()));
760+
var maybeRetiredLeafRA = releasedLibrary.getRelatedArtifact().stream()
761+
.filter(ra -> ra.getResource().contains("2.16.840.1.113762.1.4.1146.77"))
762+
.findFirst();
763+
assertTrue(maybeRetiredLeafRA.isPresent());
764+
assertEquals(
765+
retiredLeaf.getUrl() + "|" + retiredLeaf.getVersion(),
766+
maybeRetiredLeafRA.get().getResource());
767+
assertSame(PublicationStatus.RETIRED, retiredLeaf.getStatus());
768+
assertEquals("3.2.0", Canonicals.getVersion(maybeRetiredLeafRA.get().getResource()));
754769
}
755770
}

cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/visitor/r5/ReleaseVisitorTests.java

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.opencds.cqf.fhir.cr.visitor.r5;
22

3+
import static org.junit.Assert.assertSame;
34
import static org.junit.jupiter.api.Assertions.assertEquals;
45
import static org.junit.jupiter.api.Assertions.assertNotNull;
56
import static org.junit.jupiter.api.Assertions.assertNull;
@@ -30,6 +31,7 @@
3031
import org.hl7.fhir.r5.model.CanonicalType;
3132
import org.hl7.fhir.r5.model.CodeType;
3233
import org.hl7.fhir.r5.model.DateType;
34+
import org.hl7.fhir.r5.model.Enumerations.PublicationStatus;
3335
import org.hl7.fhir.r5.model.Extension;
3436
import org.hl7.fhir.r5.model.IdType;
3537
import org.hl7.fhir.r5.model.Library;
@@ -480,7 +482,7 @@ void release_latest_from_tx_server_sets_versions() {
480482
var endpoint = createEndpoint(authoritativeSource);
481483

482484
var clientMock = mock(TerminologyServerClient.class, new ReturnsDeepStubs());
483-
when(clientMock.getResource(any(), any(), any())).thenReturn(Optional.of(latestVSet));
485+
when(clientMock.getLatestNonDraftResource(any(), any(), any())).thenReturn(Optional.of(latestVSet));
484486
var releaseVisitor = new ReleaseVisitor(repo, clientMock);
485487
var libraryAdapter = new AdapterFactory().createLibrary(library);
486488
var params = parameters(
@@ -492,16 +494,18 @@ void release_latest_from_tx_server_sets_versions() {
492494
var grouper = repo.read(ValueSet.class, new IdType("ValueSet/dxtc"));
493495
var include = grouper.getCompose().getIncludeFirstRep();
494496
assertNotNull(Canonicals.getVersion(include.getValueSet().get(0).getValue()));
495-
assertTrue(
496-
Canonicals.getVersion(include.getValueSet().get(0).getValue()).equals(latestVSet.getVersion()));
497+
assertEquals(
498+
latestVSet.getVersion(),
499+
Canonicals.getVersion(include.getValueSet().get(0).getValue()));
497500
var updatedLibrary = repo.read(Library.class, new IdType("Library/SpecificationLibrary"));
498501
var leafRelatedArtifact = updatedLibrary.getRelatedArtifact().stream()
499502
.filter(ra -> ra.getResource().contains(leafOid))
500503
.findAny();
501504
assertTrue(leafRelatedArtifact.isPresent());
502505
assertNotNull(Canonicals.getVersion(leafRelatedArtifact.get().getResource()));
503-
assertTrue(
504-
Canonicals.getVersion(leafRelatedArtifact.get().getResource()).equals(latestVSet.getVersion()));
506+
assertEquals(
507+
latestVSet.getVersion(),
508+
Canonicals.getVersion(leafRelatedArtifact.get().getResource()));
505509
}
506510

507511
private IEndpointAdapter createEndpoint(String authoritativeSource) {
@@ -733,6 +737,8 @@ void release_should_pin_the_latest_version_of_dependencies() {
733737
var releaseVisitor = new ReleaseVisitor(repo);
734738
var originalLibrary = repo.read(Library.class, new IdType("Library/SpecificationLibrary"))
735739
.copy();
740+
var retiredLeaf = repo.read(ValueSet.class, new IdType("ValueSet/2.16.840.1.113762.1.4.1146.77-old"))
741+
.copy();
736742
var testLibrary = originalLibrary.copy();
737743
var libraryAdapter = new AdapterFactory().createLibrary(testLibrary);
738744
var params =
@@ -744,10 +750,19 @@ void release_should_pin_the_latest_version_of_dependencies() {
744750
assertTrue(maybeLib.isPresent());
745751
var releasedLibrary =
746752
repo.read(Library.class, new IdType(maybeLib.get().getResponse().getLocation()));
747-
var maybeLeafRA = releasedLibrary.getRelatedArtifact().stream()
753+
var maybeActiveLeafRA = releasedLibrary.getRelatedArtifact().stream()
748754
.filter(ra -> ra.getResource().contains("2.16.840.1.113762.1.4.1146.6"))
749755
.findFirst();
750-
assertTrue(maybeLeafRA.isPresent());
751-
assertTrue(Canonicals.getVersion(maybeLeafRA.get().getResource()).equals("1.0.1"));
756+
assertTrue(maybeActiveLeafRA.isPresent());
757+
assertEquals("1.0.1", Canonicals.getVersion(maybeActiveLeafRA.get().getResource()));
758+
var maybeRetiredLeafRA = releasedLibrary.getRelatedArtifact().stream()
759+
.filter(ra -> ra.getResource().contains("2.16.840.1.113762.1.4.1146.77"))
760+
.findFirst();
761+
assertTrue(maybeRetiredLeafRA.isPresent());
762+
assertEquals(
763+
retiredLeaf.getUrl() + "|" + retiredLeaf.getVersion(),
764+
maybeRetiredLeafRA.get().getResource());
765+
assertSame(PublicationStatus.RETIRED, retiredLeaf.getStatus());
766+
assertEquals("3.2.0", Canonicals.getVersion(maybeRetiredLeafRA.get().getResource()));
752767
}
753768
}

0 commit comments

Comments
 (0)