diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_0_0/3548-has-query-parameter-resolution b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_0_0/3548-has-query-parameter-resolution new file mode 100644 index 00000000000..a22d2f9c01e --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_0_0/3548-has-query-parameter-resolution @@ -0,0 +1,6 @@ +--- +type: fix +issue: 3548 +jira: SMILE-4023 +title: "A recent change caused searches that use _has in conjuction with a `Resource` search param (e.g. _id, _text) to fail. This has been fixed." + diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/QueryStack.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/QueryStack.java index 90b133f025a..c07df367bd7 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/QueryStack.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/QueryStack.java @@ -546,17 +546,12 @@ public class QueryStack { //Ensure that the name of the search param // (e.g. the `code` in Patient?_has:Observation:subject:code=sys|val) // exists on the target resource type. - RuntimeSearchParam owningParameterDef = mySearchParamRegistry.getActiveSearchParam(targetResourceType, paramName); - if (owningParameterDef == null) { - throw new InvalidRequestException(Msg.code(1209) + "Unknown parameter name: " + targetResourceType + ':' + parameterName); - } + RuntimeSearchParam owningParameterDef = mySearchParamRegistry.getRuntimeSearchParam(targetResourceType, paramName); //Ensure that the name of the back-referenced search param on the target (e.g. the `subject` in Patient?_has:Observation:subject:code=sys|val) - //exists on the target resource. - RuntimeSearchParam joiningParameterDef = mySearchParamRegistry.getActiveSearchParam(targetResourceType, paramReference); - if (joiningParameterDef == null) { - throw new InvalidRequestException(Msg.code(1210) + "Unknown parameter name: " + targetResourceType + ':' + paramReference); - } + //exists on the target resource, or in the top-level Resource resource. + mySearchParamRegistry.getRuntimeSearchParam(targetResourceType, paramReference); + IQueryParameterAnd parsedParam = JpaParamUtil.parseQueryParams(mySearchParamRegistry, myFhirContext, owningParameterDef, paramName, parameters); diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchNoFtTest.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchNoFtTest.java index 7928541f73b..3caabff87b5 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchNoFtTest.java +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchNoFtTest.java @@ -499,7 +499,7 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test { myPatientDao.search(params); fail(); } catch (InvalidRequestException e) { - assertEquals(Msg.code(1210) + "Unknown parameter name: Observation:soooooobject", e.getMessage()); + assertEquals(Msg.code(1209) + "Unknown parameter name: Observation:soooooobject", e.getMessage()); } } diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java index c36abfaea0b..301f835e1b0 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java @@ -1157,7 +1157,7 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test { myPatientDao.search(params); fail(); } catch (InvalidRequestException e) { - assertEquals(Msg.code(1210) + "Unknown parameter name: Observation:soooooobject", e.getMessage()); + assertEquals(Msg.code(1209) + "Unknown parameter name: Observation:soooooobject", e.getMessage()); } } diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoHashesTest.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoHashesTest.java index fcbfc07c6ce..daeb832992e 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoHashesTest.java +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoHashesTest.java @@ -639,7 +639,7 @@ public class FhirResourceDaoR4SearchNoHashesTest extends BaseJpaR4Test { myPatientDao.search(params); fail(); } catch (InvalidRequestException e) { - assertEquals(Msg.code(1210) + "Unknown parameter name: Observation:soooooobject", e.getMessage()); + assertEquals(Msg.code(1209) + "Unknown parameter name: Observation:soooooobject", e.getMessage()); } } diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServerConfiguration.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServerConfiguration.java index 552557b928f..bef3d8c9e3c 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServerConfiguration.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServerConfiguration.java @@ -54,6 +54,7 @@ import java.util.IdentityHashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/ISearchParamRegistry.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/ISearchParamRegistry.java index 38a59be1b5f..825e0e3ab40 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/ISearchParamRegistry.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/ISearchParamRegistry.java @@ -22,7 +22,9 @@ package ca.uhn.fhir.rest.server.util; import ca.uhn.fhir.context.RuntimeSearchParam; import ca.uhn.fhir.context.phonetic.IPhoneticEncoder; +import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import org.hl7.fhir.instance.model.api.IAnyResource; import javax.annotation.Nullable; @@ -40,6 +42,7 @@ public interface ISearchParamRegistry { */ RuntimeSearchParam getActiveSearchParam(String theResourceName, String theParamName); + /** * @return Returns all active search params for the given resource */ @@ -57,7 +60,6 @@ public interface ISearchParamRegistry { default void requestRefresh() { } - /** * When indexing a HumanName, if a StringEncoder is set in the context, then the "phonetic" search parameter will normalize * the String using this encoder. @@ -101,4 +103,22 @@ public interface ISearchParamRegistry { @Nullable RuntimeSearchParam getActiveSearchParamByUrl(String theUrl); + /** + * Find a search param for a resource. First, check the resource itself, then check the top-level `Resource` resource. + * + * @param theResourceType the resource type. + * @param theParamName the search parameter name. + * + * @return the {@link RuntimeSearchParam} that is found. + */ + default RuntimeSearchParam getRuntimeSearchParam(String theResourceType, String theParamName) { + RuntimeSearchParam availableSearchParamDef = getActiveSearchParam(theResourceType, theParamName); + if (availableSearchParamDef == null) { + availableSearchParamDef = getActiveSearchParam("Resource", theParamName); + } + if (availableSearchParamDef == null) { + throw new InvalidRequestException(Msg.code(1209) + "Unknown parameter name: " + theResourceType + ':' + theParamName); + } + return availableSearchParamDef; + } }