Work on support for _at parameter in history operation

This commit is contained in:
James Agnew 2016-06-13 07:31:25 -05:00
parent 23550240ad
commit 1166a2ee67
64 changed files with 1391 additions and 976 deletions

View File

@ -34,6 +34,7 @@ import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.annotation.AddTags;
import ca.uhn.fhir.rest.annotation.At;
import ca.uhn.fhir.rest.annotation.ConditionalUrlParam;
import ca.uhn.fhir.rest.annotation.Count;
import ca.uhn.fhir.rest.annotation.Create;
@ -363,7 +364,11 @@ public void deletePatientConditional(@IdParam IdDt theId, @ConditionalUrlParam S
//START SNIPPET: history
@History()
public List<Patient> getPatientHistory(@IdParam IdDt theId) {
public List<Patient> getPatientHistory(
@IdParam IdDt theId,
@Since InstantDt theSince,
@At DateRangeParam theAt
) {
List<Patient> retVal = new ArrayList<Patient>();
Patient patient = new Patient();

View File

@ -21,17 +21,25 @@ package ca.uhn.fhir.context;
*/
import static org.apache.commons.lang3.StringUtils.isBlank;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Map;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseDatatype;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import ca.uhn.fhir.model.api.BasePrimitive;
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.util.CoverageIgnore;
public class RuntimePrimitiveDatatypeDefinition extends BaseRuntimeElementDefinition<IPrimitiveType<?>> implements IRuntimeDatatypeDefinition {
private Class<?> myNativeType;
private BaseRuntimeElementDefinition<?> myProfileOf;
private Class<? extends IBaseDatatype> myProfileOfType;
private boolean mySpecialization;
@ -49,6 +57,30 @@ public class RuntimePrimitiveDatatypeDefinition extends BaseRuntimeElementDefini
if (myProfileOfType.equals(IBaseDatatype.class)) {
myProfileOfType = null;
}
determineNativeType(theImplementingClass);
}
private void determineNativeType(Class<? extends IPrimitiveType<?>> theImplementingClass) {
Class<?> clazz = theImplementingClass;
while (clazz.equals(Object.class) == false) {
Type type = clazz.getGenericSuperclass();
if (type instanceof ParameterizedType) {
ParameterizedType superPt = (ParameterizedType) type;
Type rawType = superPt.getRawType();
if (rawType instanceof Class) {
Class<?> rawClass = (Class<?>) rawType;
if (rawClass.getName().endsWith(".BasePrimitive") || rawClass.getName().endsWith(".PrimitiveType")) {
Type typeVariable = superPt.getActualTypeArguments()[0];
if (typeVariable instanceof Class) {
myNativeType = (Class<?>) typeVariable;
break;
}
}
}
}
clazz = clazz.getSuperclass();
}
}
@Override
@ -56,11 +88,27 @@ public class RuntimePrimitiveDatatypeDefinition extends BaseRuntimeElementDefini
return ChildTypeEnum.PRIMITIVE_DATATYPE;
}
public Class<?> getNativeType() {
return myNativeType;
}
@Override
public Class<? extends IBaseDatatype> getProfileOf() {
return myProfileOfType;
}
@Override
public boolean isProfileOf(Class<? extends IBaseDatatype> theType) {
if (myProfileOfType != null) {
if (myProfileOfType.equals(theType)) {
return true;
} else if (myProfileOf instanceof IRuntimeDatatypeDefinition) {
return ((IRuntimeDatatypeDefinition) myProfileOf).isProfileOf(theType);
}
}
return false;
}
@Override
public boolean isSpecialization() {
return mySpecialization;
@ -85,17 +133,4 @@ public class RuntimePrimitiveDatatypeDefinition extends BaseRuntimeElementDefini
}
}
@Override
public boolean isProfileOf(Class<? extends IBaseDatatype> theType) {
if (myProfileOfType != null) {
if (myProfileOfType.equals(theType)) {
return true;
} else if (myProfileOf instanceof IRuntimeDatatypeDefinition) {
return ((IRuntimeDatatypeDefinition) myProfileOf).isProfileOf(theType);
}
}
return false;
}
}

View File

