diff --git a/examples/src/main/java/example/RestfulPatientResourceProviderMore.java b/examples/src/main/java/example/RestfulPatientResourceProviderMore.java index 21993109caa..5b777c80e73 100644 --- a/examples/src/main/java/example/RestfulPatientResourceProviderMore.java +++ b/examples/src/main/java/example/RestfulPatientResourceProviderMore.java @@ -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 getPatientHistory(@IdParam IdDt theId) { +public List getPatientHistory( + @IdParam IdDt theId, + @Since InstantDt theSince, + @At DateRangeParam theAt + ) { List retVal = new ArrayList(); Patient patient = new Patient(); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimePrimitiveDatatypeDefinition.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimePrimitiveDatatypeDefinition.java index 6130a759401..df5b6a15afe 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimePrimitiveDatatypeDefinition.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimePrimitiveDatatypeDefinition.java @@ -21,34 +21,66 @@ 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> implements IRuntimeDatatypeDefinition { + private Class myNativeType; private BaseRuntimeElementDefinition myProfileOf; private Class myProfileOfType; private boolean mySpecialization; public RuntimePrimitiveDatatypeDefinition(DatatypeDef theDef, Class> theImplementingClass, boolean theStandardType) { super(theDef.name(), theImplementingClass, theStandardType); - + String resourceName = theDef.name(); if (isBlank(resourceName)) { throw new ConfigurationException("Resource type @" + ResourceDef.class.getSimpleName() + " annotation contains no resource name: " + theImplementingClass.getCanonicalName()); } - + mySpecialization = theDef.isSpecialization(); myProfileOfType = theDef.profileOf(); if (myProfileOfType.equals(IBaseDatatype.class)) { myProfileOfType = null; } + + determineNativeType(theImplementingClass); + } + + private void determineNativeType(Class> 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 getProfileOf() { return myProfileOfType; } + @Override + public boolean isProfileOf(Class 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; @@ -69,7 +117,7 @@ public class RuntimePrimitiveDatatypeDefinition extends BaseRuntimeElementDefini @Override void sealAndInitialize(FhirContext theContext, Map, BaseRuntimeElementDefinition> theClassToElementDefinitions) { super.sealAndInitialize(theContext, theClassToElementDefinitions); - + if (myProfileOfType != null) { myProfileOf = theClassToElementDefinitions.get(myProfileOfType); if (myProfileOf == null) { @@ -85,17 +133,4 @@ public class RuntimePrimitiveDatatypeDefinition extends BaseRuntimeElementDefini } } - @Override - public boolean isProfileOf(Class theType) { - if (myProfileOfType != null) { - if (myProfileOfType.equals(theType)) { - return true; - } else if (myProfileOf instanceof IRuntimeDatatypeDefinition) { - return ((IRuntimeDatatypeDefinition) myProfileOf).isProfileOf(theType); - } - } - return false; - } - - } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/BaseDateTimeDt.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/BaseDateTimeDt.java index 97e5e23cd9b..1278f2a5e37 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/BaseDateTimeDt.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/BaseDateTimeDt.java @@ -105,10 +105,10 @@ public abstract class BaseDateTimeDt extends BasePrimitive { 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 { 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 */ @@ -198,7 +213,7 @@ public abstract class BaseDateTimeDt extends BasePrimitive { Validate.notNull(getValue(), getClass().getSimpleName() + " contains null value"); return DateUtils.isSameDay(new Date(), getValue()); } - + private void leftPadWithZeros(int theInteger, int theLength, StringBuilder theTarget) { String string = Integer.toString(theInteger); for (int i = string.length(); i < theLength; i++) { @@ -206,12 +221,13 @@ public abstract class BaseDateTimeDt extends BasePrimitive { } theTarget.append(string); } - + @Override protected Date parse(String theValue) throws DataFormatException { 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 { 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 { 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 { cal.set(Calendar.DATE, 1); } + if (fractionalSecondsSet == false) { + myFractionalSeconds = ""; + } + setPrecision(precision); return cal.getTime(); @@ -395,11 +417,11 @@ public abstract class BaseDateTimeDt extends BasePrimitive { 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 { } } + private void validateLengthIsAtLeast(String theValue, int theLength) { if (theValue.length() < theLength) { throwBadDateFormat(theValue); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/At.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/At.java new file mode 100644 index 00000000000..b6edc4a8c03 --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/At.java @@ -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. + *

+ * Parameters with this annotation should be of type {@link DateParam} or {@link DateRangeParam} + *

+ * @see History + */ +@Target(value=ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +public @interface At { + //nothing +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Since.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Since.java index b36ae3012dd..946e13e35d9 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Since.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Since.java @@ -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. + *

+ * Parameters with this annotation should be of type {@link DateParam} or {@link DateRangeParam} + *

* * @see History */ diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/AtParameter.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/AtParameter.java new file mode 100644 index 00000000000..14bc023553e --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/AtParameter.java @@ -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); + } + +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseBinder.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseBinder.java index 58d9e1d6d21..93ab879438c 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseBinder.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseBinder.java @@ -38,6 +38,7 @@ class BaseBinder { 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()); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseJavaPrimitiveBinder.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseJavaPrimitiveBinder.java new file mode 100644 index 00000000000..35fc2b1ecbf --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseJavaPrimitiveBinder.java @@ -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 BaseJavaPrimitiveBinderimplements IParamBinder { + + public BaseJavaPrimitiveBinder() { + super(); + } + + protected abstract String doEncode(T theString); + + protected abstract T doParse(String theString); + + @SuppressWarnings("unchecked") + @Override + public List> 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>) retValList; + } + + @Override + public T parse(String theName, List 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; + } + +} \ No newline at end of file diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/CalendarBinder.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/CalendarBinder.java new file mode 100644 index 00000000000..a8c8a804fc4 --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/CalendarBinder.java @@ -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 { + CalendarBinder() { + } + + @Override + protected String doEncode(Calendar theString) { + return new InstantDt(theString).getValueAsString(); + } + + @Override + protected Calendar doParse(String theString) { + return new InstantDt(theString).getValueAsCalendar(); + } + + +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/DateBinder.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/DateBinder.java new file mode 100644 index 00000000000..6fbe70982f1 --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/DateBinder.java @@ -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 { + DateBinder() { + } + + @Override + protected String doEncode(Date theString) { + return new InstantDt(theString).getValueAsString(); + } + + @Override + protected Date doParse(String theString) { + return new InstantDt(theString).getValue(); + } + + +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/FhirPrimitiveBinder.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/FhirPrimitiveBinder.java new file mode 100644 index 00000000000..c6cbb90d76f --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/FhirPrimitiveBinder.java @@ -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> { + + private Class> myType; + + FhirPrimitiveBinder(Class> 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; + } + + +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/IParamBinder.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/IParamBinder.java index e4033e71518..8f3488ebf29 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/IParamBinder.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/IParamBinder.java @@ -29,7 +29,7 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; interface IParamBinder { - List> encode(FhirContext theContext, Object theString) throws InternalErrorException; + List> encode(FhirContext theContext, T theString) throws InternalErrorException; T parse(String theName, List theList) throws InternalErrorException, InvalidRequestException; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/MethodUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/MethodUtil.java index ac78cc55be2..06b9fb781b1 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/MethodUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/MethodUtil.java @@ -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) { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationParameter.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationParameter.java index e8a84887692..fb6e844e6b7 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationParameter.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationParameter.java @@ -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()); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/QueryParameterAndBinder.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/QueryParameterAndBinder.java index 07889f8cb9b..bf15ddd4254 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/QueryParameterAndBinder.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/QueryParameterAndBinder.java @@ -37,7 +37,7 @@ final class QueryParameterAndBinder extends BaseBinder> im @SuppressWarnings("unchecked") @Override - public List> encode(FhirContext theContext, Object theString) throws InternalErrorException { + public List> encode(FhirContext theContext, IQueryParameterAnd theString) throws InternalErrorException { List> retVal = (List>) ((IQueryParameterAnd) theString).getValuesAsQueryTokens(); return retVal; } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/QueryParameterOrBinder.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/QueryParameterOrBinder.java index 960729b8120..683b377b482 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/QueryParameterOrBinder.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/QueryParameterOrBinder.java @@ -37,8 +37,8 @@ final class QueryParameterOrBinder extends BaseBinder> impl @SuppressWarnings("unchecked") @Override - public List> encode(FhirContext theContext, Object theString) throws InternalErrorException { - IQueryParameterOr retVal = ((IQueryParameterOr) theString); + public List> encode(FhirContext theContext, IQueryParameterOr theValue) throws InternalErrorException { + IQueryParameterOr retVal = (theValue); List retVal2 = Collections.singletonList((IQueryParameterOr)retVal); return (List>) retVal2; } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/QueryParameterTypeBinder.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/QueryParameterTypeBinder.java index 0292e511b0e..866a834bda6 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/QueryParameterTypeBinder.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/QueryParameterTypeBinder.java @@ -39,8 +39,8 @@ final class QueryParameterTypeBinder extends BaseBinder imp @SuppressWarnings("unchecked") @Override - public List> encode(FhirContext theContext, Object theString) throws InternalErrorException { - IQueryParameterType param = (IQueryParameterType) theString; + public List> encode(FhirContext theContext, IQueryParameterType theValue) throws InternalErrorException { + IQueryParameterType param = theValue; List retVal = Collections.singletonList(MethodUtil.singleton(param)); return (List>) retVal; } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SearchParameter.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SearchParameter.java index 6fd07a9b34e..74ef260db97 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SearchParameter.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SearchParameter.java @@ -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 { @@ -133,13 +135,13 @@ public class SearchParameter extends BaseQueryParameter { ourParamTypes.put(CompositeOrListParam.class, RestSearchParameterTypeEnum.COMPOSITE); ourParamTypes.put(CompositeAndListParam.class, RestSearchParameterTypeEnum.COMPOSITE); ourParamQualifiers.put(RestSearchParameterTypeEnum.COMPOSITE, CollectionUtil.newSet(Constants.PARAMQUALIFIER_MISSING, EMPTY_STRING)); - + ourParamTypes.put(HasParam.class, RestSearchParameterTypeEnum.HAS); ourParamTypes.put(HasOrListParam.class, RestSearchParameterTypeEnum.HAS); ourParamTypes.put(HasAndListParam.class, RestSearchParameterTypeEnum.HAS); } - - private List> myCompositeTypes; + + private List> myCompositeTypes = Collections.emptyList(); private List> myDeclaredTypes; private String myDescription; private String myName; @@ -167,7 +169,12 @@ public class SearchParameter extends BaseQueryParameter { public List encode(FhirContext theContext, Object theObject) throws InternalErrorException { ArrayList retVal = new ArrayList(); - List> val = myParamBinder.encode(theContext, theObject); + // TODO: declaring method should probably have a generic type.. + @SuppressWarnings("rawtypes") + IParamBinder paramBinder = myParamBinder; + + @SuppressWarnings("unchecked") + List> 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> theInnerCollectionType, Class> theOuterCollectionType) { + public void setType(FhirContext theContext, final Class type, Class> theInnerCollectionType, Class> theOuterCollectionType) { this.myType = type; if (IQueryParameterType.class.isAssignableFrom(type)) { myParamBinder = new QueryParameterTypeBinder((Class) 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>) type); + if (def.getNativeType() != null) { + if (def.getNativeType().equals(Date.class)) { + myParamBinder = new FhirPrimitiveBinder((Class>) type); + myParamType = RestSearchParameterTypeEnum.DATE; + } else if (def.getNativeType().equals(String.class)) { + myParamBinder = new FhirPrimitiveBinder((Class>) type); + myParamType = RestSearchParameterTypeEnum.STRING; + } + } } else { throw new ConfigurationException("Unsupported data type for parameter: " + type.getCanonicalName()); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SinceOrAtParameter.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SinceOrAtParameter.java new file mode 100644 index 00000000000..cca7639d3d1 --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SinceOrAtParameter.java @@ -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 getQualifierBlacklist() { + return null; + } + + @Override + public Set getQualifierWhitelist() { + return null; + } + +// @Override +// public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map> 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> theOuterCollectionType, Class> 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()); +// } +// } + +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SinceParameter.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SinceParameter.java index f9bbf432b27..fd540949917 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SinceParameter.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SinceParameter.java @@ -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> 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> theOuterCollectionType, Class> 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); } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/StringBinder.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/StringBinder.java index b3b960676ce..d6d9dde5afd 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/StringBinder.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/StringBinder.java @@ -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 { +final class StringBinder extends BaseJavaPrimitiveBinder { StringBinder() { } - @SuppressWarnings("unchecked") @Override - public List> encode(FhirContext theContext, Object theString) throws InternalErrorException { - String retVal = ((String) theString); - List retValList = Collections.singletonList(MethodUtil.singleton(new StringParam(retVal))); - return (List>) retValList; + protected String doEncode(String theString) { + return theString; } @Override - public String parse(String theName, List 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."); - } - - return theParams.get(0).get(0); + protected String doParse(String theString) { + return theString; } + } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/DateParam.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/DateParam.java index 9a6a254707d..fab42ef1793 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/DateParam.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/DateParam.java @@ -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; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ParameterUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ParameterUtil.java index 2136224d8ce..1b73f2f6965 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ParameterUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ParameterUtil.java @@ -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> BINDABLE_INTEGER_TYPES; - private static final Set> BINDABLE_TIME_TYPES; static { HashSet> intTypes = new HashSet>(); @@ -45,12 +39,6 @@ public class ParameterUtil { intTypes.add(Integer.class); BINDABLE_INTEGER_TYPES = Collections.unmodifiableSet(intTypes); - HashSet> timeTypes = new HashSet>(); - 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> getBindableInstantTypes() { - return BINDABLE_TIME_TYPES; - } - public static Set> 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) { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Constants.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Constants.java index 16eda352113..5483257cf84 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Constants.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Constants.java @@ -58,6 +58,8 @@ public class Constants { * "text/html" and "html" */ public static final Set 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 valToEncoding = new HashMap(); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ReflectionUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ReflectionUtil.java index 4694ef4f34f..f26898da8d0 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ReflectionUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ReflectionUtil.java @@ -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()); + } + } diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderTest.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderTest.java index 95fe581f049..a9e984cf88d 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderTest.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderTest.java @@ -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(" ")); - assertTrue(response.getEntity().toString().contains("\"$someCustomOperation")); + assertTrue(response.getEntity().toString().contains("\"someCustomOperation")); System.out.println(response.getEntity()); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java index fcd294ebfc3..ba0bb04c87d 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java @@ -474,19 +474,6 @@ public abstract class BaseHapiFhirDao implements IDao { } } - private List extractValues(String thePath, IBaseResource theResource) { - List values = new ArrayList(); - 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 tagIds, Class entityClass) { { CriteriaBuilder builder = myEntityManager.getCriteriaBuilder(); @@ -621,13 +608,13 @@ public abstract class BaseHapiFhirDao 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 implements IDao { theProvider.setContext(getContext()); theProvider.setEntityManager(myEntityManager); theProvider.setPlatformTransactionManager(myPlatformTransactionManager); - theProvider.setResourceHistoryTableDao(myResourceHistoryTableDao); theProvider.setSearchDao(mySearchDao); theProvider.setSearchResultDao(mySearchResultDao); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java index 783c9d76aa8..648f8285719 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java @@ -101,19 +101,22 @@ public abstract class BaseHapiFhirResourceDao extends B @Autowired protected PlatformTransactionManager myPlatformTransactionManager; - private String myResourceName; + @Autowired + private IResourceHistoryTableDao myResourceHistoryTableDao; + @Autowired() + protected IResourceIndexedSearchParamUriDao myResourceIndexedSearchParamUriDao; + private String myResourceName; private Class myResourceType; @Autowired(required = false) protected IFulltextSearchSvc mySearchDao; @Autowired() protected ISearchResultDao mySearchResultDao; - @Autowired() - protected IResourceIndexedSearchParamUriDao myResourceIndexedSearchParamUriDao; + + private String mySecondaryPrimaryKeyParamName; + @Autowired() protected IHapiTerminologySvc myTerminologySvc; - - private String mySecondaryPrimaryKeyParamName; @Override public void addTag(IIdType theId, TagTypeEnum theTagType, String theScheme, String theTerm, String theLabel) { @@ -149,11 +152,6 @@ public abstract class BaseHapiFhirResourceDao 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 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 extends B protected abstract IBaseOperationOutcome createOperationOutcome(String theSeverity, String theMessage, String theCode); - @Override - public DaoMethodOutcome delete(IIdType theId, RequestDetails theRequestDetails) { - List deleteConflicts = new ArrayList(); - 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 deleteConflicts, RequestDetails theRequestDetails) { if (theId == null || !theId.hasIdPart()) { @@ -225,20 +215,16 @@ public abstract class BaseHapiFhirResourceDao extends B } @Override - public DaoMethodOutcome deleteByUrl(String theUrl, RequestDetails theRequestDetails) { - StopWatch w = new StopWatch(); + public DaoMethodOutcome delete(IIdType theId, RequestDetails theRequestDetails) { List deleteConflicts = new ArrayList(); + StopWatch w = new StopWatch(); - List 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 extends B return retVal; } + @Override + public DaoMethodOutcome deleteByUrl(String theUrl, RequestDetails theRequestDetails) { + StopWatch w = new StopWatch(); + List deleteConflicts = new ArrayList(); + + List 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 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 extends B return outcome; } + private void doMetaAdd(MT theMetaAdd, BaseHasResource entity) { + List tags = toTagList(theMetaAdd); + + //@formatter:off + for (TagDefinition nextDef : tags) { + + boolean hasTag = false; + for (BaseTag next : new ArrayList(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 void doMetaDelete(MT theMetaDel, BaseHasResource entity) { + List tags = toTagList(theMetaDel); + + //@formatter:off + for (TagDefinition nextDef : tags) { + for (BaseTag next : new ArrayList(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 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); @@ -399,87 +455,13 @@ public abstract class BaseHapiFhirResourceDao 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 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 void doMetaAdd(MT theMetaAdd, BaseHasResource entity) { - List tags = toTagList(theMetaAdd); - - //@formatter:off - for (TagDefinition nextDef : tags) { - - boolean hasTag = false; - for (BaseTag next : new ArrayList(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 extends B // }; // } + @Override + public 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 metaDeleteOperation(IIdType theResourceId, MT theMetaDel, RequestDetails theRequestDetails) { // Notify interceptors @@ -579,12 +591,12 @@ public abstract class BaseHapiFhirResourceDao extends B doMetaDelete(theMetaDel, entity); } else { doMetaDelete(theMetaDel, latestVersion); - + // Also update history entry ResourceHistoryTable history = myResourceHistoryTableDao.findForIdAndVersion(entity.getId(), entity.getVersion()); doMetaDelete(theMetaDel, history); } - + myEntityManager.flush(); ourLog.info("Processed metaDeleteOperation on {} in {}ms", new Object[] { theResourceId.getValue(), w.getMillisAndRestart() }); @@ -594,68 +606,6 @@ public abstract class BaseHapiFhirResourceDao extends B return retVal; } - private void doMetaDelete(MT theMetaDel, BaseHasResource entity) { - List tags = toTagList(theMetaDel); - - //@formatter:off - for (TagDefinition nextDef : tags) { - for (BaseTag next : new ArrayList(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 metaGetOperation(Class 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 q = myEntityManager.createQuery(sql, TagDefinition.class); - q.setParameter("res_type", myResourceName); - List tagDefinitions = q.getResultList(); - - MT retVal = toMetaDt(theType, tagDefinitions); - - return retVal; - } - - protected MT toMetaDt(Class theType, Collection 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 metaGetOperation(Class theType, IIdType theId, RequestDetails theRequestDetails) { // Notify interceptors @@ -675,6 +625,22 @@ public abstract class BaseHapiFhirResourceDao extends B return retVal; } + @Override + public MT metaGetOperation(Class 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 q = myEntityManager.createQuery(sql, TagDefinition.class); + q.setParameter("res_type", myResourceName); + List tagDefinitions = q.getResultList(); + + MT retVal = toMetaDt(theType, tagDefinitions); + + return retVal; + } + @PostConstruct public void postConstruct() { RuntimeResourceDefinition def = getContext().getResourceDefinition(myResourceType); @@ -703,7 +669,7 @@ public abstract class BaseHapiFhirResourceDao extends B if (!getResourceName().equals(type)) { throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "incorrectResourceType", type, getResourceName())); } - + if (theResource.getIdElement().hasIdPart()) { if (!theResource.getIdElement().isIdPartValid()) { throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "failedToCreateWithInvalidId", theResource.getIdElement().getIdPart())); @@ -711,8 +677,7 @@ public abstract class BaseHapiFhirResourceDao 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(); @@ -771,7 +736,6 @@ public abstract class BaseHapiFhirResourceDao extends B return entity; } - @Override public BaseHasResource readEntity(IIdType theId, boolean theCheckForForcedId) { @@ -795,7 +759,8 @@ public abstract class BaseHapiFhirResourceDao extends B if (entity == null) { if (theId.hasVersionIdPart()) { - TypedQuery q = myEntityManager.createQuery("SELECT t from ResourceHistoryTable t WHERE t.myResourceId = :RID AND t.myResourceType = :RTYP AND t.myResourceVersion = :RVER", ResourceHistoryTable.class); + TypedQuery 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 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); } @@ -903,8 +869,9 @@ public abstract class BaseHapiFhirResourceDao extends B @Override public Set 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,21 +884,43 @@ public abstract class BaseHapiFhirResourceDao 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 toMetaDt(Class theType, Collection 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(); - + IIdType id = theEntity.getIdDt(); if (getContext().getVersion().getVersion().isRi()) { id = new IdType(id.getValue()); } - + outcome.setId(id); outcome.setResource(theResource); if (theResource != null) { @@ -973,18 +962,12 @@ public abstract class BaseHapiFhirResourceDao 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,16 +984,17 @@ public abstract class BaseHapiFhirResourceDao 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(); - + try { 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 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 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()) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirSystemDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirSystemDao.java index 6a451122eb1..a068a68b921 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirSystemDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirSystemDao.java @@ -219,13 +219,13 @@ public abstract class BaseHapiFhirSystemDao extends BaseHapiFhirDao 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! diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirSystemDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirSystemDao.java index fbe4fff3a3c..cbb4831e0c6 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirSystemDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirSystemDao.java @@ -47,7 +47,7 @@ public interface IFhirSystemDao extends IDao { Map getResourceCounts(); - IBundleProvider history(Date theDate, RequestDetails theRequestDetails); + IBundleProvider history(Date theDate, Date theUntil, RequestDetails theRequestDetails); /** * Marks all indexes as needing fresh indexing diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceHistoryTableDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceHistoryTableDao.java index 990ab9af93c..c895c06458f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceHistoryTableDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceHistoryTableDao.java @@ -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= :cutoff ORDER BY t.myUpdated DESC") - List 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 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 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 findForAllResourceTypes( - Pageable thePageable); - - @Query("SELECT t FROM ResourceHistoryTable t WHERE t.myResourceId = :id ORDER BY t.myUpdated DESC") - List findForResourceInstance( - @Param("id") Long theId, - Pageable thePageable); - - @Query("SELECT t FROM ResourceHistoryTable t WHERE t.myResourceType = :type ORDER BY t.myUpdated DESC") - List findForResourceType( - @Param("type") String theType, - Pageable thePageable); +// @Query("SELECT t FROM ResourceHistoryTable t WHERE t.myUpdated >= :cutoff ORDER BY t.myUpdated DESC") +// List 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 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 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 findForAllResourceTypes( +// Pageable thePageable); +// +// @Query("SELECT t FROM ResourceHistoryTable t WHERE t.myResourceId = :id ORDER BY t.myUpdated DESC") +// List findForResourceInstance( +// @Param("id") Long theId, +// Pageable thePageable); +// +// @Query("SELECT t FROM ResourceHistoryTable t WHERE t.myResourceType = :type ORDER BY t.myUpdated DESC") +// List 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); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/Search.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/Search.java index eea9b1c5be7..7c6ce267d29 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/Search.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/Search.java @@ -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; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProvider.java index bdaf0101308..d7f4edb58b8 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProvider.java @@ -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 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 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); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaSystemProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaSystemProvider.java index 007515a7591..510e3c6e5f8 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaSystemProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaSystemProvider.java @@ -51,7 +51,7 @@ public class BaseJpaSystemProvider 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); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProvider.java index b9cc362d927..fe05c48f5df 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProvider.java @@ -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,43 +73,52 @@ public final class PersistedJpaBundleProvider implements IBundleProvider { myDao = theDao; } - @Autowired - private IResourceTableDao myResourceTableDao; - protected List doHistoryInTransaction(int theFromIndex, int theToIndex) { - - Date cutoff = mySearchEntity.getLastUpdatedHigh(); - - Pageable pageable = toPage(theFromIndex, theToIndex); - List results; + + CriteriaBuilder cb = myEntityManager.getCriteriaBuilder(); + CriteriaQuery q = cb.createQuery(ResourceHistoryTable.class); + Root from = q.from(ResourceHistoryTable.class); + List predicates = new ArrayList(); - if (cutoff != null) { - if (mySearchEntity.getResourceType() == null) { - results = myResourceHistoryTableDao.findForAllResourceTypes(cutoff, pageable); - } else if (mySearchEntity.getResourceId() == null) { - results = myResourceHistoryTableDao.findForResourceType(mySearchEntity.getResourceType(), cutoff, pageable); - } else { - results = myResourceHistoryTableDao.findForResourceInstance(mySearchEntity.getResourceId(), cutoff, pageable); - } + if (mySearchEntity.getResourceType() == null) { + // All resource types + } else if (mySearchEntity.getResourceId() == null) { + predicates.add(cb.equal(from.get("myResourceType"), mySearchEntity.getResourceType())); } 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); - } + predicates.add(cb.equal(from.get("myResourceId"), mySearchEntity.getResourceId())); } + + 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 query = myEntityManager.createQuery(q); + + if (theToIndex - theFromIndex > 0) { + query.setFirstResult(theFromIndex); + query.setMaxResults(theToIndex - theFromIndex); + } + + results = query.getResultList(); ArrayList retVal = new ArrayList(); for (ResourceHistoryTable next : results) { BaseHasResource resource; resource = next; - + retVal.add(myDao.toResource(resource, true)); } - + return retVal; } @@ -118,7 +128,7 @@ public final class PersistedJpaBundleProvider implements IBundleProvider { if (page == null) { return Collections.emptyList(); } - + Page search = mySearchResultDao.findWithSearchUuid(mySearchEntity, page); List pidsSubList = new ArrayList(); @@ -148,7 +158,7 @@ public final class PersistedJpaBundleProvider implements IBundleProvider { public boolean ensureSearchEntityLoaded() { if (mySearchEntity == null) { ensureDependenciesInjected(); - + TransactionTemplate template = new TransactionTemplate(myPlatformTransactionManager); template.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRED); return template.execute(new TransactionCallback() { @@ -156,14 +166,14 @@ public final class PersistedJpaBundleProvider implements IBundleProvider { public Boolean doInTransaction(TransactionStatus theStatus) { try { mySearchEntity = mySearchDao.findByUuid(myUuid); - + if (mySearchEntity == null) { return false; } // Load the includes now so that they are available outside of this transaction mySearchEntity.getIncludes().size(); - + return true; } catch (NoResultException e) { return false; @@ -176,7 +186,7 @@ public final class PersistedJpaBundleProvider implements IBundleProvider { private void ensureDependenciesInjected() { if (myPlatformTransactionManager == null) { - myDao.injectDependenciesIntoBundleProvider(this); + myDao.injectDependenciesIntoBundleProvider(this); } } @@ -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; } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java index 479540678d6..c6ab1cbb57e 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java @@ -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 toUnqualifiedIdValues(IBundleProvider theFound) { + List retVal = new ArrayList(); + int size = theFound.size(); + ourLog.info("Found {} results", size); + List resources = theFound.getResources(0, size); + for (IBaseResource next : resources) { + retVal.add(next.getIdElement().toUnqualified().getValue()); + } + return retVal; + } + protected String[] toValues(IIdType... theValues) { ArrayList retVal = new ArrayList(); for (IIdType next : theValues) { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu1Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu1Test.java index f0f89293eed..24172c52d0f 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu1Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu1Test.java @@ -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 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()); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2SearchNoFtTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2SearchNoFtTest.java index d7bf3ddc1a9..9550e04736a 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2SearchNoFtTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2SearchNoFtTest.java @@ -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") diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2Test.java index 648c83281e4..6feb8bef951 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2Test.java @@ -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 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 patients = toList(myPatientDao.history(idv1.toVersionless(), null, mySrd)); + List 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 entries = history.getResources(0, 3); ourLog.info("" + ResourceMetadataKeyEnum.UPDATED.get((IResource) entries.get(0))); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2UpdateTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2UpdateTest.java index 3426322cbad..b0535478486 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2UpdateTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2UpdateTest.java @@ -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()); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirSystemDaoDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirSystemDaoDstu2Test.java index 47a1006c401..c0952b4aa4e 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirSystemDaoDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirSystemDaoDstu2Test.java @@ -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))); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3Test.java index 4b522fe4e4c..e657933d2c6 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3Test.java @@ -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 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 patients = toList(myPatientDao.history(idv1.toVersionless(), null, mySrd)); + List 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 preDates = Lists.newArrayList(); + List 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 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 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()); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3UpdateTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3UpdateTest.java index 99290abbc4d..a90312f0347 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3UpdateTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3UpdateTest.java @@ -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()); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java index ce0fd67957b..6e2a2243d85 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java @@ -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))); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/SystemProviderDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/SystemProviderDstu2Test.java index db9814cf222..4fd7a995c19 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/SystemProviderDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/SystemProviderDstu2Test.java @@ -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 diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3Test.java index 2395dcc9d39..9196d0aed02 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3Test.java @@ -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)); } /** diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/SystemProviderDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/SystemProviderDstu3Test.java index 6a98c8238d1..7dc0518d287 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/SystemProviderDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/SystemProviderDstu3Test.java @@ -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()); } diff --git a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/param/DateRangeParamTest.java b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/param/DateRangeParamTest.java index f23f82e842d..c465c84f54b 100644 --- a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/param/DateRangeParamTest.java +++ b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/param/DateRangeParamTest.java @@ -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()); @@ -55,7 +74,7 @@ public class DateRangeParamTest { assertEquals(QuantityCompararatorEnum.LESSTHAN_OR_EQUALS, param.getUpperBound().getComparator()); } - + @Test public void testAddAnd() { assertEquals(1, new DateAndListParam().addAnd(new DateOrListParam()).getValuesAsQueryTokens().size()); @@ -169,7 +188,6 @@ public class DateRangeParamTest { return new Date(ourFmt.parse(theString).getTime() - 1L); } - @AfterClass public static void afterClassClearContext() { TestUtil.clearAllStaticFieldsForUnitTest(); diff --git a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/PlainProviderTest.java b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/PlainProviderTest.java index c9ad25d3165..1fcd6d4dbb7 100644 --- a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/PlainProviderTest.java +++ b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/PlainProviderTest.java @@ -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 getIdToPatient() { - Map idToPatient = new HashMap(); - { - Patient patient = createPatient(); - idToPatient.put("1", patient); - } - { - Patient patient = new Patient(); - 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("00002"); - patient.getName().add(new HumanNameDt()); - 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()); - } + 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.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("00001"); + patient.addName(); + patient.getName().get(0).addFamily("Test"); + 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 getGlobalHistory(@Since InstantDt theSince, @Count IntegerDt theCount) { @@ -246,35 +233,54 @@ public class PlainProviderTest { } - private static Patient createPatient() { - Patient patient = new Patient(); - 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("00001"); - patient.addName(); - patient.getName().get(0).addFamily("Test"); - patient.getName().get(0).addGiven("PatientOne"); - patient.getGender().setText("M"); - return 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; - } + 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 getIdToPatient() { + Map idToPatient = new HashMap(); + { + Patient patient = createPatient(); + idToPatient.put("1", patient); + } + { + Patient patient = new Patient(); + 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("00002"); + patient.getName().add(new HumanNameDt()); + patient.getName().get(0).addFamily("Test"); + 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()); + } - @AfterClass - public static void afterClassClearContext() { - TestUtil.clearAllStaticFieldsForUnitTest(); } } diff --git a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/ServerConformanceProvider.java b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/ServerConformanceProvider.java index 5fdbd3f6aab..17d1e2f4ee9 100644 --- a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/ServerConformanceProvider.java +++ b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/ServerConformanceProvider.java @@ -163,14 +163,14 @@ public class ServerConformanceProvider implements IServerConformanceProvider history(@Since InstantDt theSince) { + public List history(@Since InstantDt theSince, @At DateRangeParam theAt) { + ourLastAt = theAt; + ourLastSince = theSince; + ArrayList retVal = new ArrayList(); - Patient patient = new Patient(); - patient.setId("Patient/h1/_history/1"); - patient.addName().addFamily("history"); - retVal.add(patient); + Patient patient = new Patient(); + patient.setId("Patient/h1/_history/1"); + patient.addName().addFamily("history"); + retVal.add(patient); - Patient patient2 = new Patient(); - patient2.setId("Patient/h1/_history/2"); - patient2.addName().addFamily("history"); - retVal.add(patient2); + Patient patient2 = new Patient(); + patient2.setId("Patient/h1/_history/2"); + patient2.addName().addFamily("history"); + retVal.add(patient2); return retVal; } - - - } public static class DummyResourceProvider implements IResourceProvider { @Override - public Class getResourceType() { + public Class getResourceType() { return Patient.class; } - + @History public List instanceHistory(@IdParam IdDt theId) { ArrayList retVal = new ArrayList(); - Patient patient = new Patient(); - patient.setId("Patient/ih1/_history/1"); - patient.addName().addFamily("history"); - retVal.add(patient); + Patient patient = new Patient(); + patient.setId("Patient/ih1/_history/1"); + patient.addName().addFamily("history"); + retVal.add(patient); - Patient patient2 = new Patient(); - patient2.setId("Patient/ih1/_history/2"); - patient2.addName().addFamily("history"); - retVal.add(patient2); + Patient patient2 = new Patient(); + patient2.setId("Patient/ih1/_history/2"); + patient2.addName().addFamily("history"); + retVal.add(patient2); return retVal; } @@ -201,20 +238,20 @@ public class HistoryTest { public List typeHistory() { ArrayList retVal = new ArrayList(); - Patient patient = new Patient(); - patient.setId("Patient/th1/_history/1"); - patient.addName().addFamily("history"); - retVal.add(patient); + Patient patient = new Patient(); + patient.setId("Patient/th1/_history/1"); + patient.addName().addFamily("history"); + retVal.add(patient); - Patient patient2 = new Patient(); - patient2.setId("Patient/th1/_history/2"); - patient2.addName().addFamily("history"); - retVal.add(patient2); + Patient patient2 = new Patient(); + patient2.setId("Patient/th1/_history/2"); + patient2.addName().addFamily("history"); + retVal.add(patient2); return retVal; } - - @Read(version=true) + + @Read(version = true) public Patient vread(@IdParam IdDt theId) { Patient retVal = new Patient(); retVal.addName().addFamily("vread"); @@ -222,8 +259,6 @@ public class HistoryTest { return retVal; } - } - } diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationDuplicateServerDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationDuplicateServerDstu2Test.java index f8709196ad3..c26b8599514 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationDuplicateServerDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationDuplicateServerDstu2Test.java @@ -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()); } } diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationServerDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationServerDstu2Test.java index 64d0e5c22f4..990c541d8bd 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationServerDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationServerDstu2Test.java @@ -104,7 +104,7 @@ public class OperationServerDstu2Test { List 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)); diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationServerWithSearchParamTypesDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationServerWithSearchParamTypesDstu2Test.java index 7f5abcb51c4..59cf71d379f 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationServerWithSearchParamTypesDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationServerWithSearchParamTypesDstu2Test.java @@ -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 diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerConformanceProviderDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerConformanceProviderDstu2Test.java index cc0f2ec2ccc..702af742bf0 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerConformanceProviderDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerConformanceProviderDstu2Test.java @@ -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 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 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 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 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()); diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerConformanceProvider.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerConformanceProvider.java index 341df4563d0..5520e7ce995 100644 --- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerConformanceProvider.java +++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerConformanceProvider.java @@ -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 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() { + @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 */ @@ -144,14 +189,14 @@ public class SearchClientDstu3Test { ILocationClient client = ourCtx.newRestfulClient(ILocationClient.class, "http://localhost:8081/hapi-fhir/fhir"); Bundle matches = client.getMatchesReturnBundle(new StringParam("smith"), 100); - + assertEquals(1, matches.getEntry().size()); BundleEntryComponent entry = matches.getEntry().get(0); - assertEquals("Sample Clinic", ((Location)entry.getResource()).getName()); + assertEquals("Sample Clinic", ((Location) entry.getResource()).getName()); List ext = entry.getSearch().getExtensionsByUrl("http://hl7.org/fhir/StructureDefinition/algorithmic-match"); assertEquals(1, ext.size()); - + HttpGet value = (HttpGet) capt.getValue(); assertEquals("http://localhost:8081/hapi-fhir/fhir/Location?_query=match&name=smith&_count=100", value.getURI().toString()); } @@ -183,17 +228,20 @@ public class SearchClientDstu3Test { return response; } - - public interface ILocationClient extends IRestfulClient { @Search(queryName = "match") public List getMatches(final @RequiredParam(name = Location.SP_NAME) StringParam name, final @Count Integer count); - @Search(queryName = "match", type=Location.class) + @Search(queryName = "match", type = Location.class) public Bundle getMatchesReturnBundle(final @RequiredParam(name = Location.SP_NAME) StringParam name, final @Count Integer count); - + @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); + } } diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/OperationServerDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/OperationServerDstu3Test.java index 242f078ce97..ba6319f4f7a 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/OperationServerDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/OperationServerDstu3Test.java @@ -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 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)); diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/OperationServerWithSearchParamTypesDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/OperationServerWithSearchParamTypesDstu3Test.java index 201d06e31a7..96583bf633b 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/OperationServerWithSearchParamTypesDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/OperationServerWithSearchParamTypesDstu3Test.java @@ -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 diff --git a/hapi-fhir-structures-dstu3/src/test/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerConformanceProviderDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerConformanceProviderDstu3Test.java index 0883ed6aff1..d1b00a2211c 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerConformanceProviderDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerConformanceProviderDstu3Test.java @@ -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 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 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 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 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()); diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 97e6d262627..26d87c5638a 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -344,6 +344,21 @@ annotation describes. Thanks to Michael Lawley for reporting! + + Server parameters annotated with + @Since]]> + or + @CountS]]> + 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. + + + Server now supports the _at parameter (including multiple repetitions) + for history operation + diff --git a/src/site/xdoc/doc_rest_operations.xml b/src/site/xdoc/doc_rest_operations.xml index 37f7e384f7a..57062b5e134 100644 --- a/src/site/xdoc/doc_rest_operations.xml +++ b/src/site/xdoc/doc_rest_operations.xml @@ -1379,8 +1379,22 @@

- 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:

+ +
  • + The @Since method argument implements the _since + parameter and should be of type DateTimeDt or DateTimeType +
  • +
  • + The @At method argument implements the _at + parameter and may be of type + DateRangeParam, + DateTimeDt or DateTimeType +
  • +