Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

APHL-1023-withdraw_draft_programs #511

Merged
merged 19 commits into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.hl7.fhir.dstu3.model.Bundle;
import org.hl7.fhir.dstu3.model.IdType;
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;

public class BundleHelper {
private BundleHelper() {}
Expand Down Expand Up @@ -185,6 +189,37 @@ public static boolean isEntryRequestPost(FhirVersionEnum fhirVersion, IBaseBackb
}
}

/**
* Checks if an entry has a request type of DELETE
*
* @param fhirVersion FhirVersionEnum
* @param entry IBaseBackboneElement type
* @return
*/
public static boolean isEntryRequestDelete(FhirVersionEnum fhirVersion, IBaseBackboneElement entry) {
switch (fhirVersion) {
case DSTU3:
return Optional.ofNullable(((org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent) entry).getRequest())
.map(Bundle.BundleEntryRequestComponent::getMethod)
.filter(r -> r == org.hl7.fhir.dstu3.model.Bundle.HTTPVerb.DELETE)
.isPresent();
case R4:
return Optional.ofNullable(((org.hl7.fhir.r4.model.Bundle.BundleEntryComponent) entry).getRequest())
.map(BundleEntryRequestComponent::getMethod)
.filter(r -> r == org.hl7.fhir.r4.model.Bundle.HTTPVerb.DELETE)
.isPresent();
case R5:
return Optional.ofNullable(((org.hl7.fhir.r5.model.Bundle.BundleEntryComponent) entry).getRequest())
.map(org.hl7.fhir.r5.model.Bundle.BundleEntryRequestComponent::getMethod)
.filter(r -> r == org.hl7.fhir.r5.model.Bundle.HTTPVerb.DELETE)
.isPresent();

default:
throw new IllegalArgumentException(
String.format("Unsupported version of FHIR: %s", fhirVersion.getFhirVersionString()));
}
}

/**
* Returns the list of entries from the Bundle
*
Expand All @@ -207,6 +242,40 @@ public static List<? extends IBaseBackboneElement> getEntry(IBaseBundle bundle)
}
}

/**
* Gets request id if present
*
* @param fhirVersion FhirVersionEnum
* @param entry IBaseBackboneElement type
* @return
*/
public static Optional<IIdType> getEntryRequestId(FhirVersionEnum fhirVersion, IBaseBackboneElement entry) {
switch (fhirVersion) {
case DSTU3:
return Optional.ofNullable(((org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent) entry)
.getRequest()
.getUrl())
.map(Canonicals::getIdPart)
.map(IdType::new);
case R4:
return Optional.ofNullable(((org.hl7.fhir.r4.model.Bundle.BundleEntryComponent) entry)
.getRequest()
.getUrl())
.map(Canonicals::getIdPart)
.map(org.hl7.fhir.r4.model.IdType::new);
case R5:
return Optional.ofNullable(((org.hl7.fhir.r5.model.Bundle.BundleEntryComponent) entry)
.getRequest()
.getUrl())
.map(Canonicals::getIdPart)
.map(org.hl7.fhir.r5.model.IdType::new);

default:
throw new IllegalArgumentException(
String.format("Unsupported version of FHIR: %s", fhirVersion.getFhirVersionString()));
}
}

