diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/InMemoryMatchResult.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/InMemoryMatchResult.java index 9bf3832421f..30b43f5cd7f 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/InMemoryMatchResult.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/InMemoryMatchResult.java @@ -23,8 +23,9 @@ package ca.uhn.fhir.jpa.searchparam.matcher; public class InMemoryMatchResult { public static final String PARSE_FAIL = "Failed to translate parse query string"; public static final String STANDARD_PARAMETER = "Standard parameters not supported"; - public static final String CHAIN = "Chained references are not supported"; - public static final String PARAM = "Param not supported"; + public static final String CHAIN = "Chained parameters are not supported"; + public static final String PARAM = "Parameter not supported"; + public static final String QUALIFIER = "Qualified parameter not supported"; private final boolean myMatch; private final boolean mySupported; diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/InMemoryResourceMatcher.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/InMemoryResourceMatcher.java index 253d5cdb9ab..5454a44bba7 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/InMemoryResourceMatcher.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/InMemoryResourceMatcher.java @@ -44,6 +44,7 @@ import org.springframework.stereotype.Service; import java.util.List; import java.util.Map; +import java.util.Optional; @Service public class InMemoryResourceMatcher { @@ -100,7 +101,15 @@ public class InMemoryResourceMatcher { } if (hasQualifiers(theAndOrParams)) { - return InMemoryMatchResult.unsupportedFromParameterAndReason(theParamName, InMemoryMatchResult.STANDARD_PARAMETER); + Optional optionalParameter = theAndOrParams.stream().flatMap(List::stream).filter(param -> param.getQueryParameterQualifier() != null).findAny(); + if (optionalParameter.isPresent()) { + IQueryParameterType parameter = optionalParameter.get(); + if (parameter instanceof ReferenceParam) { + ReferenceParam referenceParam = (ReferenceParam) parameter; + return InMemoryMatchResult.unsupportedFromParameterAndReason(theParamName + "." + referenceParam.getChain(), InMemoryMatchResult.CHAIN); + } + return InMemoryMatchResult.unsupportedFromParameterAndReason(theParamName + parameter.getQueryParameterQualifier(), InMemoryMatchResult.QUALIFIER); + } } if (hasChain(theAndOrParams)) { diff --git a/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/matcher/InMemoryResourceMatcherR5Test.java b/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/matcher/InMemoryResourceMatcherR5Test.java index e51b0c69a01..58f0cd714ef 100644 --- a/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/matcher/InMemoryResourceMatcherR5Test.java +++ b/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/matcher/InMemoryResourceMatcherR5Test.java @@ -9,7 +9,9 @@ import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry; import ca.uhn.fhir.model.primitive.BaseDateTimeDt; import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum; import ca.uhn.fhir.rest.param.ParamPrefixEnum; +import ca.uhn.fhir.rest.param.TokenParamModifier; import org.hl7.fhir.r5.model.BaseDateTimeType; +import org.hl7.fhir.r5.model.CodeableConcept; import org.hl7.fhir.r5.model.DateTimeType; import org.hl7.fhir.r5.model.Observation; import org.junit.Before; @@ -27,6 +29,7 @@ import java.util.Date; import static org.junit.Assert.*; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; @RunWith(SpringRunner.class) @@ -34,6 +37,7 @@ public class InMemoryResourceMatcherR5Test { public static final String OBSERVATION_DATE = "1970-10-17"; private static final String EARLY_DATE = "1965-08-09"; private static final String LATE_DATE = "2000-06-29"; + public static final String OBSERVATION_CODE = "MATCH"; @Autowired private @@ -64,24 +68,49 @@ public class InMemoryResourceMatcherR5Test { @Before public void before() { - RuntimeSearchParam searchParams = new RuntimeSearchParam(null, null, null, null, "Observation.effective", RestSearchParameterTypeEnum.DATE, null, null, null, RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE); - when(mySearchParamRegistry.getSearchParamByName(any(), any())).thenReturn(searchParams); - when(mySearchParamRegistry.getActiveSearchParam("Observation", "date")).thenReturn(searchParams); + RuntimeSearchParam dateSearchParam = new RuntimeSearchParam(null, null, null, null, "Observation.effective", RestSearchParameterTypeEnum.DATE, null, null, null, RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE); + when(mySearchParamRegistry.getSearchParamByName(any(), eq("date"))).thenReturn(dateSearchParam); + when(mySearchParamRegistry.getActiveSearchParam("Observation", "date")).thenReturn(dateSearchParam); + + RuntimeSearchParam codeSearchParam = new RuntimeSearchParam(null, null, null, null, "Observation.code", RestSearchParameterTypeEnum.TOKEN, null, null, null, RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE); + when(mySearchParamRegistry.getSearchParamByName(any(), eq("code"))).thenReturn(codeSearchParam); + when(mySearchParamRegistry.getActiveSearchParam("Observation", "code")).thenReturn(codeSearchParam); + + RuntimeSearchParam encSearchParam = new RuntimeSearchParam(null, null, null, null, "Observation.encounter", RestSearchParameterTypeEnum.REFERENCE, null, null, null, RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE); + when(mySearchParamRegistry.getSearchParamByName(any(), eq("encounter"))).thenReturn(encSearchParam); + when(mySearchParamRegistry.getActiveSearchParam("Observation", "encounter")).thenReturn(encSearchParam); + myObservation = new Observation(); myObservation.setEffective(new DateTimeType(OBSERVATION_DATE)); + CodeableConcept codeableConcept = new CodeableConcept(); + codeableConcept.addCoding().setCode(OBSERVATION_CODE); + myObservation.setCode(codeableConcept); mySearchParams = extractDateSearchParam(myObservation); } - @Test - public void testDateUnsupportedOps() { - testDateUnsupportedOp(ParamPrefixEnum.APPROXIMATE); - testDateUnsupportedOp(ParamPrefixEnum.STARTS_AFTER); - testDateUnsupportedOp(ParamPrefixEnum.ENDS_BEFORE); - testDateUnsupportedOp(ParamPrefixEnum.NOT_EQUAL); + public void testUnsupportedChained() { + InMemoryMatchResult result = myInMemoryResourceMatcher.match("encounter.class=FOO", myObservation, mySearchParams); + assertFalse(result.supported()); + assertEquals("Parameter: Reason: Chained parameters are not supported", result.getUnsupportedReason()); } - private void testDateUnsupportedOp(ParamPrefixEnum theOperator) { + @Test + public void testUnsupportedNot() { + InMemoryMatchResult result = myInMemoryResourceMatcher.match("code" + TokenParamModifier.NOT.getValue() + "=" + OBSERVATION_CODE, myObservation, mySearchParams); + assertFalse(result.supported()); + assertEquals("Parameter: Reason: Qualified parameter not supported", result.getUnsupportedReason()); + } + + @Test + public void testDateUnsupportedDateOps() { + testDateUnsupportedDateOp(ParamPrefixEnum.APPROXIMATE); + testDateUnsupportedDateOp(ParamPrefixEnum.STARTS_AFTER); + testDateUnsupportedDateOp(ParamPrefixEnum.ENDS_BEFORE); + testDateUnsupportedDateOp(ParamPrefixEnum.NOT_EQUAL); + } + + private void testDateUnsupportedDateOp(ParamPrefixEnum theOperator) { InMemoryMatchResult result = myInMemoryResourceMatcher.match("date=" + theOperator.getValue() + OBSERVATION_DATE, myObservation, mySearchParams); assertFalse(result.supported()); assertEquals("Parameter: Reason: The prefix " + theOperator + " is not supported for param type DATE", result.getUnsupportedReason());