Add SearchBundleEntryParts (#3095)

* Add the ability to parse a search bundle into its component parts

* remove deadspace

* wip
This commit is contained in:
Tadgh 2021-10-19 19:41:33 -04:00 committed by GitHub
parent d07764e8e5
commit 3d681134e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 191 additions and 0 deletions

View File

@ -12,6 +12,8 @@ import ca.uhn.fhir.util.bundle.BundleEntryMutator;
import ca.uhn.fhir.util.bundle.BundleEntryParts; import ca.uhn.fhir.util.bundle.BundleEntryParts;
import ca.uhn.fhir.util.bundle.EntryListAccumulator; import ca.uhn.fhir.util.bundle.EntryListAccumulator;
import ca.uhn.fhir.util.bundle.ModifiableBundleEntry; import ca.uhn.fhir.util.bundle.ModifiableBundleEntry;
import ca.uhn.fhir.util.bundle.SearchBundleEntryParts;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseBinary; import org.hl7.fhir.instance.model.api.IBaseBinary;
@ -25,6 +27,7 @@ import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -365,6 +368,52 @@ public class BundleUtil {
return map; return map;
} }
public static List<SearchBundleEntryParts> getSearchBundleEntryParts(FhirContext theContext, IBaseBundle theBundle) {
RuntimeResourceDefinition bundleDef = theContext.getResourceDefinition(theBundle);
BaseRuntimeChildDefinition entryChildDef = bundleDef.getChildByName("entry");
List<IBase> entries = entryChildDef.getAccessor().getValues(theBundle);
BaseRuntimeElementCompositeDefinition<?> entryChildContentsDef = (BaseRuntimeElementCompositeDefinition<?>) entryChildDef.getChildByName("entry");
BaseRuntimeChildDefinition fullUrlChildDef = entryChildContentsDef.getChildByName("fullUrl");
BaseRuntimeChildDefinition resourceChildDef = entryChildContentsDef.getChildByName("resource");
BaseRuntimeChildDefinition searchChildDef = entryChildContentsDef.getChildByName("search");
BaseRuntimeElementCompositeDefinition<?> searchChildContentsDef = (BaseRuntimeElementCompositeDefinition<?>) searchChildDef.getChildByName("search");
BaseRuntimeChildDefinition searchModeChildDef = searchChildContentsDef.getChildByName("mode");
List<SearchBundleEntryParts> retVal = new ArrayList<>();
for (IBase nextEntry : entries) {
SearchBundleEntryParts parts = getSearchBundleEntryParts(fullUrlChildDef, resourceChildDef, searchChildDef, searchModeChildDef, nextEntry);
retVal.add(parts);
}
return retVal;
}
private static SearchBundleEntryParts getSearchBundleEntryParts( BaseRuntimeChildDefinition fullUrlChildDef, BaseRuntimeChildDefinition resourceChildDef, BaseRuntimeChildDefinition searchChildDef, BaseRuntimeChildDefinition searchModeChildDef, IBase entry) {
IBaseResource resource = null;
String matchMode = null;
String fullUrl = fullUrlChildDef
.getAccessor()
.getFirstValueOrNull(entry)
.map(t->((IPrimitiveType<?>)t).getValueAsString())
.orElse(null);
for (IBase nextResource : resourceChildDef.getAccessor().getValues(entry)) {
resource = (IBaseResource) nextResource;
}
for (IBase nextSearch : searchChildDef.getAccessor().getValues(entry)) {
for (IBase nextUrl : searchModeChildDef.getAccessor().getValues(nextSearch)) {
matchMode = ((IPrimitiveType<?>) nextUrl).getValueAsString();
}
}
SearchBundleEntryParts parts = new SearchBundleEntryParts(fullUrl, resource, matchMode);
return parts;
}
/** /**
* Given a bundle, and a consumer, apply the consumer to each entry in the bundle. * Given a bundle, and a consumer, apply the consumer to each entry in the bundle.
* @param theContext The FHIR Context * @param theContext The FHIR Context

View File

@ -0,0 +1,54 @@
package ca.uhn.fhir.util.bundle;
/*-
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2021 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
import org.hl7.fhir.instance.model.api.IBaseResource;
public class SearchBundleEntryParts {
private final IBaseResource myResource;
private final BundleEntrySearchModeEnum mySearchMode;
private final String myFullUrl;
public SearchBundleEntryParts(String theFullUrl, IBaseResource theResource, String theSearchMode) {
myFullUrl = theFullUrl;
myResource = theResource;
if ("match".equalsIgnoreCase(theSearchMode)) {
mySearchMode = BundleEntrySearchModeEnum.MATCH;
} else {
mySearchMode = BundleEntrySearchModeEnum.INCLUDE;
}
}
public String getFullUrl() {
return myFullUrl;
}
public IBaseResource getResource() {
return myResource;
}
public BundleEntrySearchModeEnum getSearchMode() {
return mySearchMode;
}
}

View File

@ -1,9 +1,11 @@
package ca.uhn.fhir.util.bundle; package ca.uhn.fhir.util.bundle;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
import ca.uhn.fhir.util.BundleBuilder; import ca.uhn.fhir.util.BundleBuilder;
import ca.uhn.fhir.util.BundleUtil; import ca.uhn.fhir.util.BundleUtil;
import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.TestUtil;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.ExplanationOfBenefit; import org.hl7.fhir.r4.model.ExplanationOfBenefit;
import org.hl7.fhir.r4.model.Medication; import org.hl7.fhir.r4.model.Medication;
@ -20,9 +22,11 @@ import java.util.List;
import java.util.function.Consumer; import java.util.function.Consumer;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.hl7.fhir.r4.model.Bundle.HTTPVerb.DELETE; import static org.hl7.fhir.r4.model.Bundle.HTTPVerb.DELETE;
import static org.hl7.fhir.r4.model.Bundle.HTTPVerb.GET; import static org.hl7.fhir.r4.model.Bundle.HTTPVerb.GET;
import static org.hl7.fhir.r4.model.Bundle.HTTPVerb.POST; import static org.hl7.fhir.r4.model.Bundle.HTTPVerb.POST;
@ -266,6 +270,90 @@ public class BundleUtilTest {
BundleUtil.sortEntriesIntoProcessingOrder(ourCtx, builder.getBundle()); BundleUtil.sortEntriesIntoProcessingOrder(ourCtx, builder.getBundle());
} }
@Test
public void testBundleToSearchBundleEntryParts() {
//Given
String bundleString = "{\n" +
" \"resourceType\": \"Bundle\",\n" +
" \"id\": \"bd194b7f-ac1e-429a-a206-ee2c470f23b5\",\n" +
" \"meta\": {\n" +
" \"lastUpdated\": \"2021-10-18T16:25:55.330-07:00\"\n" +
" },\n" +
" \"type\": \"searchset\",\n" +
" \"total\": 1,\n" +
" \"link\": [\n" +
" {\n" +
" \"relation\": \"self\",\n" +
" \"url\": \"http://localhost:8000/Patient?_count=1&_id=pata&_revinclude=Condition%3Asubject%3APatient\"\n" +
" }\n" +
" ],\n" +
" \"entry\": [\n" +
" {\n" +
" \"fullUrl\": \"http://localhost:8000/Patient/pata\",\n" +
" \"resource\": {\n" +
" \"resourceType\": \"Patient\",\n" +
" \"id\": \"pata\",\n" +
" \"meta\": {\n" +
" \"versionId\": \"1\",\n" +
" \"lastUpdated\": \"2021-10-18T16:25:48.954-07:00\",\n" +
" \"source\": \"#rnEjIucr8LR6Ze3x\"\n" +
" },\n" +
" \"name\": [\n" +
" {\n" +
" \"family\": \"Simpson\",\n" +
" \"given\": [\n" +
" \"Homer\",\n" +
" \"J\"\n" +
" ]\n" +
" }\n" +
" ]\n" +
" },\n" +
" \"search\": {\n" +
" \"mode\": \"match\"\n" +
" }\n" +
" },\n" +
" {\n" +
" \"fullUrl\": \"http://localhost:8000/Condition/1626\",\n" +
" \"resource\": {\n" +
" \"resourceType\": \"Condition\",\n" +
" \"id\": \"1626\",\n" +
" \"meta\": {\n" +
" \"versionId\": \"1\",\n" +
" \"lastUpdated\": \"2021-10-18T16:25:51.672-07:00\",\n" +
" \"source\": \"#gSOcGAdA3acaaNq1\"\n" +
" },\n" +
" \"identifier\": [\n" +
" {\n" +
" \"system\": \"urn:hssc:musc:conditionid\",\n" +
" \"value\": \"1064115000.1.5\"\n" +
" }\n" +
" ],\n" +
" \"subject\": {\n" +
" \"reference\": \"Patient/pata\"\n" +
" }\n" +
" },\n" +
" \"search\": {\n" +
" \"mode\": \"include\"\n" +
" }\n" +
" }\n" +
" ]\n" +
"}";
Bundle bundle = ourCtx.newJsonParser().parseResource(Bundle.class, bundleString);
//When
List<SearchBundleEntryParts> searchBundleEntryParts = BundleUtil.getSearchBundleEntryParts(ourCtx, bundle);
//Then
assertThat(searchBundleEntryParts, hasSize(2));
assertThat(searchBundleEntryParts.get(0).getSearchMode(), is(equalTo(BundleEntrySearchModeEnum.MATCH)));
assertThat(searchBundleEntryParts.get(0).getFullUrl(), is(containsString("Patient/pata")));
assertThat(searchBundleEntryParts.get(0).getResource(), is(notNullValue()));
assertThat(searchBundleEntryParts.get(1).getSearchMode(), is(equalTo(BundleEntrySearchModeEnum.INCLUDE)));
assertThat(searchBundleEntryParts.get(1).getFullUrl(), is(containsString("Condition/")));
assertThat(searchBundleEntryParts.get(1).getResource(), is(notNullValue()));
}
@Test @Test
public void testTransactionSorterReturnsDeletesInCorrectProcessingOrder() { public void testTransactionSorterReturnsDeletesInCorrectProcessingOrder() {
Bundle b = new Bundle(); Bundle b = new Bundle();