Work on #164 - Improve error messages when an invalid or missing content
type header is detected for creat/update requests
This commit is contained in:
parent
5cf5bb0473
commit
5f4a966077
|
@ -581,7 +581,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected IResource parseResourceBody(String theResourceBody) {
|
protected IResource parseResourceBody(String theResourceBody) {
|
||||||
EncodingEnum encoding = determineRawEncoding(theResourceBody);
|
EncodingEnum encoding = MethodUtil.detectEncodingNoDefault(theResourceBody);
|
||||||
if (encoding == null) {
|
if (encoding == null) {
|
||||||
throw new InvalidRequestException("FHIR client can't determine resource encoding");
|
throw new InvalidRequestException("FHIR client can't determine resource encoding");
|
||||||
}
|
}
|
||||||
|
@ -598,24 +598,6 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns null if encoding can't be determined
|
|
||||||
*/
|
|
||||||
private static EncodingEnum determineRawEncoding(String theResourceBody) {
|
|
||||||
EncodingEnum encoding = null;
|
|
||||||
for (int i = 0; i < theResourceBody.length() && encoding == null; i++) {
|
|
||||||
switch (theResourceBody.charAt(i)) {
|
|
||||||
case '<':
|
|
||||||
encoding = EncodingEnum.XML;
|
|
||||||
break;
|
|
||||||
case '{':
|
|
||||||
encoding = EncodingEnum.JSON;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return encoding;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final class BundleResponseHandler implements IClientResponseHandler<Bundle> {
|
private final class BundleResponseHandler implements IClientResponseHandler<Bundle> {
|
||||||
|
|
||||||
private Class<? extends IBaseResource> myType;
|
private Class<? extends IBaseResource> myType;
|
||||||
|
@ -1579,7 +1561,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
|
|
||||||
public TransactionExecutable(String theBundle) {
|
public TransactionExecutable(String theBundle) {
|
||||||
myRawBundle = theBundle;
|
myRawBundle = theBundle;
|
||||||
myRawBundleEncoding = determineRawEncoding(myRawBundle);
|
myRawBundleEncoding = MethodUtil.detectEncodingNoDefault(myRawBundle);
|
||||||
if (myRawBundleEncoding == null) {
|
if (myRawBundleEncoding == null) {
|
||||||
throw new IllegalArgumentException("Can not determine encoding of raw resource body");
|
throw new IllegalArgumentException("Can not determine encoding of raw resource body");
|
||||||
}
|
}
|
||||||
|
@ -1603,7 +1585,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
* If the user has explicitly requested a given encoding, we may need to reencode the raw string
|
* If the user has explicitly requested a given encoding, we may need to reencode the raw string
|
||||||
*/
|
*/
|
||||||
if (getParamEncoding() != null) {
|
if (getParamEncoding() != null) {
|
||||||
if (determineRawEncoding(myRawBundle) != getParamEncoding()) {
|
if (MethodUtil.detectEncodingNoDefault(myRawBundle) != getParamEncoding()) {
|
||||||
IResource parsed = parseResourceBody(myRawBundle);
|
IResource parsed = parseResourceBody(myRawBundle);
|
||||||
myRawBundle = getParamEncoding().newParser(getFhirContext()).encodeResourceToString(parsed);
|
myRawBundle = getParamEncoding().newParser(getFhirContext()).encodeResourceToString(parsed);
|
||||||
}
|
}
|
||||||
|
|
|
@ -167,6 +167,25 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
|
||||||
*/
|
*/
|
||||||
public abstract String getResourceName();
|
public abstract String getResourceName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value of {@link #getResourceOperationType()} or {@link #getSystemOperationType()} or {@link #getOtherOperationType()}
|
||||||
|
*/
|
||||||
|
public String getResourceOrSystemOperationType() {
|
||||||
|
Enum<?> retVal = getResourceOperationType();
|
||||||
|
if (retVal != null) {
|
||||||
|
return retVal.name();
|
||||||
|
}
|
||||||
|
retVal = getSystemOperationType();
|
||||||
|
if (retVal != null) {
|
||||||
|
return retVal.name();
|
||||||
|
}
|
||||||
|
retVal = getOtherOperationType();
|
||||||
|
if (retVal != null) {
|
||||||
|
return retVal.name();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public abstract RestfulOperationTypeEnum getResourceOperationType();
|
public abstract RestfulOperationTypeEnum getResourceOperationType();
|
||||||
|
|
||||||
public abstract RestfulOperationSystemEnum getSystemOperationType();
|
public abstract RestfulOperationSystemEnum getSystemOperationType();
|
||||||
|
|
|
@ -20,9 +20,12 @@ package ca.uhn.fhir.rest.method;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
|
import java.io.StringReader;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
|
@ -32,6 +35,7 @@ import java.util.Set;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.hl7.fhir.instance.model.IBaseResource;
|
import org.hl7.fhir.instance.model.IBaseResource;
|
||||||
|
|
||||||
|
@ -52,6 +56,7 @@ import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServerUtils;
|
import ca.uhn.fhir.rest.server.RestfulServerUtils;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||||
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.interceptor.IServerInterceptor;
|
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||||
|
|
||||||
abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<MethodOutcome> {
|
abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<MethodOutcome> {
|
||||||
|
@ -64,7 +69,8 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
|
||||||
|
|
||||||
if (!theMethod.getReturnType().equals(MethodOutcome.class)) {
|
if (!theMethod.getReturnType().equals(MethodOutcome.class)) {
|
||||||
if (!allowVoidReturnType()) {
|
if (!allowVoidReturnType()) {
|
||||||
throw new ConfigurationException("Method " + theMethod.getName() + " in type " + theMethod.getDeclaringClass().getCanonicalName() + " is a @" + theMethodAnnotation.getSimpleName() + " method but it does not return " + MethodOutcome.class);
|
throw new ConfigurationException("Method " + theMethod.getName() + " in type " + theMethod.getDeclaringClass().getCanonicalName() + " is a @" + theMethodAnnotation.getSimpleName()
|
||||||
|
+ " method but it does not return " + MethodOutcome.class);
|
||||||
} else if (theMethod.getReturnType() == void.class) {
|
} else if (theMethod.getReturnType() == void.class) {
|
||||||
myReturnVoid = true;
|
myReturnVoid = true;
|
||||||
}
|
}
|
||||||
|
@ -100,8 +106,7 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
|
||||||
protected abstract BaseHttpClientInvocation createClientInvocation(Object[] theArgs, IResource resource);
|
protected abstract BaseHttpClientInvocation createClientInvocation(Object[] theArgs, IResource resource);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For servers, this method will match only incoming requests that match the given operation, or which have no
|
* For servers, this method will match only incoming requests that match the given operation, or which have no operation in the URL if this method returns null.
|
||||||
* operation in the URL if this method returns null.
|
|
||||||
*/
|
*/
|
||||||
protected abstract String getMatchingOperation();
|
protected abstract String getMatchingOperation();
|
||||||
|
|
||||||
|
@ -125,7 +130,8 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MethodOutcome invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
|
public MethodOutcome invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException,
|
||||||
|
BaseServerResponseException {
|
||||||
switch (theResponseStatusCode) {
|
switch (theResponseStatusCode) {
|
||||||
case Constants.STATUS_HTTP_200_OK:
|
case Constants.STATUS_HTTP_200_OK:
|
||||||
case Constants.STATUS_HTTP_201_CREATED:
|
case Constants.STATUS_HTTP_201_CREATED:
|
||||||
|
@ -195,7 +201,8 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
|
||||||
switch (getResourceOperationType()) {
|
switch (getResourceOperationType()) {
|
||||||
case CREATE:
|
case CREATE:
|
||||||
if (response == null) {
|
if (response == null) {
|
||||||
throw new InternalErrorException("Method " + getMethod().getName() + " in type " + getMethod().getDeclaringClass().getCanonicalName() + " returned null, which is not allowed for create operation");
|
throw new InternalErrorException("Method " + getMethod().getName() + " in type " + getMethod().getDeclaringClass().getCanonicalName()
|
||||||
|
+ " returned null, which is not allowed for create operation");
|
||||||
}
|
}
|
||||||
if (response.getCreated() == null || Boolean.TRUE.equals(response.getCreated())) {
|
if (response.getCreated() == null || Boolean.TRUE.equals(response.getCreated())) {
|
||||||
servletResponse.setStatus(Constants.STATUS_HTTP_201_CREATED);
|
servletResponse.setStatus(Constants.STATUS_HTTP_201_CREATED);
|
||||||
|
@ -269,9 +276,39 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
protected IResource parseIncomingServerResource(Request theRequest) throws IOException {
|
protected IResource parseIncomingServerResource(Request theRequest) throws IOException {
|
||||||
EncodingEnum encoding = RestfulServerUtils.determineRequestEncoding(theRequest);
|
|
||||||
|
Reader requestReader;
|
||||||
|
EncodingEnum encoding = RestfulServerUtils.determineRequestEncodingNoDefault(theRequest);
|
||||||
|
if (encoding == null) {
|
||||||
|
String ctValue = theRequest.getServletRequest().getHeader(Constants.HEADER_CONTENT_TYPE);
|
||||||
|
if (ctValue != null) {
|
||||||
|
if (ctValue.startsWith("application/x-www-form-urlencoded")) {
|
||||||
|
String msg = getContext().getLocalizer().getMessage(BaseOutcomeReturningMethodBinding.class, "invalidContentTypeInRequest", ctValue, getResourceOrSystemOperationType());
|
||||||
|
throw new InvalidRequestException(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isBlank(ctValue)) {
|
||||||
|
/*
|
||||||
|
* If the client didn't send a content type, try to guess
|
||||||
|
*/
|
||||||
|
requestReader = theRequest.getServletRequest().getReader();
|
||||||
|
String body = IOUtils.toString(requestReader);
|
||||||
|
encoding = MethodUtil.detectEncodingNoDefault(body);
|
||||||
|
if (encoding == null) {
|
||||||
|
String msg = getContext().getLocalizer().getMessage(BaseOutcomeReturningMethodBinding.class, "noContentTypeInRequest", getResourceOrSystemOperationType());
|
||||||
|
throw new InvalidRequestException(msg);
|
||||||
|
} else {
|
||||||
|
requestReader = new StringReader(body);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String msg = getContext().getLocalizer().getMessage(BaseOutcomeReturningMethodBinding.class, "invalidContentTypeInRequest", ctValue, getResourceOrSystemOperationType());
|
||||||
|
throw new InvalidRequestException(msg);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
requestReader = theRequest.getServletRequest().getReader();
|
||||||
|
}
|
||||||
|
|
||||||
IParser parser = encoding.newParser(getContext());
|
IParser parser = encoding.newParser(getContext());
|
||||||
BufferedReader requestReader = theRequest.getServletRequest().getReader();
|
|
||||||
|
|
||||||
Class<? extends IBaseResource> wantedResourceType = requestContainsResourceType();
|
Class<? extends IBaseResource> wantedResourceType = requestContainsResourceType();
|
||||||
IResource retVal;
|
IResource retVal;
|
||||||
|
@ -296,14 +333,14 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subclasses may override to provide a specific resource type that this method wants
|
* Subclasses may override to provide a specific resource type that this method wants as a parameter
|
||||||
* as a parameter
|
|
||||||
*/
|
*/
|
||||||
protected Class<? extends IBaseResource> requestContainsResourceType() {
|
protected Class<? extends IBaseResource> requestContainsResourceType() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void streamOperationOutcome(BaseServerResponseException theE, RestfulServer theServer, EncodingEnum theEncodingNotNull, HttpServletResponse theResponse, Request theRequest) throws IOException {
|
protected void streamOperationOutcome(BaseServerResponseException theE, RestfulServer theServer, EncodingEnum theEncodingNotNull, HttpServletResponse theResponse, Request theRequest)
|
||||||
|
throws IOException {
|
||||||
theResponse.setStatus(theE.getStatusCode());
|
theResponse.setStatus(theE.getStatusCode());
|
||||||
|
|
||||||
theServer.addHeadersToResponse(theResponse);
|
theServer.addHeadersToResponse(theResponse);
|
||||||
|
|
|
@ -248,15 +248,26 @@ public class MethodUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static EncodingEnum detectEncoding(String theBody) {
|
public static EncodingEnum detectEncoding(String theBody) {
|
||||||
for (int i = 0; i < theBody.length(); i++) {
|
EncodingEnum retVal = detectEncodingNoDefault(theBody);
|
||||||
|
if (retVal == null) {
|
||||||
|
retVal = EncodingEnum.XML;
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EncodingEnum detectEncodingNoDefault(String theBody) {
|
||||||
|
EncodingEnum retVal = null;
|
||||||
|
for (int i = 0; i < theBody.length() && retVal == null; i++) {
|
||||||
switch (theBody.charAt(i)) {
|
switch (theBody.charAt(i)) {
|
||||||
case '<':
|
case '<':
|
||||||
return EncodingEnum.XML;
|
retVal = EncodingEnum.XML;
|
||||||
|
break;
|
||||||
case '{':
|
case '{':
|
||||||
return EncodingEnum.JSON;
|
retVal = EncodingEnum.JSON;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return EncodingEnum.XML;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void extractDescription(SearchParameter theParameter, Annotation[] theAnnotations) {
|
public static void extractDescription(SearchParameter theParameter, Annotation[] theAnnotations) {
|
||||||
|
|
|
@ -195,9 +195,18 @@ public class RestfulServerUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static EncodingEnum determineRequestEncoding(Request theReq) {
|
public static EncodingEnum determineRequestEncoding(Request theReq) {
|
||||||
|
EncodingEnum retVal = determineRequestEncodingNoDefault(theReq);
|
||||||
|
if (retVal != null) {
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
return EncodingEnum.XML;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EncodingEnum determineRequestEncodingNoDefault(Request theReq) {
|
||||||
|
EncodingEnum retVal = null;
|
||||||
Enumeration<String> acceptValues = theReq.getServletRequest().getHeaders(Constants.HEADER_CONTENT_TYPE);
|
Enumeration<String> acceptValues = theReq.getServletRequest().getHeaders(Constants.HEADER_CONTENT_TYPE);
|
||||||
if (acceptValues != null) {
|
if (acceptValues != null) {
|
||||||
while (acceptValues.hasMoreElements()) {
|
while (acceptValues.hasMoreElements() && retVal == null) {
|
||||||
String nextAcceptHeaderValue = acceptValues.nextElement();
|
String nextAcceptHeaderValue = acceptValues.nextElement();
|
||||||
if (nextAcceptHeaderValue != null && isNotBlank(nextAcceptHeaderValue)) {
|
if (nextAcceptHeaderValue != null && isNotBlank(nextAcceptHeaderValue)) {
|
||||||
for (String nextPart : nextAcceptHeaderValue.split(",")) {
|
for (String nextPart : nextAcceptHeaderValue.split(",")) {
|
||||||
|
@ -209,16 +218,16 @@ public class RestfulServerUtils {
|
||||||
nextPart = nextPart.substring(0, scIdx);
|
nextPart = nextPart.substring(0, scIdx);
|
||||||
}
|
}
|
||||||
nextPart = nextPart.trim();
|
nextPart = nextPart.trim();
|
||||||
EncodingEnum retVal = Constants.FORMAT_VAL_TO_ENCODING.get(nextPart);
|
retVal = Constants.FORMAT_VAL_TO_ENCODING.get(nextPart);
|
||||||
if (retVal != null) {
|
if (retVal != null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return EncodingEnum.XML;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String createPagingLink(Set<Include> theIncludes, String theServerBase, String theSearchId, int theOffset, int theCount, EncodingEnum theResponseEncoding, boolean thePrettyPrint) {
|
public static String createPagingLink(Set<Include> theIncludes, String theServerBase, String theSearchId, int theOffset, int theCount, EncodingEnum theResponseEncoding, boolean thePrettyPrint) {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -12,6 +12,9 @@ ca.uhn.fhir.rest.client.GenericClient.cannotDetermineResourceTypeFromUri=Unable
|
||||||
ca.uhn.fhir.rest.client.RestfulClientFactory.failedToRetrieveConformance=Failed to retrieve the server's metadata statement during client initialization. URL used was: {0}
|
ca.uhn.fhir.rest.client.RestfulClientFactory.failedToRetrieveConformance=Failed to retrieve the server's metadata statement during client initialization. URL used was: {0}
|
||||||
ca.uhn.fhir.rest.client.RestfulClientFactory.wrongVersionInConformance=The server at base URL "{0}" returned a conformance statement indicating that it supports FHIR version "{1}" which corresponds to {2}, but this client is configured to use {3} (via the FhirContext).
|
ca.uhn.fhir.rest.client.RestfulClientFactory.wrongVersionInConformance=The server at base URL "{0}" returned a conformance statement indicating that it supports FHIR version "{1}" which corresponds to {2}, but this client is configured to use {3} (via the FhirContext).
|
||||||
|
|
||||||
|
ca.uhn.fhir.rest.method.BaseOutcomeReturningMethodBinding.noContentTypeInRequest=No Content-Type header was provided in the request. This is required for "{0}" operation
|
||||||
|
ca.uhn.fhir.rest.method.BaseOutcomeReturningMethodBinding.invalidContentTypeInRequest=Incorrect Content-Type header value of "{0}" was provided in the request. A FHIR Content-Type is required for "{1}" operation
|
||||||
|
|
||||||
ca.uhn.fhir.rest.method.OperationMethodBinding.methodNotSupported=HTTP Method {0} is not allowed for this operation. Allowed method(s): {1}
|
ca.uhn.fhir.rest.method.OperationMethodBinding.methodNotSupported=HTTP Method {0} is not allowed for this operation. Allowed method(s): {1}
|
||||||
ca.uhn.fhir.rest.method.OperationParamBinder.urlParamNotPrimitive=Can not invoke operation {0} using HTTP GET because parameter {1} is not a primitive datatype
|
ca.uhn.fhir.rest.method.OperationParamBinder.urlParamNotPrimitive=Can not invoke operation {0} using HTTP GET because parameter {1} is not a primitive datatype
|
||||||
ca.uhn.fhir.rest.method.IncludeParameter.invalidIncludeNameInRequest=Invalid {2} parameter value: "{0}". Valid values are: {1}
|
ca.uhn.fhir.rest.method.IncludeParameter.invalidIncludeNameInRequest=Invalid {2} parameter value: "{0}". Valid values are: {1}
|
||||||
|
|
|
@ -9,12 +9,14 @@ import java.util.List;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.http.HttpEntity;
|
||||||
import org.apache.http.HttpResponse;
|
import org.apache.http.HttpResponse;
|
||||||
import org.apache.http.NameValuePair;
|
import org.apache.http.NameValuePair;
|
||||||
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
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.client.methods.HttpPost;
|
import org.apache.http.client.methods.HttpPost;
|
||||||
|
import org.apache.http.entity.ByteArrayEntity;
|
||||||
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;
|
||||||
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||||
|
@ -38,10 +40,13 @@ import ca.uhn.fhir.model.dstu.resource.Observation;
|
||||||
import ca.uhn.fhir.model.dstu.resource.Patient;
|
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||||
import ca.uhn.fhir.model.primitive.IdDt;
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Create;
|
||||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||||
import ca.uhn.fhir.rest.annotation.OptionalParam;
|
import ca.uhn.fhir.rest.annotation.OptionalParam;
|
||||||
import ca.uhn.fhir.rest.annotation.RequiredParam;
|
import ca.uhn.fhir.rest.annotation.RequiredParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||||
import ca.uhn.fhir.rest.annotation.Search;
|
import ca.uhn.fhir.rest.annotation.Search;
|
||||||
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
import ca.uhn.fhir.rest.client.IGenericClient;
|
import ca.uhn.fhir.rest.client.IGenericClient;
|
||||||
import ca.uhn.fhir.rest.param.ReferenceParam;
|
import ca.uhn.fhir.rest.param.ReferenceParam;
|
||||||
import ca.uhn.fhir.rest.param.StringOrListParam;
|
import ca.uhn.fhir.rest.param.StringOrListParam;
|
||||||
|
@ -233,6 +238,75 @@ public class SearchTest {
|
||||||
assertEquals("IDAAA (identifier123)", bundle.getEntries().get(0).getTitle().getValue());
|
assertEquals("IDAAA (identifier123)", bundle.getEntries().get(0).getTitle().getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See #164
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testSearchByPostWithParamsInBodyAndUrl() throws Exception {
|
||||||
|
HttpPost filePost = new HttpPost("http://localhost:" + ourPort + "/Patient/_search?name=Central");
|
||||||
|
|
||||||
|
// add parameters to the post method
|
||||||
|
List<NameValuePair> parameters = new ArrayList<NameValuePair>();
|
||||||
|
parameters.add(new BasicNameValuePair("_id", "aaa"));
|
||||||
|
|
||||||
|
UrlEncodedFormEntity sendentity = new UrlEncodedFormEntity(parameters, "UTF-8");
|
||||||
|
filePost.setEntity(sendentity);
|
||||||
|
|
||||||
|
HttpResponse status = ourClient.execute(filePost);
|
||||||
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
ourLog.info(responseContent);
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
|
||||||
|
Bundle bundle = ourCtx.newXmlParser().parseBundle(responseContent);
|
||||||
|
assertEquals(1, bundle.getEntries().size());
|
||||||
|
|
||||||
|
Patient p = bundle.getResources(Patient.class).get(0);
|
||||||
|
assertEquals("idaaa", p.getName().get(0).getFamilyAsSingleString());
|
||||||
|
assertEquals("nameCentral", p.getName().get(1).getFamilyAsSingleString());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See #164
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testSearchByPostWithInvalidPostUrl() throws Exception {
|
||||||
|
HttpPost filePost = new HttpPost("http://localhost:" + ourPort + "/Patient?name=Central"); // should end with _search
|
||||||
|
|
||||||
|
// add parameters to the post method
|
||||||
|
List<NameValuePair> parameters = new ArrayList<NameValuePair>();
|
||||||
|
parameters.add(new BasicNameValuePair("_id", "aaa"));
|
||||||
|
|
||||||
|
UrlEncodedFormEntity sendentity = new UrlEncodedFormEntity(parameters, "UTF-8");
|
||||||
|
filePost.setEntity(sendentity);
|
||||||
|
|
||||||
|
HttpResponse status = ourClient.execute(filePost);
|
||||||
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
ourLog.info(responseContent);
|
||||||
|
assertEquals(400, status.getStatusLine().getStatusCode());
|
||||||
|
assertThat(responseContent, containsString("<details value=\"Incorrect Content-Type header value of "application/x-www-form-urlencoded; charset=UTF-8" was provided in the request. This is required for "CREATE" operation\"/>"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See #164
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testSearchByPostWithMissingContentType() throws Exception {
|
||||||
|
HttpPost filePost = new HttpPost("http://localhost:" + ourPort + "/Patient?name=Central"); // should end with _search
|
||||||
|
|
||||||
|
HttpEntity sendentity = new ByteArrayEntity(new byte[] {1,2,3,4} );
|
||||||
|
filePost.setEntity(sendentity);
|
||||||
|
|
||||||
|
HttpResponse status = ourClient.execute(filePost);
|
||||||
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
ourLog.info(responseContent);
|
||||||
|
assertEquals(400, status.getStatusLine().getStatusCode());
|
||||||
|
assertThat(responseContent, containsString("<details value=\"No Content-Type header was provided in the request. This is required for "CREATE" operation\"/>"));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSearchCompartment() throws Exception {
|
public void testSearchCompartment() throws Exception {
|
||||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/fooCompartment");
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/fooCompartment");
|
||||||
|
@ -354,6 +428,14 @@ public class SearchTest {
|
||||||
*/
|
*/
|
||||||
public static class DummyPatientResourceProvider implements IResourceProvider {
|
public static class DummyPatientResourceProvider implements IResourceProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only needed for #164
|
||||||
|
*/
|
||||||
|
@Create
|
||||||
|
public MethodOutcome create(@ResourceParam Patient thePatient) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
|
||||||
@Search(compartmentName = "fooCompartment")
|
@Search(compartmentName = "fooCompartment")
|
||||||
public List<Patient> compartment(@IdParam IdDt theId) {
|
public List<Patient> compartment(@IdParam IdDt theId) {
|
||||||
ArrayList<Patient> retVal = new ArrayList<Patient>();
|
ArrayList<Patient> retVal = new ArrayList<Patient>();
|
||||||
|
@ -383,7 +465,7 @@ public class SearchTest {
|
||||||
|
|
||||||
|
|
||||||
@Search
|
@Search
|
||||||
public List<Patient> findPatient(@RequiredParam(name = "_id") StringParam theParam) {
|
public List<Patient> findPatient(@RequiredParam(name = "_id") StringParam theParam, @OptionalParam(name="name") StringParam theName) {
|
||||||
ArrayList<Patient> retVal = new ArrayList<Patient>();
|
ArrayList<Patient> retVal = new ArrayList<Patient>();
|
||||||
|
|
||||||
Patient patient = new Patient();
|
Patient patient = new Patient();
|
||||||
|
@ -391,6 +473,7 @@ public class SearchTest {
|
||||||
patient.addIdentifier("system", "identifier123");
|
patient.addIdentifier("system", "identifier123");
|
||||||
if (theParam != null) {
|
if (theParam != null) {
|
||||||
patient.addName().addFamily("id" + theParam.getValue());
|
patient.addName().addFamily("id" + theParam.getValue());
|
||||||
|
patient.addName().addFamily("name" + theName.getValue());
|
||||||
}
|
}
|
||||||
retVal.add(patient);
|
retVal.add(patient);
|
||||||
return retVal;
|
return retVal;
|
||||||
|
|
|
@ -183,6 +183,10 @@
|
||||||
default bean name expected in other parts of the Spring framework.
|
default bean name expected in other parts of the Spring framework.
|
||||||
Thanks to Mohammad Jafari for the suggestion!
|
Thanks to Mohammad Jafari for the suggestion!
|
||||||
</action>
|
</action>
|
||||||
|
<action type="add" issue="164">
|
||||||
|
Improve error message when a user tries to perform a create/update with an invalid
|
||||||
|
or missing Content-Type header.
|
||||||
|
</action>
|
||||||
</release>
|
</release>
|
||||||
<release version="0.9" date="2015-Mar-14">
|
<release version="0.9" date="2015-Mar-14">
|
||||||
<action type="add">
|
<action type="add">
|
||||||
|
|
Loading…
Reference in New Issue