diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/SearchHelper.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/SearchHelper.java index eba1849ef..d59fbc675 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/SearchHelper.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/SearchHelper.java @@ -6,6 +6,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import ca.uhn.fhir.parser.DataFormatException; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -51,12 +52,78 @@ public static IBaseResource readRepository(Repository repository, IIdType id) { */ public static > IBaseResource searchRepositoryByCanonical( Repository repository, CanonicalType canonical) { - var resourceType = repository + + var resourceType = getResourceType(repository, canonical); + return searchRepositoryByCanonical(repository, canonical, resourceType); + } + + /** + * Gets the resource type for the given canonical, based on the convention that canonical + * URLs are of the form [base]/[resourceType]/[tail] + * + * If the URL does not conform to the convention, the cqf-resourceType extension is used + * to determine the type of the resource, if present. + * + * If no extension is present, the type of the canonical is assumed to be CodeSystem, on + * the grounds that most (if not all) non-conventional URLs are for CodeSystem uris. + * + * @param + * @param repository the repository to search + * @param canonical the canonical url to search for + * @return + */ + private static > Class getResourceType(Repository repository, CanonicalType canonical) { + Class resourceType = null; + try { + resourceType = repository .fhirContext() .getResourceDefinition(Canonicals.getResourceType(canonical)) .getImplementingClass(); + } + catch (DataFormatException e) { + // TODO: Use the "cqf-resourceType" extension to figure this out, if it's present + // NOTE: This is based on the assumption that only CodeSystems don't follow the canonical pattern... + resourceType = repository + .fhirContext() + .getResourceDefinition("CodeSystem") + .getImplementingClass(); + } - return searchRepositoryByCanonical(repository, canonical, resourceType); + return resourceType; + } + + /** + * Gets the resource type for the given canonical, based on the convention that canonical + * URLs are of the form [base]/[resourceType]/[tail] + * + * If the URL does not conform to the convention, the cqf-resourceType extension is used + * to determine the type of the resource, if present. + * + * If no extension is present, the type of the canonical is assumed to be CodeSystem, on + * the grounds that most (if not all) non-conventional URLs are for CodeSystem uris. + * + * @param repository + * @param canonical + * @return + */ + private static Class getResourceType(Repository repository, String canonical) { + Class resourceType = null; + try { + resourceType = repository + .fhirContext() + .getResourceDefinition(Canonicals.getResourceType(canonical)) + .getImplementingClass(); + } + catch (RuntimeException e) { + // TODO: Use the "cqf-resourceType" extension to figure this out, if it's present + // NOTE: This is based on the assumption that only CodeSystems don't follow the canonical pattern... + resourceType = repository + .fhirContext() + .getResourceDefinition("CodeSystem") + .getImplementingClass(); + } + + return resourceType; } /** @@ -95,11 +162,7 @@ IBaseResource searchRepositoryByCanonical( */ public static > IBaseBundle searchRepositoryByCanonicalWithPaging( Repository repository, CanonicalType canonical) { - var resourceType = repository - .fhirContext() - .getResourceDefinition(Canonicals.getResourceType(canonical)) - .getImplementingClass(); - + var resourceType = getResourceType(repository, canonical); return searchRepositoryByCanonicalWithPaging(repository, canonical, resourceType); } @@ -113,11 +176,7 @@ public static > IBaseBundle searchR */ public static > IBaseBundle searchRepositoryByCanonicalWithPaging( Repository repository, String canonical) { - var resourceType = repository - .fhirContext() - .getResourceDefinition(Canonicals.getResourceType(canonical)) - .getImplementingClass(); - + var resourceType = getResourceType(repository, canonical); return searchRepositoryByCanonicalWithPaging(repository, canonical, resourceType); } diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/KnowledgeArtifactAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/KnowledgeArtifactAdapter.java index 0072b98b6..385cd5cd6 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 @@ -40,6 +40,16 @@ default void setId(IIdType id) { void setName(String name); + boolean hasTitle(); + + String getTitle(); + + void setTitle(String title); + + default String getDescriptor() { + return String.format("%s %s%s", this.get().fhirType(), this.hasTitle() ? this.getTitle() : this.getName(), this.hasVersion() ? ", " + this.getVersion() : ""); + } + boolean hasUrl(); String getUrl(); @@ -78,22 +88,25 @@ default void setId(IIdType id) { @SuppressWarnings("unchecked") static T newRelatedArtifact( - FhirVersionEnum version, String type, String reference) { + FhirVersionEnum version, String type, String reference, String display) { switch (version) { case DSTU3: var dstu3 = new org.hl7.fhir.dstu3.model.RelatedArtifact(); dstu3.setType(org.hl7.fhir.dstu3.model.RelatedArtifact.RelatedArtifactType.fromCode(type)) - .setResource(new Reference(reference)); + .setResource(new Reference(reference)) + .setDisplay(display); return (T) dstu3; case R4: var r4 = new org.hl7.fhir.r4.model.RelatedArtifact(); r4.setType(org.hl7.fhir.r4.model.RelatedArtifact.RelatedArtifactType.fromCode(type)) - .setResource(reference); + .setResource(reference) + .setDisplay(display); return (T) r4; case R5: var r5 = new org.hl7.fhir.r5.model.RelatedArtifact(); r5.setType(org.hl7.fhir.r5.model.RelatedArtifact.RelatedArtifactType.fromCode(type)) - .setResource(reference); + .setResource(reference) + .setDisplay(display); return (T) r5; default: @@ -150,13 +163,13 @@ private static String getRelatedArtifactType(org.hl7.fhir.r5.model.RelatedArtifa } static void setRelatedArtifactReference( - T relatedArtifact, String reference) { + T relatedArtifact, String reference, String display) { if (relatedArtifact instanceof org.hl7.fhir.dstu3.model.RelatedArtifact) { - setRelatedArtifactReference((org.hl7.fhir.dstu3.model.RelatedArtifact) relatedArtifact, reference); + setRelatedArtifactReference((org.hl7.fhir.dstu3.model.RelatedArtifact) relatedArtifact, reference, display); } else if (relatedArtifact instanceof org.hl7.fhir.r4.model.RelatedArtifact) { - setRelatedArtifactReference((org.hl7.fhir.r4.model.RelatedArtifact) relatedArtifact, reference); + setRelatedArtifactReference((org.hl7.fhir.r4.model.RelatedArtifact) relatedArtifact, reference, display); } else if (relatedArtifact instanceof org.hl7.fhir.r5.model.RelatedArtifact) { - setRelatedArtifactReference((org.hl7.fhir.r5.model.RelatedArtifact) relatedArtifact, reference); + setRelatedArtifactReference((org.hl7.fhir.r5.model.RelatedArtifact) relatedArtifact, reference, display); } else { throw new UnprocessableEntityException("Must be a valid RelatedArtifact"); } @@ -164,18 +177,21 @@ static void setRelatedArtifactRe ; private static void setRelatedArtifactReference( - org.hl7.fhir.dstu3.model.RelatedArtifact relatedArtifact, String reference) { + org.hl7.fhir.dstu3.model.RelatedArtifact relatedArtifact, String reference, String display) { relatedArtifact.getResource().setReference(reference); + relatedArtifact.setDisplay(display); } private static void setRelatedArtifactReference( - org.hl7.fhir.r4.model.RelatedArtifact relatedArtifact, String reference) { + org.hl7.fhir.r4.model.RelatedArtifact relatedArtifact, String reference, String display) { relatedArtifact.setResource(reference); + relatedArtifact.setDisplay(display); } private static void setRelatedArtifactReference( - org.hl7.fhir.r5.model.RelatedArtifact relatedArtifact, String reference) { + org.hl7.fhir.r5.model.RelatedArtifact relatedArtifact, String reference, String display) { relatedArtifact.setResource(reference); + relatedArtifact.setDisplay(display); } void setEffectivePeriod(ICompositeType effectivePeriod); diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/KnowledgeArtifactAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/KnowledgeArtifactAdapter.java index 510383d12..ed3ea9b7c 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/KnowledgeArtifactAdapter.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/KnowledgeArtifactAdapter.java @@ -77,6 +77,16 @@ public String getName() { return this.get().getName(); } + @Override + public boolean hasTitle() { + return this.get().hasTitle(); + } + + @Override + public String getTitle() { + return this.get().getTitle(); + } + @Override public String getPurpose() { return null; @@ -87,6 +97,11 @@ public void setName(String name) { this.get().setName(name); } + @Override + public void setTitle(String title) { + this.get().setTitle(title); + } + @Override public Date getApprovalDate() { return null; diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/LibraryAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/LibraryAdapter.java index d376efa1a..43dd3616e 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/LibraryAdapter.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/LibraryAdapter.java @@ -79,6 +79,16 @@ public String getName() { return this.getLibrary().getName(); } + @Override + public boolean hasTitle() { + return this.getLibrary().hasTitle(); + } + + @Override + public String getTitle() { + return this.getLibrary().getTitle(); + } + @Override public String getPurpose() { return this.getLibrary().getPurpose(); @@ -89,6 +99,11 @@ public void setName(String name) { this.getLibrary().setName(name); } + @Override + public void setTitle(String title) { + this.getLibrary().setTitle(title); + } + @Override public String getUrl() { return this.getLibrary().getUrl(); diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/PlanDefinitionAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/PlanDefinitionAdapter.java index 9bf4b6b15..28bf513cd 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/PlanDefinitionAdapter.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/PlanDefinitionAdapter.java @@ -75,6 +75,16 @@ public String getName() { return this.getPlanDefinition().getName(); } + @Override + public boolean hasTitle() { + return this.getPlanDefinition().hasTitle(); + } + + @Override + public String getTitle() { + return this.getPlanDefinition().getTitle(); + } + @Override public String getPurpose() { return this.getPlanDefinition().getPurpose(); @@ -85,6 +95,11 @@ public void setName(String name) { this.getPlanDefinition().setName(name); } + @Override + public void setTitle(String title) { + this.getPlanDefinition().setTitle(title); + } + @Override public String getUrl() { return this.getPlanDefinition().getUrl(); diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/ValueSetAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/ValueSetAdapter.java index 8e11f2d87..de96dc7d6 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/ValueSetAdapter.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/dstu3/ValueSetAdapter.java @@ -74,6 +74,16 @@ public String getName() { return this.getValueSet().getName(); } + @Override + public boolean hasTitle() { + return this.getValueSet().hasTitle(); + } + + @Override + public String getTitle() { + return this.getValueSet().getTitle(); + } + @Override public String getPurpose() { return this.getValueSet().getPurpose(); @@ -84,6 +94,11 @@ public void setName(String name) { this.getValueSet().setName(name); } + @Override + public void setTitle(String title) { + this.getValueSet().setTitle(title); + } + @Override public String getUrl() { return this.getValueSet().getUrl(); diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/AdapterFactory.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/AdapterFactory.java index 892aa20f8..fa0508aab 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/AdapterFactory.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/AdapterFactory.java @@ -7,6 +7,7 @@ import org.hl7.fhir.instance.model.api.ICompositeType; import org.hl7.fhir.instance.model.api.IDomainResource; import org.hl7.fhir.r4.model.Library; +import org.hl7.fhir.r4.model.Measure; import org.hl7.fhir.r4.model.MetadataResource; import org.hl7.fhir.r4.model.PlanDefinition; import org.hl7.fhir.r4.model.ValueSet; @@ -29,6 +30,8 @@ public KnowledgeArtifactAdapter createKnowledgeArtifactAdapter(IDomainResource r KnowledgeArtifactAdapter retval; if (resource instanceof Library) { retval = createLibrary(resource); + } else if (resource instanceof Measure) { + retval = new org.opencds.cqf.fhir.utility.adapter.r4.MeasureAdapter((Measure)resource); } else if (resource instanceof PlanDefinition) { retval = new org.opencds.cqf.fhir.utility.adapter.r4.PlanDefinitionAdapter((PlanDefinition) resource); } else if (resource instanceof ValueSet) { @@ -39,7 +42,7 @@ public KnowledgeArtifactAdapter createKnowledgeArtifactAdapter(IDomainResource r (MetadataResource) resource); } else { throw new UnprocessableEntityException( - String.format("Resouce must be instance of %s", MetadataResource.class.getName())); + String.format("Resource must be instance of %s", MetadataResource.class.getName())); } } return retval; diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/KnowledgeArtifactAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/KnowledgeArtifactAdapter.java index 8550f1d04..a17b81774 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/KnowledgeArtifactAdapter.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/KnowledgeArtifactAdapter.java @@ -77,6 +77,16 @@ public String getName() { return this.get().getName(); } + @Override + public boolean hasTitle() { + return this.get().hasTitle(); + } + + @Override + public String getTitle() { + return this.get().getTitle(); + } + @Override public String getPurpose() { return null; @@ -87,6 +97,11 @@ public void setName(String name) { this.get().setName(name); } + @Override + public void setTitle(String title) { + this.get().setTitle(title); + } + @Override public Date getApprovalDate() { return null; diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/LibraryAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/LibraryAdapter.java index d362daf9e..e367975e4 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/LibraryAdapter.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/LibraryAdapter.java @@ -62,6 +62,16 @@ public String getName() { return this.getLibrary().getName(); } + @Override + public boolean hasTitle() { + return this.getLibrary().hasTitle(); + } + + @Override + public String getTitle() { + return this.getLibrary().getTitle(); + } + @Override public String getPurpose() { return this.getLibrary().getPurpose(); @@ -72,6 +82,11 @@ public void setName(String name) { this.getLibrary().setName(name); } + @Override + public void setTitle(String title) { + this.getLibrary().setTitle(title); + } + @Override public boolean hasUrl() { return this.getLibrary().hasUrl(); diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/MeasureAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/MeasureAdapter.java new file mode 100644 index 000000000..ab3326a04 --- /dev/null +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/MeasureAdapter.java @@ -0,0 +1,317 @@ +package org.opencds.cqf.fhir.utility.adapter.r4; + +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.instance.model.api.IBase; +import org.hl7.fhir.instance.model.api.IBaseExtension; +import org.hl7.fhir.instance.model.api.IBaseHasExtensions; +import org.hl7.fhir.instance.model.api.IBaseParameters; +import org.hl7.fhir.instance.model.api.ICompositeType; +import org.hl7.fhir.instance.model.api.IDomainResource; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.instance.model.api.IPrimitiveType; +import org.hl7.fhir.r4.model.CanonicalType; +import org.hl7.fhir.r4.model.DateTimeType; +import org.hl7.fhir.r4.model.Enumerations.PublicationStatus; +import org.hl7.fhir.r4.model.Extension; +import org.hl7.fhir.r4.model.Library; +import org.hl7.fhir.r4.model.Measure; +import org.hl7.fhir.r4.model.Period; +import org.hl7.fhir.r4.model.PlanDefinition; +import org.hl7.fhir.r4.model.Reference; +import org.hl7.fhir.r4.model.RelatedArtifact; +import org.hl7.fhir.r4.model.RelatedArtifact.RelatedArtifactType; +import org.opencds.cqf.fhir.api.Repository; +import org.opencds.cqf.fhir.utility.adapter.DependencyInfo; +import org.opencds.cqf.fhir.utility.adapter.IDependencyInfo; +import org.opencds.cqf.fhir.utility.adapter.KnowledgeArtifactAdapter; +import org.opencds.cqf.fhir.utility.visitor.KnowledgeArtifactVisitor; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class MeasureAdapter extends ResourceAdapter implements KnowledgeArtifactAdapter { + + private Measure measure; + + public MeasureAdapter(IDomainResource measure) { + super(measure); + + if (!(measure instanceof Measure)) { + throw new IllegalArgumentException( + "resource passed as measure argument is not a Measure resource"); + } + + this.measure = (Measure) measure; + } + + public MeasureAdapter(Measure measure) { + super(measure); + this.measure = measure; + } + + @Override + public IBase accept( + KnowledgeArtifactVisitor visitor, Repository repository, IBaseParameters operationParameters) { + return visitor.visit(this, repository, operationParameters); + } + + protected Measure getMeasure() { + return this.measure; + } + + @Override + public Measure get() { + return this.measure; + } + + @Override + public Measure copy() { + return this.get().copy(); + } + + @Override + public IIdType getId() { + return this.getMeasure().getIdElement(); + } + + @Override + public void setId(IIdType id) { + this.getMeasure().setId(id); + } + + @Override + public String getName() { + return this.getMeasure().getName(); + } + + @Override + public boolean hasTitle() { + return this.getMeasure().hasTitle(); + } + + @Override + public String getTitle() { + return this.getMeasure().getTitle(); + } + + @Override + public String getPurpose() { + return this.getMeasure().getPurpose(); + } + + @Override + public void setName(String name) { + this.getMeasure().setName(name); + } + + @Override + public void setTitle(String title) { + this.getMeasure().setTitle(title); + } + + @Override + public String getUrl() { + return this.getMeasure().getUrl(); + } + + @Override + public boolean hasUrl() { + return this.getMeasure().hasUrl(); + } + + @Override + public void setUrl(String url) { + this.getMeasure().setUrl(url); + } + + @Override + public String getVersion() { + return this.getMeasure().getVersion(); + } + + @Override + public boolean hasVersion() { + return this.getMeasure().hasVersion(); + } + + @Override + public void setVersion(String version) { + this.getMeasure().setVersion(version); + } + + private boolean checkedEffectiveDataRequirements; + private Library effectiveDataRequirements; + private LibraryAdapter effectiveDataRequirementsAdapter; + + private void findEffectiveDataRequirements() { + if (!checkedEffectiveDataRequirements) { + var edrExtensions = this.getMeasure().getExtension().stream() + .filter(ext -> ext.getUrl().endsWith("-effectiveDataRequirements")) + .filter(ext -> ext.hasValue()) + .collect(Collectors.toList()); + + var edrExtension = edrExtensions.size() == 1 ? edrExtensions.get(0) : null; + if (edrExtension != null) { + var edrReference = ((Reference)edrExtension.getValue()).getReference(); + for (var c : getMeasure().getContained()) { + if (c.hasId() && String.format("#%s", c.getId()).equals(edrReference) && c instanceof Library) { + effectiveDataRequirements = (Library)c; + effectiveDataRequirementsAdapter = new LibraryAdapter(effectiveDataRequirements); + } + } + } + checkedEffectiveDataRequirements = true; + } + } + + @Override + public List getDependencies() { + + // If an effectiveDataRequirements library is present, use it exclusively + findEffectiveDataRequirements(); + if (effectiveDataRequirements != null) { + return effectiveDataRequirementsAdapter.getDependencies(); + } + + // Otherwise, fall back to the relatedArtifact and library + List references = new ArrayList<>(); + final String referenceSource = this.getMeasure().hasVersion() + ? this.getMeasure().getUrl() + "|" + + this.getMeasure().getVersion() + : this.getMeasure().getUrl(); + /* + relatedArtifact[].resource + library[] + group[].population[].criteria.reference + group[].stratifier[].criteria.reference + group[].stratifier[].component[].criteria.reference + supplementalData[].criteria.reference + extension[cqfm-inputParameters][] + extension[cqfm-expansionParameters][] + extension[cqfm-effectiveDataRequirements] + extension[cqfm-cqlOptions] + extension[cqfm-component][].resource + extension[crmi-effectiveDataRequirements] + */ + + // relatedArtifact[].resource + references.addAll(this.getRelatedArtifact().stream() + .map(ra -> DependencyInfo.convertRelatedArtifact(ra, referenceSource)) + .collect(Collectors.toList())); + + // library[] + List libraries = this.getMeasure().getLibrary(); + for (CanonicalType ct : libraries) { + DependencyInfo dependency = new DependencyInfo( + referenceSource, ct.getValue(), ct.getExtension(), (reference) -> ct.setValue(reference)); + references.add(dependency); + } + + return references; + } + + @Override + public Date getApprovalDate() { + return this.getMeasure().getApprovalDate(); + } + + @Override + public Date getDate() { + return this.getMeasure().getDate(); + } + + @Override + public void setDate(Date date) { + this.getMeasure().setDate(date); + } + + @Override + public void setDateElement(IPrimitiveType date) { + if (date != null && !(date instanceof DateTimeType)) { + throw new UnprocessableEntityException("Date must be " + DateTimeType.class.getName()); + } + this.getMeasure().setDateElement((DateTimeType) date); + } + + @Override + public Period getEffectivePeriod() { + return this.getMeasure().getEffectivePeriod(); + } + + @Override + public void setApprovalDate(Date date) { + this.getMeasure().setApprovalDate(date); + } + + @Override + public boolean hasRelatedArtifact() { + return this.getMeasure().hasRelatedArtifact(); + } + + @SuppressWarnings("unchecked") + @Override + public List getRelatedArtifact() { + return this.getMeasure().getRelatedArtifact(); + } + + @Override + public void setRelatedArtifact(List relatedArtifacts) { + this.getMeasure() + .setRelatedArtifact(relatedArtifacts.stream() + .map(ra -> (RelatedArtifact) ra) + .collect(Collectors.toList())); + } + + @SuppressWarnings("unchecked") + @Override + public List getRelatedArtifactsOfType(String codeString) { + RelatedArtifactType type; + try { + type = RelatedArtifactType.fromCode(codeString); + } catch (FHIRException e) { + throw new UnprocessableEntityException("Invalid related artifact code"); + } + return this.getRelatedArtifact().stream() + .filter(ra -> ra.getType() == type) + .collect(Collectors.toList()); + } + + @Override + public void setEffectivePeriod(ICompositeType effectivePeriod) { + if (effectivePeriod != null && !(effectivePeriod instanceof Period)) { + throw new UnprocessableEntityException("EffectivePeriod must be " + Period.class.getName()); + } + this.getMeasure().setEffectivePeriod((Period) effectivePeriod); + } + + @Override + public void setStatus(String statusCodeString) { + PublicationStatus status; + try { + status = PublicationStatus.fromCode(statusCodeString); + } catch (FHIRException e) { + throw new UnprocessableEntityException("Invalid status code"); + } + this.getMeasure().setStatus(status); + } + + @Override + public String getStatus() { + return this.getMeasure().getStatus() == null + ? null + : this.getMeasure().getStatus().toCode(); + } + + @Override + public boolean getExperimental() { + return this.getMeasure().getExperimental(); + } + + @Override + public void setExtension(List> extensions) { + this.get().setExtension(extensions.stream().map(e -> (Extension) e).collect(Collectors.toList())); + } +} diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/PlanDefinitionAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/PlanDefinitionAdapter.java index 0088e07c2..eaa5c22de 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/PlanDefinitionAdapter.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/PlanDefinitionAdapter.java @@ -83,6 +83,16 @@ public String getName() { return this.getPlanDefinition().getName(); } + @Override + public boolean hasTitle() { + return this.getPlanDefinition().hasTitle(); + } + + @Override + public String getTitle() { + return this.getPlanDefinition().getTitle(); + } + @Override public String getPurpose() { return this.getPlanDefinition().getPurpose(); @@ -93,6 +103,11 @@ public void setName(String name) { this.getPlanDefinition().setName(name); } + @Override + public void setTitle(String title) { + this.getPlanDefinition().setTitle(title); + } + @Override public String getUrl() { return this.getPlanDefinition().getUrl(); diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/ValueSetAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/ValueSetAdapter.java index 0eea2c0d5..34c216193 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/ValueSetAdapter.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r4/ValueSetAdapter.java @@ -80,6 +80,16 @@ public String getName() { return this.getValueSet().getName(); } + @Override + public boolean hasTitle() { + return this.getValueSet().hasTitle(); + } + + @Override + public String getTitle() { + return this.getValueSet().getTitle(); + } + @Override public String getPurpose() { return this.getValueSet().getPurpose(); @@ -90,6 +100,11 @@ public void setName(String name) { this.getValueSet().setName(name); } + @Override + public void setTitle(String title) { + this.getValueSet().setTitle(title); + } + @Override public String getUrl() { return this.getValueSet().getUrl(); diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/KnowledgeArtifactAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/KnowledgeArtifactAdapter.java index 7a1ed87d1..f0672c243 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/KnowledgeArtifactAdapter.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/KnowledgeArtifactAdapter.java @@ -91,6 +91,21 @@ public void setName(String name) { this.get().setName(name); } + @Override + public boolean hasTitle() { + return this.get().hasTitle(); + } + + @Override + public String getTitle() { + return this.get().getTitle(); + } + + @Override + public void setTitle(String title) { + this.get().setTitle(title); + } + @Override public Date getApprovalDate() { return this.get().getApprovalDate(); diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/LibraryAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/LibraryAdapter.java index be93dd368..436db0cbd 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/LibraryAdapter.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/LibraryAdapter.java @@ -59,6 +59,16 @@ public String getName() { return this.getLibrary().getName(); } + @Override + public boolean hasTitle() { + return this.getLibrary().hasTitle(); + } + + @Override + public String getTitle() { + return this.getLibrary().getTitle(); + } + @Override public String getPurpose() { return this.getLibrary().getPurpose(); @@ -69,6 +79,11 @@ public void setName(String name) { this.getLibrary().setName(name); } + @Override + public void setTitle(String title) { + this.getLibrary().setTitle(title); + } + @Override public String getUrl() { return this.getLibrary().getUrl(); diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/PlanDefinitionAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/PlanDefinitionAdapter.java index 11772d385..832731b8d 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/PlanDefinitionAdapter.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/PlanDefinitionAdapter.java @@ -78,6 +78,16 @@ public String getName() { return this.getPlanDefinition().getName(); } + @Override + public boolean hasTitle() { + return this.getPlanDefinition().hasTitle(); + } + + @Override + public String getTitle() { + return this.getPlanDefinition().getTitle(); + } + @Override public String getPurpose() { return this.getPlanDefinition().getPurpose(); @@ -88,6 +98,11 @@ public void setName(String name) { this.getPlanDefinition().setName(name); } + @Override + public void setTitle(String title) { + this.getPlanDefinition().setTitle(title); + } + @Override public String getUrl() { return this.getPlanDefinition().getUrl(); diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/ValueSetAdapter.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/ValueSetAdapter.java index 06bc4e55a..c3f17be36 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/ValueSetAdapter.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/adapter/r5/ValueSetAdapter.java @@ -62,6 +62,16 @@ public String getName() { return this.getValueSet().getName(); } + @Override + public boolean hasTitle() { + return this.getValueSet().hasTitle(); + } + + @Override + public String getTitle() { + return this.getValueSet().getTitle(); + } + @Override public String getPurpose() { return this.getValueSet().getPurpose(); @@ -72,6 +82,11 @@ public void setName(String name) { this.getValueSet().setName(name); } + @Override + public void setTitle(String title) { + this.getValueSet().setTitle(title); + } + @Override public boolean hasUrl() { return this.getValueSet().hasUrl(); diff --git a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/KnowledgeArtifactReleaseVisitor.java b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/KnowledgeArtifactReleaseVisitor.java index a33820255..7fb3ba1af 100644 --- a/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/KnowledgeArtifactReleaseVisitor.java +++ b/cqf-fhir-utility/src/main/java/org/opencds/cqf/fhir/utility/visitor/KnowledgeArtifactReleaseVisitor.java @@ -98,63 +98,9 @@ public IBase visit(LibraryAdapter rootLibraryAdapter, Repository repository, IBa rootLibraryAdapter.getRelatedArtifact().removeIf(ra -> KnowledgeArtifactAdapter.getRelatedArtifactType(ra) .equalsIgnoreCase("depends-on")); - var transactionBundle = BundleHelper.newBundle(fhirVersion, null, "transaction"); - for (var artifact : releasedResources) { - var entry = PackageHelper.createEntry(artifact, true); - BundleHelper.addEntry(transactionBundle, entry); - var artifactAdapter = AdapterFactory.forFhirVersion(fhirVersion).createKnowledgeArtifactAdapter(artifact); - var components = artifactAdapter.getComponents(); - // add all root artifact components and child artifact components recursively as root artifact dependencies - for (var component : components) { - IDomainResource resource; - var relatedArtifactReference = KnowledgeArtifactAdapter.getRelatedArtifactReference(component); - // if the relatedArtifact is Owned, need to update the reference to the new Version - if (KnowledgeArtifactAdapter.checkIfRelatedArtifactIsOwned(component)) { - resource = checkIfReferenceInList(relatedArtifactReference, releasedResources) - // should never happen since we check all references as part of `internalRelease` - .orElseThrow(() -> - new InternalErrorException("Owned resource reference not found during release")); - var adapter = AdapterFactory.forFhirVersion(resource.getStructureFhirVersionEnum()) - .createKnowledgeArtifactAdapter(resource); - var reference = String.format("%s|%s", adapter.getUrl(), adapter.getVersion()); - KnowledgeArtifactAdapter.setRelatedArtifactReference(component, reference); - } else if (Canonicals.getVersion(relatedArtifactReference) == null - || Canonicals.getVersion(relatedArtifactReference).isEmpty()) { - // if the not Owned component doesn't have a version, try to find the latest version - String updatedReference = tryUpdateReferenceToLatestActiveVersion( - relatedArtifactReference, repository, artifactAdapter.getUrl()); - KnowledgeArtifactAdapter.setRelatedArtifactReference(component, updatedReference); - } - var componentToDependency = KnowledgeArtifactAdapter.newRelatedArtifact( - fhirVersion, "depends-on", KnowledgeArtifactAdapter.getRelatedArtifactReference(component)); - rootLibraryAdapter.getRelatedArtifact().add(componentToDependency); - } + // Report all dependencies, resolving unversioned dependencies to the latest known version, recursively + gatherDependencies(rootLibraryAdapter, rootLibraryAdapter, releasedResources, fhirVersion, repository); - var dependencies = artifactAdapter.getDependencies(); - for (var dependency : dependencies) { - // if the dependency gets updated as part of $release then update the reference as well - var maybeReference = checkIfReferenceInList(dependency, releasedResources); - if (maybeReference.isPresent()) { - var adapter = AdapterFactory.forFhirVersion(fhirVersion) - .createKnowledgeArtifactAdapter(maybeReference.get()); - String updatedReference = adapter.hasVersion() - ? String.format("%s|%s", adapter.getUrl(), adapter.getVersion()) - : adapter.getUrl(); - dependency.setReference(updatedReference); - } else if (StringUtils.isBlank(Canonicals.getVersion(dependency.getReference()))) { - // TODO: update when we support expansionParameters and requireVersionedDependencies - String updatedReference = tryUpdateReferenceToLatestActiveVersion( - dependency.getReference(), repository, artifactAdapter.getUrl()); - dependency.setReference(updatedReference); - } - // only add the dependency to the manifest if it is from a leaf artifact - if (!artifactAdapter.getUrl().equals(rootLibraryAdapter.getUrl())) { - var newDep = KnowledgeArtifactAdapter.newRelatedArtifact( - fhirVersion, "depends-on", dependency.getReference()); - rootLibraryAdapter.getRelatedArtifact().add(newDep); - } - } - } // removed duplicates and add var relatedArtifacts = rootLibraryAdapter.getRelatedArtifact(); var distinctResolvedRelatedArtifacts = new ArrayList<>(relatedArtifacts); @@ -186,13 +132,22 @@ public IBase visit(LibraryAdapter rootLibraryAdapter, Repository repository, IBa }); } } + + // Add all updated resources to a transaction bundle for the result + var transactionBundle = BundleHelper.newBundle(fhirVersion, null, "transaction"); + for (var artifact : releasedResources) { + var entry = PackageHelper.createEntry(artifact, true); + BundleHelper.addEntry(transactionBundle, entry); + } + // update ArtifactComments referencing the old Canonical Reference findArtifactCommentsToUpdate(rootLibrary, releaseVersion, repository).forEach(entry -> { BundleHelper.addEntry(transactionBundle, entry); }); rootLibraryAdapter.setRelatedArtifact(distinctResolvedRelatedArtifacts); - return repository.transaction(transactionBundle); + return transactionBundle; + //return repository.transaction(transactionBundle); } private List internalRelease( @@ -259,6 +214,76 @@ private List internalRelease( return resourcesToUpdate; } + private void gatherDependencies(LibraryAdapter rootLibraryAdapter, KnowledgeArtifactAdapter artifactAdapter, List releasedResources, FhirVersionEnum fhirVersion, Repository repository) { + for (var component : rootLibraryAdapter.getComponents()) { + IDomainResource resource; + var relatedArtifactReference = KnowledgeArtifactAdapter.getRelatedArtifactReference(component); + // if the relatedArtifact is Owned, need to update the reference to the new Version + if (KnowledgeArtifactAdapter.checkIfRelatedArtifactIsOwned(component)) { + resource = checkIfReferenceInList(relatedArtifactReference, releasedResources) + // should never happen since we check all references as part of `internalRelease` + .orElseThrow(() -> + new InternalErrorException("Owned resource reference not found during release")); + var adapter = AdapterFactory.forFhirVersion(resource.getStructureFhirVersionEnum()) + .createKnowledgeArtifactAdapter(resource); + var reference = String.format("%s|%s", adapter.getUrl(), adapter.getVersion()); + KnowledgeArtifactAdapter.setRelatedArtifactReference(component, reference, adapter.getDescriptor()); + } else if (Canonicals.getVersion(relatedArtifactReference) == null + || Canonicals.getVersion(relatedArtifactReference).isEmpty()) { + // if the not Owned component doesn't have a version, try to find the latest version + var adapter = tryGetLatestActiveVersion(relatedArtifactReference, repository); + if (adapter != null) { + relatedArtifactReference = addVersionToReference(relatedArtifactReference, adapter); + } + KnowledgeArtifactAdapter.setRelatedArtifactReference(component, relatedArtifactReference, adapter.getDescriptor()); + } + var componentToDependency = KnowledgeArtifactAdapter.newRelatedArtifact( + fhirVersion, "depends-on", KnowledgeArtifactAdapter.getRelatedArtifactReference(component), null); + rootLibraryAdapter.getRelatedArtifact().add(componentToDependency); + } + + var dependencies = artifactAdapter.getDependencies(); + for (var dependency : dependencies) { + // if the dependency gets updated as part of $release then update the reference as well + var maybeReference = checkIfReferenceInList(dependency, releasedResources); + if (maybeReference.isPresent()) { + var adapter = AdapterFactory.forFhirVersion(fhirVersion) + .createKnowledgeArtifactAdapter(maybeReference.get()); + String updatedReference = adapter.hasVersion() + ? String.format("%s|%s", adapter.getUrl(), adapter.getVersion()) + : adapter.getUrl(); + dependency.setReference(updatedReference); + + // Recurse into the dependency + gatherDependencies(rootLibraryAdapter, adapter, releasedResources, fhirVersion, repository); + } + else if (StringUtils.isBlank(Canonicals.getVersion(dependency.getReference()))) { + // TODO: update when we support expansionParameters and requireVersionedDependencies + var adapter = tryGetLatestActiveVersion(dependency.getReference(), repository); + if (adapter != null) { + String versionedReference = addVersionToReference(dependency.getReference(), adapter); + dependency.setReference(versionedReference); + + // Recurse into the dependency + gatherDependencies(rootLibraryAdapter, adapter, releasedResources, fhirVersion, repository); + } + } + else { + // This is a versioned reference, get the dependency and trace it + var adapter = getArtifactByCanonical(dependency.getReference(), repository); + if (adapter != null) { + gatherDependencies(rootLibraryAdapter, adapter, releasedResources, fhirVersion, repository); + } + } + // only add the dependency to the manifest if it is from a leaf artifact + if (!artifactAdapter.getUrl().equals(rootLibraryAdapter.getUrl())) { + var newDep = KnowledgeArtifactAdapter.newRelatedArtifact( + fhirVersion, "depends-on", dependency.getReference(), null); + rootLibraryAdapter.getRelatedArtifact().add(newDep); + } + } + } + private void checkNonExperimental( IDomainResource resource, Optional experimentalBehavior, Repository repository) throws UnprocessableEntityException { @@ -308,30 +333,69 @@ private void propagageEffectivePeriod( } } - private String tryUpdateReferenceToLatestActiveVersion( - String inputReference, Repository repository, String sourceArtifactUrl) throws ResourceNotFoundException { + private KnowledgeArtifactAdapter getArtifactByCanonical(String inputReference, Repository repository) { + List matchingResources = getArtifactsByCanonical(inputReference, repository); + if (matchingResources.isEmpty()) { + return null; + } + else if (matchingResources.size() == 1) { + return matchingResources.get(0); + } + else { + // TODO: Log that multiple resources matched by url and version... + return matchingResources.get(0); + } + } + + private List getArtifactsByCanonical(String inputReference, Repository repository) { List matchingResources = VisitorHelper.getMetadataResourcesFromBundle( - SearchHelper.searchRepositoryByCanonicalWithPaging(repository, inputReference)) - .stream() - .map(r -> AdapterFactory.forFhirVersion(r.getStructureFhirVersionEnum()) - .createKnowledgeArtifactAdapter((IDomainResource) r)) - .filter(a -> a.getStatus().equals("active")) - .collect(Collectors.toList()); + SearchHelper.searchRepositoryByCanonicalWithPaging(repository, inputReference)) + .stream() + .map(r -> AdapterFactory.forFhirVersion(r.getStructureFhirVersionEnum()) + .createKnowledgeArtifactAdapter((IDomainResource) r)) + .filter(a -> a.getStatus().equals("active")) + .collect(Collectors.toList()); + return matchingResources; + } + private KnowledgeArtifactAdapter tryGetLatestActiveVersion(String inputReference, Repository repository) { + List matchingResources = getArtifactsByCanonical(inputReference, repository); if (matchingResources.isEmpty()) { - return inputReference; + return null; } else { // TODO: Log which version was selected matchingResources.sort( - comparing(r -> ((KnowledgeArtifactAdapter) r).getVersion()).reversed()); + comparing(r -> ((KnowledgeArtifactAdapter) r).getVersion()).reversed()); var latestActiveVersion = matchingResources.get(0); + return latestActiveVersion; + } + } + + private String tryUpdateReferenceToLatestActiveVersion( + String inputReference, Repository repository, String sourceArtifactUrl) throws ResourceNotFoundException { + KnowledgeArtifactAdapter latestActiveVersion = tryGetLatestActiveVersion(inputReference, repository); + if (latestActiveVersion == null) { + return inputReference; + } + else { String latestActiveReference = latestActiveVersion.hasVersion() - ? String.format("%s|%s", latestActiveVersion.getUrl(), latestActiveVersion.getVersion()) - : latestActiveVersion.getUrl(); + ? String.format("%s|%s", latestActiveVersion.getUrl(), latestActiveVersion.getVersion()) + : latestActiveVersion.getUrl(); return latestActiveReference; } } + private String addVersionToReference(String inputReference, KnowledgeArtifactAdapter adapter) { + if (adapter != null) { + String versionedReference = adapter.hasVersion() + ? String.format("%s|%s", adapter.getUrl(), adapter.getVersion()) + : adapter.getUrl(); + return versionedReference; + } + + return inputReference; + } + private Optional getReleaseVersion( String version, Optional versionBehavior, String existingVersion, FhirVersionEnum fhirVersion) throws UnprocessableEntityException { diff --git a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r4/KnowledgeArtifactReleaseVisitorTests.java b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r4/KnowledgeArtifactReleaseVisitorTests.java index 888d7c7db..a0ce8d43d 100644 --- a/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r4/KnowledgeArtifactReleaseVisitorTests.java +++ b/cqf-fhir-utility/src/test/java/org/opencds/cqf/fhir/utility/visitor/r4/KnowledgeArtifactReleaseVisitorTests.java @@ -17,6 +17,7 @@ import ch.qos.logback.classic.Level; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.Appender; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; @@ -30,6 +31,7 @@ import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent; import org.hl7.fhir.r4.model.CodeType; +import org.hl7.fhir.r4.model.DateType; import org.hl7.fhir.r4.model.Extension; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Library; @@ -49,6 +51,12 @@ import org.opencds.cqf.fhir.utility.adapter.r4.AdapterFactory; import org.opencds.cqf.fhir.utility.r4.MetadataResourceHelper; import org.opencds.cqf.fhir.utility.repository.InMemoryFhirRepository; +import org.opencds.cqf.fhir.utility.repository.ig.EncodingBehavior; +import org.opencds.cqf.fhir.utility.repository.ig.IgConventions; +import org.opencds.cqf.fhir.utility.repository.ig.IgConventions.CategoryLayout; +import org.opencds.cqf.fhir.utility.repository.ig.IgConventions.FhirTypeLayout; +import org.opencds.cqf.fhir.utility.repository.ig.IgConventions.FilenameMode; +import org.opencds.cqf.fhir.utility.repository.ig.IgRepository; import org.opencds.cqf.fhir.utility.visitor.KnowledgeArtifactReleaseVisitor; import org.slf4j.LoggerFactory; @@ -92,6 +100,28 @@ public Bundle answer(InvocationOnMock a) throws Throwable { .transaction(any()); } + @Test + void visitMeasureCollectionTest() { + //IgConventions conventions = new IgConventions(FhirTypeLayout.DIRECTORY_PER_TYPE, CategoryLayout.DIRECTORY_PER_CATEGORY, FilenameMode.ID_ONLY); + //Repository repository = new IgRepository(fhirContext, Path.of("C:\\Users\\Bryn\\Documents\\Src\\CQF\\ecqm-content-qicore-2024-subset"), conventions, EncodingBehavior.DEFAULT, null); + // TODO: This IG repository implementation is built based on the "input" directory being the root, but the parent is usually considered the root... + Repository repository = new IgRepository(fhirContext, Path.of("C:\\Users\\Bryn\\Documents\\Src\\CQF\\ecqm-content-qicore-2024-subset\\input")); + Library library = repository.read(Library.class, new IdType("Library/Manifest-Final-Draft")); + LibraryAdapter libraryAdapter = new AdapterFactory().createLibrary(library); + Parameters params = new Parameters(); + params.addParameter("version", "1.0.0"); + params.addParameter("versionBehavior", new CodeType("default")); + + KnowledgeArtifactReleaseVisitor releaseVisitor = new KnowledgeArtifactReleaseVisitor(); + // Approval date is required to release an artifact + library.setApprovalDateElement(new DateType("2024-04-23")); + // Set the ID to Manifest-Release + Bundle returnResource = (Bundle)libraryAdapter.accept(releaseVisitor, repository, params); + library.setId(new IdType("Library/Manifest-Release")); + repository.create(library); + assertNotNull(returnResource); + } + @Test void visitLibraryTest() { Bundle bundle = (Bundle) jsonParser.parseResource(