diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/RestSearchParameterTypeEnum.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/RestSearchParameterTypeEnum.java
index 0bcce616947..1da52a194c6 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/RestSearchParameterTypeEnum.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/RestSearchParameterTypeEnum.java
@@ -89,7 +89,7 @@ public enum RestSearchParameterTypeEnum {
*/
HAS("string", "http://hl7.org/fhir/search-param-type"),
- /**
+ /**
* Code Value: number
*
* Search parameter SHALL be a number (a whole number, or a decimal).
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ParameterUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ParameterUtil.java
index d58758f9133..19b825ddca4 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ParameterUtil.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ParameterUtil.java
@@ -61,43 +61,43 @@ public class ParameterUtil {
* This is a utility method intended provided to help the JPA module.
*/
public static IQueryParameterAnd> parseQueryParams(FhirContext theContext, RestSearchParameterTypeEnum paramType,
- String theUnqualifiedParamName, List theParameters) {
+ String theUnqualifiedParamName, List theParameters) {
QueryParameterAndBinder binder = null;
switch (paramType) {
- case COMPOSITE:
- throw new UnsupportedOperationException();
- case DATE:
- binder = new QueryParameterAndBinder(DateAndListParam.class,
- Collections.> emptyList());
- break;
- case NUMBER:
- binder = new QueryParameterAndBinder(NumberAndListParam.class,
- Collections.> emptyList());
- break;
- case QUANTITY:
- binder = new QueryParameterAndBinder(QuantityAndListParam.class,
- Collections.> emptyList());
- break;
- case REFERENCE:
- binder = new QueryParameterAndBinder(ReferenceAndListParam.class,
- Collections.> emptyList());
- break;
- case STRING:
- binder = new QueryParameterAndBinder(StringAndListParam.class,
- Collections.> emptyList());
- break;
- case TOKEN:
- binder = new QueryParameterAndBinder(TokenAndListParam.class,
- Collections.> emptyList());
- break;
- case URI:
- binder = new QueryParameterAndBinder(UriAndListParam.class,
- Collections.> emptyList());
- break;
- case HAS:
- binder = new QueryParameterAndBinder(HasAndListParam.class,
- Collections.> emptyList());
- break;
+ case COMPOSITE:
+ throw new UnsupportedOperationException();
+ case DATE:
+ binder = new QueryParameterAndBinder(DateAndListParam.class,
+ Collections.>emptyList());
+ break;
+ case NUMBER:
+ binder = new QueryParameterAndBinder(NumberAndListParam.class,
+ Collections.>emptyList());
+ break;
+ case QUANTITY:
+ binder = new QueryParameterAndBinder(QuantityAndListParam.class,
+ Collections.>emptyList());
+ break;
+ case REFERENCE:
+ binder = new QueryParameterAndBinder(ReferenceAndListParam.class,
+ Collections.>emptyList());
+ break;
+ case STRING:
+ binder = new QueryParameterAndBinder(StringAndListParam.class,
+ Collections.>emptyList());
+ break;
+ case TOKEN:
+ binder = new QueryParameterAndBinder(TokenAndListParam.class,
+ Collections.>emptyList());
+ break;
+ case URI:
+ binder = new QueryParameterAndBinder(UriAndListParam.class,
+ Collections.>emptyList());
+ break;
+ case HAS:
+ binder = new QueryParameterAndBinder(HasAndListParam.class,
+ Collections.>emptyList());
+ break;
}
// FIXME null access
@@ -108,7 +108,7 @@ public class ParameterUtil {
* This is a utility method intended provided to help the JPA module.
*/
public static IQueryParameterAnd> parseQueryParams(FhirContext theContext, RuntimeSearchParam theParamDef,
- String theUnqualifiedParamName, List theParameters) {
+ String theUnqualifiedParamName, List theParameters) {
RestSearchParameterTypeEnum paramType = theParamDef.getParamType();
return parseQueryParams(theContext, paramType, theUnqualifiedParamName, theParameters);
}
@@ -126,14 +126,14 @@ public class ParameterUtil {
for (int i = 0; i < theValue.length(); i++) {
char next = theValue.charAt(i);
switch (next) {
- case '$':
- case ',':
- case '|':
- case '\\':
- b.append('\\');
- break;
- default:
- break;
+ case '$':
+ case ',':
+ case '|':
+ case '\\':
+ b.append('\\');
+ break;
+ default:
+ break;
}
b.append(next);
}
@@ -207,7 +207,7 @@ public class ParameterUtil {
public static boolean isBindableIntegerType(Class> theClass) {
return Integer.class.isAssignableFrom(theClass)
- || IPrimitiveType.class.isAssignableFrom(theClass);
+ || IPrimitiveType.class.isAssignableFrom(theClass);
}
public static String escapeAndJoinOrList(Collection theValues) {
@@ -236,7 +236,7 @@ public class ParameterUtil {
if (value.charAt(0) == '"') {
eTagVersion = value.substring(1, value.length() - 1);
} else if (value.length() > 3 && value.charAt(0) == 'W' && value.charAt(1) == '/'
- && value.charAt(2) == '"') {
+ && value.charAt(2) == '"') {
eTagVersion = value.substring(3, value.length() - 1);
} else {
eTagVersion = value;
@@ -262,16 +262,16 @@ public class ParameterUtil {
@Override
public void setValuesAsQueryTokens(FhirContext theContext, String theParamName,
- QualifiedParamList theParameters) {
+ QualifiedParamList theParameters) {
if (theParameters.isEmpty()) {
return;
}
if (theParameters.size() > 1) {
throw new IllegalArgumentException(
- "Type " + theParam.getClass().getCanonicalName() + " does not support multiple values");
+ "Type " + theParam.getClass().getCanonicalName() + " does not support multiple values");
}
theParam.setValueAsQueryToken(theContext, theParamName, theParameters.getQualifier(),
- theParameters.get(0));
+ theParameters.get(0));
}
};
}
@@ -351,13 +351,13 @@ public class ParameterUtil {
b.append(next);
} else {
switch (theValue.charAt(i + 1)) {
- case '$':
- case ',':
- case '|':
- case '\\':
- continue;
- default:
- b.append(next);
+ case '$':
+ case ',':
+ case '|':
+ case '\\':
+ continue;
+ default:
+ b.append(next);
}
}
} else {
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java
index b3ccb922442..1e3f16fcfd6 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java
@@ -39,6 +39,7 @@ import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
import ca.uhn.fhir.jpa.searchparam.ResourceMetaParams;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
+import ca.uhn.fhir.jpa.searchparam.util.SourceParam;
import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
import ca.uhn.fhir.jpa.term.VersionIndependentConcept;
import ca.uhn.fhir.jpa.util.*;
@@ -861,19 +862,9 @@ public class SearchBuilder implements ISearchBuilder {
List codePredicates = new ArrayList<>();
for (IQueryParameterType nextParameter : theList) {
- String nextParamValue = nextParameter.getValueAsQueryToken(myContext);
- int lastHashValueIndex = nextParamValue.lastIndexOf('#');
- String sourceUri;
- String requestId;
- if (lastHashValueIndex == -1) {
- sourceUri = nextParamValue;
- requestId = null;
- } else {
- sourceUri = nextParamValue.substring(0, lastHashValueIndex);
- requestId = nextParamValue.substring(lastHashValueIndex + 1);
- }
- requestId = left(requestId, Constants.REQUEST_ID_LENGTH);
-
+ SourceParam sourceParameter = new SourceParam(nextParameter.getValueAsQueryToken(myContext));
+ String sourceUri = sourceParameter.getSourceUri();
+ String requestId = sourceParameter.getRequestId();
Predicate sourceUriPredicate = myBuilder.equal(join.get("mySourceUri"), sourceUri);
Predicate requestIdPredicate = myBuilder.equal(join.get("myRequestId"), requestId);
if (isNotBlank(sourceUri) && isNotBlank(requestId)) {
diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/MatchUrlService.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/MatchUrlService.java
index be810cca188..09a260d5f4d 100644
--- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/MatchUrlService.java
+++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/MatchUrlService.java
@@ -93,16 +93,10 @@ public class MatchUrlService {
paramMap.setLastUpdated(p1);
}
}
- continue;
- }
-
- if (Constants.PARAM_HAS.equals(nextParamName)) {
+ } else if (Constants.PARAM_HAS.equals(nextParamName)) {
IQueryParameterAnd> param = ParameterUtil.parseQueryParams(myContext, RestSearchParameterTypeEnum.HAS, nextParamName, paramList);
paramMap.add(nextParamName, param);
- continue;
- }
-
- if (Constants.PARAM_COUNT.equals(nextParamName)) {
+ } else if (Constants.PARAM_COUNT.equals(nextParamName)) {
if (paramList.size() > 0 && paramList.get(0).size() > 0) {
String intString = paramList.get(0).get(0);
try {
@@ -111,16 +105,16 @@ public class MatchUrlService {
throw new InvalidRequestException("Invalid " + Constants.PARAM_COUNT + " value: " + intString);
}
}
- continue;
- }
-
- if (ResourceMetaParams.RESOURCE_META_PARAMS.containsKey(nextParamName)) {
+ } else if (ResourceMetaParams.RESOURCE_META_PARAMS.containsKey(nextParamName)) {
if (isNotBlank(paramList.get(0).getQualifier()) && paramList.get(0).getQualifier().startsWith(".")) {
throw new InvalidRequestException("Invalid parameter chain: " + nextParamName + paramList.get(0).getQualifier());
}
IQueryParameterAnd> type = newInstanceAnd(nextParamName);
type.setValuesAsQueryTokens(myContext, nextParamName, (paramList));
paramMap.add(nextParamName, type);
+ } else if (Constants.PARAM_SOURCE.equals(nextParamName)) {
+ IQueryParameterAnd> param = ParameterUtil.parseQueryParams(myContext, RestSearchParameterTypeEnum.TOKEN, nextParamName, paramList);
+ paramMap.add(nextParamName, param);
} else if (nextParamName.startsWith("_")) {
// ignore these since they aren't search params (e.g. _sort)
} else {
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 5454a44bba7..e7c509de7eb 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
@@ -27,6 +27,7 @@ import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.searchparam.extractor.ResourceIndexedSearchParams;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
+import ca.uhn.fhir.jpa.searchparam.util.SourceParam;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
@@ -35,6 +36,7 @@ import ca.uhn.fhir.rest.param.ParamPrefixEnum;
import ca.uhn.fhir.rest.param.ReferenceParam;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
+import ca.uhn.fhir.util.MetaUtil;
import ca.uhn.fhir.util.UrlUtil;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource;
@@ -125,7 +127,6 @@ public class InMemoryResourceMatcher {
switch (theParamName) {
case IAnyResource.SP_RES_ID:
-
return InMemoryMatchResult.fromBoolean(matchIdsAndOr(theAndOrParams, theResource));
case IAnyResource.SP_RES_LANGUAGE:
@@ -133,16 +134,38 @@ public class InMemoryResourceMatcher {
case Constants.PARAM_TAG:
case Constants.PARAM_PROFILE:
case Constants.PARAM_SECURITY:
-
return InMemoryMatchResult.unsupportedFromParameterAndReason(theParamName, InMemoryMatchResult.PARAM);
-
+ case Constants.PARAM_SOURCE:
+ return InMemoryMatchResult.fromBoolean(matchSourcesAndOr(theAndOrParams, theResource));
default:
-
-
return matchResourceParam(theParamName, theAndOrParams, theSearchParams, resourceName, paramDef);
}
}
+ private boolean matchSourcesAndOr(List> theAndOrParams, IBaseResource theResource) {
+ if (theResource == null) {
+ return true;
+ }
+ return theAndOrParams.stream().allMatch(nextAnd -> matchSourcesOr(nextAnd, theResource));
+ }
+
+ private boolean matchSourcesOr(List theOrParams, IBaseResource theResource) {
+ return theOrParams.stream().anyMatch(param -> matchSource(param, theResource));
+ }
+
+ private boolean matchSource(IQueryParameterType theSourceParam, IBaseResource theResource) {
+ SourceParam paramSource = new SourceParam(theSourceParam.getValueAsQueryToken(myFhirContext));
+ SourceParam resourceSource = new SourceParam(MetaUtil.getSource(myFhirContext, theResource.getMeta()));
+ boolean matches = true;
+ if (paramSource.getSourceUri() != null) {
+ matches = paramSource.getSourceUri().equals(resourceSource.getSourceUri());
+ }
+ if (paramSource.getRequestId() != null) {
+ matches &= paramSource.getRequestId().equals(resourceSource.getRequestId());
+ }
+ return matches;
+ }
+
private boolean matchIdsAndOr(List> theAndOrParams, IBaseResource theResource) {
if (theResource == null) {
return true;
@@ -151,9 +174,6 @@ public class InMemoryResourceMatcher {
}
private boolean matchIdsOr(List theOrParams, IBaseResource theResource) {
- if (theResource == null) {
- return true;
- }
return theOrParams.stream().anyMatch(param -> param instanceof StringParam && matchId(((StringParam) param).getValue(), theResource.getIdElement()));
}
diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/SourceParam.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/SourceParam.java
new file mode 100644
index 00000000000..61c082df9b6
--- /dev/null
+++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/SourceParam.java
@@ -0,0 +1,62 @@
+package ca.uhn.fhir.jpa.searchparam.util;
+
+import ca.uhn.fhir.rest.api.Constants;
+
+import static org.apache.commons.lang3.StringUtils.left;
+
+/*
+ * #%L
+ * HAPI FHIR - Core Library
+ * %%
+ * Copyright (C) 2014 - 2019 University Health Network
+ * %%
+ * 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%
+ */
+
+/**
+ * Model of the _source parameter
+ */
+public class SourceParam {
+
+ private static final long serialVersionUID = 1L;
+ private final String myParameterValue;
+ private final String mySourceUri;
+ private final String myRequestId;
+
+ public SourceParam(String theParameterValue) {
+ myParameterValue = theParameterValue;
+ String requestId;
+ int lastHashValueIndex = theParameterValue.lastIndexOf('#');
+ if (lastHashValueIndex == -1) {
+ mySourceUri = theParameterValue;
+ requestId = null;
+ } else {
+ if (lastHashValueIndex == 0) {
+ mySourceUri = null;
+ } else {
+ mySourceUri = theParameterValue.substring(0, lastHashValueIndex);
+ }
+ requestId = theParameterValue.substring(lastHashValueIndex + 1);
+ }
+ myRequestId = left(requestId, Constants.REQUEST_ID_LENGTH);
+ }
+
+ public String getSourceUri() {
+ return mySourceUri;
+ }
+
+ public String getRequestId() {
+ return myRequestId;
+ }
+}
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 58f0cd714ef..b151af6232c 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
@@ -7,6 +7,7 @@ import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
import ca.uhn.fhir.jpa.searchparam.extractor.ResourceIndexedSearchParams;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.model.primitive.BaseDateTimeDt;
+import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
import ca.uhn.fhir.rest.param.ParamPrefixEnum;
import ca.uhn.fhir.rest.param.TokenParamModifier;
@@ -38,10 +39,12 @@ public class InMemoryResourceMatcherR5Test {
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";
+ private static final String SOURCE_URI = "urn:source:0";
+ private static final String REQUEST_ID = "a_request_id";
+ private static final String TEST_SOURCE = SOURCE_URI + "#" + REQUEST_ID;
@Autowired
- private
- InMemoryResourceMatcher myInMemoryResourceMatcher;
+ private InMemoryResourceMatcher myInMemoryResourceMatcher;
@MockBean
ISearchParamRegistry mySearchParamRegistry;
@@ -81,6 +84,7 @@ public class InMemoryResourceMatcherR5Test {
when(mySearchParamRegistry.getActiveSearchParam("Observation", "encounter")).thenReturn(encSearchParam);
myObservation = new Observation();
+ myObservation.getMeta().setSource(TEST_SOURCE);
myObservation.setEffective(new DateTimeType(OBSERVATION_DATE));
CodeableConcept codeableConcept = new CodeableConcept();
codeableConcept.addCoding().setCode(OBSERVATION_CODE);
@@ -88,6 +92,26 @@ public class InMemoryResourceMatcherR5Test {
mySearchParams = extractDateSearchParam(myObservation);
}
+ @Test
+ public void testSupportedSource() {
+ {
+ InMemoryMatchResult result = myInMemoryResourceMatcher.match(Constants.PARAM_SOURCE + "=" + TEST_SOURCE, myObservation, mySearchParams);
+ assertTrue(result.matched());
+ }
+ {
+ InMemoryMatchResult result = myInMemoryResourceMatcher.match(Constants.PARAM_SOURCE + "=" + SOURCE_URI, myObservation, mySearchParams);
+ assertTrue(result.matched());
+ }
+ {
+ InMemoryMatchResult result = myInMemoryResourceMatcher.match(Constants.PARAM_SOURCE + "=" + REQUEST_ID, myObservation, mySearchParams);
+ assertFalse(result.matched());
+ }
+ {
+ InMemoryMatchResult result = myInMemoryResourceMatcher.match(Constants.PARAM_SOURCE + "=#" + REQUEST_ID, myObservation, mySearchParams);
+ assertTrue(result.matched());
+ }
+ }
+
@Test
public void testUnsupportedChained() {
InMemoryMatchResult result = myInMemoryResourceMatcher.match("encounter.class=FOO", myObservation, mySearchParams);