@ -105,10 +105,10 @@ public abstract class BaseDateTimeDt extends BasePrimitive<Date> {
leftPadWithZeros(cal.get(Calendar.SECOND), 2, b);
if (myPrecision.ordinal() > TemporalPrecisionEnum.SECOND.ordinal()) {
b.append('.');
b.append(myFractionalSeconds);
for (int i = myFractionalSeconds.length(); i < 3; i++) {
b.append('0');
}
b.append(myFractionalSeconds);
}
}
@ -179,6 +179,21 @@ public abstract class BaseDateTimeDt extends BasePrimitive<Date> {
return myTimeZone;
}
/**
* Returns the value of this object as a {@link GregorianCalendar}
*/
public GregorianCalendar getValueAsCalendar() {
if (getValue() == null) {
return null;
}
GregorianCalendar cal = new GregorianCalendar();
cal.setTime(getValue());
if (getTimeZone() != null) {
cal.setTimeZone(getTimeZone());
}
return cal;
}
/**
* To be implemented by subclasses to indicate whether the given precision is allowed by this type
*/
@ -212,6 +227,7 @@ public abstract class BaseDateTimeDt extends BasePrimitive<Date> {
Calendar cal = new GregorianCalendar(0, 0, 0);
cal.setTimeZone(TimeZone.getDefault());
String value = theValue;
boolean fractionalSecondsSet = false;
if (value.length() > 0 && (value.charAt(0) == ' ' || value.charAt(value.length()-1) == ' ')) {
value = value.trim();
@ -279,6 +295,7 @@ public abstract class BaseDateTimeDt extends BasePrimitive<Date> {
String millisString;
if (endIndex > 23) {
myFractionalSeconds = value.substring(20, endIndex);
fractionalSecondsSet = true;
endIndex = 23;
millisString = value.substring(20, endIndex);
millis = parseInt(value, millisString, 0, 999);
@ -286,6 +303,7 @@ public abstract class BaseDateTimeDt extends BasePrimitive<Date> {
millisString = value.substring(20, endIndex);
millis = parseInt(value, millisString, 0, 999);
myFractionalSeconds = millisString;
fractionalSecondsSet = true;
}
if (millisString.length() == 1) {
millis = millis * 100;
@ -304,6 +322,10 @@ public abstract class BaseDateTimeDt extends BasePrimitive<Date> {
cal.set(Calendar.DATE, 1);
}
if (fractionalSecondsSet == false) {
myFractionalSeconds = "";
}
setPrecision(precision);
return cal.getTime();
@ -395,11 +417,11 @@ public abstract class BaseDateTimeDt extends BasePrimitive<Date> {
public void setValue(Date theValue, TemporalPrecisionEnum thePrecision) throws DataFormatException {
setTimeZone(TimeZone.getDefault());
myPrecision = thePrecision;
super.setValue(theValue);
myFractionalSeconds = "";
if (theValue != null) {
myFractionalSeconds = Integer.toString((int) (theValue.getTime() % 1000));
}
super.setValue(theValue);
}
@Override
@ -468,6 +490,7 @@ public abstract class BaseDateTimeDt extends BasePrimitive<Date> {
}
}
private void validateLengthIsAtLeast(String theValue, int theLength) {
if (theValue.length() < theLength) {
throwBadDateFormat(theValue);

View File

@ -0,0 +1,43 @@
package ca.uhn.fhir.rest.annotation;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2016 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%
*/
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import ca.uhn.fhir.rest.param.DateParam;
import ca.uhn.fhir.rest.param.DateRangeParam;
/**
* Parameter annotation for the _at parameter, which indicates to the
* server that only results dated since the given instant will be returned.
* <p>
* Parameters with this annotation should be of type {@link DateParam} or {@link DateRangeParam}
* </p>
* @see History
*/
@Target(value=ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface At {
//nothing
}

View File

@ -25,9 +25,15 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import ca.uhn.fhir.rest.param.DateParam;
import ca.uhn.fhir.rest.param.DateRangeParam;
/**
* Parameter annotation for the _since parameter, which indicates to the
* server that only results dated since the given instant will be returned.
* <p>
* Parameters with this annotation should be of type {@link DateParam} or {@link DateRangeParam}
* </p>
*
* @see History
*/

View File

@ -0,0 +1,12 @@
package ca.uhn.fhir.rest.method;
import ca.uhn.fhir.rest.annotation.At;
import ca.uhn.fhir.rest.server.Constants;
class AtParameter extends SinceOrAtParameter {
public AtParameter() {
super(Constants.PARAM_AT, At.class);
}
}

View File

@ -38,6 +38,7 @@ class BaseBinder<T> {
myType = theType;
myCompositeTypes = theCompositeTypes;
if (myType.equals(CompositeParam.class)) {
if (myCompositeTypes.size() != 2) {
throw new ConfigurationException("Search parameter of type " + myType.getName() + " must have 2 composite types declared in parameter annotation, found " + theCompositeTypes.size());

View File

@ -0,0 +1,48 @@
package ca.uhn.fhir.rest.method;
import static org.apache.commons.lang3.StringUtils.isBlank;
import java.util.Collections;
import java.util.List;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IQueryParameterOr;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
abstract class BaseJavaPrimitiveBinder<T>implements IParamBinder<T> {
public BaseJavaPrimitiveBinder() {
super();
}
protected abstract String doEncode(T theString);
protected abstract T doParse(String theString);
@SuppressWarnings("unchecked")
@Override
public List<IQueryParameterOr<?>> encode(FhirContext theContext, T theString) throws InternalErrorException {
String retVal = doEncode(theString);
if (isBlank(retVal)) {
return Collections.emptyList();
}
List<?> retValList = Collections.singletonList(MethodUtil.singleton(new StringParam(retVal)));
return (List<IQueryParameterOr<?>>) retValList;
}
@Override
public T parse(String theName, List<QualifiedParamList> theParams) throws InternalErrorException, InvalidRequestException {
if (theParams.size() == 0 || theParams.get(0).size() == 0) {
return null;
}
if (theParams.size() > 1 || theParams.get(0).size() > 1) {
throw new InvalidRequestException("Multiple values detected for non-repeatable parameter '" + theName + "'. This server is not configured to allow multiple (AND) values for this param.");
}
T value = doParse(theParams.get(0).get(0));
return value;
}
}

View File

@ -0,0 +1,22 @@
package ca.uhn.fhir.rest.method;
import java.util.Calendar;
import ca.uhn.fhir.model.primitive.InstantDt;
final class CalendarBinder extends BaseJavaPrimitiveBinder<Calendar> {
CalendarBinder() {
}
@Override
protected String doEncode(Calendar theString) {
return new InstantDt(theString).getValueAsString();
}
@Override
protected Calendar doParse(String theString) {
return new InstantDt(theString).getValueAsCalendar();
}
}

View File

@ -0,0 +1,22 @@
package ca.uhn.fhir.rest.method;
import java.util.Date;
import ca.uhn.fhir.model.primitive.InstantDt;
final class DateBinder extends BaseJavaPrimitiveBinder<Date> {
DateBinder() {
}
@Override
protected String doEncode(Date theString) {
return new InstantDt(theString).getValueAsString();
}
@Override
protected Date doParse(String theString) {
return new InstantDt(theString).getValue();
}
}

View File

@ -0,0 +1,30 @@
package ca.uhn.fhir.rest.method;
import static org.apache.commons.lang3.StringUtils.defaultString;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import ca.uhn.fhir.util.ReflectionUtil;
final class FhirPrimitiveBinder extends BaseJavaPrimitiveBinder<IPrimitiveType<?>> {
private Class<IPrimitiveType<?>> myType;
FhirPrimitiveBinder(Class<IPrimitiveType<?>> theType) {
myType = theType;
}
@Override
protected String doEncode(IPrimitiveType<?> theString) {
return theString.getValueAsString();
}
@Override
protected IPrimitiveType<?> doParse(String theString) {
IPrimitiveType<?> instance = ReflectionUtil.newInstance(myType);
instance.setValueAsString(theString);
return instance;
}
}

View File

@ -29,7 +29,7 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
interface IParamBinder<T> {
List<IQueryParameterOr<?>> encode(FhirContext theContext, Object theString) throws InternalErrorException;
List<IQueryParameterOr<?>> encode(FhirContext theContext, T theString) throws InternalErrorException;
T parse(String theName, List<QualifiedParamList> theList) throws InternalErrorException, InvalidRequestException;

View File

@ -45,11 +45,11 @@ import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.Tag;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.annotation.At;
import ca.uhn.fhir.rest.annotation.ConditionalUrlParam;
import ca.uhn.fhir.rest.annotation.Count;
import ca.uhn.fhir.rest.annotation.Elements;
@ -112,6 +112,7 @@ import ca.uhn.fhir.util.ReflectionUtil;
* #L%
*/
@SuppressWarnings("deprecation")
public class MethodUtil {
private static final String LABEL = "label=\"";
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(MethodUtil.class);
@ -360,7 +361,6 @@ public class MethodUtil {
return MethodUtil.findParamAnnotationIndex(theMethod, TagListParam.class);
}
@SuppressWarnings("deprecation")
public static Integer findVersionIdParameterIndex(Method theMethod) {
return MethodUtil.findParamAnnotationIndex(theMethod, VersionIdParam.class);
}
@ -422,7 +422,7 @@ public class MethodUtil {
parameter.setDeclaredTypes(((RequiredParam) nextAnnotation).targetTypes());
parameter.setCompositeTypes(((RequiredParam) nextAnnotation).compositeTypes());
parameter.setChainlists(((RequiredParam) nextAnnotation).chainWhitelist(), ((RequiredParam) nextAnnotation).chainBlacklist());
parameter.setType(parameterType, innerCollectionType, outerCollectionType);
parameter.setType(theContext, parameterType, innerCollectionType, outerCollectionType);
MethodUtil.extractDescription(parameter, annotations);
param = parameter;
} else if (nextAnnotation instanceof OptionalParam) {
@ -432,7 +432,7 @@ public class MethodUtil {
parameter.setDeclaredTypes(((OptionalParam) nextAnnotation).targetTypes());
parameter.setCompositeTypes(((OptionalParam) nextAnnotation).compositeTypes());
parameter.setChainlists(((OptionalParam) nextAnnotation).chainWhitelist(), ((OptionalParam) nextAnnotation).chainBlacklist());
parameter.setType(parameterType, innerCollectionType, outerCollectionType);
parameter.setType(theContext, parameterType, innerCollectionType, outerCollectionType);
MethodUtil.extractDescription(parameter, annotations);
param = parameter;
} else if (nextAnnotation instanceof IncludeParam) {
@ -480,6 +480,10 @@ public class MethodUtil {
param = new ElementsParameter();
} else if (nextAnnotation instanceof Since) {
param = new SinceParameter();
((SinceParameter)param).setType(theContext, parameterType, innerCollectionType, outerCollectionType);
} else if (nextAnnotation instanceof At) {
param = new AtParameter();
((AtParameter)param).setType(theContext, parameterType, innerCollectionType, outerCollectionType);
} else if (nextAnnotation instanceof Count) {
param = new CountParameter();
} else if (nextAnnotation instanceof Sort) {

View File

@ -202,7 +202,7 @@ public class OperationParameter implements IParameter {
myParamType = "string";
mySearchParameterBinding = new SearchParameter(myName, myMin > 0);
mySearchParameterBinding.setCompositeTypes(COMPOSITE_TYPES);
mySearchParameterBinding.setType(theParameterType, theInnerCollectionType, theOuterCollectionType);
mySearchParameterBinding.setType(myContext, theParameterType, theInnerCollectionType, theOuterCollectionType);
myConverter = new QueryParameterConverter();
} else {
throw new ConfigurationException("Invalid type for @OperationParam: " + myParameterType.getName());

View File

@ -37,7 +37,7 @@ final class QueryParameterAndBinder extends BaseBinder<IQueryParameterAnd<?>> im
@SuppressWarnings("unchecked")
@Override
public List<IQueryParameterOr<?>> encode(FhirContext theContext, Object theString) throws InternalErrorException {
public List<IQueryParameterOr<?>> encode(FhirContext theContext, IQueryParameterAnd<?> theString) throws InternalErrorException {
List<IQueryParameterOr<?>> retVal = (List<IQueryParameterOr<?>>) ((IQueryParameterAnd<?>) theString).getValuesAsQueryTokens();
return retVal;
}

View File

@ -37,8 +37,8 @@ final class QueryParameterOrBinder extends BaseBinder<IQueryParameterOr<?>> impl
@SuppressWarnings("unchecked")
@Override
public List<IQueryParameterOr<?>> encode(FhirContext theContext, Object theString) throws InternalErrorException {
IQueryParameterOr<?> retVal = ((IQueryParameterOr<?>) theString);
public List<IQueryParameterOr<?>> encode(FhirContext theContext, IQueryParameterOr<?> theValue) throws InternalErrorException {
IQueryParameterOr<?> retVal = (theValue);
List<?> retVal2 = Collections.singletonList((IQueryParameterOr<?>)retVal);
return (List<IQueryParameterOr<?>>) retVal2;
}

View File

@ -39,8 +39,8 @@ final class QueryParameterTypeBinder extends BaseBinder<IQueryParameterType> imp
@SuppressWarnings("unchecked")
@Override
public List<IQueryParameterOr<?>> encode(FhirContext theContext, Object theString) throws InternalErrorException {
IQueryParameterType param = (IQueryParameterType) theString;
public List<IQueryParameterOr<?>> encode(FhirContext theContext, IQueryParameterType theValue) throws InternalErrorException {
IQueryParameterType param = theValue;
List<?> retVal = Collections.singletonList(MethodUtil.singleton(param));
return (List<IQueryParameterOr<?>>) retVal;
}

View File

@ -22,8 +22,10 @@ package ca.uhn.fhir.rest.method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@ -31,9 +33,11 @@ import java.util.Set;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimePrimitiveDatatypeDefinition;
import ca.uhn.fhir.model.api.IQueryParameterAnd;
import ca.uhn.fhir.model.api.IQueryParameterOr;
import ca.uhn.fhir.model.api.IQueryParameterType;
@ -75,10 +79,8 @@ import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.util.CollectionUtil;
import ca.uhn.fhir.util.ReflectionUtil;
/**
* Created by dsotnikov on 2/25/2014.
*/
@SuppressWarnings("deprecation")
public class SearchParameter extends BaseQueryParameter {
@ -139,7 +141,7 @@ public class SearchParameter extends BaseQueryParameter {
ourParamTypes.put(HasAndListParam.class, RestSearchParameterTypeEnum.HAS);
}
private List<Class<? extends IQueryParameterType>> myCompositeTypes;
private List<Class<? extends IQueryParameterType>> myCompositeTypes = Collections.emptyList();
private List<Class<? extends IBaseResource>> myDeclaredTypes;
private String myDescription;
private String myName;
@ -167,7 +169,12 @@ public class SearchParameter extends BaseQueryParameter {
public List<QualifiedParamList> encode(FhirContext theContext, Object theObject) throws InternalErrorException {
ArrayList<QualifiedParamList> retVal = new ArrayList<QualifiedParamList>();
List<IQueryParameterOr<?>> val = myParamBinder.encode(theContext, theObject);
// TODO: declaring method should probably have a generic type..
@SuppressWarnings("rawtypes")
IParamBinder paramBinder = myParamBinder;
@SuppressWarnings("unchecked")
List<IQueryParameterOr<?>> val = paramBinder.encode(theContext, theObject);
for (IQueryParameterOr<?> nextOr : val) {
retVal.add(new QualifiedParamList(nextOr, theContext));
}
@ -279,7 +286,7 @@ public class SearchParameter extends BaseQueryParameter {
}
@SuppressWarnings({ "unchecked", "unused" })
public void setType(final Class<?> type, Class<? extends Collection<?>> theInnerCollectionType, Class<? extends Collection<?>> theOuterCollectionType) {
public void setType(FhirContext theContext, final Class<?> type, Class<? extends Collection<?>> theInnerCollectionType, Class<? extends Collection<?>> theOuterCollectionType) {
this.myType = type;
if (IQueryParameterType.class.isAssignableFrom(type)) {
myParamBinder = new QueryParameterTypeBinder((Class<? extends IQueryParameterType>) type, myCompositeTypes);
@ -290,6 +297,23 @@ public class SearchParameter extends BaseQueryParameter {
} else if (String.class.equals(type)) {
myParamBinder = new StringBinder();
myParamType = RestSearchParameterTypeEnum.STRING;
} else if (Date.class.equals(type)) {
myParamBinder = new DateBinder();
myParamType = RestSearchParameterTypeEnum.DATE;
} else if (Calendar.class.equals(type)) {
myParamBinder = new CalendarBinder();
myParamType = RestSearchParameterTypeEnum.DATE;
} else if (IPrimitiveType.class.isAssignableFrom(type) && ReflectionUtil.isInstantiable(type)) {
RuntimePrimitiveDatatypeDefinition def = (RuntimePrimitiveDatatypeDefinition) theContext.getElementDefinition((Class<? extends IPrimitiveType<?>>) type);
if (def.getNativeType() != null) {
if (def.getNativeType().equals(Date.class)) {
myParamBinder = new FhirPrimitiveBinder((Class<IPrimitiveType<?>>) type);
myParamType = RestSearchParameterTypeEnum.DATE;
} else if (def.getNativeType().equals(String.class)) {
myParamBinder = new FhirPrimitiveBinder((Class<IPrimitiveType<?>>) type);
myParamType = RestSearchParameterTypeEnum.STRING;
}
}
} else {
throw new ConfigurationException("Unsupported data type for parameter: " + type.getCanonicalName());
}

View File

@ -0,0 +1,104 @@
package ca.uhn.fhir.rest.method;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2016 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%
*/
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.annotation.Since;
import ca.uhn.fhir.rest.param.ParameterUtil;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
class SinceOrAtParameter extends SearchParameter {
private Class<?> myType;
private String myParamName;
private Class<?> myAnnotationType;
public SinceOrAtParameter(String theParamName, Class<?> theAnnotationType) {
super(theParamName, false);
myParamName = theParamName;
myAnnotationType = theAnnotationType;
}
@Override
public Set<String> getQualifierBlacklist() {
return null;
}
@Override
public Set<String> getQualifierWhitelist() {
return null;
}
// @Override
// public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map<String, List<String>> theTargetQueryArguments, IBaseResource theTargetResource) throws InternalErrorException {
// if (theSourceClientArgument != null) {
// InstantDt since = ParameterUtil.toInstant(theSourceClientArgument);
// if (since.isEmpty() == false) {
// theTargetQueryArguments.put(myParamName, Collections.singletonList(since.getValueAsString()));
// }
// }
// }
//
// @Override
// public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
// String[] sinceParams = theRequest.getParameters().remove(myParamName);
// if (sinceParams != null) {
// if (sinceParams.length > 0) {
// if (StringUtils.isNotBlank(sinceParams[0])) {
// try {
// return ParameterUtil.fromInstant(myType, sinceParams);
// } catch (DataFormatException e) {
// throw new InvalidRequestException("Invalid " + Constants.PARAM_SINCE + " value: " + sinceParams[0]);
// }
// }
// }
// }
// return ParameterUtil.fromInstant(myType, null);
// }
//
// @Override
// public void initializeTypes(Method theMethod, Class<? extends Collection<?>> theOuterCollectionType, Class<? extends Collection<?>> theInnerCollectionType, Class<?> theParameterType) {
// if (theOuterCollectionType != null) {
// throw new ConfigurationException("Method '" + theMethod.getName() + "' in type '" + "' is annotated with @" + myAnnotationType.getName() + " but can not be of collection type");
// }
// if (ParameterUtil.getBindableInstantTypes().contains(theParameterType)) {
// myType = theParameterType;
// } else {
// throw new ConfigurationException("Method '" + theMethod.getName() + "' in type '" + "' is annotated with @" + myAnnotationType.getName() + " but is an invalid type, must be one of: " + ParameterUtil.getBindableInstantTypes());
// }
// }
}

View File

@ -1,85 +1,12 @@
package ca.uhn.fhir.rest.method;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2016 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%
*/
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.annotation.Since;
import ca.uhn.fhir.rest.param.ParameterUtil;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
class SinceParameter implements IParameter {
class SinceParameter extends SinceOrAtParameter {
private Class<?> myType;
@Override
public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map<String, List<String>> theTargetQueryArguments, IBaseResource theTargetResource) throws InternalErrorException {
if (theSourceClientArgument != null) {
InstantDt since = ParameterUtil.toInstant(theSourceClientArgument);
if (since.isEmpty() == false) {
theTargetQueryArguments.put(Constants.PARAM_SINCE, Collections.singletonList(since.getValueAsString()));
}
}
}
@Override
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
String[] sinceParams = theRequest.getParameters().remove(Constants.PARAM_SINCE);
if (sinceParams != null) {
if (sinceParams.length > 0) {
if (StringUtils.isNotBlank(sinceParams[0])) {
try {
InstantDt since = new InstantDt(sinceParams[0]);
return ParameterUtil.fromInstant(myType, since);
} catch (DataFormatException e) {
throw new InvalidRequestException("Invalid " + Constants.PARAM_SINCE + " value: " + sinceParams[0]);
}
}
}
}
return ParameterUtil.fromInstant(myType, null);
}
@Override
public void initializeTypes(Method theMethod, Class<? extends Collection<?>> theOuterCollectionType, Class<? extends Collection<?>> theInnerCollectionType, Class<?> theParameterType) {
if (theOuterCollectionType != null) {
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type '" + "' is annotated with @" + Since.class.getName() + " but can not be of collection type");
}
if (!ParameterUtil.getBindableInstantTypes().contains(theParameterType)) {
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type '" + "' is annotated with @" + Since.class.getName() + " but is an invalid type, must be one of: " + ParameterUtil.getBindableInstantTypes());
}
myType = theParameterType;
public SinceParameter() {
super(Constants.PARAM_SINCE, Since.class);
}
}

View File

@ -1,56 +1,18 @@
package ca.uhn.fhir.rest.method;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2016 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%
*/
import java.util.Collections;
import java.util.List;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IQueryParameterOr;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
final class StringBinder implements IParamBinder<String> {
final class StringBinder extends BaseJavaPrimitiveBinder<String> {
StringBinder() {
}
@SuppressWarnings("unchecked")
@Override
public List<IQueryParameterOr<?>> encode(FhirContext theContext, Object theString) throws InternalErrorException {
String retVal = ((String) theString);
List<?> retValList = Collections.singletonList(MethodUtil.singleton(new StringParam(retVal)));
return (List<IQueryParameterOr<?>>) retValList;
protected String doEncode(String theString) {
return theString;
}
@Override
public String parse(String theName, List<QualifiedParamList> theParams) throws InternalErrorException, InvalidRequestException {
if (theParams.size() == 0 || theParams.get(0).size() == 0) {
return "";
}
if (theParams.size() > 1 || theParams.get(0).size() > 1) {
throw new InvalidRequestException("Multiple values detected for non-repeatable parameter '" + theName + "'. This server is not configured to allow multiple (AND) values for this param.");
protected String doParse(String theString) {
return theString;
}
return theParams.get(0).get(0);
}
}

View File

@ -32,7 +32,6 @@ import org.apache.commons.lang3.builder.ToStringStyle;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.model.api.IQueryParameterOr;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;

View File

@ -21,23 +21,17 @@ package ca.uhn.fhir.rest.param;
*/
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.lang3.time.DateUtils;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.IntegerDt;
import ca.uhn.fhir.util.UrlUtil;
public class ParameterUtil {
private static final Set<Class<?>> BINDABLE_INTEGER_TYPES;
private static final Set<Class<?>> BINDABLE_TIME_TYPES;
static {
HashSet<Class<?>> intTypes = new HashSet<Class<?>>();
@ -45,12 +39,6 @@ public class ParameterUtil {
intTypes.add(Integer.class);
BINDABLE_INTEGER_TYPES = Collections.unmodifiableSet(intTypes);
HashSet<Class<?>> timeTypes = new HashSet<Class<?>>();
timeTypes.add(InstantDt.class);
timeTypes.add(Date.class);
timeTypes.add(Calendar.class);
BINDABLE_TIME_TYPES = Collections.unmodifiableSet(timeTypes);
}
// public static Integer findSinceParameterIndex(Method theMethod) {
@ -68,32 +56,11 @@ public class ParameterUtil {
return -1;
}
public static Object fromInstant(Class<?> theType, InstantDt theArgument) {
if (theType.equals(InstantDt.class)) {
if (theArgument == null) {
return new InstantDt();
}
return theArgument;
}
if (theType.equals(Date.class)) {
if (theArgument == null) {
return null;
}
return theArgument.getValue();
}
if (theType.equals(Calendar.class)) {
if (theArgument == null) {
return null;
}
return DateUtils.toCalendar(theArgument.getValue());
}
throw new IllegalArgumentException("Invalid instant type:" + theType);
}
public static Object fromInteger(Class<?> theType, IntegerDt theArgument) {
if (theType.equals(IntegerDt.class)) {
if (theArgument == null) {
return new IntegerDt();
return null;
}
return theArgument;
}
@ -106,26 +73,10 @@ public class ParameterUtil {
throw new IllegalArgumentException("Invalid Integer type:" + theType);
}
public static Set<Class<?>> getBindableInstantTypes() {
return BINDABLE_TIME_TYPES;
}
public static Set<Class<?>> getBindableIntegerTypes() {
return BINDABLE_INTEGER_TYPES;
}
public static InstantDt toInstant(Object theArgument) {
if (theArgument instanceof InstantDt) {
return (InstantDt) theArgument;
}
if (theArgument instanceof Date) {
return new InstantDt((Date) theArgument);
}
if (theArgument instanceof Calendar) {
return new InstantDt((Calendar) theArgument);
}
return null;
}
public static IntegerDt toInteger(Object theArgument) {
if (theArgument instanceof IntegerDt) {

View File

@ -58,6 +58,8 @@ public class Constants {
* "text/html" and "html"
*/
public static final Set<String> FORMATS_HTML;
public static final String FORMATS_HTML_JSON = "html/json";
public static final String FORMATS_HTML_XML = "html/xml";
public static final String HEADER_ACCEPT = "Accept";
public static final String HEADER_ACCEPT_ENCODING = "Accept-Encoding";
public static final String HEADER_ACCEPT_VALUE_XML_OR_JSON = CT_FHIR_XML + ";q=1.0, " + CT_FHIR_JSON + ";q=1.0";
@ -103,6 +105,7 @@ public class Constants {
public static final String LINK_PREVIOUS = "previous";
public static final String LINK_SELF = "self";
public static final String OPENSEARCH_NS_OLDER = "http://purl.org/atompub/tombstones/1.0";
public static final String PARAM_AT = "_at";
/**
* Used in paging links
*/
@ -155,15 +158,13 @@ public class Constants {
public static final int STATUS_HTTP_410_GONE = 410;
public static final int STATUS_HTTP_412_PRECONDITION_FAILED = 412;
public static final int STATUS_HTTP_422_UNPROCESSABLE_ENTITY = 422;
public static final int STATUS_HTTP_500_INTERNAL_ERROR = 500;
public static final int STATUS_HTTP_501_NOT_IMPLEMENTED = 501;
public static final String TAG_SUBSETTED_CODE = "SUBSETTED";
public static final String TAG_SUBSETTED_SYSTEM = "http://hl7.org/fhir/v3/ObservationValue";
public static final String URL_TOKEN_HISTORY = "_history";
public static final String URL_TOKEN_METADATA = "metadata";
public static final String FORMATS_HTML_JSON = "html/json";
public static final String FORMATS_HTML_XML = "html/xml";
static {
Map<String, EncodingEnum> valToEncoding = new HashMap<String, EncodingEnum>();

View File

@ -30,6 +30,7 @@ import java.util.LinkedHashSet;
import java.util.List;
import ca.uhn.fhir.context.ConfigurationException;
import javassist.Modifier;
public class ReflectionUtil {
@ -153,4 +154,8 @@ public class ReflectionUtil {
}
}
public static boolean isInstantiable(Class<?> theType) {
return !theType.isInterface() && !Modifier.isAbstract(theType.getModifiers());
}
}

View File

@ -69,7 +69,7 @@ public class AbstractJaxRsConformanceProviderTest {
Response response = createConformanceProvider(providers).conformance();
assertEquals(Constants.STATUS_HTTP_200_OK, response.getStatus());
assertTrue(response.getEntity().toString().contains("\"type\":\"Patient\""));
assertTrue(response.getEntity().toString().contains("\"$someCustomOperation"));
assertTrue(response.getEntity().toString().contains("\"someCustomOperation"));
System.out.println(response);
System.out.println(response.getEntity());
}
@ -83,7 +83,7 @@ public class AbstractJaxRsConformanceProviderTest {
assertEquals(Constants.STATUS_HTTP_200_OK, response.getStatus());
System.out.println(response.getEntity());
assertTrue(response.getEntity().toString().contains(" <type value=\"Patient\"/>"));
assertTrue(response.getEntity().toString().contains("\"$someCustomOperation"));
assertTrue(response.getEntity().toString().contains("\"someCustomOperation"));
System.out.println(response.getEntity());
}

View File

@ -474,19 +474,6 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
}
}
private List<Object> extractValues(String thePath, IBaseResource theResource) {
List<Object> values = new ArrayList<Object>();
FhirTerser t = getContext().newTerser();
String nextPathTrimmed = thePath.trim();
try {
values.addAll(t.getValues(theResource, nextPathTrimmed));
} catch (Exception e) {
RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource);
ourLog.warn("Failed to index values from path[{}] in resource type[{}]: ", new Object[] { nextPathTrimmed, def.getName(), e.toString() });
}
return values;
}
private void findMatchingTagIds(String theResourceName, IIdType theResourceId, Set<Long> tagIds, Class<? extends BaseTag> entityClass) {
{
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
@ -621,13 +608,13 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
}
}
protected IBundleProvider history(String theResourceName, Long theId, Date theSince) {
protected IBundleProvider history(String theResourceName, Long theId, Date theSince, Date theUntil) {
String resourceName = defaultIfBlank(theResourceName, null);
Search search = new Search();
search.setCreated(new Date());
search.setLastUpdated(null, theSince);
search.setLastUpdated(theSince, theUntil);
search.setUuid(UUID.randomUUID().toString());
search.setResourceType(resourceName);
search.setResourceId(theId);
@ -661,7 +648,6 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
theProvider.setContext(getContext());
theProvider.setEntityManager(myEntityManager);
theProvider.setPlatformTransactionManager(myPlatformTransactionManager);
theProvider.setResourceHistoryTableDao(myResourceHistoryTableDao);
theProvider.setSearchDao(mySearchDao);
theProvider.setSearchResultDao(mySearchResultDao);
}

View File

@ -101,20 +101,23 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
@Autowired
protected PlatformTransactionManager myPlatformTransactionManager;
private String myResourceName;
@Autowired
private IResourceHistoryTableDao myResourceHistoryTableDao;
@Autowired()
protected IResourceIndexedSearchParamUriDao myResourceIndexedSearchParamUriDao;
private String myResourceName;
private Class<T> myResourceType;
@Autowired(required = false)
protected IFulltextSearchSvc mySearchDao;
@Autowired()
protected ISearchResultDao mySearchResultDao;
@Autowired()
protected IResourceIndexedSearchParamUriDao myResourceIndexedSearchParamUriDao;
@Autowired()
protected IHapiTerminologySvc myTerminologySvc;
private String mySecondaryPrimaryKeyParamName;
@Autowired()
protected IHapiTerminologySvc myTerminologySvc;
@Override
public void addTag(IIdType theId, TagTypeEnum theTagType, String theScheme, String theTerm, String theLabel) {
StopWatch w = new StopWatch();
@ -149,11 +152,6 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
return create(theResource, null, true, theRequestDetails);
}
@Override
public DaoMethodOutcome create(final T theResource, String theIfNoneExist, RequestDetails theRequestDetails) {
return create(theResource, theIfNoneExist, true, theRequestDetails);
}
@Override
public DaoMethodOutcome create(T theResource, String theIfNoneExist, boolean thePerformIndexing, RequestDetails theRequestDetails) {
if (isNotBlank(theResource.getIdElement().getIdPart())) {
@ -171,6 +169,11 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
return doCreate(theResource, theIfNoneExist, thePerformIndexing, new Date(), theRequestDetails);
}
@Override
public DaoMethodOutcome create(final T theResource, String theIfNoneExist, RequestDetails theRequestDetails) {
return create(theResource, theIfNoneExist, true, theRequestDetails);
}
public IBaseOperationOutcome createErrorOperationOutcome(String theMessage, String theCode) {
return createOperationOutcome(OO_SEVERITY_ERROR, theMessage, theCode);
}
@ -181,19 +184,6 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
protected abstract IBaseOperationOutcome createOperationOutcome(String theSeverity, String theMessage, String theCode);
@Override
public DaoMethodOutcome delete(IIdType theId, RequestDetails theRequestDetails) {
List<DeleteConflict> deleteConflicts = new ArrayList<DeleteConflict>();
StopWatch w = new StopWatch();
ResourceTable savedEntity = delete(theId, deleteConflicts, theRequestDetails);
validateDeleteConflictsEmptyOrThrowException(deleteConflicts);
ourLog.info("Processed delete on {} in {}ms", theId.getValue(), w.getMillisAndRestart());
return toMethodOutcome(savedEntity, null);
}
@Override
public ResourceTable delete(IIdType theId, List<DeleteConflict> deleteConflicts, RequestDetails theRequestDetails) {
if (theId == null || !theId.hasIdPart()) {
@ -225,20 +215,16 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
}
@Override
public DaoMethodOutcome deleteByUrl(String theUrl, RequestDetails theRequestDetails) {
StopWatch w = new StopWatch();
public DaoMethodOutcome delete(IIdType theId, RequestDetails theRequestDetails) {
List<DeleteConflict> deleteConflicts = new ArrayList<DeleteConflict>();
StopWatch w = new StopWatch();
List<ResourceTable> deletedResources = deleteByUrl(theUrl, deleteConflicts, theRequestDetails);
ResourceTable savedEntity = delete(theId, deleteConflicts, theRequestDetails);
validateDeleteConflictsEmptyOrThrowException(deleteConflicts);
if (deletedResources.isEmpty()) {
throw new ResourceNotFoundException(getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "unableToDeleteNotFound", theUrl));
}
ourLog.info("Processed delete on {} (matched {} resource(s)) in {}ms", new Object[] { theUrl, deletedResources.size(), w.getMillisAndRestart() });
return new DaoMethodOutcome();
ourLog.info("Processed delete on {} in {}ms", theId.getValue(), w.getMillisAndRestart());
return toMethodOutcome(savedEntity, null);
}
@Override
@ -280,6 +266,23 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
return retVal;
}
@Override
public DaoMethodOutcome deleteByUrl(String theUrl, RequestDetails theRequestDetails) {
StopWatch w = new StopWatch();
List<DeleteConflict> deleteConflicts = new ArrayList<DeleteConflict>();
List<ResourceTable> deletedResources = deleteByUrl(theUrl, deleteConflicts, theRequestDetails);
validateDeleteConflictsEmptyOrThrowException(deleteConflicts);
if (deletedResources.isEmpty()) {
throw new ResourceNotFoundException(getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "unableToDeleteNotFound", theUrl));
}
ourLog.info("Processed delete on {} (matched {} resource(s)) in {}ms", new Object[] { theUrl, deletedResources.size(), w.getMillisAndRestart() });
return new DaoMethodOutcome();
}
private DaoMethodOutcome doCreate(T theResource, String theIfNoneExist, boolean thePerformIndexing, Date theUpdateTime, RequestDetails theRequestDetails) {
StopWatch w = new StopWatch();
@ -302,7 +305,8 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
if (isNotBlank(theResource.getIdElement().getIdPart())) {
if (isValidPid(theResource.getIdElement())) {
throw new UnprocessableEntityException("This server cannot create an entity with a user-specified numeric ID - Client should not specify an ID when creating a new resource, or should include at least one letter in the ID to force a client-defined ID");
throw new UnprocessableEntityException(
"This server cannot create an entity with a user-specified numeric ID - Client should not specify an ID when creating a new resource, or should include at least one letter in the ID to force a client-defined ID");
}
createForcedIdIfNeeded(entity, theResource.getIdElement());
@ -342,6 +346,58 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
return outcome;
}
private <MT extends IBaseMetaType> void doMetaAdd(MT theMetaAdd, BaseHasResource entity) {
List<TagDefinition> tags = toTagList(theMetaAdd);
//@formatter:off
for (TagDefinition nextDef : tags) {
boolean hasTag = false;
for (BaseTag next : new ArrayList<BaseTag>(entity.getTags())) {
if (ObjectUtil.equals(next.getTag().getTagType(), nextDef.getTagType()) &&
ObjectUtil.equals(next.getTag().getSystem(), nextDef.getSystem()) &&
ObjectUtil.equals(next.getTag().getCode(), nextDef.getCode())) {
hasTag = true;
break;
}
}
if (!hasTag) {
entity.setHasTags(true);
TagDefinition def = getTag(nextDef.getTagType(), nextDef.getSystem(), nextDef.getCode(), nextDef.getDisplay());
BaseTag newEntity = entity.addTag(def);
myEntityManager.persist(newEntity);
}
}
//@formatter:on
myEntityManager.merge(entity);
}
private <MT extends IBaseMetaType> void doMetaDelete(MT theMetaDel, BaseHasResource entity) {
List<TagDefinition> tags = toTagList(theMetaDel);
//@formatter:off
for (TagDefinition nextDef : tags) {
for (BaseTag next : new ArrayList<BaseTag>(entity.getTags())) {
if (ObjectUtil.equals(next.getTag().getTagType(), nextDef.getTagType()) &&
ObjectUtil.equals(next.getTag().getSystem(), nextDef.getSystem()) &&
ObjectUtil.equals(next.getTag().getCode(), nextDef.getCode())) {
myEntityManager.remove(next);
entity.getTags().remove(next);
}
}
}
//@formatter:on
if (entity.getTags().isEmpty()) {
entity.setHasTags(false);
}
myEntityManager.merge(entity);
}
@Override
public TagList getAllResourceTags(RequestDetails theRequestDetails) {
// Notify interceptors
@ -378,19 +434,19 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
}
@Override
public IBundleProvider history(Date theSince, RequestDetails theRequestDetails) {
public IBundleProvider history(Date theSince, Date theUntil, RequestDetails theRequestDetails) {
// Notify interceptors
ActionRequestDetails requestDetails = new ActionRequestDetails(null, null, getContext(), theRequestDetails);
notifyInterceptors(RestOperationTypeEnum.HISTORY_SYSTEM, requestDetails);
notifyInterceptors(RestOperationTypeEnum.HISTORY_TYPE, requestDetails);
StopWatch w = new StopWatch();
IBundleProvider retVal = super.history(myResourceName, null, theSince);
IBundleProvider retVal = super.history(myResourceName, null, theSince, theUntil);
ourLog.info("Processed history on {} in {}ms", myResourceName, w.getMillisAndRestart());
return retVal;
}
@Override
public IBundleProvider history(final IIdType theId, final Date theSince, RequestDetails theRequestDetails) {
public IBundleProvider history(final IIdType theId, final Date theSince, Date theUntil, RequestDetails theRequestDetails) {
// Notify interceptors
ActionRequestDetails requestDetails = new ActionRequestDetails(theId, getResourceName(), getContext(), theRequestDetails);
notifyInterceptors(RestOperationTypeEnum.HISTORY_INSTANCE, requestDetails);
@ -400,86 +456,12 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
IIdType id = theId.withResourceType(myResourceName).toUnqualifiedVersionless();
BaseHasResource entity = readEntity(id);
IBundleProvider retVal = super.history(myResourceName, entity.getId(), theSince);
IBundleProvider retVal = super.history(myResourceName, entity.getId(), theSince, theUntil);
ourLog.info("Processed history on {} in {}ms", id, w.getMillisAndRestart());
return retVal;
}
@Override
public IBundleProvider history(Long theId, Date theSince, RequestDetails theRequestDetails) {
// Notify interceptors
ActionRequestDetails requestDetails = new ActionRequestDetails(null, getResourceName(), getContext(), theRequestDetails);
notifyInterceptors(RestOperationTypeEnum.HISTORY_TYPE, requestDetails);
StopWatch w = new StopWatch();
IBundleProvider retVal = super.history(myResourceName, theId, theSince);
ourLog.info("Processed history on {} in {}ms", theId, w.getMillisAndRestart());
return retVal;
}
@Autowired
private IResourceHistoryTableDao myResourceHistoryTableDao;
@Override
public <MT extends IBaseMetaType> MT metaAddOperation(IIdType theResourceId, MT theMetaAdd, RequestDetails theRequestDetails) {
// Notify interceptors
ActionRequestDetails requestDetails = new ActionRequestDetails(theResourceId, getResourceName(), getContext(), theRequestDetails);
notifyInterceptors(RestOperationTypeEnum.META_ADD, requestDetails);
StopWatch w = new StopWatch();
BaseHasResource entity = readEntity(theResourceId);
if (entity == null) {
throw new ResourceNotFoundException(theResourceId);
}
ResourceTable latestVersion = readEntityLatestVersion(theResourceId);
if (latestVersion.getVersion() != entity.getVersion()) {
doMetaAdd(theMetaAdd, entity);
} else {
doMetaAdd(theMetaAdd, latestVersion);
// Also update history entry
ResourceHistoryTable history = myResourceHistoryTableDao.findForIdAndVersion(entity.getId(), entity.getVersion());
doMetaAdd(theMetaAdd, history);
}
ourLog.info("Processed metaAddOperation on {} in {}ms", new Object[] { theResourceId, w.getMillisAndRestart() });
@SuppressWarnings("unchecked")
MT retVal = (MT) metaGetOperation(theMetaAdd.getClass(), theResourceId, theRequestDetails);
return retVal;
}
private <MT extends IBaseMetaType> void doMetaAdd(MT theMetaAdd, BaseHasResource entity) {
List<TagDefinition> tags = toTagList(theMetaAdd);
//@formatter:off
for (TagDefinition nextDef : tags) {
boolean hasTag = false;
for (BaseTag next : new ArrayList<BaseTag>(entity.getTags())) {
if (ObjectUtil.equals(next.getTag().getTagType(), nextDef.getTagType()) &&
ObjectUtil.equals(next.getTag().getSystem(), nextDef.getSystem()) &&
ObjectUtil.equals(next.getTag().getCode(), nextDef.getCode())) {
hasTag = true;
break;
}
}
if (!hasTag) {
entity.setHasTags(true);
TagDefinition def = getTag(nextDef.getTagType(), nextDef.getSystem(), nextDef.getCode(), nextDef.getDisplay());
BaseTag newEntity = entity.addTag(def);
myEntityManager.persist(newEntity);
}
}
//@formatter:on
myEntityManager.merge(entity);
}
// @Override
// public IBundleProvider everything(IIdType theId) {
// Search search = new Search();
@ -562,6 +544,36 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
// };
// }
@Override
public <MT extends IBaseMetaType> MT metaAddOperation(IIdType theResourceId, MT theMetaAdd, RequestDetails theRequestDetails) {
// Notify interceptors
ActionRequestDetails requestDetails = new ActionRequestDetails(theResourceId, getResourceName(), getContext(), theRequestDetails);
notifyInterceptors(RestOperationTypeEnum.META_ADD, requestDetails);
StopWatch w = new StopWatch();
BaseHasResource entity = readEntity(theResourceId);
if (entity == null) {
throw new ResourceNotFoundException(theResourceId);
}
ResourceTable latestVersion = readEntityLatestVersion(theResourceId);
if (latestVersion.getVersion() != entity.getVersion()) {
doMetaAdd(theMetaAdd, entity);
} else {
doMetaAdd(theMetaAdd, latestVersion);
// Also update history entry
ResourceHistoryTable history = myResourceHistoryTableDao.findForIdAndVersion(entity.getId(), entity.getVersion());
doMetaAdd(theMetaAdd, history);
}
ourLog.info("Processed metaAddOperation on {} in {}ms", new Object[] { theResourceId, w.getMillisAndRestart() });
@SuppressWarnings("unchecked")
MT retVal = (MT) metaGetOperation(theMetaAdd.getClass(), theResourceId, theRequestDetails);
return retVal;
}
@Override
public <MT extends IBaseMetaType> MT metaDeleteOperation(IIdType theResourceId, MT theMetaDel, RequestDetails theRequestDetails) {
// Notify interceptors
@ -594,68 +606,6 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
return retVal;
}
private <MT extends IBaseMetaType> void doMetaDelete(MT theMetaDel, BaseHasResource entity) {
List<TagDefinition> tags = toTagList(theMetaDel);
//@formatter:off
for (TagDefinition nextDef : tags) {
for (BaseTag next : new ArrayList<BaseTag>(entity.getTags())) {
if (ObjectUtil.equals(next.getTag().getTagType(), nextDef.getTagType()) &&
ObjectUtil.equals(next.getTag().getSystem(), nextDef.getSystem()) &&
ObjectUtil.equals(next.getTag().getCode(), nextDef.getCode())) {
myEntityManager.remove(next);
entity.getTags().remove(next);
}
}
}
//@formatter:on
if (entity.getTags().isEmpty()) {
entity.setHasTags(false);
}
myEntityManager.merge(entity);
}
@Override
public <MT extends IBaseMetaType> MT metaGetOperation(Class<MT> theType, RequestDetails theRequestDetails) {
// Notify interceptors
ActionRequestDetails requestDetails = new ActionRequestDetails(null, getResourceName(), getContext(), theRequestDetails);
notifyInterceptors(RestOperationTypeEnum.META, requestDetails);
String sql = "SELECT d FROM TagDefinition d WHERE d.myId IN (SELECT DISTINCT t.myTagId FROM ResourceTag t WHERE t.myResourceType = :res_type)";
TypedQuery<TagDefinition> q = myEntityManager.createQuery(sql, TagDefinition.class);
q.setParameter("res_type", myResourceName);
List<TagDefinition> tagDefinitions = q.getResultList();
MT retVal = toMetaDt(theType, tagDefinitions);
return retVal;
}
protected <MT extends IBaseMetaType> MT toMetaDt(Class<MT> theType, Collection<TagDefinition> tagDefinitions) {
MT retVal;
try {
retVal = theType.newInstance();
} catch (Exception e) {
throw new InternalErrorException("Failed to instantiate " + theType.getName(), e);
}
for (TagDefinition next : tagDefinitions) {
switch (next.getTagType()) {
case PROFILE:
retVal.addProfile(next.getCode());
break;
case SECURITY_LABEL:
retVal.addSecurity().setSystem(next.getSystem()).setCode(next.getCode()).setDisplay(next.getDisplay());
break;
case TAG:
retVal.addTag().setSystem(next.getSystem()).setCode(next.getCode()).setDisplay(next.getDisplay());
break;
}
}
return retVal;
}
@Override
public <MT extends IBaseMetaType> MT metaGetOperation(Class<MT> theType, IIdType theId, RequestDetails theRequestDetails) {
// Notify interceptors
@ -675,6 +625,22 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
return retVal;
}
@Override
public <MT extends IBaseMetaType> MT metaGetOperation(Class<MT> theType, RequestDetails theRequestDetails) {
// Notify interceptors
ActionRequestDetails requestDetails = new ActionRequestDetails(null, getResourceName(), getContext(), theRequestDetails);
notifyInterceptors(RestOperationTypeEnum.META, requestDetails);
String sql = "SELECT d FROM TagDefinition d WHERE d.myId IN (SELECT DISTINCT t.myTagId FROM ResourceTag t WHERE t.myResourceType = :res_type)";
TypedQuery<TagDefinition> q = myEntityManager.createQuery(sql, TagDefinition.class);
q.setParameter("res_type", myResourceName);
List<TagDefinition> tagDefinitions = q.getResultList();
MT retVal = toMetaDt(theType, tagDefinitions);
return retVal;
}
@PostConstruct
public void postConstruct() {
RuntimeResourceDefinition def = getContext().getResourceDefinition(myResourceType);
@ -711,8 +677,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
}
/*
* Replace absolute references with relative ones if configured to
* do so
* Replace absolute references with relative ones if configured to do so
*/
if (getConfig().getTreatBaseUrlsAsLocal().isEmpty() == false) {
FhirTerser t = getContext().newTerser();
@ -772,7 +737,6 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
return entity;
}
@Override
public BaseHasResource readEntity(IIdType theId, boolean theCheckForForcedId) {
validateResourceTypeAndThrowIllegalArgumentException(theId);
@ -795,7 +759,8 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
if (entity == null) {
if (theId.hasVersionIdPart()) {
TypedQuery<ResourceHistoryTable> q = myEntityManager.createQuery("SELECT t from ResourceHistoryTable t WHERE t.myResourceId = :RID AND t.myResourceType = :RTYP AND t.myResourceVersion = :RVER", ResourceHistoryTable.class);
TypedQuery<ResourceHistoryTable> q = myEntityManager
.createQuery("SELECT t from ResourceHistoryTable t WHERE t.myResourceId = :RID AND t.myResourceType = :RTYP AND t.myResourceVersion = :RVER", ResourceHistoryTable.class);
q.setParameter("RID", pid);
q.setParameter("RTYP", myResourceName);
q.setParameter("RVER", theId.getVersionIdPartAsLong());
@ -876,7 +841,8 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
ActionRequestDetails requestDetails = new ActionRequestDetails(null, getResourceName(), getContext(), theParams.getRequestDetails());
notifyInterceptors(RestOperationTypeEnum.SEARCH_TYPE, requestDetails);
SearchBuilder builder = new SearchBuilder(getContext(), myEntityManager, myPlatformTransactionManager, mySearchDao, mySearchResultDao, this, myResourceIndexedSearchParamUriDao, myForcedIdDao, myTerminologySvc);
SearchBuilder builder = new SearchBuilder(getContext(), myEntityManager, myPlatformTransactionManager, mySearchDao, mySearchResultDao, this, myResourceIndexedSearchParamUriDao, myForcedIdDao,
myTerminologySvc);
builder.setType(getResourceType(), getResourceName());
return builder.search(theParams);
}
@ -904,7 +870,8 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
public Set<Long> searchForIdsWithAndOr(SearchParameterMap theParams) {
theParams.setPersistResults(false);
SearchBuilder builder = new SearchBuilder(getContext(), myEntityManager, myPlatformTransactionManager, mySearchDao, mySearchResultDao, this, myResourceIndexedSearchParamUriDao, myForcedIdDao, myTerminologySvc);
SearchBuilder builder = new SearchBuilder(getContext(), myEntityManager, myPlatformTransactionManager, mySearchDao, mySearchResultDao, this, myResourceIndexedSearchParamUriDao, myForcedIdDao,
myTerminologySvc);
builder.setType(getResourceType(), getResourceName());
builder.search(theParams);
return builder.doGetPids();
@ -917,13 +884,35 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
}
/**
* If set, the given param will be treated as a secondary primary key, and multiple resources will not be able to
* share the same value.
* If set, the given param will be treated as a secondary primary key, and multiple resources will not be able to share the same value.
*/
public void setSecondaryPrimaryKeyParamName(String theSecondaryPrimaryKeyParamName) {
mySecondaryPrimaryKeyParamName = theSecondaryPrimaryKeyParamName;
}
protected <MT extends IBaseMetaType> MT toMetaDt(Class<MT> theType, Collection<TagDefinition> tagDefinitions) {
MT retVal;
try {
retVal = theType.newInstance();
} catch (Exception e) {
throw new InternalErrorException("Failed to instantiate " + theType.getName(), e);
}
for (TagDefinition next : tagDefinitions) {
switch (next.getTagType()) {
case PROFILE:
retVal.addProfile(next.getCode());
break;
case SECURITY_LABEL:
retVal.addSecurity().setSystem(next.getSystem()).setCode(next.getCode()).setDisplay(next.getDisplay());
break;
case TAG:
retVal.addTag().setSystem(next.getSystem()).setCode(next.getCode()).setDisplay(next.getDisplay());
break;
}
}
return retVal;
}
private DaoMethodOutcome toMethodOutcome(final BaseHasResource theEntity, IBaseResource theResource) {
DaoMethodOutcome outcome = new DaoMethodOutcome();
@ -973,18 +962,12 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
return update(theResource, null, theRequestDetails);
}
@Override
public DaoMethodOutcome update(T theResource, String theMatchUrl, RequestDetails theRequestDetails) {
return update(theResource, theMatchUrl, true, theRequestDetails);
}
@Override
public DaoMethodOutcome update(T theResource, String theMatchUrl, boolean thePerformIndexing, RequestDetails theRequestDetails) {
StopWatch w = new StopWatch();
preProcessResourceForStorage(theResource);
final ResourceTable entity;
IIdType resourceId;
@ -1001,8 +984,8 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
return create(theResource, null, thePerformIndexing, theRequestDetails);
}
} else {
/* Note: resourcdeId will not be null or empty here, because
* we check it and reject requests in BaseOutcomeReturningMethodBindingWithResourceParam
/*
* Note: resourcdeId will not be null or empty here, because we check it and reject requests in BaseOutcomeReturningMethodBindingWithResourceParam
*/
resourceId = theResource.getIdElement();
@ -1010,7 +993,8 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
entity = readEntityLatestVersion(resourceId);
} catch (ResourceNotFoundException e) {
if (resourceId.isIdPartValidLong()) {
throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "failedToCreateWithClientAssignedNumericId", theResource.getIdElement().getIdPart()));
throw new InvalidRequestException(
getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "failedToCreateWithClientAssignedNumericId", theResource.getIdElement().getIdPart()));
}
return doCreate(theResource, null, thePerformIndexing, new Date(), theRequestDetails);
}
@ -1021,7 +1005,8 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
}
if (resourceId.hasResourceType() && !resourceId.getResourceType().equals(getResourceName())) {
throw new UnprocessableEntityException("Invalid resource ID[" + entity.getIdDt().toUnqualifiedVersionless() + "] of type[" + entity.getResourceType() + "] - Does not match expected [" + getResourceName() + "]");
throw new UnprocessableEntityException(
"Invalid resource ID[" + entity.getIdDt().toUnqualifiedVersionless() + "] of type[" + entity.getResourceType() + "] - Does not match expected [" + getResourceName() + "]");
}
// Notify interceptors
@ -1048,6 +1033,11 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
return outcome;
}
@Override
public DaoMethodOutcome update(T theResource, String theMatchUrl, RequestDetails theRequestDetails) {
return update(theResource, theMatchUrl, true, theRequestDetails);
}
private void validateGivenIdIsAppropriateToRetrieveResource(IIdType theId, BaseHasResource entity) {
if (entity.getForcedId() != null) {
if (theId.isIdPartValidLong()) {

View File

@ -219,13 +219,13 @@ public abstract class BaseHapiFhirSystemDao<T, MT> extends BaseHapiFhirDao<IBase
}
@Override
public IBundleProvider history(Date theSince, RequestDetails theRequestDetails) {
public IBundleProvider history(Date theSince, Date theUntil, RequestDetails theRequestDetails) {
// Notify interceptors
ActionRequestDetails requestDetails = new ActionRequestDetails(null, null, getContext(), theRequestDetails);
notifyInterceptors(RestOperationTypeEnum.HISTORY_SYSTEM, requestDetails);
StopWatch w = new StopWatch();
IBundleProvider retVal = super.history(null, null, theSince);
IBundleProvider retVal = super.history(null, null, theSince, theUntil);
ourLog.info("Processed global history in {}ms", w.getMillisAndRestart());
return retVal;
}

View File

@ -38,7 +38,6 @@ import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.ValidationModeEnum;
import ca.uhn.fhir.rest.method.RequestDetails;
import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
@ -92,11 +91,9 @@ public interface IFhirResourceDao<T extends IBaseResource> extends IDao {
TagList getTags(IIdType theResourceId, RequestDetails theRequestDetails);
IBundleProvider history(Date theSince, RequestDetails theRequestDetails);
IBundleProvider history(Date theSince, Date theUntil, RequestDetails theRequestDetails);
IBundleProvider history(IIdType theId, Date theSince, RequestDetails theRequestDetails);
IBundleProvider history(Long theId, Date theSince, RequestDetails theRequestDetails);
IBundleProvider history(IIdType theId, Date theSince, Date theUntil, RequestDetails theRequestDetails);
/**
* Not supported in DSTU1!

View File

@ -47,7 +47,7 @@ public interface IFhirSystemDao<T, MT> extends IDao {
Map<String, Long> getResourceCounts();
IBundleProvider history(Date theDate, RequestDetails theRequestDetails);
IBundleProvider history(Date theDate, Date theUntil, RequestDetails theRequestDetails);
/**
* Marks all indexes as needing fresh indexing

View File

@ -1,12 +1,9 @@
package ca.uhn.fhir.jpa.dao.data;
import java.util.Date;
import java.util.List;
import javax.persistence.TemporalType;
import org.springframework.data.domain.Pageable;
/*
* #%L
* HAPI FHIR JPA Server
@ -68,36 +65,36 @@ public interface IResourceHistoryTableDao extends JpaRepository<ResourceHistoryT
@Param("type") String theType
);
@Query("SELECT t FROM ResourceHistoryTable t WHERE t.myUpdated >= :cutoff ORDER BY t.myUpdated DESC")
List<ResourceHistoryTable> findForAllResourceTypes(
@Temporal(value=TemporalType.TIMESTAMP) @Param("cutoff") Date theCutoff,
Pageable thePageable);
@Query("SELECT t FROM ResourceHistoryTable t WHERE t.myResourceId = :id AND t.myUpdated >= :cutoff ORDER BY t.myUpdated DESC")
List<ResourceHistoryTable> findForResourceInstance(
@Param("id") Long theId,
@Temporal(value=TemporalType.TIMESTAMP) @Param("cutoff") Date theCutoff,
Pageable thePageable);
@Query("SELECT t FROM ResourceHistoryTable t WHERE t.myResourceType = :type AND t.myUpdated >= :cutoff ORDER BY t.myUpdated DESC")
List<ResourceHistoryTable> findForResourceType(
@Param("type") String theType,
@Temporal(value=TemporalType.TIMESTAMP) @Param("cutoff") Date theCutoff,
Pageable thePageable);
@Query("SELECT t FROM ResourceHistoryTable t ORDER BY t.myUpdated DESC")
List<ResourceHistoryTable> findForAllResourceTypes(
Pageable thePageable);
@Query("SELECT t FROM ResourceHistoryTable t WHERE t.myResourceId = :id ORDER BY t.myUpdated DESC")
List<ResourceHistoryTable> findForResourceInstance(
@Param("id") Long theId,
Pageable thePageable);
@Query("SELECT t FROM ResourceHistoryTable t WHERE t.myResourceType = :type ORDER BY t.myUpdated DESC")
List<ResourceHistoryTable> findForResourceType(
@Param("type") String theType,
Pageable thePageable);
// @Query("SELECT t FROM ResourceHistoryTable t WHERE t.myUpdated >= :cutoff ORDER BY t.myUpdated DESC")
// List<ResourceHistoryTable> findForAllResourceTypes(
// @Temporal(value=TemporalType.TIMESTAMP) @Param("cutoff") Date theCutoff,
// Pageable thePageable);
//
// @Query("SELECT t FROM ResourceHistoryTable t WHERE t.myResourceId = :id AND t.myUpdated >= :cutoff ORDER BY t.myUpdated DESC")
// List<ResourceHistoryTable> findForResourceInstance(
// @Param("id") Long theId,
// @Temporal(value=TemporalType.TIMESTAMP) @Param("cutoff") Date theCutoff,
// Pageable thePageable);
//
// @Query("SELECT t FROM ResourceHistoryTable t WHERE t.myResourceType = :type AND t.myUpdated >= :cutoff ORDER BY t.myUpdated DESC")
// List<ResourceHistoryTable> findForResourceType(
// @Param("type") String theType,
// @Temporal(value=TemporalType.TIMESTAMP) @Param("cutoff") Date theCutoff,
// Pageable thePageable);
//
// @Query("SELECT t FROM ResourceHistoryTable t ORDER BY t.myUpdated DESC")
// List<ResourceHistoryTable> findForAllResourceTypes(
// Pageable thePageable);
//
// @Query("SELECT t FROM ResourceHistoryTable t WHERE t.myResourceId = :id ORDER BY t.myUpdated DESC")
// List<ResourceHistoryTable> findForResourceInstance(
// @Param("id") Long theId,
// Pageable thePageable);
//
// @Query("SELECT t FROM ResourceHistoryTable t WHERE t.myResourceType = :type ORDER BY t.myUpdated DESC")
// List<ResourceHistoryTable> findForResourceType(
// @Param("type") String theType,
// Pageable thePageable);
@Query("SELECT t FROM ResourceHistoryTable t WHERE t.myResourceId = :id AND t.myResourceVersion = :version")
ResourceHistoryTable findForIdAndVersion(@Param("id") long theId, @Param("version") long theVersion);

View File

@ -120,6 +120,10 @@ public class Search implements Serializable {
return myLastUpdatedHigh;
}
public Date getLastUpdatedLow() {
return myLastUpdatedLow;
}
public DateRangeParam getLastUpdated() {
if (myLastUpdatedLow == null && myLastUpdatedHigh == null) {
return null;

View File

@ -30,12 +30,14 @@ import org.springframework.beans.factory.annotation.Required;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.rest.annotation.At;
import ca.uhn.fhir.rest.annotation.GetTags;
import ca.uhn.fhir.rest.annotation.History;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.rest.annotation.Since;
import ca.uhn.fhir.rest.method.RequestDetails;
import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.util.CoverageIgnore;
@ -57,11 +59,20 @@ public abstract class BaseJpaResourceProvider<T extends IBaseResource> extends B
return myDao;
}
//@formatter:off
@History
public IBundleProvider getHistoryForResourceInstance(HttpServletRequest theRequest, @IdParam IIdType theId, @Since Date theDate, RequestDetails theRequestDetails) {
public IBundleProvider getHistoryForResourceInstance(
HttpServletRequest theRequest,
@IdParam IIdType theId,
@Since Date theSince,
// @At DateRangeParam theAt,
RequestDetails theRequestDetails) {
//@formatter:on
startRequest(theRequest);
try {
return myDao.history(theId, theDate, theRequestDetails);
// DateRangeParam sinceOrAt = processSinceOrAt(theSince)
return myDao.history(theId, theSince, null, theRequestDetails);
} finally {
endRequest(theRequest);
}
@ -71,7 +82,7 @@ public abstract class BaseJpaResourceProvider<T extends IBaseResource> extends B
public IBundleProvider getHistoryForResourceType(HttpServletRequest theRequest, @Since Date theDate, RequestDetails theRequestDetails) {
startRequest(theRequest);
try {
return myDao.history(theDate, theRequestDetails);
return myDao.history(theDate, null, theRequestDetails);
} finally {
endRequest(theRequest);
}

View File

@ -51,7 +51,7 @@ public class BaseJpaSystemProvider<T, MT> extends BaseJpaProvider {
public IBundleProvider historyServer(HttpServletRequest theRequest, @Since Date theDate, RequestDetails theRequestDetails) {
startRequest(theRequest);
try {
return myDao.history(theDate, theRequestDetails);
return myDao.history(theDate, null, theRequestDetails);
} finally {
endRequest(theRequest);
}

View File

@ -29,9 +29,13 @@ import java.util.Set;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
@ -43,8 +47,6 @@ import org.springframework.transaction.support.TransactionTemplate;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.IDao;
import ca.uhn.fhir.jpa.dao.SearchBuilder;
import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTableDao;
import ca.uhn.fhir.jpa.dao.data.IResourceTableDao;
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
import ca.uhn.fhir.jpa.dao.data.ISearchResultDao;
import ca.uhn.fhir.jpa.entity.BaseHasResource;
@ -61,7 +63,6 @@ public final class PersistedJpaBundleProvider implements IBundleProvider {
private IDao myDao;
private EntityManager myEntityManager;
private PlatformTransactionManager myPlatformTransactionManager;
private IResourceHistoryTableDao myResourceHistoryTableDao;
private ISearchDao mySearchDao;
private Search mySearchEntity;
private ISearchResultDao mySearchResultDao;
@ -72,35 +73,44 @@ public final class PersistedJpaBundleProvider implements IBundleProvider {
myDao = theDao;
}
@Autowired
private IResourceTableDao myResourceTableDao;
protected List<IBaseResource> doHistoryInTransaction(int theFromIndex, int theToIndex) {
Date cutoff = mySearchEntity.getLastUpdatedHigh();
Pageable pageable = toPage(theFromIndex, theToIndex);
List<ResourceHistoryTable> results;
if (cutoff != null) {
CriteriaBuilder cb = myEntityManager.getCriteriaBuilder();
CriteriaQuery<ResourceHistoryTable> q = cb.createQuery(ResourceHistoryTable.class);
Root<ResourceHistoryTable> from = q.from(ResourceHistoryTable.class);
List<Predicate> predicates = new ArrayList<Predicate>();
if (mySearchEntity.getResourceType() == null) {
results = myResourceHistoryTableDao.findForAllResourceTypes(cutoff, pageable);
// All resource types
} else if (mySearchEntity.getResourceId() == null) {
results = myResourceHistoryTableDao.findForResourceType(mySearchEntity.getResourceType(), cutoff, pageable);
predicates.add(cb.equal(from.get("myResourceType"), mySearchEntity.getResourceType()));
} else {
results = myResourceHistoryTableDao.findForResourceInstance(mySearchEntity.getResourceId(), cutoff, pageable);
predicates.add(cb.equal(from.get("myResourceId"), mySearchEntity.getResourceId()));
}
} else {
if (mySearchEntity.getResourceType() == null) {
results = myResourceHistoryTableDao.findForAllResourceTypes(pageable);
} else if (mySearchEntity.getResourceId() == null) {
results = myResourceHistoryTableDao.findForResourceType(mySearchEntity.getResourceType(), pageable);
} else {
results = myResourceHistoryTableDao.findForResourceInstance(mySearchEntity.getResourceId(), pageable);
if (mySearchEntity.getLastUpdatedLow() != null) {
predicates.add(cb.greaterThanOrEqualTo(from.get("myUpdated").as(Date.class), mySearchEntity.getLastUpdatedLow()));
}
if (mySearchEntity.getLastUpdatedHigh() != null) {
predicates.add(cb.lessThanOrEqualTo(from.get("myUpdated").as(Date.class), mySearchEntity.getLastUpdatedHigh()));
}
if (predicates.size() > 0) {
q.where(predicates.toArray(new Predicate[predicates.size()]));
}
q.orderBy(cb.desc(from.get("myUpdated")));
TypedQuery<ResourceHistoryTable> query = myEntityManager.createQuery(q);
if (theToIndex - theFromIndex > 0) {
query.setFirstResult(theFromIndex);
query.setMaxResults(theToIndex - theFromIndex);
}
results = query.getResultList();
ArrayList<IBaseResource> retVal = new ArrayList<IBaseResource>();
for (ResourceHistoryTable next : results) {
BaseHasResource resource;
@ -207,7 +217,6 @@ public final class PersistedJpaBundleProvider implements IBundleProvider {
}
}
});
}
@ -229,15 +238,10 @@ public final class PersistedJpaBundleProvider implements IBundleProvider {
myEntityManager = theEntityManager;
}
public void setPlatformTransactionManager(PlatformTransactionManager thePlatformTransactionManager) {
myPlatformTransactionManager = thePlatformTransactionManager;
}
public void setResourceHistoryTableDao(IResourceHistoryTableDao theResourceHistoryTableDao) {
myResourceHistoryTableDao = theResourceHistoryTableDao;
}
public void setSearchDao(ISearchDao theSearchDao) {
mySearchDao = theSearchDao;
}

View File

@ -18,7 +18,6 @@ import org.hibernate.search.jpa.Search;
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.mockito.Mockito;
@ -125,6 +124,17 @@ public class BaseJpaTest {
return retVal;
}
protected List<String> toUnqualifiedIdValues(IBundleProvider theFound) {
List<String> retVal = new ArrayList<String>();
int size = theFound.size();
ourLog.info("Found {} results", size);
List<IBaseResource> resources = theFound.getResources(0, size);
for (IBaseResource next : resources) {
retVal.add(next.getIdElement().toUnqualified().getValue());
}
return retVal;
}
protected String[] toValues(IIdType... theValues) {
ArrayList<String> retVal = new ArrayList<String>();
for (IIdType next : theValues) {

View File

@ -121,7 +121,7 @@ public class FhirSystemDaoDstu1Test extends BaseJpaTest {
patient.setId(pid);
IIdType newpid3 = ourPatientDao.update(patient, mySrd).getId();
IBundleProvider values = ourSystemDao.history(start, mySrd);
IBundleProvider values = ourSystemDao.history(start, null, mySrd);
assertEquals(4, values.size());
List<IBaseResource> res = values.getResources(0, 4);
@ -140,10 +140,10 @@ public class FhirSystemDaoDstu1Test extends BaseJpaTest {
Thread.sleep(2000);
values = ourLocationDao.history(start, mySrd);
values = ourLocationDao.history(start, null, mySrd);
assertEquals(2, values.size());
values = ourLocationDao.history(lid.getIdPartAsLong(), start, mySrd);
values = ourLocationDao.history(lid.toUnqualifiedVersionless(), start, null, mySrd);
assertEquals(1, values.size());
}

View File

@ -98,7 +98,6 @@ import ca.uhn.fhir.rest.param.UriParam;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.util.TestUtil;
@SuppressWarnings("unchecked")

View File

@ -34,7 +34,6 @@ import java.util.Set;
import org.apache.commons.lang3.RandomStringUtils;
import org.hamcrest.Matchers;
import org.hamcrest.core.StringContains;
import org.hl7.fhir.dstu3.model.Bundle.BundleType;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.AfterClass;
@ -829,7 +828,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
@Test
public void testDeleteResource() {
int initialHistory = myPatientDao.history(null, mySrd).size();
int initialHistory = myPatientDao.history(null, null, mySrd).size();
IIdType id1;
IIdType id2;
@ -871,7 +870,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
// good
}
IBundleProvider history = myPatientDao.history(null, mySrd);
IBundleProvider history = myPatientDao.history(null, null, mySrd);
assertEquals(4 + initialHistory, history.size());
List<IBaseResource> resources = history.getResources(0, 4);
assertNotNull(ResourceMetadataKeyEnum.DELETED_AT.get((IResource) resources.get(0)));
@ -953,7 +952,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
// ok
}
IBundleProvider history = myPatientDao.history(id, null, mySrd);
IBundleProvider history = myPatientDao.history(id, null, null, mySrd);
assertEquals(2, history.size());
assertNotNull(ResourceMetadataKeyEnum.DELETED_AT.get((IResource) history.getResources(0, 1).get(0)));
@ -1180,7 +1179,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
idv2 = myPatientDao.update(patient, mySrd).getId();
}
List<Patient> patients = toList(myPatientDao.history(idv1.toVersionless(), null, mySrd));
List<Patient> patients = toList(myPatientDao.history(idv1.toVersionless(), null, null, mySrd));
assertTrue(patients.size() == 2);
// Newest first
assertEquals("Patient/testHistoryByForcedId/_history/2", patients.get(0).getId().toUnqualified().getValue());
@ -1219,7 +1218,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
}
// By instance
IBundleProvider history = myPatientDao.history(id, null, mySrd);
IBundleProvider history = myPatientDao.history(id, null, null, mySrd);
assertEquals(fullSize + 1, history.size());
for (int i = 0; i < fullSize; i++) {
String expected = id.withVersion(Integer.toString(fullSize + 1 - i)).getValue();
@ -1228,7 +1227,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
}
// By type
history = myPatientDao.history(null, mySrd);
history = myPatientDao.history(null, null, mySrd);
assertEquals(fullSize + 1, history.size());
for (int i = 0; i < fullSize; i++) {
String expected = id.withVersion(Integer.toString(fullSize + 1 - i)).getValue();
@ -1237,7 +1236,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
}
// By server
history = mySystemDao.history(null, mySrd);
history = mySystemDao.history(null, null, mySrd);
assertEquals(fullSize + 1, history.size());
for (int i = 0; i < fullSize; i++) {
String expected = id.withVersion(Integer.toString(fullSize + 1 - i)).getValue();
@ -1250,7 +1249,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
*/
// By instance
history = myPatientDao.history(id, middleDate, mySrd);
history = myPatientDao.history(id, middleDate, null, mySrd);
assertEquals(halfSize, history.size());
for (int i = 0; i < halfSize; i++) {
String expected = id.withVersion(Integer.toString(fullSize + 1 - i)).getValue();
@ -1259,7 +1258,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
}
// By type
history = myPatientDao.history(middleDate, mySrd);
history = myPatientDao.history(middleDate, null, mySrd);
assertEquals(halfSize, history.size());
for (int i = 0; i < halfSize; i++) {
String expected = id.withVersion(Integer.toString(fullSize + 1 - i)).getValue();
@ -1268,7 +1267,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
}
// By server
history = mySystemDao.history(middleDate, mySrd);
history = mySystemDao.history(middleDate, null, mySrd);
assertEquals(halfSize, history.size());
for (int i = 0; i < halfSize; i++) {
String expected = id.withVersion(Integer.toString(fullSize + 1 - i)).getValue();
@ -1286,7 +1285,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
halfSize++;
// By instance
history = myPatientDao.history(id, null, mySrd);
history = myPatientDao.history(id, null, null, mySrd);
for (int i = 0; i < fullSize; i++) {
String expected = id.withVersion(Integer.toString(fullSize + 1 - i)).getValue();
String actual = history.getResources(i, i + 1).get(0).getIdElement().getValue();
@ -1295,7 +1294,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
assertEquals(log(history), fullSize + 1, history.size());
// By type
history = myPatientDao.history(null, mySrd);
history = myPatientDao.history(null, null, mySrd);
for (int i = 0; i < fullSize; i++) {
String expected = id.withVersion(Integer.toString(fullSize + 1 - i)).getValue();
String actual = history.getResources(i, i + 1).get(0).getIdElement().getValue();
@ -1306,7 +1305,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
assertEquals(log(history), fullSize + 1, history.size()); // fails?
// By server
history = mySystemDao.history(null, mySrd);
history = mySystemDao.history(null, null, mySrd);
for (int i = 0; i < fullSize; i++) {
String expected = id.withVersion(Integer.toString(fullSize + 1 - i)).getValue();
String actual = history.getResources(i, i + 1).get(0).getIdElement().getValue();
@ -1319,7 +1318,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
*/
// By instance
history = myPatientDao.history(id, middleDate, mySrd);
history = myPatientDao.history(id, middleDate, null, mySrd);
for (int i = 0; i < halfSize; i++) {
String expected = id.withVersion(Integer.toString(fullSize + 1 - i)).getValue();
String actual = history.getResources(i, i + 1).get(0).getIdElement().getValue();
@ -1328,7 +1327,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
assertEquals(halfSize, history.size());
// By type
history = myPatientDao.history(middleDate, mySrd);
history = myPatientDao.history(middleDate, null, mySrd);
for (int i = 0; i < halfSize; i++) {
String expected = id.withVersion(Integer.toString(fullSize + 1 - i)).getValue();
String actual = history.getResources(i, i + 1).get(0).getIdElement().getValue();
@ -1337,7 +1336,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
assertEquals(halfSize, history.size());
// By server
history = mySystemDao.history(middleDate, mySrd);
history = mySystemDao.history(middleDate, null, mySrd);
for (int i = 0; i < halfSize; i++) {
String expected = id.withVersion(Integer.toString(fullSize + 1 - i)).getValue();
String actual = history.getResources(i, i + 1).get(0).getIdElement().getValue();
@ -1359,7 +1358,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
patient.setId(id);
myPatientDao.update(patient, mySrd);
IBundleProvider history = myPatientDao.history(id, null, mySrd);
IBundleProvider history = myPatientDao.history(id, null, null, mySrd);
assertEquals(3, history.size());
List<IBaseResource> entries = history.getResources(0, 3);
ourLog.info("" + ResourceMetadataKeyEnum.UPDATED.get((IResource) entries.get(0)));

View File

@ -111,7 +111,7 @@ public class FhirResourceDaoDstu2UpdateTest extends BaseJpaDstu2Test {
* Get history
*/
IBundleProvider historyBundle = myPatientDao.history(outcome.getId(), null, mySrd);
IBundleProvider historyBundle = myPatientDao.history(outcome.getId(), null, null, mySrd);
assertEquals(2, historyBundle.size());

View File

@ -835,7 +835,7 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest {
// ok
}
IBundleProvider history = myPatientDao.history(id, null, mySrd);
IBundleProvider history = myPatientDao.history(id, null, null, mySrd);
assertEquals(2, history.size());
assertNotNull(ResourceMetadataKeyEnum.DELETED_AT.get((IResource) history.getResources(0, 1).get(0)));

View File

@ -85,6 +85,8 @@ import org.junit.Ignore;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import com.google.common.collect.Lists;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
@ -1124,7 +1126,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
@Test
public void testDeleteResource() {
int initialHistory = myPatientDao.history(null, mySrd).size();
int initialHistory = myPatientDao.history((Date)null, null, mySrd).size();
IIdType id1;
IIdType id2;
@ -1166,7 +1168,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
// good
}
IBundleProvider history = myPatientDao.history(null, mySrd);
IBundleProvider history = myPatientDao.history((Date)null, null, mySrd);
assertEquals(4 + initialHistory, history.size());
List<IBaseResource> resources = history.getResources(0, 4);
assertNotNull(ResourceMetadataKeyEnum.DELETED_AT.get((IAnyResource) resources.get(0)));
@ -1246,7 +1248,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
// ok
}
IBundleProvider history = myPatientDao.history(id, null, mySrd);
IBundleProvider history = myPatientDao.history(id, null, null, mySrd);
assertEquals(2, history.size());
assertNotNull(ResourceMetadataKeyEnum.DELETED_AT.get((IAnyResource) history.getResources(0, 1).get(0)));
@ -1470,7 +1472,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
idv2 = myPatientDao.update(patient, mySrd).getId();
}
List<Patient> patients = toList(myPatientDao.history(idv1.toVersionless(), null, mySrd));
List<Patient> patients = toList(myPatientDao.history(idv1.toVersionless(), null, null, mySrd));
assertTrue(patients.size() == 2);
// Newest first
assertEquals("Patient/testHistoryByForcedId/_history/2", patients.get(0).getIdElement().toUnqualified().getValue());
@ -1478,6 +1480,36 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
assertNotEquals(idv1, idv2);
}
@Test
public void testHistoryWithFromAndTo() throws Exception {
String methodName = "testHistoryWithFromAndTo";
Patient patient = new Patient();
patient.addName().addFamily(methodName);
IIdType id = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
List<Date> preDates = Lists.newArrayList();
List<String> ids = Lists.newArrayList();
for (int i = 0; i < 10; i++) {
Thread.sleep(10);
preDates.add(new Date());
patient.setId(id);
patient.getName().get(0).getFamily().get(0).setValue(methodName + "_i");
ids.add(myPatientDao.update(patient, mySrd).getId().toUnqualified().getValue());
}
List<String> idValues;
idValues = toUnqualifiedIdValues(myPatientDao.history(id, preDates.get(0), preDates.get(3), mySrd));
assertThat(idValues, contains(ids.get(2), ids.get(1), ids.get(0)));
idValues = toUnqualifiedIdValues(myPatientDao.history(preDates.get(0), preDates.get(3), mySrd));
assertThat(idValues, contains(ids.get(2), ids.get(1), ids.get(0)));
idValues = toUnqualifiedIdValues(mySystemDao.history(preDates.get(0), preDates.get(3), mySrd));
assertThat(idValues, contains(ids.get(2), ids.get(1), ids.get(0)));
}
@Test
public void testHistoryOverMultiplePages() throws Exception {
String methodName = "testHistoryOverMultiplePages";
@ -1502,7 +1534,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
}
// By instance
IBundleProvider history = myPatientDao.history(id, null, mySrd);
IBundleProvider history = myPatientDao.history(id, null, null, mySrd);
assertEquals(fullSize + 1, history.size());
for (int i = 0; i < fullSize; i++) {
String expected = id.withVersion(Integer.toString(fullSize + 1 - i)).getValue();
@ -1511,7 +1543,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
}
// By type
history = myPatientDao.history(null, mySrd);
history = myPatientDao.history((Date)null, null, mySrd);
assertEquals(fullSize + 1, history.size());
for (int i = 0; i < fullSize; i++) {
String expected = id.withVersion(Integer.toString(fullSize + 1 - i)).getValue();
@ -1520,7 +1552,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
}
// By server
history = mySystemDao.history(null, mySrd);
history = mySystemDao.history(null, null, mySrd);
assertEquals(fullSize + 1, history.size());
for (int i = 0; i < fullSize; i++) {
String expected = id.withVersion(Integer.toString(fullSize + 1 - i)).getValue();
@ -1533,7 +1565,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
*/
// By instance
history = myPatientDao.history(id, middleDate, mySrd);
history = myPatientDao.history(id, middleDate, null, mySrd);
assertEquals(halfSize, history.size());
for (int i = 0; i < halfSize; i++) {
String expected = id.withVersion(Integer.toString(fullSize + 1 - i)).getValue();
@ -1542,7 +1574,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
}
// By type
history = myPatientDao.history(middleDate, mySrd);
history = myPatientDao.history(middleDate, null, mySrd);
assertEquals(halfSize, history.size());
for (int i = 0; i < halfSize; i++) {
String expected = id.withVersion(Integer.toString(fullSize + 1 - i)).getValue();
@ -1551,7 +1583,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
}
// By server
history = mySystemDao.history(middleDate, mySrd);
history = mySystemDao.history(middleDate, null, mySrd);
assertEquals(halfSize, history.size());
for (int i = 0; i < halfSize; i++) {
String expected = id.withVersion(Integer.toString(fullSize + 1 - i)).getValue();
@ -1569,7 +1601,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
halfSize++;
// By instance
history = myPatientDao.history(id, null, mySrd);
history = myPatientDao.history(id, null, null, mySrd);
assertEquals(fullSize + 1, history.size());
for (int i = 0; i < fullSize; i++) {
String expected = id.withVersion(Integer.toString(fullSize + 1 - i)).getValue();
@ -1578,7 +1610,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
}
// By type
history = myPatientDao.history(null, mySrd);
history = myPatientDao.history((Date)null, null, mySrd);
assertEquals(fullSize + 1, history.size());
for (int i = 0; i < fullSize; i++) {
String expected = id.withVersion(Integer.toString(fullSize + 1 - i)).getValue();
@ -1587,7 +1619,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
}
// By server
history = mySystemDao.history(null, mySrd);
history = mySystemDao.history(null, null, mySrd);
assertEquals(fullSize + 1, history.size());
for (int i = 0; i < fullSize; i++) {
String expected = id.withVersion(Integer.toString(fullSize + 1 - i)).getValue();
@ -1600,7 +1632,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
*/
// By instance
history = myPatientDao.history(id, middleDate, mySrd);
history = myPatientDao.history(id, middleDate, null, mySrd);
assertEquals(halfSize, history.size());
for (int i = 0; i < halfSize; i++) {
String expected = id.withVersion(Integer.toString(fullSize + 1 - i)).getValue();
@ -1609,7 +1641,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
}
// By type
history = myPatientDao.history(middleDate, mySrd);
history = myPatientDao.history(middleDate, null, mySrd);
assertEquals(halfSize, history.size());
for (int i = 0; i < halfSize; i++) {
String expected = id.withVersion(Integer.toString(fullSize + 1 - i)).getValue();
@ -1618,7 +1650,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
}
// By server
history = mySystemDao.history(middleDate, mySrd);
history = mySystemDao.history(middleDate, null, mySrd);
assertEquals(halfSize, history.size());
for (int i = 0; i < halfSize; i++) {
String expected = id.withVersion(Integer.toString(fullSize + 1 - i)).getValue();
@ -1644,7 +1676,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
// No since
IBundleProvider history = myPatientDao.history(null, mySrd);
IBundleProvider history = myPatientDao.history((Date)null, null, mySrd);
assertEquals(1, history.size());
Patient outPatient = (Patient) history.getResources(0, 1).get(0);
assertEquals("version1", inPatient.getName().get(0).getFamilyAsSingleString());
@ -1653,7 +1685,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
// Before since
history = myPatientDao.history(before, mySrd);
history = myPatientDao.history(before, null, mySrd);
assertEquals(1, history.size());
outPatient = (Patient) history.getResources(0, 1).get(0);
assertEquals("version1", inPatient.getName().get(0).getFamilyAsSingleString());
@ -1662,7 +1694,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
// After since
history = myPatientDao.history(after, mySrd);
history = myPatientDao.history(after, null, mySrd);
assertEquals(0, history.size());
}
@ -1674,7 +1706,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
inPatient.getMeta().addProfile("http://example.com/1");
IIdType id = myPatientDao.create(inPatient, mySrd).getId().toUnqualifiedVersionless();
IBundleProvider history = myPatientDao.history(null, mySrd);
IBundleProvider history = myPatientDao.history((Date)null, null, mySrd);
assertEquals(1, history.size());
Patient outPatient = (Patient) history.getResources(0, 1).get(0);
assertEquals("version1", inPatient.getName().get(0).getFamilyAsSingleString());
@ -1688,7 +1720,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
inPatient.getMeta().addProfile("http://example.com/2");
myPatientDao.metaAddOperation(id, inPatient.getMeta(), mySrd);
history = myPatientDao.history(null, mySrd);
history = myPatientDao.history((Date)null, null, mySrd);
assertEquals(1, history.size());
outPatient = (Patient) history.getResources(0, 1).get(0);
assertEquals("version1", inPatient.getName().get(0).getFamilyAsSingleString());
@ -1704,7 +1736,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
inPatient.getName().get(0).addFamily("version2");
myPatientDao.update(inPatient, mySrd);
history = myPatientDao.history(null, mySrd);
history = myPatientDao.history((Date)null, null, mySrd);
assertEquals(2, history.size());
outPatient = (Patient) history.getResources(0, 2).get(0);
assertEquals("version1 version2", outPatient.getName().get(0).getFamilyAsSingleString());
@ -1730,7 +1762,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
patient.setId(id);
myPatientDao.update(patient, mySrd);
IBundleProvider history = myPatientDao.history(id, null, mySrd);
IBundleProvider history = myPatientDao.history(id, null, null, mySrd);
List<IBaseResource> entries = history.getResources(0, 3);
ourLog.info(((IAnyResource) entries.get(0)).getIdElement() + " - " + ((IAnyResource) entries.get(0)).getMeta().getLastUpdated());
ourLog.info(((IAnyResource) entries.get(1)).getIdElement() + " - " + ((IAnyResource) entries.get(1)).getMeta().getLastUpdated());
@ -1754,7 +1786,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
@Test
public void testHistoryWithInvalidId() throws Exception {
try {
myPatientDao.history(new IdDt("Patient/FOOFOOFOO"), null, mySrd);
myPatientDao.history(new IdDt("Patient/FOOFOOFOO"), null, null, mySrd);
fail();
} catch (ResourceNotFoundException e) {
assertEquals("Resource Patient/FOOFOOFOO is not known", e.getMessage());

View File

@ -107,7 +107,7 @@ public class FhirResourceDaoDstu3UpdateTest extends BaseJpaDstu3Test {
* Get history
*/
IBundleProvider historyBundle = myPatientDao.history(outcome.getId(), null, mySrd);
IBundleProvider historyBundle = myPatientDao.history(outcome.getId(), null, null, mySrd);
assertEquals(2, historyBundle.size());

View File

@ -1059,7 +1059,7 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
// ok
}
IBundleProvider history = myPatientDao.history(id, null, mySrd);
IBundleProvider history = myPatientDao.history(id, null, null, mySrd);
assertEquals(2, history.size());
assertNotNull(ResourceMetadataKeyEnum.DELETED_AT.get((IAnyResource) history.getResources(0, 1).get(0)));

View File

@ -279,8 +279,8 @@ public class SystemProviderDstu2Test extends BaseJpaDstu2Test {
@Test
public void testGetOperationDefinition() {
OperationDefinition op = ourClient.read(OperationDefinition.class, "get-resource-counts");
assertEquals("$get-resource-counts", op.getCode());
OperationDefinition op = ourClient.read(OperationDefinition.class, "-s-get-resource-counts");
assertEquals("get-resource-counts", op.getCode());
}
@Test

View File

@ -100,6 +100,7 @@ import org.junit.Test;
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.SummaryEnum;
@ -1868,7 +1869,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
ourLog.info(value.getTime() + "");
ourLog.info(before.getTime() + "");
assertTrue(value.after(before));
assertTrue(value.before(after));
assertTrue(new InstantDt(value) + " should be before " + new InstantDt(after), value.before(after));
}
/**

View File

@ -453,7 +453,7 @@ public class SystemProviderDstu3Test extends BaseJpaDstu3Test {
@Test
public void testGetOperationDefinition() {
OperationDefinition op = ourClient.read(OperationDefinition.class, "get-resource-counts");
OperationDefinition op = ourClient.read(OperationDefinition.class, "-s-get-resource-counts");
assertEquals("get-resource-counts", op.getCode());
}

View File

@ -1,16 +1,19 @@
package ca.uhn.fhir.rest.param;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
import org.junit.AfterClass;
import org.junit.Test;
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum;
import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.model.primitive.InstantDt;
@ -31,6 +34,23 @@ public class DateRangeParamTest {
return new DateRangeParam(new DateParam(theString));
}
@Test
public void testRangeFromDates() {
TimeZone tz = TimeZone.getDefault();
TimeZone.setDefault(TimeZone.getTimeZone("America/Toronto"));
try {
Date startDate = new InstantDt("2010-01-01T00:00:00.000Z").getValue();
Date endDate = new InstantDt("2010-01-01T00:00:00.001Z").getValue();
DateTimeDt startDateTime = new DateTimeDt(startDate, TemporalPrecisionEnum.MILLI);
DateTimeDt endDateTime = new DateTimeDt(endDate, TemporalPrecisionEnum.MILLI);
DateRangeParam range = new DateRangeParam(startDateTime, endDateTime);
assertEquals("2009-12-31T19:00:00.000-05:00", range.getValuesAsQueryTokens().get(0).getValueAsString());
} finally {
TimeZone.setDefault(tz);
}
}
@Test
public void testRange() {
InstantDt start = new InstantDt("2015-09-23T07:43:34.811-04:00");
@ -41,8 +61,7 @@ public class DateRangeParamTest {
assertEquals(QuantityCompararatorEnum.LESSTHAN, upperBound.getComparator());
/*
* When DateParam (which extends DateTimeDt) gets passed in, make sure we preserve the
* comparators..
* When DateParam (which extends DateTimeDt) gets passed in, make sure we preserve the comparators..
*/
DateRangeParam param = new DateRangeParam(lowerBound, upperBound);
ourLog.info(param.toString());
@ -169,7 +188,6 @@ public class DateRangeParamTest {
return new Date(ourFmt.parse(theString).getTime() - 1L);
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();

View File

@ -10,6 +10,7 @@ import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
@ -49,12 +50,17 @@ import ca.uhn.fhir.util.TestUtil;
public class PlainProviderTest {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(PlainProviderTest.class);
private int myPort;
private Server myServer;
private CloseableHttpClient myClient;
private static final FhirContext ourCtx = FhirContext.forDstu1();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(PlainProviderTest.class);
private CloseableHttpClient myClient;
private int myPort;
private RestfulServer myRestfulServer;
private Server myServer;
@After
public void after() throws Exception {
myServer.stop();
}
@Before
public void before() throws Exception {
@ -76,9 +82,60 @@ public class PlainProviderTest {
}
@After
public void after() throws Exception {
myServer.stop();
@Test
public void testGlobalHistory() throws Exception {
GlobalHistoryProvider provider = new GlobalHistoryProvider();
myRestfulServer.setProviders(provider);
myServer.start();
String baseUri = "http://localhost:" + myPort + "/fhir/context";
HttpResponse status = myClient.execute(new HttpGet(baseUri + "/_history?_since=2012-01-02T00%3A01%3A02&_count=12"));
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
Bundle bundle = ourCtx.newXmlParser().parseBundle(responseContent);
assertEquals(3, bundle.getEntries().size());
assertThat(provider.myLastSince.getValueAsString(), StringStartsWith.startsWith("2012-01-02T00:01:02"));
assertThat(provider.myLastCount.getValueAsString(), IsEqual.equalTo("12"));
status = myClient.execute(new HttpGet(baseUri + "/_history?&_count=12"));
responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
bundle = ourCtx.newXmlParser().parseBundle(responseContent);
assertEquals(3, bundle.getEntries().size());
assertNull(provider.myLastSince);
assertThat(provider.myLastCount.getValueAsString(), IsEqual.equalTo("12"));
status =myClient.execute(new HttpGet(baseUri + "/_history?_since=2012-01-02T00%3A01%3A02"));
responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
bundle = ourCtx.newXmlParser().parseBundle(responseContent);
assertEquals(3, bundle.getEntries().size());
assertThat(provider.myLastSince.getValueAsString(), StringStartsWith.startsWith("2012-01-02T00:01:02"));
assertNull(provider.myLastCount);
}
@Test
public void testGlobalHistoryNoParams() throws Exception {
GlobalHistoryProvider provider = new GlobalHistoryProvider();
myRestfulServer.setProviders(provider);
myServer.start();
String baseUri = "http://localhost:" + myPort + "/fhir/context";
CloseableHttpResponse status = myClient.execute(new HttpGet(baseUri + "/_history"));
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
Bundle bundle = ourCtx.newXmlParser().parseBundle(responseContent);
assertEquals(3, bundle.getEntries().size());
assertNull(provider.myLastSince);
assertNull(provider.myLastCount);
}
@Test
@ -109,110 +166,40 @@ public class PlainProviderTest {
httpGet.releaseConnection();
}
@Test
public void testGlobalHistory() throws Exception {
GlobalHistoryProvider provider = new GlobalHistoryProvider();
myRestfulServer.setProviders(provider);
myServer.start();
String baseUri = "http://localhost:" + myPort + "/fhir/context";
HttpResponse status = myClient.execute(new HttpGet(baseUri + "/_history?_since=2012-01-02T00%3A01%3A02&_count=12"));
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
Bundle bundle = ourCtx.newXmlParser().parseBundle(responseContent);
assertEquals(3, bundle.getEntries().size());
assertThat(provider.myLastSince.getValueAsString(), StringStartsWith.startsWith("2012-01-02T00:01:02"));
assertThat(provider.myLastCount.getValueAsString(), IsEqual.equalTo("12"));
status = myClient.execute(new HttpGet(baseUri + "/_history?&_count=12"));
responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
bundle = ourCtx.newXmlParser().parseBundle(responseContent);
assertEquals(3, bundle.getEntries().size());
assertNull(provider.myLastSince.getValueAsString());
assertThat(provider.myLastCount.getValueAsString(), IsEqual.equalTo("12"));
status =myClient.execute(new HttpGet(baseUri + "/_history?_since=2012-01-02T00%3A01%3A02"));
responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
bundle = ourCtx.newXmlParser().parseBundle(responseContent);
assertEquals(3, bundle.getEntries().size());
assertThat(provider.myLastSince.getValueAsString(), StringStartsWith.startsWith("2012-01-02T00:01:02"));
assertNull(provider.myLastCount.getValueAsString());
status =myClient.execute(new HttpGet(baseUri + "/_history"));
responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
bundle = ourCtx.newXmlParser().parseBundle(responseContent);
assertEquals(3, bundle.getEntries().size());
assertNull(provider.myLastSince.getValueAsString());
assertNull(provider.myLastCount.getValueAsString());
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
/**
* Created by dsotnikov on 2/25/2014.
*/
public static class SearchProvider {
public Map<String, Patient> getIdToPatient() {
Map<String, Patient> idToPatient = new HashMap<String, Patient>();
{
Patient patient = createPatient();
idToPatient.put("1", patient);
private static Organization createOrganization() {
Organization retVal = new Organization();
retVal.setId("1");
retVal.addIdentifier();
retVal.getIdentifier().get(0).setUse(IdentifierUseEnum.OFFICIAL);
retVal.getIdentifier().get(0).setSystem(new UriDt("urn:hapitest:mrns"));
retVal.getIdentifier().get(0).setValue("00001");
retVal.getName().setValue("Test Org");
return retVal;
}
{
private static Patient createPatient() {
Patient patient = new Patient();
patient.getIdentifier().add(new IdentifierDt());
patient.setId("1");
patient.addIdentifier();
patient.getIdentifier().get(0).setUse(IdentifierUseEnum.OFFICIAL);
patient.getIdentifier().get(0).setSystem(new UriDt("urn:hapitest:mrns"));
patient.getIdentifier().get(0).setValue("00002");
patient.getName().add(new HumanNameDt());
patient.getIdentifier().get(0).setValue("00001");
patient.addName();
patient.getName().get(0).addFamily("Test");
patient.getName().get(0).addGiven("PatientTwo");
patient.getGender().setText("F");
idToPatient.put("2", patient);
}
return idToPatient;
}
@Search(type = Patient.class)
public Patient findPatient(@RequiredParam(name = Patient.SP_IDENTIFIER) IdentifierDt theIdentifier) {
for (Patient next : getIdToPatient().values()) {
for (IdentifierDt nextId : next.getIdentifier()) {
if (nextId.matchesSystemAndValue(theIdentifier)) {
return next;
}
}
}
return null;
}
/**
* Retrieve the resource by its identifier
*
* @param theId
* The resource identity
* @return The resource
*/
@Read(type = Patient.class)
public Patient getPatientById(@IdParam IdDt theId) {
return getIdToPatient().get(theId.getValue());
}
patient.getName().get(0).addGiven("PatientOne");
patient.getGender().setText("M");
return patient;
}
public static class GlobalHistoryProvider {
private InstantDt myLastSince;
private IntegerDt myLastCount;
private InstantDt myLastSince;
@History
public List<IResource> getGlobalHistory(@Since InstantDt theSince, @Count IntegerDt theCount) {
@ -246,35 +233,54 @@ public class PlainProviderTest {
}
private static Patient createPatient() {
public static class SearchProvider {
@Search(type = Patient.class)
public Patient findPatient(@RequiredParam(name = Patient.SP_IDENTIFIER) IdentifierDt theIdentifier) {
for (Patient next : getIdToPatient().values()) {
for (IdentifierDt nextId : next.getIdentifier()) {
if (nextId.matchesSystemAndValue(theIdentifier)) {
return next;
}
}
}
return null;
}
public Map<String, Patient> getIdToPatient() {
Map<String, Patient> idToPatient = new HashMap<String, Patient>();
{
Patient patient = createPatient();
idToPatient.put("1", patient);
}
{
Patient patient = new Patient();
patient.setId("1");
patient.addIdentifier();
patient.getIdentifier().add(new IdentifierDt());
patient.getIdentifier().get(0).setUse(IdentifierUseEnum.OFFICIAL);
patient.getIdentifier().get(0).setSystem(new UriDt("urn:hapitest:mrns"));
patient.getIdentifier().get(0).setValue("00001");
patient.addName();
patient.getIdentifier().get(0).setValue("00002");
patient.getName().add(new HumanNameDt());
patient.getName().get(0).addFamily("Test");
patient.getName().get(0).addGiven("PatientOne");
patient.getGender().setText("M");
return patient;
patient.getName().get(0).addGiven("PatientTwo");
patient.getGender().setText("F");
idToPatient.put("2", patient);
}
return idToPatient;
}
/**
* Retrieve the resource by its identifier
*
* @param theId
* The resource identity
* @return The resource
*/
@Read(type = Patient.class)
public Patient getPatientById(@IdParam IdDt theId) {
return getIdToPatient().get(theId.getValue());
}
private static Organization createOrganization() {
Organization retVal = new Organization();
retVal.setId("1");
retVal.addIdentifier();
retVal.getIdentifier().get(0).setUse(IdentifierUseEnum.OFFICIAL);
retVal.getIdentifier().get(0).setSystem(new UriDt("urn:hapitest:mrns"));
retVal.getIdentifier().get(0).setValue("00001");
retVal.getName().setValue("Test Org");
return retVal;
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
}

View File

@ -163,14 +163,14 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
retVal.append(theMethodBinding.getResourceName());
}
retVal.append('_');
retVal.append('-');
if (theMethodBinding.isCanOperateAtInstanceLevel()) {
retVal.append('i');
}
if (theMethodBinding.isCanOperateAtServerLevel()) {
retVal.append('s');
}
retVal.append('_');
retVal.append('-');
// Exclude the leading $
retVal.append(theMethodBinding.getName(), 1, theMethodBinding.getName().length());
@ -318,7 +318,7 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
OperationMethodBinding methodBinding = (OperationMethodBinding) nextMethodBinding;
String opName = myOperationBindingToName.get(methodBinding);
if (operationNames.add(opName)) {
rest.addOperation().setName(methodBinding.getName()).getDefinition().setReference("OperationDefinition/" + opName);
rest.addOperation().setName(methodBinding.getName().substring(1)).getDefinition().setReference("OperationDefinition/" + opName);
}
}
}

View File

@ -3,15 +3,12 @@ package ca.uhn.fhir.model.primitive;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.either;
import static org.hamcrest.Matchers.endsWith;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import static org.junit.Assert.*;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.TimeZone;
@ -36,30 +33,15 @@ public class BaseDateTimeDtDstu2Test {
private static Locale ourDefaultLocale;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseDateTimeDtDstu2Test.class);
private SimpleDateFormat myDateInstantParser;
private FastDateFormat myDateInstantZoneParser;
@Before
public void before() {
myDateInstantParser = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
myDateInstantZoneParser = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss.SSSZ", TimeZone.getTimeZone("GMT-02:00"));
}
@Test
public void testEncodeZeroOffset() {
DateTimeDt dt = new DateTimeDt();
dt.setValueAsString("2011-01-01T12:00:00-04:00");
dt.setTimeZone(TimeZone.getTimeZone("GMT-0:00"));
String val = dt.getValueAsString();
assertEquals("2011-01-01T16:00:00+00:00", val);
}
@Test
public void setTimezoneToZulu() {
DateTimeDt dt = new DateTimeDt(new Date(816411488000L));
@ -87,6 +69,37 @@ public class BaseDateTimeDtDstu2Test {
}
}
@Test
public void testDateFormatsInvalid() {
// No spaces in dates
verifyFails("1974 12-25");
verifyFails("1974-12 25");
// No letters
verifyFails("A974-12-25");
verifyFails("1974-A2-25");
verifyFails("1974-12-A5");
// Date shouldn't have a time zone
verifyFails("1974-12-25Z");
verifyFails("1974-12-25+10:00");
// Out of range
verifyFails("1974-13-25");
verifyFails("1974-12-32");
verifyFails("2015-02-29");
verifyFails("-016-02-01");
verifyFails("2016--2-01");
verifyFails("2016-02--1");
// Invalid length
verifyFails("2");
verifyFails("20");
verifyFails("201");
verifyFails("2016-0");
verifyFails("2016-02-0");
}
/**
* Test for #57
*/
@ -112,6 +125,46 @@ public class BaseDateTimeDtDstu2Test {
assertThat(outcomeStr, containsString("date-primitive"));
}
@Test
public void testDateTimeFormatsInvalid() {
// Bad timezone
verifyFails("1974-12-01T00:00:00A");
verifyFails("1974-12-01T00:00:00=00:00");
verifyFails("1974-12-01T00:00:00+");
verifyFails("1974-12-01T00:00:00+25:00");
verifyFails("1974-12-01T00:00:00+00:61");
verifyFails("1974-12-01T00:00:00+00 401");
verifyFails("1974-12-01T00:00:00+0");
verifyFails("1974-12-01T00:00:00+01");
verifyFails("1974-12-01T00:00:00+011");
verifyFails("1974-12-01T00:00:00+0110");
// Out of range
verifyFails("1974-12-25T25:00:00Z");
verifyFails("1974-12-25T24:00:00Z");
verifyFails("1974-12-25T23:60:00Z");
verifyFails("1974-12-25T23:59:60Z");
// Invalid Separators
verifyFails("1974-12-25T23 59:00Z");
verifyFails("1974-12-25T23:59 00Z");
// Invalid length
verifyFails("1974-12-25T2Z");
verifyFails("1974-12-25T22:Z");
verifyFails("1974-12-25T22:1Z");
verifyFails("1974-12-25T22:11:Z");
verifyFails("1974-12-25T22:11:1Z");
}
@Test
public void testDateTimeFormatsInvalidMillis() {
verifyFails("1974-12-01T00:00:00.AZ");
verifyFails("1974-12-01T00:00:00.-Z");
verifyFails("1974-12-01T00:00:00.-1Z");
verifyFails("1974-12-01T00:00:00..1111Z");
}
@Test
public void testDateTimeInLocalTimezone() {
DateTimeDt dt = DateTimeDt.withCurrentTime();
@ -128,6 +181,28 @@ public class BaseDateTimeDtDstu2Test {
assertThat(offset, either(endsWith("-05:00")).or(endsWith("-04:00")));
}
@Test
public void testEncodeZeroOffset() {
DateTimeDt dt = new DateTimeDt();
dt.setValueAsString("2011-01-01T12:00:00-04:00");
dt.setTimeZone(TimeZone.getTimeZone("GMT-0:00"));
String val = dt.getValueAsString();
assertEquals("2011-01-01T16:00:00+00:00", val);
}
@Test
public void testGetValueAsCalendar() {
assertNull(new InstantDt().getValueAsCalendar());
InstantDt dt = new InstantDt("2011-01-03T07:11:22.002-08:00");
GregorianCalendar cal = dt.getValueAsCalendar();
assertEquals(2011, cal.get(Calendar.YEAR));
assertEquals(7, cal.get(Calendar.HOUR_OF_DAY));
assertEquals(2, cal.get(Calendar.MILLISECOND));
assertEquals("GMT-08:00", cal.getTimeZone().getID());
}
@Test
public void testInstantInLocalTimezone() {
@ -163,17 +238,16 @@ public class BaseDateTimeDtDstu2Test {
}
@Test
public void testParseDate() {
new DateDt("2012-03-31");
public void testNewInstance() throws InterruptedException {
InstantDt now = InstantDt.withCurrentTime();
Thread.sleep(100);
InstantDt then = InstantDt.withCurrentTime();
assertTrue(now.getValue().before(then.getValue()));
}
/*
* Just to be lenient
*/
@Test
public void testParseIgnoresLeadingAndTrailingSpace() {
DateTimeDt dt = new DateTimeDt(" 2014-10-11T12:11:00Z ");
assertEquals("2014-10-11 10:11:00.000-0200", myDateInstantZoneParser.format(dt.getValue()));
public void testParseDate() {
new DateDt("2012-03-31");
}
@Test
@ -231,95 +305,13 @@ public class BaseDateTimeDtDstu2Test {
validateMillisPartial("2015-06-22T00:00:00.00123Z", 1);
}
private void validateMillisPartial(String input, int expected) {
InstantDt dt = new InstantDt();
dt.setValueAsString(input);
Date date = dt.getValue();
assertEquals(expected, date.getTime() % 1000);
}
/*
* Just to be lenient
*/
@Test
public void testDateTimeFormatsInvalidMillis() {
verifyFails("1974-12-01T00:00:00.AZ");
verifyFails("1974-12-01T00:00:00.-Z");
verifyFails("1974-12-01T00:00:00.-1Z");
verifyFails("1974-12-01T00:00:00..1111Z");
}
@Test
public void testDateTimeFormatsInvalid() {
// Bad timezone
verifyFails("1974-12-01T00:00:00A");
verifyFails("1974-12-01T00:00:00=00:00");
verifyFails("1974-12-01T00:00:00+");
verifyFails("1974-12-01T00:00:00+25:00");
verifyFails("1974-12-01T00:00:00+00:61");
verifyFails("1974-12-01T00:00:00+00 401");
verifyFails("1974-12-01T00:00:00+0");
verifyFails("1974-12-01T00:00:00+01");
verifyFails("1974-12-01T00:00:00+011");
verifyFails("1974-12-01T00:00:00+0110");
// Out of range
verifyFails("1974-12-25T25:00:00Z");
verifyFails("1974-12-25T24:00:00Z");
verifyFails("1974-12-25T23:60:00Z");
verifyFails("1974-12-25T23:59:60Z");
// Invalid Separators
verifyFails("1974-12-25T23 59:00Z");
verifyFails("1974-12-25T23:59 00Z");
// Invalid length
verifyFails("1974-12-25T2Z");
verifyFails("1974-12-25T22:Z");
verifyFails("1974-12-25T22:1Z");
verifyFails("1974-12-25T22:11:Z");
verifyFails("1974-12-25T22:11:1Z");
}
@Test
public void testDateFormatsInvalid() {
// No spaces in dates
verifyFails("1974 12-25");
verifyFails("1974-12 25");
// No letters
verifyFails("A974-12-25");
verifyFails("1974-A2-25");
verifyFails("1974-12-A5");
// Date shouldn't have a time zone
verifyFails("1974-12-25Z");
verifyFails("1974-12-25+10:00");
// Out of range
verifyFails("1974-13-25");
verifyFails("1974-12-32");
verifyFails("2015-02-29");
verifyFails("-016-02-01");
verifyFails("2016--2-01");
verifyFails("2016-02--1");
// Invalid length
verifyFails("2");
verifyFails("20");
verifyFails("201");
verifyFails("2016-0");
verifyFails("2016-02-0");
}
private void verifyFails(String input) {
try {
DateTimeDt dt = new DateTimeDt();
dt.setValueAsString(input);
fail();
} catch (ca.uhn.fhir.parser.DataFormatException e) {
assertThat(e.getMessage(), containsString("Invalid date/time format: \"" + input + "\""));
}
public void testParseIgnoresLeadingAndTrailingSpace() {
DateTimeDt dt = new DateTimeDt(" 2014-10-11T12:11:00Z ");
assertEquals("2014-10-11 10:11:00.000-0200", myDateInstantZoneParser.format(dt.getValue()));
}
@Test
@ -328,17 +320,15 @@ public class BaseDateTimeDtDstu2Test {
new DateTimeDt("2010-01-01T00:00:00.1234-09:00Z");
fail();
} catch (DataFormatException e) {
assertEquals("Invalid FHIR date/time string: 2010-01-01T00:00:00.1234-09:00Z", e.getMessage());
assertEquals("Invalid date/time format: \"2010-01-01T00:00:00.1234-09:00Z\"", e.getMessage());
}
}
@Test(expected = DataFormatException.class)
public void testParseMalformatted() throws DataFormatException {
new DateTimeDt("20120102");
}
@Test
public void testParseMilli() throws DataFormatException {
InstantDt dt = new InstantDt();
@ -451,12 +441,12 @@ public class BaseDateTimeDtDstu2Test {
DateTimeDt dt = new DateTimeDt("2010-01-01T00:00:00.1-09:00");
assertEquals("2010-01-01T00:00:00.1-09:00", dt.getValueAsString());
assertEquals("2010-01-01 04:00:00.001", myDateInstantParser.format(dt.getValue()));
assertEquals("2010-01-01 04:00:00.100", myDateInstantParser.format(dt.getValue()));
assertEquals("GMT-09:00", dt.getTimeZone().getID());
assertEquals(-32400000L, dt.getTimeZone().getRawOffset());
dt.setTimeZoneZulu(true);
assertEquals("2010-01-01T09:00:00.001Z", dt.getValueAsString());
assertEquals("2010-01-01T09:00:00.100Z", dt.getValueAsString());
}
@Test
@ -466,12 +456,12 @@ public class BaseDateTimeDtDstu2Test {
DateTimeDt dt = new DateTimeDt("2010-01-01T00:00:00.12-09:00");
assertEquals("2010-01-01T00:00:00.12-09:00", dt.getValueAsString());
assertEquals("2010-01-01 04:00:00.012", myDateInstantParser.format(dt.getValue()));
assertEquals("2010-01-01 04:00:00.120", myDateInstantParser.format(dt.getValue()));
assertEquals("GMT-09:00", dt.getTimeZone().getID());
assertEquals(-32400000L, dt.getTimeZone().getRawOffset());
dt.setTimeZoneZulu(true);
assertEquals("2010-01-01T09:00:00.012Z", dt.getValueAsString());
assertEquals("2010-01-01T09:00:00.120Z", dt.getValueAsString());
}
@Test
@ -599,6 +589,24 @@ public class BaseDateTimeDtDstu2Test {
assertThat(human, containsString("12"));
}
private void validateMillisPartial(String input, int expected) {
InstantDt dt = new InstantDt();
dt.setValueAsString(input);
Date date = dt.getValue();
assertEquals(expected, date.getTime() % 1000);
}
private void verifyFails(String input) {
try {
DateTimeDt dt = new DateTimeDt();
dt.setValueAsString(input);
fail();
} catch (ca.uhn.fhir.parser.DataFormatException e) {
assertThat(e.getMessage(), containsString("Invalid date/time format: \"" + input + "\""));
}
}
public static void afterClass() {
Locale.setDefault(ourDefaultLocale);
}
@ -611,8 +619,7 @@ public class BaseDateTimeDtDstu2Test {
@BeforeClass
public static void beforeClass() {
/*
* We cache the default locale, but temporarily set it to a random value during this test. This helps ensure
* that there are no language specific dependencies in the test.
* We cache the default locale, but temporarily set it to a random value during this test. This helps ensure that there are no language specific dependencies in the test.
*/
ourDefaultLocale = Locale.getDefault();

View File

@ -16,33 +16,72 @@ import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.rest.annotation.At;
import ca.uhn.fhir.rest.annotation.History;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.rest.annotation.Since;
import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.param.ParamPrefixEnum;
import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil;
/**
* Created by dsotnikov on 2/25/2014.
*/
public class HistoryTest {
public class HistoryDstu2Test {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx= FhirContext.forDstu1();
private static FhirContext ourCtx = FhirContext.forDstu2();
private static DateRangeParam ourLastAt;
private static InstantDt ourLastSince;
private static int ourPort;
private static Server ourServer;
@Before
public void before() {
ourLastAt = null;
ourLastSince = null;
}
@Test
public void testSince() throws Exception {
{
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/_history?_since=2005");
HttpResponse status = ourClient.execute(httpGet);
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals(null, ourLastAt);
assertEquals("2005", ourLastSince.getValueAsString());
}
}
@Test
public void testAt() throws Exception {
{
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/_history?_at=gt2001&_at=lt2005");
HttpResponse status = ourClient.execute(httpGet);
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals(ParamPrefixEnum.GREATERTHAN, ourLastAt.getLowerBound().getPrefix());
assertEquals("2001", ourLastAt.getLowerBound().getValueAsString());
assertEquals(ParamPrefixEnum.LESSTHAN, ourLastAt.getUpperBound().getPrefix());
assertEquals("2005", ourLastAt.getUpperBound().getValueAsString());
}
}
@Test
public void testInstanceHistory() throws Exception {
{
@ -53,10 +92,10 @@ public class HistoryTest {
assertEquals(200, status.getStatusLine().getStatusCode());
Bundle bundle = ourCtx.newXmlParser().parseBundle(responseContent);
assertEquals(2, bundle.getEntries().size());
assertEquals("http://localhost:" + ourPort +"/Patient/ih1/_history/1", bundle.getEntries().get(0).getLinkSelf().getValue());
assertEquals("http://localhost:" + ourPort +"/Patient/ih1/_history/2", bundle.getEntries().get(1).getLinkSelf().getValue());
Bundle bundle = ourCtx.newXmlParser().parseResource(Bundle.class, responseContent);
assertEquals(2, bundle.getEntry().size());
assertEquals("http://localhost:" + ourPort + "/Patient/ih1/_history/1", bundle.getEntry().get(0).getResource().getId().getValue());
assertEquals("http://localhost:" + ourPort + "/Patient/ih1/_history/2", bundle.getEntry().get(1).getResource().getId().getValue());
}
}
@ -71,10 +110,10 @@ public class HistoryTest {
assertEquals(200, status.getStatusLine().getStatusCode());
Bundle bundle = ourCtx.newXmlParser().parseBundle(responseContent);
assertEquals(2, bundle.getEntries().size());
assertEquals("http://localhost:" + ourPort +"/Patient/h1/_history/1", bundle.getEntries().get(0).getLinkSelf().getValue());
assertEquals("http://localhost:" + ourPort +"/Patient/h1/_history/2", bundle.getEntries().get(1).getLinkSelf().getValue());
Bundle bundle = ourCtx.newXmlParser().parseResource(Bundle.class, responseContent);
assertEquals(2, bundle.getEntry().size());
assertEquals("http://localhost:" + ourPort + "/Patient/h1/_history/1", bundle.getEntry().get(0).getResource().getId().getValue());
assertEquals("http://localhost:" + ourPort + "/Patient/h1/_history/2", bundle.getEntry().get(1).getResource().getId().getValue());
}
}
@ -89,10 +128,12 @@ public class HistoryTest {
assertEquals(200, status.getStatusLine().getStatusCode());
Bundle bundle = ourCtx.newXmlParser().parseBundle(responseContent);
assertEquals(2, bundle.getEntries().size());
assertEquals("http://localhost:" + ourPort +"/Patient/th1/_history/1", bundle.getEntries().get(0).getLinkSelf().getValue());
assertEquals("http://localhost:" + ourPort +"/Patient/th1/_history/2", bundle.getEntries().get(1).getLinkSelf().getValue());
assertNull(ourLastAt);
Bundle bundle = ourCtx.newXmlParser().parseResource(Bundle.class, responseContent);
assertEquals(2, bundle.getEntry().size());
assertEquals("http://localhost:" + ourPort + "/Patient/th1/_history/1", bundle.getEntry().get(0).getResource().getId().getValue());
assertEquals("http://localhost:" + ourPort + "/Patient/th1/_history/2", bundle.getEntry().get(1).getResource().getId().getValue());
}
}
@ -145,14 +186,13 @@ public class HistoryTest {
}
/**
* Created by dsotnikov on 2/25/2014.
*/
public static class DummyPlainProvider {
@History
public List<Patient> history(@Since InstantDt theSince) {
public List<Patient> history(@Since InstantDt theSince, @At DateRangeParam theAt) {
ourLastAt = theAt;
ourLastSince = theSince;
ArrayList<Patient> retVal = new ArrayList<Patient>();
Patient patient = new Patient();
@ -168,15 +208,12 @@ public class HistoryTest {
return retVal;
}
}
public static class DummyResourceProvider implements IResourceProvider {
@Override
public Class<? extends IResource> getResourceType() {
public Class<? extends Patient> getResourceType() {
return Patient.class;
}
@ -222,8 +259,6 @@ public class HistoryTest {
return retVal;
}
}
}

View File

@ -52,14 +52,14 @@ public class OperationDuplicateServerDstu2Test {
ourLog.info(response);
Conformance resp = ourCtx.newXmlParser().parseResource(Conformance.class, response);
assertEquals(1, resp.getRest().get(0).getOperation().size());
assertEquals("$myoperation", resp.getRest().get(0).getOperation().get(0).getName());
assertEquals("OperationDefinition/myoperation", resp.getRest().get(0).getOperation().get(0).getDefinition().getReference().getValue());
assertEquals(3, resp.getRest().get(0).getOperation().size());
assertEquals("myoperation", resp.getRest().get(0).getOperation().get(0).getName());
assertEquals("OperationDefinition/-s-myoperation", resp.getRest().get(0).getOperation().get(0).getDefinition().getReference().getValue());
}
// OperationDefinition
{
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/OperationDefinition/myoperation?_pretty=true");
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/OperationDefinition/-s-myoperation?_pretty=true");
HttpResponse status = ourClient.execute(httpGet);
assertEquals(200, status.getStatusLine().getStatusCode());
@ -69,10 +69,27 @@ public class OperationDuplicateServerDstu2Test {
OperationDefinition resp = ourCtx.newXmlParser().parseResource(OperationDefinition.class, response);
assertEquals(true, resp.getSystemElement().getValue().booleanValue());
assertEquals("$myoperation", resp.getCode());
assertEquals("myoperation", resp.getCode());
assertEquals(true, resp.getIdempotent().booleanValue());
assertEquals(2, resp.getType().size());
assertThat(Arrays.asList(resp.getType().get(0).getValue(), resp.getType().get(1).getValue()), containsInAnyOrder("Organization", "Patient"));
assertEquals(0, resp.getType().size());
assertEquals(1, resp.getParameter().size());
}
// OperationDefinition
{
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/OperationDefinition/Organization--myoperation?_pretty=true");
HttpResponse status = ourClient.execute(httpGet);
assertEquals(200, status.getStatusLine().getStatusCode());
String response = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info(response);
OperationDefinition resp = ourCtx.newXmlParser().parseResource(OperationDefinition.class, response);
assertEquals(false, resp.getSystemElement().getValue().booleanValue());
assertEquals("myoperation", resp.getCode());
assertEquals(true, resp.getIdempotent().booleanValue());
assertEquals(1, resp.getType().size());
assertEquals("Organization", resp.getType().get(0).getValue());
assertEquals(1, resp.getParameter().size());
}
}

View File

@ -104,7 +104,7 @@ public class OperationServerDstu2Test {
List<String> opNames = toOpNames(ops);
assertThat(opNames, containsInRelativeOrder("OP_TYPE"));
assertEquals("OperationDefinition/OP_TYPE", ops.get(opNames.indexOf("OP_TYPE")).getDefinition().getReference().getValue());
assertEquals("OperationDefinition/Patient--OP_TYPE", ops.get(opNames.indexOf("OP_TYPE")).getDefinition().getReference().getValue());
}
/**
@ -112,7 +112,7 @@ public class OperationServerDstu2Test {
*/
@Test
public void testOperationDefinition() {
OperationDefinition def = myFhirClient.read().resource(OperationDefinition.class).withId("OperationDefinition/OP_TYPE").execute();
OperationDefinition def = myFhirClient.read().resource(OperationDefinition.class).withId("OperationDefinition/Patient--OP_TYPE").execute();
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(def));

View File

@ -181,7 +181,7 @@ public class OperationServerWithSearchParamTypesDstu2Test {
/*
* Check the operation definitions themselves
*/
OperationDefinition andListDef = sc.readOperationDefinition(new IdDt("OperationDefinition/andlist"));
OperationDefinition andListDef = sc.readOperationDefinition(new IdDt("OperationDefinition/Patient--andlist"));
String def = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(andListDef);
ourLog.info(def);
//@formatter:off

View File

@ -171,7 +171,7 @@ public class ServerConformanceProviderDstu2Test {
assertEquals(1, conformance.getRest().get(0).getOperation().size());
assertEquals("everything", conformance.getRest().get(0).getOperation().get(0).getName());
assertEquals("OperationDefinition/Patient_i_everything", conformance.getRest().get(0).getOperation().get(0).getDefinition().getReference().getValue());
assertEquals("OperationDefinition/Patient-i-everything", conformance.getRest().get(0).getOperation().get(0).getDefinition().getReference().getValue());
}
@Test
@ -186,7 +186,7 @@ public class ServerConformanceProviderDstu2Test {
rs.init(createServletConfig());
OperationDefinition opDef = sc.readOperationDefinition(new IdDt("OperationDefinition/Patient_i_everything"));
OperationDefinition opDef = sc.readOperationDefinition(new IdDt("OperationDefinition/Patient-i-everything"));
validate(opDef);
assertEquals("everything", opDef.getCode());
@ -290,10 +290,10 @@ public class ServerConformanceProviderDstu2Test {
assertThat(operationNames, containsInAnyOrder("someOp", "validate", "someOp", "validate"));
List<String> operationIdParts = toOperationIdParts(conformance.getRest().get(0).getOperation());
assertThat(operationIdParts, containsInAnyOrder("Patient_i_someOp","Encounter_i_someOp","Patient_i_validate","Encounter_i_validate"));
assertThat(operationIdParts, containsInAnyOrder("Patient-i-someOp","Encounter-i-someOp","Patient-i-validate","Encounter-i-validate"));
{
OperationDefinition opDef = sc.readOperationDefinition(new IdDt("OperationDefinition/Patient_i_someOp"));
OperationDefinition opDef = sc.readOperationDefinition(new IdDt("OperationDefinition/Patient-i-someOp"));
validate(opDef);
Set<String> types = toStrings(opDef.getType());
@ -308,7 +308,7 @@ public class ServerConformanceProviderDstu2Test {
assertEquals("Patient", opDef.getParameter().get(1).getType());
}
{
OperationDefinition opDef = sc.readOperationDefinition(new IdDt("OperationDefinition/Encounter_i_someOp"));
OperationDefinition opDef = sc.readOperationDefinition(new IdDt("OperationDefinition/Encounter-i-someOp"));
validate(opDef);
Set<String> types = toStrings(opDef.getType());
@ -323,7 +323,7 @@ public class ServerConformanceProviderDstu2Test {
assertEquals("Encounter", opDef.getParameter().get(1).getType());
}
{
OperationDefinition opDef = sc.readOperationDefinition(new IdDt("OperationDefinition/Patient_i_validate"));
OperationDefinition opDef = sc.readOperationDefinition(new IdDt("OperationDefinition/Patient-i-validate"));
validate(opDef);
Set<String> types = toStrings(opDef.getType());
@ -374,9 +374,9 @@ public class ServerConformanceProviderDstu2Test {
rs.init(createServletConfig());
Conformance sconf = sc.getServerConformance(createHttpServletRequest());
assertEquals("OperationDefinition/_is_plain", sconf.getRest().get(0).getOperation().get(0).getDefinition().getReference().getValue());
assertEquals("OperationDefinition/-is-plain", sconf.getRest().get(0).getOperation().get(0).getDefinition().getReference().getValue());
OperationDefinition opDef = sc.readOperationDefinition(new IdDt("OperationDefinition/_is_plain"));
OperationDefinition opDef = sc.readOperationDefinition(new IdDt("OperationDefinition/-is-plain"));
validate(opDef);
assertEquals("plain", opDef.getCode());

View File

@ -41,10 +41,6 @@ import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.dstu3.exceptions.FHIRException;
import org.hl7.fhir.dstu3.model.Conformance;
import org.hl7.fhir.dstu3.model.DateTimeType;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.dstu3.model.OperationDefinition;
import org.hl7.fhir.dstu3.model.ResourceType;
import org.hl7.fhir.dstu3.model.Conformance.ConditionalDeleteStatus;
import org.hl7.fhir.dstu3.model.Conformance.ConformanceRestComponent;
import org.hl7.fhir.dstu3.model.Conformance.ConformanceRestResourceComponent;
@ -55,11 +51,15 @@ import org.hl7.fhir.dstu3.model.Conformance.RestfulConformanceMode;
import org.hl7.fhir.dstu3.model.Conformance.SystemRestfulInteraction;
import org.hl7.fhir.dstu3.model.Conformance.TypeRestfulInteraction;
import org.hl7.fhir.dstu3.model.Conformance.UnknownContentCode;
import org.hl7.fhir.dstu3.model.DateTimeType;
import org.hl7.fhir.dstu3.model.Enumerations.ConformanceResourceStatus;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.dstu3.model.OperationDefinition;
import org.hl7.fhir.dstu3.model.OperationDefinition.OperationDefinitionParameterComponent;
import org.hl7.fhir.dstu3.model.OperationDefinition.OperationKind;
import org.hl7.fhir.dstu3.model.OperationDefinition.OperationParameterUse;
import org.hl7.fhir.dstu3.model.Reference;
import org.hl7.fhir.dstu3.model.ResourceType;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.FhirVersionEnum;
@ -79,7 +79,6 @@ import ca.uhn.fhir.rest.method.OperationParameter;
import ca.uhn.fhir.rest.method.RestSearchParameterTypeEnum;
import ca.uhn.fhir.rest.method.SearchMethodBinding;
import ca.uhn.fhir.rest.method.SearchParameter;
import ca.uhn.fhir.rest.method.ValidateMethodBindingDstu3;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.IServerConformanceProvider;
import ca.uhn.fhir.rest.server.ResourceBinding;
@ -181,14 +180,14 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
retVal.append(theMethodBinding.getResourceName());
}
retVal.append('_');
retVal.append('-');
if (theMethodBinding.isCanOperateAtInstanceLevel()) {
retVal.append('i');
}
if (theMethodBinding.isCanOperateAtServerLevel()) {
retVal.append('s');
}
retVal.append('_');
retVal.append('-');
// Exclude the leading $
retVal.append(theMethodBinding.getName(), 1, theMethodBinding.getName().length());
@ -344,7 +343,7 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
String opName = myOperationBindingToName.get(methodBinding);
if (operationNames.add(opName)) {
ourLog.info("Found bound operation: {}", opName);
rest.addOperation().setName(opName).setDefinition(new Reference("OperationDefinition/" + opName));
rest.addOperation().setName(methodBinding.getName().substring(1)).setDefinition(new Reference("OperationDefinition/" + opName));
}
}
}

View File

@ -7,7 +7,11 @@ import static org.mockito.Mockito.when;
import java.io.InputStream;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.TimeZone;
import org.apache.commons.io.input.ReaderInputStream;
import org.apache.http.HttpResponse;
@ -21,6 +25,7 @@ import org.hl7.fhir.dstu3.model.Bundle;
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.dstu3.model.Extension;
import org.hl7.fhir.dstu3.model.Location;
import org.hl7.fhir.dstu3.model.StringType;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Test;
@ -31,16 +36,18 @@ import org.mockito.stubbing.Answer;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.annotation.Count;
import ca.uhn.fhir.rest.annotation.OptionalParam;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.annotation.Sort;
import ca.uhn.fhir.rest.api.SortOrderEnum;
import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.rest.client.api.IRestfulClient;
import ca.uhn.fhir.rest.param.DateParam;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.util.TestUtil;
import ca.uhn.fhir.util.UrlUtil;
public class SearchClientDstu3Test {
@ -54,7 +61,6 @@ public class SearchClientDstu3Test {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@Before
public void before() {
ourCtx = FhirContext.forDstu3();
@ -123,6 +129,45 @@ public class SearchClientDstu3Test {
assertEquals("http://localhost/fhir/Bundle?_sort=param1%2C-param2", ((HttpGet) capt.getAllValues().get(idx++)).getURI().toString());
}
@Test
public void testSearchWithPrimitiveTypes() throws Exception {
TimeZone tz = TimeZone.getDefault();
try {
TimeZone.setDefault(TimeZone.getTimeZone("America/Toronto"));
Date date = new Date(23898235986L);
Calendar cal = new GregorianCalendar();
cal.setTime(date);
;
final String response = createBundleWithSearchExtension();
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(ourHttpClient.execute(capt.capture())).thenReturn(ourHttpResponse);
when(ourHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(ourHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(ourHttpResponse.getEntity().getContent()).thenAnswer(new Answer<InputStream>() {
@Override
public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(response), Charset.forName("UTF-8"));
}
});
ILocationClient client = ourCtx.newRestfulClient(ILocationClient.class, "http://localhost/fhir");
int idx = 0;
client.search("STRING1", new StringType("STRING2"), date, cal);
assertEquals("http://localhost/fhir/Bundle?stringParam=STRING1&stringTypeParam=STRING2&dateParam=1970-10-04T10:23:55.986-04:00&calParam=1970-10-04T10:23:55.986-04:00",
UrlUtil.unescape(((HttpGet) capt.getAllValues().get(idx++)).getURI().toString()));
client.search(null, null, null, null);
assertEquals("http://localhost/fhir/Bundle",
UrlUtil.unescape(((HttpGet) capt.getAllValues().get(idx++)).getURI().toString()));
} finally {
TimeZone.setDefault(tz);
}
}
/**
* See #299
*/
@ -183,8 +228,6 @@ public class SearchClientDstu3Test {
return response;
}
public interface ILocationClient extends IRestfulClient {
@Search(queryName = "match")
public List<Location> getMatches(final @RequiredParam(name = Location.SP_NAME) StringParam name, final @Count Integer count);
@ -194,6 +237,11 @@ public class SearchClientDstu3Test {
@Search
public Bundle search(@Sort SortSpec theSort);
@Search
public Bundle search(@OptionalParam(name = "stringParam") String theString, @OptionalParam(name = "stringTypeParam") StringType theStringDt, @OptionalParam(name = "dateParam") Date theDate,
@OptionalParam(name = "calParam") Calendar theCal);
}
}

View File

@ -91,6 +91,8 @@ public class OperationServerDstu3Test {
myFhirClient.registerInterceptor(loggingInterceptor);
Conformance p = myFhirClient.fetchConformance().ofType(Conformance.class).prettyPrint().execute();
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(p));
List<ConformanceRestOperationComponent> ops = p.getRest().get(0).getOperation();
assertThat(ops.size(), greaterThan(1));
@ -107,7 +109,7 @@ public class OperationServerDstu3Test {
*/
@Test
public void testOperationDefinition() {
OperationDefinition def = myFhirClient.read().resource(OperationDefinition.class).withId("OperationDefinition/OP_TYPE").execute();
OperationDefinition def = myFhirClient.read().resource(OperationDefinition.class).withId("OperationDefinition/Patient--OP_TYPE").execute();
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(def));

View File

@ -192,7 +192,7 @@ public class OperationServerWithSearchParamTypesDstu3Test {
/*
* Check the operation definitions themselves
*/
OperationDefinition andListDef = sc.readOperationDefinition(new IdType("OperationDefinition/andlist"));
OperationDefinition andListDef = sc.readOperationDefinition(new IdType("OperationDefinition/Patient--andlist"));
String def = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(andListDef);
ourLog.info(def);
//@formatter:off
@ -208,7 +208,7 @@ public class OperationServerWithSearchParamTypesDstu3Test {
));
//@formatter:on
andListDef = sc.readOperationDefinition(new IdType("OperationDefinition/andlist-withnomax"));
andListDef = sc.readOperationDefinition(new IdType("OperationDefinition/Patient--andlist-withnomax"));
def = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(andListDef);
ourLog.info(def);
//@formatter:off
@ -224,7 +224,7 @@ public class OperationServerWithSearchParamTypesDstu3Test {
));
//@formatter:on
OperationDefinition orListDef = sc.readOperationDefinition(new IdType("OperationDefinition/orlist"));
OperationDefinition orListDef = sc.readOperationDefinition(new IdType("OperationDefinition/Patient--orlist"));
def = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(orListDef);
ourLog.info(def);
//@formatter:off
@ -240,7 +240,7 @@ public class OperationServerWithSearchParamTypesDstu3Test {
));
//@formatter:on
orListDef = sc.readOperationDefinition(new IdType("OperationDefinition/orlist-withnomax"));
orListDef = sc.readOperationDefinition(new IdType("OperationDefinition/Patient--orlist-withnomax"));
def = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(orListDef);
ourLog.info(def);
//@formatter:off

View File

@ -159,7 +159,7 @@ public class ServerConformanceProviderDstu3Test {
assertEquals(1, conformance.getRest().get(0).getOperation().size());
assertEquals("everything", conformance.getRest().get(0).getOperation().get(0).getName());
OperationDefinition opDef = sc.readOperationDefinition(new IdType("OperationDefinition/Patient_i_everything"));
OperationDefinition opDef = sc.readOperationDefinition(new IdType("OperationDefinition/Patient-i-everything"));
validate(opDef);
assertEquals("everything", opDef.getCode());
}
@ -176,7 +176,7 @@ public class ServerConformanceProviderDstu3Test {
rs.init(createServletConfig());
OperationDefinition opDef = sc.readOperationDefinition(new IdType("OperationDefinition/Patient_i_everything"));
OperationDefinition opDef = sc.readOperationDefinition(new IdType("OperationDefinition/Patient-i-everything"));
validate(opDef);
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(opDef);
@ -282,10 +282,10 @@ public class ServerConformanceProviderDstu3Test {
assertThat(operationNames, containsInAnyOrder("someOp", "validate", "someOp", "validate"));
List<String> operationIdParts = toOperationIdParts(conformance.getRest().get(0).getOperation());
assertThat(operationIdParts, containsInAnyOrder("Patient_i_someOp", "Encounter_i_someOp", "Patient_i_validate", "Encounter_i_validate"));
assertThat(operationIdParts, containsInAnyOrder("Patient-i-someOp", "Encounter-i-someOp", "Patient-i-validate", "Encounter-i-validate"));
{
OperationDefinition opDef = sc.readOperationDefinition(new IdType("OperationDefinition/Patient_i_someOp"));
OperationDefinition opDef = sc.readOperationDefinition(new IdType("OperationDefinition/Patient-i-someOp"));
validate(opDef);
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(opDef));
Set<String> types = toStrings(opDef.getType());
@ -300,7 +300,7 @@ public class ServerConformanceProviderDstu3Test {
assertEquals("Patient", opDef.getParameter().get(1).getType());
}
{
OperationDefinition opDef = sc.readOperationDefinition(new IdType("OperationDefinition/Encounter_i_someOp"));
OperationDefinition opDef = sc.readOperationDefinition(new IdType("OperationDefinition/Encounter-i-someOp"));
validate(opDef);
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(opDef));
Set<String> types = toStrings(opDef.getType());
@ -315,7 +315,7 @@ public class ServerConformanceProviderDstu3Test {
assertEquals("Encounter", opDef.getParameter().get(1).getType());
}
{
OperationDefinition opDef = sc.readOperationDefinition(new IdType("OperationDefinition/Patient_i_validate"));
OperationDefinition opDef = sc.readOperationDefinition(new IdType("OperationDefinition/Patient-i-validate"));
validate(opDef);
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(opDef));
Set<String> types = toStrings(opDef.getType());
@ -364,7 +364,7 @@ public class ServerConformanceProviderDstu3Test {
rs.init(createServletConfig());
OperationDefinition opDef = sc.readOperationDefinition(new IdType("OperationDefinition/_is_plain"));
OperationDefinition opDef = sc.readOperationDefinition(new IdType("OperationDefinition/-is-plain"));
validate(opDef);
assertEquals("plain", opDef.getCode());

View File

@ -344,6 +344,21 @@
<![CDATA[@OperationParam]]> annotation describes. Thanks to Michael Lawley
for reporting!
</action>
<action type="fix">
Server parameters annotated with
<![CDATA[<code>@Since</code>]]>
or
<![CDATA[<code>@CountS</code>]]>
which are of a FHIR type such as IntegerDt or DateTimeType will
now be set to null if the client's URL does not
contain this parameter. Previously they would be populated
with an empty instance of the FHIR type, which was inconsistent with
the way other server parameters worked.
</action>
<action type="add">
Server now supports the _at parameter (including multiple repetitions)
for history operation
</action>
</release>
<release version="1.5" date="2016-04-20">
<action type="fix" issue="339">

View File

@ -1379,8 +1379,22 @@
</li>
</ul>
<p>
The following snippet shows how to define a history method on a server:
The following snippet shows how to define a history method on a server. Note that
the following parameters are both optional, but may be useful in
implementing the history operation:
</p>
<u>
<li>
The <code>@Since</code> method argument implements the <code>_since</code>
parameter and should be of type <code>DateTimeDt</code> or <code>DateTimeType</code>
</li>
<li>
The <code>@At</code> method argument implements the <code>_at</code>
parameter and may be of type
<code>DateRangeParam</code>,
<code>DateTimeDt</code> or <code>DateTimeType</code>
</li>
</u>
<macro name="snippet">
<param name="id" value="history" />