Fix #371 - STU3 server and client should use new sort parameter style
This commit is contained in:
parent
f4b9c6423c
commit
d966190f9e
|
@ -34,6 +34,7 @@ public class SortSpec {
|
||||||
* Constructor
|
* Constructor
|
||||||
*/
|
*/
|
||||||
public SortSpec() {
|
public SortSpec() {
|
||||||
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -70,6 +70,8 @@ import ca.uhn.fhir.parser.DataFormatException;
|
||||||
import ca.uhn.fhir.parser.IParser;
|
import ca.uhn.fhir.parser.IParser;
|
||||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
import ca.uhn.fhir.rest.api.PreferReturnEnum;
|
import ca.uhn.fhir.rest.api.PreferReturnEnum;
|
||||||
|
import ca.uhn.fhir.rest.api.SortOrderEnum;
|
||||||
|
import ca.uhn.fhir.rest.api.SortSpec;
|
||||||
import ca.uhn.fhir.rest.api.SummaryEnum;
|
import ca.uhn.fhir.rest.api.SummaryEnum;
|
||||||
import ca.uhn.fhir.rest.client.api.IHttpClient;
|
import ca.uhn.fhir.rest.client.api.IHttpClient;
|
||||||
import ca.uhn.fhir.rest.client.api.IHttpRequest;
|
import ca.uhn.fhir.rest.client.api.IHttpRequest;
|
||||||
|
@ -131,6 +133,7 @@ import ca.uhn.fhir.rest.method.OperationMethodBinding;
|
||||||
import ca.uhn.fhir.rest.method.ReadMethodBinding;
|
import ca.uhn.fhir.rest.method.ReadMethodBinding;
|
||||||
import ca.uhn.fhir.rest.method.SearchMethodBinding;
|
import ca.uhn.fhir.rest.method.SearchMethodBinding;
|
||||||
import ca.uhn.fhir.rest.method.SearchStyleEnum;
|
import ca.uhn.fhir.rest.method.SearchStyleEnum;
|
||||||
|
import ca.uhn.fhir.rest.method.SortParameter;
|
||||||
import ca.uhn.fhir.rest.method.TransactionMethodBinding;
|
import ca.uhn.fhir.rest.method.TransactionMethodBinding;
|
||||||
import ca.uhn.fhir.rest.method.ValidateMethodBindingDstu1;
|
import ca.uhn.fhir.rest.method.ValidateMethodBindingDstu1;
|
||||||
import ca.uhn.fhir.rest.method.ValidateMethodBindingDstu2;
|
import ca.uhn.fhir.rest.method.ValidateMethodBindingDstu2;
|
||||||
|
@ -233,8 +236,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
return delete(theType, new IdDt(theId));
|
return delete(theType, new IdDt(theId));
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T extends IBaseResource> T doReadOrVRead(final Class<T> theType, IIdType theId, boolean theVRead, ICallable<T> theNotModifiedHandler, String theIfVersionMatches, Boolean thePrettyPrint,
|
private <T extends IBaseResource> T doReadOrVRead(final Class<T> theType, IIdType theId, boolean theVRead, ICallable<T> theNotModifiedHandler, String theIfVersionMatches, Boolean thePrettyPrint, SummaryEnum theSummary, EncodingEnum theEncoding, Set<String> theSubsetElements) {
|
||||||
SummaryEnum theSummary, EncodingEnum theEncoding, Set<String> theSubsetElements) {
|
|
||||||
String resName = toResourceName(theType);
|
String resName = toResourceName(theType);
|
||||||
IIdType id = theId;
|
IIdType id = theId;
|
||||||
if (!id.hasBaseUrl()) {
|
if (!id.hasBaseUrl()) {
|
||||||
|
@ -264,7 +266,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean allowHtmlResponse = (theSummary == SummaryEnum.TEXT) || (theSummary == null && getSummary() == SummaryEnum.TEXT);
|
boolean allowHtmlResponse = (theSummary == SummaryEnum.TEXT) || (theSummary == null && getSummary() == SummaryEnum.TEXT);
|
||||||
ResourceResponseHandler<T> binding = new ResourceResponseHandler<T>(theType, (Class<? extends IBaseResource>)null, id, allowHtmlResponse);
|
ResourceResponseHandler<T> binding = new ResourceResponseHandler<T>(theType, (Class<? extends IBaseResource>) null, id, allowHtmlResponse);
|
||||||
|
|
||||||
if (theNotModifiedHandler == null) {
|
if (theNotModifiedHandler == null) {
|
||||||
return invokeClient(myContext, binding, invocation, theEncoding, thePrettyPrint, myLogRequestAndResponse, theSummary, theSubsetElements);
|
return invokeClient(myContext, binding, invocation, theEncoding, thePrettyPrint, myLogRequestAndResponse, theSummary, theSubsetElements);
|
||||||
|
@ -596,44 +598,48 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String validateAndEscapeConditionalUrl(String theSearchUrl) {
|
||||||
|
Validate.notBlank(theSearchUrl, "Conditional URL can not be blank/null");
|
||||||
|
StringBuilder b = new StringBuilder();
|
||||||
|
boolean haveHadQuestionMark = false;
|
||||||
|
for (int i = 0; i < theSearchUrl.length(); i++) {
|
||||||
|
char nextChar = theSearchUrl.charAt(i);
|
||||||
|
if (!haveHadQuestionMark) {
|
||||||
|
if (nextChar == '?') {
|
||||||
|
haveHadQuestionMark = true;
|
||||||
|
} else if (!Character.isLetter(nextChar)) {
|
||||||
|
throw new IllegalArgumentException("Conditional URL must be in the format \"[ResourceType]?[Params]\" and must not have a base URL - Found: " + theSearchUrl);
|
||||||
|
}
|
||||||
|
b.append(nextChar);
|
||||||
|
} else {
|
||||||
|
switch (nextChar) {
|
||||||
|
case '|':
|
||||||
|
case '?':
|
||||||
|
case '$':
|
||||||
|
case ':':
|
||||||
|
b.append(UrlUtil.escape(Character.toString(nextChar)));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
b.append(nextChar);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return b.toString();
|
||||||
|
}
|
||||||
|
|
||||||
private abstract class BaseClientExecutable<T extends IClientExecutable<?, ?>, Y> implements IClientExecutable<T, Y> {
|
private abstract class BaseClientExecutable<T extends IClientExecutable<?, ?>, Y> implements IClientExecutable<T, Y> {
|
||||||
|
|
||||||
|
protected EncodingEnum myParamEncoding;
|
||||||
|
|
||||||
private List<Class<? extends IBaseResource>> myPreferResponseTypes;
|
private List<Class<? extends IBaseResource>> myPreferResponseTypes;
|
||||||
|
|
||||||
public List<Class<? extends IBaseResource>> getPreferResponseTypes() {
|
|
||||||
return myPreferResponseTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Class<? extends IBaseResource>> getPreferResponseTypes(Class<? extends IBaseResource> theDefault) {
|
|
||||||
if (myPreferResponseTypes != null) {
|
|
||||||
return myPreferResponseTypes;
|
|
||||||
} else {
|
|
||||||
return toTypeList(theDefault);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
|
||||||
public T preferResponseType(Class<? extends IBaseResource> theClass) {
|
|
||||||
myPreferResponseTypes = null;
|
|
||||||
if (theClass != null) {
|
|
||||||
myPreferResponseTypes = new ArrayList<Class<? extends IBaseResource>>();
|
|
||||||
myPreferResponseTypes.add(theClass);
|
|
||||||
}
|
|
||||||
return (T) this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
|
||||||
public T preferResponseTypes(List<Class<? extends IBaseResource>> theClass) {
|
|
||||||
myPreferResponseTypes = theClass;
|
|
||||||
return (T) this;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected EncodingEnum myParamEncoding;
|
|
||||||
protected Boolean myPrettyPrint;
|
protected Boolean myPrettyPrint;
|
||||||
|
|
||||||
private boolean myQueryLogRequestAndResponse;
|
private boolean myQueryLogRequestAndResponse;
|
||||||
|
|
||||||
private HashSet<String> mySubsetElements;
|
private HashSet<String> mySubsetElements;
|
||||||
|
|
||||||
protected SummaryEnum mySummaryMode;
|
protected SummaryEnum mySummaryMode;
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
@ -643,6 +649,17 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
return (T) this;
|
return (T) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public T elementsSubset(String... theElements) {
|
||||||
|
if (theElements != null && theElements.length > 0) {
|
||||||
|
mySubsetElements = new HashSet<String>(Arrays.asList(theElements));
|
||||||
|
} else {
|
||||||
|
mySubsetElements = null;
|
||||||
|
}
|
||||||
|
return (T) this;
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
@Override
|
||||||
public T encodedJson() {
|
public T encodedJson() {
|
||||||
|
@ -661,6 +678,18 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
return myParamEncoding;
|
return myParamEncoding;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<Class<? extends IBaseResource>> getPreferResponseTypes() {
|
||||||
|
return myPreferResponseTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Class<? extends IBaseResource>> getPreferResponseTypes(Class<? extends IBaseResource> theDefault) {
|
||||||
|
if (myPreferResponseTypes != null) {
|
||||||
|
return myPreferResponseTypes;
|
||||||
|
} else {
|
||||||
|
return toTypeList(theDefault);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected HashSet<String> getSubsetElements() {
|
protected HashSet<String> getSubsetElements() {
|
||||||
return mySubsetElements;
|
return mySubsetElements;
|
||||||
}
|
}
|
||||||
|
@ -692,19 +721,26 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
@Override
|
||||||
public T prettyPrint() {
|
public T preferResponseType(Class<? extends IBaseResource> theClass) {
|
||||||
myPrettyPrint = true;
|
myPreferResponseTypes = null;
|
||||||
|
if (theClass != null) {
|
||||||
|
myPreferResponseTypes = new ArrayList<Class<? extends IBaseResource>>();
|
||||||
|
myPreferResponseTypes.add(theClass);
|
||||||
|
}
|
||||||
return (T) this;
|
return (T) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
@Override
|
||||||
public T elementsSubset(String... theElements) {
|
public T preferResponseTypes(List<Class<? extends IBaseResource>> theClass) {
|
||||||
if (theElements != null && theElements.length > 0) {
|
myPreferResponseTypes = theClass;
|
||||||
mySubsetElements = new HashSet<String>(Arrays.asList(theElements));
|
return (T) this;
|
||||||
} else {
|
|
||||||
mySubsetElements = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public T prettyPrint() {
|
||||||
|
myPrettyPrint = true;
|
||||||
return (T) this;
|
return (T) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -736,7 +772,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class CreateInternal extends BaseClientExecutable<ICreateTyped, MethodOutcome>implements ICreate, ICreateTyped, ICreateWithQuery, ICreateWithQueryTyped {
|
private class CreateInternal extends BaseClientExecutable<ICreateTyped, MethodOutcome> implements ICreate, ICreateTyped, ICreateWithQuery, ICreateWithQueryTyped {
|
||||||
|
|
||||||
private CriterionList myCriterionList;
|
private CriterionList myCriterionList;
|
||||||
private String myId;
|
private String myId;
|
||||||
|
@ -856,7 +892,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DeleteInternal extends BaseClientExecutable<IDeleteTyped, BaseOperationOutcome>implements IDelete, IDeleteTyped, IDeleteWithQuery, IDeleteWithQueryTyped {
|
private class DeleteInternal extends BaseClientExecutable<IDeleteTyped, BaseOperationOutcome> implements IDelete, IDeleteTyped, IDeleteWithQuery, IDeleteWithQueryTyped {
|
||||||
|
|
||||||
private CriterionList myCriterionList;
|
private CriterionList myCriterionList;
|
||||||
private IIdType myId;
|
private IIdType myId;
|
||||||
|
@ -921,6 +957,14 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IDeleteWithQuery resourceConditionalByType(Class<? extends IBaseResource> theResourceType) {
|
||||||
|
Validate.notNull(theResourceType, "theResourceType can not be null");
|
||||||
|
myCriterionList = new CriterionList();
|
||||||
|
myResourceType = myContext.getResourceDefinition(theResourceType).getName();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IDeleteWithQuery resourceConditionalByType(String theResourceType) {
|
public IDeleteWithQuery resourceConditionalByType(String theResourceType) {
|
||||||
Validate.notBlank(theResourceType, "theResourceType can not be blank/null");
|
Validate.notBlank(theResourceType, "theResourceType can not be blank/null");
|
||||||
|
@ -943,14 +987,6 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
myCriterionList.add((ICriterionInternal) theCriterion);
|
myCriterionList.add((ICriterionInternal) theCriterion);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public IDeleteWithQuery resourceConditionalByType(Class<? extends IBaseResource> theResourceType) {
|
|
||||||
Validate.notNull(theResourceType, "theResourceType can not be null");
|
|
||||||
myCriterionList = new CriterionList();
|
|
||||||
myResourceType = myContext.getResourceDefinition(theResourceType).getName();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||||
|
@ -977,7 +1013,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||||
private class GetPageInternal extends BaseClientExecutable<IGetPageTyped<Object>, Object>implements IGetPageTyped<Object> {
|
private class GetPageInternal extends BaseClientExecutable<IGetPageTyped<Object>, Object> implements IGetPageTyped<Object> {
|
||||||
|
|
||||||
private Class<? extends IBaseBundle> myBundleType;
|
private Class<? extends IBaseBundle> myBundleType;
|
||||||
private String myUrl;
|
private String myUrl;
|
||||||
|
@ -1007,7 +1043,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class GetTagsInternal extends BaseClientExecutable<IGetTags, TagList>implements IGetTags {
|
private class GetTagsInternal extends BaseClientExecutable<IGetTags, TagList> implements IGetTags {
|
||||||
|
|
||||||
private String myId;
|
private String myId;
|
||||||
private String myResourceName;
|
private String myResourceName;
|
||||||
|
@ -1403,9 +1439,59 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
private IIdType myId;
|
private IIdType myId;
|
||||||
private String myOperationName;
|
private String myOperationName;
|
||||||
private IBaseParameters myParameters;
|
private IBaseParameters myParameters;
|
||||||
|
private RuntimeResourceDefinition myParametersDef;
|
||||||
private Class<? extends IBaseResource> myType;
|
private Class<? extends IBaseResource> myType;
|
||||||
private boolean myUseHttpGet;
|
private boolean myUseHttpGet;
|
||||||
private RuntimeResourceDefinition myParametersDef;
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private void addParam(String theName, IBase theValue) {
|
||||||
|
BaseRuntimeChildDefinition parameterChild = myParametersDef.getChildByName("parameter");
|
||||||
|
BaseRuntimeElementCompositeDefinition<?> parameterElem = (BaseRuntimeElementCompositeDefinition<?>) parameterChild.getChildByName("parameter");
|
||||||
|
|
||||||
|
IBase parameter = parameterElem.newInstance();
|
||||||
|
parameterChild.getMutator().addValue(myParameters, parameter);
|
||||||
|
|
||||||
|
IPrimitiveType<String> name = (IPrimitiveType<String>) myContext.getElementDefinition("string").newInstance();
|
||||||
|
name.setValue(theName);
|
||||||
|
parameterElem.getChildByName("name").getMutator().setValue(parameter, name);
|
||||||
|
|
||||||
|
if (theValue instanceof IBaseDatatype) {
|
||||||
|
BaseRuntimeElementDefinition<?> datatypeDef = myContext.getElementDefinition(theValue.getClass());
|
||||||
|
if (datatypeDef instanceof IRuntimeDatatypeDefinition) {
|
||||||
|
Class<? extends IBaseDatatype> profileOf = ((IRuntimeDatatypeDefinition) datatypeDef).getProfileOf();
|
||||||
|
if (profileOf != null) {
|
||||||
|
datatypeDef = myContext.getElementDefinition(profileOf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String childElementName = "value" + StringUtils.capitalize(datatypeDef.getName());
|
||||||
|
BaseRuntimeChildDefinition childByName = parameterElem.getChildByName(childElementName);
|
||||||
|
childByName.getMutator().setValue(parameter, theValue);
|
||||||
|
} else if (theValue instanceof IBaseResource) {
|
||||||
|
parameterElem.getChildByName("resource").getMutator().setValue(parameter, theValue);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Don't know how to handle parameter of type " + theValue.getClass());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addParam(String theName, IQueryParameterType theValue) {
|
||||||
|
IPrimitiveType<?> stringType = ParametersUtil.createString(myContext, theValue.getValueAsQueryToken(myContext));
|
||||||
|
addParam(theName, stringType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IOperationUntypedWithInputAndPartialOutput andParameter(String theName, IBase theValue) {
|
||||||
|
Validate.notEmpty(theName, "theName must not be null");
|
||||||
|
Validate.notNull(theValue, "theValue must not be null");
|
||||||
|
addParam(theName, theValue);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IOperationUntypedWithInputAndPartialOutput andSearchParameter(String theName, IQueryParameterType theValue) {
|
||||||
|
addParam(theName, theValue);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
@Override
|
||||||
|
@ -1486,21 +1572,12 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
throw new IllegalArgumentException("theOutputParameterType must refer to a HAPI FHIR Resource type: " + theOutputParameterType.getName());
|
throw new IllegalArgumentException("theOutputParameterType must refer to a HAPI FHIR Resource type: " + theOutputParameterType.getName());
|
||||||
}
|
}
|
||||||
if (!"Parameters".equals(def.getName())) {
|
if (!"Parameters".equals(def.getName())) {
|
||||||
throw new IllegalArgumentException("theOutputParameterType must refer to a HAPI FHIR Resource type for a resource named " + "Parameters" + " - " + theOutputParameterType.getName()
|
throw new IllegalArgumentException("theOutputParameterType must refer to a HAPI FHIR Resource type for a resource named " + "Parameters" + " - " + theOutputParameterType.getName() + " is a resource named: " + def.getName());
|
||||||
+ " is a resource named: " + def.getName());
|
|
||||||
}
|
}
|
||||||
myParameters = (IBaseParameters) def.newInstance();
|
myParameters = (IBaseParameters) def.newInstance();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings({ "unchecked" })
|
|
||||||
@Override
|
|
||||||
public IOperationUntypedWithInput withParameters(IBaseParameters theParameters) {
|
|
||||||
Validate.notNull(theParameters, "theParameters can not be null");
|
|
||||||
myParameters = theParameters;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
@Override
|
||||||
public <T extends IBaseParameters> IOperationUntypedWithInputAndPartialOutput<T> withParameter(Class<T> theParameterType, String theName, IBase theValue) {
|
public <T extends IBaseParameters> IOperationUntypedWithInputAndPartialOutput<T> withParameter(Class<T> theParameterType, String theName, IBase theValue) {
|
||||||
|
@ -1516,48 +1593,11 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings({ "unchecked" })
|
||||||
private void addParam(String theName, IBase theValue) {
|
|
||||||
BaseRuntimeChildDefinition parameterChild = myParametersDef.getChildByName("parameter");
|
|
||||||
BaseRuntimeElementCompositeDefinition<?> parameterElem = (BaseRuntimeElementCompositeDefinition<?>) parameterChild.getChildByName("parameter");
|
|
||||||
|
|
||||||
IBase parameter = parameterElem.newInstance();
|
|
||||||
parameterChild.getMutator().addValue(myParameters, parameter);
|
|
||||||
|
|
||||||
IPrimitiveType<String> name = (IPrimitiveType<String>) myContext.getElementDefinition("string").newInstance();
|
|
||||||
name.setValue(theName);
|
|
||||||
parameterElem.getChildByName("name").getMutator().setValue(parameter, name);
|
|
||||||
|
|
||||||
if (theValue instanceof IBaseDatatype) {
|
|
||||||
BaseRuntimeElementDefinition<?> datatypeDef = myContext.getElementDefinition(theValue.getClass());
|
|
||||||
if (datatypeDef instanceof IRuntimeDatatypeDefinition) {
|
|
||||||
Class<? extends IBaseDatatype> profileOf = ((IRuntimeDatatypeDefinition) datatypeDef).getProfileOf();
|
|
||||||
if (profileOf != null) {
|
|
||||||
datatypeDef = myContext.getElementDefinition(profileOf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
String childElementName = "value" + StringUtils.capitalize(datatypeDef.getName());
|
|
||||||
BaseRuntimeChildDefinition childByName = parameterElem.getChildByName(childElementName);
|
|
||||||
childByName.getMutator().setValue(parameter, theValue);
|
|
||||||
} else if (theValue instanceof IBaseResource) {
|
|
||||||
parameterElem.getChildByName("resource").getMutator().setValue(parameter, theValue);
|
|
||||||
} else {
|
|
||||||
throw new IllegalArgumentException("Don't know how to handle parameter of type " + theValue.getClass());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IOperationUntypedWithInputAndPartialOutput andParameter(String theName, IBase theValue) {
|
public IOperationUntypedWithInput withParameters(IBaseParameters theParameters) {
|
||||||
Validate.notEmpty(theName, "theName must not be null");
|
Validate.notNull(theParameters, "theParameters can not be null");
|
||||||
Validate.notNull(theValue, "theValue must not be null");
|
myParameters = theParameters;
|
||||||
addParam(theName, theValue);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IOperationUntypedWithInputAndPartialOutput andSearchParameter(String theName, IQueryParameterType theValue) {
|
|
||||||
addParam(theName, theValue);
|
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1576,18 +1616,12 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addParam(String theName, IQueryParameterType theValue) {
|
|
||||||
IPrimitiveType<?> stringType = ParametersUtil.createString(myContext, theValue.getValueAsQueryToken(myContext));
|
|
||||||
addParam(theName, stringType);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class OperationOutcomeResponseHandler implements IClientResponseHandler<BaseOperationOutcome> {
|
private final class OperationOutcomeResponseHandler implements IClientResponseHandler<BaseOperationOutcome> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BaseOperationOutcome invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders)
|
public BaseOperationOutcome invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws BaseServerResponseException {
|
||||||
throws BaseServerResponseException {
|
|
||||||
EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType);
|
EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType);
|
||||||
if (respType == null) {
|
if (respType == null) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -1608,18 +1642,18 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class OutcomeResponseHandler implements IClientResponseHandler<MethodOutcome> {
|
private final class OutcomeResponseHandler implements IClientResponseHandler<MethodOutcome> {
|
||||||
private final String myResourceName;
|
|
||||||
private PreferReturnEnum myPrefer;
|
private PreferReturnEnum myPrefer;
|
||||||
|
private final String myResourceName;
|
||||||
|
|
||||||
|
private OutcomeResponseHandler(String theResourceName) {
|
||||||
|
myResourceName = theResourceName;
|
||||||
|
}
|
||||||
|
|
||||||
private OutcomeResponseHandler(String theResourceName, PreferReturnEnum thePrefer) {
|
private OutcomeResponseHandler(String theResourceName, PreferReturnEnum thePrefer) {
|
||||||
this(theResourceName);
|
this(theResourceName);
|
||||||
myPrefer = thePrefer;
|
myPrefer = thePrefer;
|
||||||
}
|
}
|
||||||
|
|
||||||
private OutcomeResponseHandler(String theResourceName) {
|
|
||||||
myResourceName = theResourceName;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MethodOutcome invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws BaseServerResponseException {
|
public MethodOutcome invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws BaseServerResponseException {
|
||||||
MethodOutcome response = MethodUtil.process2xxResponse(myContext, myResourceName, theResponseStatusCode, theResponseMimeType, theResponseReader, theHeaders);
|
MethodOutcome response = MethodUtil.process2xxResponse(myContext, myResourceName, theResponseStatusCode, theResponseMimeType, theResponseReader, theHeaders);
|
||||||
|
@ -1779,8 +1813,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
@Override
|
||||||
public List<IBaseResource> invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders)
|
public List<IBaseResource> invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws BaseServerResponseException {
|
||||||
throws BaseServerResponseException {
|
|
||||||
if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) {
|
if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) {
|
||||||
Class<? extends IBaseResource> bundleType = myContext.getResourceDefinition("Bundle").getImplementingClass();
|
Class<? extends IBaseResource> bundleType = myContext.getResourceDefinition("Bundle").getImplementingClass();
|
||||||
ResourceResponseHandler<IBaseResource> handler = new ResourceResponseHandler<IBaseResource>((Class<IBaseResource>) bundleType);
|
ResourceResponseHandler<IBaseResource> handler = new ResourceResponseHandler<IBaseResource>((Class<IBaseResource>) bundleType);
|
||||||
|
@ -1795,7 +1828,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||||
private class SearchInternal extends BaseClientExecutable<IQuery<Object>, Object>implements IQuery<Object>, IUntypedQuery {
|
private class SearchInternal extends BaseClientExecutable<IQuery<Object>, Object> implements IQuery<Object>, IUntypedQuery {
|
||||||
|
|
||||||
private String myCompartmentName;
|
private String myCompartmentName;
|
||||||
private CriterionList myCriterion = new CriterionList();
|
private CriterionList myCriterion = new CriterionList();
|
||||||
|
@ -1809,10 +1842,10 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
private Class<? extends IBaseBundle> myReturnBundleType;
|
private Class<? extends IBaseBundle> myReturnBundleType;
|
||||||
private List<Include> myRevInclude = new ArrayList<Include>();
|
private List<Include> myRevInclude = new ArrayList<Include>();
|
||||||
private SearchStyleEnum mySearchStyle;
|
private SearchStyleEnum mySearchStyle;
|
||||||
|
private String mySearchUrl;
|
||||||
private List<TokenParam> mySecurity = new ArrayList<TokenParam>();
|
private List<TokenParam> mySecurity = new ArrayList<TokenParam>();
|
||||||
private List<SortInternal> mySort = new ArrayList<SortInternal>();
|
private List<SortInternal> mySort = new ArrayList<SortInternal>();
|
||||||
private List<TokenParam> myTags = new ArrayList<TokenParam>();
|
private List<TokenParam> myTags = new ArrayList<TokenParam>();
|
||||||
private String mySearchUrl;
|
|
||||||
|
|
||||||
public SearchInternal() {
|
public SearchInternal() {
|
||||||
myResourceType = null;
|
myResourceType = null;
|
||||||
|
@ -1826,6 +1859,44 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IQuery byUrl(String theSearchUrl) {
|
||||||
|
Validate.notBlank(theSearchUrl, "theSearchUrl must not be blank/null");
|
||||||
|
|
||||||
|
if (theSearchUrl.startsWith("http://") || theSearchUrl.startsWith("https://")) {
|
||||||
|
mySearchUrl = theSearchUrl;
|
||||||
|
int qIndex = mySearchUrl.indexOf('?');
|
||||||
|
if (qIndex != -1) {
|
||||||
|
mySearchUrl = mySearchUrl.substring(0, qIndex) + validateAndEscapeConditionalUrl(mySearchUrl.substring(qIndex));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String searchUrl = theSearchUrl;
|
||||||
|
if (searchUrl.startsWith("/")) {
|
||||||
|
searchUrl = searchUrl.substring(1);
|
||||||
|
}
|
||||||
|
if (!searchUrl.matches("[a-zA-Z]+($|\\?.*)")) {
|
||||||
|
throw new IllegalArgumentException("Search URL must be either a complete URL starting with http: or https:, or a relative FHIR URL in the form [ResourceType]?[Params]");
|
||||||
|
}
|
||||||
|
int qIndex = searchUrl.indexOf('?');
|
||||||
|
if (qIndex == -1) {
|
||||||
|
mySearchUrl = getUrlBase() + '/' + searchUrl;
|
||||||
|
} else {
|
||||||
|
mySearchUrl = getUrlBase() + '/' + validateAndEscapeConditionalUrl(searchUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IQuery count(int theLimitTo) {
|
||||||
|
if (theLimitTo > 0) {
|
||||||
|
myParamLimit = theLimitTo;
|
||||||
|
} else {
|
||||||
|
myParamLimit = null;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IBase execute() {
|
public IBase execute() {
|
||||||
|
|
||||||
|
@ -1861,9 +1932,28 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
addParam(params, Constants.PARAM_REVINCLUDE, next.getValue());
|
addParam(params, Constants.PARAM_REVINCLUDE, next.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU2)) {
|
||||||
|
SortSpec rootSs = null;
|
||||||
|
SortSpec lastSs = null;
|
||||||
|
for (SortInternal next : mySort) {
|
||||||
|
SortSpec nextSortSpec = new SortSpec();
|
||||||
|
nextSortSpec.setParamName(next.getParamValue());
|
||||||
|
nextSortSpec.setOrder(next.getDirection());
|
||||||
|
if (rootSs == null) {
|
||||||
|
rootSs = nextSortSpec;
|
||||||
|
} else {
|
||||||
|
lastSs.setChain(nextSortSpec);
|
||||||
|
}
|
||||||
|
lastSs = nextSortSpec;
|
||||||
|
}
|
||||||
|
if (rootSs != null) {
|
||||||
|
addParam(params, Constants.PARAM_SORT, SortParameter.createSortStringDstu3(rootSs));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
for (SortInternal next : mySort) {
|
for (SortInternal next : mySort) {
|
||||||
addParam(params, next.getParamName(), next.getParamValue());
|
addParam(params, next.getParamName(), next.getParamValue());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (myParamLimit != null) {
|
if (myParamLimit != null) {
|
||||||
addParam(params, Constants.PARAM_COUNT, Integer.toString(myParamLimit));
|
addParam(params, Constants.PARAM_COUNT, Integer.toString(myParamLimit));
|
||||||
|
@ -1876,8 +1966,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (myReturnBundleType == null && myContext.getVersion().getVersion().isRi()) {
|
if (myReturnBundleType == null && myContext.getVersion().getVersion().isRi()) {
|
||||||
throw new IllegalArgumentException("When using the client with HL7.org structures, you must specify "
|
throw new IllegalArgumentException("When using the client with HL7.org structures, you must specify " + "the bundle return type for the client by adding \".returnBundle(org.hl7.fhir.instance.model.Bundle.class)\" to your search method call before the \".execute()\" method");
|
||||||
+ "the bundle return type for the client by adding \".returnBundle(org.hl7.fhir.instance.model.Bundle.class)\" to your search method call before the \".execute()\" method");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IClientResponseHandler<? extends IBase> binding;
|
IClientResponseHandler<? extends IBase> binding;
|
||||||
|
@ -1934,16 +2023,6 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
return count(theLimitTo);
|
return count(theLimitTo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public IQuery count(int theLimitTo) {
|
|
||||||
if (theLimitTo > 0) {
|
|
||||||
myParamLimit = theLimitTo;
|
|
||||||
} else {
|
|
||||||
myParamLimit = null;
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IQuery returnBundle(Class theClass) {
|
public IQuery returnBundle(Class theClass) {
|
||||||
if (theClass == null) {
|
if (theClass == null) {
|
||||||
|
@ -2017,34 +2096,6 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public IQuery byUrl(String theSearchUrl) {
|
|
||||||
Validate.notBlank(theSearchUrl, "theSearchUrl must not be blank/null");
|
|
||||||
|
|
||||||
if (theSearchUrl.startsWith("http://") || theSearchUrl.startsWith("https://")) {
|
|
||||||
mySearchUrl = theSearchUrl;
|
|
||||||
int qIndex = mySearchUrl.indexOf('?');
|
|
||||||
if (qIndex != -1) {
|
|
||||||
mySearchUrl = mySearchUrl.substring(0, qIndex) + validateAndEscapeConditionalUrl(mySearchUrl.substring(qIndex));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
String searchUrl = theSearchUrl;
|
|
||||||
if (searchUrl.startsWith("/")) {
|
|
||||||
searchUrl = searchUrl.substring(1);
|
|
||||||
}
|
|
||||||
if (!searchUrl.matches("[a-zA-Z]+($|\\?.*)")) {
|
|
||||||
throw new IllegalArgumentException("Search URL must be either a complete URL starting with http: or https:, or a relative FHIR URL in the form [ResourceType]?[Params]");
|
|
||||||
}
|
|
||||||
int qIndex = searchUrl.indexOf('?');
|
|
||||||
if (qIndex == -1) {
|
|
||||||
mySearchUrl = getUrlBase() + '/' + searchUrl;
|
|
||||||
} else {
|
|
||||||
mySearchUrl = getUrlBase() + '/' + validateAndEscapeConditionalUrl(searchUrl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
|
@ -2053,6 +2104,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
private SearchInternal myFor;
|
private SearchInternal myFor;
|
||||||
private String myParamName;
|
private String myParamName;
|
||||||
private String myParamValue;
|
private String myParamValue;
|
||||||
|
private SortOrderEnum myDirection;
|
||||||
|
|
||||||
public SortInternal(SearchInternal theFor) {
|
public SortInternal(SearchInternal theFor) {
|
||||||
myFor = theFor;
|
myFor = theFor;
|
||||||
|
@ -2061,13 +2113,23 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
@Override
|
@Override
|
||||||
public IQuery ascending(IParam theParam) {
|
public IQuery ascending(IParam theParam) {
|
||||||
myParamName = Constants.PARAM_SORT_ASC;
|
myParamName = Constants.PARAM_SORT_ASC;
|
||||||
|
myDirection = SortOrderEnum.ASC;
|
||||||
myParamValue = theParam.getParamName();
|
myParamValue = theParam.getParamName();
|
||||||
return myFor;
|
return myFor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IQuery ascending(String theParam) {
|
||||||
|
myParamName = Constants.PARAM_SORT_ASC;
|
||||||
|
myDirection = SortOrderEnum.ASC;
|
||||||
|
myParamValue = theParam;
|
||||||
|
return myFor;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IQuery defaultOrder(IParam theParam) {
|
public IQuery defaultOrder(IParam theParam) {
|
||||||
myParamName = Constants.PARAM_SORT;
|
myParamName = Constants.PARAM_SORT;
|
||||||
|
myDirection = null;
|
||||||
myParamValue = theParam.getParamName();
|
myParamValue = theParam.getParamName();
|
||||||
return myFor;
|
return myFor;
|
||||||
}
|
}
|
||||||
|
@ -2075,10 +2137,23 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
@Override
|
@Override
|
||||||
public IQuery descending(IParam theParam) {
|
public IQuery descending(IParam theParam) {
|
||||||
myParamName = Constants.PARAM_SORT_DESC;
|
myParamName = Constants.PARAM_SORT_DESC;
|
||||||
|
myDirection = SortOrderEnum.DESC;
|
||||||
myParamValue = theParam.getParamName();
|
myParamValue = theParam.getParamName();
|
||||||
return myFor;
|
return myFor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IQuery descending(String theParam) {
|
||||||
|
myParamName = Constants.PARAM_SORT_DESC;
|
||||||
|
myDirection = SortOrderEnum.DESC;
|
||||||
|
myParamValue = theParam;
|
||||||
|
return myFor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SortOrderEnum getDirection() {
|
||||||
|
return myDirection;
|
||||||
|
}
|
||||||
|
|
||||||
public String getParamName() {
|
public String getParamName() {
|
||||||
return myParamName;
|
return myParamName;
|
||||||
}
|
}
|
||||||
|
@ -2092,8 +2167,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
private final class StringResponseHandler implements IClientResponseHandler<String> {
|
private final class StringResponseHandler implements IClientResponseHandler<String> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders)
|
public String invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
|
||||||
throws IOException, BaseServerResponseException {
|
|
||||||
return IOUtils.toString(theResponseReader);
|
return IOUtils.toString(theResponseReader);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2111,7 +2185,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class TransactionExecutable<T> extends BaseClientExecutable<ITransactionTyped<T>, T>implements ITransactionTyped<T> {
|
private final class TransactionExecutable<T> extends BaseClientExecutable<ITransactionTyped<T>, T> implements ITransactionTyped<T> {
|
||||||
|
|
||||||
private IBaseBundle myBaseBundle;
|
private IBaseBundle myBaseBundle;
|
||||||
private Bundle myBundle;
|
private Bundle myBundle;
|
||||||
|
@ -2201,7 +2275,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class UpdateInternal extends BaseClientExecutable<IUpdateExecutable, MethodOutcome>implements IUpdate, IUpdateTyped, IUpdateExecutable, IUpdateWithQuery, IUpdateWithQueryTyped {
|
private class UpdateInternal extends BaseClientExecutable<IUpdateExecutable, MethodOutcome> implements IUpdate, IUpdateTyped, IUpdateExecutable, IUpdateWithQuery, IUpdateWithQueryTyped {
|
||||||
|
|
||||||
private CriterionList myCriterionList;
|
private CriterionList myCriterionList;
|
||||||
private IIdType myId;
|
private IIdType myId;
|
||||||
|
@ -2319,7 +2393,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ValidateInternal extends BaseClientExecutable<IValidateUntyped, MethodOutcome>implements IValidate, IValidateUntyped {
|
private class ValidateInternal extends BaseClientExecutable<IValidateUntyped, MethodOutcome> implements IValidate, IValidateUntyped {
|
||||||
private IBaseResource myResource;
|
private IBaseResource myResource;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -2361,34 +2435,4 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String validateAndEscapeConditionalUrl(String theSearchUrl) {
|
|
||||||
Validate.notBlank(theSearchUrl, "Conditional URL can not be blank/null");
|
|
||||||
StringBuilder b = new StringBuilder();
|
|
||||||
boolean haveHadQuestionMark = false;
|
|
||||||
for (int i = 0; i < theSearchUrl.length(); i++) {
|
|
||||||
char nextChar = theSearchUrl.charAt(i);
|
|
||||||
if (!haveHadQuestionMark) {
|
|
||||||
if (nextChar == '?') {
|
|
||||||
haveHadQuestionMark = true;
|
|
||||||
} else if (!Character.isLetter(nextChar)) {
|
|
||||||
throw new IllegalArgumentException("Conditional URL must be in the format \"[ResourceType]?[Params]\" and must not have a base URL - Found: " + theSearchUrl);
|
|
||||||
}
|
|
||||||
b.append(nextChar);
|
|
||||||
} else {
|
|
||||||
switch (nextChar) {
|
|
||||||
case '|':
|
|
||||||
case '?':
|
|
||||||
case '$':
|
|
||||||
case ':':
|
|
||||||
b.append(UrlUtil.escape(Character.toString(nextChar)));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
b.append(nextChar);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return b.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,10 +22,39 @@ package ca.uhn.fhir.rest.gclient;
|
||||||
|
|
||||||
public interface ISort<T> {
|
public interface ISort<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort ascending
|
||||||
|
*/
|
||||||
IQuery<T> ascending(IParam theParam);
|
IQuery<T> ascending(IParam theParam);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort ascending
|
||||||
|
*
|
||||||
|
* @param theParam The param name, e.g. "address"
|
||||||
|
*/
|
||||||
|
IQuery<T> ascending(String theParam);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort by the default order. Note that as of STU3, there is no longer
|
||||||
|
* a concept of default order, only ascending and descending. This method
|
||||||
|
* technically implies "ascending" but it makes more sense to use
|
||||||
|
* {@link #ascending(IParam)}
|
||||||
|
*/
|
||||||
IQuery<T> defaultOrder(IParam theParam);
|
IQuery<T> defaultOrder(IParam theParam);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort descending
|
||||||
|
*
|
||||||
|
* @param A query param - Could be a constant such as <code>Patient.ADDRESS</code> or a custom
|
||||||
|
* param such as <code>new StringClientParam("foo")</code>
|
||||||
|
*/
|
||||||
IQuery<T> descending(IParam theParam);
|
IQuery<T> descending(IParam theParam);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort ascending
|
||||||
|
*
|
||||||
|
* @param theParam The param name, e.g. "address"
|
||||||
|
*/
|
||||||
|
IQuery<T> descending(String theParam);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -483,7 +483,7 @@ public class MethodUtil {
|
||||||
} else if (nextAnnotation instanceof Count) {
|
} else if (nextAnnotation instanceof Count) {
|
||||||
param = new CountParameter();
|
param = new CountParameter();
|
||||||
} else if (nextAnnotation instanceof Sort) {
|
} else if (nextAnnotation instanceof Sort) {
|
||||||
param = new SortParameter();
|
param = new SortParameter(theContext);
|
||||||
} else if (nextAnnotation instanceof TransactionParam) {
|
} else if (nextAnnotation instanceof TransactionParam) {
|
||||||
param = new TransactionParameter(theContext);
|
param = new TransactionParameter(theContext);
|
||||||
} else if (nextAnnotation instanceof ConditionalUrlParam) {
|
} else if (nextAnnotation instanceof ConditionalUrlParam) {
|
||||||
|
|
|
@ -26,23 +26,55 @@ import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
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.context.FhirVersionEnum;
|
||||||
import ca.uhn.fhir.rest.annotation.Sort;
|
import ca.uhn.fhir.rest.annotation.Sort;
|
||||||
import ca.uhn.fhir.rest.api.SortOrderEnum;
|
import ca.uhn.fhir.rest.api.SortOrderEnum;
|
||||||
import ca.uhn.fhir.rest.api.SortSpec;
|
import ca.uhn.fhir.rest.api.SortSpec;
|
||||||
|
import ca.uhn.fhir.rest.param.ParameterUtil;
|
||||||
import ca.uhn.fhir.rest.server.Constants;
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
|
||||||
public class SortParameter implements IParameter {
|
public class SortParameter implements IParameter {
|
||||||
|
|
||||||
|
private FhirContext myContext;
|
||||||
|
|
||||||
|
public SortParameter(FhirContext theContext) {
|
||||||
|
myContext = theContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initializeTypes(Method theMethod, Class<? extends Collection<?>> theOuterCollectionType, Class<? extends Collection<?>> theInnerCollectionType, Class<?> theParameterType) {
|
||||||
|
if (theOuterCollectionType != null || theInnerCollectionType != null) {
|
||||||
|
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type '" + theMethod.getDeclaringClass().getCanonicalName() + "' is annotated with @" + Sort.class.getName() + " but can not be of collection type");
|
||||||
|
}
|
||||||
|
if (!theParameterType.equals(SortSpec.class)) {
|
||||||
|
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type '" + theMethod.getDeclaringClass().getCanonicalName() + "' is annotated with @" + Sort.class.getName() + " but is an invalid type, must be: " + SortSpec.class.getCanonicalName());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map<String, List<String>> theTargetQueryArguments, IBaseResource theTargetResource) throws InternalErrorException {
|
public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map<String, List<String>> theTargetQueryArguments, IBaseResource theTargetResource) throws InternalErrorException {
|
||||||
SortSpec ss = (SortSpec) theSourceClientArgument;
|
SortSpec ss = (SortSpec) theSourceClientArgument;
|
||||||
|
|
||||||
|
if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU2)) {
|
||||||
|
String string = createSortStringDstu3(ss);
|
||||||
|
if (string.length() > 0) {
|
||||||
|
if (!theTargetQueryArguments.containsKey(Constants.PARAM_SORT)) {
|
||||||
|
theTargetQueryArguments.put(Constants.PARAM_SORT, new ArrayList<String>());
|
||||||
|
}
|
||||||
|
theTargetQueryArguments.get(Constants.PARAM_SORT).add(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
while (ss != null) {
|
while (ss != null) {
|
||||||
String name;
|
String name;
|
||||||
if (ss.getOrder() == null) {
|
if (ss.getOrder() == null) {
|
||||||
|
@ -57,11 +89,13 @@ public class SortParameter implements IParameter {
|
||||||
if (!theTargetQueryArguments.containsKey(name)) {
|
if (!theTargetQueryArguments.containsKey(name)) {
|
||||||
theTargetQueryArguments.put(name, new ArrayList<String>());
|
theTargetQueryArguments.put(name, new ArrayList<String>());
|
||||||
}
|
}
|
||||||
|
|
||||||
theTargetQueryArguments.get(name).add(ss.getParamName());
|
theTargetQueryArguments.get(name).add(ss.getParamName());
|
||||||
}
|
}
|
||||||
ss = ss.getChain();
|
ss = ss.getChain();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||||
|
@ -89,7 +123,36 @@ public class SortParameter implements IParameter {
|
||||||
|
|
||||||
String[] values = theRequest.getParameters().get(nextParamName);
|
String[] values = theRequest.getParameters().get(nextParamName);
|
||||||
if (values != null) {
|
if (values != null) {
|
||||||
|
|
||||||
for (String nextValue : values) {
|
for (String nextValue : values) {
|
||||||
|
|
||||||
|
if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU2) && order == null) {
|
||||||
|
StringTokenizer tok = new StringTokenizer(nextValue, ",");
|
||||||
|
while (tok.hasMoreTokens()) {
|
||||||
|
String next = tok.nextToken();
|
||||||
|
if (isNotBlank(next) && !next.equals("-")) {
|
||||||
|
order = SortOrderEnum.ASC;
|
||||||
|
if (next.startsWith("-")) {
|
||||||
|
order = SortOrderEnum.DESC;
|
||||||
|
next = next.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
SortSpec spec = new SortSpec();
|
||||||
|
spec.setOrder(order);
|
||||||
|
spec.setParamName(next);
|
||||||
|
if (innerSpec == null) {
|
||||||
|
outerSpec = spec;
|
||||||
|
innerSpec = spec;
|
||||||
|
} else {
|
||||||
|
innerSpec.setChain(spec);
|
||||||
|
innerSpec = spec;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
if (isNotBlank(nextValue)) {
|
if (isNotBlank(nextValue)) {
|
||||||
SortSpec spec = new SortSpec();
|
SortSpec spec = new SortSpec();
|
||||||
spec.setOrder(order);
|
spec.setOrder(order);
|
||||||
|
@ -105,19 +168,30 @@ public class SortParameter implements IParameter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return outerSpec;
|
return outerSpec;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public static String createSortStringDstu3(SortSpec ss) {
|
||||||
public void initializeTypes(Method theMethod, Class<? extends Collection<?>> theOuterCollectionType, Class<? extends Collection<?>> theInnerCollectionType, Class<?> theParameterType) {
|
StringBuilder val = new StringBuilder();
|
||||||
if (theOuterCollectionType != null || theInnerCollectionType != null) {
|
while (ss != null) {
|
||||||
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type '" + theMethod.getDeclaringClass().getCanonicalName() + "' is annotated with @" + Sort.class.getName() + " but can not be of collection type");
|
|
||||||
|
if (isNotBlank(ss.getParamName())) {
|
||||||
|
if (val.length() > 0) {
|
||||||
|
val.append(',');
|
||||||
}
|
}
|
||||||
if (!theParameterType.equals(SortSpec.class)) {
|
if (ss.getOrder() == SortOrderEnum.DESC) {
|
||||||
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type '" + theMethod.getDeclaringClass().getCanonicalName() + "' is annotated with @" + Sort.class.getName() + " but is an invalid type, must be: " + SortSpec.class.getCanonicalName());
|
val.append('-');
|
||||||
|
}
|
||||||
|
val.append(ParameterUtil.escape(ss.getParamName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ss = ss.getChain();
|
||||||
|
}
|
||||||
|
|
||||||
|
String string = val.toString();
|
||||||
|
return string;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import org.apache.http.message.BasicHeader;
|
||||||
import org.apache.http.message.BasicStatusLine;
|
import org.apache.http.message.BasicStatusLine;
|
||||||
import org.hl7.fhir.dstu3.model.Binary;
|
import org.hl7.fhir.dstu3.model.Binary;
|
||||||
import org.hl7.fhir.dstu3.model.Bundle;
|
import org.hl7.fhir.dstu3.model.Bundle;
|
||||||
|
import org.hl7.fhir.dstu3.model.Bundle.BundleType;
|
||||||
import org.hl7.fhir.dstu3.model.Conformance;
|
import org.hl7.fhir.dstu3.model.Conformance;
|
||||||
import org.hl7.fhir.dstu3.model.OperationOutcome;
|
import org.hl7.fhir.dstu3.model.OperationOutcome;
|
||||||
import org.hl7.fhir.dstu3.model.Parameters;
|
import org.hl7.fhir.dstu3.model.Parameters;
|
||||||
|
@ -607,6 +608,60 @@ public class GenericClientDstu3Test {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See #371
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testSortDstu3Test() throws Exception {
|
||||||
|
IParser p = ourCtx.newXmlParser();
|
||||||
|
|
||||||
|
Bundle b = new Bundle();
|
||||||
|
b.setType(BundleType.SEARCHSET);
|
||||||
|
|
||||||
|
final String respString = p.encodeResourceToString(b);
|
||||||
|
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) throws Throwable {
|
||||||
|
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||||
|
int idx = 0;
|
||||||
|
|
||||||
|
//@formatter:off
|
||||||
|
client
|
||||||
|
.search()
|
||||||
|
.forResource(Patient.class)
|
||||||
|
.sort().ascending("address")
|
||||||
|
.returnBundle(Bundle.class)
|
||||||
|
.execute();
|
||||||
|
assertEquals("http://example.com/fhir/Patient?_sort=address", capt.getAllValues().get(idx++).getURI().toASCIIString());
|
||||||
|
|
||||||
|
client
|
||||||
|
.search()
|
||||||
|
.forResource(Patient.class)
|
||||||
|
.sort().descending("address")
|
||||||
|
.returnBundle(Bundle.class)
|
||||||
|
.execute();
|
||||||
|
assertEquals("http://example.com/fhir/Patient?_sort=-address", capt.getAllValues().get(idx++).getURI().toASCIIString());
|
||||||
|
|
||||||
|
client
|
||||||
|
.search()
|
||||||
|
.forResource(Patient.class)
|
||||||
|
.sort().descending("address")
|
||||||
|
.sort().ascending("name")
|
||||||
|
.sort().descending(Patient.BIRTHDATE)
|
||||||
|
.returnBundle(Bundle.class)
|
||||||
|
.execute();
|
||||||
|
assertEquals("http://example.com/fhir/Patient?_sort=-address%2Cname%2C-birthdate", capt.getAllValues().get(idx++).getURI().toASCIIString());
|
||||||
|
//@formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUserAgentForConformance() throws Exception {
|
public void testUserAgentForConformance() throws Exception {
|
||||||
IParser p = ourCtx.newXmlParser();
|
IParser p = ourCtx.newXmlParser();
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package ca.uhn.fhir.rest.client;
|
package ca.uhn.fhir.rest.client;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@ -33,6 +33,9 @@ import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.rest.annotation.Count;
|
import ca.uhn.fhir.rest.annotation.Count;
|
||||||
import ca.uhn.fhir.rest.annotation.RequiredParam;
|
import ca.uhn.fhir.rest.annotation.RequiredParam;
|
||||||
import ca.uhn.fhir.rest.annotation.Search;
|
import ca.uhn.fhir.rest.annotation.Search;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Sort;
|
||||||
|
import ca.uhn.fhir.rest.api.SortOrderEnum;
|
||||||
|
import ca.uhn.fhir.rest.api.SortSpec;
|
||||||
import ca.uhn.fhir.rest.client.api.IRestfulClient;
|
import ca.uhn.fhir.rest.client.api.IRestfulClient;
|
||||||
import ca.uhn.fhir.rest.param.StringParam;
|
import ca.uhn.fhir.rest.param.StringParam;
|
||||||
import ca.uhn.fhir.rest.server.Constants;
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
|
@ -91,6 +94,35 @@ public class SearchClientDstu3Test {
|
||||||
assertEquals("http://localhost:8081/hapi-fhir/fhir/Location?_query=match&name=smith&_count=100", value.getURI().toString());
|
assertEquals("http://localhost:8081/hapi-fhir/fhir/Location?_query=match&name=smith&_count=100", value.getURI().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See #371
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testSortForDstu3() throws Exception {
|
||||||
|
|
||||||
|
final String response = createBundleWithSearchExtension();
|
||||||
|
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||||
|
when(ourHttpClient.execute(capt.capture())).thenReturn(ourHttpResponse);
|
||||||
|
when(ourHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||||
|
when(ourHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
||||||
|
when(ourHttpResponse.getEntity().getContent()).thenAnswer(new Answer<InputStream>() {
|
||||||
|
@Override
|
||||||
|
public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||||
|
return new ReaderInputStream(new StringReader(response), Charset.forName("UTF-8"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ILocationClient client = ourCtx.newRestfulClient(ILocationClient.class, "http://localhost/fhir");
|
||||||
|
|
||||||
|
int idx = 0;
|
||||||
|
|
||||||
|
client.search(new SortSpec("param1", SortOrderEnum.ASC));
|
||||||
|
assertEquals("http://localhost/fhir/Bundle?_sort=param1", ((HttpGet) capt.getAllValues().get(idx++)).getURI().toString());
|
||||||
|
|
||||||
|
client.search(new SortSpec("param1", SortOrderEnum.ASC).setChain(new SortSpec("param2", SortOrderEnum.DESC)));
|
||||||
|
assertEquals("http://localhost/fhir/Bundle?_sort=param1%2C-param2", ((HttpGet) capt.getAllValues().get(idx++)).getURI().toString());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See #299
|
* See #299
|
||||||
*/
|
*/
|
||||||
|
@ -159,6 +191,9 @@ public class SearchClientDstu3Test {
|
||||||
|
|
||||||
@Search(queryName = "match", type=Location.class)
|
@Search(queryName = "match", type=Location.class)
|
||||||
public Bundle getMatchesReturnBundle(final @RequiredParam(name = Location.SP_NAME) StringParam name, final @Count Integer count);
|
public Bundle getMatchesReturnBundle(final @RequiredParam(name = Location.SP_NAME) StringParam name, final @Count Integer count);
|
||||||
|
|
||||||
|
@Search
|
||||||
|
public Bundle search(@Sort SortSpec theSort);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,138 @@
|
||||||
|
package ca.uhn.fhir.rest.server;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.stringContainsInOrder;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
|
import org.apache.http.client.methods.HttpGet;
|
||||||
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
|
import org.apache.http.impl.client.HttpClientBuilder;
|
||||||
|
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||||
|
import org.eclipse.jetty.server.Server;
|
||||||
|
import org.eclipse.jetty.servlet.ServletHandler;
|
||||||
|
import org.eclipse.jetty.servlet.ServletHolder;
|
||||||
|
import org.hl7.fhir.dstu3.model.HumanName;
|
||||||
|
import org.hl7.fhir.dstu3.model.Patient;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Count;
|
||||||
|
import ca.uhn.fhir.rest.annotation.OptionalParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Search;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Sort;
|
||||||
|
import ca.uhn.fhir.rest.api.SortOrderEnum;
|
||||||
|
import ca.uhn.fhir.rest.api.SortSpec;
|
||||||
|
import ca.uhn.fhir.rest.param.TokenParam;
|
||||||
|
import ca.uhn.fhir.util.PortUtil;
|
||||||
|
import ca.uhn.fhir.util.TestUtil;
|
||||||
|
|
||||||
|
public class SearchSortDstu3Test {
|
||||||
|
|
||||||
|
private static CloseableHttpClient ourClient;
|
||||||
|
private static FhirContext ourCtx = FhirContext.forDstu3();
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchSortDstu3Test.class);
|
||||||
|
private static int ourPort;
|
||||||
|
private static Server ourServer;
|
||||||
|
private static String ourLastMethod;
|
||||||
|
private static SortSpec ourLastSortSpec;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void before() {
|
||||||
|
ourLastMethod = null;
|
||||||
|
ourLastSortSpec = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSearch() throws Exception {
|
||||||
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_sort=param1,-param2,param3,-param4");
|
||||||
|
CloseableHttpResponse status = ourClient.execute(httpGet);
|
||||||
|
try {
|
||||||
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
ourLog.info(responseContent);
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
assertEquals("search", ourLastMethod);
|
||||||
|
|
||||||
|
assertEquals("param1", ourLastSortSpec.getParamName());
|
||||||
|
assertEquals(SortOrderEnum.ASC, ourLastSortSpec.getOrder());
|
||||||
|
|
||||||
|
assertEquals("param2", ourLastSortSpec.getChain().getParamName());
|
||||||
|
assertEquals(SortOrderEnum.DESC, ourLastSortSpec.getChain().getOrder());
|
||||||
|
|
||||||
|
assertEquals("param3", ourLastSortSpec.getChain().getChain().getParamName());
|
||||||
|
assertEquals(SortOrderEnum.ASC, ourLastSortSpec.getChain().getChain().getOrder());
|
||||||
|
|
||||||
|
assertEquals("param4", ourLastSortSpec.getChain().getChain().getChain().getParamName());
|
||||||
|
assertEquals(SortOrderEnum.DESC, ourLastSortSpec.getChain().getChain().getChain().getOrder());
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void afterClassClearContext() throws Exception {
|
||||||
|
ourServer.stop();
|
||||||
|
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void beforeClass() throws Exception {
|
||||||
|
ourPort = PortUtil.findFreePort();
|
||||||
|
ourServer = new Server(ourPort);
|
||||||
|
|
||||||
|
DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();
|
||||||
|
|
||||||
|
ServletHandler proxyHandler = new ServletHandler();
|
||||||
|
RestfulServer servlet = new RestfulServer(ourCtx);
|
||||||
|
servlet.setPagingProvider(new FifoMemoryPagingProvider(10));
|
||||||
|
|
||||||
|
servlet.setResourceProviders(patientProvider);
|
||||||
|
ServletHolder servletHolder = new ServletHolder(servlet);
|
||||||
|
proxyHandler.addServletWithMapping(servletHolder, "/*");
|
||||||
|
ourServer.setHandler(proxyHandler);
|
||||||
|
ourServer.start();
|
||||||
|
|
||||||
|
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||||
|
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||||
|
builder.setConnectionManager(connectionManager);
|
||||||
|
ourClient = builder.build();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class DummyPatientResourceProvider implements IResourceProvider {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<? extends IBaseResource> getResourceType() {
|
||||||
|
return Patient.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
//@formatter:off
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
@Search()
|
||||||
|
public List search(
|
||||||
|
@Sort SortSpec theSortSpec
|
||||||
|
) {
|
||||||
|
ourLastMethod = "search";
|
||||||
|
ourLastSortSpec = theSortSpec;
|
||||||
|
ArrayList<Patient> retVal = new ArrayList<Patient>();
|
||||||
|
for (int i = 1; i < 100; i++) {
|
||||||
|
retVal.add((Patient) new Patient().addName(new HumanName().addFamily("FAMILY")).setId("" + i));
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
//@formatter:on
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -259,6 +259,10 @@
|
||||||
the spec says they should. Thanks to Jim Steel for
|
the spec says they should. Thanks to Jim Steel for
|
||||||
reporting!
|
reporting!
|
||||||
</action>
|
</action>
|
||||||
|
<action type="fix" issue="371">
|
||||||
|
Update STU3 client and server to use the new sort parameter style (param1,-param2,param). Thanks to GitHub user @euz1e4r for
|
||||||
|
reporting!
|
||||||
|
</action>
|
||||||
</release>
|
</release>
|
||||||
<release version="1.5" date="2016-04-20">
|
<release version="1.5" date="2016-04-20">
|
||||||
<action type="fix" issue="339">
|
<action type="fix" issue="339">
|
||||||
|
|
Loading…
Reference in New Issue