Fix #374 - Include ETag and Last-Modified headers in response
This commit is contained in:
parent
a2954ef181
commit
39a96f0258
|
@ -24,6 +24,7 @@ import java.io.IOException;
|
|||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -45,9 +46,11 @@ import ca.uhn.fhir.rest.api.MethodOutcome;
|
|||
import ca.uhn.fhir.rest.api.PreferReturnEnum;
|
||||
import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.SummaryEnum;
|
||||
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.IRestfulResponse;
|
||||
import ca.uhn.fhir.rest.server.IRestfulServer;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.RestfulServerUtils;
|
||||
|
@ -58,6 +61,8 @@ import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
|||
abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<MethodOutcome> {
|
||||
static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseOutcomeReturningMethodBinding.class);
|
||||
|
||||
private static EnumSet<RestOperationTypeEnum> ourOperationsWhichAllowPreferHeader = EnumSet.of(RestOperationTypeEnum.CREATE, RestOperationTypeEnum.UPDATE);
|
||||
|
||||
private boolean myReturnVoid;
|
||||
|
||||
public BaseOutcomeReturningMethodBinding(Method theMethod, FhirContext theContext, Class<?> theMethodAnnotation, Object theProvider) {
|
||||
|
@ -89,6 +94,43 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
|
|||
*/
|
||||
protected abstract String getMatchingOperation();
|
||||
|
||||
private int getOperationStatus(MethodOutcome response) {
|
||||
switch (getRestOperationType()) {
|
||||
case CREATE:
|
||||
if (response == null) {
|
||||
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())) {
|
||||
return Constants.STATUS_HTTP_201_CREATED;
|
||||
} else {
|
||||
return Constants.STATUS_HTTP_200_OK;
|
||||
}
|
||||
|
||||
case UPDATE:
|
||||
if (response == null || response.getCreated() == null || Boolean.FALSE.equals(response.getCreated())) {
|
||||
return Constants.STATUS_HTTP_200_OK;
|
||||
} else {
|
||||
return Constants.STATUS_HTTP_201_CREATED;
|
||||
}
|
||||
|
||||
case VALIDATE:
|
||||
case DELETE:
|
||||
default:
|
||||
if (response == null) {
|
||||
if (isReturnVoid() == false) {
|
||||
throw new InternalErrorException("Method " + getMethod().getName() + " in type " + getMethod().getDeclaringClass().getCanonicalName() + " returned null");
|
||||
}
|
||||
return Constants.STATUS_HTTP_204_NO_CONTENT;
|
||||
} else {
|
||||
if (response.getOperationOutcome() == null) {
|
||||
return Constants.STATUS_HTTP_204_NO_CONTENT;
|
||||
} else {
|
||||
return Constants.STATUS_HTTP_200_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean incomingServerRequestMatchesMethod(RequestDetails theRequest) {
|
||||
Set<RequestTypeEnum> allowableRequestTypes = provideAllowableRequestTypes();
|
||||
|
@ -156,50 +198,18 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
|
|||
|
||||
return returnResponse(theServer, theRequest, response, outcome, resource);
|
||||
}
|
||||
|
||||
private int getOperationStatus(MethodOutcome response) {
|
||||
switch (getRestOperationType()) {
|
||||
case CREATE:
|
||||
if (response == null) {
|
||||
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())) {
|
||||
return Constants.STATUS_HTTP_201_CREATED;
|
||||
} else {
|
||||
return Constants.STATUS_HTTP_200_OK;
|
||||
}
|
||||
|
||||
case UPDATE:
|
||||
if (response == null || response.getCreated() == null || Boolean.FALSE.equals(response.getCreated())) {
|
||||
return Constants.STATUS_HTTP_200_OK;
|
||||
} else {
|
||||
return Constants.STATUS_HTTP_201_CREATED;
|
||||
}
|
||||
|
||||
case VALIDATE:
|
||||
case DELETE:
|
||||
default:
|
||||
if (response == null) {
|
||||
if (isReturnVoid() == false) {
|
||||
throw new InternalErrorException("Method " + getMethod().getName() + " in type " + getMethod().getDeclaringClass().getCanonicalName() + " returned null");
|
||||
}
|
||||
return Constants.STATUS_HTTP_204_NO_CONTENT;
|
||||
} else {
|
||||
if (response.getOperationOutcome() == null) {
|
||||
return Constants.STATUS_HTTP_204_NO_CONTENT;
|
||||
} else {
|
||||
return Constants.STATUS_HTTP_200_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
public boolean isReturnVoid() {
|
||||
return myReturnVoid;
|
||||
}
|
||||
|
||||
protected abstract Set<RequestTypeEnum> provideAllowableRequestTypes();
|
||||
|
||||
private Object returnResponse(IRestfulServer<?> theServer, RequestDetails theRequest, MethodOutcome response, IBaseResource originalOutcome, IBaseResource resource) throws IOException {
|
||||
boolean allowPrefer = false;
|
||||
int operationStatus = getOperationStatus(response);
|
||||
IBaseResource outcome = originalOutcome;
|
||||
|
||||
if (EnumSet.of(RestOperationTypeEnum.CREATE, RestOperationTypeEnum.UPDATE).contains(getRestOperationType())) {
|
||||
if (ourOperationsWhichAllowPreferHeader.contains(getRestOperationType())) {
|
||||
allowPrefer = true;
|
||||
}
|
||||
|
||||
|
@ -220,16 +230,33 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
IRestfulResponse restfulResponse = theRequest.getResponse();
|
||||
|
||||
if (response != null) {
|
||||
if (response.getResource() != null) {
|
||||
restfulResponse.setOperationResourceLastUpdated(RestfulServerUtils.extractLastUpdatedFromResource(response.getResource()));
|
||||
}
|
||||
|
||||
IIdType responseId = response.getId();
|
||||
if (responseId != null && responseId.getResourceType() == null && responseId.hasIdPart()) {
|
||||
responseId = responseId.withResourceType(getResourceName());
|
||||
}
|
||||
|
||||
if (responseId != null) {
|
||||
String serverBase = theRequest.getFhirServerBase();
|
||||
responseId = RestfulServerUtils.fullyQualifyResourceIdOrReturnNull(theServer, resource, serverBase, responseId);
|
||||
restfulResponse.setOperationResourceId(responseId);
|
||||
}
|
||||
}
|
||||
|
||||
return theRequest.getResponse().returnResponse(ParseAction.create(outcome), operationStatus, allowPrefer, response, getResourceName());
|
||||
boolean prettyPrint = RestfulServerUtils.prettyPrintResponse(theServer, theRequest);
|
||||
Set<SummaryEnum> summaryMode = Collections.emptySet();
|
||||
|
||||
return restfulResponse.streamResponseAsResource(outcome, prettyPrint, summaryMode, operationStatus, theRequest.isRespondGzip(), true);
|
||||
// return theRequest.getResponse().returnResponse(ParseAction.create(outcome), operationStatus, allowPrefer, response, getResourceName());
|
||||
}
|
||||
|
||||
public boolean isReturnVoid() {
|
||||
return myReturnVoid;
|
||||
}
|
||||
|
||||
protected abstract Set<RequestTypeEnum> provideAllowableRequestTypes();
|
||||
|
||||
protected void streamOperationOutcome(BaseServerResponseException theE, RestfulServer theServer, EncodingEnum theEncodingNotNull, HttpServletResponse theResponse, RequestDetails theRequest) throws IOException {
|
||||
theResponse.setStatus(theE.getStatusCode());
|
||||
|
||||
|
|
|
@ -23,10 +23,13 @@ package ca.uhn.fhir.rest.server;
|
|||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.io.Writer;
|
||||
import java.util.Date;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseBinary;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
|
@ -35,10 +38,14 @@ import ca.uhn.fhir.rest.method.ParseAction;
|
|||
|
||||
public interface IRestfulResponse {
|
||||
|
||||
Object streamResponseAsResource(IBaseResource resource, boolean prettyPrint, Set<SummaryEnum> summaryMode, int operationStatus, boolean respondGzip, boolean addContentLocationHeader) throws IOException;
|
||||
Object streamResponseAsResource(IBaseResource theActualResourceToReturn, boolean prettyPrint, Set<SummaryEnum> summaryMode, int operationStatus, boolean respondGzip, boolean addContentLocationHeader) throws IOException;
|
||||
|
||||
Object streamResponseAsBundle(Bundle bundle, Set<SummaryEnum> summaryMode, boolean respondGzip, boolean requestIsBrowser) throws IOException;
|
||||
|
||||
/**
|
||||
* This is only used for DSTU1 getTags operations, so it can be removed at some point when we
|
||||
* drop DSTU1
|
||||
*/
|
||||
Object returnResponse(ParseAction<?> outcome, int operationStatus, boolean allowPrefer, MethodOutcome response, String resourceName) throws IOException;
|
||||
|
||||
Writer getResponseWriter(int statusCode, String contentType, String charset, boolean respondGzip) throws UnsupportedEncodingException, IOException;
|
||||
|
@ -49,4 +56,8 @@ public interface IRestfulResponse {
|
|||
|
||||
Object sendAttachmentResponse(IBaseBinary bin, int stausCode, String contentType) throws IOException;
|
||||
|
||||
void setOperationResourceLastUpdated(IPrimitiveType<Date> theOperationResourceLastUpdated);
|
||||
|
||||
void setOperationResourceId(IIdType theOperationResourceId);
|
||||
|
||||
}
|
||||
|
|
|
@ -21,10 +21,13 @@ package ca.uhn.fhir.rest.server;
|
|||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.rest.api.SummaryEnum;
|
||||
|
@ -32,27 +35,15 @@ import ca.uhn.fhir.rest.method.RequestDetails;
|
|||
|
||||
public abstract class RestfulResponse<T extends RequestDetails> implements IRestfulResponse {
|
||||
|
||||
private T theRequestDetails;
|
||||
private IIdType myOperationResourceId;
|
||||
private IPrimitiveType<Date> myOperationResourceLastUpdated;
|
||||
private ConcurrentHashMap<String, String> theHeaders = new ConcurrentHashMap<String, String>();
|
||||
private T theRequestDetails;
|
||||
|
||||
public RestfulResponse(T requestDetails) {
|
||||
this.theRequestDetails = requestDetails;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Object streamResponseAsResource(IBaseResource resource, boolean prettyPrint, Set<SummaryEnum> summaryMode,
|
||||
int statusCode, boolean respondGzip, boolean addContentLocationHeader)
|
||||
throws IOException {
|
||||
return RestfulServerUtils.streamResponseAsResource(theRequestDetails.getServer(), resource, summaryMode, statusCode, addContentLocationHeader, respondGzip, getRequestDetails());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object streamResponseAsBundle(Bundle bundle, Set<SummaryEnum> summaryMode, boolean respondGzip, boolean requestIsBrowser)
|
||||
throws IOException {
|
||||
return RestfulServerUtils.streamResponseAsBundle(theRequestDetails.getServer(), bundle, summaryMode, respondGzip, getRequestDetails());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addHeader(String headerKey, String headerValue) {
|
||||
this.getHeaders().put(headerKey, headerValue);
|
||||
|
@ -74,6 +65,16 @@ public abstract class RestfulResponse<T extends RequestDetails> implements IRest
|
|||
return theRequestDetails;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOperationResourceId(IIdType theOperationResourceId) {
|
||||
myOperationResourceId = theOperationResourceId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOperationResourceLastUpdated(IPrimitiveType<Date> theOperationResourceLastUpdated) {
|
||||
myOperationResourceLastUpdated = theOperationResourceLastUpdated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the requestDetails
|
||||
* @param requestDetails the requestDetails to set
|
||||
|
@ -82,4 +83,18 @@ public abstract class RestfulResponse<T extends RequestDetails> implements IRest
|
|||
this.theRequestDetails = requestDetails;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object streamResponseAsBundle(Bundle bundle, Set<SummaryEnum> summaryMode, boolean respondGzip, boolean requestIsBrowser)
|
||||
throws IOException {
|
||||
return RestfulServerUtils.streamResponseAsBundle(theRequestDetails.getServer(), bundle, summaryMode, respondGzip, getRequestDetails());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Object streamResponseAsResource(IBaseResource resource, boolean prettyPrint, Set<SummaryEnum> summaryMode,
|
||||
int statusCode, boolean respondGzip, boolean addContentLocationHeader)
|
||||
throws IOException {
|
||||
return RestfulServerUtils.streamResponseAsResource(theRequestDetails.getServer(), resource, summaryMode, statusCode, addContentLocationHeader, respondGzip, getRequestDetails(), myOperationResourceId, myOperationResourceLastUpdated);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -46,8 +46,10 @@ import org.hl7.fhir.instance.model.api.IAnyResource;
|
|||
import org.hl7.fhir.instance.model.api.IBaseBinary;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.api.Include;
|
||||
|
@ -113,8 +115,7 @@ public class RestfulServerUtils {
|
|||
}
|
||||
}
|
||||
|
||||
public static String createPagingLink(Set<Include> theIncludes, String theServerBase, String theSearchId, int theOffset, int theCount, EncodingEnum theResponseEncoding, boolean thePrettyPrint,
|
||||
BundleTypeEnum theBundleType) {
|
||||
public static String createPagingLink(Set<Include> theIncludes, String theServerBase, String theSearchId, int theOffset, int theCount, EncodingEnum theResponseEncoding, boolean thePrettyPrint, BundleTypeEnum theBundleType) {
|
||||
try {
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append(theServerBase);
|
||||
|
@ -204,7 +205,8 @@ public class RestfulServerUtils {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns null if the request doesn't express that it wants FHIR. If it expresses that it wants XML and JSON equally, returns thePrefer.
|
||||
* Returns null if the request doesn't express that it wants FHIR. If it expresses that it wants XML and JSON
|
||||
* equally, returns thePrefer.
|
||||
*/
|
||||
public static EncodingEnum determineResponseEncodingNoDefault(RequestDetails theReq, EncodingEnum thePrefer) {
|
||||
String[] format = theReq.getParameters().get(Constants.PARAM_FORMAT);
|
||||
|
@ -230,7 +232,7 @@ public class RestfulServerUtils {
|
|||
if ("Binary".equals(theReq.getResourceName())) {
|
||||
contentTypeToEncoding = EncodingEnum.getContentTypeToEncodingStrict();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* The Accept header is kind of ridiculous, e.g.
|
||||
*/
|
||||
|
@ -239,8 +241,8 @@ public class RestfulServerUtils {
|
|||
List<String> acceptValues = theReq.getHeaders(Constants.HEADER_ACCEPT);
|
||||
float bestQ = -1f;
|
||||
EncodingEnum retVal = null;
|
||||
if (acceptValues != null) {
|
||||
for (String nextAcceptHeaderValue : acceptValues) {
|
||||
if (acceptValues != null) {
|
||||
for (String nextAcceptHeaderValue : acceptValues) {
|
||||
StringTokenizer tok = new StringTokenizer(nextAcceptHeaderValue, ",");
|
||||
while (tok.hasMoreTokens()) {
|
||||
String nextToken = tok.nextToken();
|
||||
|
@ -348,7 +350,8 @@ public class RestfulServerUtils {
|
|||
}
|
||||
|
||||
/**
|
||||
* Determine whether a response should be given in JSON or XML format based on the incoming HttpServletRequest's <code>"_format"</code> parameter and <code>"Accept:"</code> HTTP header.
|
||||
* Determine whether a response should be given in JSON or XML format based on the incoming HttpServletRequest's
|
||||
* <code>"_format"</code> parameter and <code>"Accept:"</code> HTTP header.
|
||||
*/
|
||||
public static EncodingEnum determineResponseEncodingWithDefault(RequestDetails theReq) {
|
||||
EncodingEnum retVal = determineResponseEncodingNoDefault(theReq, theReq.getServer().getDefaultResponseEncoding());
|
||||
|
@ -365,7 +368,8 @@ public class RestfulServerUtils {
|
|||
|
||||
if (retVal == null) {
|
||||
/*
|
||||
* HAPI originally supported a custom parameter called _narrative, but this has been superceded by an official parameter called _summary
|
||||
* HAPI originally supported a custom parameter called _narrative, but this has been superceded by an official
|
||||
* parameter called _summary
|
||||
*/
|
||||
String[] narrative = requestParams.get(Constants.PARAM_NARRATIVE);
|
||||
if (narrative != null && narrative.length > 0) {
|
||||
|
@ -439,8 +443,7 @@ public class RestfulServerUtils {
|
|||
try {
|
||||
q = Float.parseFloat(value);
|
||||
q = Math.max(q, 0.0f);
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
} catch (NumberFormatException e) {
|
||||
ourLog.debug("Invalid Accept header q value: {}", value);
|
||||
}
|
||||
}
|
||||
|
@ -524,8 +527,7 @@ public class RestfulServerUtils {
|
|||
return prettyPrint;
|
||||
}
|
||||
|
||||
public static Object streamResponseAsBundle(IRestfulServerDefaults theServer, Bundle bundle, Set<SummaryEnum> theSummaryMode, boolean respondGzip, RequestDetails theRequestDetails)
|
||||
throws IOException {
|
||||
public static Object streamResponseAsBundle(IRestfulServerDefaults theServer, Bundle bundle, Set<SummaryEnum> theSummaryMode, boolean respondGzip, RequestDetails theRequestDetails) throws IOException {
|
||||
|
||||
int status = 200;
|
||||
|
||||
|
@ -543,34 +545,42 @@ public class RestfulServerUtils {
|
|||
parser.setEncodeElements(TEXT_ENCODE_ELEMENTS);
|
||||
}
|
||||
parser.encodeBundleToWriter(bundle, writer);
|
||||
}
|
||||
catch (Exception e) {
|
||||
} catch (Exception e) {
|
||||
//always send a response, even if the parsing went wrong
|
||||
}
|
||||
return theRequestDetails.getResponse().sendWriterResponse(status, contentType, charset, writer);
|
||||
}
|
||||
|
||||
public static Object streamResponseAsResource(IRestfulServerDefaults theServer, IBaseResource theResource, Set<SummaryEnum> theSummaryMode,
|
||||
int stausCode, boolean theAddContentLocationHeader, boolean respondGzip,
|
||||
RequestDetails theRequestDetails)
|
||||
throws IOException {
|
||||
public static Object streamResponseAsResource(IRestfulServerDefaults theServer, IBaseResource theResource, Set<SummaryEnum> theSummaryMode, int stausCode, boolean theAddContentLocationHeader, boolean respondGzip, RequestDetails theRequestDetails) throws IOException {
|
||||
return streamResponseAsResource(theServer, theResource, theSummaryMode, stausCode, theAddContentLocationHeader, respondGzip, theRequestDetails, null, null);
|
||||
}
|
||||
|
||||
|
||||
public static Object streamResponseAsResource(IRestfulServerDefaults theServer, IBaseResource theResource, Set<SummaryEnum> theSummaryMode, int stausCode, boolean theAddContentLocationHeader, boolean respondGzip, RequestDetails theRequestDetails, IIdType theOperationResourceId, IPrimitiveType<Date> theOperationResourceLastUpdated) throws IOException {
|
||||
IRestfulResponse restUtil = theRequestDetails.getResponse();
|
||||
|
||||
// Determine response encoding
|
||||
EncodingEnum responseEncoding = RestfulServerUtils.determineResponseEncodingNoDefault(theRequestDetails,
|
||||
theServer.getDefaultResponseEncoding());
|
||||
EncodingEnum responseEncoding = RestfulServerUtils.determineResponseEncodingNoDefault(theRequestDetails, theServer.getDefaultResponseEncoding());
|
||||
|
||||
String serverBase = theRequestDetails.getFhirServerBase();
|
||||
if (theAddContentLocationHeader && theResource.getIdElement() != null && theResource.getIdElement().hasIdPart()
|
||||
&& isNotBlank(serverBase)) {
|
||||
String resName = theServer.getFhirContext().getResourceDefinition(theResource).getName();
|
||||
IIdType fullId = theResource.getIdElement().withServerBase(serverBase, resName);
|
||||
IIdType fullId = null;
|
||||
if (theOperationResourceId != null) {
|
||||
fullId = theOperationResourceId;
|
||||
} else if (theResource != null) {
|
||||
if (theResource.getIdElement() != null) {
|
||||
IIdType resourceId = theResource.getIdElement();
|
||||
fullId = fullyQualifyResourceIdOrReturnNull(theServer, theResource, serverBase, resourceId);
|
||||
}
|
||||
}
|
||||
|
||||
if (theAddContentLocationHeader && fullId != null) {
|
||||
restUtil.addHeader(Constants.HEADER_LOCATION, fullId.getValue());
|
||||
restUtil.addHeader(Constants.HEADER_CONTENT_LOCATION, fullId.getValue());
|
||||
}
|
||||
|
||||
if (theServer.getETagSupport() == ETagSupportEnum.ENABLED) {
|
||||
if (theResource.getIdElement().hasVersionIdPart()) {
|
||||
restUtil.addHeader(Constants.HEADER_ETAG, "W/\"" + theResource.getIdElement().getVersionIdPart() + '"');
|
||||
if (fullId != null && fullId.hasVersionIdPart()) {
|
||||
restUtil.addHeader(Constants.HEADER_ETAG, "W/\"" + fullId.getVersionIdPart() + '"');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -595,7 +605,8 @@ public class RestfulServerUtils {
|
|||
boolean encodingDomainResourceAsText = theSummaryMode.contains(SummaryEnum.TEXT);
|
||||
if (encodingDomainResourceAsText) {
|
||||
/*
|
||||
* If the user requests "text" for a bundle, only suppress the non text elements in the Element.entry.resource parts, we're not streaming just the narrative as HTML (since bundles don't even
|
||||
* If the user requests "text" for a bundle, only suppress the non text elements in the Element.entry.resource
|
||||
* parts, we're not streaming just the narrative as HTML (since bundles don't even
|
||||
* have one)
|
||||
*/
|
||||
if ("Bundle".equals(theServer.getFhirContext().getResourceDefinition(theResource).getName())) {
|
||||
|
@ -603,19 +614,25 @@ public class RestfulServerUtils {
|
|||
}
|
||||
}
|
||||
|
||||
if (encodingDomainResourceAsText) {
|
||||
contentType = Constants.CT_HTML;
|
||||
/*
|
||||
* Last-Modified header
|
||||
*/
|
||||
|
||||
IPrimitiveType<Date> lastUpdated;
|
||||
if (theOperationResourceLastUpdated != null) {
|
||||
lastUpdated = theOperationResourceLastUpdated;
|
||||
} else {
|
||||
contentType = responseEncoding.getResourceContentType();
|
||||
lastUpdated = extractLastUpdatedFromResource(theResource);
|
||||
}
|
||||
if (lastUpdated != null && lastUpdated.isEmpty() == false) {
|
||||
restUtil.addHeader(Constants.HEADER_LAST_MODIFIED, DateUtils.formatDate(lastUpdated.getValue()));
|
||||
}
|
||||
String charset = Constants.CHARSET_NAME_UTF8;
|
||||
|
||||
if (theResource instanceof IResource) {
|
||||
InstantDt lastUpdated = ResourceMetadataKeyEnum.UPDATED.get((IResource) theResource);
|
||||
if (lastUpdated != null && lastUpdated.isEmpty() == false) {
|
||||
restUtil.addHeader(Constants.HEADER_LAST_MODIFIED, DateUtils.formatDate(lastUpdated.getValue()));
|
||||
}
|
||||
|
||||
/*
|
||||
* Category header (DSTU1 only)
|
||||
*/
|
||||
|
||||
if (theResource instanceof IResource && theServer.getFhirContext().getVersion().getVersion() == FhirVersionEnum.DSTU1) {
|
||||
TagList list = (TagList) ((IResource) theResource).getResourceMetadata().get(ResourceMetadataKeyEnum.TAG_LIST);
|
||||
if (list != null) {
|
||||
for (Tag tag : list) {
|
||||
|
@ -624,25 +641,58 @@ public class RestfulServerUtils {
|
|||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Date lastUpdated = ((IAnyResource) theResource).getMeta().getLastUpdated();
|
||||
if (lastUpdated != null) {
|
||||
restUtil.addHeader(Constants.HEADER_LAST_MODIFIED, DateUtils.formatDate(lastUpdated));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Stream the response body
|
||||
*/
|
||||
|
||||
if (theResource == null) {
|
||||
contentType = null;
|
||||
} else if (encodingDomainResourceAsText) {
|
||||
contentType = Constants.CT_HTML;
|
||||
} else {
|
||||
contentType = responseEncoding.getResourceContentType();
|
||||
}
|
||||
String charset = Constants.CHARSET_NAME_UTF8;
|
||||
|
||||
Writer writer = restUtil.getResponseWriter(stausCode, contentType, charset, respondGzip);
|
||||
|
||||
if (encodingDomainResourceAsText && theResource instanceof IResource) {
|
||||
if (theResource == null) {
|
||||
// No response is being returned
|
||||
} else if (encodingDomainResourceAsText && theResource instanceof IResource) {
|
||||
writer.append(((IResource) theResource).getText().getDiv().getValueAsString());
|
||||
} else {
|
||||
IParser parser = getNewParser(theServer.getFhirContext(), theRequestDetails);
|
||||
parser.encodeResourceToWriter(theResource, writer);
|
||||
}
|
||||
|
||||
|
||||
return restUtil.sendWriterResponse(stausCode, contentType, charset, writer);
|
||||
}
|
||||
|
||||
public static IIdType fullyQualifyResourceIdOrReturnNull(IRestfulServerDefaults theServer, IBaseResource theResource, String theServerBase, IIdType theResourceId) {
|
||||
IIdType retVal = null;
|
||||
if (theResourceId.hasIdPart() && isNotBlank(theServerBase)) {
|
||||
String resName = theResourceId.getResourceType();
|
||||
if (theResource != null && isBlank(resName)) {
|
||||
resName = theServer.getFhirContext().getResourceDefinition(theResource).getName();
|
||||
}
|
||||
if (isNotBlank(resName)) {
|
||||
retVal = theResourceId.withServerBase(theServerBase, resName);
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public static IPrimitiveType<Date> extractLastUpdatedFromResource(IBaseResource theResource) {
|
||||
IPrimitiveType<Date> lastUpdated = null;
|
||||
if (theResource instanceof IResource) {
|
||||
lastUpdated = ResourceMetadataKeyEnum.UPDATED.get((IResource) theResource);
|
||||
} else if (theResource instanceof IAnyResource) {
|
||||
lastUpdated = new InstantDt(((IAnyResource) theResource).getMeta().getLastUpdated());
|
||||
}
|
||||
return lastUpdated;
|
||||
}
|
||||
|
||||
// static Integer tryToExtractNamedParameter(HttpServletRequest theRequest, String name) {
|
||||
// String countString = theRequest.getParameter(name);
|
||||
// Integer count = null;
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package ca.uhn.fhir.jaxrs.server.util;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JAX-RS Server
|
||||
|
@ -69,11 +71,15 @@ public class JaxRsResponse extends RestfulResponse<JaxRsRequest> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Response sendWriterResponse(int status, String contentType, String charset, Writer writer) {
|
||||
String charContentType = contentType + "; charset="
|
||||
+ StringUtils.defaultIfBlank(charset, Constants.CHARSET_NAME_UTF8);
|
||||
return buildResponse(status).header(Constants.HEADER_CONTENT_TYPE, charContentType).entity(writer.toString())
|
||||
.build();
|
||||
public Response sendWriterResponse(int theStatus, String theContentType, String theCharset, Writer theWriter) {
|
||||
ResponseBuilder builder = buildResponse(theStatus);
|
||||
if (isNotBlank(theContentType)) {
|
||||
String charContentType = theContentType + "; charset=" + StringUtils.defaultIfBlank(theCharset, Constants.CHARSET_NAME_UTF8);
|
||||
builder.header(Constants.HEADER_CONTENT_TYPE, charContentType);
|
||||
}
|
||||
builder.entity(theWriter.toString());
|
||||
Response retVal = builder.build();
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -47,9 +47,6 @@ import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
|||
import ca.uhn.fhir.util.PortUtil;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
|
||||
/**
|
||||
* Created by dsotnikov on 2/25/2014.
|
||||
*/
|
||||
public class CreateTest {
|
||||
private static CloseableHttpClient ourClient;
|
||||
private static final FhirContext ourCtx = FhirContext.forDstu1();
|
||||
|
@ -85,7 +82,7 @@ public class CreateTest {
|
|||
|
||||
assertEquals(201, status.getStatusLine().getStatusCode());
|
||||
assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("location").getValue());
|
||||
assertThat(status.getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue().toUpperCase(), StringContains.containsString("UTF-8"));
|
||||
assertNull(status.getFirstHeader(Constants.HEADER_CONTENT_TYPE));
|
||||
|
||||
assertThat(ourLastResourceBody, stringContainsInOrder("<Patient ", "<identifier>","<value value=\"001"));
|
||||
assertEquals(EncodingEnum.XML, ourLastEncoding);
|
||||
|
@ -176,7 +173,7 @@ public class CreateTest {
|
|||
|
||||
assertEquals(201, status.getStatusLine().getStatusCode());
|
||||
assertEquals("http://localhost:" + ourPort + "/Organization/001", status.getFirstHeader("location").getValue());
|
||||
assertThat(status.getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue(), StringContains.containsStringIgnoringCase("utf-8"));
|
||||
assertNull(status.getFirstHeader(Constants.HEADER_CONTENT_TYPE));
|
||||
|
||||
assertThat(ourLastResourceBody, stringContainsInOrder("\"resourceType\":\"Organization\"", "\"identifier\"","\"value\":\"001"));
|
||||
assertEquals(EncodingEnum.JSON, ourLastEncoding);
|
||||
|
|
|
@ -49,7 +49,7 @@ public class DeleteDstu2Test {
|
|||
|
||||
|
||||
@Test
|
||||
public void testUpdateWithConditionalUrl() throws Exception {
|
||||
public void testDeleteWithConditionalUrl() throws Exception {
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier().setValue("002");
|
||||
|
||||
|
@ -64,7 +64,7 @@ public class DeleteDstu2Test {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateWithoutConditionalUrl() throws Exception {
|
||||
public void testDeleteWithoutConditionalUrl() throws Exception {
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier().setValue("002");
|
||||
|
||||
|
@ -73,6 +73,7 @@ public class DeleteDstu2Test {
|
|||
HttpResponse status = ourClient.execute(httpPost);
|
||||
|
||||
assertEquals(204, status.getStatusLine().getStatusCode());
|
||||
assertNull(status.getFirstHeader(Constants.HEADER_CONTENT_TYPE));
|
||||
|
||||
assertEquals("Patient/2", ourLastIdParam.toUnqualified().getValue());
|
||||
assertNull(ourLastConditionalUrl);
|
||||
|
|
|
@ -3,9 +3,8 @@ package ca.uhn.fhir.rest.server;
|
|||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.emptyOrNullString;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.isEmptyOrNullString;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
@ -21,12 +20,15 @@ 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.instance.model.api.IBaseOperationOutcome;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.dstu2.resource.OperationOutcome;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.annotation.Create;
|
||||
|
@ -39,15 +41,21 @@ import ca.uhn.fhir.util.TestUtil;
|
|||
|
||||
public class PreferTest {
|
||||
private static CloseableHttpClient ourClient;
|
||||
|
||||
|
||||
private static FhirContext ourCtx = FhirContext.forDstu2();
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(PreferTest.class);
|
||||
private static int ourPort;
|
||||
private static Server ourServer;
|
||||
|
||||
|
||||
public static IBaseOperationOutcome ourReturnOperationOutcome;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
ourReturnOperationOutcome = null;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatePreferMinimal() throws Exception {
|
||||
public void testCreatePreferMinimalNoOperationOutcome() throws Exception {
|
||||
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier().setValue("002");
|
||||
|
@ -65,12 +73,41 @@ public class PreferTest {
|
|||
|
||||
assertEquals(Constants.STATUS_HTTP_201_CREATED, status.getStatusLine().getStatusCode());
|
||||
assertThat(responseContent, is(emptyOrNullString()));
|
||||
assertThat(status.getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue(), not(containsString("fhir")));
|
||||
// assertThat(status.getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue(), not(containsString("fhir")));
|
||||
assertNull(status.getFirstHeader(Constants.HEADER_CONTENT_TYPE));
|
||||
assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("location").getValue());
|
||||
assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue());
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatePreferMinimalWithOperationOutcome() throws Exception {
|
||||
|
||||
OperationOutcome oo = new OperationOutcome();
|
||||
oo.addIssue().setDiagnostics("DIAG");
|
||||
ourReturnOperationOutcome = oo;
|
||||
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier().setValue("002");
|
||||
|
||||
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient");
|
||||
httpPost.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RETURN + "=" + Constants.HEADER_PREFER_RETURN_MINIMAL);
|
||||
httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||
|
||||
HttpResponse status = ourClient.execute(httpPost);
|
||||
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
||||
ourLog.info("Response was:\n{}", responseContent);
|
||||
|
||||
assertEquals(Constants.STATUS_HTTP_201_CREATED, status.getStatusLine().getStatusCode());
|
||||
assertThat(responseContent, containsString("DIAG"));
|
||||
assertEquals("application/xml+fhir;charset=utf-8", status.getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue().toLowerCase().replace(" ", ""));
|
||||
assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("location").getValue());
|
||||
assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatePreferRepresentation() throws Exception {
|
||||
|
@ -94,10 +131,9 @@ public class PreferTest {
|
|||
assertEquals("<Patient xmlns=\"http://hl7.org/fhir\"><id value=\"001\"/><meta><versionId value=\"002\"/></meta></Patient>", responseContent);
|
||||
assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("location").getValue());
|
||||
assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue());
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testCreateWithNoPrefer() throws Exception {
|
||||
|
||||
|
@ -117,17 +153,15 @@ public class PreferTest {
|
|||
assertEquals(201, status.getStatusLine().getStatusCode());
|
||||
assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("location").getValue());
|
||||
assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue());
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() throws Exception {
|
||||
ourServer.stop();
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception {
|
||||
ourPort = PortUtil.findFreePort();
|
||||
|
@ -149,18 +183,20 @@ public class PreferTest {
|
|||
ourClient = builder.build();
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static class PatientProvider implements IResourceProvider {
|
||||
|
||||
@Create()
|
||||
public MethodOutcome createPatient(@ResourceParam Patient thePatient) {
|
||||
IdDt id = new IdDt("Patient/001/_history/002");
|
||||
MethodOutcome retVal = new MethodOutcome(id);
|
||||
|
||||
|
||||
Patient pt = new Patient();
|
||||
pt.setId(id);
|
||||
retVal.setResource(pt);
|
||||
|
||||
|
||||
retVal.setOperationOutcome(ourReturnOperationOutcome);
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
@ -173,11 +209,11 @@ public class PreferTest {
|
|||
public MethodOutcome updatePatient(@ResourceParam Patient thePatient, @IdParam IdDt theIdParam) {
|
||||
IdDt id = new IdDt("Patient/001/_history/002");
|
||||
MethodOutcome retVal = new MethodOutcome(id);
|
||||
|
||||
|
||||
Patient pt = new Patient();
|
||||
pt.setId(id);
|
||||
retVal.setResource(pt);
|
||||
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package ca.uhn.fhir.rest.server;
|
||||
|
||||
import static org.hamcrest.Matchers.blankOrNullString;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -27,8 +29,10 @@ import org.junit.Test;
|
|||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||
import ca.uhn.fhir.model.primitive.StringDt;
|
||||
import ca.uhn.fhir.rest.annotation.ConditionalUrlParam;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
|
@ -40,19 +44,17 @@ import ca.uhn.fhir.rest.api.MethodOutcome;
|
|||
import ca.uhn.fhir.util.PortUtil;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
|
||||
/**
|
||||
* Created by dsotnikov on 2/25/2014.
|
||||
*/
|
||||
public class UpdateConditionalTest {
|
||||
public class UpdateDstu2Test {
|
||||
private static CloseableHttpClient ourClient;
|
||||
private static FhirContext ourCtx = FhirContext.forDstu2();
|
||||
private static String ourLastConditionalUrl;
|
||||
private static IdDt ourLastId;
|
||||
private static IdDt ourLastIdParam;
|
||||
private static boolean ourLastRequestWasSearch;
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(UpdateConditionalTest.class);
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(UpdateDstu2Test.class);
|
||||
private static int ourPort;
|
||||
private static Server ourServer;
|
||||
private static InstantDt ourSetLastUpdated;
|
||||
|
||||
|
||||
@Before
|
||||
|
@ -113,6 +115,34 @@ public class UpdateConditionalTest {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateReturnsETagAndUpdate() throws Exception {
|
||||
|
||||
Patient patient = new Patient();
|
||||
patient.setId("123");
|
||||
patient.addIdentifier().setValue("002");
|
||||
ourSetLastUpdated = new InstantDt("2002-04-22T11:22:33.022Z");
|
||||
|
||||
HttpPut httpPost = new HttpPut("http://localhost:" + ourPort + "/Patient/123");
|
||||
httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||
|
||||
HttpResponse status = ourClient.execute(httpPost);
|
||||
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
||||
ourLog.info("Response was:\n{}", responseContent);
|
||||
ourLog.info("Response was:\n{}", status);
|
||||
|
||||
assertThat(responseContent, blankOrNullString());
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("location").getValue());
|
||||
assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue());
|
||||
assertEquals("W/\"002\"", status.getFirstHeader(Constants.HEADER_ETAG_LC).getValue());
|
||||
assertEquals("Mon, 22 Apr 2002 11:22:33 GMT", status.getFirstHeader(Constants.HEADER_LAST_MODIFIED_LOWERCASE).getValue());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateWithoutConditionalUrl() throws Exception {
|
||||
|
||||
|
@ -188,7 +218,12 @@ public class UpdateConditionalTest {
|
|||
ourLastConditionalUrl = theConditional;
|
||||
ourLastId = thePatient.getId();
|
||||
ourLastIdParam = theIdParam;
|
||||
return new MethodOutcome(new IdDt("Patient/001/_history/002"));
|
||||
MethodOutcome retVal = new MethodOutcome(new IdDt("Patient/001/_history/002"));
|
||||
|
||||
ResourceMetadataKeyEnum.UPDATED.put(thePatient, ourSetLastUpdated);
|
||||
|
||||
retVal.setResource(thePatient);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
}
|
|
@ -6,6 +6,8 @@ import static org.junit.Assert.assertNull;
|
|||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.entity.ByteArrayEntity;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
|
@ -39,7 +41,6 @@ public class CreateBinaryDstu3Test {
|
|||
private static String ourLastBinaryString;
|
||||
private static int ourPort;
|
||||
private static Server ourServer;
|
||||
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
|
@ -48,72 +49,80 @@ public class CreateBinaryDstu3Test {
|
|||
ourLastBinaryString = null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testRawBytesBinaryContentType() throws Exception {
|
||||
HttpPost post = new HttpPost("http://localhost:" + ourPort + "/Binary");
|
||||
post.setEntity(new ByteArrayEntity(new byte[] {0,1,2,3,4}));
|
||||
post.setEntity(new ByteArrayEntity(new byte[] { 0, 1, 2, 3, 4 }));
|
||||
post.addHeader("Content-Type", "application/foo");
|
||||
ourClient.execute(post);
|
||||
|
||||
assertEquals("application/foo", ourLastBinary.getContentType());
|
||||
assertArrayEquals(new byte[] {0,1,2,3,4}, ourLastBinary.getContent());
|
||||
assertArrayEquals(new byte[] {0,1,2,3,4}, ourLastBinaryBytes);
|
||||
CloseableHttpResponse status = ourClient.execute(post);
|
||||
try {
|
||||
assertEquals("application/foo", ourLastBinary.getContentType());
|
||||
assertArrayEquals(new byte[] { 0, 1, 2, 3, 4 }, ourLastBinary.getContent());
|
||||
assertArrayEquals(new byte[] { 0, 1, 2, 3, 4 }, ourLastBinaryBytes);
|
||||
} finally {
|
||||
IOUtils.closeQuietly(status);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Technically the client shouldn't be doing it this way,
|
||||
* but we'll be accepting
|
||||
* Technically the client shouldn't be doing it this way, but we'll be accepting
|
||||
*/
|
||||
@Test
|
||||
public void testRawBytesFhirContentType() throws Exception {
|
||||
|
||||
|
||||
Binary b = new Binary();
|
||||
b.setContentType("application/foo");
|
||||
b.setContent(new byte[] {0,1,2,3,4});
|
||||
b.setContent(new byte[] { 0, 1, 2, 3, 4 });
|
||||
String encoded = ourCtx.newJsonParser().encodeResourceToString(b);
|
||||
|
||||
|
||||
HttpPost post = new HttpPost("http://localhost:" + ourPort + "/Binary");
|
||||
post.setEntity(new StringEntity(encoded));
|
||||
post.addHeader("Content-Type", Constants.CT_FHIR_JSON);
|
||||
ourClient.execute(post);
|
||||
|
||||
assertEquals("application/foo", ourLastBinary.getContentType());
|
||||
assertArrayEquals(new byte[] {0,1,2,3,4}, ourLastBinary.getContent());
|
||||
CloseableHttpResponse status = ourClient.execute(post);
|
||||
try {
|
||||
assertEquals("application/foo", ourLastBinary.getContentType());
|
||||
assertArrayEquals(new byte[] { 0, 1, 2, 3, 4 }, ourLastBinary.getContent());
|
||||
} finally {
|
||||
IOUtils.closeQuietly(status);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRawBytesFhirContentTypeContainingFhir() throws Exception {
|
||||
|
||||
|
||||
Patient p = new Patient();
|
||||
p.getText().setDivAsString("A PATIENT");
|
||||
|
||||
|
||||
Binary b = new Binary();
|
||||
b.setContentType("application/xml+fhir");
|
||||
b.setContent(ourCtx.newXmlParser().encodeResourceToString(p).getBytes("UTF-8"));
|
||||
String encoded = ourCtx.newJsonParser().encodeResourceToString(b);
|
||||
|
||||
|
||||
HttpPost post = new HttpPost("http://localhost:" + ourPort + "/Binary");
|
||||
post.setEntity(new StringEntity(encoded));
|
||||
post.addHeader("Content-Type", Constants.CT_FHIR_JSON);
|
||||
ourClient.execute(post);
|
||||
|
||||
assertEquals("application/xml+fhir", ourLastBinary.getContentType());
|
||||
assertArrayEquals(b.getContent(), ourLastBinary.getContent());
|
||||
assertEquals(encoded, ourLastBinaryString);
|
||||
assertArrayEquals(encoded.getBytes("UTF-8"), ourLastBinaryBytes);
|
||||
CloseableHttpResponse status = ourClient.execute(post);
|
||||
try {
|
||||
assertEquals("application/xml+fhir", ourLastBinary.getContentType());
|
||||
assertArrayEquals(b.getContent(), ourLastBinary.getContent());
|
||||
assertEquals(encoded, ourLastBinaryString);
|
||||
assertArrayEquals(encoded.getBytes("UTF-8"), ourLastBinaryBytes);
|
||||
} finally {
|
||||
IOUtils.closeQuietly(status);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testRawBytesNoContentType() throws Exception {
|
||||
HttpPost post = new HttpPost("http://localhost:" + ourPort + "/Binary");
|
||||
post.setEntity(new ByteArrayEntity(new byte[] {0,1,2,3,4}));
|
||||
ourClient.execute(post);
|
||||
|
||||
assertNull(ourLastBinary.getContentType());
|
||||
assertArrayEquals(new byte[] {0,1,2,3,4}, ourLastBinary.getContent());
|
||||
post.setEntity(new ByteArrayEntity(new byte[] { 0, 1, 2, 3, 4 }));
|
||||
CloseableHttpResponse status = ourClient.execute(post);
|
||||
try {
|
||||
assertNull(ourLastBinary.getContentType());
|
||||
assertArrayEquals(new byte[] { 0, 1, 2, 3, 4 }, ourLastBinary.getContent());
|
||||
} finally {
|
||||
IOUtils.closeQuietly(status);
|
||||
}
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
|
@ -121,8 +130,7 @@ public class CreateBinaryDstu3Test {
|
|||
ourServer.stop();
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception {
|
||||
ourPort = PortUtil.findFreePort();
|
||||
|
@ -143,7 +151,7 @@ public class CreateBinaryDstu3Test {
|
|||
builder.setConnectionManager(connectionManager);
|
||||
ourClient = builder.build();
|
||||
}
|
||||
|
||||
|
||||
public static class BinaryProvider implements IResourceProvider {
|
||||
|
||||
@Create()
|
||||
|
@ -153,7 +161,7 @@ public class CreateBinaryDstu3Test {
|
|||
ourLastBinaryBytes = theBinaryBytes;
|
||||
return new MethodOutcome(new IdType("Binary/001/_history/002"));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return Binary.class;
|
||||
|
|
|
@ -248,6 +248,17 @@
|
|||
Improve error messages when the $validate operation is called but no resource
|
||||
is actually supplied to validate
|
||||
</action>
|
||||
<action type="remove">
|
||||
DSTU2+ servers no longer return the Category header, as this has been
|
||||
removed from the FHIR specification (and tags are now available in the
|
||||
resource body so the header was duplication/wasted bandwidth)
|
||||
</action>
|
||||
<action type="fix" issue="374">
|
||||
Create and Update operations in server did not
|
||||
include ETag or Last-Modified headers even though
|
||||
the spec says they should. Thanks to Jim Steel for
|
||||
reporting!
|
||||
</action>
|
||||
</release>
|
||||
<release version="1.5" date="2016-04-20">
|
||||
<action type="fix" issue="339">
|
||||
|
|
Loading…
Reference in New Issue