/**
* Sets the list of entries of the Bundle
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,15 @@ public static IBaseBackboneElement createEntry(IBaseResource resource, boolean i
}
return entry;
}

public static IBaseBackboneElement deleteEntry(IBaseResource resource) {
final var fhirVersion = resource.getStructureFhirVersionEnum();
final var entry = BundleHelper.newEntryWithResource(resource.getStructureFhirVersionEnum(), resource);
var requestUrl = resource.fhirType() + "/" + resource.getIdElement().getIdPart();

final var request = BundleHelper.newRequest(fhirVersion, "DELETE", requestUrl);
BundleHelper.setEntryRequest(fhirVersion, entry, request);

return entry;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,15 @@ protected static Class<IBaseBundle> getBundleClass(Repository repository) {
repository.fhirContext().getResourceDefinition("Bundle").getImplementingClass();
}

/**
* Gets a resource class
*
* @param repository the repository to search
* @param resourceType String of the resource typeget
* @return
*/
@SuppressWarnings("unchecked")
protected static Class<IBaseResource> getResourceClass(Repository repository, String resourceType) {
public static Class<IBaseResource> getResourceClass(Repository repository, String resourceType) {
return (Class<IBaseResource>)
repository.fhirContext().getResourceDefinition(resourceType).getImplementingClass();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,12 @@
import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;
import org.opencds.cqf.fhir.api.Repository;
import org.opencds.cqf.fhir.utility.BundleHelper;
import org.opencds.cqf.fhir.utility.Canonicals;
import org.opencds.cqf.fhir.utility.Ids;
import org.opencds.cqf.fhir.utility.SearchHelper;

public class InMemoryFhirRepository implements Repository {

Expand Down Expand Up @@ -111,7 +114,7 @@ public <T extends IBaseResource> MethodOutcome update(T resource, Map<String, St
@Override
public <T extends IBaseResource, I extends IIdType> MethodOutcome delete(
Class<T> resourceType, I id, Map<String, String> headers) {
var outcome = new MethodOutcome();
var outcome = new MethodOutcome(id, false);
var resources = resourceMap.computeIfAbsent(id.getResourceType(), r -> new HashMap<>());
var keyId = id.toUnqualifiedVersionless();
if (resources.containsKey(keyId)) {
Expand Down Expand Up @@ -226,8 +229,21 @@ public static <B extends IBaseBundle> B transactionStub(B transaction, Repositor
returnBundle,
BundleHelper.newEntryWithResponse(
version, BundleHelper.newResponseWithLocation(version, location)));
} else if (BundleHelper.isEntryRequestDelete(version, e)) {
if (BundleHelper.getEntryRequestId(version, e).isPresent()) {
var resourceType = Canonicals.getResourceType(
((BundleEntryComponent) e).getRequest().getUrl());
var resourceClass = SearchHelper.getResourceClass(repository, resourceType);
var res = repository.delete(
resourceClass,
BundleHelper.getEntryRequestId(version, e).get().withResourceType(resourceType));
BundleHelper.addEntry(returnBundle, BundleHelper.newEntryWithResource(version, res.getResource()));
} else {
throw new ResourceNotFoundException("Trying to delete an entry without id");
}

} else {
throw new NotImplementedOperationException("Transaction stub only supports PUT or POST");
throw new NotImplementedOperationException("Transaction stub only supports PUT, POST or DELETE");
}
});
return returnBundle;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package org.opencds.cqf.fhir.utility.visitor;

import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.instance.model.api.IDomainResource;
import org.opencds.cqf.fhir.api.Repository;
import org.opencds.cqf.fhir.utility.BundleHelper;
import org.opencds.cqf.fhir.utility.PackageHelper;
import org.opencds.cqf.fhir.utility.adapter.KnowledgeArtifactAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

@Override
public IBase visit(
KnowledgeArtifactAdapter rootAdapter, Repository repository, IBaseParameters operationParameters) {
if (!rootAdapter.getStatus().equals("draft")) {
throw new PreconditionFailedException("Cannot withdraw an artifact that is not in draft status");
}
var resToUpdate = new ArrayList<IDomainResource>();
resToUpdate.add(rootAdapter.get());

var resourcesToUpdate = getComponents(rootAdapter, repository, resToUpdate);

var fhirVersion = rootAdapter.get().getStructureFhirVersionEnum();

var transactionBundle = BundleHelper.newBundle(fhirVersion, null, "transaction");
for (var artifact : resourcesToUpdate) {
var entry = PackageHelper.deleteEntry(artifact);
BundleHelper.addEntry(transactionBundle, entry);
}

return repository.transaction(transactionBundle);
}

private List<IDomainResource> getComponents(
KnowledgeArtifactAdapter adapter, Repository repository, ArrayList<IDomainResource> resourcesToUpdate) {
adapter.getRelatedArtifactsOfType("composed-of").stream().forEach(c -> {
final var preReleaseReference = KnowledgeArtifactAdapter.getRelatedArtifactReference(c);
Optional<KnowledgeArtifactAdapter> maybeArtifact =
VisitorHelper.tryGetLatestVersion(preReleaseReference, repository);
if (maybeArtifact.isPresent()) {
if (resourcesToUpdate.stream()
.filter(rtu ->
rtu.getId().equals(maybeArtifact.get().getId().toString())
&& (rtu.getExtension().stream()
.anyMatch(ext -> ext.getUrl().equals(isOwnedUrl))))
.collect(Collectors.toList())
.isEmpty()) {
resourcesToUpdate.add(maybeArtifact.get().get());
getComponents(maybeArtifact.get(), repository, resourcesToUpdate);
}
}
});

return resourcesToUpdate;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@

import ca.uhn.fhir.context.FhirVersionEnum;
import java.util.Collections;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.r4.model.Bundle.BundleEntryRequestComponent;
import org.hl7.fhir.r4.model.Bundle.HTTPVerb;
import org.hl7.fhir.r4.model.IdType;
import org.junit.jupiter.api.Test;

class BundleHelperTests {
Expand Down Expand Up @@ -106,4 +111,103 @@ void r5() {
assertEquals(resource, getEntryResource(fhirVersion, entry));
assertFalse(getEntryResources(bundle).isEmpty());
}

@Test
void isEntryRequestDeleteDstu3() {
org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent bundle =
new org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent()
.setRequest(new org.hl7.fhir.dstu3.model.Bundle.BundleEntryRequestComponent()
.setMethod(org.hl7.fhir.dstu3.model.Bundle.HTTPVerb.DELETE));
var res = BundleHelper.isEntryRequestDelete(FhirVersionEnum.DSTU3, bundle);

assertTrue(res);
}

@Test
void isEntryRequestDeleteR4() {
BundleEntryComponent bundle = new Bundle.BundleEntryComponent()
.setRequest(new BundleEntryRequestComponent().setMethod(HTTPVerb.DELETE));
var res = BundleHelper.isEntryRequestDelete(FhirVersionEnum.R4, bundle);

assertTrue(res);
}

@Test
void isEntryRequestDeleteR5() {
org.hl7.fhir.r5.model.Bundle.BundleEntryComponent bundle =
new org.hl7.fhir.r5.model.Bundle.BundleEntryComponent()
.setRequest(new org.hl7.fhir.r5.model.Bundle.BundleEntryRequestComponent()
.setMethod(org.hl7.fhir.r5.model.Bundle.HTTPVerb.DELETE));
var res = BundleHelper.isEntryRequestDelete(FhirVersionEnum.R5, bundle);

assertTrue(res);
}

@Test
void isEntryRequestDeleteUnsupported() {
try {
org.hl7.fhir.r5.model.Bundle.BundleEntryComponent bundle =
new org.hl7.fhir.r5.model.Bundle.BundleEntryComponent()
.setRequest(new org.hl7.fhir.r5.model.Bundle.BundleEntryRequestComponent()
.setMethod(org.hl7.fhir.r5.model.Bundle.HTTPVerb.DELETE));

BundleHelper.isEntryRequestDelete(FhirVersionEnum.DSTU2, bundle);
} catch (IllegalArgumentException e) {
assertTrue(e.getMessage().contains("Unsupported version of FHIR"));
}
}

@Test
void getEntryRequestIdDstu3() {
org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent bundle =
new org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent()
.setRequest(new org.hl7.fhir.dstu3.model.Bundle.BundleEntryRequestComponent()
.setMethod(org.hl7.fhir.dstu3.model.Bundle.HTTPVerb.GET));

bundle.getRequest().setUrl("Library/123");

var res = BundleHelper.getEntryRequestId(FhirVersionEnum.DSTU3, bundle);

assertEquals(new org.hl7.fhir.dstu3.model.IdType("123"), res.get());
}

@Test
void getEntryRequestIdR4() {
BundleEntryComponent bundle =
new Bundle.BundleEntryComponent().setRequest(new BundleEntryRequestComponent().setMethod(HTTPVerb.GET));

bundle.getRequest().setUrl("Library/123");

var res = BundleHelper.getEntryRequestId(FhirVersionEnum.R4, bundle);

assertEquals(new IdType("123"), res.get());
}

@Test
void getEntryRequestIdR5() {
org.hl7.fhir.r5.model.Bundle.BundleEntryComponent bundle =
new org.hl7.fhir.r5.model.Bundle.BundleEntryComponent()
.setRequest(new org.hl7.fhir.r5.model.Bundle.BundleEntryRequestComponent()
.setMethod(org.hl7.fhir.r5.model.Bundle.HTTPVerb.GET));

bundle.getRequest().setUrl("Library/123");

var res = BundleHelper.getEntryRequestId(FhirVersionEnum.R5, bundle);

assertEquals(new org.hl7.fhir.r5.model.IdType("123"), res.get());
}

@Test
void getEntryRequestIdUnsupported() {
try {
org.hl7.fhir.r5.model.Bundle.BundleEntryComponent bundle =
new org.hl7.fhir.r5.model.Bundle.BundleEntryComponent()
.setRequest(new org.hl7.fhir.r5.model.Bundle.BundleEntryRequestComponent()
.setMethod(org.hl7.fhir.r5.model.Bundle.HTTPVerb.DELETE));

BundleHelper.getEntryRequestId(FhirVersionEnum.DSTU2, bundle);
} catch (IllegalArgumentException e) {
assertTrue(e.getMessage().contains("Unsupported version of FHIR"));
}
}
}
Loading