Add support for _lastUpdated parameter in client and JPA server
This commit is contained in:
parent
886fa76ef2
commit
e8c75c5a45
|
@ -16,6 +16,7 @@ import ca.uhn.fhir.model.dstu2.resource.OperationOutcome.Issue;
|
|||
import ca.uhn.fhir.model.dstu2.resource.Organization;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Parameters;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Provenance;
|
||||
import ca.uhn.fhir.model.dstu2.valueset.AdministrativeGenderEnum;
|
||||
import ca.uhn.fhir.model.dstu2.valueset.IssueSeverityEnum;
|
||||
import ca.uhn.fhir.model.primitive.DateDt;
|
||||
|
@ -25,6 +26,7 @@ import ca.uhn.fhir.rest.api.MethodOutcome;
|
|||
import ca.uhn.fhir.rest.client.IGenericClient;
|
||||
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
||||
import ca.uhn.fhir.rest.method.SearchStyleEnum;
|
||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||
|
||||
public class GenericClientExample {
|
||||
|
@ -268,6 +270,8 @@ public class GenericClientExample {
|
|||
.where(Patient.BIRTHDATE.beforeOrEquals().day("2012-01-22"))
|
||||
.and(Patient.BIRTHDATE.after().day("2011-01-01"))
|
||||
.include(Patient.INCLUDE_ORGANIZATION)
|
||||
.revInclude(Provenance.INCLUDE_TARGET)
|
||||
.lastUpdated(new DateRangeParam("2011-01-01", null))
|
||||
.sort().ascending(Patient.BIRTHDATE)
|
||||
.sort().descending(Patient.NAME).limitTo(123)
|
||||
.returnBundle(Bundle.class)
|
||||
|
|
|
@ -445,7 +445,7 @@ public abstract class BaseDateTimeDt extends BasePrimitive<Date> {
|
|||
* @throws DataFormatException
|
||||
*/
|
||||
public void setValue(Date theValue, TemporalPrecisionEnum thePrecision) throws DataFormatException {
|
||||
clearTimeZone();
|
||||
setTimeZone(TimeZone.getDefault());
|
||||
myPrecision = thePrecision;
|
||||
super.setValue(theValue);
|
||||
}
|
||||
|
|
|
@ -120,6 +120,8 @@ import ca.uhn.fhir.rest.method.SearchStyleEnum;
|
|||
import ca.uhn.fhir.rest.method.TransactionMethodBinding;
|
||||
import ca.uhn.fhir.rest.method.ValidateMethodBindingDstu1;
|
||||
import ca.uhn.fhir.rest.method.ValidateMethodBindingDstu2;
|
||||
import ca.uhn.fhir.rest.param.DateParam;
|
||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory;
|
||||
|
@ -634,7 +636,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Bundle invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
|
||||
public Bundle invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws BaseServerResponseException {
|
||||
EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType);
|
||||
if (respType == null) {
|
||||
throw NonFhirResponseException.newInstance(theResponseStatusCode, theResponseMimeType, theResponseReader);
|
||||
|
@ -1468,6 +1470,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
private Class<? extends IBaseBundle> myReturnBundleType;
|
||||
private List<Include> myRevInclude = new ArrayList<Include>();
|
||||
private SearchStyleEnum mySearchStyle;
|
||||
private DateRangeParam myLastUpdated;
|
||||
private List<SortInternal> mySort = new ArrayList<SortInternal>();
|
||||
|
||||
public SearchInternal() {
|
||||
|
@ -1507,6 +1510,12 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
if (myParamLimit != null) {
|
||||
addParam(params, Constants.PARAM_COUNT, Integer.toString(myParamLimit));
|
||||
}
|
||||
|
||||
if (myLastUpdated != null) {
|
||||
for (DateParam next : myLastUpdated.getValuesAsQueryTokens()) {
|
||||
addParam(params, Constants.PARAM_LASTUPDATED, next.getValueAsQueryToken());
|
||||
}
|
||||
}
|
||||
|
||||
if (myReturnBundleType == null && myContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU2_HL7ORG)) {
|
||||
throw new IllegalArgumentException("When using the client with HL7.org structures, you must specify " + "the bundle return type for the client by adding \".returnBundle(org.hl7.fhir.instance.model.Bundle.class)\" to your search method call before the \".execute()\" method");
|
||||
|
@ -1612,6 +1621,12 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IQuery lastUpdated(DateRangeParam theLastUpdated) {
|
||||
myLastUpdated = theLastUpdated;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
|
|
|
@ -24,6 +24,7 @@ import org.hl7.fhir.instance.model.api.IBaseBundle;
|
|||
|
||||
import ca.uhn.fhir.model.api.Include;
|
||||
import ca.uhn.fhir.rest.method.SearchStyleEnum;
|
||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||
|
||||
public interface IQuery<T> extends IClientExecutable<IQuery<T>, T>, IBaseQuery<IQuery<T>> {
|
||||
|
||||
|
@ -50,10 +51,17 @@ public interface IQuery<T> extends IClientExecutable<IQuery<T>, T>, IBaseQuery<I
|
|||
/**
|
||||
* Add a "_revinclude" specification
|
||||
*
|
||||
* @since 1.0
|
||||
* @since HAPI FHIR 1.0 - Note that option was added to FHIR itself in DSTU2
|
||||
*/
|
||||
IQuery<T> revInclude(Include theIncludeTarget);
|
||||
|
||||
/**
|
||||
* Add a "_lastUpdated" specification
|
||||
*
|
||||
* @since HAPI FHIR 1.1 - Note that option was added to FHIR itself in DSTU2
|
||||
*/
|
||||
IQuery<T> lastUpdated(DateRangeParam theLastUpdated);
|
||||
|
||||
/**
|
||||
* Request that the client return the specified bundle type, e.g. <code>org.hl7.fhir.instance.model.Bundle.class</code>
|
||||
* or <code>ca.uhn.fhir.model.dstu2.resource.Bundle.class</code>
|
||||
|
|
|
@ -60,6 +60,14 @@ public class DateParam extends DateTimeDt implements IQueryParameterType, IQuery
|
|||
setValueAsString(theDate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public DateParam(QuantityCompararatorEnum theComparator, DateTimeDt theDate) {
|
||||
myComparator = theComparator;
|
||||
setValueAsString(theDate != null ? theDate.getValueAsString() : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor which takes a complete [qualifier]{date} string.
|
||||
*
|
||||
|
|
|
@ -26,6 +26,7 @@ import java.util.List;
|
|||
|
||||
import ca.uhn.fhir.model.api.IQueryParameterAnd;
|
||||
import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum;
|
||||
import ca.uhn.fhir.model.primitive.DateTimeDt;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import ca.uhn.fhir.rest.method.QualifiedParamList;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
|
@ -48,15 +49,29 @@ public class DateRangeParam implements IQueryParameterAnd<DateParam> {
|
|||
*
|
||||
* @param theLowerBound
|
||||
* A qualified date param representing the lower date bound (optionally may include time), e.g.
|
||||
* "2011-02-22" or "2011-02-22T13:12:00". Will be treated inclusively.
|
||||
* "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
|
||||
* @param theUpperBound
|
||||
* A qualified date param representing the upper date bound (optionally may include time), e.g.
|
||||
* "2011-02-22" or "2011-02-22T13:12:00". Will be treated inclusively.
|
||||
* "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
|
||||
*/
|
||||
public DateRangeParam(Date theLowerBound, Date theUpperBound) {
|
||||
setRangeFromDatesInclusive(theLowerBound, theUpperBound);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor which takes two Dates representing the lower and upper bounds of the range (inclusive on both ends)
|
||||
*
|
||||
* @param theLowerBound
|
||||
* A qualified date param representing the lower date bound (optionally may include time), e.g.
|
||||
* "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
|
||||
* @param theUpperBound
|
||||
* A qualified date param representing the upper date bound (optionally may include time), e.g.
|
||||
* "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
|
||||
*/
|
||||
public DateRangeParam(DateTimeDt theLowerBound, DateTimeDt theUpperBound) {
|
||||
setRangeFromDatesInclusive(theLowerBound, theUpperBound);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the range from a single date param. If theDateParam has no qualifier, treats it as the lower and upper bound
|
||||
* (e.g. 2011-01-02 would match any time on that day). If theDateParam has a qualifier, treats it as either the
|
||||
|
@ -96,10 +111,10 @@ public class DateRangeParam implements IQueryParameterAnd<DateParam> {
|
|||
*
|
||||
* @param theLowerBound
|
||||
* An unqualified date param representing the lower date bound (optionally may include time), e.g.
|
||||
* "2011-02-22" or "2011-02-22T13:12:00"
|
||||
* "2011-02-22" or "2011-02-22T13:12:00Z". Either theLowerBound or theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
|
||||
* @param theUpperBound
|
||||
* An unqualified date param representing the upper date bound (optionally may include time), e.g.
|
||||
* "2011-02-22" or "2011-02-22T13:12:00"
|
||||
* "2011-02-22" or "2011-02-22T13:12:00Z". Either theLowerBound or theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
|
||||
*/
|
||||
public DateRangeParam(String theLowerBound, String theUpperBound) {
|
||||
setRangeFromDatesInclusive(theLowerBound, theUpperBound);
|
||||
|
@ -177,14 +192,14 @@ public class DateRangeParam implements IQueryParameterAnd<DateParam> {
|
|||
*
|
||||
* @param theLowerBound
|
||||
* A qualified date param representing the lower date bound (optionally may include time), e.g.
|
||||
* "2011-02-22" or "2011-02-22T13:12:00". Will be treated inclusively.
|
||||
* "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
|
||||
* @param theUpperBound
|
||||
* A qualified date param representing the upper date bound (optionally may include time), e.g.
|
||||
* "2011-02-22" or "2011-02-22T13:12:00". Will be treated inclusively.
|
||||
* "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
|
||||
*/
|
||||
public void setRangeFromDatesInclusive(Date theLowerBound, Date theUpperBound) {
|
||||
myLowerBound = new DateParam(QuantityCompararatorEnum.GREATERTHAN_OR_EQUALS, theLowerBound);
|
||||
myUpperBound = new DateParam(QuantityCompararatorEnum.LESSTHAN_OR_EQUALS, theUpperBound);
|
||||
myLowerBound = theLowerBound != null ? new DateParam(QuantityCompararatorEnum.GREATERTHAN_OR_EQUALS, theLowerBound) : null;
|
||||
myUpperBound = theUpperBound != null ? new DateParam(QuantityCompararatorEnum.LESSTHAN_OR_EQUALS, theUpperBound) : null;
|
||||
validateAndThrowDataFormatExceptionIfInvalid();
|
||||
}
|
||||
|
||||
|
@ -193,14 +208,30 @@ public class DateRangeParam implements IQueryParameterAnd<DateParam> {
|
|||
*
|
||||
* @param theLowerBound
|
||||
* A qualified date param representing the lower date bound (optionally may include time), e.g.
|
||||
* "2011-02-22" or "2011-02-22T13:12:00". Will be treated inclusively.
|
||||
* "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
|
||||
* @param theUpperBound
|
||||
* A qualified date param representing the upper date bound (optionally may include time), e.g.
|
||||
* "2011-02-22" or "2011-02-22T13:12:00". Will be treated inclusively.
|
||||
* "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
|
||||
*/
|
||||
public void setRangeFromDatesInclusive(DateTimeDt theLowerBound, DateTimeDt theUpperBound) {
|
||||
myLowerBound = theLowerBound != null ? new DateParam(QuantityCompararatorEnum.GREATERTHAN_OR_EQUALS, theLowerBound) : null;
|
||||
myUpperBound = theUpperBound != null ? new DateParam(QuantityCompararatorEnum.LESSTHAN_OR_EQUALS, theUpperBound) : null;
|
||||
validateAndThrowDataFormatExceptionIfInvalid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the range from a pair of dates, inclusive on both ends
|
||||
*
|
||||
* @param theLowerBound
|
||||
* A qualified date param representing the lower date bound (optionally may include time), e.g.
|
||||
* "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
|
||||
* @param theUpperBound
|
||||
* A qualified date param representing the upper date bound (optionally may include time), e.g.
|
||||
* "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
|
||||
*/
|
||||
public void setRangeFromDatesInclusive(String theLowerBound, String theUpperBound) {
|
||||
myLowerBound = new DateParam(QuantityCompararatorEnum.GREATERTHAN_OR_EQUALS, theLowerBound);
|
||||
myUpperBound = new DateParam(QuantityCompararatorEnum.LESSTHAN_OR_EQUALS, theUpperBound);
|
||||
myLowerBound = theLowerBound != null ? new DateParam(QuantityCompararatorEnum.GREATERTHAN_OR_EQUALS, theLowerBound) : null;
|
||||
myUpperBound = theUpperBound != null ? new DateParam(QuantityCompararatorEnum.LESSTHAN_OR_EQUALS, theUpperBound) : null;
|
||||
validateAndThrowDataFormatExceptionIfInvalid();
|
||||
}
|
||||
|
||||
|
|
|
@ -96,6 +96,7 @@ public class Constants {
|
|||
public static final String PARAM_FORMAT = "_format";
|
||||
public static final String PARAM_HISTORY = "_history";
|
||||
public static final String PARAM_INCLUDE = "_include";
|
||||
public static final String PARAM_LASTUPDATED = "_lastUpdated";
|
||||
public static final String PARAM_NARRATIVE = "_narrative";
|
||||
public static final String PARAM_PAGINGACTION = "_getpages";
|
||||
public static final String PARAM_PAGINGOFFSET = "_getpagesoffset";
|
||||
|
|
|
@ -98,6 +98,8 @@ import ca.uhn.fhir.parser.IParser;
|
|||
import ca.uhn.fhir.rest.method.MethodUtil;
|
||||
import ca.uhn.fhir.rest.method.QualifiedParamList;
|
||||
import ca.uhn.fhir.rest.method.RestSearchParameterTypeEnum;
|
||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
|
@ -635,7 +637,7 @@ public abstract class BaseFhirDao implements IDao {
|
|||
return ids;
|
||||
}
|
||||
|
||||
protected SearchParameterMap translateMatchUrl(String theMatchUrl, RuntimeResourceDefinition resourceDef) {
|
||||
static SearchParameterMap translateMatchUrl(String theMatchUrl, RuntimeResourceDefinition resourceDef) {
|
||||
SearchParameterMap paramMap = new SearchParameterMap();
|
||||
List<NameValuePair> parameters;
|
||||
try {
|
||||
|
@ -644,6 +646,10 @@ public abstract class BaseFhirDao implements IDao {
|
|||
throw new InvalidRequestException("Failed to parse match URL[" + theMatchUrl + "] - Error was: URL does not contain any parameters ('?' not detected)");
|
||||
}
|
||||
matchUrl = matchUrl.replace("|", "%7C");
|
||||
matchUrl = matchUrl.replace("=>=", "=%3E%3D");
|
||||
matchUrl = matchUrl.replace("=<=", "=%3C%3D");
|
||||
matchUrl = matchUrl.replace("=>", "=%3E");
|
||||
matchUrl = matchUrl.replace("=<", "=%3C");
|
||||
parameters = URLEncodedUtils.parse(new URI(matchUrl), "UTF-8");
|
||||
} catch (URISyntaxException e) {
|
||||
throw new InvalidRequestException("Failed to parse match URL[" + theMatchUrl + "] - Error was: " + e.toString());
|
||||
|
@ -669,12 +675,25 @@ public abstract class BaseFhirDao implements IDao {
|
|||
}
|
||||
|
||||
for (String nextParamName : nameToParamLists.keySet()) {
|
||||
List<QualifiedParamList> paramList = nameToParamLists.get(nextParamName);
|
||||
if (Constants.PARAM_LASTUPDATED.equals(nextParamName)) {
|
||||
if (paramList != null && paramList.size() > 0) {
|
||||
if (paramList.size() > 2) {
|
||||
throw new InvalidRequestException("Failed to parse match URL[" + theMatchUrl + "] - Can not have more than 2 " + Constants.PARAM_LASTUPDATED + " parameter repetitions");
|
||||
} else {
|
||||
DateRangeParam p1 = new DateRangeParam();
|
||||
p1.setValuesAsQueryTokens(paramList);
|
||||
paramMap.setLastUpdated(p1);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
RuntimeSearchParam paramDef = resourceDef.getSearchParam(nextParamName);
|
||||
if (paramDef == null) {
|
||||
throw new InvalidRequestException("Failed to parse match URL[" + theMatchUrl + "] - Resource type " + resourceDef.getName() + " does not have a parameter with name: " + nextParamName);
|
||||
}
|
||||
|
||||
List<QualifiedParamList> paramList = nameToParamLists.get(nextParamName);
|
||||
IQueryParameterAnd<?> param = MethodUtil.parseQueryParams(paramDef, nextParamName, paramList);
|
||||
paramMap.add(nextParamName, param);
|
||||
}
|
||||
|
|
|
@ -1620,9 +1620,34 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
|
|||
}
|
||||
}
|
||||
|
||||
final List<Long> pids;
|
||||
// Handle _lastUpdated
|
||||
DateRangeParam lu = theParams.getLastUpdated();
|
||||
if (lu != null && (lu.getLowerBoundAsInstant() != null || lu.getUpperBoundAsInstant() != null)) {
|
||||
|
||||
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<Long> cq = builder.createQuery(Long.class);
|
||||
Root<ResourceTable> from = cq.from(ResourceTable.class);
|
||||
cq.select(from.get("myId").as(Long.class));
|
||||
|
||||
Predicate predicateIds = (from.get("myId").in(loadPids));
|
||||
Predicate predicateLower = lu.getLowerBoundAsInstant() != null ? builder.greaterThanOrEqualTo(from.<Date>get("myUpdated"), lu.getLowerBoundAsInstant()) : null;
|
||||
Predicate predicateUpper = lu.getUpperBoundAsInstant() != null ? builder.lessThanOrEqualTo(from.<Date>get("myUpdated"), lu.getUpperBoundAsInstant()) : null;
|
||||
if (predicateLower != null && predicateUpper != null) {
|
||||
cq.where(predicateIds, predicateLower, predicateUpper);
|
||||
} else if (predicateLower != null) {
|
||||
cq.where(predicateIds, predicateLower);
|
||||
} else {
|
||||
cq.where(predicateIds, predicateUpper);
|
||||
}
|
||||
TypedQuery<Long> query = myEntityManager.createQuery(cq);
|
||||
loadPids.clear();
|
||||
for (Long next : query.getResultList()) {
|
||||
loadPids.add(next);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle sorting if any was provided
|
||||
final List<Long> pids;
|
||||
if (theParams.getSort() != null && isNotBlank(theParams.getSort().getParamName())) {
|
||||
List<Order> orders = new ArrayList<Order>();
|
||||
List<Predicate> predicates = new ArrayList<Predicate>();
|
||||
|
|
|
@ -34,6 +34,7 @@ import ca.uhn.fhir.model.api.IQueryParameterOr;
|
|||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.model.api.Include;
|
||||
import ca.uhn.fhir.rest.api.SortSpec;
|
||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||
|
||||
public class SearchParameterMap extends HashMap<String, List<List<? extends IQueryParameterType>>> {
|
||||
|
||||
|
@ -41,7 +42,9 @@ public class SearchParameterMap extends HashMap<String, List<List<? extends IQue
|
|||
|
||||
private Integer myCount;
|
||||
private Set<Include> myIncludes;
|
||||
private DateRangeParam myLastUpdated;
|
||||
private Set<Include> myRevIncludes;
|
||||
|
||||
private SortSpec mySort;
|
||||
|
||||
public void add(String theName, IQueryParameterAnd<?> theAnd) {
|
||||
|
@ -98,6 +101,10 @@ public class SearchParameterMap extends HashMap<String, List<List<? extends IQue
|
|||
return myIncludes;
|
||||
}
|
||||
|
||||
public DateRangeParam getLastUpdated() {
|
||||
return myLastUpdated;
|
||||
}
|
||||
|
||||
public Set<Include> getRevIncludes() {
|
||||
return myRevIncludes;
|
||||
}
|
||||
|
@ -114,6 +121,10 @@ public class SearchParameterMap extends HashMap<String, List<List<? extends IQue
|
|||
myIncludes = theIncludes;
|
||||
}
|
||||
|
||||
public void setLastUpdated(DateRangeParam theLastUpdated) {
|
||||
myLastUpdated = theLastUpdated;
|
||||
}
|
||||
|
||||
public void setRevIncludes(Set<Include> theRevIncludes) {
|
||||
myRevIncludes = theRevIncludes;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
package ca.uhn.fhir.jpa.dao;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Condition;
|
||||
|
||||
public class BaseFhirDaoTest {
|
||||
|
||||
private static FhirContext ourCtx = FhirContext.forDstu2();
|
||||
|
||||
@Test
|
||||
public void testTranslateMatchUrl() {
|
||||
SearchParameterMap match = BaseFhirDao.translateMatchUrl("Condition?subject=304&_lastUpdated=>2011-01-01T11:12:21.0000Z", ourCtx.getResourceDefinition(Condition.class));
|
||||
assertEquals("2011-01-01T11:12:21.0000Z", match.getLastUpdated().getLowerBound().getValueAsString());
|
||||
}
|
||||
|
||||
}
|
|
@ -7,6 +7,7 @@ import static org.hamcrest.Matchers.containsString;
|
|||
import static org.hamcrest.Matchers.endsWith;
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
import static org.hamcrest.Matchers.hasItem;
|
||||
import static org.hamcrest.Matchers.hasItems;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
@ -42,6 +43,7 @@ import ca.uhn.fhir.model.api.IResource;
|
|||
import ca.uhn.fhir.model.api.Include;
|
||||
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||
import ca.uhn.fhir.model.api.TagList;
|
||||
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
||||
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
|
||||
import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum;
|
||||
import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt;
|
||||
|
@ -1309,6 +1311,75 @@ public class FhirResourceDaoDstu2Test {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchLastUpdatedParam() throws InterruptedException {
|
||||
String methodName = "testSearchLastUpdatedParam";
|
||||
|
||||
int sleep = 100;
|
||||
Thread.sleep(sleep);
|
||||
|
||||
DateTimeDt beforeAny = new DateTimeDt(new Date(), TemporalPrecisionEnum.MILLI);
|
||||
IdDt id1a;
|
||||
{
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier().setSystem("urn:system").setValue("001");
|
||||
patient.addName().addFamily(methodName).addGiven("Joe");
|
||||
id1a = ourPatientDao.create(patient).getId().toUnqualifiedVersionless();
|
||||
}
|
||||
IdDt id1b;
|
||||
{
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier().setSystem("urn:system").setValue("002");
|
||||
patient.addName().addFamily(methodName + "XXXX").addGiven("Joe");
|
||||
id1b = ourPatientDao.create(patient).getId().toUnqualifiedVersionless();
|
||||
}
|
||||
|
||||
Thread.sleep(1100);
|
||||
DateTimeDt beforeR2 = new DateTimeDt(new Date(), TemporalPrecisionEnum.MILLI);
|
||||
Thread.sleep(1100);
|
||||
|
||||
IdDt id2;
|
||||
{
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier().setSystem("urn:system").setValue("002");
|
||||
patient.addName().addFamily(methodName).addGiven("John");
|
||||
id2 = ourPatientDao.create(patient).getId().toUnqualifiedVersionless();
|
||||
}
|
||||
|
||||
{
|
||||
SearchParameterMap params = new SearchParameterMap();
|
||||
List<IdDt> patients = toUnqualifiedVersionlessIds(ourPatientDao.search(params));
|
||||
assertThat(patients, hasItems(id1a, id1b, id2));
|
||||
}
|
||||
{
|
||||
SearchParameterMap params = new SearchParameterMap();
|
||||
params.setLastUpdated(new DateRangeParam(beforeAny, null));
|
||||
List<IdDt> patients = toUnqualifiedVersionlessIds(ourPatientDao.search(params));
|
||||
assertThat(patients, hasItems(id1a, id1b, id2));
|
||||
}
|
||||
{
|
||||
SearchParameterMap params = new SearchParameterMap();
|
||||
params.setLastUpdated(new DateRangeParam(beforeR2, null));
|
||||
List<IdDt> patients = toUnqualifiedVersionlessIds(ourPatientDao.search(params));
|
||||
assertThat(patients, hasItems(id2));
|
||||
assertThat(patients, not(hasItems(id1a, id1b)));
|
||||
}
|
||||
{
|
||||
SearchParameterMap params = new SearchParameterMap();
|
||||
params.setLastUpdated(new DateRangeParam(beforeAny, beforeR2));
|
||||
List<IdDt> patients = toUnqualifiedVersionlessIds(ourPatientDao.search(params));
|
||||
assertThat(patients.toString(), patients, not(hasItems(id2)));
|
||||
assertThat(patients.toString(), patients, (hasItems(id1a, id1b)));
|
||||
}
|
||||
{
|
||||
SearchParameterMap params = new SearchParameterMap();
|
||||
params.setLastUpdated(new DateRangeParam(null, beforeR2));
|
||||
List<IdDt> patients = toUnqualifiedVersionlessIds(ourPatientDao.search(params));
|
||||
assertThat(patients, (hasItems(id1a, id1b)));
|
||||
assertThat(patients, not(hasItems(id2)));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchNameParam() {
|
||||
IdDt id1;
|
||||
|
|
|
@ -4,6 +4,7 @@ import static org.hamcrest.Matchers.containsInAnyOrder;
|
|||
import static org.hamcrest.Matchers.containsInRelativeOrder;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
import static org.hamcrest.Matchers.hasItems;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
@ -54,6 +55,7 @@ import ca.uhn.fhir.model.api.Bundle;
|
|||
import ca.uhn.fhir.model.api.BundleEntry;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
||||
import ca.uhn.fhir.model.dstu.resource.Device;
|
||||
import ca.uhn.fhir.model.dstu.resource.Practitioner;
|
||||
import ca.uhn.fhir.model.dstu2.composite.PeriodDt;
|
||||
|
@ -74,6 +76,7 @@ import ca.uhn.fhir.model.dstu2.valueset.EncounterClassEnum;
|
|||
import ca.uhn.fhir.model.dstu2.valueset.EncounterStateEnum;
|
||||
import ca.uhn.fhir.model.dstu2.valueset.HTTPVerbEnum;
|
||||
import ca.uhn.fhir.model.primitive.DateDt;
|
||||
import ca.uhn.fhir.model.primitive.DateTimeDt;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.primitive.UnsignedIntDt;
|
||||
import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
|
||||
|
@ -85,6 +88,7 @@ import ca.uhn.fhir.rest.client.ServerValidationModeEnum;
|
|||
import ca.uhn.fhir.rest.gclient.IQuery;
|
||||
import ca.uhn.fhir.rest.gclient.StringClientParam;
|
||||
import ca.uhn.fhir.rest.gclient.TokenClientParam;
|
||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
|
@ -1102,5 +1106,102 @@ public class ResourceProviderDstu2Test {
|
|||
ourHttpClient = builder.build();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testSearchLastUpdatedParamRp() throws InterruptedException {
|
||||
String methodName = "testSearchLastUpdatedParamRp";
|
||||
|
||||
int sleep = 100;
|
||||
Thread.sleep(sleep);
|
||||
|
||||
DateTimeDt beforeAny = new DateTimeDt(new Date(), TemporalPrecisionEnum.MILLI);
|
||||
IdDt id1a;
|
||||
{
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier().setSystem("urn:system").setValue("001");
|
||||
patient.addName().addFamily(methodName).addGiven("Joe");
|
||||
id1a = ourClient.create().resource(patient).execute().getId().toUnqualifiedVersionless();
|
||||
}
|
||||
IdDt id1b;
|
||||
{
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier().setSystem("urn:system").setValue("002");
|
||||
patient.addName().addFamily(methodName + "XXXX").addGiven("Joe");
|
||||
id1b = ourClient.create().resource(patient).execute().getId().toUnqualifiedVersionless();
|
||||
}
|
||||
|
||||
Thread.sleep(1100);
|
||||
DateTimeDt beforeR2 = new DateTimeDt(new Date(), TemporalPrecisionEnum.MILLI);
|
||||
Thread.sleep(1100);
|
||||
|
||||
IdDt id2;
|
||||
{
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier().setSystem("urn:system").setValue("002");
|
||||
patient.addName().addFamily(methodName).addGiven("John");
|
||||
id2 = ourClient.create().resource(patient).execute().getId().toUnqualifiedVersionless();
|
||||
}
|
||||
|
||||
{
|
||||
//@formatter:off
|
||||
Bundle found = ourClient.search()
|
||||
.forResource(Patient.class)
|
||||
.where(Patient.NAME.matches().value("testSearchLastUpdatedParamRp"))
|
||||
.execute();
|
||||
//@formatter:on
|
||||
List<IdDt> patients = toIdListUnqualifiedVersionless(found);
|
||||
assertThat(patients, hasItems(id1a, id1b, id2));
|
||||
}
|
||||
{
|
||||
//@formatter:off
|
||||
Bundle found = ourClient.search()
|
||||
.forResource(Patient.class)
|
||||
.where(Patient.NAME.matches().value("testSearchLastUpdatedParamRp"))
|
||||
.lastUpdated(new DateRangeParam(beforeAny, null))
|
||||
.execute();
|
||||
//@formatter:on
|
||||
List<IdDt> patients = toIdListUnqualifiedVersionless(found);
|
||||
assertThat(patients, hasItems(id1a, id1b, id2));
|
||||
}
|
||||
{
|
||||
//@formatter:off
|
||||
Bundle found = ourClient.search()
|
||||
.forResource(Patient.class)
|
||||
.where(Patient.NAME.matches().value("testSearchLastUpdatedParamRp"))
|
||||
.lastUpdated(new DateRangeParam(beforeR2, null))
|
||||
.execute();
|
||||
//@formatter:on
|
||||
List<IdDt> patients = toIdListUnqualifiedVersionless(found);
|
||||
assertThat(patients, hasItems(id2));
|
||||
assertThat(patients, not(hasItems(id1a, id1b)));
|
||||
}
|
||||
{
|
||||
//@formatter:off
|
||||
Bundle found = ourClient.search()
|
||||
.forResource(Patient.class)
|
||||
.where(Patient.NAME.matches().value("testSearchLastUpdatedParamRp"))
|
||||
.lastUpdated(new DateRangeParam(beforeAny, beforeR2))
|
||||
.execute();
|
||||
//@formatter:on
|
||||
List<IdDt> patients = toIdListUnqualifiedVersionless(found);
|
||||
assertThat(patients.toString(), patients, not(hasItems(id2)));
|
||||
assertThat(patients.toString(), patients, (hasItems(id1a, id1b)));
|
||||
}
|
||||
{
|
||||
//@formatter:off
|
||||
Bundle found = ourClient.search()
|
||||
.forResource(Patient.class)
|
||||
.where(Patient.NAME.matches().value("testSearchLastUpdatedParamRp"))
|
||||
.lastUpdated(new DateRangeParam(null, beforeR2))
|
||||
.execute();
|
||||
//@formatter:on
|
||||
List<IdDt> patients = toIdListUnqualifiedVersionless(found);
|
||||
assertThat(patients, (hasItems(id1a, id1b)));
|
||||
assertThat(patients, not(hasItems(id2)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ import ca.uhn.fhir.model.primitive.StringDt;
|
|||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
|
||||
|
@ -792,6 +793,31 @@ public class GenericClientDstu2Test {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchWithLastUpdated() throws Exception {
|
||||
String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}";
|
||||
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
|
||||
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
|
||||
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
|
||||
//@formatter:off
|
||||
Bundle response = client.search()
|
||||
.forResource("Patient")
|
||||
.where(Patient.NAME.matches().value("james"))
|
||||
.lastUpdated(new DateRangeParam("2011-01-01", "2012-01-01"))
|
||||
.execute();
|
||||
//@formatter:on
|
||||
|
||||
assertEquals("http://example.com/fhir/Patient?name=james&_lastUpdated=%3E%3D2011-01-01&_lastUpdated=%3C%3D2012-01-01", capt.getValue().getURI().toString());
|
||||
assertEquals(Patient.class, response.getEntries().get(0).getResource().getClass());
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Test
|
||||
public void testSearchWithReverseInclude() throws Exception {
|
||||
|
|
|
@ -70,6 +70,9 @@ public class ${className}ResourceProvider extends JpaResourceProvider${versionCa
|
|||
#if ( $version != 'dstu' )
|
||||
@IncludeParam(reverse=true)
|
||||
Set<Include> theRevIncludes,
|
||||
@Description(shortDefinition="Only return resources which were last updated as specified by the given range")
|
||||
@OptionalParam(name="_lastUpdated")
|
||||
DateRangeParam theLastUpdated,
|
||||
#end
|
||||
|
||||
@IncludeParam(allow= {
|
||||
|
@ -105,6 +108,7 @@ public class ${className}ResourceProvider extends JpaResourceProvider${versionCa
|
|||
#end
|
||||
#if ( $version != 'dstu' )
|
||||
paramMap.setRevIncludes(theRevIncludes);
|
||||
paramMap.setLastUpdated(theLastUpdated);
|
||||
#end
|
||||
paramMap.setIncludes(theIncludes);
|
||||
paramMap.setSort(theSort);
|
||||
|
|
|
@ -111,6 +111,14 @@
|
|||
<action type="add">
|
||||
JPA server now supports ifNoneMatch in GET within a transaction request.
|
||||
</action>
|
||||
<action type="add">
|
||||
DateRangeParam now supports null values in the constructor for lower or upper bounds (but
|
||||
still not both)
|
||||
</action>
|
||||
<action type="add">
|
||||
Generic/fluent client and JPA server now both support _lastUpdated search parameter
|
||||
which was added in DSTU2
|
||||
</action>
|
||||
</release>
|
||||
<release version="1.0" date="2015-May-8">
|
||||
<action type="add">
|
||||
|
|
|
@ -178,7 +178,7 @@
|
|||
<h4>Search - Other Query Options</h4>
|
||||
<p>
|
||||
The fluent search also has methods for sorting, limiting, specifying
|
||||
JSON encoding, etc.
|
||||
JSON encoding, _include, _revinclude, _lastUpdated, etc.
|
||||
</p>
|
||||
<macro name="snippet">
|
||||
<param name="id" value="searchAdv" />
|
||||
|
|
Loading…
Reference in New Issue