Skip to content

Commit

Permalink
APHL-1023-withdraw_draft_programs (#530)
Browse files Browse the repository at this point in the history
* initial withdraw implementation

* add test for non-draft library + only depends-on cascade delete

* first check request resource id to work with

* use specific withdraw resource file

* return bundle and delete on ecr

* fix in memory fhir repo

* run code formatting

* add InMemoryRepository tests

* reformat code

* add getEntryRequestId & isEntryRequestDelete tests

* run code check

* throw error if delete request entry is not present

* artifacts that is owned and has composed-of relationship

* run code check

* update Exception thrown in in memory repo

* run code check

* add unsupported fhir version tests to bundle helper

* rename getComponents method name

* add approval removal on withdraw

* add customSearchParams and fix runtime search param property

---------

Co-authored-by: Ivan Baisi <[email protected]>
  • Loading branch information
ibaisi and ibaisismile authored Sep 24, 2024
1 parent 6100130 commit 3b34066
Show file tree
Hide file tree
Showing 17 changed files with 12,627 additions and 59 deletions.
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
package org.opencds.cqf.fhir.utility;

import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.context.RuntimeSearchParam.RuntimeSearchParamStatusEnum;
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.hl7.fhir.dstu3.model.Bundle;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.dstu3.model.SearchParameter;
import org.hl7.fhir.dstu3.model.StringType;
import org.hl7.fhir.instance.model.api.IBaseBackboneElement;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.Bundle.BundleEntryRequestComponent;
import org.hl7.fhir.r5.model.PrimitiveType;

public class BundleHelper {
private BundleHelper() {}
Expand Down Expand Up @@ -640,4 +647,57 @@ public static IBaseBackboneElement setEntryRequest(
String.format("Unsupported version of FHIR: %s", fhirVersion.getFhirVersionString()));
}
}

