From 0c642b6113fcd39af2a8afa5a9a294dae85e4eef Mon Sep 17 00:00:00 2001 From: jmarchionatto <60409882+jmarchionatto@users.noreply.github.com> Date: Wed, 26 Jun 2024 11:36:47 -0400 Subject: [PATCH] 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 --- .../extractor/BaseSearchParamExtractor.java | 41 ++--- .../extractor/SearchParamExtractionUtil.java | 167 ++++++++++++++++++ .../SearchParamExtractorService.java | 154 ++-------------- .../BaseSearchParamExtractorTest.java | 71 +++++++- .../test/utilities/RestServerR4Helper.java | 12 ++ 5 files changed, 285 insertions(+), 160 deletions(-) create mode 100644 hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractionUtil.java diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/BaseSearchParamExtractor.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/BaseSearchParamExtractor.java index fe7e89a58c1..73bb07c878d 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/BaseSearchParamExtractor.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/BaseSearchParamExtractor.java @@ -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 getSearchParams(IBaseResource theResource) { + @VisibleForTesting + Collection getSearchParams(IBaseResource theResource) { RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource); Collection retVal = mySearchParamRegistry.getActiveSearchParams(def.getName()).values(); @@ -1317,7 +1321,6 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor List> values = extractValuesAsFhirDates(myTimingEventValueChild, theValue); TreeSet dates = new TreeSet<>(); - TreeSet dateStrings = new TreeSet<>(); String firstValue = null; String finalValue = null; for (IPrimitiveType nextEvent : values) { @@ -1397,7 +1400,6 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor } } - @SuppressWarnings("unchecked") private void addNumber_Range( String theResourceType, Set 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 topLevelOrExpressions = splitOutOfParensToken(thePaths, " or "); - List 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 splitOutOfParensToken(String thePath, String theToken) { @@ -2018,8 +2014,9 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor List get() throws FHIRException; } + @VisibleForTesting @FunctionalInterface - private interface IExtractor { + interface IExtractor { void extract( SearchParamSet 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; + } } diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractionUtil.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractionUtil.java new file mode 100644 index 00000000000..cea26163420 --- /dev/null +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractionUtil.java @@ -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 strings = + extractSearchParamStrings(theResource, theSearchParamFilter); + handleWarnings(theRequestDetails, myInterceptorBroadcaster, strings); + theParams.myStringParams.addAll(strings); + + // Numbers + ISearchParamExtractor.SearchParamSet numbers = + extractSearchParamNumber(theResource, theSearchParamFilter); + handleWarnings(theRequestDetails, myInterceptorBroadcaster, numbers); + theParams.myNumberParams.addAll(numbers); + + // Quantities + ISearchParamExtractor.SearchParamSet 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 quantitiesNormalized = + extractSearchParamQuantityNormalized(theResource, theSearchParamFilter); + handleWarnings(theRequestDetails, myInterceptorBroadcaster, quantitiesNormalized); + theParams.myQuantityNormalizedParams.addAll(quantitiesNormalized); + } + + // Dates + ISearchParamExtractor.SearchParamSet dates = + extractSearchParamDates(theResource, theSearchParamFilter); + handleWarnings(theRequestDetails, myInterceptorBroadcaster, dates); + theParams.myDateParams.addAll(dates); + + // URIs + ISearchParamExtractor.SearchParamSet 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 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 composites = + extractSearchParamComposites(theResource, theSearchParamFilter); + handleWarnings(theRequestDetails, myInterceptorBroadcaster, composites); + theParams.myCompositeParams.addAll(composites); + } + + // Specials + ISearchParamExtractor.SearchParamSet specials = + extractSearchParamSpecial(theResource, theSearchParamFilter); + for (BaseResourceIndexedSearchParam next : specials) { + if (next instanceof ResourceIndexedSearchParamCoords) { + theParams.myCoordsParams.add((ResourceIndexedSearchParamCoords) next); + } + } + } + + private ISearchParamExtractor.SearchParamSet extractSearchParamDates( + IBaseResource theResource, ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) { + return mySearchParamExtractor.extractSearchParamDates(theResource, theSearchParamFilter); + } + + private ISearchParamExtractor.SearchParamSet extractSearchParamNumber( + IBaseResource theResource, ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) { + return mySearchParamExtractor.extractSearchParamNumber(theResource, theSearchParamFilter); + } + + private ISearchParamExtractor.SearchParamSet extractSearchParamQuantity( + IBaseResource theResource, ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) { + return mySearchParamExtractor.extractSearchParamQuantity(theResource, theSearchParamFilter); + } + + private ISearchParamExtractor.SearchParamSet + extractSearchParamQuantityNormalized( + IBaseResource theResource, ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) { + return mySearchParamExtractor.extractSearchParamQuantityNormalized(theResource, theSearchParamFilter); + } + + private ISearchParamExtractor.SearchParamSet extractSearchParamStrings( + IBaseResource theResource, ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) { + return mySearchParamExtractor.extractSearchParamStrings(theResource, theSearchParamFilter); + } + + private ISearchParamExtractor.SearchParamSet extractSearchParamTokens( + IBaseResource theResource, ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) { + return mySearchParamExtractor.extractSearchParamTokens(theResource, theSearchParamFilter); + } + + private ISearchParamExtractor.SearchParamSet extractSearchParamSpecial( + IBaseResource theResource, ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) { + return mySearchParamExtractor.extractSearchParamSpecial(theResource, theSearchParamFilter); + } + + private ISearchParamExtractor.SearchParamSet extractSearchParamUri( + IBaseResource theResource, ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) { + return mySearchParamExtractor.extractSearchParamUri(theResource, theSearchParamFilter); + } + + private ISearchParamExtractor.SearchParamSet extractSearchParamComposites( + IBaseResource theResource, ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) { + return mySearchParamExtractor.extractSearchParamComposites(theResource, theSearchParamFilter); + } +} diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorService.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorService.java index 15aa6b89fc6..51c2d79fea9 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorService.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorService.java @@ -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 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 strings = - extractSearchParamStrings(theResource, theSearchParamFilter); - handleWarnings(theRequestDetails, myInterceptorBroadcaster, strings); - theParams.myStringParams.addAll(strings); - - // Numbers - ISearchParamExtractor.SearchParamSet numbers = - extractSearchParamNumber(theResource, theSearchParamFilter); - handleWarnings(theRequestDetails, myInterceptorBroadcaster, numbers); - theParams.myNumberParams.addAll(numbers); - - // Quantities - ISearchParamExtractor.SearchParamSet 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 quantitiesNormalized = - extractSearchParamQuantityNormalized(theResource, theSearchParamFilter); - handleWarnings(theRequestDetails, myInterceptorBroadcaster, quantitiesNormalized); - theParams.myQuantityNormalizedParams.addAll(quantitiesNormalized); - } - - // Dates - ISearchParamExtractor.SearchParamSet dates = - extractSearchParamDates(theResource, theSearchParamFilter); - handleWarnings(theRequestDetails, myInterceptorBroadcaster, dates); - theParams.myDateParams.addAll(dates); - - // URIs - ISearchParamExtractor.SearchParamSet 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 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 composites = - extractSearchParamComposites(theResource, theSearchParamFilter); - handleWarnings(theRequestDetails, myInterceptorBroadcaster, composites); - theParams.myCompositeParams.addAll(composites); - } - - // Specials - ISearchParamExtractor.SearchParamSet 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 extractSearchParamDates( - IBaseResource theResource, ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) { - return mySearchParamExtractor.extractSearchParamDates(theResource, theSearchParamFilter); - } - - private ISearchParamExtractor.SearchParamSet extractSearchParamNumber( - IBaseResource theResource, ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) { - return mySearchParamExtractor.extractSearchParamNumber(theResource, theSearchParamFilter); - } - - private ISearchParamExtractor.SearchParamSet extractSearchParamQuantity( - IBaseResource theResource, ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) { - return mySearchParamExtractor.extractSearchParamQuantity(theResource, theSearchParamFilter); - } - - private ISearchParamExtractor.SearchParamSet - extractSearchParamQuantityNormalized( - IBaseResource theResource, ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) { - return mySearchParamExtractor.extractSearchParamQuantityNormalized(theResource, theSearchParamFilter); - } - - private ISearchParamExtractor.SearchParamSet extractSearchParamStrings( - IBaseResource theResource, ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) { - return mySearchParamExtractor.extractSearchParamStrings(theResource, theSearchParamFilter); - } - - private ISearchParamExtractor.SearchParamSet extractSearchParamTokens( - IBaseResource theResource, ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) { - return mySearchParamExtractor.extractSearchParamTokens(theResource, theSearchParamFilter); - } - - private ISearchParamExtractor.SearchParamSet extractSearchParamSpecial( - IBaseResource theResource, ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) { - return mySearchParamExtractor.extractSearchParamSpecial(theResource, theSearchParamFilter); - } - - private ISearchParamExtractor.SearchParamSet extractSearchParamUri( - IBaseResource theResource, ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) { - return mySearchParamExtractor.extractSearchParamUri(theResource, theSearchParamFilter); - } - - private ISearchParamExtractor.SearchParamSet extractSearchParamComposites( - IBaseResource theResource, ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) { - return mySearchParamExtractor.extractSearchParamComposites(theResource, theSearchParamFilter); - } - @VisibleForTesting void setInterceptorBroadcasterForUnitTest(IInterceptorBroadcaster theInterceptorBroadcaster) { myInterceptorBroadcaster = theInterceptorBroadcaster; diff --git a/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/extractor/BaseSearchParamExtractorTest.java b/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/extractor/BaseSearchParamExtractorTest.java index fbaba334c7c..8bd2d57d305 100644 --- a/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/extractor/BaseSearchParamExtractorTest.java +++ b/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/extractor/BaseSearchParamExtractorTest.java @@ -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 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)); + } } } diff --git a/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/test/utilities/RestServerR4Helper.java b/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/test/utilities/RestServerR4Helper.java index 37f8aa2daa4..5ac8aa67343 100644 --- a/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/test/utilities/RestServerR4Helper.java +++ b/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/test/utilities/RestServerR4Helper.java @@ -208,6 +208,10 @@ public class RestServerR4Helper extends BaseRestServerHelper implements BeforeEa return myRestServer.getPatientResourceProvider(); } + public void setPatientResourceProvider(HashMapResourceProvider theResourceProvider) { + myRestServer.setPatientResourceProvider(theResourceProvider); + } + @Override public HashMapResourceProvider getConceptMapResourceProvider() { return myRestServer.getConceptMapResourceProvider(); @@ -387,6 +391,14 @@ public class RestServerR4Helper extends BaseRestServerHelper implements BeforeEa return myObservationResourceProvider; } + public void setPatientResourceProvider(HashMapResourceProvider theResourceProvider) { + myPatientResourceProvider.getStoredResources().forEach(theResourceProvider::store); + + unregisterProvider(myPatientResourceProvider); + registerProvider(theResourceProvider); + myPatientResourceProvider = theResourceProvider; + } + public void setObservationResourceProvider(HashMapResourceProvider theResourceProvider) { myObservationResourceProvider.getStoredResources().forEach(theResourceProvider::store);