Return a content-location header on update

This commit is contained in:
jamesagnew 2014-09-29 21:05:55 -04:00
parent 4622af7a82
commit dcccf5553e
3 changed files with 38 additions and 27 deletions

View File

@ -63,6 +63,11 @@
HAPI now logs a single line indicating the StAX implementation being used upon the HAPI now logs a single line indicating the StAX implementation being used upon the
first time an XML parser is created. first time an XML parser is created.
</action> </action>
<action type="fix">
Update methods on the server did not return a "content-location" header, but
only a "location" header. Both are required according to the FHIR specification.
Thanks to Bill de Beaubien of Systems Made Simple for reporting this!
</action>
</release> </release>
<release version="0.6" date="2014-Sep-08" description="This release brings a number of new features and bug fixes!"> <release version="0.6" date="2014-Sep-08" description="This release brings a number of new features and bug fixes!">
<!-- <!--

View File

@ -61,8 +61,7 @@ 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() throw new ConfigurationException("Method " + theMethod.getName() + " in type " + theMethod.getDeclaringClass().getCanonicalName() + " is a @" + theMethodAnnotation.getSimpleName() + " method but it does not return " + MethodOutcome.class);
+ " method but it does not return " + MethodOutcome.class);
} else if (theMethod.getReturnType() == void.class) { } else if (theMethod.getReturnType() == void.class) {
myReturnVoid = true; myReturnVoid = true;
} }
@ -89,8 +88,7 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
} }
@Override @Override
public MethodOutcome invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, public MethodOutcome invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
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:
@ -147,8 +145,8 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
throw new InternalErrorException("Server method returned invalid resource ID: " + response.getId().getValue()); throw new InternalErrorException("Server method returned invalid resource ID: " + response.getId().getValue());
} }
} }
OperationOutcome outcome = response != null ? response.getOperationOutcome():null; OperationOutcome outcome = response != null ? response.getOperationOutcome() : null;
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) { for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
IServerInterceptor next = theServer.getInterceptors().get(i); IServerInterceptor next = theServer.getInterceptors().get(i);
boolean continueProcessing = next.outgoingResponse(theRequest, outcome, theRequest.getServletRequest(), theRequest.getServletResponse()); boolean continueProcessing = next.outgoingResponse(theRequest, outcome, theRequest.getServletRequest(), theRequest.getServletResponse());
@ -156,19 +154,18 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
return; return;
} }
} }
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() throw new InternalErrorException("Method " + getMethod().getName() + " in type " + getMethod().getDeclaringClass().getCanonicalName() + " returned null, which is not allowed for create operation");
+ " 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);
} else { } else {
servletResponse.setStatus(Constants.STATUS_HTTP_200_OK); servletResponse.setStatus(Constants.STATUS_HTTP_200_OK);
} }
addLocationHeader(theRequest, servletResponse, response); addLocationHeader(theRequest, servletResponse, response, Constants.HEADER_LOCATION);
break; break;
case UPDATE: case UPDATE:
@ -178,7 +175,8 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
servletResponse.setStatus(Constants.STATUS_HTTP_201_CREATED); servletResponse.setStatus(Constants.STATUS_HTTP_201_CREATED);
} }
if (response != null && response.getId() != null) { if (response != null && response.getId() != null) {
addLocationHeader(theRequest, servletResponse, response); addLocationHeader(theRequest, servletResponse, response, Constants.HEADER_LOCATION);
addLocationHeader(theRequest, servletResponse, response, Constants.HEADER_CONTENT_LOCATION);
} }
break; break;
@ -226,7 +224,7 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
return myReturnVoid; return myReturnVoid;
} }
private void addLocationHeader(Request theRequest, HttpServletResponse theResponse, MethodOutcome response) { private void addLocationHeader(Request theRequest, HttpServletResponse theResponse, MethodOutcome response, String headerLocation) {
StringBuilder b = new StringBuilder(); StringBuilder b = new StringBuilder();
b.append(theRequest.getFhirServerBase()); b.append(theRequest.getFhirServerBase());
b.append('/'); b.append('/');
@ -240,31 +238,38 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
b.append("/" + Constants.PARAM_HISTORY + "/"); b.append("/" + Constants.PARAM_HISTORY + "/");
b.append(response.getVersionId().getValue()); b.append(response.getVersionId().getValue());
} }
theResponse.addHeader(Constants.HEADER_LOCATION, b.toString()); theResponse.addHeader(headerLocation, b.toString());
} }
/* /*
* @Override public void invokeServer(RestfulServer theServer, Request theRequest, HttpServletResponse theResponse) throws BaseServerResponseException, IOException { Object[] params = new * @Override public void invokeServer(RestfulServer theServer, Request theRequest, HttpServletResponse theResponse)
* Object[getParameters().size()]; for (int i = 0; i < getParameters().size(); i++) { IParameter param = getParameters().get(i); if (param != null) { params[i] = * throws BaseServerResponseException, IOException { Object[] params = new Object[getParameters().size()]; for (int
* param.translateQueryParametersIntoServerArgument(theRequest, null); } } * i = 0; i < getParameters().size(); i++) { IParameter param = getParameters().get(i); if (param != null) {
* params[i] = param.translateQueryParametersIntoServerArgument(theRequest, null); } }
* *
* addParametersForServerRequest(theRequest, params); * addParametersForServerRequest(theRequest, params);
* *
* MethodOutcome response = (MethodOutcome) invokeServerMethod(getProvider(), params); * MethodOutcome response = (MethodOutcome) invokeServerMethod(getProvider(), params);
* *
* if (response == null) { if (myReturnVoid == false) { throw new ConfigurationException("Method " + getMethod().getName() + " in type " + getMethod().getDeclaringClass().getCanonicalName() + * if (response == null) { if (myReturnVoid == false) { throw new ConfigurationException("Method " +
* " returned null"); } else { theResponse.setStatus(Constants.STATUS_HTTP_204_NO_CONTENT); } } else if (!myReturnVoid) { if (response.isCreated()) { * getMethod().getName() + " in type " + getMethod().getDeclaringClass().getCanonicalName() + " returned null"); }
* theResponse.setStatus(Constants.STATUS_HTTP_201_CREATED); StringBuilder b = new StringBuilder(); b.append(theRequest.getFhirServerBase()); b.append('/'); b.append(getResourceName()); * else { theResponse.setStatus(Constants.STATUS_HTTP_204_NO_CONTENT); } } else if (!myReturnVoid) { if
* b.append('/'); b.append(response.getId().getValue()); if (response.getVersionId() != null && response.getVersionId().isEmpty() == false) { b.append("/_history/"); * (response.isCreated()) { theResponse.setStatus(Constants.STATUS_HTTP_201_CREATED); StringBuilder b = new
* b.append(response.getVersionId().getValue()); } theResponse.addHeader("Location", b.toString()); } else { theResponse.setStatus(Constants.STATUS_HTTP_200_OK); } } else { * StringBuilder(); b.append(theRequest.getFhirServerBase()); b.append('/'); b.append(getResourceName());
* b.append('/'); b.append(response.getId().getValue()); if (response.getVersionId() != null &&
* response.getVersionId().isEmpty() == false) { b.append("/_history/");
* b.append(response.getVersionId().getValue()); } theResponse.addHeader("Location", b.toString()); } else {
* theResponse.setStatus(Constants.STATUS_HTTP_200_OK); } } else {
* theResponse.setStatus(Constants.STATUS_HTTP_204_NO_CONTENT); } * theResponse.setStatus(Constants.STATUS_HTTP_204_NO_CONTENT); }
* *
* theServer.addHeadersToResponse(theResponse); * theServer.addHeadersToResponse(theResponse);
* *
* Writer writer = theResponse.getWriter(); try { if (response != null) { OperationOutcome outcome = new OperationOutcome(); if (response.getOperationOutcome() != null && * Writer writer = theResponse.getWriter(); try { if (response != null) { OperationOutcome outcome = new
* response.getOperationOutcome().getIssue() != null) { outcome.getIssue().addAll(response.getOperationOutcome().getIssue()); } EncodingUtil encoding = * OperationOutcome(); if (response.getOperationOutcome() != null && response.getOperationOutcome().getIssue() !=
* BaseMethodBinding.determineResponseEncoding(theRequest .getServletRequest(), theRequest.getParameters()); theResponse.setContentType(encoding.getResourceContentType()); IParser parser = * null) { outcome.getIssue().addAll(response.getOperationOutcome().getIssue()); } EncodingUtil encoding =
* encoding.newParser(getContext()); parser.encodeResourceToWriter(outcome, writer); } } finally { writer.close(); } // getMethod().in } * BaseMethodBinding.determineResponseEncoding(theRequest .getServletRequest(), theRequest.getParameters());
* theResponse.setContentType(encoding.getResourceContentType()); IParser parser = encoding.newParser(getContext());
* parser.encodeResourceToWriter(outcome, writer); } } finally { writer.close(); } // getMethod().in }
*/ */
protected abstract void addParametersForServerRequest(Request theRequest, Object[] theParams); protected abstract void addParametersForServerRequest(Request theRequest, Object[] theParams);
@ -279,7 +284,8 @@ 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 operation in the URL if this method returns null. * 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.
*/ */
protected abstract String getMatchingOperation(); protected abstract String getMatchingOperation();
@ -352,5 +358,4 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
} }
} }
} }

View File

@ -69,6 +69,7 @@ public class UpdateTest {
assertEquals(200, status.getStatusLine().getStatusCode()); 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("location").getValue());
assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue());
} }