public static RuntimeSearchParam resourceToRuntimeSearchParam(IBaseResource resource) {
var fhirVersion = resource.getStructureFhirVersionEnum();
switch (fhirVersion) {
case DSTU3:
var res = (SearchParameter) resource;
return new RuntimeSearchParam(
res.getIdElement(),
res.getUrl(),
res.getCode(),
res.getDescription(),
res.getExpression(),
RestSearchParameterTypeEnum.REFERENCE,
null,
res.getTarget().stream().map(StringType::toString).collect(Collectors.toSet()),
RuntimeSearchParamStatusEnum.ACTIVE,
res.getBase().stream().map(StringType::toString).collect(Collectors.toList()));
case R4:
var resR4 = (org.hl7.fhir.r4.model.SearchParameter) resource;
return new RuntimeSearchParam(
resR4.getIdElement(),
resR4.getUrl(),
resR4.getCode(),
resR4.getDescription(),
resR4.getExpression(),
RestSearchParameterTypeEnum.REFERENCE,
null,
resR4.getTarget().stream()
.map(org.hl7.fhir.r4.model.StringType::toString)
.collect(Collectors.toSet()),
RuntimeSearchParamStatusEnum.ACTIVE,
resR4.getBase().stream()
.map(org.hl7.fhir.r4.model.StringType::toString)
.collect(Collectors.toList()));
case R5:
var resR5 = (org.hl7.fhir.r5.model.SearchParameter) resource;
return new RuntimeSearchParam(
resR5.getIdElement(),
resR5.getUrl(),
resR5.getCode(),
resR5.getDescription(),
resR5.getExpression(),
RestSearchParameterTypeEnum.REFERENCE,
null,
resR5.getTarget().stream().map(PrimitiveType::toString).collect(Collectors.toSet()),
RuntimeSearchParamStatusEnum.ACTIVE,
resR5.getBase().stream().map(PrimitiveType::toString).collect(Collectors.toList()));

default:
throw new IllegalArgumentException(
String.format("Unsupported version of FHIR: %s", fhirVersion.getFhirVersionString()));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.opencds.cqf.fhir.api.Repository;
import org.opencds.cqf.fhir.utility.BundleHelper;
import org.opencds.cqf.fhir.utility.visitor.KnowledgeArtifactVisitor;
import org.opencds.cqf.fhir.utility.visitor.IKnowledgeArtifactVisitor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -293,7 +293,8 @@ default List<IDependencyInfo> combineComponentsAndDependencies() {
.collect(Collectors.toList());
}

default IBase accept(KnowledgeArtifactVisitor visitor, Repository repository, IBaseParameters operationParameters) {
default IBase accept(
IKnowledgeArtifactVisitor visitor, Repository repository, IBaseParameters operationParameters) {
return visitor.visit(this, repository, operationParameters);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.opencds.cqf.fhir.utility.matcher;

import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.fhirpath.IFhirPath;
import ca.uhn.fhir.fhirpath.IFhirPath.IParsedExpression;
import ca.uhn.fhir.model.api.IQueryParameterType;
Expand All @@ -12,6 +13,7 @@
import ca.uhn.fhir.rest.param.TokenParamModifier;
import ca.uhn.fhir.rest.param.UriParam;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.NotImplementedException;
Expand Down Expand Up @@ -69,12 +71,25 @@ public boolean equals(Object obj) {

public Map<SPPathKey, IParsedExpression> getPathCache();

public Map<String, RuntimeSearchParam> customSearchParams = new HashMap<>();

default void addCustomParameter(RuntimeSearchParam searchParam) {
this.customSearchParams.put(searchParam.getName(), searchParam);
}

default Map<String, RuntimeSearchParam> getCustomParameters() {
return this.customSearchParams;
}

// The list here is an OR list. Meaning, if any element matches it's a match
default boolean matches(String name, List<IQueryParameterType> params, IBaseResource resource) {
boolean match = true;

var context = getContext();
var s = context.getResourceDefinition(resource).getSearchParam(name);
if (s == null) {
s = this.getCustomParameters().get(name);
}
if (s == null) {
throw new RuntimeException(String.format(
"The SearchParameter %s for Resource %s is not supported.", name, resource.fhirType()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,12 @@ public <T extends IBaseResource> MethodOutcome update(T resource, Map<String, St
if (!resources.containsKey(theId)) {
outcome.setCreated(true);
}
if (resource.fhirType().equals("SearchParameter")) {
var resourceMatcher = Repositories.getResourceMatcher(this.context);
resourceMatcher.addCustomParameter(BundleHelper.resourceToRuntimeSearchParam(resource));
}
resources.put(theId, resource);

return outcome;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package org.opencds.cqf.fhir.utility.visitor;

import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import java.util.List;
import java.util.stream.Collectors;
import org.hl7.fhir.instance.model.api.IBaseBackboneElement;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.opencds.cqf.fhir.api.Repository;

public abstract class AbstractKnowledgeArtifactVisitor implements IKnowledgeArtifactVisitor {
protected List<IBaseBackboneElement> findArtifactCommentsToUpdate(
IBaseResource artifact, String releaseVersion, Repository repository) {
if (artifact instanceof org.hl7.fhir.dstu3.model.MetadataResource) {
return org.opencds.cqf.fhir.utility.visitor.dstu3.ReleaseVisitor.findArtifactCommentsToUpdate(
(org.hl7.fhir.dstu3.model.MetadataResource) artifact, releaseVersion, repository)
.stream()
.map(r -> (IBaseBackboneElement) r)
.collect(Collectors.toList());
} else if (artifact instanceof org.hl7.fhir.r4.model.MetadataResource) {
return org.opencds.cqf.fhir.utility.visitor.r4.ReleaseVisitor.findArtifactCommentsToUpdate(
(org.hl7.fhir.r4.model.MetadataResource) artifact, releaseVersion, repository)
.stream()
.map(r -> (IBaseBackboneElement) r)
.collect(Collectors.toList());
} else if (artifact instanceof org.hl7.fhir.r5.model.MetadataResource) {
return org.opencds.cqf.fhir.utility.visitor.r5.ReleaseVisitor.findArtifactCommentsToUpdate(
(org.hl7.fhir.r5.model.MetadataResource) artifact, releaseVersion, repository)
.stream()
.map(r -> (IBaseBackboneElement) r)
.collect(Collectors.toList());
} else {
throw new UnprocessableEntityException("Version not supported");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import org.opencds.cqf.fhir.utility.PackageHelper;
import org.opencds.cqf.fhir.utility.adapter.KnowledgeArtifactAdapter;

public class ApproveVisitor implements KnowledgeArtifactVisitor {
public class ApproveVisitor implements IKnowledgeArtifactVisitor {
@Override
public IBase visit(KnowledgeArtifactAdapter adapter, Repository repository, IBaseParameters approveParameters) {
Date currentDate = new Date();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
import org.opencds.cqf.fhir.utility.adapter.KnowledgeArtifactAdapter;
import org.opencds.cqf.fhir.utility.r4.PackageHelper;

public class DraftVisitor implements KnowledgeArtifactVisitor {
public class DraftVisitor implements IKnowledgeArtifactVisitor {
@Override
public IBase visit(KnowledgeArtifactAdapter adapter, Repository repository, IBaseParameters draftParameters) {
var fhirVersion = adapter.get().getStructureFhirVersionEnum();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
import org.opencds.cqf.fhir.api.Repository;
import org.opencds.cqf.fhir.utility.adapter.KnowledgeArtifactAdapter;

public interface KnowledgeArtifactVisitor {
public interface IKnowledgeArtifactVisitor {
IBase visit(KnowledgeArtifactAdapter knowledgeArtifact, Repository repository, IBaseParameters draftParameters);
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
import org.opencds.cqf.fhir.utility.adapter.ValueSetAdapter;
import org.opencds.cqf.fhir.utility.client.TerminologyServerClient;

public class PackageVisitor implements KnowledgeArtifactVisitor {
public class PackageVisitor implements IKnowledgeArtifactVisitor {
protected final FhirContext fhirContext;
protected final TerminologyServerClient terminologyServerClient;
protected final ExpandHelper expandHelper;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseBackboneElement;
import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.ICompositeType;
Expand All @@ -35,7 +34,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ReleaseVisitor implements KnowledgeArtifactVisitor {
public class ReleaseVisitor extends AbstractKnowledgeArtifactVisitor {
private Logger log = LoggerFactory.getLogger(ReleaseVisitor.class);

@Override
Expand Down Expand Up @@ -527,31 +526,6 @@ private void checkReleasePreconditions(KnowledgeArtifactAdapter artifact, Date a
}
}

private List<IBaseBackboneElement> findArtifactCommentsToUpdate(
IBaseResource artifact, String releaseVersion, Repository repository) {
if (artifact instanceof org.hl7.fhir.dstu3.model.MetadataResource) {
return org.opencds.cqf.fhir.utility.visitor.dstu3.ReleaseVisitor.findArtifactCommentsToUpdate(
(org.hl7.fhir.dstu3.model.MetadataResource) artifact, releaseVersion, repository)
.stream()
.map(r -> (IBaseBackboneElement) r)
.collect(Collectors.toList());
} else if (artifact instanceof org.hl7.fhir.r4.model.MetadataResource) {
return org.opencds.cqf.fhir.utility.visitor.r4.ReleaseVisitor.findArtifactCommentsToUpdate(
(org.hl7.fhir.r4.model.MetadataResource) artifact, releaseVersion, repository)
.stream()
.map(r -> (IBaseBackboneElement) r)
.collect(Collectors.toList());
} else if (artifact instanceof org.hl7.fhir.r5.model.MetadataResource) {
return org.opencds.cqf.fhir.utility.visitor.r5.ReleaseVisitor.findArtifactCommentsToUpdate(
(org.hl7.fhir.r5.model.MetadataResource) artifact, releaseVersion, repository)
.stream()
.map(r -> (IBaseBackboneElement) r)
.collect(Collectors.toList());
} else {
throw new UnprocessableEntityException("Version not supported");
}
}

private void checkReleaseVersion(String version, Optional<String> versionBehavior)
throws UnprocessableEntityException {
if (!versionBehavior.isPresent()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class WithdrawVisitor implements KnowledgeArtifactVisitor {
public class WithdrawVisitor extends AbstractKnowledgeArtifactVisitor {
private Logger log = LoggerFactory.getLogger(WithdrawVisitor.class);
String isOwnedUrl = "http://hl7.org/fhir/StructureDefinition/crmi-isOwned";

Expand All @@ -25,14 +25,21 @@ public IBase visit(
if (!rootAdapter.getStatus().equals("draft")) {
throw new PreconditionFailedException("Cannot withdraw an artifact that is not in draft status");
}
var fhirVersion = rootAdapter.get().getStructureFhirVersionEnum();
var transactionBundle = BundleHelper.newBundle(fhirVersion, null, "transaction");

var resToUpdate = new ArrayList<IDomainResource>();
resToUpdate.add(rootAdapter.get());

var resourcesToUpdate = getComponents(rootAdapter, repository, resToUpdate);
findArtifactCommentsToUpdate(rootAdapter.get(), fhirVersion.getFhirVersionString(), repository)
.forEach(artifact -> {
var resource = BundleHelper.getEntryResource(fhirVersion, artifact);
var entry = PackageHelper.deleteEntry(resource);
BundleHelper.addEntry(transactionBundle, entry);
});

var fhirVersion = rootAdapter.get().getStructureFhirVersionEnum();
var resourcesToUpdate = getComponents(rootAdapter, repository, resToUpdate);

var transactionBundle = BundleHelper.newBundle(fhirVersion, null, "transaction");
for (var artifact : resourcesToUpdate) {
var entry = PackageHelper.deleteEntry(artifact);
BundleHelper.addEntry(transactionBundle, entry);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public static void updateReleaseLabel(MetadataResource artifact, String releaseL
public static Bundle searchArtifactAssessmentForArtifact(IIdType reference, Repository repository) {
Map<String, List<IQueryParameterType>> searchParams = new HashMap<>();
List<IQueryParameterType> urlList = new ArrayList<>();
urlList.add(new ReferenceParam(reference));
urlList.add(new ReferenceParam(reference.getResourceType() + "/" + reference.getIdPart()));
searchParams.put("artifact", urlList);
Bundle searchResultsBundle = repository.search(Bundle.class, Basic.class, searchParams);
return searchResultsBundle;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
import org.opencds.cqf.fhir.utility.dstu3.MetadataResourceHelper;
import org.opencds.cqf.fhir.utility.repository.InMemoryFhirRepository;
import org.opencds.cqf.fhir.utility.visitor.DraftVisitor;
import org.opencds.cqf.fhir.utility.visitor.KnowledgeArtifactVisitor;
import org.opencds.cqf.fhir.utility.visitor.IKnowledgeArtifactVisitor;

class DraftVisitorTests {
private final FhirContext fhirContext = FhirContext.forDstu3Cached();
Expand Down Expand Up @@ -86,7 +86,7 @@ void library_draft_test() {
Bundle bundle = (Bundle)
jsonParser.parseResource(DraftVisitorTests.class.getResourceAsStream("Bundle-ersd-example.json"));
spyRepository.transaction(bundle);
KnowledgeArtifactVisitor draftVisitor = new DraftVisitor();
IKnowledgeArtifactVisitor draftVisitor = new DraftVisitor();
Library library = spyRepository
.read(Library.class, new IdType("Library/SpecificationLibrary"))
.copy();
Expand Down Expand Up @@ -149,7 +149,7 @@ void draftOperation_no_effectivePeriod_test() {
.copy();
assertTrue(baseLib.hasEffectivePeriod());
LibraryAdapter libraryAdapter = new AdapterFactory().createLibrary(baseLib);
KnowledgeArtifactVisitor draftVisitor = new DraftVisitor();
IKnowledgeArtifactVisitor draftVisitor = new DraftVisitor();
PlanDefinition planDef = spyRepository
.read(PlanDefinition.class, new IdType("PlanDefinition/plandefinition-ersd-instance-example"))
.copy();
Expand Down Expand Up @@ -182,7 +182,7 @@ void draftOperation_version_conflict_test() {
.read(Library.class, new IdType(specificationLibReference))
.copy();
LibraryAdapter libraryAdapter = new AdapterFactory().createLibrary(baseLib);
KnowledgeArtifactVisitor draftVisitor = new DraftVisitor();
IKnowledgeArtifactVisitor draftVisitor = new DraftVisitor();

try {
libraryAdapter.accept(draftVisitor, spyRepository, params);
Expand All @@ -205,7 +205,7 @@ void draftOperation_cannot_create_draft_of_draft_test() {
.read(Library.class, new IdType("Library/SpecificationLibraryDraftVersion-1-0-0"))
.copy();
LibraryAdapter libraryAdapter = new AdapterFactory().createLibrary(baseLib);
KnowledgeArtifactVisitor draftVisitor = new DraftVisitor();
IKnowledgeArtifactVisitor draftVisitor = new DraftVisitor();
try {
libraryAdapter.accept(draftVisitor, spyRepository, params);
} catch (PreconditionFailedException e) {
Expand All @@ -224,7 +224,7 @@ void draftOperation_version_format_test() {
.read(Library.class, new IdType("Library/SpecificationLibraryDraftVersion-1-0-0"))
.copy();
LibraryAdapter libraryAdapter = new AdapterFactory().createLibrary(baseLib);
KnowledgeArtifactVisitor draftVisitor = new DraftVisitor();
IKnowledgeArtifactVisitor draftVisitor = new DraftVisitor();

for (String version : badVersionList) {
UnprocessableEntityException maybeException = null;
Expand Down
Loading

0 comments on commit 3b34066

Please sign in to comment.