Improve search param extraction flexibility (#6030)

* Allow extractor to extract values for Resource-level parameters also

* Rename flag

* spotless

* Allow setting test-specific resource provider

* Add tests

* Move common mockings to setup

---------

Co-authored-by: juan.marchionatto <juan.marchionatto@smilecdr.com>
This commit is contained in:
jmarchionatto 2024-06-26 11:36:47 -04:00 committed by GitHub
parent 7b68c4d91d
commit 0c642b6113
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 285 additions and 160 deletions

View File

@ -171,6 +171,9 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
private BaseRuntimeChildDefinition myCodeableReferenceConcept;
private BaseRuntimeChildDefinition myCodeableReferenceReference;
// allow extraction of Resource-level search param values
private boolean myExtractResourceLevelParams = false;
/**
* Constructor
*/
@ -186,9 +189,9 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
PartitionSettings thePartitionSettings,
FhirContext theCtx,
ISearchParamRegistry theSearchParamRegistry) {
Validate.notNull(theStorageSettings);
Validate.notNull(theCtx);
Validate.notNull(theSearchParamRegistry);
Objects.requireNonNull(theStorageSettings);
Objects.requireNonNull(theCtx);
Objects.requireNonNull(theSearchParamRegistry);
myStorageSettings = theStorageSettings;
myContext = theCtx;
@ -972,7 +975,8 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
mySearchParamRegistry = theSearchParamRegistry;
}
private Collection<RuntimeSearchParam> getSearchParams(IBaseResource theResource) {
@VisibleForTesting
Collection<RuntimeSearchParam> getSearchParams(IBaseResource theResource) {
RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource);
Collection<RuntimeSearchParam> retVal =
mySearchParamRegistry.getActiveSearchParams(def.getName()).values();
@ -1317,7 +1321,6 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
List<IPrimitiveType<Date>> values = extractValuesAsFhirDates(myTimingEventValueChild, theValue);
TreeSet<Date> dates = new TreeSet<>();
TreeSet<String> dateStrings = new TreeSet<>();
String firstValue = null;
String finalValue = null;
for (IPrimitiveType<Date> nextEvent : values) {
@ -1397,7 +1400,6 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
}
}
@SuppressWarnings("unchecked")
private void addNumber_Range(
String theResourceType,
Set<ResourceIndexedSearchParamNumber> theParams,
@ -1612,7 +1614,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
}
// See the method javadoc for an explanation of this
if (RuntimeSearchParamHelper.isResourceLevel(nextSpDef)) {
if (!myExtractResourceLevelParams && RuntimeSearchParamHelper.isResourceLevel(nextSpDef)) {
continue;
}
@ -1805,16 +1807,11 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
public boolean shouldAttemptToSplitPath(String thePath) {
if (getContext().getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.R4)) {
if (thePath.contains("|")) {
return true;
}
return thePath.contains("|");
} else {
// DSTU 3 and below used "or" as well as "|"
if (thePath.contains("|") || thePath.contains(" or ")) {
return true;
}
return thePath.contains("|") || thePath.contains(" or ");
}
return false;
}
/**
@ -1830,10 +1827,9 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
*/
private String[] splitOutOfParensOrs(String thePaths) {
List<String> topLevelOrExpressions = splitOutOfParensToken(thePaths, " or ");
List<String> retVal = topLevelOrExpressions.stream()
return topLevelOrExpressions.stream()
.flatMap(s -> splitOutOfParensToken(s, " |").stream())
.collect(Collectors.toList());
return retVal.toArray(new String[retVal.size()]);
.toArray(String[]::new);
}
private List<String> splitOutOfParensToken(String thePath, String theToken) {
@ -2018,8 +2014,9 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
List<? extends IBase> get() throws FHIRException;
}
@VisibleForTesting
@FunctionalInterface
private interface IExtractor<T> {
interface IExtractor<T> {
void extract(
SearchParamSet<T> theParams,
@ -2052,7 +2049,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
.findFirst();
// if the SP doesn't care, use the system default.
if (!noSuppressForSearchParam.isPresent()) {
if (noSuppressForSearchParam.isEmpty()) {
return !theStorageSettings.isSuppressStringIndexingInTokens();
// If the SP does care, use its value.
} else {
@ -2427,7 +2424,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
// DSTU2 only
if (value instanceof BoundCodeDt) {
BoundCodeDt boundCode = (BoundCodeDt) value;
Enum valueAsEnum = boundCode.getValueAsEnum();
Enum<?> valueAsEnum = boundCode.getValueAsEnum();
String system = null;
if (valueAsEnum != null) {
//noinspection unchecked
@ -2535,4 +2532,8 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
myExtractor1.extract(theParams, theSearchParam, theValue, thePath, theWantLocalReferences);
}
}
public void setExtractResourceLevelParams(boolean theExtractResourceLevelParams) {
myExtractResourceLevelParams = theExtractResourceLevelParams;
}
}

View File

@ -0,0 +1,167 @@
package ca.uhn.fhir.jpa.searchparam.extractor;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
import ca.uhn.fhir.jpa.model.entity.NormalizedQuantitySearchLevel;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamCoords;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamNumber;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantity;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantityNormalized;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamUri;
import ca.uhn.fhir.jpa.model.entity.StorageSettings;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import jakarta.annotation.Nonnull;
import org.hl7.fhir.instance.model.api.IBaseResource;
import static ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorService.handleWarnings;
public class SearchParamExtractionUtil {
private final FhirContext myFhirContext;
private final StorageSettings myStorageSettings;
private final ISearchParamExtractor mySearchParamExtractor;
private final IInterceptorBroadcaster myInterceptorBroadcaster;
public SearchParamExtractionUtil(
FhirContext theFhirContext,
StorageSettings theStorageSettings,
ISearchParamExtractor theSearchParamExtractor,
IInterceptorBroadcaster theInterceptorBroadcaster) {
myFhirContext = theFhirContext;
myStorageSettings = theStorageSettings;
mySearchParamExtractor = theSearchParamExtractor;
myInterceptorBroadcaster = theInterceptorBroadcaster;
}
public void extractSearchIndexParameters(
RequestDetails theRequestDetails,
ResourceIndexedSearchParams theParams,
IBaseResource theResource,
@Nonnull ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) {
// Strings
ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamString> strings =
extractSearchParamStrings(theResource, theSearchParamFilter);
handleWarnings(theRequestDetails, myInterceptorBroadcaster, strings);
theParams.myStringParams.addAll(strings);
// Numbers
ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamNumber> numbers =
extractSearchParamNumber(theResource, theSearchParamFilter);
handleWarnings(theRequestDetails, myInterceptorBroadcaster, numbers);
theParams.myNumberParams.addAll(numbers);
// Quantities
ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamQuantity> quantities =
extractSearchParamQuantity(theResource, theSearchParamFilter);
handleWarnings(theRequestDetails, myInterceptorBroadcaster, quantities);
theParams.myQuantityParams.addAll(quantities);
if (myStorageSettings
.getNormalizedQuantitySearchLevel()
.equals(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_STORAGE_SUPPORTED)
|| myStorageSettings
.getNormalizedQuantitySearchLevel()
.equals(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED)) {
ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamQuantityNormalized> quantitiesNormalized =
extractSearchParamQuantityNormalized(theResource, theSearchParamFilter);
handleWarnings(theRequestDetails, myInterceptorBroadcaster, quantitiesNormalized);
theParams.myQuantityNormalizedParams.addAll(quantitiesNormalized);
}
// Dates
ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamDate> dates =
extractSearchParamDates(theResource, theSearchParamFilter);
handleWarnings(theRequestDetails, myInterceptorBroadcaster, dates);
theParams.myDateParams.addAll(dates);
// URIs
ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamUri> uris =
extractSearchParamUri(theResource, theSearchParamFilter);
handleWarnings(theRequestDetails, myInterceptorBroadcaster, uris);
theParams.myUriParams.addAll(uris);
// Tokens (can result in both Token and String, as we index the display name for
// the types: Coding, CodeableConcept)
ISearchParamExtractor.SearchParamSet<BaseResourceIndexedSearchParam> tokens =
extractSearchParamTokens(theResource, theSearchParamFilter);
for (BaseResourceIndexedSearchParam next : tokens) {
if (next instanceof ResourceIndexedSearchParamToken) {
theParams.myTokenParams.add((ResourceIndexedSearchParamToken) next);
} else if (next instanceof ResourceIndexedSearchParamCoords) {
theParams.myCoordsParams.add((ResourceIndexedSearchParamCoords) next);
} else {
theParams.myStringParams.add((ResourceIndexedSearchParamString) next);
}
}
// Composites
// dst2 composites use stuff like value[x] , and we don't support them.
if (myFhirContext.getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.DSTU3)) {
ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamComposite> composites =
extractSearchParamComposites(theResource, theSearchParamFilter);
handleWarnings(theRequestDetails, myInterceptorBroadcaster, composites);
theParams.myCompositeParams.addAll(composites);
}
// Specials
ISearchParamExtractor.SearchParamSet<BaseResourceIndexedSearchParam> specials =
extractSearchParamSpecial(theResource, theSearchParamFilter);
for (BaseResourceIndexedSearchParam next : specials) {
if (next instanceof ResourceIndexedSearchParamCoords) {
theParams.myCoordsParams.add((ResourceIndexedSearchParamCoords) next);
}
}
}
private ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamDate> extractSearchParamDates(
IBaseResource theResource, ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) {
return mySearchParamExtractor.extractSearchParamDates(theResource, theSearchParamFilter);
}
private ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamNumber> extractSearchParamNumber(
IBaseResource theResource, ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) {
return mySearchParamExtractor.extractSearchParamNumber(theResource, theSearchParamFilter);
}
private ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamQuantity> extractSearchParamQuantity(
IBaseResource theResource, ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) {
return mySearchParamExtractor.extractSearchParamQuantity(theResource, theSearchParamFilter);
}
private ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamQuantityNormalized>
extractSearchParamQuantityNormalized(
IBaseResource theResource, ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) {
return mySearchParamExtractor.extractSearchParamQuantityNormalized(theResource, theSearchParamFilter);
}
private ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamString> extractSearchParamStrings(
IBaseResource theResource, ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) {
return mySearchParamExtractor.extractSearchParamStrings(theResource, theSearchParamFilter);
}
private ISearchParamExtractor.SearchParamSet<BaseResourceIndexedSearchParam> extractSearchParamTokens(
IBaseResource theResource, ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) {
return mySearchParamExtractor.extractSearchParamTokens(theResource, theSearchParamFilter);
}
private ISearchParamExtractor.SearchParamSet<BaseResourceIndexedSearchParam> extractSearchParamSpecial(
IBaseResource theResource, ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) {
return mySearchParamExtractor.extractSearchParamSpecial(theResource, theSearchParamFilter);
}
private ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamUri> extractSearchParamUri(
IBaseResource theResource, ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) {
return mySearchParamExtractor.extractSearchParamUri(theResource, theSearchParamFilter);
}
private ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamComposite> extractSearchParamComposites(
IBaseResource theResource, ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) {
return mySearchParamExtractor.extractSearchParamComposites(theResource, theSearchParamFilter);
}
}

View File

@ -20,7 +20,6 @@
package ca.uhn.fhir.jpa.searchparam.extractor;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.i18n.Msg;
@ -34,17 +33,9 @@ import ca.uhn.fhir.jpa.model.dao.JpaPid;
import ca.uhn.fhir.jpa.model.entity.BasePartitionable;
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
import ca.uhn.fhir.jpa.model.entity.IResourceIndexComboSearchParameter;
import ca.uhn.fhir.jpa.model.entity.NormalizedQuantitySearchLevel;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedComboStringUnique;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedComboTokenNonUnique;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamCoords;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamNumber;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantity;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantityNormalized;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamUri;
import ca.uhn.fhir.jpa.model.entity.ResourceLink;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.model.entity.SearchParamPresentEntity;
@ -107,6 +98,8 @@ public class SearchParamExtractorService {
@Autowired(required = false)
private IResourceLinkResolver myResourceLinkResolver;
private SearchParamExtractionUtil mySearchParamExtractionUtil;
@VisibleForTesting
public void setSearchParamExtractor(ISearchParamExtractor theSearchParamExtractor) {
mySearchParamExtractor = theSearchParamExtractor;
@ -150,7 +143,8 @@ public class SearchParamExtractorService {
@Nonnull ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) {
// All search parameter types except Reference
ResourceIndexedSearchParams normalParams = ResourceIndexedSearchParams.withSets();
extractSearchIndexParameters(theRequestDetails, normalParams, theResource, theSearchParamFilter);
getExtractionUtil()
.extractSearchIndexParameters(theRequestDetails, normalParams, theResource, theSearchParamFilter);
mergeParams(normalParams, theNewParams);
boolean indexOnContainedResources = myStorageSettings.isIndexOnContainedResources();
@ -234,6 +228,14 @@ public class SearchParamExtractorService {
theNewParams.setUpdatedTime(theTransactionDetails.getTransactionDate());
}
private SearchParamExtractionUtil getExtractionUtil() {
if (mySearchParamExtractionUtil == null) {
mySearchParamExtractionUtil = new SearchParamExtractionUtil(
myContext, myStorageSettings, mySearchParamExtractor, myInterceptorBroadcaster);
}
return mySearchParamExtractionUtil;
}
@Nonnull
private Map<String, Boolean> getReferenceSearchParamPresenceMap(
ResourceTable entity, ResourceIndexedSearchParams newParams) {
@ -430,7 +432,8 @@ public class SearchParamExtractorService {
ResourceIndexedSearchParams currParams = ResourceIndexedSearchParams.withSets();
// 3.3 create indexes for the current contained resource
extractSearchIndexParameters(theRequestDetails, currParams, targetResource, searchParamsToIndex);
getExtractionUtil()
.extractSearchIndexParameters(theRequestDetails, currParams, targetResource, searchParamsToIndex);
// 3.4 recurse to process any other contained resources referenced by this one
// Recursing is currently only allowed for contained resources and not
@ -490,87 +493,6 @@ public class SearchParamExtractorService {
theTargetParams.myCompositeParams.addAll(theSrcParams.myCompositeParams);
}
void extractSearchIndexParameters(
RequestDetails theRequestDetails,
ResourceIndexedSearchParams theParams,
IBaseResource theResource,
@Nonnull ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) {
// Strings
ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamString> strings =
extractSearchParamStrings(theResource, theSearchParamFilter);
handleWarnings(theRequestDetails, myInterceptorBroadcaster, strings);
theParams.myStringParams.addAll(strings);
// Numbers
ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamNumber> numbers =
extractSearchParamNumber(theResource, theSearchParamFilter);
handleWarnings(theRequestDetails, myInterceptorBroadcaster, numbers);
theParams.myNumberParams.addAll(numbers);
// Quantities
ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamQuantity> quantities =
extractSearchParamQuantity(theResource, theSearchParamFilter);
handleWarnings(theRequestDetails, myInterceptorBroadcaster, quantities);
theParams.myQuantityParams.addAll(quantities);
if (myStorageSettings
.getNormalizedQuantitySearchLevel()
.equals(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_STORAGE_SUPPORTED)
|| myStorageSettings
.getNormalizedQuantitySearchLevel()
.equals(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED)) {
ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamQuantityNormalized> quantitiesNormalized =
extractSearchParamQuantityNormalized(theResource, theSearchParamFilter);
handleWarnings(theRequestDetails, myInterceptorBroadcaster, quantitiesNormalized);
theParams.myQuantityNormalizedParams.addAll(quantitiesNormalized);
}
// Dates
ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamDate> dates =
extractSearchParamDates(theResource, theSearchParamFilter);
handleWarnings(theRequestDetails, myInterceptorBroadcaster, dates);
theParams.myDateParams.addAll(dates);
// URIs
ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamUri> uris =
extractSearchParamUri(theResource, theSearchParamFilter);
handleWarnings(theRequestDetails, myInterceptorBroadcaster, uris);
theParams.myUriParams.addAll(uris);
// Tokens (can result in both Token and String, as we index the display name for
// the types: Coding, CodeableConcept)
ISearchParamExtractor.SearchParamSet<BaseResourceIndexedSearchParam> tokens =
extractSearchParamTokens(theResource, theSearchParamFilter);
for (BaseResourceIndexedSearchParam next : tokens) {
if (next instanceof ResourceIndexedSearchParamToken) {
theParams.myTokenParams.add((ResourceIndexedSearchParamToken) next);
} else if (next instanceof ResourceIndexedSearchParamCoords) {
theParams.myCoordsParams.add((ResourceIndexedSearchParamCoords) next);
} else {
theParams.myStringParams.add((ResourceIndexedSearchParamString) next);
}
}
// Composites
// dst2 composites use stuff like value[x] , and we don't support them.
if (myContext.getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.DSTU3)) {
ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamComposite> composites =
extractSearchParamComposites(theResource, theSearchParamFilter);
handleWarnings(theRequestDetails, myInterceptorBroadcaster, composites);
theParams.myCompositeParams.addAll(composites);
}
// Specials
ISearchParamExtractor.SearchParamSet<BaseResourceIndexedSearchParam> specials =
extractSearchParamSpecial(theResource, theSearchParamFilter);
for (BaseResourceIndexedSearchParam next : specials) {
if (next instanceof ResourceIndexedSearchParamCoords) {
theParams.myCoordsParams.add((ResourceIndexedSearchParamCoords) next);
}
}
}
private void populateResourceTables(ResourceIndexedSearchParams theParams, ResourceTable theEntity) {
populateResourceTable(theParams.myNumberParams, theEntity);
@ -643,7 +565,7 @@ public class SearchParamExtractorService {
}
}
theEntity.setHasLinks(theNewParams.myLinks.size() > 0);
theEntity.setHasLinks(!theNewParams.myLinks.isEmpty());
}
private void extractResourceLinks(
@ -1100,52 +1022,6 @@ public class SearchParamExtractorService {
}
}
private ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamDate> extractSearchParamDates(
IBaseResource theResource, ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) {
return mySearchParamExtractor.extractSearchParamDates(theResource, theSearchParamFilter);
}
private ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamNumber> extractSearchParamNumber(
IBaseResource theResource, ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) {
return mySearchParamExtractor.extractSearchParamNumber(theResource, theSearchParamFilter);
}
private ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamQuantity> extractSearchParamQuantity(
IBaseResource theResource, ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) {
return mySearchParamExtractor.extractSearchParamQuantity(theResource, theSearchParamFilter);
}
private ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamQuantityNormalized>
extractSearchParamQuantityNormalized(
IBaseResource theResource, ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) {
return mySearchParamExtractor.extractSearchParamQuantityNormalized(theResource, theSearchParamFilter);
}
private ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamString> extractSearchParamStrings(
IBaseResource theResource, ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) {
return mySearchParamExtractor.extractSearchParamStrings(theResource, theSearchParamFilter);
}
private ISearchParamExtractor.SearchParamSet<BaseResourceIndexedSearchParam> extractSearchParamTokens(
IBaseResource theResource, ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) {
return mySearchParamExtractor.extractSearchParamTokens(theResource, theSearchParamFilter);
}
private ISearchParamExtractor.SearchParamSet<BaseResourceIndexedSearchParam> extractSearchParamSpecial(
IBaseResource theResource, ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) {
return mySearchParamExtractor.extractSearchParamSpecial(theResource, theSearchParamFilter);
}
private ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamUri> extractSearchParamUri(
IBaseResource theResource, ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) {
return mySearchParamExtractor.extractSearchParamUri(theResource, theSearchParamFilter);
}
private ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamComposite> extractSearchParamComposites(
IBaseResource theResource, ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) {
return mySearchParamExtractor.extractSearchParamComposites(theResource, theSearchParamFilter);
}
@VisibleForTesting
void setInterceptorBroadcasterForUnitTest(IInterceptorBroadcaster theInterceptorBroadcaster) {
myInterceptorBroadcaster = theInterceptorBroadcaster;

View File

@ -1,18 +1,87 @@
package ca.uhn.fhir.jpa.searchparam.extractor;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.jpa.model.entity.StorageSettings;
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.Arrays;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class BaseSearchParamExtractorTest {
@Test
public void testSplitPathsR4() {
void testSplitPathsR4() {
List<String> tokens = Arrays.asList(BaseSearchParamExtractor.splitPathsR4(" aaa | bbb + '|' | ccc ddd "));
assertThat(tokens).containsExactly("aaa", "bbb + '|'", "ccc ddd");
}
@Nested
class ExtractResourceLevelParams {
@Spy
BaseSearchParamExtractor myExtractor;
@Mock
IBaseResource myResource;
@Mock
RestSearchParameterTypeEnum mySearchParamType;
@Mock
ISearchParamExtractor.ISearchParamFilter mySearchParamFilter;
@Mock
RuntimeSearchParam myRuntimeSearchParam;
@Mock
StorageSettings myStorageSettings;
@BeforeEach
void setUp() {
myExtractor.setStorageSettings(myStorageSettings);
doReturn(List.of(myRuntimeSearchParam)).when(myExtractor).getSearchParams(myResource);
when(mySearchParamFilter.filterSearchParams(any())).thenReturn(List.of(myRuntimeSearchParam));
when(myRuntimeSearchParam.getParamType()).thenReturn(mySearchParamType);
}
@Test
void testWhenSet_resourceLevelParamsAreExtracted() {
myExtractor.setExtractResourceLevelParams(true);
doNothing().when(myExtractor).extractSearchParam(any(), any(), any(), any(), eq(false));
// execute
myExtractor.extractSearchParams(myResource, null, mySearchParamType, false, mySearchParamFilter);
verify(myExtractor).extractSearchParam(any(), any(), any(), any(), eq(false));
}
@Test
void testWhenNotSet_resourceLevelParamsAreNotExtracted() {
when(myRuntimeSearchParam.getPath()).thenReturn("Resource.something");
// execute
myExtractor.extractSearchParams(myResource, null, mySearchParamType, false, mySearchParamFilter);
verify(myExtractor, never()).extractSearchParam(any(), any(), any(), any(), eq(false));
}
}
}

View File

@ -208,6 +208,10 @@ public class RestServerR4Helper extends BaseRestServerHelper implements BeforeEa
return myRestServer.getPatientResourceProvider();
}
public void setPatientResourceProvider(HashMapResourceProvider<Patient> theResourceProvider) {
myRestServer.setPatientResourceProvider(theResourceProvider);
}
@Override
public HashMapResourceProvider<ConceptMap> getConceptMapResourceProvider() {
return myRestServer.getConceptMapResourceProvider();
@ -387,6 +391,14 @@ public class RestServerR4Helper extends BaseRestServerHelper implements BeforeEa
return myObservationResourceProvider;
}
public void setPatientResourceProvider(HashMapResourceProvider<Patient> theResourceProvider) {
myPatientResourceProvider.getStoredResources().forEach(theResourceProvider::store);
unregisterProvider(myPatientResourceProvider);
registerProvider(theResourceProvider);
myPatientResourceProvider = theResourceProvider;
}
public void setObservationResourceProvider(HashMapResourceProvider<Observation> theResourceProvider) {
myObservationResourceProvider.getStoredResources().forEach(theResourceProvider::store);