Skip to content

Commit fe37dce

Browse files
authored
Include deletion of related Basic Resources in $delete operation (#967)
* Include deletion of related Basic Resources in $delete operation * Fix version handling & add versioned tests * Spotless
1 parent 08cf4dc commit fe37dce

7 files changed

Lines changed: 1072 additions & 2 deletions

File tree

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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,17 @@
22

33
import ca.uhn.fhir.repository.IRepository;
44
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
5+
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
56
import java.util.ArrayList;
7+
import java.util.List;
68
import org.hl7.fhir.instance.model.api.IBase;
79
import org.hl7.fhir.instance.model.api.IBaseParameters;
10+
import org.hl7.fhir.instance.model.api.IBaseResource;
811
import org.hl7.fhir.instance.model.api.IDomainResource;
912
import org.opencds.cqf.fhir.utility.BundleHelper;
1013
import org.opencds.cqf.fhir.utility.PackageHelper;
1114
import org.opencds.cqf.fhir.utility.adapter.IKnowledgeArtifactAdapter;
15+
import org.opencds.cqf.fhir.utility.search.Searches;
1216

1317
public class DeleteVisitor extends BaseKnowledgeArtifactVisitor {
1418

@@ -32,11 +36,38 @@ public IBase visit(IKnowledgeArtifactAdapter rootAdapter, IBaseParameters operat
3236

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

39+
var resourceReference = rootAdapter.get().getIdElement().getResourceType() + "/"
40+
+ rootAdapter.get().getIdElement().getIdPart();
41+
var approvals = retrieveApprovals(resourceReference);
42+
for (var approval : approvals) {
43+
resourcesToUpdate.add((IDomainResource) approval);
44+
}
45+
3546
for (var res : resourcesToUpdate) {
3647
var entry = PackageHelper.deleteEntry(res);
3748
BundleHelper.addEntry(transactionBundle, entry);
3849
}
3950

4051
return repository.transaction(transactionBundle);
4152
}
53+
54+
private List<IBaseResource> retrieveApprovals(String resourceReference) {
55+
var searchParams = Searches.builder()
56+
.withReferenceParam("artifact", resourceReference)
57+
.build();
58+
return switch (fhirVersion()) {
59+
case DSTU3 ->
60+
BundleHelper.getEntryResources(repository.search(
61+
org.hl7.fhir.dstu3.model.Bundle.class, org.hl7.fhir.dstu3.model.Basic.class, searchParams));
62+
case R4 ->
63+
BundleHelper.getEntryResources(repository.search(
64+
org.hl7.fhir.r4.model.Bundle.class, org.hl7.fhir.r4.model.Basic.class, searchParams));
65+
case R5 ->
66+
BundleHelper.getEntryResources(repository.search(
67+
org.hl7.fhir.r5.model.Bundle.class, org.hl7.fhir.r5.model.Basic.class, searchParams));
68+
default ->
69+
throw new UnprocessableEntityException("Unsupported version of FHIR: %s"
70+
.formatted(fhirVersion().getFhirVersionString()));
71+
};
72+
}
4273
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package org.opencds.cqf.fhir.cr.visitor.dstu3;
2+
3+
import static org.junit.jupiter.api.Assertions.*;
4+
import static org.opencds.cqf.fhir.utility.dstu3.Parameters.parameters;
5+
import static org.opencds.cqf.fhir.utility.dstu3.Parameters.part;
6+
7+
import ca.uhn.fhir.context.FhirContext;
8+
import ca.uhn.fhir.parser.IParser;
9+
import ca.uhn.fhir.repository.IRepository;
10+
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
11+
import org.hl7.fhir.dstu3.model.Bundle;
12+
import org.hl7.fhir.dstu3.model.IdType;
13+
import org.hl7.fhir.dstu3.model.Library;
14+
import org.hl7.fhir.dstu3.model.Parameters;
15+
import org.hl7.fhir.dstu3.model.SearchParameter;
16+
import org.junit.jupiter.api.BeforeEach;
17+
import org.junit.jupiter.api.Test;
18+
import org.opencds.cqf.fhir.cr.visitor.DeleteVisitor;
19+
import org.opencds.cqf.fhir.utility.BundleHelper;
20+
import org.opencds.cqf.fhir.utility.adapter.ILibraryAdapter;
21+
import org.opencds.cqf.fhir.utility.adapter.dstu3.AdapterFactory;
22+
import org.opencds.cqf.fhir.utility.repository.InMemoryFhirRepository;
23+
24+
class DeleteVisitorTests {
25+
26+
private final FhirContext fhirContext = FhirContext.forDstu3Cached();
27+
private IRepository repo;
28+
private final IParser jsonParser = fhirContext.newJsonParser();
29+
30+
@BeforeEach
31+
void setup() {
32+
SearchParameter sp = (SearchParameter) jsonParser.parseResource(
33+
ReleaseVisitorTests.class.getResourceAsStream("SearchParameter-artifactAssessment.json"));
34+
repo = new InMemoryFhirRepository(fhirContext);
35+
repo.update(sp);
36+
}
37+
38+
@Test
39+
void library_delete_test() {
40+
Bundle bundle = (Bundle)
41+
jsonParser.parseResource(DeleteVisitorTests.class.getResourceAsStream("Bundle-small-retired.json"));
42+
Bundle tsBundle = repo.transaction(bundle);
43+
// Resource is uploaded using POST - need to get id like this
44+
String id = tsBundle.getEntry().get(0).getResponse().getLocation();
45+
String version = "1.2.3";
46+
Library library = repo.read(Library.class, new IdType(id)).copy();
47+
ILibraryAdapter libraryAdapter = new AdapterFactory().createLibrary(library);
48+
var deleteVisitor = new DeleteVisitor(repo);
49+
Parameters params = parameters(part("version", version));
50+
Bundle returnedBundle = (Bundle) libraryAdapter.accept(deleteVisitor, params);
51+
52+
var libraryEntries = BundleHelper.getEntryResources(returnedBundle).stream()
53+
.filter(r -> r.fhirType().equals("Library"))
54+
.toList();
55+
var planDefEntries = BundleHelper.getEntryResources(returnedBundle).stream()
56+
.filter(r -> r.fhirType().equals("PlanDefinition"))
57+
.toList();
58+
var valueSetEntries = BundleHelper.getEntryResources(returnedBundle).stream()
59+
.filter(r -> r.fhirType().equals("ValueSet"))
60+
.toList();
61+
var basicEntries = BundleHelper.getEntryResources(returnedBundle).stream()
62+
.filter(r -> r.fhirType().equals("Basic"))
63+
.toList();
64+
65+
assertEquals(2, libraryEntries.size());
66+
assertEquals(1, planDefEntries.size());
67+
assertEquals(1, valueSetEntries.size());
68+
assertEquals(1, basicEntries.size());
69+
}
70+
71+
@Test
72+
void library_delete_active_test() {
73+
Bundle bundle = (Bundle)
74+
jsonParser.parseResource(DeleteVisitorTests.class.getResourceAsStream("Bundle-ersd-small-active.json"));
75+
repo.transaction(bundle);
76+
String version = "1.2.3";
77+
Library library = repo.read(Library.class, new IdType("Library/SpecificationLibrary"))
78+
.copy();
79+
ILibraryAdapter libraryAdapter = new AdapterFactory().createLibrary(library);
80+
var deleteVisitor = new DeleteVisitor(repo);
81+
Parameters params = parameters(part("version", version));
82+
83+
var exception =
84+
assertThrows(PreconditionFailedException.class, () -> libraryAdapter.accept(deleteVisitor, params));
85+
assertTrue(exception.getMessage().contains("Cannot delete an artifact that is not in retired status"));
86+
}
87+
88+
@Test
89+
void library_delete_draft_test() {
90+
Bundle bundle = (Bundle) jsonParser.parseResource(
91+
DeleteVisitorTests.class.getResourceAsStream("Bundle-small-approved-draft.json"));
92+
Bundle tsBundle = repo.transaction(bundle);
93+
String id = tsBundle.getEntry().get(0).getResponse().getLocation();
94+
String version = "1.2.3-draft";
95+
Library library = repo.read(Library.class, new IdType(id)).copy();
96+
ILibraryAdapter libraryAdapter = new AdapterFactory().createLibrary(library);
97+
var deleteVisitor = new DeleteVisitor(repo);
98+
Parameters params = parameters(part("version", version));
99+
100+
var exception =
101+
assertThrows(PreconditionFailedException.class, () -> libraryAdapter.accept(deleteVisitor, params));
102+
assertTrue(exception.getMessage().contains("Cannot delete an artifact that is not in retired status"));
103+
}
104+
}

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

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import org.junit.jupiter.api.BeforeEach;
1717
import org.junit.jupiter.api.Test;
1818
import org.opencds.cqf.fhir.cr.visitor.DeleteVisitor;
19+
import org.opencds.cqf.fhir.utility.BundleHelper;
1920
import org.opencds.cqf.fhir.utility.adapter.ILibraryAdapter;
2021
import org.opencds.cqf.fhir.utility.adapter.r4.AdapterFactory;
2122
import org.opencds.cqf.fhir.utility.repository.InMemoryFhirRepository;
@@ -48,9 +49,23 @@ void library_delete_test() {
4849
Parameters params = parameters(part("version", version));
4950
Bundle returnedBundle = (Bundle) libraryAdapter.accept(deleteVisitor, params);
5051

51-
var res = returnedBundle.getEntry();
52+
var libraryEntries = BundleHelper.getEntryResources(returnedBundle).stream()
53+
.filter(r -> r.fhirType().equals("Library"))
54+
.toList();
55+
var planDefEntries = BundleHelper.getEntryResources(returnedBundle).stream()
56+
.filter(r -> r.fhirType().equals("PlanDefinition"))
57+
.toList();
58+
var valueSetEntries = BundleHelper.getEntryResources(returnedBundle).stream()
59+
.filter(r -> r.fhirType().equals("ValueSet"))
60+
.toList();
61+
var basicEntries = BundleHelper.getEntryResources(returnedBundle).stream()
62+
.filter(r -> r.fhirType().equals("Basic"))
63+
.toList();
5264

53-
assertEquals(4, res.size());
65+
assertEquals(2, libraryEntries.size());
66+
assertEquals(1, planDefEntries.size());
67+
assertEquals(1, valueSetEntries.size());
68+
assertEquals(1, basicEntries.size());
5469
}
5570

5671
@Test
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package org.opencds.cqf.fhir.cr.visitor.r5;
2+
3+
import static org.junit.jupiter.api.Assertions.*;
4+
import static org.opencds.cqf.fhir.utility.r5.Parameters.parameters;
5+
import static org.opencds.cqf.fhir.utility.r5.Parameters.part;
6+
7+
import ca.uhn.fhir.context.FhirContext;
8+
import ca.uhn.fhir.parser.IParser;
9+
import ca.uhn.fhir.repository.IRepository;
10+
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
11+
import org.hl7.fhir.r5.model.Bundle;
12+
import org.hl7.fhir.r5.model.IdType;
13+
import org.hl7.fhir.r5.model.Library;
14+
import org.hl7.fhir.r5.model.Parameters;
15+
import org.hl7.fhir.r5.model.SearchParameter;
16+
import org.junit.jupiter.api.BeforeEach;
17+
import org.junit.jupiter.api.Test;
18+
import org.opencds.cqf.fhir.cr.visitor.DeleteVisitor;
19+
import org.opencds.cqf.fhir.utility.BundleHelper;
20+
import org.opencds.cqf.fhir.utility.adapter.ILibraryAdapter;
21+
import org.opencds.cqf.fhir.utility.adapter.r5.AdapterFactory;
22+
import org.opencds.cqf.fhir.utility.repository.InMemoryFhirRepository;
23+
24+
class DeleteVisitorTests {
25+
26+
private final FhirContext fhirContext = FhirContext.forR5Cached();
27+
private IRepository repo;
28+
private final IParser jsonParser = fhirContext.newJsonParser();
29+
30+
@BeforeEach
31+
void setup() {
32+
SearchParameter sp = (SearchParameter) jsonParser.parseResource(
33+
ReleaseVisitorTests.class.getResourceAsStream("SearchParameter-artifactAssessment.json"));
34+
repo = new InMemoryFhirRepository(fhirContext);
35+
repo.update(sp);
36+
}
37+
38+
@Test
39+
void library_delete_test() {
40+
Bundle bundle = (Bundle)
41+
jsonParser.parseResource(DeleteVisitorTests.class.getResourceAsStream("Bundle-small-retired.json"));
42+
Bundle tsBundle = repo.transaction(bundle);
43+
// Resource is uploaded using POST - need to get id like this
44+
String id = tsBundle.getEntry().get(0).getResponse().getLocation();
45+
String version = "1.2.3";
46+
Library library = repo.read(Library.class, new IdType(id)).copy();
47+
ILibraryAdapter libraryAdapter = new AdapterFactory().createLibrary(library);
48+
var deleteVisitor = new DeleteVisitor(repo);
49+
Parameters params = parameters(part("version", version));
50+
Bundle returnedBundle = (Bundle) libraryAdapter.accept(deleteVisitor, params);
51+
52+
var libraryEntries = BundleHelper.getEntryResources(returnedBundle).stream()
53+
.filter(r -> r.fhirType().equals("Library"))
54+
.toList();
55+
var planDefEntries = BundleHelper.getEntryResources(returnedBundle).stream()
56+
.filter(r -> r.fhirType().equals("PlanDefinition"))
57+
.toList();
58+
var valueSetEntries = BundleHelper.getEntryResources(returnedBundle).stream()
59+
.filter(r -> r.fhirType().equals("ValueSet"))
60+
.toList();
61+
var basicEntries = BundleHelper.getEntryResources(returnedBundle).stream()
62+
.filter(r -> r.fhirType().equals("Basic"))
63+
.toList();
64+
65+
assertEquals(2, libraryEntries.size());
66+
assertEquals(1, planDefEntries.size());
67+
assertEquals(1, valueSetEntries.size());
68+
assertEquals(1, basicEntries.size());
69+
}
70+
71+
@Test
72+
void library_delete_active_test() {
73+
Bundle bundle = (Bundle)
74+
jsonParser.parseResource(DeleteVisitorTests.class.getResourceAsStream("Bundle-ersd-small-active.json"));
75+
repo.transaction(bundle);
76+
String version = "1.2.3";
77+
Library library = repo.read(Library.class, new IdType("Library/SpecificationLibrary"))
78+
.copy();
79+
ILibraryAdapter libraryAdapter = new AdapterFactory().createLibrary(library);
80+
var deleteVisitor = new DeleteVisitor(repo);
81+
Parameters params = parameters(part("version", version));
82+
83+
var exception =
84+
assertThrows(PreconditionFailedException.class, () -> libraryAdapter.accept(deleteVisitor, params));
85+
assertTrue(exception.getMessage().contains("Cannot delete an artifact that is not in retired status"));
86+
}
87+
88+
@Test
89+
void library_delete_draft_test() {
90+
Bundle bundle = (Bundle) jsonParser.parseResource(
91+
DeleteVisitorTests.class.getResourceAsStream("Bundle-small-approved-draft.json"));
92+
Bundle tsBundle = repo.transaction(bundle);
93+
String id = tsBundle.getEntry().get(0).getResponse().getLocation();
94+
String version = "1.2.3-draft";
95+
Library library = repo.read(Library.class, new IdType(id)).copy();
96+
ILibraryAdapter libraryAdapter = new AdapterFactory().createLibrary(library);
97+
var deleteVisitor = new DeleteVisitor(repo);
98+
Parameters params = parameters(part("version", version));
99+
100+
var exception =
101+
assertThrows(PreconditionFailedException.class, () -> libraryAdapter.accept(deleteVisitor, params));
102+
assertTrue(exception.getMessage().contains("Cannot delete an artifact that is not in retired status"));
103+
}
104+
}

0 commit comments

Comments
 (0)