Improvements to client and SP indexing in JPA server
This commit is contained in:
parent
519590ee27
commit
23083a9283
|
@ -20,16 +20,23 @@ package ca.uhn.fhir.rest.gclient;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import java.util.Date;
|
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||||
|
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
public interface IHistoryTyped<T> extends IClientExecutable<IHistoryTyped<T>, T> {
|
public interface IHistoryTyped<T> extends IClientExecutable<IHistoryTyped<T>, T> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request that the server return only resource versions that were created at or after the given time (inclusive)
|
* Request that the server return only the history elements between the
|
||||||
|
* specific range
|
||||||
*/
|
*/
|
||||||
IHistoryTyped<T> since(Date theCutoff);
|
IHistoryTyped<T> at(DateRangeParam theDateRangeParam);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request that the server return only up to <code>theCount</code> number of resources
|
||||||
|
*/
|
||||||
|
IHistoryTyped<T> count(Integer theCount);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request that the server return only resource versions that were created at or after the given time (inclusive)
|
* Request that the server return only resource versions that were created at or after the given time (inclusive)
|
||||||
|
@ -41,9 +48,9 @@ public interface IHistoryTyped<T> extends IClientExecutable<IHistoryTyped<T>, T>
|
||||||
IHistoryTyped<T> since(IPrimitiveType<Date> theCutoff);
|
IHistoryTyped<T> since(IPrimitiveType<Date> theCutoff);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request that the server return only up to <code>theCount</code> number of resources
|
* Request that the server return only resource versions that were created at or after the given time (inclusive)
|
||||||
*/
|
*/
|
||||||
IHistoryTyped<T> count(Integer theCount);
|
IHistoryTyped<T> since(Date theCutoff);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,18 @@
|
||||||
package ca.uhn.fhir.rest.param;
|
package ca.uhn.fhir.rest.param;
|
||||||
|
|
||||||
import static ca.uhn.fhir.rest.param.ParamPrefixEnum.EQUAL;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import static ca.uhn.fhir.rest.param.ParamPrefixEnum.GREATERTHAN_OR_EQUALS;
|
import ca.uhn.fhir.model.api.IQueryParameterAnd;
|
||||||
import static ca.uhn.fhir.rest.param.ParamPrefixEnum.LESSTHAN_OR_EQUALS;
|
import ca.uhn.fhir.parser.DataFormatException;
|
||||||
|
import ca.uhn.fhir.rest.api.QualifiedParamList;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import static ca.uhn.fhir.rest.param.ParamPrefixEnum.*;
|
||||||
import static java.lang.String.format;
|
import static java.lang.String.format;
|
||||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
|
||||||
|
@ -25,15 +35,6 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
|
||||||
import ca.uhn.fhir.model.api.IQueryParameterAnd;
|
|
||||||
import ca.uhn.fhir.parser.DataFormatException;
|
|
||||||
import ca.uhn.fhir.rest.api.QualifiedParamList;
|
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
|
||||||
|
|
||||||
public class DateRangeParam implements IQueryParameterAnd<DateParam> {
|
public class DateRangeParam implements IQueryParameterAnd<DateParam> {
|
||||||
|
|
||||||
|
@ -53,12 +54,10 @@ public class DateRangeParam implements IQueryParameterAnd<DateParam> {
|
||||||
/**
|
/**
|
||||||
* Constructor which takes two Dates representing the lower and upper bounds of the range (inclusive on both ends)
|
* Constructor which takes two Dates representing the lower and upper bounds of the range (inclusive on both ends)
|
||||||
*
|
*
|
||||||
* @param theLowerBound
|
* @param theLowerBound A qualified date param representing the lower date bound (optionally may include time), e.g.
|
||||||
* A qualified date param representing the lower date bound (optionally may include time), e.g.
|
|
||||||
* "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or
|
* "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or
|
||||||
* theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
|
* theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
|
||||||
* @param theUpperBound
|
* @param theUpperBound A qualified date param representing the upper date bound (optionally may include time), e.g.
|
||||||
* A qualified date param representing the upper date bound (optionally may include time), e.g.
|
|
||||||
* "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or
|
* "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or
|
||||||
* theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
|
* theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
|
||||||
*/
|
*/
|
||||||
|
@ -107,12 +106,10 @@ public class DateRangeParam implements IQueryParameterAnd<DateParam> {
|
||||||
/**
|
/**
|
||||||
* Constructor which takes two Dates representing the lower and upper bounds of the range (inclusive on both ends)
|
* Constructor which takes two Dates representing the lower and upper bounds of the range (inclusive on both ends)
|
||||||
*
|
*
|
||||||
* @param theLowerBound
|
* @param theLowerBound A qualified date param representing the lower date bound (optionally may include time), e.g.
|
||||||
* A qualified date param representing the lower date bound (optionally may include time), e.g.
|
|
||||||
* "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or
|
* "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or
|
||||||
* theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
|
* theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
|
||||||
* @param theUpperBound
|
* @param theUpperBound A qualified date param representing the upper date bound (optionally may include time), e.g.
|
||||||
* A qualified date param representing the upper date bound (optionally may include time), e.g.
|
|
||||||
* "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or
|
* "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or
|
||||||
* theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
|
* theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
|
||||||
*/
|
*/
|
||||||
|
@ -124,12 +121,10 @@ public class DateRangeParam implements IQueryParameterAnd<DateParam> {
|
||||||
/**
|
/**
|
||||||
* Constructor which takes two Dates representing the lower and upper bounds of the range (inclusive on both ends)
|
* Constructor which takes two Dates representing the lower and upper bounds of the range (inclusive on both ends)
|
||||||
*
|
*
|
||||||
* @param theLowerBound
|
* @param theLowerBound A qualified date param representing the lower date bound (optionally may include time), e.g.
|
||||||
* A qualified date param representing the lower date bound (optionally may include time), e.g.
|
|
||||||
* "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or
|
* "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or
|
||||||
* theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
|
* theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
|
||||||
* @param theUpperBound
|
* @param theUpperBound A qualified date param representing the upper date bound (optionally may include time), e.g.
|
||||||
* A qualified date param representing the upper date bound (optionally may include time), e.g.
|
|
||||||
* "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or
|
* "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or
|
||||||
* theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
|
* theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
|
||||||
*/
|
*/
|
||||||
|
@ -141,12 +136,10 @@ public class DateRangeParam implements IQueryParameterAnd<DateParam> {
|
||||||
/**
|
/**
|
||||||
* Constructor which takes two strings representing the lower and upper bounds of the range (inclusive on both ends)
|
* Constructor which takes two strings representing the lower and upper bounds of the range (inclusive on both ends)
|
||||||
*
|
*
|
||||||
* @param theLowerBound
|
* @param theLowerBound An unqualified date param representing the lower date bound (optionally may include time), e.g.
|
||||||
* An unqualified date param representing the lower date bound (optionally may include time), e.g.
|
|
||||||
* "2011-02-22" or "2011-02-22T13:12:00Z". Either theLowerBound or theUpperBound may both be populated, or
|
* "2011-02-22" or "2011-02-22T13:12:00Z". Either theLowerBound or theUpperBound may both be populated, or
|
||||||
* one may be null, but it is not valid for both to be null.
|
* one may be null, but it is not valid for both to be null.
|
||||||
* @param theUpperBound
|
* @param theUpperBound An unqualified date param representing the upper date bound (optionally may include time), e.g.
|
||||||
* An unqualified date param representing the upper date bound (optionally may include time), e.g.
|
|
||||||
* "2011-02-22" or "2011-02-22T13:12:00Z". Either theLowerBound or theUpperBound may both be populated, or
|
* "2011-02-22" or "2011-02-22T13:12:00Z". Either theLowerBound or theUpperBound may both be populated, or
|
||||||
* one may be null, but it is not valid for both to be null.
|
* one may be null, but it is not valid for both to be null.
|
||||||
*/
|
*/
|
||||||
|
@ -193,10 +186,28 @@ public class DateRangeParam implements IQueryParameterAnd<DateParam> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj == this) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!(obj instanceof DateRangeParam)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
DateRangeParam other = (DateRangeParam) obj;
|
||||||
|
return Objects.equals(myLowerBound, other.myLowerBound) &&
|
||||||
|
Objects.equals(myUpperBound, other.myUpperBound);
|
||||||
|
}
|
||||||
|
|
||||||
public DateParam getLowerBound() {
|
public DateParam getLowerBound() {
|
||||||
return myLowerBound;
|
return myLowerBound;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DateRangeParam setLowerBound(DateParam theLowerBound) {
|
||||||
|
validateAndSet(theLowerBound, myUpperBound);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public Date getLowerBoundAsInstant() {
|
public Date getLowerBoundAsInstant() {
|
||||||
if (myLowerBound == null) {
|
if (myLowerBound == null) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -226,6 +237,11 @@ public class DateRangeParam implements IQueryParameterAnd<DateParam> {
|
||||||
return myUpperBound;
|
return myUpperBound;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DateRangeParam setUpperBound(DateParam theUpperBound) {
|
||||||
|
validateAndSet(myLowerBound, theUpperBound);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public Date getUpperBoundAsInstant() {
|
public Date getUpperBoundAsInstant() {
|
||||||
if (myUpperBound == null) {
|
if (myUpperBound == null) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -273,24 +289,35 @@ public class DateRangeParam implements IQueryParameterAnd<DateParam> {
|
||||||
return bound != null && !bound.isEmpty();
|
return bound != null && !bound.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(myLowerBound, myUpperBound);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
return (getLowerBoundAsInstant() == null) && (getUpperBoundAsInstant() == null);
|
return (getLowerBoundAsInstant() == null) && (getUpperBoundAsInstant() == null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DateRangeParam setLowerBound(DateParam theLowerBound) {
|
/**
|
||||||
validateAndSet(theLowerBound, myUpperBound);
|
* Sets the lower bound using a string that is compliant with
|
||||||
|
* FHIR dateTime format (ISO-8601).
|
||||||
|
* <p>
|
||||||
|
* This lower bound is assumed to have a <code>ge</code>
|
||||||
|
* (greater than or equals) modifier.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public DateRangeParam setLowerBound(String theLowerBound) {
|
||||||
|
setLowerBound(new DateParam(GREATERTHAN_OR_EQUALS, theLowerBound));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the range from a pair of dates, inclusive on both ends
|
* Sets the range from a pair of dates, inclusive on both ends
|
||||||
*
|
*
|
||||||
* @param theLowerBound
|
* @param theLowerBound A qualified date param representing the lower date bound (optionally may include time), e.g.
|
||||||
* A qualified date param representing the lower date bound (optionally may include time), e.g.
|
|
||||||
* "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or
|
* "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or
|
||||||
* theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
|
* theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
|
||||||
* @param theUpperBound
|
* @param theUpperBound A qualified date param representing the upper date bound (optionally may include time), e.g.
|
||||||
* A qualified date param representing the upper date bound (optionally may include time), e.g.
|
|
||||||
* "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or
|
* "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or
|
||||||
* theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
|
* theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
|
||||||
*/
|
*/
|
||||||
|
@ -305,12 +332,10 @@ public class DateRangeParam implements IQueryParameterAnd<DateParam> {
|
||||||
/**
|
/**
|
||||||
* Sets the range from a pair of dates, inclusive on both ends
|
* Sets the range from a pair of dates, inclusive on both ends
|
||||||
*
|
*
|
||||||
* @param theLowerBound
|
* @param theLowerBound A qualified date param representing the lower date bound (optionally may include time), e.g.
|
||||||
* A qualified date param representing the lower date bound (optionally may include time), e.g.
|
|
||||||
* "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or
|
* "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or
|
||||||
* theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
|
* theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
|
||||||
* @param theUpperBound
|
* @param theUpperBound A qualified date param representing the upper date bound (optionally may include time), e.g.
|
||||||
* A qualified date param representing the upper date bound (optionally may include time), e.g.
|
|
||||||
* "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or
|
* "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or
|
||||||
* theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
|
* theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
|
||||||
*/
|
*/
|
||||||
|
@ -323,12 +348,10 @@ public class DateRangeParam implements IQueryParameterAnd<DateParam> {
|
||||||
* theLowerBound is after theUpperBound, thie method will automatically reverse
|
* theLowerBound is after theUpperBound, thie method will automatically reverse
|
||||||
* the order of the arguments in order to create an inclusive range.
|
* the order of the arguments in order to create an inclusive range.
|
||||||
*
|
*
|
||||||
* @param theLowerBound
|
* @param theLowerBound A qualified date param representing the lower date bound (optionally may include time), e.g.
|
||||||
* A qualified date param representing the lower date bound (optionally may include time), e.g.
|
|
||||||
* "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or
|
* "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or
|
||||||
* theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
|
* theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
|
||||||
* @param theUpperBound
|
* @param theUpperBound A qualified date param representing the upper date bound (optionally may include time), e.g.
|
||||||
* A qualified date param representing the upper date bound (optionally may include time), e.g.
|
|
||||||
* "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or
|
* "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or
|
||||||
* theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
|
* theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
|
||||||
*/
|
*/
|
||||||
|
@ -350,12 +373,10 @@ public class DateRangeParam implements IQueryParameterAnd<DateParam> {
|
||||||
/**
|
/**
|
||||||
* Sets the range from a pair of dates, inclusive on both ends
|
* Sets the range from a pair of dates, inclusive on both ends
|
||||||
*
|
*
|
||||||
* @param theLowerBound
|
* @param theLowerBound A qualified date param representing the lower date bound (optionally may include time), e.g.
|
||||||
* A qualified date param representing the lower date bound (optionally may include time), e.g.
|
|
||||||
* "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or
|
* "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or
|
||||||
* theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
|
* theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
|
||||||
* @param theUpperBound
|
* @param theUpperBound A qualified date param representing the upper date bound (optionally may include time), e.g.
|
||||||
* A qualified date param representing the upper date bound (optionally may include time), e.g.
|
|
||||||
* "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or
|
* "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or
|
||||||
* theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
|
* theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
|
||||||
*/
|
*/
|
||||||
|
@ -373,8 +394,16 @@ public class DateRangeParam implements IQueryParameterAnd<DateParam> {
|
||||||
validateAndSet(lowerBound, upperBound);
|
validateAndSet(lowerBound, upperBound);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DateRangeParam setUpperBound(DateParam theUpperBound) {
|
/**
|
||||||
validateAndSet(myLowerBound, theUpperBound);
|
* Sets the upper bound using a string that is compliant with
|
||||||
|
* FHIR dateTime format (ISO-8601).
|
||||||
|
* <p>
|
||||||
|
* This upper bound is assumed to have a <code>le</code>
|
||||||
|
* (less than or equals) modifier.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public DateRangeParam setUpperBound(String theUpperBound) {
|
||||||
|
setUpperBound(new DateParam(LESSTHAN_OR_EQUALS, theUpperBound));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -413,24 +442,6 @@ public class DateRangeParam implements IQueryParameterAnd<DateParam> {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj) {
|
|
||||||
if (obj == this) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (!(obj instanceof DateRangeParam)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
DateRangeParam other = (DateRangeParam) obj;
|
|
||||||
return Objects.equals(myLowerBound, other.myLowerBound) &&
|
|
||||||
Objects.equals(myUpperBound, other.myUpperBound);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hash(myLowerBound, myUpperBound);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder b = new StringBuilder();
|
StringBuilder b = new StringBuilder();
|
||||||
|
|
|
@ -86,7 +86,7 @@ public class ReflectionUtil {
|
||||||
public static Class<?> getGenericCollectionTypeOfMethodParameter(Method theMethod, int theParamIndex) {
|
public static Class<?> getGenericCollectionTypeOfMethodParameter(Method theMethod, int theParamIndex) {
|
||||||
Class<?> type;
|
Class<?> type;
|
||||||
Type genericParameterType = theMethod.getGenericParameterTypes()[theParamIndex];
|
Type genericParameterType = theMethod.getGenericParameterTypes()[theParamIndex];
|
||||||
if (Class.class.equals(genericParameterType)) {
|
if (Class.class.equals(genericParameterType) || Class.class.equals(genericParameterType.getClass())) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
ParameterizedType collectionType = (ParameterizedType) genericParameterType;
|
ParameterizedType collectionType = (ParameterizedType) genericParameterType;
|
||||||
|
|
|
@ -57,6 +57,7 @@ ca.uhn.fhir.validation.ValidationResult.noIssuesDetected=No issues detected duri
|
||||||
|
|
||||||
# JPA Messages
|
# JPA Messages
|
||||||
|
|
||||||
|
|
||||||
ca.uhn.fhir.jpa.config.HapiFhirHibernateJpaDialect.resourceVersionConstraintFailure=The operation has failed with a version constraint failure. This generally means that two clients/threads were trying to update the same resource at the same time, and this request was chosen as the failing request.
|
ca.uhn.fhir.jpa.config.HapiFhirHibernateJpaDialect.resourceVersionConstraintFailure=The operation has failed with a version constraint failure. This generally means that two clients/threads were trying to update the same resource at the same time, and this request was chosen as the failing request.
|
||||||
ca.uhn.fhir.jpa.config.HapiFhirHibernateJpaDialect.resourceIndexedCompositeStringUniqueConstraintFailure=The operation has failed with a unique index constraint failure. This probably means that the operation was trying to create/update a resource that would have resulted in a duplicate value for a unique index.
|
ca.uhn.fhir.jpa.config.HapiFhirHibernateJpaDialect.resourceIndexedCompositeStringUniqueConstraintFailure=The operation has failed with a unique index constraint failure. This probably means that the operation was trying to create/update a resource that would have resulted in a duplicate value for a unique index.
|
||||||
|
|
||||||
|
@ -90,9 +91,13 @@ ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.successfulUpdate=Successfully update
|
||||||
ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.successfulDeletes=Successfully deleted {0} resource(s) in {1}ms
|
ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.successfulDeletes=Successfully deleted {0} resource(s) in {1}ms
|
||||||
ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.invalidSearchParameter=Unknown search parameter "{0}". Value search parameters for this search are: {1}
|
ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.invalidSearchParameter=Unknown search parameter "{0}". Value search parameters for this search are: {1}
|
||||||
|
|
||||||
|
ca.uhn.fhir.jpa.dao.BaseSearchParamExtractor.failedToExtractPaths=Failed to extract values from resource using FHIRPath "{0}": {1}
|
||||||
|
|
||||||
ca.uhn.fhir.jpa.dao.SearchBuilder.invalidQuantityPrefix=Unable to handle quantity prefix "{0}" for value: {1}
|
ca.uhn.fhir.jpa.dao.SearchBuilder.invalidQuantityPrefix=Unable to handle quantity prefix "{0}" for value: {1}
|
||||||
ca.uhn.fhir.jpa.dao.SearchBuilder.invalidNumberPrefix=Unable to handle number prefix "{0}" for value: {1}
|
ca.uhn.fhir.jpa.dao.SearchBuilder.invalidNumberPrefix=Unable to handle number prefix "{0}" for value: {1}
|
||||||
|
|
||||||
|
ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoSearchParameterR4.invalidSearchParamExpression=The expression "{0}" can not be evaluated and may be invalid: {1}
|
||||||
|
|
||||||
ca.uhn.fhir.jpa.provider.BaseJpaProvider.cantCombintAtAndSince=Unable to combine _at and _since parameters for history operation
|
ca.uhn.fhir.jpa.provider.BaseJpaProvider.cantCombintAtAndSince=Unable to combine _at and _since parameters for history operation
|
||||||
|
|
||||||
ca.uhn.fhir.jpa.term.BaseHapiTerminologySvcImpl.cannotCreateDuplicateConceptMapUrl=Can not create multiple ConceptMap resources with ConceptMap.url "{0}", already have one with resource ID: {1}
|
ca.uhn.fhir.jpa.term.BaseHapiTerminologySvcImpl.cannotCreateDuplicateConceptMapUrl=Can not create multiple ConceptMap resources with ConceptMap.url "{0}", already have one with resource ID: {1}
|
||||||
|
|
|
@ -743,6 +743,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
private Class<? extends IBaseBundle> myReturnType;
|
private Class<? extends IBaseBundle> myReturnType;
|
||||||
private IPrimitiveType mySince;
|
private IPrimitiveType mySince;
|
||||||
private Class<? extends IBaseResource> myType;
|
private Class<? extends IBaseResource> myType;
|
||||||
|
private DateRangeParam myAt;
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
@Override
|
||||||
|
@ -752,6 +753,12 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IHistoryTyped at(DateRangeParam theDateRangeParam) {
|
||||||
|
myAt = theDateRangeParam;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IHistoryTyped count(Integer theCount) {
|
public IHistoryTyped count(Integer theCount) {
|
||||||
myCount = theCount;
|
myCount = theCount;
|
||||||
|
@ -774,7 +781,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
id = null;
|
id = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpGetClientInvocation invocation = HistoryMethodBinding.createHistoryInvocation(myContext, resourceName, id, mySince, myCount);
|
HttpGetClientInvocation invocation = HistoryMethodBinding.createHistoryInvocation(myContext, resourceName, id, mySince, myCount, myAt);
|
||||||
|
|
||||||
IClientResponseHandler handler;
|
IClientResponseHandler handler;
|
||||||
handler = new ResourceResponseHandler(myReturnType, getPreferResponseTypes(myType));
|
handler = new ResourceResponseHandler(myReturnType, getPreferResponseTypes(myType));
|
||||||
|
|
|
@ -26,6 +26,8 @@ import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.rest.param.DateParam;
|
||||||
|
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||||
import org.hl7.fhir.instance.model.api.*;
|
import org.hl7.fhir.instance.model.api.*;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
@ -96,7 +98,7 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
|
||||||
}
|
}
|
||||||
|
|
||||||
String historyId = id != null ? id.getIdPart() : null;
|
String historyId = id != null ? id.getIdPart() : null;
|
||||||
HttpGetClientInvocation retVal = createHistoryInvocation(getContext(), resourceName, historyId, null, null);
|
HttpGetClientInvocation retVal = createHistoryInvocation(getContext(), resourceName, historyId, null, null, null);
|
||||||
|
|
||||||
if (theArgs != null) {
|
if (theArgs != null) {
|
||||||
for (int idx = 0; idx < theArgs.length; idx++) {
|
for (int idx = 0; idx < theArgs.length; idx++) {
|
||||||
|
@ -108,7 +110,7 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HttpGetClientInvocation createHistoryInvocation(FhirContext theContext, String theResourceName, String theId, IPrimitiveType<Date> theSince, Integer theLimit) {
|
public static HttpGetClientInvocation createHistoryInvocation(FhirContext theContext, String theResourceName, String theId, IPrimitiveType<Date> theSince, Integer theLimit, DateRangeParam theAt) {
|
||||||
StringBuilder b = new StringBuilder();
|
StringBuilder b = new StringBuilder();
|
||||||
if (theResourceName != null) {
|
if (theResourceName != null) {
|
||||||
b.append(theResourceName);
|
b.append(theResourceName);
|
||||||
|
@ -129,8 +131,18 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
|
||||||
}
|
}
|
||||||
if (theLimit != null) {
|
if (theLimit != null) {
|
||||||
b.append(haveParam ? '&' : '?');
|
b.append(haveParam ? '&' : '?');
|
||||||
|
haveParam = true;
|
||||||
b.append(Constants.PARAM_COUNT).append('=').append(theLimit);
|
b.append(Constants.PARAM_COUNT).append('=').append(theLimit);
|
||||||
}
|
}
|
||||||
|
if (theAt != null) {
|
||||||
|
for (DateParam next : theAt.getValuesAsQueryTokens()) {
|
||||||
|
b.append(haveParam ? '&' : '?');
|
||||||
|
haveParam = true;
|
||||||
|
b.append(Constants.PARAM_AT);
|
||||||
|
b.append("=");
|
||||||
|
b.append(next.getValueAsQueryToken(theContext));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
HttpGetClientInvocation retVal = new HttpGetClientInvocation(theContext, b.toString());
|
HttpGetClientInvocation retVal = new HttpGetClientInvocation(theContext, b.toString());
|
||||||
return retVal;
|
return retVal;
|
||||||
|
|
|
@ -23,6 +23,7 @@ package ca.uhn.fhir.jpa.dao;
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import ca.uhn.fhir.util.FhirTerser;
|
import ca.uhn.fhir.util.FhirTerser;
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import org.apache.commons.lang3.ObjectUtils;
|
import org.apache.commons.lang3.ObjectUtils;
|
||||||
|
@ -47,6 +48,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
|
||||||
private DaoConfig myDaoConfig;
|
private DaoConfig myDaoConfig;
|
||||||
@Autowired
|
@Autowired
|
||||||
private ISearchParamRegistry mySearchParamRegistry;
|
private ISearchParamRegistry mySearchParamRegistry;
|
||||||
|
|
||||||
public BaseSearchParamExtractor() {
|
public BaseSearchParamExtractor() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
@ -73,31 +75,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
|
||||||
return refs;
|
return refs;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected List<Object> extractValues(String thePaths, IBaseResource theResource) {
|
protected abstract List<Object> extractValues(String thePaths, IBaseResource theResource);
|
||||||
List<Object> values = new ArrayList<Object>();
|
|
||||||
String[] nextPathsSplit = SPLIT.split(thePaths);
|
|
||||||
FhirTerser t = myContext.newTerser();
|
|
||||||
for (String nextPath : nextPathsSplit) {
|
|
||||||
String nextPathTrimmed = nextPath.trim();
|
|
||||||
try {
|
|
||||||
List<Object> allValues = t.getValues(theResource, nextPathTrimmed);
|
|
||||||
for (Object next : allValues) {
|
|
||||||
if (next instanceof IBaseExtension) {
|
|
||||||
IBaseDatatype value = ((IBaseExtension) next).getValue();
|
|
||||||
if (value != null) {
|
|
||||||
values.add(value);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
values.add(next);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} 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(), e});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected FhirContext getContext() {
|
protected FhirContext getContext() {
|
||||||
return myContext;
|
return myContext;
|
||||||
|
|
|
@ -144,6 +144,7 @@ public class DaoConfig {
|
||||||
private boolean myExpungeEnabled;
|
private boolean myExpungeEnabled;
|
||||||
private int myReindexThreadCount;
|
private int myReindexThreadCount;
|
||||||
private Set<String> myBundleTypesAllowedForStorage;
|
private Set<String> myBundleTypesAllowedForStorage;
|
||||||
|
private boolean myValidateSearchParameterExpressionsOnSave = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
|
@ -786,7 +787,6 @@ public class DaoConfig {
|
||||||
this.myAllowContainsSearches = theAllowContainsSearches;
|
this.myAllowContainsSearches = theAllowContainsSearches;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If set to <code>true</code> (default is <code>false</code>) the server will allow
|
* If set to <code>true</code> (default is <code>false</code>) the server will allow
|
||||||
* resources to have references to external servers. For example if this server is
|
* resources to have references to external servers. For example if this server is
|
||||||
|
@ -1188,6 +1188,34 @@ public class DaoConfig {
|
||||||
myUniqueIndexesEnabled = theUniqueIndexesEnabled;
|
myUniqueIndexesEnabled = theUniqueIndexesEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If <code>true</code> (default is <code>true</code>), before allowing a
|
||||||
|
* SearchParameter resource to be stored (create, update, etc.) the
|
||||||
|
* expression will be performed against an empty resource to ensure that
|
||||||
|
* the FHIRPath executor is able to process it.
|
||||||
|
* <p>
|
||||||
|
* This should proabably always be set to true, but is configurable
|
||||||
|
* in order to support some unit tests.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public boolean isValidateSearchParameterExpressionsOnSave() {
|
||||||
|
return myValidateSearchParameterExpressionsOnSave;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If <code>true</code> (default is <code>true</code>), before allowing a
|
||||||
|
* SearchParameter resource to be stored (create, update, etc.) the
|
||||||
|
* expression will be performed against an empty resource to ensure that
|
||||||
|
* the FHIRPath executor is able to process it.
|
||||||
|
* <p>
|
||||||
|
* This should proabably always be set to true, but is configurable
|
||||||
|
* in order to support some unit tests.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public void setValidateSearchParameterExpressionsOnSave(boolean theValidateSearchParameterExpressionsOnSave) {
|
||||||
|
myValidateSearchParameterExpressionsOnSave = theValidateSearchParameterExpressionsOnSave;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Do not call this method, it exists only for legacy reasons. It
|
* Do not call this method, it exists only for legacy reasons. It
|
||||||
* will be removed in a future version. Configure the page size on your
|
* will be removed in a future version. Configure the page size on your
|
||||||
|
|
|
@ -29,14 +29,8 @@ import ca.uhn.fhir.model.dstu2.resource.SearchParameter;
|
||||||
import ca.uhn.fhir.model.dstu2.valueset.ResourceTypeEnum;
|
import ca.uhn.fhir.model.dstu2.valueset.ResourceTypeEnum;
|
||||||
import ca.uhn.fhir.model.dstu2.valueset.SearchParamTypeEnum;
|
import ca.uhn.fhir.model.dstu2.valueset.SearchParamTypeEnum;
|
||||||
import ca.uhn.fhir.model.primitive.BoundCodeDt;
|
import ca.uhn.fhir.model.primitive.BoundCodeDt;
|
||||||
import ca.uhn.fhir.model.primitive.CodeDt;
|
|
||||||
import org.apache.commons.lang3.time.DateUtils;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
|
||||||
import org.springframework.transaction.annotation.Propagation;
|
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -83,7 +77,7 @@ public class FhirResourceDaoSearchParameterDstu2 extends FhirResourceDaoDstu2<Se
|
||||||
FhirContext context = getContext();
|
FhirContext context = getContext();
|
||||||
SearchParamTypeEnum type = theResource.getTypeElement().getValueAsEnum();
|
SearchParamTypeEnum type = theResource.getTypeElement().getValueAsEnum();
|
||||||
|
|
||||||
FhirResourceDaoSearchParameterR4.validateSearchParam(type, status, base, expression, context);
|
FhirResourceDaoSearchParameterR4.validateSearchParam(type, status, base, expression, context, getConfig());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -19,33 +19,40 @@ package ca.uhn.fhir.jpa.dao;
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
|
||||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
import javax.measure.quantity.Quantity;
|
|
||||||
import javax.measure.unit.NonSI;
|
|
||||||
import javax.measure.unit.Unit;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.context.ConfigurationException;
|
import ca.uhn.fhir.context.ConfigurationException;
|
||||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||||
import ca.uhn.fhir.jpa.entity.*;
|
import ca.uhn.fhir.jpa.entity.*;
|
||||||
import ca.uhn.fhir.model.api.*;
|
import ca.uhn.fhir.model.api.IDatatype;
|
||||||
|
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
|
||||||
|
import ca.uhn.fhir.model.api.IValueSetEnumBinder;
|
||||||
import ca.uhn.fhir.model.base.composite.BaseHumanNameDt;
|
import ca.uhn.fhir.model.base.composite.BaseHumanNameDt;
|
||||||
import ca.uhn.fhir.model.dstu2.composite.*;
|
import ca.uhn.fhir.model.dstu2.composite.*;
|
||||||
import ca.uhn.fhir.model.dstu2.composite.BoundCodeableConceptDt;
|
|
||||||
import ca.uhn.fhir.model.dstu2.resource.*;
|
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Conformance.RestSecurity;
|
import ca.uhn.fhir.model.dstu2.resource.Conformance.RestSecurity;
|
||||||
|
import ca.uhn.fhir.model.dstu2.resource.Location;
|
||||||
|
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Patient.Communication;
|
import ca.uhn.fhir.model.dstu2.resource.Patient.Communication;
|
||||||
|
import ca.uhn.fhir.model.dstu2.resource.Questionnaire;
|
||||||
|
import ca.uhn.fhir.model.dstu2.resource.ValueSet;
|
||||||
import ca.uhn.fhir.model.dstu2.valueset.RestfulSecurityServiceEnum;
|
import ca.uhn.fhir.model.dstu2.valueset.RestfulSecurityServiceEnum;
|
||||||
import ca.uhn.fhir.model.primitive.*;
|
import ca.uhn.fhir.model.primitive.*;
|
||||||
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
|
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
|
import ca.uhn.fhir.util.FhirTerser;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseDatatype;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseExtension;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
|
||||||
|
import javax.measure.quantity.Quantity;
|
||||||
|
import javax.measure.unit.NonSI;
|
||||||
|
import javax.measure.unit.Unit;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
|
||||||
public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implements ISearchParamExtractor {
|
public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implements ISearchParamExtractor {
|
||||||
|
|
||||||
|
@ -87,7 +94,7 @@ public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implemen
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Set<ResourceIndexedSearchParamDate> extractSearchParamDates(ResourceTable theEntity, IBaseResource theResource) {
|
public Set<ResourceIndexedSearchParamDate> extractSearchParamDates(ResourceTable theEntity, IBaseResource theResource) {
|
||||||
HashSet<ResourceIndexedSearchParamDate> retVal = new HashSet<ResourceIndexedSearchParamDate>();
|
HashSet<ResourceIndexedSearchParamDate> retVal = new HashSet<>();
|
||||||
|
|
||||||
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
|
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
|
||||||
for (RuntimeSearchParam nextSpDef : searchParams) {
|
for (RuntimeSearchParam nextSpDef : searchParams) {
|
||||||
|
@ -626,6 +633,35 @@ public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implemen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<Object> extractValues(String thePaths, IBaseResource theResource) {
|
||||||
|
List<Object> values = new ArrayList<>();
|
||||||
|
String[] nextPathsSplit = SPLIT.split(thePaths);
|
||||||
|
FhirTerser t = getContext().newTerser();
|
||||||
|
for (String nextPath : nextPathsSplit) {
|
||||||
|
String nextPathTrimmed = nextPath.trim();
|
||||||
|
List<Object> allValues;
|
||||||
|
try {
|
||||||
|
allValues = t.getValues(theResource, nextPathTrimmed);
|
||||||
|
} catch (Exception e) {
|
||||||
|
String msg = getContext().getLocalizer().getMessage(BaseSearchParamExtractor.class, "failedToExtractPaths", nextPath, e.toString());
|
||||||
|
throw new InternalErrorException(msg, e);
|
||||||
|
}
|
||||||
|
for (Object next : allValues) {
|
||||||
|
if (next instanceof IBaseExtension) {
|
||||||
|
IBaseDatatype value = ((IBaseExtension) next).getValue();
|
||||||
|
if (value != null) {
|
||||||
|
values.add(value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
values.add(next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private static <T extends Enum<?>> String extractSystem(BoundCodeDt<T> theBoundCode) {
|
private static <T extends Enum<?>> String extractSystem(BoundCodeDt<T> theBoundCode) {
|
||||||
if (theBoundCode.getValueAsEnum() != null) {
|
if (theBoundCode.getValueAsEnum() != null) {
|
||||||
IValueSetEnumBinder<T> binder = theBoundCode.getBinder();
|
IValueSetEnumBinder<T> binder = theBoundCode.getBinder();
|
||||||
|
|
|
@ -74,7 +74,7 @@ public class FhirResourceDaoSearchParameterDstu3 extends FhirResourceDaoDstu3<Se
|
||||||
FhirContext context = getContext();
|
FhirContext context = getContext();
|
||||||
Enumerations.SearchParamType type = theResource.getType();
|
Enumerations.SearchParamType type = theResource.getType();
|
||||||
|
|
||||||
FhirResourceDaoSearchParameterR4.validateSearchParam(type, status, base, expression, context);
|
FhirResourceDaoSearchParameterR4.validateSearchParam(type, status, base, expression, context, getConfig());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,17 +19,15 @@ package ca.uhn.fhir.jpa.dao.dstu3;
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
|
||||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|
||||||
import static org.apache.commons.lang3.StringUtils.trim;
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
|
||||||
import javax.measure.unit.NonSI;
|
|
||||||
import javax.measure.unit.Unit;
|
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.ConfigurationException;
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||||
|
import ca.uhn.fhir.jpa.dao.*;
|
||||||
|
import ca.uhn.fhir.jpa.entity.*;
|
||||||
|
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
import org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext;
|
import org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext;
|
||||||
|
@ -41,16 +39,18 @@ import org.hl7.fhir.dstu3.model.Location.LocationPositionComponent;
|
||||||
import org.hl7.fhir.dstu3.model.Patient.PatientCommunicationComponent;
|
import org.hl7.fhir.dstu3.model.Patient.PatientCommunicationComponent;
|
||||||
import org.hl7.fhir.dstu3.utils.FHIRPathEngine;
|
import org.hl7.fhir.dstu3.utils.FHIRPathEngine;
|
||||||
import org.hl7.fhir.exceptions.FHIRException;
|
import org.hl7.fhir.exceptions.FHIRException;
|
||||||
import org.hl7.fhir.instance.model.api.*;
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import javax.annotation.PostConstruct;
|
||||||
|
import javax.measure.unit.NonSI;
|
||||||
|
import javax.measure.unit.Unit;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.*;
|
import static org.apache.commons.lang3.StringUtils.*;
|
||||||
import ca.uhn.fhir.jpa.dao.*;
|
|
||||||
import ca.uhn.fhir.jpa.entity.*;
|
|
||||||
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
|
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
|
||||||
|
|
||||||
public class SearchParamExtractorDstu3 extends BaseSearchParamExtractor implements ISearchParamExtractor {
|
public class SearchParamExtractorDstu3 extends BaseSearchParamExtractor implements ISearchParamExtractor {
|
||||||
|
|
||||||
|
@ -61,11 +61,6 @@ public class SearchParamExtractorDstu3 extends BaseSearchParamExtractor implemen
|
||||||
|
|
||||||
private HapiWorkerContext myWorkerContext;
|
private HapiWorkerContext myWorkerContext;
|
||||||
|
|
||||||
@PostConstruct
|
|
||||||
public void start() {
|
|
||||||
myWorkerContext = new HapiWorkerContext(getContext(), myValidationSupport);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*/
|
*/
|
||||||
|
@ -78,6 +73,17 @@ public class SearchParamExtractorDstu3 extends BaseSearchParamExtractor implemen
|
||||||
myValidationSupport = theValidationSupport;
|
myValidationSupport = theValidationSupport;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addQuantity(ResourceTable theEntity, HashSet<ResourceIndexedSearchParamQuantity> retVal, String resourceName, Quantity nextValue) {
|
||||||
|
if (!nextValue.getValueElement().isEmpty()) {
|
||||||
|
BigDecimal nextValueValue = nextValue.getValueElement().getValue();
|
||||||
|
String nextValueString = nextValue.getSystemElement().getValueAsString();
|
||||||
|
String nextValueCode = nextValue.getCode();
|
||||||
|
ResourceIndexedSearchParamQuantity nextEntity = new ResourceIndexedSearchParamQuantity(resourceName, nextValueValue, nextValueString, nextValueCode);
|
||||||
|
nextEntity.setResource(theEntity);
|
||||||
|
retVal.add(nextEntity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void addSearchTerm(ResourceTable theEntity, Set<ResourceIndexedSearchParamString> retVal, String resourceName, String searchTerm) {
|
private void addSearchTerm(ResourceTable theEntity, Set<ResourceIndexedSearchParamString> retVal, String resourceName, String searchTerm) {
|
||||||
if (isBlank(searchTerm)) {
|
if (isBlank(searchTerm)) {
|
||||||
return;
|
return;
|
||||||
|
@ -100,6 +106,23 @@ public class SearchParamExtractorDstu3 extends BaseSearchParamExtractor implemen
|
||||||
retVal.add(nextEntity);
|
retVal.add(nextEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<PathAndRef> extractResourceLinks(IBaseResource theResource, RuntimeSearchParam theNextSpDef) {
|
||||||
|
ArrayList<PathAndRef> retVal = new ArrayList<PathAndRef>();
|
||||||
|
|
||||||
|
String[] nextPathsSplit = SPLIT.split(theNextSpDef.getPath());
|
||||||
|
for (String path : nextPathsSplit) {
|
||||||
|
path = path.trim();
|
||||||
|
if (isNotBlank(path)) {
|
||||||
|
for (Object next : extractValues(path, theResource)) {
|
||||||
|
retVal.add(new PathAndRef(path, next));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<ResourceIndexedSearchParamCoords> extractSearchParamCoords(ResourceTable theEntity, IBaseResource theResource) {
|
public Set<ResourceIndexedSearchParamCoords> extractSearchParamCoords(ResourceTable theEntity, IBaseResource theResource) {
|
||||||
// TODO: implement
|
// TODO: implement
|
||||||
|
@ -200,7 +223,7 @@ public class SearchParamExtractorDstu3 extends BaseSearchParamExtractor implemen
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public HashSet<ResourceIndexedSearchParamNumber> extractSearchParamNumber(ResourceTable theEntity, IBaseResource theResource) {
|
public HashSet<ResourceIndexedSearchParamNumber> extractSearchParamNumber(ResourceTable theEntity, IBaseResource theResource) {
|
||||||
HashSet<ResourceIndexedSearchParamNumber> retVal = new HashSet<ResourceIndexedSearchParamNumber>();
|
HashSet<ResourceIndexedSearchParamNumber> retVal = new HashSet<>();
|
||||||
|
|
||||||
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
|
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
|
||||||
for (RuntimeSearchParam nextSpDef : searchParams) {
|
for (RuntimeSearchParam nextSpDef : searchParams) {
|
||||||
|
@ -349,17 +372,6 @@ public class SearchParamExtractorDstu3 extends BaseSearchParamExtractor implemen
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addQuantity(ResourceTable theEntity, HashSet<ResourceIndexedSearchParamQuantity> retVal, String resourceName, Quantity nextValue) {
|
|
||||||
if (!nextValue.getValueElement().isEmpty()) {
|
|
||||||
BigDecimal nextValueValue = nextValue.getValueElement().getValue();
|
|
||||||
String nextValueString = nextValue.getSystemElement().getValueAsString();
|
|
||||||
String nextValueCode = nextValue.getCode();
|
|
||||||
ResourceIndexedSearchParamQuantity nextEntity = new ResourceIndexedSearchParamQuantity(resourceName, nextValueValue, nextValueString, nextValueCode);
|
|
||||||
nextEntity.setResource(theEntity);
|
|
||||||
retVal.add(nextEntity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
*
|
||||||
|
@ -706,17 +718,19 @@ public class SearchParamExtractorDstu3 extends BaseSearchParamExtractor implemen
|
||||||
FHIRPathEngine fp = new FHIRPathEngine(myWorkerContext);
|
FHIRPathEngine fp = new FHIRPathEngine(myWorkerContext);
|
||||||
|
|
||||||
List<Object> values = new ArrayList<>();
|
List<Object> values = new ArrayList<>();
|
||||||
try {
|
|
||||||
String[] nextPathsSplit = SPLIT.split(thePaths);
|
String[] nextPathsSplit = SPLIT.split(thePaths);
|
||||||
for (String nextPath : nextPathsSplit) {
|
for (String nextPath : nextPathsSplit) {
|
||||||
List<Base> allValues = fp.evaluate((Base) theResource, trim(nextPath));
|
List<Base> allValues;
|
||||||
|
try {
|
||||||
|
allValues = fp.evaluate((Base) theResource, trim(nextPath));
|
||||||
|
} catch (FHIRException e) {
|
||||||
|
String msg = getContext().getLocalizer().getMessage(BaseSearchParamExtractor.class, "failedToExtractPaths", nextPath, e.toString());
|
||||||
|
throw new InternalErrorException(msg, e);
|
||||||
|
}
|
||||||
if (allValues.isEmpty() == false) {
|
if (allValues.isEmpty() == false) {
|
||||||
values.addAll(allValues);
|
values.addAll(allValues);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (FHIRException e) {
|
|
||||||
throw new InternalErrorException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < values.size(); i++) {
|
for (int i = 0; i < values.size(); i++) {
|
||||||
Object nextObject = values.get(i);
|
Object nextObject = values.get(i);
|
||||||
|
@ -730,28 +744,16 @@ public class SearchParamExtractorDstu3 extends BaseSearchParamExtractor implemen
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<PathAndRef> extractResourceLinks(IBaseResource theResource, RuntimeSearchParam theNextSpDef) {
|
|
||||||
ArrayList<PathAndRef> retVal = new ArrayList<PathAndRef>();
|
|
||||||
|
|
||||||
String[] nextPathsSplit = SPLIT.split(theNextSpDef.getPath());
|
|
||||||
for (String path : nextPathsSplit) {
|
|
||||||
path = path.trim();
|
|
||||||
if (isNotBlank(path)) {
|
|
||||||
for (Object next : extractValues(path, theResource)) {
|
|
||||||
retVal.add(new PathAndRef(path, next));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void setValidationSupportForTesting(org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport theValidationSupport) {
|
void setValidationSupportForTesting(org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport theValidationSupport) {
|
||||||
myValidationSupport = theValidationSupport;
|
myValidationSupport = theValidationSupport;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void start() {
|
||||||
|
myWorkerContext = new HapiWorkerContext(getContext(), myValidationSupport);
|
||||||
|
}
|
||||||
|
|
||||||
private static <T extends Enum<?>> String extractSystem(Enumeration<T> theBoundCode) {
|
private static <T extends Enum<?>> String extractSystem(Enumeration<T> theBoundCode) {
|
||||||
if (theBoundCode.getValue() != null) {
|
if (theBoundCode.getValue() != null) {
|
||||||
return theBoundCode.getEnumFactory().toSystem(theBoundCode.getValue());
|
return theBoundCode.getEnumFactory().toSystem(theBoundCode.getValue());
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
package ca.uhn.fhir.jpa.dao.r4;
|
package ca.uhn.fhir.jpa.dao.r4;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||||
import ca.uhn.fhir.jpa.dao.BaseSearchParamExtractor;
|
import ca.uhn.fhir.jpa.dao.BaseSearchParamExtractor;
|
||||||
|
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoSearchParameter;
|
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoSearchParameter;
|
||||||
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
||||||
import ca.uhn.fhir.jpa.entity.ResourceTable;
|
import ca.uhn.fhir.jpa.entity.ResourceTable;
|
||||||
import ca.uhn.fhir.parser.DataFormatException;
|
import ca.uhn.fhir.parser.DataFormatException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||||
import ca.uhn.fhir.util.ElementUtil;
|
import ca.uhn.fhir.util.ElementUtil;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
import org.hl7.fhir.r4.model.*;
|
import org.hl7.fhir.r4.model.*;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
@ -76,10 +80,10 @@ public class FhirResourceDaoSearchParameterR4 extends FhirResourceDaoR4<SearchPa
|
||||||
FhirContext context = getContext();
|
FhirContext context = getContext();
|
||||||
Enum<?> type = theResource.getType();
|
Enum<?> type = theResource.getType();
|
||||||
|
|
||||||
FhirResourceDaoSearchParameterR4.validateSearchParam(type, status, base, expression, context);
|
FhirResourceDaoSearchParameterR4.validateSearchParam(type, status, base, expression, context, getConfig());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void validateSearchParam(Enum<?> theType, Enum<?> theStatus, List<? extends IPrimitiveType> theBase, String theExpression, FhirContext theContext) {
|
public static void validateSearchParam(Enum<?> theType, Enum<?> theStatus, List<? extends IPrimitiveType> theBase, String theExpression, FhirContext theContext, DaoConfig theDaoConfig) {
|
||||||
if (theStatus == null) {
|
if (theStatus == null) {
|
||||||
throw new UnprocessableEntityException("SearchParameter.status is missing or invalid");
|
throw new UnprocessableEntityException("SearchParameter.status is missing or invalid");
|
||||||
}
|
}
|
||||||
|
@ -116,6 +120,17 @@ public class FhirResourceDaoSearchParameterR4 extends FhirResourceDaoR4<SearchPa
|
||||||
throw new UnprocessableEntityException("Invalid SearchParameter.expression value \"" + nextPath + "\": " + e.getMessage());
|
throw new UnprocessableEntityException("Invalid SearchParameter.expression value \"" + nextPath + "\": " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (theContext.getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.DSTU3)) {
|
||||||
|
if (theDaoConfig.isValidateSearchParameterExpressionsOnSave()) {
|
||||||
|
IBaseResource temporaryInstance = theContext.getResourceDefinition(resourceName).newInstance();
|
||||||
|
try {
|
||||||
|
theContext.newFluentPath().evaluate(temporaryInstance, nextPath, IBase.class);
|
||||||
|
} catch (Exception e) {
|
||||||
|
String msg = theContext.getLocalizer().getMessage(FhirResourceDaoSearchParameterR4.class, "invalidSearchParamExpression", nextPath, e.getMessage());
|
||||||
|
throw new UnprocessableEntityException(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // if have expression
|
} // if have expression
|
||||||
|
|
|
@ -19,17 +19,21 @@ package ca.uhn.fhir.jpa.dao.r4;
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
|
||||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
import javax.measure.unit.NonSI;
|
|
||||||
import javax.measure.unit.Unit;
|
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.ConfigurationException;
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||||
|
import ca.uhn.fhir.jpa.dao.*;
|
||||||
|
import ca.uhn.fhir.jpa.entity.*;
|
||||||
|
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
import org.hl7.fhir.exceptions.FHIRException;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
import org.hl7.fhir.r4.context.IWorkerContext;
|
import org.hl7.fhir.r4.context.IWorkerContext;
|
||||||
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport;
|
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport;
|
||||||
import org.hl7.fhir.r4.model.*;
|
import org.hl7.fhir.r4.model.*;
|
||||||
|
@ -38,17 +42,15 @@ import org.hl7.fhir.r4.model.Enumeration;
|
||||||
import org.hl7.fhir.r4.model.Location.LocationPositionComponent;
|
import org.hl7.fhir.r4.model.Location.LocationPositionComponent;
|
||||||
import org.hl7.fhir.r4.model.Patient.PatientCommunicationComponent;
|
import org.hl7.fhir.r4.model.Patient.PatientCommunicationComponent;
|
||||||
import org.hl7.fhir.r4.utils.FHIRPathEngine;
|
import org.hl7.fhir.r4.utils.FHIRPathEngine;
|
||||||
import org.hl7.fhir.exceptions.FHIRException;
|
|
||||||
import org.hl7.fhir.instance.model.api.*;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import javax.measure.unit.NonSI;
|
||||||
|
import javax.measure.unit.Unit;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.*;
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
import ca.uhn.fhir.jpa.dao.*;
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
import ca.uhn.fhir.jpa.entity.*;
|
|
||||||
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
|
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
|
||||||
|
|
||||||
public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements ISearchParamExtractor {
|
public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements ISearchParamExtractor {
|
||||||
|
|
||||||
|
@ -69,6 +71,17 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
|
||||||
myValidationSupport = theValidationSupport;
|
myValidationSupport = theValidationSupport;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addQuantity(ResourceTable theEntity, HashSet<ResourceIndexedSearchParamQuantity> retVal, String resourceName, Quantity nextValue) {
|
||||||
|
if (!nextValue.getValueElement().isEmpty()) {
|
||||||
|
BigDecimal nextValueValue = nextValue.getValueElement().getValue();
|
||||||
|
String nextValueString = nextValue.getSystemElement().getValueAsString();
|
||||||
|
String nextValueCode = nextValue.getCode();
|
||||||
|
ResourceIndexedSearchParamQuantity nextEntity = new ResourceIndexedSearchParamQuantity(resourceName, nextValueValue, nextValueString, nextValueCode);
|
||||||
|
nextEntity.setResource(theEntity);
|
||||||
|
retVal.add(nextEntity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void addSearchTerm(ResourceTable theEntity, Set<ResourceIndexedSearchParamString> retVal, String resourceName, String searchTerm) {
|
private void addSearchTerm(ResourceTable theEntity, Set<ResourceIndexedSearchParamString> retVal, String resourceName, String searchTerm) {
|
||||||
if (isBlank(searchTerm)) {
|
if (isBlank(searchTerm)) {
|
||||||
return;
|
return;
|
||||||
|
@ -91,6 +104,23 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
|
||||||
retVal.add(nextEntity);
|
retVal.add(nextEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<PathAndRef> extractResourceLinks(IBaseResource theResource, RuntimeSearchParam theNextSpDef) {
|
||||||
|
ArrayList<PathAndRef> retVal = new ArrayList<>();
|
||||||
|
|
||||||
|
String[] nextPathsSplit = SPLIT.split(theNextSpDef.getPath());
|
||||||
|
for (String path : nextPathsSplit) {
|
||||||
|
path = path.trim();
|
||||||
|
if (isNotBlank(path)) {
|
||||||
|
for (Object next : extractValues(path, theResource)) {
|
||||||
|
retVal.add(new PathAndRef(path, next));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<ResourceIndexedSearchParamCoords> extractSearchParamCoords(ResourceTable theEntity, IBaseResource theResource) {
|
public Set<ResourceIndexedSearchParamCoords> extractSearchParamCoords(ResourceTable theEntity, IBaseResource theResource) {
|
||||||
// TODO: implement
|
// TODO: implement
|
||||||
|
@ -336,17 +366,6 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addQuantity(ResourceTable theEntity, HashSet<ResourceIndexedSearchParamQuantity> retVal, String resourceName, Quantity nextValue) {
|
|
||||||
if (!nextValue.getValueElement().isEmpty()) {
|
|
||||||
BigDecimal nextValueValue = nextValue.getValueElement().getValue();
|
|
||||||
String nextValueString = nextValue.getSystemElement().getValueAsString();
|
|
||||||
String nextValueCode = nextValue.getCode();
|
|
||||||
ResourceIndexedSearchParamQuantity nextEntity = new ResourceIndexedSearchParamQuantity(resourceName, nextValueValue, nextValueString, nextValueCode);
|
|
||||||
nextEntity.setResource(theEntity);
|
|
||||||
retVal.add(nextEntity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
*
|
||||||
|
@ -691,17 +710,19 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
|
||||||
FHIRPathEngine fp = new FHIRPathEngine(worker);
|
FHIRPathEngine fp = new FHIRPathEngine(worker);
|
||||||
|
|
||||||
List<Object> values = new ArrayList<>();
|
List<Object> values = new ArrayList<>();
|
||||||
try {
|
|
||||||
String[] nextPathsSplit = SPLIT.split(thePaths);
|
String[] nextPathsSplit = SPLIT.split(thePaths);
|
||||||
for (String nextPath : nextPathsSplit) {
|
for (String nextPath : nextPathsSplit) {
|
||||||
List<Base> allValues = fp.evaluate((Base) theResource, nextPath);
|
List<Base> allValues;
|
||||||
|
try {
|
||||||
|
allValues = fp.evaluate((Base) theResource, nextPath);
|
||||||
|
} catch (FHIRException e) {
|
||||||
|
String msg = getContext().getLocalizer().getMessage(BaseSearchParamExtractor.class, "failedToExtractPaths", nextPath, e.toString());
|
||||||
|
throw new InternalErrorException(msg, e);
|
||||||
|
}
|
||||||
if (allValues.isEmpty() == false) {
|
if (allValues.isEmpty() == false) {
|
||||||
values.addAll(allValues);
|
values.addAll(allValues);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (FHIRException e) {
|
|
||||||
throw new InternalErrorException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < values.size(); i++) {
|
for (int i = 0; i < values.size(); i++) {
|
||||||
Object nextObject = values.get(i);
|
Object nextObject = values.get(i);
|
||||||
|
@ -715,23 +736,6 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<PathAndRef> extractResourceLinks(IBaseResource theResource, RuntimeSearchParam theNextSpDef) {
|
|
||||||
ArrayList<PathAndRef> retVal = new ArrayList<>();
|
|
||||||
|
|
||||||
String[] nextPathsSplit = SPLIT.split(theNextSpDef.getPath());
|
|
||||||
for (String path : nextPathsSplit) {
|
|
||||||
path = path.trim();
|
|
||||||
if (isNotBlank(path)) {
|
|
||||||
for (Object next : extractValues(path, theResource)) {
|
|
||||||
retVal.add(new PathAndRef(path, next));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void setValidationSupportForTesting(org.hl7.fhir.r4.hapi.ctx.IValidationSupport theValidationSupport) {
|
void setValidationSupportForTesting(org.hl7.fhir.r4.hapi.ctx.IValidationSupport theValidationSupport) {
|
||||||
myValidationSupport = theValidationSupport;
|
myValidationSupport = theValidationSupport;
|
||||||
|
|
|
@ -62,6 +62,9 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest {
|
||||||
@Qualifier("mySearchParameterDaoDstu2")
|
@Qualifier("mySearchParameterDaoDstu2")
|
||||||
protected IFhirResourceDao<SearchParameter> mySearchParameterDao;
|
protected IFhirResourceDao<SearchParameter> mySearchParameterDao;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@Qualifier("myCommunicationDaoDstu2")
|
||||||
|
protected IFhirResourceDao<Communication> myCommunicationDao;
|
||||||
|
@Autowired
|
||||||
@Qualifier("myBundleDaoDstu2")
|
@Qualifier("myBundleDaoDstu2")
|
||||||
protected IFhirResourceDao<Bundle> myBundleDao;
|
protected IFhirResourceDao<Bundle> myBundleDao;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
|
|
@ -13,14 +13,12 @@ import ca.uhn.fhir.model.dstu2.valueset.*;
|
||||||
import ca.uhn.fhir.model.primitive.*;
|
import ca.uhn.fhir.model.primitive.*;
|
||||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||||
import ca.uhn.fhir.rest.param.*;
|
import ca.uhn.fhir.rest.param.*;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||||
import ca.uhn.fhir.util.TestUtil;
|
import ca.uhn.fhir.util.TestUtil;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
import org.junit.AfterClass;
|
import org.junit.*;
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Ignore;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.mockito.internal.util.collections.ListUtil;
|
import org.mockito.internal.util.collections.ListUtil;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -37,6 +35,12 @@ public class FhirResourceDaoDstu2SearchCustomSearchParamTest extends BaseJpaDstu
|
||||||
myDaoConfig.setDefaultSearchParamsCanBeOverridden(new DaoConfig().isDefaultSearchParamsCanBeOverridden());
|
myDaoConfig.setDefaultSearchParamsCanBeOverridden(new DaoConfig().isDefaultSearchParamsCanBeOverridden());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void after() {
|
||||||
|
myDaoConfig.setValidateSearchParameterExpressionsOnSave(new DaoConfig().isValidateSearchParameterExpressionsOnSave());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCreateInvalidNoBase() {
|
public void testCreateInvalidNoBase() {
|
||||||
SearchParameter fooSp = new SearchParameter();
|
SearchParameter fooSp = new SearchParameter();
|
||||||
|
@ -53,6 +57,31 @@ public class FhirResourceDaoDstu2SearchCustomSearchParamTest extends BaseJpaDstu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIndexFailsIfInvalidSearchParameterExists() {
|
||||||
|
myDaoConfig.setValidateSearchParameterExpressionsOnSave(false);
|
||||||
|
|
||||||
|
SearchParameter threadIdSp = new SearchParameter();
|
||||||
|
threadIdSp.setBase(ResourceTypeEnum.COMMUNICATION);
|
||||||
|
threadIdSp.setCode("has-attachments");
|
||||||
|
threadIdSp.setType(SearchParamTypeEnum.REFERENCE);
|
||||||
|
threadIdSp.setXpath("Communication.payload[1].contentAttachment is not null");
|
||||||
|
threadIdSp.setXpathUsage(XPathUsageTypeEnum.NORMAL);
|
||||||
|
threadIdSp.setStatus(ConformanceResourceStatusEnum.ACTIVE);
|
||||||
|
mySearchParameterDao.create(threadIdSp, mySrd);
|
||||||
|
mySearchParamRegsitry.forceRefresh();
|
||||||
|
|
||||||
|
Communication com = new Communication();
|
||||||
|
com.setStatus(CommunicationStatusEnum.IN_PROGRESS);
|
||||||
|
try {
|
||||||
|
myCommunicationDao.create(com, mySrd);
|
||||||
|
fail();
|
||||||
|
} catch (InternalErrorException e) {
|
||||||
|
assertThat(e.getMessage(), startsWith("Failed to extract values from resource using FHIRPath \"Communication.payload[1].contentAttachment is not null\": ca.uhn"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCreateInvalidParamInvalidResourceName() {
|
public void testCreateInvalidParamInvalidResourceName() {
|
||||||
SearchParameter fooSp = new SearchParameter();
|
SearchParameter fooSp = new SearchParameter();
|
||||||
|
|
|
@ -168,6 +168,9 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest {
|
||||||
@Qualifier("myPatientDaoDstu3")
|
@Qualifier("myPatientDaoDstu3")
|
||||||
protected IFhirResourceDaoPatient<Patient> myPatientDao;
|
protected IFhirResourceDaoPatient<Patient> myPatientDao;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@Qualifier("myCommunicationDaoDstu3")
|
||||||
|
protected IFhirResourceDao<Communication> myCommunicationDao;
|
||||||
|
@Autowired
|
||||||
@Qualifier("myPractitionerDaoDstu3")
|
@Qualifier("myPractitionerDaoDstu3")
|
||||||
protected IFhirResourceDao<Practitioner> myPractitionerDao;
|
protected IFhirResourceDao<Practitioner> myPractitionerDao;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
package ca.uhn.fhir.jpa.dao.dstu3;
|
package ca.uhn.fhir.jpa.dao.dstu3;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||||
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
|
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
|
||||||
import ca.uhn.fhir.model.api.Include;
|
import ca.uhn.fhir.model.api.Include;
|
||||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||||
import ca.uhn.fhir.rest.param.*;
|
import ca.uhn.fhir.rest.param.*;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||||
import ca.uhn.fhir.util.TestUtil;
|
import ca.uhn.fhir.util.TestUtil;
|
||||||
|
@ -12,6 +14,7 @@ import org.hl7.fhir.dstu3.model.Appointment.AppointmentStatus;
|
||||||
import org.hl7.fhir.dstu3.model.Enumerations.AdministrativeGender;
|
import org.hl7.fhir.dstu3.model.Enumerations.AdministrativeGender;
|
||||||
import org.hl7.fhir.exceptions.FHIRException;
|
import org.hl7.fhir.exceptions.FHIRException;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
import org.junit.After;
|
||||||
import org.junit.AfterClass;
|
import org.junit.AfterClass;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -29,6 +32,11 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu
|
||||||
myDaoConfig.setReuseCachedSearchResultsForMillis(null);
|
myDaoConfig.setReuseCachedSearchResultsForMillis(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void after() {
|
||||||
|
myDaoConfig.setValidateSearchParameterExpressionsOnSave(new DaoConfig().isValidateSearchParameterExpressionsOnSave());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCreateInvalidNoBase() {
|
public void testCreateInvalidNoBase() {
|
||||||
SearchParameter fooSp = new SearchParameter();
|
SearchParameter fooSp = new SearchParameter();
|
||||||
|
@ -211,6 +219,47 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIndexFailsIfInvalidSearchParameterExists() {
|
||||||
|
myDaoConfig.setValidateSearchParameterExpressionsOnSave(false);
|
||||||
|
|
||||||
|
SearchParameter threadIdSp = new SearchParameter();
|
||||||
|
threadIdSp.addBase("Communication");
|
||||||
|
threadIdSp.setCode("has-attachments");
|
||||||
|
threadIdSp.setType(Enumerations.SearchParamType.REFERENCE);
|
||||||
|
threadIdSp.setExpression("Communication.payload[1].contentAttachment is not null");
|
||||||
|
threadIdSp.setXpathUsage(SearchParameter.XPathUsageType.NORMAL);
|
||||||
|
threadIdSp.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||||
|
mySearchParameterDao.create(threadIdSp, mySrd);
|
||||||
|
mySearchParamRegsitry.forceRefresh();
|
||||||
|
|
||||||
|
Communication com = new Communication();
|
||||||
|
com.setStatus(Communication.CommunicationStatus.INPROGRESS);
|
||||||
|
try {
|
||||||
|
myCommunicationDao.create(com, mySrd);
|
||||||
|
fail();
|
||||||
|
} catch (InternalErrorException e) {
|
||||||
|
assertThat(e.getMessage(), startsWith("Failed to extract values from resource using FHIRPath \"Communication.payload[1].contentAttachment is not null\": org.hl7.fhir"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRejectSearchParamWithInvalidExpression() {
|
||||||
|
SearchParameter threadIdSp = new SearchParameter();
|
||||||
|
threadIdSp.addBase("Communication");
|
||||||
|
threadIdSp.setCode("has-attachments");
|
||||||
|
threadIdSp.setType(Enumerations.SearchParamType.REFERENCE);
|
||||||
|
threadIdSp.setExpression("Communication.payload[1].contentAttachment is not null");
|
||||||
|
threadIdSp.setXpathUsage(SearchParameter.XPathUsageType.NORMAL);
|
||||||
|
threadIdSp.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||||
|
try {
|
||||||
|
mySearchParameterDao.create(threadIdSp, mySrd);
|
||||||
|
fail();
|
||||||
|
} catch (UnprocessableEntityException e) {
|
||||||
|
assertThat(e.getMessage(), startsWith("The expression \"Communication.payload[1].contentAttachment is not null\" can not be evaluated and may be invalid: "));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See #863
|
* See #863
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -92,6 +92,9 @@ public abstract class BaseJpaR4Test extends BaseJpaTest {
|
||||||
@Qualifier("myBundleDaoR4")
|
@Qualifier("myBundleDaoR4")
|
||||||
protected IFhirResourceDao<Bundle> myBundleDao;
|
protected IFhirResourceDao<Bundle> myBundleDao;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@Qualifier("myCommunicationDaoR4")
|
||||||
|
protected IFhirResourceDao<Communication> myCommunicationDao;
|
||||||
|
@Autowired
|
||||||
@Qualifier("myCarePlanDaoR4")
|
@Qualifier("myCarePlanDaoR4")
|
||||||
protected IFhirResourceDao<CarePlan> myCarePlanDao;
|
protected IFhirResourceDao<CarePlan> myCarePlanDao;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
|
|
@ -6,6 +6,7 @@ import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken;
|
||||||
import ca.uhn.fhir.model.api.Include;
|
import ca.uhn.fhir.model.api.Include;
|
||||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||||
import ca.uhn.fhir.rest.param.*;
|
import ca.uhn.fhir.rest.param.*;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||||
import ca.uhn.fhir.util.TestUtil;
|
import ca.uhn.fhir.util.TestUtil;
|
||||||
|
@ -13,6 +14,7 @@ import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
import org.hl7.fhir.r4.model.*;
|
import org.hl7.fhir.r4.model.*;
|
||||||
import org.hl7.fhir.r4.model.Appointment.AppointmentStatus;
|
import org.hl7.fhir.r4.model.Appointment.AppointmentStatus;
|
||||||
import org.hl7.fhir.r4.model.Enumerations.AdministrativeGender;
|
import org.hl7.fhir.r4.model.Enumerations.AdministrativeGender;
|
||||||
|
import org.junit.After;
|
||||||
import org.junit.AfterClass;
|
import org.junit.AfterClass;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -30,6 +32,11 @@ import static org.junit.Assert.*;
|
||||||
public class FhirResourceDaoR4SearchCustomSearchParamTest extends BaseJpaR4Test {
|
public class FhirResourceDaoR4SearchCustomSearchParamTest extends BaseJpaR4Test {
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4SearchCustomSearchParamTest.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4SearchCustomSearchParamTest.class);
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void after() {
|
||||||
|
myDaoConfig.setValidateSearchParameterExpressionsOnSave(new DaoConfig().isValidateSearchParameterExpressionsOnSave());
|
||||||
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void beforeDisableResultReuse() {
|
public void beforeDisableResultReuse() {
|
||||||
myDaoConfig.setReuseCachedSearchResultsForMillis(null);
|
myDaoConfig.setReuseCachedSearchResultsForMillis(null);
|
||||||
|
@ -71,7 +78,6 @@ public class FhirResourceDaoR4SearchCustomSearchParamTest extends BaseJpaR4Test
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCreateInvalidParamNoPath() {
|
public void testCreateInvalidParamNoPath() {
|
||||||
SearchParameter fooSp = new SearchParameter();
|
SearchParameter fooSp = new SearchParameter();
|
||||||
|
@ -237,6 +243,30 @@ public class FhirResourceDaoR4SearchCustomSearchParamTest extends BaseJpaR4Test
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIndexFailsIfInvalidSearchParameterExists() {
|
||||||
|
myDaoConfig.setValidateSearchParameterExpressionsOnSave(false);
|
||||||
|
|
||||||
|
SearchParameter threadIdSp = new SearchParameter();
|
||||||
|
threadIdSp.addBase("Communication");
|
||||||
|
threadIdSp.setCode("has-attachments");
|
||||||
|
threadIdSp.setType(Enumerations.SearchParamType.REFERENCE);
|
||||||
|
threadIdSp.setExpression("Communication.payload[1].contentAttachment is not null");
|
||||||
|
threadIdSp.setXpathUsage(SearchParameter.XPathUsageType.NORMAL);
|
||||||
|
threadIdSp.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||||
|
mySearchParameterDao.create(threadIdSp, mySrd);
|
||||||
|
mySearchParamRegsitry.forceRefresh();
|
||||||
|
|
||||||
|
Communication com = new Communication();
|
||||||
|
com.setStatus(Communication.CommunicationStatus.INPROGRESS);
|
||||||
|
try {
|
||||||
|
myCommunicationDao.create(com, mySrd);
|
||||||
|
fail();
|
||||||
|
} catch (InternalErrorException e) {
|
||||||
|
assertThat(e.getMessage(), startsWith("Failed to extract values from resource using FHIRPath \"Communication.payload[1].contentAttachment is not null\": org.hl7.fhir"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOverrideAndDisableBuiltInSearchParametersWithOverridingDisabled() {
|
public void testOverrideAndDisableBuiltInSearchParametersWithOverridingDisabled() {
|
||||||
myDaoConfig.setDefaultSearchParamsCanBeOverridden(false);
|
myDaoConfig.setDefaultSearchParamsCanBeOverridden(false);
|
||||||
|
@ -387,6 +417,23 @@ public class FhirResourceDaoR4SearchCustomSearchParamTest extends BaseJpaR4Test
|
||||||
assertThat(results, contains(mrId));
|
assertThat(results, contains(mrId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRejectSearchParamWithInvalidExpression() {
|
||||||
|
SearchParameter threadIdSp = new SearchParameter();
|
||||||
|
threadIdSp.addBase("Communication");
|
||||||
|
threadIdSp.setCode("has-attachments");
|
||||||
|
threadIdSp.setType(Enumerations.SearchParamType.REFERENCE);
|
||||||
|
threadIdSp.setExpression("Communication.payload[1].contentAttachment is not null");
|
||||||
|
threadIdSp.setXpathUsage(SearchParameter.XPathUsageType.NORMAL);
|
||||||
|
threadIdSp.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||||
|
try {
|
||||||
|
mySearchParameterDao.create(threadIdSp, mySrd);
|
||||||
|
fail();
|
||||||
|
} catch (UnprocessableEntityException e) {
|
||||||
|
assertThat(e.getMessage(), startsWith("The expression \"Communication.payload[1].contentAttachment is not null\" can not be evaluated and may be invalid: "));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSearchForExtensionReferenceWithNonMatchingTarget() {
|
public void testSearchForExtensionReferenceWithNonMatchingTarget() {
|
||||||
SearchParameter siblingSp = new SearchParameter();
|
SearchParameter siblingSp = new SearchParameter();
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
{
|
||||||
|
"resourceType": "Communication",
|
||||||
|
"meta": {
|
||||||
|
"lastUpdated": "2018-07-20T19:34:56.236+05:30",
|
||||||
|
"tag": [
|
||||||
|
{
|
||||||
|
"system": "systemDefined",
|
||||||
|
"code": "read"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"text": {
|
||||||
|
"status": "generated"
|
||||||
|
},
|
||||||
|
"extension": [
|
||||||
|
{
|
||||||
|
"url": "http://telus.com/fhir/StructureDefinition/ext-communication-msgOwner",
|
||||||
|
"valueString": "17427"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "http://telus.com/fhir/StructureDefinition/ext-communication-priority",
|
||||||
|
"valueCode": "normal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "http://telus.com/fhir/StructureDefinition/ext-communication-topic",
|
||||||
|
"valueString": "dsads"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "http://telus.com/fhir/StructureDefinition/ext-communication-state",
|
||||||
|
"valueCode": "draft"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "http://telus.com/fhir/StructureDefinition/ext-communication-thread-id",
|
||||||
|
"valueString": "bd7bc833-953b-4379-b0b0-be3d898dee40"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"status": "in-progress",
|
||||||
|
"recipient": [
|
||||||
|
{
|
||||||
|
"reference": "RelatedPerson/17852"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sender": {
|
||||||
|
"reference": "RelatedPerson/17427"
|
||||||
|
}
|
||||||
|
}
|
|
@ -122,7 +122,6 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
||||||
*/
|
*/
|
||||||
private String myServerVersion = createPoweredByHeaderProductVersion();
|
private String myServerVersion = createPoweredByHeaderProductVersion();
|
||||||
private boolean myStarted;
|
private boolean myStarted;
|
||||||
private Map<String, IResourceProvider> myTypeToProvider = new HashMap<>();
|
|
||||||
private boolean myUncompressIncomingContents = true;
|
private boolean myUncompressIncomingContents = true;
|
||||||
private boolean myUseBrowserFriendlyContentTypes;
|
private boolean myUseBrowserFriendlyContentTypes;
|
||||||
private ITenantIdentificationStrategy myTenantIdentificationStrategy;
|
private ITenantIdentificationStrategy myTenantIdentificationStrategy;
|
||||||
|
@ -376,7 +375,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
||||||
try {
|
try {
|
||||||
count += findResourceMethods(theProvider, clazz);
|
count += findResourceMethods(theProvider, clazz);
|
||||||
} catch (ConfigurationException e) {
|
} catch (ConfigurationException e) {
|
||||||
throw new ConfigurationException("Failure scanning class " + clazz.getSimpleName() + ": " + e.getMessage());
|
throw new ConfigurationException("Failure scanning class " + clazz.getSimpleName() + ": " + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
if (count == 0) {
|
if (count == 0) {
|
||||||
throw new ConfigurationException("Did not find any annotated RESTful methods on provider class " + theProvider.getClass().getCanonicalName());
|
throw new ConfigurationException("Did not find any annotated RESTful methods on provider class " + theProvider.getClass().getCanonicalName());
|
||||||
|
@ -1365,14 +1364,9 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
||||||
throw new NullPointerException("getResourceType() on class '" + rsrcProvider.getClass().getCanonicalName() + "' returned null");
|
throw new NullPointerException("getResourceType() on class '" + rsrcProvider.getClass().getCanonicalName() + "' returned null");
|
||||||
}
|
}
|
||||||
String resourceName = getFhirContext().getResourceDefinition(resourceType).getName();
|
String resourceName = getFhirContext().getResourceDefinition(resourceType).getName();
|
||||||
if (myTypeToProvider.containsKey(resourceName)) {
|
|
||||||
throw new ConfigurationException("Multiple resource providers return resource type[" + resourceName + "]: First[" + myTypeToProvider.get(resourceName).getClass().getCanonicalName()
|
|
||||||
+ "] and Second[" + rsrcProvider.getClass().getCanonicalName() + "]");
|
|
||||||
}
|
|
||||||
if (!inInit) {
|
if (!inInit) {
|
||||||
myResourceProviders.add(rsrcProvider);
|
myResourceProviders.add(rsrcProvider);
|
||||||
}
|
}
|
||||||
myTypeToProvider.put(resourceName, rsrcProvider);
|
|
||||||
providedResourceScanner.scanForProvidedResources(rsrcProvider);
|
providedResourceScanner.scanForProvidedResources(rsrcProvider);
|
||||||
newResourceProviders.add(rsrcProvider);
|
newResourceProviders.add(rsrcProvider);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1384,7 +1378,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
||||||
|
|
||||||
}
|
}
|
||||||
if (!newResourceProviders.isEmpty()) {
|
if (!newResourceProviders.isEmpty()) {
|
||||||
ourLog.info("Added {} resource provider(s). Total {}", newResourceProviders.size(), myTypeToProvider.size());
|
ourLog.info("Added {} resource provider(s). Total {}", newResourceProviders.size(), myResourceProviders.size());
|
||||||
for (IResourceProvider provider : newResourceProviders) {
|
for (IResourceProvider provider : newResourceProviders) {
|
||||||
assertProviderIsValid(provider);
|
assertProviderIsValid(provider);
|
||||||
findResourceMethods(provider);
|
findResourceMethods(provider);
|
||||||
|
@ -1594,7 +1588,6 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
||||||
IResourceProvider rsrcProvider = (IResourceProvider) provider;
|
IResourceProvider rsrcProvider = (IResourceProvider) provider;
|
||||||
Class<? extends IBaseResource> resourceType = rsrcProvider.getResourceType();
|
Class<? extends IBaseResource> resourceType = rsrcProvider.getResourceType();
|
||||||
String resourceName = getFhirContext().getResourceDefinition(resourceType).getName();
|
String resourceName = getFhirContext().getResourceDefinition(resourceType).getName();
|
||||||
myTypeToProvider.remove(resourceName);
|
|
||||||
providedResourceScanner.removeProvidedResources(rsrcProvider);
|
providedResourceScanner.removeProvidedResources(rsrcProvider);
|
||||||
} else {
|
} else {
|
||||||
myPlainProviders.remove(provider);
|
myPlainProviders.remove(provider);
|
||||||
|
|
|
@ -20,18 +20,7 @@ package ca.uhn.fhir.rest.server.method;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
import javax.servlet.ServletRequest;
|
|
||||||
import javax.servlet.ServletResponse;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.context.ConfigurationException;
|
import ca.uhn.fhir.context.ConfigurationException;
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.model.api.Include;
|
import ca.uhn.fhir.model.api.Include;
|
||||||
|
@ -44,8 +33,22 @@ import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
import ca.uhn.fhir.rest.param.binder.CollectionBinder;
|
import ca.uhn.fhir.rest.param.binder.CollectionBinder;
|
||||||
import ca.uhn.fhir.rest.server.method.OperationParameter.IOperationParamConverter;
|
import ca.uhn.fhir.rest.server.method.OperationParameter.IOperationParamConverter;
|
||||||
import ca.uhn.fhir.rest.server.method.ResourceParameter.Mode;
|
import ca.uhn.fhir.rest.server.method.ResourceParameter.Mode;
|
||||||
|
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||||
import ca.uhn.fhir.util.ParametersUtil;
|
import ca.uhn.fhir.util.ParametersUtil;
|
||||||
import ca.uhn.fhir.util.ReflectionUtil;
|
import ca.uhn.fhir.util.ReflectionUtil;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
|
|
||||||
|
import javax.servlet.ServletRequest;
|
||||||
|
import javax.servlet.ServletResponse;
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
|
||||||
public class MethodUtil {
|
public class MethodUtil {
|
||||||
|
|
||||||
|
@ -92,6 +95,25 @@ public class MethodUtil {
|
||||||
throw new ConfigurationException("Argument #" + paramIndex + " of Method '" + theMethod.getName() + "' in type '" + theMethod.getDeclaringClass().getCanonicalName()
|
throw new ConfigurationException("Argument #" + paramIndex + " of Method '" + theMethod.getName() + "' in type '" + theMethod.getDeclaringClass().getCanonicalName()
|
||||||
+ "' is of an invalid generic type (can not be a collection of a collection of a collection)");
|
+ "' is of an invalid generic type (can not be a collection of a collection of a collection)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the user is trying to bind IPrimitiveType they are probably
|
||||||
|
* trying to write code that is compatible across versions of FHIR.
|
||||||
|
* We'll try and come up with an appropriate subtype to give
|
||||||
|
* them.
|
||||||
|
*
|
||||||
|
* This gets tested in HistoryR4Test
|
||||||
|
*/
|
||||||
|
if (IPrimitiveType.class.equals(parameterType)) {
|
||||||
|
Class<?> genericType = ReflectionUtil.getGenericCollectionTypeOfMethodParameter(theMethod, paramIndex);
|
||||||
|
if (Date.class.equals(genericType)) {
|
||||||
|
BaseRuntimeElementDefinition<?> dateTimeDef = theContext.getElementDefinition("dateTime");
|
||||||
|
parameterType = dateTimeDef.getImplementingClass();
|
||||||
|
} else if (String.class.equals(genericType) || genericType == null) {
|
||||||
|
BaseRuntimeElementDefinition<?> dateTimeDef = theContext.getElementDefinition("string");
|
||||||
|
parameterType = dateTimeDef.getImplementingClass();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ServletRequest.class.isAssignableFrom(parameterType)) {
|
if (ServletRequest.class.isAssignableFrom(parameterType)) {
|
||||||
|
|
|
@ -47,8 +47,8 @@ public class SearchParameter extends BaseQueryParameter {
|
||||||
static final String QUALIFIER_ANY_TYPE = ":*";
|
static final String QUALIFIER_ANY_TYPE = ":*";
|
||||||
|
|
||||||
static {
|
static {
|
||||||
ourParamTypes = new HashMap<Class<?>, RestSearchParameterTypeEnum>();
|
ourParamTypes = new HashMap<>();
|
||||||
ourParamQualifiers = new HashMap<RestSearchParameterTypeEnum, Set<String>>();
|
ourParamQualifiers = new HashMap<>();
|
||||||
|
|
||||||
ourParamTypes.put(StringParam.class, RestSearchParameterTypeEnum.STRING);
|
ourParamTypes.put(StringParam.class, RestSearchParameterTypeEnum.STRING);
|
||||||
ourParamTypes.put(StringOrListParam.class, RestSearchParameterTypeEnum.STRING);
|
ourParamTypes.put(StringOrListParam.class, RestSearchParameterTypeEnum.STRING);
|
||||||
|
@ -124,7 +124,7 @@ public class SearchParameter extends BaseQueryParameter {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public List<QualifiedParamList> encode(FhirContext theContext, Object theObject) throws InternalErrorException {
|
public List<QualifiedParamList> encode(FhirContext theContext, Object theObject) throws InternalErrorException {
|
||||||
ArrayList<QualifiedParamList> retVal = new ArrayList<QualifiedParamList>();
|
ArrayList<QualifiedParamList> retVal = new ArrayList<>();
|
||||||
|
|
||||||
// TODO: declaring method should probably have a generic type..
|
// TODO: declaring method should probably have a generic type..
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
|
@ -197,7 +197,7 @@ public class SearchParameter extends BaseQueryParameter {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setChainlists(String[] theChainWhitelist, String[] theChainBlacklist) {
|
public void setChainlists(String[] theChainWhitelist, String[] theChainBlacklist) {
|
||||||
myQualifierWhitelist = new HashSet<String>(theChainWhitelist.length);
|
myQualifierWhitelist = new HashSet<>(theChainWhitelist.length);
|
||||||
myQualifierWhitelist.add(QUALIFIER_ANY_TYPE);
|
myQualifierWhitelist.add(QUALIFIER_ANY_TYPE);
|
||||||
|
|
||||||
for (int i = 0; i < theChainWhitelist.length; i++) {
|
for (int i = 0; i < theChainWhitelist.length; i++) {
|
||||||
|
@ -211,7 +211,7 @@ public class SearchParameter extends BaseQueryParameter {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (theChainBlacklist.length > 0) {
|
if (theChainBlacklist.length > 0) {
|
||||||
myQualifierBlacklist = new HashSet<String>(theChainBlacklist.length);
|
myQualifierBlacklist = new HashSet<>(theChainBlacklist.length);
|
||||||
for (String next : theChainBlacklist) {
|
for (String next : theChainBlacklist) {
|
||||||
if (next.equals(EMPTY_STRING)) {
|
if (next.equals(EMPTY_STRING)) {
|
||||||
myQualifierBlacklist.add(EMPTY_STRING);
|
myQualifierBlacklist.add(EMPTY_STRING);
|
||||||
|
@ -282,7 +282,7 @@ public class SearchParameter extends BaseQueryParameter {
|
||||||
Set<String> builtInQualifiers = ourParamQualifiers.get(typeEnum);
|
Set<String> builtInQualifiers = ourParamQualifiers.get(typeEnum);
|
||||||
if (builtInQualifiers != null) {
|
if (builtInQualifiers != null) {
|
||||||
if (myQualifierWhitelist != null) {
|
if (myQualifierWhitelist != null) {
|
||||||
HashSet<String> qualifierWhitelist = new HashSet<String>();
|
HashSet<String> qualifierWhitelist = new HashSet<>();
|
||||||
qualifierWhitelist.addAll(myQualifierWhitelist);
|
qualifierWhitelist.addAll(myQualifierWhitelist);
|
||||||
qualifierWhitelist.addAll(builtInQualifiers);
|
qualifierWhitelist.addAll(builtInQualifiers);
|
||||||
myQualifierWhitelist = qualifierWhitelist;
|
myQualifierWhitelist = qualifierWhitelist;
|
||||||
|
|
|
@ -20,7 +20,9 @@ package ca.uhn.fhir.rest.server.provider;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.*;
|
||||||
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||||
import ca.uhn.fhir.rest.annotation.*;
|
import ca.uhn.fhir.rest.annotation.*;
|
||||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
import ca.uhn.fhir.rest.param.TokenAndListParam;
|
import ca.uhn.fhir.rest.param.TokenAndListParam;
|
||||||
|
@ -29,13 +31,16 @@ import ca.uhn.fhir.rest.param.TokenParam;
|
||||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
|
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is a simple implementation of the resource provider
|
* This class is a simple implementation of the resource provider
|
||||||
|
@ -59,6 +64,8 @@ public class HashMapResourceProvider<T extends IBaseResource> implements IResour
|
||||||
private final FhirContext myFhirContext;
|
private final FhirContext myFhirContext;
|
||||||
private final String myResourceName;
|
private final String myResourceName;
|
||||||
protected Map<String, TreeMap<Long, T>> myIdToVersionToResourceMap = new HashMap<>();
|
protected Map<String, TreeMap<Long, T>> myIdToVersionToResourceMap = new HashMap<>();
|
||||||
|
protected Map<String, LinkedList<T>> myIdToHistory = new HashMap<>();
|
||||||
|
protected LinkedList<T> myTypeHistory = new LinkedList<>();
|
||||||
private long myNextId;
|
private long myNextId;
|
||||||
private AtomicLong myDeleteCount = new AtomicLong(0);
|
private AtomicLong myDeleteCount = new AtomicLong(0);
|
||||||
private AtomicLong mySearchCount = new AtomicLong(0);
|
private AtomicLong mySearchCount = new AtomicLong(0);
|
||||||
|
@ -86,6 +93,8 @@ public class HashMapResourceProvider<T extends IBaseResource> implements IResour
|
||||||
public void clear() {
|
public void clear() {
|
||||||
myNextId = 1;
|
myNextId = 1;
|
||||||
myIdToVersionToResourceMap.clear();
|
myIdToVersionToResourceMap.clear();
|
||||||
|
myIdToHistory.clear();
|
||||||
|
myTypeHistory.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -183,6 +192,21 @@ public class HashMapResourceProvider<T extends IBaseResource> implements IResour
|
||||||
return myIdToVersionToResourceMap.get(theIdPart);
|
return myIdToVersionToResourceMap.get(theIdPart);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@History
|
||||||
|
public List<T> historyInstance(@IdParam IIdType theId) {
|
||||||
|
LinkedList<T> retVal = myIdToHistory.get(theId.getIdPart());
|
||||||
|
if (retVal == null) {
|
||||||
|
throw new ResourceNotFoundException(theId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@History
|
||||||
|
public List<T> historyType() {
|
||||||
|
return myTypeHistory;
|
||||||
|
}
|
||||||
|
|
||||||
@Read(version = true)
|
@Read(version = true)
|
||||||
public IBaseResource read(@IdParam IIdType theId) {
|
public IBaseResource read(@IdParam IIdType theId) {
|
||||||
TreeMap<Long, T> versions = myIdToVersionToResourceMap.get(theId.getIdPart());
|
TreeMap<Long, T> versions = myIdToVersionToResourceMap.get(theId.getIdPart());
|
||||||
|
@ -252,16 +276,52 @@ public class HashMapResourceProvider<T extends IBaseResource> implements IResour
|
||||||
|
|
||||||
private IIdType store(@ResourceParam T theResource, String theIdPart, Long theVersionIdPart) {
|
private IIdType store(@ResourceParam T theResource, String theIdPart, Long theVersionIdPart) {
|
||||||
IIdType id = myFhirContext.getVersion().newIdType();
|
IIdType id = myFhirContext.getVersion().newIdType();
|
||||||
id.setParts(null, myResourceName, theIdPart, Long.toString(theVersionIdPart));
|
String versionIdPart = Long.toString(theVersionIdPart);
|
||||||
|
id.setParts(null, myResourceName, theIdPart, versionIdPart);
|
||||||
if (theResource != null) {
|
if (theResource != null) {
|
||||||
theResource.setId(id);
|
theResource.setId(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
TreeMap<Long, T> versionToResource = getVersionToResource(theIdPart);
|
/*
|
||||||
versionToResource.put(theVersionIdPart, theResource);
|
* This is a bit of magic to make sure that the versionId attribute
|
||||||
|
* in the resource being stored accurately represents the version
|
||||||
|
* that was assigned by this provider
|
||||||
|
*/
|
||||||
|
if (theResource != null) {
|
||||||
|
if (myFhirContext.getVersion().getVersion() == FhirVersionEnum.DSTU2) {
|
||||||
|
ResourceMetadataKeyEnum.VERSION.put((IResource) theResource, versionIdPart);
|
||||||
|
} else {
|
||||||
|
BaseRuntimeChildDefinition metaChild = myFhirContext.getResourceDefinition(myResourceType).getChildByName("meta");
|
||||||
|
List<IBase> metaValues = metaChild.getAccessor().getValues(theResource);
|
||||||
|
if (metaValues.size() > 0) {
|
||||||
|
IBase meta = metaValues.get(0);
|
||||||
|
BaseRuntimeElementCompositeDefinition<?> metaDef = (BaseRuntimeElementCompositeDefinition<?>) myFhirContext.getElementDefinition(meta.getClass());
|
||||||
|
BaseRuntimeChildDefinition versionIdDef = metaDef.getChildByName("versionId");
|
||||||
|
List<IBase> versionIdValues = versionIdDef.getAccessor().getValues(meta);
|
||||||
|
if (versionIdValues.size() > 0) {
|
||||||
|
IPrimitiveType<?> versionId = (IPrimitiveType<?>) versionIdValues.get(0);
|
||||||
|
versionId.setValueAsString(versionIdPart);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ourLog.info("Storing resource with ID: {}", id.getValue());
|
ourLog.info("Storing resource with ID: {}", id.getValue());
|
||||||
|
|
||||||
|
// Store to ID->version->resource map
|
||||||
|
TreeMap<Long, T> versionToResource = getVersionToResource(theIdPart);
|
||||||
|
versionToResource.put(theVersionIdPart, theResource);
|
||||||
|
|
||||||
|
// Store to type history map
|
||||||
|
myTypeHistory.addFirst(theResource);
|
||||||
|
|
||||||
|
// Store to ID history map
|
||||||
|
if (!myIdToHistory.containsKey(theIdPart)) {
|
||||||
|
myIdToHistory.put(theIdPart, new LinkedList<>());
|
||||||
|
}
|
||||||
|
myIdToHistory.get(theIdPart).addFirst(theResource);
|
||||||
|
|
||||||
|
// Return the newly assigned ID including the version ID
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,43 +1,5 @@
|
||||||
package ca.uhn.fhir.rest.client;
|
package ca.uhn.fhir.rest.client;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.containsString;
|
|
||||||
import static org.hamcrest.Matchers.startsWith;
|
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertFalse;
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
|
||||||
import static org.junit.Assert.assertNull;
|
|
||||||
import static org.junit.Assert.assertThat;
|
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
import com.helger.commons.io.stream.StringInputStream;
|
|
||||||
import org.apache.commons.io.IOUtils;
|
|
||||||
import org.apache.commons.io.input.ReaderInputStream;
|
|
||||||
import org.apache.http.*;
|
|
||||||
import org.apache.http.client.ClientProtocolException;
|
|
||||||
import org.apache.http.client.HttpClient;
|
|
||||||
import org.apache.http.client.methods.*;
|
|
||||||
import org.apache.http.message.BasicHeader;
|
|
||||||
import org.apache.http.message.BasicStatusLine;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
|
||||||
import org.hl7.fhir.r4.model.*;
|
|
||||||
import org.hl7.fhir.r4.model.Bundle.BundleType;
|
|
||||||
import org.junit.*;
|
|
||||||
import org.mockito.ArgumentCaptor;
|
|
||||||
import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs;
|
|
||||||
import org.mockito.invocation.InvocationOnMock;
|
|
||||||
import org.mockito.stubbing.Answer;
|
|
||||||
|
|
||||||
import com.google.common.base.Charsets;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||||
|
@ -47,8 +9,10 @@ import ca.uhn.fhir.model.primitive.StringDt;
|
||||||
import ca.uhn.fhir.parser.CustomTypeR4Test;
|
import ca.uhn.fhir.parser.CustomTypeR4Test;
|
||||||
import ca.uhn.fhir.parser.CustomTypeR4Test.MyCustomPatient;
|
import ca.uhn.fhir.parser.CustomTypeR4Test.MyCustomPatient;
|
||||||
import ca.uhn.fhir.parser.IParser;
|
import ca.uhn.fhir.parser.IParser;
|
||||||
import ca.uhn.fhir.rest.api.*;
|
|
||||||
import ca.uhn.fhir.rest.api.Constants;
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
|
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||||
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
|
import ca.uhn.fhir.rest.api.PreferReturnEnum;
|
||||||
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||||
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
|
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
|
||||||
import ca.uhn.fhir.rest.client.exceptions.FhirClientConnectionException;
|
import ca.uhn.fhir.rest.client.exceptions.FhirClientConnectionException;
|
||||||
|
@ -57,14 +21,55 @@ import ca.uhn.fhir.rest.client.impl.BaseClient;
|
||||||
import ca.uhn.fhir.rest.client.interceptor.CookieInterceptor;
|
import ca.uhn.fhir.rest.client.interceptor.CookieInterceptor;
|
||||||
import ca.uhn.fhir.rest.client.interceptor.UserInfoInterceptor;
|
import ca.uhn.fhir.rest.client.interceptor.UserInfoInterceptor;
|
||||||
import ca.uhn.fhir.rest.param.DateParam;
|
import ca.uhn.fhir.rest.param.DateParam;
|
||||||
|
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||||
import ca.uhn.fhir.rest.param.ParamPrefixEnum;
|
import ca.uhn.fhir.rest.param.ParamPrefixEnum;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.NotImplementedOperationException;
|
import ca.uhn.fhir.rest.server.exceptions.NotImplementedOperationException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.UnclassifiedServerFailureException;
|
import ca.uhn.fhir.rest.server.exceptions.UnclassifiedServerFailureException;
|
||||||
import ca.uhn.fhir.util.*;
|
import ca.uhn.fhir.util.TestUtil;
|
||||||
|
import ca.uhn.fhir.util.UrlUtil;
|
||||||
|
import ca.uhn.fhir.util.VersionUtil;
|
||||||
|
import com.google.common.base.Charsets;
|
||||||
|
import com.helger.commons.io.stream.StringInputStream;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.commons.io.input.ReaderInputStream;
|
||||||
|
import org.apache.http.Header;
|
||||||
|
import org.apache.http.HttpResponse;
|
||||||
|
import org.apache.http.ProtocolVersion;
|
||||||
|
import org.apache.http.client.HttpClient;
|
||||||
|
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
|
||||||
|
import org.apache.http.client.methods.HttpPut;
|
||||||
|
import org.apache.http.client.methods.HttpUriRequest;
|
||||||
|
import org.apache.http.message.BasicHeader;
|
||||||
|
import org.apache.http.message.BasicStatusLine;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
|
import org.hl7.fhir.r4.model.*;
|
||||||
|
import org.hl7.fhir.r4.model.Bundle.BundleType;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs;
|
||||||
|
import org.mockito.invocation.InvocationOnMock;
|
||||||
|
import org.mockito.stubbing.Answer;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.containsString;
|
||||||
|
import static org.hamcrest.Matchers.startsWith;
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
public class GenericClientR4Test {
|
public class GenericClientR4Test {
|
||||||
private static FhirContext ourCtx;
|
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(GenericClientR4Test.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(GenericClientR4Test.class);
|
||||||
|
private static FhirContext ourCtx;
|
||||||
private int myAnswerCount;
|
private int myAnswerCount;
|
||||||
private HttpClient myHttpClient;
|
private HttpClient myHttpClient;
|
||||||
private HttpResponse myHttpResponse;
|
private HttpResponse myHttpResponse;
|
||||||
|
@ -109,30 +114,6 @@ public class GenericClientR4Test {
|
||||||
return capt;
|
return capt;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSearchWithNoExplicitBundleReturnType() throws Exception {
|
|
||||||
|
|
||||||
String msg = ClientR4Test.getPatientFeedWithOneResult(ourCtx);
|
|
||||||
|
|
||||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
|
||||||
|
|
||||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
|
||||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
|
||||||
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
|
|
||||||
|
|
||||||
// httpResponse = new BasicHttpResponse(statusline, catalog, locale)
|
|
||||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
|
||||||
|
|
||||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://foo");
|
|
||||||
Bundle response = (Bundle) client.search().forResource(Patient.class).execute();
|
|
||||||
|
|
||||||
assertEquals("http://foo/Patient", capt.getValue().getURI().toString());
|
|
||||||
Patient patient = (Patient) response.getEntry().get(0).getResource();
|
|
||||||
assertEquals("PRP1660", patient.getIdentifier().get(0).getValueElement().getValue());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAcceptHeaderWithEncodingSpecified() throws Exception {
|
public void testAcceptHeaderWithEncodingSpecified() throws Exception {
|
||||||
final String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}";
|
final String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}";
|
||||||
|
@ -216,184 +197,6 @@ public class GenericClientR4Test {
|
||||||
assertEquals("<div xmlns=\"http://www.w3.org/1999/xhtml\">A PATIENT</div>", outputPt.getText().getDivAsString());
|
assertEquals("<div xmlns=\"http://www.w3.org/1999/xhtml\">A PATIENT</div>", outputPt.getText().getDivAsString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testOperationServer() throws Exception {
|
|
||||||
IParser p = ourCtx.newXmlParser();
|
|
||||||
|
|
||||||
Parameters inputParams = new Parameters();
|
|
||||||
inputParams.addParameter().setName("name").setValue(new BooleanType(true));
|
|
||||||
|
|
||||||
Parameters outputParams = new Parameters();
|
|
||||||
outputParams.addParameter().setName("name").setValue(new BooleanType(false));
|
|
||||||
|
|
||||||
final String respString = p.encodeResourceToString(outputParams);
|
|
||||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
|
||||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
|
||||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
|
||||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
|
||||||
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
|
|
||||||
@Override
|
|
||||||
public ReaderInputStream answer(InvocationOnMock theInvocation) {
|
|
||||||
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
|
||||||
|
|
||||||
Parameters result = client
|
|
||||||
.operation()
|
|
||||||
.onServer()
|
|
||||||
.named("opname")
|
|
||||||
.withParameters(inputParams)
|
|
||||||
.execute();
|
|
||||||
|
|
||||||
assertEquals("name", result.getParameterFirstRep().getName());
|
|
||||||
assertEquals("false", ((IPrimitiveType<?>)result.getParameterFirstRep().getValue()).getValueAsString());
|
|
||||||
|
|
||||||
assertEquals("http://example.com/fhir/$opname", capt.getAllValues().get(0).getURI().toASCIIString());
|
|
||||||
validateUserAgent(capt);
|
|
||||||
|
|
||||||
assertEquals("application/fhir+xml;charset=utf-8", capt.getAllValues().get(0).getHeaders("Content-Type")[0].getValue().toLowerCase().replace(" ", ""));
|
|
||||||
assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_NON_LEGACY, capt.getAllValues().get(0).getHeaders("Accept")[0].getValue());
|
|
||||||
Parameters output = ourCtx.newXmlParser().parseResource(Parameters.class, extractBodyAsString(capt));
|
|
||||||
assertEquals("name", output.getParameterFirstRep().getName());
|
|
||||||
assertEquals("true", ((IPrimitiveType<?>)output.getParameterFirstRep().getValue()).getValueAsString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testOperationType() throws Exception {
|
|
||||||
IParser p = ourCtx.newXmlParser();
|
|
||||||
|
|
||||||
Parameters inputParams = new Parameters();
|
|
||||||
inputParams.addParameter().setName("name").setValue(new BooleanType(true));
|
|
||||||
|
|
||||||
Parameters outputParams = new Parameters();
|
|
||||||
outputParams.addParameter().setName("name").setValue(new BooleanType(false));
|
|
||||||
|
|
||||||
final String respString = p.encodeResourceToString(outputParams);
|
|
||||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
|
||||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
|
||||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
|
||||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
|
||||||
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
|
|
||||||
@Override
|
|
||||||
public ReaderInputStream answer(InvocationOnMock theInvocation) {
|
|
||||||
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
|
||||||
|
|
||||||
Parameters result = client
|
|
||||||
.operation()
|
|
||||||
.onType(Patient.class)
|
|
||||||
.named("opname")
|
|
||||||
.withParameters(inputParams)
|
|
||||||
.execute();
|
|
||||||
|
|
||||||
assertEquals("name", result.getParameterFirstRep().getName());
|
|
||||||
assertEquals("false", ((IPrimitiveType<?>)result.getParameterFirstRep().getValue()).getValueAsString());
|
|
||||||
|
|
||||||
assertEquals("http://example.com/fhir/Patient/$opname", capt.getAllValues().get(0).getURI().toASCIIString());
|
|
||||||
validateUserAgent(capt);
|
|
||||||
|
|
||||||
assertEquals("application/fhir+xml;charset=utf-8", capt.getAllValues().get(0).getHeaders("Content-Type")[0].getValue().toLowerCase().replace(" ", ""));
|
|
||||||
assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_NON_LEGACY, capt.getAllValues().get(0).getHeaders("Accept")[0].getValue());
|
|
||||||
Parameters output = ourCtx.newXmlParser().parseResource(Parameters.class, extractBodyAsString(capt));
|
|
||||||
assertEquals("name", output.getParameterFirstRep().getName());
|
|
||||||
assertEquals("true", ((IPrimitiveType<?>)output.getParameterFirstRep().getValue()).getValueAsString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testOperationInstance() throws Exception {
|
|
||||||
IParser p = ourCtx.newXmlParser();
|
|
||||||
|
|
||||||
Parameters inputParams = new Parameters();
|
|
||||||
inputParams.addParameter().setName("name").setValue(new BooleanType(true));
|
|
||||||
|
|
||||||
Parameters outputParams = new Parameters();
|
|
||||||
outputParams.addParameter().setName("name").setValue(new BooleanType(false));
|
|
||||||
|
|
||||||
final String respString = p.encodeResourceToString(outputParams);
|
|
||||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
|
||||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
|
||||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
|
||||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
|
||||||
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
|
|
||||||
@Override
|
|
||||||
public ReaderInputStream answer(InvocationOnMock theInvocation) {
|
|
||||||
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
|
||||||
|
|
||||||
Parameters result = client
|
|
||||||
.operation()
|
|
||||||
.onInstance(new IdType("Patient/123/_history/456"))
|
|
||||||
.named("opname")
|
|
||||||
.withParameters(inputParams)
|
|
||||||
.execute();
|
|
||||||
|
|
||||||
assertEquals("name", result.getParameterFirstRep().getName());
|
|
||||||
assertEquals("false", ((IPrimitiveType<?>)result.getParameterFirstRep().getValue()).getValueAsString());
|
|
||||||
|
|
||||||
assertEquals("http://example.com/fhir/Patient/123/$opname", capt.getAllValues().get(0).getURI().toASCIIString());
|
|
||||||
validateUserAgent(capt);
|
|
||||||
|
|
||||||
assertEquals("application/fhir+xml;charset=utf-8", capt.getAllValues().get(0).getHeaders("Content-Type")[0].getValue().toLowerCase().replace(" ", ""));
|
|
||||||
assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_NON_LEGACY, capt.getAllValues().get(0).getHeaders("Accept")[0].getValue());
|
|
||||||
Parameters output = ourCtx.newXmlParser().parseResource(Parameters.class, extractBodyAsString(capt));
|
|
||||||
assertEquals("name", output.getParameterFirstRep().getName());
|
|
||||||
assertEquals("true", ((IPrimitiveType<?>)output.getParameterFirstRep().getValue()).getValueAsString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testOperationInstanceVersion() throws Exception {
|
|
||||||
IParser p = ourCtx.newXmlParser();
|
|
||||||
|
|
||||||
Parameters inputParams = new Parameters();
|
|
||||||
inputParams.addParameter().setName("name").setValue(new BooleanType(true));
|
|
||||||
|
|
||||||
Parameters outputParams = new Parameters();
|
|
||||||
outputParams.addParameter().setName("name").setValue(new BooleanType(false));
|
|
||||||
|
|
||||||
final String respString = p.encodeResourceToString(outputParams);
|
|
||||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
|
||||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
|
||||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
|
||||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
|
||||||
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
|
|
||||||
@Override
|
|
||||||
public ReaderInputStream answer(InvocationOnMock theInvocation) {
|
|
||||||
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
|
||||||
|
|
||||||
Parameters result = client
|
|
||||||
.operation()
|
|
||||||
.onInstanceVersion(new IdType("Patient/123/_history/456"))
|
|
||||||
.named("opname")
|
|
||||||
.withParameters(inputParams)
|
|
||||||
.execute();
|
|
||||||
|
|
||||||
assertEquals("name", result.getParameterFirstRep().getName());
|
|
||||||
assertEquals("false", ((IPrimitiveType<?>)result.getParameterFirstRep().getValue()).getValueAsString());
|
|
||||||
|
|
||||||
assertEquals("http://example.com/fhir/Patient/123/_history/456/$opname", capt.getAllValues().get(0).getURI().toASCIIString());
|
|
||||||
validateUserAgent(capt);
|
|
||||||
|
|
||||||
assertEquals("application/fhir+xml;charset=utf-8", capt.getAllValues().get(0).getHeaders("Content-Type")[0].getValue().toLowerCase().replace(" ", ""));
|
|
||||||
assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_NON_LEGACY, capt.getAllValues().get(0).getHeaders("Accept")[0].getValue());
|
|
||||||
Parameters output = ourCtx.newXmlParser().parseResource(Parameters.class, extractBodyAsString(capt));
|
|
||||||
assertEquals("name", output.getParameterFirstRep().getName());
|
|
||||||
assertEquals("true", ((IPrimitiveType<?>)output.getParameterFirstRep().getValue()).getValueAsString());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBinaryCreateWithNoContentType() throws Exception {
|
public void testBinaryCreateWithNoContentType() throws Exception {
|
||||||
IParser p = ourCtx.newXmlParser();
|
IParser p = ourCtx.newXmlParser();
|
||||||
|
@ -416,7 +219,7 @@ public class GenericClientR4Test {
|
||||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||||
|
|
||||||
Binary bin = new Binary();
|
Binary bin = new Binary();
|
||||||
bin.setContent(new byte[] { 0, 1, 2, 3, 4 });
|
bin.setContent(new byte[] {0, 1, 2, 3, 4});
|
||||||
client.create().resource(bin).execute();
|
client.create().resource(bin).execute();
|
||||||
|
|
||||||
ourLog.info(Arrays.asList(capt.getAllValues().get(0).getAllHeaders()).toString());
|
ourLog.info(Arrays.asList(capt.getAllValues().get(0).getAllHeaders()).toString());
|
||||||
|
@ -426,7 +229,7 @@ public class GenericClientR4Test {
|
||||||
|
|
||||||
assertEquals("application/fhir+xml;charset=utf-8", capt.getAllValues().get(0).getHeaders("Content-Type")[0].getValue().toLowerCase().replace(" ", ""));
|
assertEquals("application/fhir+xml;charset=utf-8", capt.getAllValues().get(0).getHeaders("Content-Type")[0].getValue().toLowerCase().replace(" ", ""));
|
||||||
assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_NON_LEGACY, capt.getAllValues().get(0).getHeaders("Accept")[0].getValue());
|
assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_NON_LEGACY, capt.getAllValues().get(0).getHeaders("Accept")[0].getValue());
|
||||||
assertArrayEquals(new byte[] { 0, 1, 2, 3, 4 }, ourCtx.newXmlParser().parseResource(Binary.class, extractBodyAsString(capt)).getContent());
|
assertArrayEquals(new byte[] {0, 1, 2, 3, 4}, ourCtx.newXmlParser().parseResource(Binary.class, extractBodyAsString(capt)).getContent());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -505,7 +308,7 @@ public class GenericClientR4Test {
|
||||||
when(myHttpResponse.getAllHeaders()).thenAnswer(new Answer<Header[]>() {
|
when(myHttpResponse.getAllHeaders()).thenAnswer(new Answer<Header[]>() {
|
||||||
@Override
|
@Override
|
||||||
public Header[] answer(InvocationOnMock theInvocation) {
|
public Header[] answer(InvocationOnMock theInvocation) {
|
||||||
return new Header[] { new BasicHeader(Constants.HEADER_LOCATION, "http://foo.com/base/Patient/222/_history/3") };
|
return new Header[] {new BasicHeader(Constants.HEADER_LOCATION, "http://foo.com/base/Patient/222/_history/3")};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
||||||
|
@ -554,7 +357,7 @@ public class GenericClientR4Test {
|
||||||
when(myHttpResponse.getAllHeaders()).thenAnswer(new Answer<Header[]>() {
|
when(myHttpResponse.getAllHeaders()).thenAnswer(new Answer<Header[]>() {
|
||||||
@Override
|
@Override
|
||||||
public Header[] answer(InvocationOnMock theInvocation) {
|
public Header[] answer(InvocationOnMock theInvocation) {
|
||||||
return new Header[] { new BasicHeader(Constants.HEADER_LOCATION, "http://foo.com/base/Patient/222/_history/3") };
|
return new Header[] {new BasicHeader(Constants.HEADER_LOCATION, "http://foo.com/base/Patient/222/_history/3")};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
||||||
|
@ -820,6 +623,39 @@ public class GenericClientR4Test {
|
||||||
assertEquals("http://testForceConformance.com/fhir/metadata", capt.getAllValues().get(3).getURI().toASCIIString());
|
assertEquals("http://testForceConformance.com/fhir/metadata", capt.getAllValues().get(3).getURI().toASCIIString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHistoryTypeWithAt() throws Exception {
|
||||||
|
|
||||||
|
final Bundle resp1 = new Bundle();
|
||||||
|
resp1.setTotal(0);
|
||||||
|
|
||||||
|
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||||
|
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||||
|
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||||
|
when(myHttpResponse.getAllHeaders()).thenAnswer(new Answer<Header[]>() {
|
||||||
|
@Override
|
||||||
|
public Header[] answer(InvocationOnMock theInvocation) {
|
||||||
|
return new Header[0];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
||||||
|
when(myHttpResponse.getEntity().getContent()).thenAnswer(t -> {
|
||||||
|
IParser p = ourCtx.newXmlParser();
|
||||||
|
return new ReaderInputStream(new StringReader(p.encodeResourceToString(resp1)), Charset.forName("UTF-8"));
|
||||||
|
});
|
||||||
|
|
||||||
|
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||||
|
|
||||||
|
Bundle outcome = client
|
||||||
|
.history()
|
||||||
|
.onServer().andReturnBundle(Bundle.class)
|
||||||
|
.at(new DateRangeParam().setLowerBound("2011").setUpperBound("2018"))
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
assertEquals(0, outcome.getTotal());
|
||||||
|
assertEquals("http://example.com/fhir/_history?_at=ge2011&_at=le2018", capt.getAllValues().get(0).getURI().toASCIIString());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testHttp499() throws Exception {
|
public void testHttp499() throws Exception {
|
||||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||||
|
@ -914,6 +750,182 @@ public class GenericClientR4Test {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOperationInstance() throws Exception {
|
||||||
|
IParser p = ourCtx.newXmlParser();
|
||||||
|
|
||||||
|
Parameters inputParams = new Parameters();
|
||||||
|
inputParams.addParameter().setName("name").setValue(new BooleanType(true));
|
||||||
|
|
||||||
|
Parameters outputParams = new Parameters();
|
||||||
|
outputParams.addParameter().setName("name").setValue(new BooleanType(false));
|
||||||
|
|
||||||
|
final String respString = p.encodeResourceToString(outputParams);
|
||||||
|
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||||
|
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||||
|
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||||
|
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
||||||
|
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
|
||||||
|
@Override
|
||||||
|
public ReaderInputStream answer(InvocationOnMock theInvocation) {
|
||||||
|
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||||
|
|
||||||
|
Parameters result = client
|
||||||
|
.operation()
|
||||||
|
.onInstance(new IdType("Patient/123/_history/456"))
|
||||||
|
.named("opname")
|
||||||
|
.withParameters(inputParams)
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
assertEquals("name", result.getParameterFirstRep().getName());
|
||||||
|
assertEquals("false", ((IPrimitiveType<?>) result.getParameterFirstRep().getValue()).getValueAsString());
|
||||||
|
|
||||||
|
assertEquals("http://example.com/fhir/Patient/123/$opname", capt.getAllValues().get(0).getURI().toASCIIString());
|
||||||
|
validateUserAgent(capt);
|
||||||
|
|
||||||
|
assertEquals("application/fhir+xml;charset=utf-8", capt.getAllValues().get(0).getHeaders("Content-Type")[0].getValue().toLowerCase().replace(" ", ""));
|
||||||
|
assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_NON_LEGACY, capt.getAllValues().get(0).getHeaders("Accept")[0].getValue());
|
||||||
|
Parameters output = ourCtx.newXmlParser().parseResource(Parameters.class, extractBodyAsString(capt));
|
||||||
|
assertEquals("name", output.getParameterFirstRep().getName());
|
||||||
|
assertEquals("true", ((IPrimitiveType<?>) output.getParameterFirstRep().getValue()).getValueAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOperationInstanceVersion() throws Exception {
|
||||||
|
IParser p = ourCtx.newXmlParser();
|
||||||
|
|
||||||
|
Parameters inputParams = new Parameters();
|
||||||
|
inputParams.addParameter().setName("name").setValue(new BooleanType(true));
|
||||||
|
|
||||||
|
Parameters outputParams = new Parameters();
|
||||||
|
outputParams.addParameter().setName("name").setValue(new BooleanType(false));
|
||||||
|
|
||||||
|
final String respString = p.encodeResourceToString(outputParams);
|
||||||
|
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||||
|
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||||
|
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||||
|
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
||||||
|
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
|
||||||
|
@Override
|
||||||
|
public ReaderInputStream answer(InvocationOnMock theInvocation) {
|
||||||
|
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||||
|
|
||||||
|
Parameters result = client
|
||||||
|
.operation()
|
||||||
|
.onInstanceVersion(new IdType("Patient/123/_history/456"))
|
||||||
|
.named("opname")
|
||||||
|
.withParameters(inputParams)
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
assertEquals("name", result.getParameterFirstRep().getName());
|
||||||
|
assertEquals("false", ((IPrimitiveType<?>) result.getParameterFirstRep().getValue()).getValueAsString());
|
||||||
|
|
||||||
|
assertEquals("http://example.com/fhir/Patient/123/_history/456/$opname", capt.getAllValues().get(0).getURI().toASCIIString());
|
||||||
|
validateUserAgent(capt);
|
||||||
|
|
||||||
|
assertEquals("application/fhir+xml;charset=utf-8", capt.getAllValues().get(0).getHeaders("Content-Type")[0].getValue().toLowerCase().replace(" ", ""));
|
||||||
|
assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_NON_LEGACY, capt.getAllValues().get(0).getHeaders("Accept")[0].getValue());
|
||||||
|
Parameters output = ourCtx.newXmlParser().parseResource(Parameters.class, extractBodyAsString(capt));
|
||||||
|
assertEquals("name", output.getParameterFirstRep().getName());
|
||||||
|
assertEquals("true", ((IPrimitiveType<?>) output.getParameterFirstRep().getValue()).getValueAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOperationServer() throws Exception {
|
||||||
|
IParser p = ourCtx.newXmlParser();
|
||||||
|
|
||||||
|
Parameters inputParams = new Parameters();
|
||||||
|
inputParams.addParameter().setName("name").setValue(new BooleanType(true));
|
||||||
|
|
||||||
|
Parameters outputParams = new Parameters();
|
||||||
|
outputParams.addParameter().setName("name").setValue(new BooleanType(false));
|
||||||
|
|
||||||
|
final String respString = p.encodeResourceToString(outputParams);
|
||||||
|
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||||
|
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||||
|
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||||
|
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
||||||
|
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
|
||||||
|
@Override
|
||||||
|
public ReaderInputStream answer(InvocationOnMock theInvocation) {
|
||||||
|
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||||
|
|
||||||
|
Parameters result = client
|
||||||
|
.operation()
|
||||||
|
.onServer()
|
||||||
|
.named("opname")
|
||||||
|
.withParameters(inputParams)
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
assertEquals("name", result.getParameterFirstRep().getName());
|
||||||
|
assertEquals("false", ((IPrimitiveType<?>) result.getParameterFirstRep().getValue()).getValueAsString());
|
||||||
|
|
||||||
|
assertEquals("http://example.com/fhir/$opname", capt.getAllValues().get(0).getURI().toASCIIString());
|
||||||
|
validateUserAgent(capt);
|
||||||
|
|
||||||
|
assertEquals("application/fhir+xml;charset=utf-8", capt.getAllValues().get(0).getHeaders("Content-Type")[0].getValue().toLowerCase().replace(" ", ""));
|
||||||
|
assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_NON_LEGACY, capt.getAllValues().get(0).getHeaders("Accept")[0].getValue());
|
||||||
|
Parameters output = ourCtx.newXmlParser().parseResource(Parameters.class, extractBodyAsString(capt));
|
||||||
|
assertEquals("name", output.getParameterFirstRep().getName());
|
||||||
|
assertEquals("true", ((IPrimitiveType<?>) output.getParameterFirstRep().getValue()).getValueAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOperationType() throws Exception {
|
||||||
|
IParser p = ourCtx.newXmlParser();
|
||||||
|
|
||||||
|
Parameters inputParams = new Parameters();
|
||||||
|
inputParams.addParameter().setName("name").setValue(new BooleanType(true));
|
||||||
|
|
||||||
|
Parameters outputParams = new Parameters();
|
||||||
|
outputParams.addParameter().setName("name").setValue(new BooleanType(false));
|
||||||
|
|
||||||
|
final String respString = p.encodeResourceToString(outputParams);
|
||||||
|
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||||
|
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||||
|
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||||
|
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
||||||
|
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
|
||||||
|
@Override
|
||||||
|
public ReaderInputStream answer(InvocationOnMock theInvocation) {
|
||||||
|
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||||
|
|
||||||
|
Parameters result = client
|
||||||
|
.operation()
|
||||||
|
.onType(Patient.class)
|
||||||
|
.named("opname")
|
||||||
|
.withParameters(inputParams)
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
assertEquals("name", result.getParameterFirstRep().getName());
|
||||||
|
assertEquals("false", ((IPrimitiveType<?>) result.getParameterFirstRep().getValue()).getValueAsString());
|
||||||
|
|
||||||
|
assertEquals("http://example.com/fhir/Patient/$opname", capt.getAllValues().get(0).getURI().toASCIIString());
|
||||||
|
validateUserAgent(capt);
|
||||||
|
|
||||||
|
assertEquals("application/fhir+xml;charset=utf-8", capt.getAllValues().get(0).getHeaders("Content-Type")[0].getValue().toLowerCase().replace(" ", ""));
|
||||||
|
assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_NON_LEGACY, capt.getAllValues().get(0).getHeaders("Accept")[0].getValue());
|
||||||
|
Parameters output = ourCtx.newXmlParser().parseResource(Parameters.class, extractBodyAsString(capt));
|
||||||
|
assertEquals("name", output.getParameterFirstRep().getName());
|
||||||
|
assertEquals("true", ((IPrimitiveType<?>) output.getParameterFirstRep().getValue()).getValueAsString());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPatchInvalid() {
|
public void testPatchInvalid() {
|
||||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||||
|
@ -1746,6 +1758,29 @@ public class GenericClientR4Test {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSearchWithNoExplicitBundleReturnType() throws Exception {
|
||||||
|
|
||||||
|
String msg = ClientR4Test.getPatientFeedWithOneResult(ourCtx);
|
||||||
|
|
||||||
|
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||||
|
|
||||||
|
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||||
|
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
||||||
|
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
|
||||||
|
|
||||||
|
// httpResponse = new BasicHttpResponse(statusline, catalog, locale)
|
||||||
|
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||||
|
|
||||||
|
IGenericClient client = ourCtx.newRestfulGenericClient("http://foo");
|
||||||
|
Bundle response = (Bundle) client.search().forResource(Patient.class).execute();
|
||||||
|
|
||||||
|
assertEquals("http://foo/Patient", capt.getValue().getURI().toString());
|
||||||
|
Patient patient = (Patient) response.getEntry().get(0).getResource();
|
||||||
|
assertEquals("PRP1660", patient.getIdentifier().get(0).getValueElement().getValue());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSearchWithNullParameters() throws Exception {
|
public void testSearchWithNullParameters() throws Exception {
|
||||||
final String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}";
|
final String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}";
|
||||||
|
@ -1921,7 +1956,7 @@ public class GenericClientR4Test {
|
||||||
when(myHttpResponse.getAllHeaders()).thenAnswer(new Answer<Header[]>() {
|
when(myHttpResponse.getAllHeaders()).thenAnswer(new Answer<Header[]>() {
|
||||||
@Override
|
@Override
|
||||||
public Header[] answer(InvocationOnMock theInvocation) {
|
public Header[] answer(InvocationOnMock theInvocation) {
|
||||||
return new Header[] { new BasicHeader(Constants.HEADER_LOCATION, "http://foo.com/base/Patient/222/_history/3") };
|
return new Header[] {new BasicHeader(Constants.HEADER_LOCATION, "http://foo.com/base/Patient/222/_history/3")};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
||||||
|
@ -1969,7 +2004,7 @@ public class GenericClientR4Test {
|
||||||
when(myHttpResponse.getAllHeaders()).thenAnswer(new Answer<Header[]>() {
|
when(myHttpResponse.getAllHeaders()).thenAnswer(new Answer<Header[]>() {
|
||||||
@Override
|
@Override
|
||||||
public Header[] answer(InvocationOnMock theInvocation) {
|
public Header[] answer(InvocationOnMock theInvocation) {
|
||||||
return new Header[] { new BasicHeader(Constants.HEADER_LOCATION, "http://foo.com/base/Patient/222/_history/3") };
|
return new Header[] {new BasicHeader(Constants.HEADER_LOCATION, "http://foo.com/base/Patient/222/_history/3")};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
||||||
|
@ -2022,7 +2057,7 @@ public class GenericClientR4Test {
|
||||||
|
|
||||||
Binary bin = new Binary();
|
Binary bin = new Binary();
|
||||||
bin.setContentType("application/foo");
|
bin.setContentType("application/foo");
|
||||||
bin.setContent(new byte[] { 0, 1, 2, 3, 4 });
|
bin.setContent(new byte[] {0, 1, 2, 3, 4});
|
||||||
client.create().resource(bin).execute();
|
client.create().resource(bin).execute();
|
||||||
|
|
||||||
ourLog.info(Arrays.asList(capt.getAllValues().get(0).getAllHeaders()).toString());
|
ourLog.info(Arrays.asList(capt.getAllValues().get(0).getAllHeaders()).toString());
|
||||||
|
@ -2032,7 +2067,7 @@ public class GenericClientR4Test {
|
||||||
|
|
||||||
assertEquals("application/foo", capt.getAllValues().get(0).getHeaders("Content-Type")[0].getValue());
|
assertEquals("application/foo", capt.getAllValues().get(0).getHeaders("Content-Type")[0].getValue());
|
||||||
assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_NON_LEGACY, capt.getAllValues().get(0).getHeaders("Accept")[0].getValue());
|
assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_NON_LEGACY, capt.getAllValues().get(0).getHeaders("Accept")[0].getValue());
|
||||||
assertArrayEquals(new byte[] { 0, 1, 2, 3, 4 }, extractBodyAsByteArray(capt));
|
assertArrayEquals(new byte[] {0, 1, 2, 3, 4}, extractBodyAsByteArray(capt));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2210,4 +2245,5 @@ public class GenericClientR4Test {
|
||||||
ourCtx = FhirContext.forR4();
|
ourCtx = FhirContext.forR4();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -230,12 +230,14 @@ public class CreateR4Test {
|
||||||
ourPort = PortUtil.findFreePort();
|
ourPort = PortUtil.findFreePort();
|
||||||
ourServer = new Server(ourPort);
|
ourServer = new Server(ourPort);
|
||||||
|
|
||||||
PatientProvider patientProvider = new PatientProvider();
|
PatientProviderCreate patientProviderCreate = new PatientProviderCreate();
|
||||||
|
PatientProviderRead patientProviderRead = new PatientProviderRead();
|
||||||
|
PatientProviderSearch patientProviderSearch = new PatientProviderSearch();
|
||||||
|
|
||||||
ServletHandler proxyHandler = new ServletHandler();
|
ServletHandler proxyHandler = new ServletHandler();
|
||||||
RestfulServer servlet = new RestfulServer(ourCtx);
|
RestfulServer servlet = new RestfulServer(ourCtx);
|
||||||
|
|
||||||
servlet.setResourceProviders(patientProvider);
|
servlet.setResourceProviders(patientProviderCreate, patientProviderRead, patientProviderSearch);
|
||||||
ServletHolder servletHolder = new ServletHolder(servlet);
|
ServletHolder servletHolder = new ServletHolder(servlet);
|
||||||
proxyHandler.addServletWithMapping(servletHolder, "/*");
|
proxyHandler.addServletWithMapping(servletHolder, "/*");
|
||||||
ourServer.setHandler(proxyHandler);
|
ourServer.setHandler(proxyHandler);
|
||||||
|
@ -247,19 +249,7 @@ public class CreateR4Test {
|
||||||
ourClient = builder.build();
|
ourClient = builder.build();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
public static class PatientProviderRead implements IResourceProvider {
|
||||||
public static class PatientProvider implements IResourceProvider {
|
|
||||||
|
|
||||||
@Create()
|
|
||||||
public MethodOutcome create(@ResourceParam Patient theIdParam) {
|
|
||||||
assertNull(theIdParam.getIdElement().getIdPart());
|
|
||||||
return new MethodOutcome(new IdType("Patient", "1"), true).setOperationOutcome(ourReturnOo);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class<Patient> getResourceType() {
|
|
||||||
return Patient.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Read()
|
@Read()
|
||||||
public MyPatientWithExtensions read(@IdParam IdType theIdParam) {
|
public MyPatientWithExtensions read(@IdParam IdType theIdParam) {
|
||||||
|
@ -269,6 +259,33 @@ public class CreateR4Test {
|
||||||
return p0;
|
return p0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<Patient> getResourceType() {
|
||||||
|
return Patient.class;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PatientProviderCreate implements IResourceProvider {
|
||||||
|
@Override
|
||||||
|
public Class<Patient> getResourceType() {
|
||||||
|
return Patient.class;
|
||||||
|
}
|
||||||
|
@Create()
|
||||||
|
public MethodOutcome create(@ResourceParam Patient theIdParam) {
|
||||||
|
assertNull(theIdParam.getIdElement().getIdPart());
|
||||||
|
return new MethodOutcome(new IdType("Patient", "1"), true).setOperationOutcome(ourReturnOo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PatientProviderSearch implements IResourceProvider {
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<Patient> getResourceType() {
|
||||||
|
return Patient.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Search
|
@Search
|
||||||
public List<IBaseResource> search() {
|
public List<IBaseResource> search() {
|
||||||
ArrayList<IBaseResource> retVal = new ArrayList<IBaseResource>();
|
ArrayList<IBaseResource> retVal = new ArrayList<IBaseResource>();
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
package ca.uhn.fhir.rest.server;
|
package ca.uhn.fhir.rest.server;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.rest.annotation.*;
|
||||||
import java.util.ArrayList;
|
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||||
import java.util.List;
|
import ca.uhn.fhir.rest.param.ParamPrefixEnum;
|
||||||
import java.util.concurrent.TimeUnit;
|
import ca.uhn.fhir.util.PortUtil;
|
||||||
|
import ca.uhn.fhir.util.TestUtil;
|
||||||
|
import com.google.common.base.Charsets;
|
||||||
import org.apache.commons.io.IOUtils;
|
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.client.methods.HttpGet;
|
||||||
import org.apache.http.impl.client.CloseableHttpClient;
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
import org.apache.http.impl.client.HttpClientBuilder;
|
import org.apache.http.impl.client.HttpClientBuilder;
|
||||||
|
@ -15,65 +16,53 @@ import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||||
import org.eclipse.jetty.server.Server;
|
import org.eclipse.jetty.server.Server;
|
||||||
import org.eclipse.jetty.servlet.ServletHandler;
|
import org.eclipse.jetty.servlet.ServletHandler;
|
||||||
import org.eclipse.jetty.servlet.ServletHolder;
|
import org.eclipse.jetty.servlet.ServletHolder;
|
||||||
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
|
import org.hl7.fhir.r4.model.*;
|
||||||
import org.junit.AfterClass;
|
import org.junit.AfterClass;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import java.util.ArrayList;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
import java.util.Date;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
import java.util.List;
|
||||||
import ca.uhn.fhir.model.primitive.IdDt;
|
import java.util.concurrent.TimeUnit;
|
||||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
|
||||||
import ca.uhn.fhir.rest.annotation.At;
|
|
||||||
import ca.uhn.fhir.rest.annotation.History;
|
|
||||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
|
||||||
import ca.uhn.fhir.rest.annotation.Read;
|
|
||||||
import ca.uhn.fhir.rest.annotation.Since;
|
|
||||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
|
||||||
import ca.uhn.fhir.rest.param.ParamPrefixEnum;
|
|
||||||
import ca.uhn.fhir.util.PortUtil;
|
|
||||||
import ca.uhn.fhir.util.TestUtil;
|
|
||||||
|
|
||||||
public class HistoryDstu2Test {
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
public class HistoryR4Test {
|
||||||
|
|
||||||
|
private static final Logger ourLog = LoggerFactory.getLogger(HistoryR4Test.class);
|
||||||
private static CloseableHttpClient ourClient;
|
private static CloseableHttpClient ourClient;
|
||||||
private static FhirContext ourCtx = FhirContext.forDstu2();
|
private static FhirContext ourCtx = FhirContext.forR4();
|
||||||
private static DateRangeParam ourLastAt;
|
private static DateRangeParam ourLastAt;
|
||||||
|
private static InstantType ourLastSince;
|
||||||
private static InstantDt ourLastSince;
|
private static IPrimitiveType<Date> ourLastSince2;
|
||||||
|
private static IPrimitiveType<String> ourLastSince3;
|
||||||
|
private static IPrimitiveType<?> ourLastSince4;
|
||||||
private static int ourPort;
|
private static int ourPort;
|
||||||
|
|
||||||
private static Server ourServer;
|
private static Server ourServer;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void before() {
|
public void before() {
|
||||||
ourLastAt = null;
|
ourLastAt = null;
|
||||||
ourLastSince = null;
|
ourLastSince = null;
|
||||||
}
|
ourLastSince2 = null;
|
||||||
|
ourLastSince3 = null;
|
||||||
@Test
|
ourLastSince4 = null;
|
||||||
public void testSince() throws Exception {
|
|
||||||
{
|
|
||||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/_history?_since=2005");
|
|
||||||
HttpResponse status = ourClient.execute(httpGet);
|
|
||||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
|
||||||
|
|
||||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
|
||||||
|
|
||||||
assertEquals(null, ourLastAt);
|
|
||||||
assertEquals("2005", ourLastSince.getValueAsString());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAt() throws Exception {
|
public void testAt() throws Exception {
|
||||||
{
|
{
|
||||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/_history?_at=gt2001&_at=lt2005");
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/_history?_at=gt2001&_at=lt2005");
|
||||||
HttpResponse status = ourClient.execute(httpGet);
|
try (CloseableHttpResponse status = ourClient.execute(httpGet)) {
|
||||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
String responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8);
|
||||||
|
ourLog.info(responseContent);
|
||||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
assertEquals(ParamPrefixEnum.GREATERTHAN, ourLastAt.getLowerBound().getPrefix());
|
assertEquals(ParamPrefixEnum.GREATERTHAN, ourLastAt.getLowerBound().getPrefix());
|
||||||
assertEquals("2001", ourLastAt.getLowerBound().getValueAsString());
|
assertEquals("2001", ourLastAt.getLowerBound().getValueAsString());
|
||||||
|
@ -86,56 +75,79 @@ public class HistoryDstu2Test {
|
||||||
public void testInstanceHistory() throws Exception {
|
public void testInstanceHistory() throws Exception {
|
||||||
{
|
{
|
||||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/_history?_pretty=true");
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/_history?_pretty=true");
|
||||||
HttpResponse status = ourClient.execute(httpGet);
|
String responseContent;
|
||||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
try (CloseableHttpResponse status = ourClient.execute(httpGet)) {
|
||||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8);
|
||||||
|
|
||||||
ourLog.info(responseContent);
|
ourLog.info(responseContent);
|
||||||
|
|
||||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
Bundle bundle = ourCtx.newXmlParser().parseResource(Bundle.class, responseContent);
|
Bundle bundle = ourCtx.newXmlParser().parseResource(Bundle.class, responseContent);
|
||||||
assertEquals(2, bundle.getEntry().size());
|
assertEquals(2, bundle.getEntry().size());
|
||||||
assertEquals("http://localhost:" + ourPort + "/Patient/ih1/_history/1", bundle.getEntry().get(0).getResource().getId().getValue());
|
assertEquals("http://localhost:" + ourPort + "/Patient/ih1/_history/1", bundle.getEntry().get(0).getResource().getId());
|
||||||
assertEquals("http://localhost:" + ourPort + "/Patient/ih1/_history/2", bundle.getEntry().get(1).getResource().getId().getValue());
|
assertEquals("http://localhost:" + ourPort + "/Patient/ih1/_history/2", bundle.getEntry().get(1).getResource().getId());
|
||||||
|
|
||||||
}
|
}
|
||||||
}private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(HistoryDstu2Test.class);
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testServerHistory() throws Exception {
|
public void testServerHistory() throws Exception {
|
||||||
{
|
{
|
||||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/_history");
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/_history");
|
||||||
HttpResponse status = ourClient.execute(httpGet);
|
String responseContent;
|
||||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
try (CloseableHttpResponse status = ourClient.execute(httpGet)) {
|
||||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8);
|
||||||
|
ourLog.info(responseContent);
|
||||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
Bundle bundle = ourCtx.newXmlParser().parseResource(Bundle.class, responseContent);
|
Bundle bundle = ourCtx.newXmlParser().parseResource(Bundle.class, responseContent);
|
||||||
assertEquals(2, bundle.getEntry().size());
|
assertEquals(2, bundle.getEntry().size());
|
||||||
assertEquals("http://localhost:" + ourPort + "/Patient/h1/_history/1", bundle.getEntry().get(0).getResource().getId().getValue());
|
assertEquals("http://localhost:" + ourPort + "/Patient/h1/_history/1", bundle.getEntry().get(0).getResource().getId());
|
||||||
assertEquals("http://localhost:" + ourPort + "/Patient/h1/_history/2", bundle.getEntry().get(1).getResource().getId().getValue());
|
assertEquals("http://localhost:" + ourPort + "/Patient/h1/_history/2", bundle.getEntry().get(1).getResource().getId());
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSince() throws Exception {
|
||||||
|
{
|
||||||
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/_history?_since=2005");
|
||||||
|
String responseContent;
|
||||||
|
try (CloseableHttpResponse status = ourClient.execute(httpGet)) {
|
||||||
|
responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8);
|
||||||
|
ourLog.info(responseContent);
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(null, ourLastAt);
|
||||||
|
assertEquals("2005", ourLastSince.getValueAsString());
|
||||||
|
assertEquals("2005", ourLastSince2.getValueAsString());
|
||||||
|
assertTrue(DateTimeType.class.equals(ourLastSince2.getClass()));
|
||||||
|
assertEquals("2005", ourLastSince3.getValueAsString());
|
||||||
|
assertTrue(StringType.class.equals(ourLastSince3.getClass()));
|
||||||
|
assertEquals("2005", ourLastSince4.getValueAsString());
|
||||||
|
assertTrue(StringType.class.equals(ourLastSince4.getClass()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTypeHistory() throws Exception {
|
public void testTypeHistory() throws Exception {
|
||||||
{
|
{
|
||||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/_history");
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/_history");
|
||||||
HttpResponse status = ourClient.execute(httpGet);
|
String responseContent;
|
||||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
try (CloseableHttpResponse status = ourClient.execute(httpGet)) {
|
||||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8);
|
||||||
|
ourLog.info(responseContent);
|
||||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
assertNull(ourLastAt);
|
assertNull(ourLastAt);
|
||||||
|
|
||||||
Bundle bundle = ourCtx.newXmlParser().parseResource(Bundle.class, responseContent);
|
Bundle bundle = ourCtx.newXmlParser().parseResource(Bundle.class, responseContent);
|
||||||
assertEquals(2, bundle.getEntry().size());
|
assertEquals(2, bundle.getEntry().size());
|
||||||
assertEquals("http://localhost:" + ourPort + "/Patient/th1/_history/1", bundle.getEntry().get(0).getResource().getId().getValue());
|
assertEquals("http://localhost:" + ourPort + "/Patient/th1/_history/1", bundle.getEntry().get(0).getResource().getId());
|
||||||
assertEquals("http://localhost:" + ourPort + "/Patient/th1/_history/2", bundle.getEntry().get(1).getResource().getId().getValue());
|
assertEquals("http://localhost:" + ourPort + "/Patient/th1/_history/2", bundle.getEntry().get(1).getResource().getId());
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -147,14 +159,15 @@ public class HistoryDstu2Test {
|
||||||
public void testVread() throws Exception {
|
public void testVread() throws Exception {
|
||||||
{
|
{
|
||||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/_history/456");
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/_history/456");
|
||||||
HttpResponse status = ourClient.execute(httpGet);
|
String responseContent;
|
||||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
try (CloseableHttpResponse status = ourClient.execute(httpGet)) {
|
||||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8);
|
||||||
|
ourLog.info(responseContent);
|
||||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
Patient bundle = ourCtx.newXmlParser().parseResource(Patient.class, responseContent);
|
Patient bundle = ourCtx.newXmlParser().parseResource(Patient.class, responseContent);
|
||||||
assertEquals("vread", bundle.getNameFirstRep().getFamilyFirstRep().getValue());
|
assertEquals("vread", bundle.getNameFirstRep().getFamily());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,20 +204,27 @@ public class HistoryDstu2Test {
|
||||||
public static class DummyPlainProvider {
|
public static class DummyPlainProvider {
|
||||||
|
|
||||||
@History
|
@History
|
||||||
public List<Patient> history(@Since InstantDt theSince, @At DateRangeParam theAt) {
|
public List<Patient> history(@Since InstantType theSince,
|
||||||
|
@Since IPrimitiveType<Date> theSince2,
|
||||||
|
@Since IPrimitiveType<String> theSince3,
|
||||||
|
@Since IPrimitiveType theSince4,
|
||||||
|
@At DateRangeParam theAt) {
|
||||||
ourLastAt = theAt;
|
ourLastAt = theAt;
|
||||||
ourLastSince = theSince;
|
ourLastSince = theSince;
|
||||||
|
ourLastSince2 = theSince2;
|
||||||
|
ourLastSince3 = theSince3;
|
||||||
|
ourLastSince4 = theSince4;
|
||||||
|
|
||||||
ArrayList<Patient> retVal = new ArrayList<Patient>();
|
ArrayList<Patient> retVal = new ArrayList<>();
|
||||||
|
|
||||||
Patient patient = new Patient();
|
Patient patient = new Patient();
|
||||||
patient.setId("Patient/h1/_history/1");
|
patient.setId("Patient/h1/_history/1");
|
||||||
patient.addName().addFamily("history");
|
patient.addName().setFamily("history");
|
||||||
retVal.add(patient);
|
retVal.add(patient);
|
||||||
|
|
||||||
Patient patient2 = new Patient();
|
Patient patient2 = new Patient();
|
||||||
patient2.setId("Patient/h1/_history/2");
|
patient2.setId("Patient/h1/_history/2");
|
||||||
patient2.addName().addFamily("history");
|
patient2.addName().setFamily("history");
|
||||||
retVal.add(patient2);
|
retVal.add(patient2);
|
||||||
|
|
||||||
return retVal;
|
return retVal;
|
||||||
|
@ -220,17 +240,17 @@ public class HistoryDstu2Test {
|
||||||
}
|
}
|
||||||
|
|
||||||
@History
|
@History
|
||||||
public List<Patient> instanceHistory(@IdParam IdDt theId) {
|
public List<Patient> instanceHistory(@IdParam IdType theId) {
|
||||||
ArrayList<Patient> retVal = new ArrayList<Patient>();
|
ArrayList<Patient> retVal = new ArrayList<>();
|
||||||
|
|
||||||
Patient patient = new Patient();
|
Patient patient = new Patient();
|
||||||
patient.setId("Patient/ih1/_history/1");
|
patient.setId("Patient/ih1/_history/1");
|
||||||
patient.addName().addFamily("history");
|
patient.addName().setFamily("history");
|
||||||
retVal.add(patient);
|
retVal.add(patient);
|
||||||
|
|
||||||
Patient patient2 = new Patient();
|
Patient patient2 = new Patient();
|
||||||
patient2.setId("Patient/ih1/_history/2");
|
patient2.setId("Patient/ih1/_history/2");
|
||||||
patient2.addName().addFamily("history");
|
patient2.addName().setFamily("history");
|
||||||
retVal.add(patient2);
|
retVal.add(patient2);
|
||||||
|
|
||||||
return retVal;
|
return retVal;
|
||||||
|
@ -238,25 +258,25 @@ public class HistoryDstu2Test {
|
||||||
|
|
||||||
@History
|
@History
|
||||||
public List<Patient> typeHistory() {
|
public List<Patient> typeHistory() {
|
||||||
ArrayList<Patient> retVal = new ArrayList<Patient>();
|
ArrayList<Patient> retVal = new ArrayList<>();
|
||||||
|
|
||||||
Patient patient = new Patient();
|
Patient patient = new Patient();
|
||||||
patient.setId("Patient/th1/_history/1");
|
patient.setId("Patient/th1/_history/1");
|
||||||
patient.addName().addFamily("history");
|
patient.addName().setFamily("history");
|
||||||
retVal.add(patient);
|
retVal.add(patient);
|
||||||
|
|
||||||
Patient patient2 = new Patient();
|
Patient patient2 = new Patient();
|
||||||
patient2.setId("Patient/th1/_history/2");
|
patient2.setId("Patient/th1/_history/2");
|
||||||
patient2.addName().addFamily("history");
|
patient2.addName().setFamily("history");
|
||||||
retVal.add(patient2);
|
retVal.add(patient2);
|
||||||
|
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Read(version = true)
|
@Read(version = true)
|
||||||
public Patient vread(@IdParam IdDt theId) {
|
public Patient vread(@IdParam IdType theId) {
|
||||||
Patient retVal = new Patient();
|
Patient retVal = new Patient();
|
||||||
retVal.addName().addFamily("vread");
|
retVal.addName().setFamily("vread");
|
||||||
retVal.setId(theId);
|
retVal.setId(theId);
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
|
@ -2,6 +2,7 @@ package ca.uhn.fhir.rest.server.provider;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||||
|
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
||||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
|
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
|
||||||
|
@ -19,17 +20,19 @@ import org.junit.AfterClass;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
import static org.hamcrest.Matchers.*;
|
||||||
import static org.hamcrest.Matchers.matchesPattern;
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
public class HashMapResourceProviderTest {
|
public class HashMapResourceProviderTest {
|
||||||
|
|
||||||
|
private static final Logger ourLog = LoggerFactory.getLogger(HashMapResourceProviderTest.class);
|
||||||
private static MyRestfulServer ourRestServer;
|
private static MyRestfulServer ourRestServer;
|
||||||
private static Server ourListenerServer;
|
private static Server ourListenerServer;
|
||||||
private static IGenericClient ourClient;
|
private static IGenericClient ourClient;
|
||||||
|
@ -100,6 +103,93 @@ public class HashMapResourceProviderTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHistoryInstance() {
|
||||||
|
// Create Res 1
|
||||||
|
Patient p = new Patient();
|
||||||
|
p.setActive(true);
|
||||||
|
IIdType id1 = ourClient.create().resource(p).execute().getId();
|
||||||
|
assertThat(id1.getIdPart(), matchesPattern("[0-9]+"));
|
||||||
|
assertEquals("1", id1.getVersionIdPart());
|
||||||
|
|
||||||
|
// Create Res 2
|
||||||
|
p = new Patient();
|
||||||
|
p.setActive(true);
|
||||||
|
IIdType id2 = ourClient.create().resource(p).execute().getId();
|
||||||
|
assertThat(id2.getIdPart(), matchesPattern("[0-9]+"));
|
||||||
|
assertEquals("1", id2.getVersionIdPart());
|
||||||
|
|
||||||
|
// Update Res 2
|
||||||
|
p = new Patient();
|
||||||
|
p.setId(id2);
|
||||||
|
p.setActive(false);
|
||||||
|
id2 = ourClient.update().resource(p).execute().getId();
|
||||||
|
assertThat(id2.getIdPart(), matchesPattern("[0-9]+"));
|
||||||
|
assertEquals("2", id2.getVersionIdPart());
|
||||||
|
|
||||||
|
Bundle history = ourClient
|
||||||
|
.history()
|
||||||
|
.onInstance(id2.toUnqualifiedVersionless())
|
||||||
|
.andReturnBundle(Bundle.class)
|
||||||
|
.encodedJson()
|
||||||
|
.prettyPrint()
|
||||||
|
.execute();
|
||||||
|
ourLog.debug(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(history));
|
||||||
|
List<String> ids = history
|
||||||
|
.getEntry()
|
||||||
|
.stream()
|
||||||
|
.map(t -> t.getResource().getIdElement().toUnqualified().getValue())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
assertThat(ids, contains(
|
||||||
|
id2.toUnqualified().withVersion("2").getValue(),
|
||||||
|
id2.toUnqualified().withVersion("1").getValue()
|
||||||
|
));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHistoryType() {
|
||||||
|
// Create Res 1
|
||||||
|
Patient p = new Patient();
|
||||||
|
p.setActive(true);
|
||||||
|
IIdType id1 = ourClient.create().resource(p).execute().getId();
|
||||||
|
assertThat(id1.getIdPart(), matchesPattern("[0-9]+"));
|
||||||
|
assertEquals("1", id1.getVersionIdPart());
|
||||||
|
|
||||||
|
// Create Res 2
|
||||||
|
p = new Patient();
|
||||||
|
p.setActive(true);
|
||||||
|
IIdType id2 = ourClient.create().resource(p).execute().getId();
|
||||||
|
assertThat(id2.getIdPart(), matchesPattern("[0-9]+"));
|
||||||
|
assertEquals("1", id2.getVersionIdPart());
|
||||||
|
|
||||||
|
// Update Res 2
|
||||||
|
p = new Patient();
|
||||||
|
p.setId(id2);
|
||||||
|
p.setActive(false);
|
||||||
|
id2 = ourClient.update().resource(p).execute().getId();
|
||||||
|
assertThat(id2.getIdPart(), matchesPattern("[0-9]+"));
|
||||||
|
assertEquals("2", id2.getVersionIdPart());
|
||||||
|
|
||||||
|
Bundle history = ourClient
|
||||||
|
.history()
|
||||||
|
.onType(Patient.class)
|
||||||
|
.andReturnBundle(Bundle.class)
|
||||||
|
.execute();
|
||||||
|
List<String> ids = history
|
||||||
|
.getEntry()
|
||||||
|
.stream()
|
||||||
|
.map(t -> t.getResource().getIdElement().toUnqualified().getValue())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
ourLog.info("Received IDs: {}", ids);
|
||||||
|
assertThat(ids, contains(
|
||||||
|
id2.toUnqualified().withVersion("2").getValue(),
|
||||||
|
id2.toUnqualified().withVersion("1").getValue(),
|
||||||
|
id1.toUnqualified().withVersion("1").getValue()
|
||||||
|
));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSearchAll() {
|
public void testSearchAll() {
|
||||||
// Create
|
// Create
|
||||||
|
|
|
@ -86,7 +86,7 @@
|
||||||
Resource loading logic for the JPA server has been optimized to
|
Resource loading logic for the JPA server has been optimized to
|
||||||
reduce the number of database round trips required when loading
|
reduce the number of database round trips required when loading
|
||||||
search results where many of the entries have a "forced ID" (an alphanumeric
|
search results where many of the entries have a "forced ID" (an alphanumeric
|
||||||
client-assigned reosurce ID). Thanks to Frank Tao for the pull
|
client-assigned resource ID). Thanks to Frank Tao for the pull
|
||||||
request!
|
request!
|
||||||
</action>
|
</action>
|
||||||
<action type="add">
|
<action type="add">
|
||||||
|
@ -150,6 +150,39 @@
|
||||||
in order to prevent HTML injection attacks via maliciously
|
in order to prevent HTML injection attacks via maliciously
|
||||||
crafted URLs.
|
crafted URLs.
|
||||||
</action>
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
The REST Server module now allows more than one Resource Provider
|
||||||
|
(i.e. more than one implementation of IResourceProvider) to be registered
|
||||||
|
to the RestfulServer for the same resource type. Previous versions of
|
||||||
|
HAPI FHIR have always limited support to a single resource provider, but
|
||||||
|
this limitation did not serve any purpose so it has been removed.
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
The HashMapResourceProvider now supports the type and
|
||||||
|
instance history operations.
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Fixed a bug when creating a custom search parameter in the JPA
|
||||||
|
server: if the SearchParameter resource contained an invalid
|
||||||
|
expression, create/update operations for the given resource would
|
||||||
|
fail with a cryptic error. SearchParameter expressions are now
|
||||||
|
validated upon storage, and the SearchParameter will be rejected
|
||||||
|
if the expression can not be processed.
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
The generic client history operations (history-instance, history-type,
|
||||||
|
and history-server) now support the
|
||||||
|
<![CDATA[<code>_at</code>]]> parameter.
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
In the plain server, many resource provider method parameters may now
|
||||||
|
use a generic
|
||||||
|
<![CDATA[<code>IPrimitiveType<String></code>]]>
|
||||||
|
or
|
||||||
|
<![CDATA[<code>IPrimitiveType<Date></code>]]> at the
|
||||||
|
parameter type. This is handy if you are trying to write code
|
||||||
|
that works across versions of FHIR.
|
||||||
|
</action>
|
||||||
</release>
|
</release>
|
||||||
<release version="3.4.0" date="2018-05-28">
|
<release version="3.4.0" date="2018-05-28">
|
||||||
<action type="add">
|
<action type="add">
|
||||||
|
|
Loading…
Reference in New Issue