Get delete working

This commit is contained in:
jamesagnew 2014-04-09 08:23:58 -04:00
parent 3a7ea0c8ba
commit acb5b02672
12 changed files with 264 additions and 93 deletions

View File

@ -47,15 +47,12 @@ public class RuntimeChildUndeclaredExtensionDefinition extends BaseRuntimeChildD
return myDatatypeToDefinition.get(theType); return myDatatypeToDefinition.get(theType);
} }
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RuntimeChildUndeclaredExtensionDefinition.class);
@Override @Override
void sealAndInitialize(Map<Class<? extends IElement>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) { void sealAndInitialize(Map<Class<? extends IElement>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) {
Map<String, BaseRuntimeElementDefinition<?>> datatypeAttributeNameToDefinition = new HashMap<String, BaseRuntimeElementDefinition<?>>(); Map<String, BaseRuntimeElementDefinition<?>> datatypeAttributeNameToDefinition = new HashMap<String, BaseRuntimeElementDefinition<?>>();
myDatatypeToAttributeName = new HashMap<Class<? extends IElement>, String>(); myDatatypeToAttributeName = new HashMap<Class<? extends IElement>, String>();
for (BaseRuntimeElementDefinition<?> next : theClassToElementDefinitions.values()) { for (BaseRuntimeElementDefinition<?> next : theClassToElementDefinitions.values()) {
ourLog.info(next.getName());
if (next instanceof IRuntimeDatatypeDefinition) { if (next instanceof IRuntimeDatatypeDefinition) {
if (!((IRuntimeDatatypeDefinition) next).isSpecialization()) { if (!((IRuntimeDatatypeDefinition) next).isSpecialization()) {
String attrName = "value" + WordUtils.capitalize(next.getName()); String attrName = "value" + WordUtils.capitalize(next.getName());

View File

@ -180,7 +180,7 @@ public abstract class BaseMethodBinding {
} }
} }
static EncodingUtil determineResponseEncoding(HttpServletRequest theRequest, Map<String, String[]> theParams) { public static EncodingUtil determineResponseEncoding(HttpServletRequest theRequest, Map<String, String[]> theParams) {
String[] format = theParams.remove(Constants.PARAM_FORMAT); String[] format = theParams.remove(Constants.PARAM_FORMAT);
if (format != null) { if (format != null) {
for (String nextFormat : format) { for (String nextFormat : format) {

View File

@ -5,8 +5,6 @@ import java.io.Reader;
import java.io.Writer; import java.io.Writer;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -40,14 +38,7 @@ import ca.uhn.fhir.rest.server.exceptions.UnclassifiedServerFailureException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
public abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding { public abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseOutcomeReturningMethodBinding.class);
private static Set<String> ALLOWED_PARAMS;
static {
HashSet<String> set = new HashSet<String>();
set.add(Constants.PARAM_FORMAT);
ALLOWED_PARAMS = Collections.unmodifiableSet(set);
}
private List<IParameter> myParameters; private List<IParameter> myParameters;
private boolean myReturnVoid; private boolean myReturnVoid;
@ -57,28 +48,15 @@ public abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBindin
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;
} }
} }
} }
protected List<IParameter> getParameters() { public abstract String getResourceName();
return myParameters;
}
/**
* Subclasses may override to allow a void method return type, which is allowable for some methods (e.g. delete)
*/
protected boolean allowVoidReturnType() {
return false;
}
protected abstract BaseClientInvocation createClientInvocation(Object[] theArgs, IResource resource, String resourceName);
@Override @Override
public Object invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException { public Object invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
@ -95,6 +73,16 @@ public abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBindin
String locationHeader = locationHeaders.get(0); String locationHeader = locationHeaders.get(0);
parseContentLocation(retVal, locationHeader); parseContentLocation(retVal, locationHeader);
} }
if (theResponseStatusCode != Constants.STATUS_HTTP_204_NO_CONTENT) {
EncodingUtil ct = EncodingUtil.forContentType(theResponseMimeType);
if (ct != null) {
IParser parser = ct.newParser(getContext());
OperationOutcome outcome = parser.parseResource(OperationOutcome.class, theResponseReader);
retVal.setOperationOutcome(outcome);
} else {
ourLog.debug("Ignoring response content of type: {}", theResponseMimeType);
}
}
return retVal; return retVal;
case Constants.STATUS_HTTP_400_BAD_REQUEST: case Constants.STATUS_HTTP_400_BAD_REQUEST:
throw new InvalidRequestException("Server responded with: " + IOUtils.toString(theResponseReader)); throw new InvalidRequestException("Server responded with: " + IOUtils.toString(theResponseReader));
@ -116,43 +104,11 @@ public abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBindin
} }
protected void parseContentLocation(MethodOutcome theOutcomeToPopulate, String theLocationHeader) {
String resourceNamePart = "/" + getResourceName() + "/";
int resourceIndex = theLocationHeader.lastIndexOf(resourceNamePart);
if (resourceIndex > -1) {
int idIndexStart = resourceIndex + resourceNamePart.length();
int idIndexEnd = theLocationHeader.indexOf('/', idIndexStart);
if (idIndexEnd == -1) {
theOutcomeToPopulate.setId(new IdDt(theLocationHeader.substring(idIndexStart)));
} else {
theOutcomeToPopulate.setId(new IdDt(theLocationHeader.substring(idIndexStart, idIndexEnd)));
String versionIdPart = "/_history/";
int historyIdStart = theLocationHeader.indexOf(versionIdPart, idIndexEnd);
if (historyIdStart != -1) {
theOutcomeToPopulate.setVersionId(new IdDt(theLocationHeader.substring(historyIdStart + versionIdPart.length())));
}
}
}
}
public abstract String getResourceName();
@Override @Override
public void invokeServer(RestfulServer theServer, Request theRequest, HttpServletResponse theResponse) throws BaseServerResponseException, IOException { public void invokeServer(RestfulServer theServer, Request theRequest, HttpServletResponse theResponse) throws BaseServerResponseException, IOException {
EncodingUtil encoding = determineResponseEncoding(theRequest.getServletRequest(), theRequest.getParameters());
IParser parser = encoding.newParser(getContext());
IResource resource = parser.parseResource(theRequest.getInputReader());
Object[] params = new Object[myParameters.size()]; Object[] params = new Object[myParameters.size()];
for (int i = 0; i < myParameters.size(); i++) {
IParameter param = myParameters.get(i);
if (param == null) {
continue;
}
params[i] = param.translateQueryParametersIntoServerArgument(theRequest.getParameters(), resource);
}
addAdditionalParams(theRequest, params); addParametersForServerRequest(theRequest, params);
MethodOutcome response; MethodOutcome response;
try { try {
@ -168,6 +124,8 @@ public abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBindin
if (response == null) { if (response == null) {
if (myReturnVoid == false) { if (myReturnVoid == false) {
throw new ConfigurationException("Method " + getMethod().getName() + " in type " + getMethod().getDeclaringClass().getCanonicalName() + " returned null"); throw new ConfigurationException("Method " + getMethod().getName() + " in type " + getMethod().getDeclaringClass().getCanonicalName() + " returned null");
} else {
theResponse.setStatus(Constants.STATUS_HTTP_204_NO_CONTENT);
} }
} else if (!myReturnVoid) { } else if (!myReturnVoid) {
if (response.isCreated()) { if (response.isCreated()) {
@ -192,26 +150,29 @@ public abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBindin
theServer.addHapiHeader(theResponse); theServer.addHapiHeader(theResponse);
theResponse.setContentType(Constants.CT_TEXT);
Writer writer = theResponse.getWriter(); Writer writer = theResponse.getWriter();
try { try {
writer.append("Resource has been created"); if (response != null) {
OperationOutcome outcome = new OperationOutcome();
if (response.getOperationOutcome() != null && response.getOperationOutcome().getIssue() != null) {
outcome.getIssue().addAll(response.getOperationOutcome().getIssue());
}
EncodingUtil encoding = BaseMethodBinding.determineResponseEncoding(theRequest.getServletRequest(), theRequest.getParameters());
theResponse.setContentType(encoding.getResourceContentType());
IParser parser = encoding.newParser(getContext());
parser.encodeResourceToWriter(outcome, writer);
}
} finally { } finally {
writer.close(); writer.close();
} }
// getMethod().in // getMethod().in
} }
/** protected abstract void addParametersForServerRequest(Request theRequest, Object[] theParams);
* For subclasses to override
*/
@SuppressWarnings("unused")
protected void addAdditionalParams(Request theRequest, Object[] theParams) {
// nothing
}
protected abstract Set<RequestType> provideAllowableRequestTypes(); public boolean isReturnVoid() {
return myReturnVoid;
}
@Override @Override
public boolean matches(Request theRequest) { public boolean matches(Request theRequest) {
@ -229,4 +190,39 @@ public abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBindin
return true; return true;
} }
/**
* Subclasses may override to allow a void method return type, which is
* allowable for some methods (e.g. delete)
*/
protected boolean allowVoidReturnType() {
return false;
}
protected abstract BaseClientInvocation createClientInvocation(Object[] theArgs, IResource resource, String resourceName);
protected List<IParameter> getParameters() {
return myParameters;
}
protected void parseContentLocation(MethodOutcome theOutcomeToPopulate, String theLocationHeader) {
String resourceNamePart = "/" + getResourceName() + "/";
int resourceIndex = theLocationHeader.lastIndexOf(resourceNamePart);
if (resourceIndex > -1) {
int idIndexStart = resourceIndex + resourceNamePart.length();
int idIndexEnd = theLocationHeader.indexOf('/', idIndexStart);
if (idIndexEnd == -1) {
theOutcomeToPopulate.setId(new IdDt(theLocationHeader.substring(idIndexStart)));
} else {
theOutcomeToPopulate.setId(new IdDt(theLocationHeader.substring(idIndexStart, idIndexEnd)));
String versionIdPart = "/_history/";
int historyIdStart = theLocationHeader.indexOf(versionIdPart, idIndexEnd);
if (historyIdStart != -1) {
theOutcomeToPopulate.setVersionId(new IdDt(theLocationHeader.substring(historyIdStart + versionIdPart.length())));
}
}
}
}
protected abstract Set<RequestType> provideAllowableRequestTypes();
} }

View File

@ -1,15 +1,26 @@
package ca.uhn.fhir.rest.method; package ca.uhn.fhir.rest.method;
import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import javax.servlet.http.HttpServletResponse;
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.RuntimeResourceDefinition; import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.client.BaseClientInvocation; import ca.uhn.fhir.rest.client.BaseClientInvocation;
import ca.uhn.fhir.rest.param.IParameter; import ca.uhn.fhir.rest.param.IParameter;
import ca.uhn.fhir.rest.param.ResourceParameter; import ca.uhn.fhir.rest.param.ResourceParameter;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingUtil;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
public abstract class BaseOutcomeReturningMethodBindingWithResourceParam extends BaseOutcomeReturningMethodBinding { public abstract class BaseOutcomeReturningMethodBindingWithResourceParam extends BaseOutcomeReturningMethodBinding {
@ -38,6 +49,79 @@ public abstract class BaseOutcomeReturningMethodBindingWithResourceParam extends
} }
/**
* For subclasses to override
*/
@Override
protected void addParametersForServerRequest(Request theRequest, Object[] theParams) {
// nothing
}
@Override
public void invokeServer(RestfulServer theServer, Request theRequest, HttpServletResponse theResponse) throws BaseServerResponseException, IOException {
EncodingUtil encoding = BaseMethodBinding.determineResponseEncoding(theRequest.getServletRequest(), theRequest.getParameters());
IParser parser = encoding.newParser(getContext());
IResource resource = parser.parseResource(theRequest.getInputReader());
Object[] params = new Object[getParameters().size()];
for (int i = 0; i < getParameters().size(); i++) {
IParameter param = getParameters().get(i);
if (param == null) {
continue;
}
params[i] = param.translateQueryParametersIntoServerArgument(theRequest.getParameters(), resource);
}
addParametersForServerRequest(theRequest, params);
MethodOutcome response;
try {
response = (MethodOutcome) this.getMethod().invoke(theRequest.getResourceProvider(), params);
} catch (IllegalAccessException e) {
throw new InternalErrorException(e);
} catch (IllegalArgumentException e) {
throw new InternalErrorException(e);
} catch (InvocationTargetException e) {
throw new InternalErrorException(e);
}
if (response == null) {
if (isReturnVoid() == false) {
throw new ConfigurationException("Method " + getMethod().getName() + " in type " + getMethod().getDeclaringClass().getCanonicalName() + " returned null");
}
} else if (!isReturnVoid()) {
if (response.isCreated()) {
theResponse.setStatus(Constants.STATUS_HTTP_201_CREATED);
StringBuilder b = new 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);
}
theServer.addHapiHeader(theResponse);
theResponse.setContentType(Constants.CT_TEXT);
Writer writer = theResponse.getWriter();
try {
writer.append("Resource has been created");
} finally {
writer.close();
}
// getMethod().in
}
@Override @Override
public String getResourceName() { public String getResourceName() {

View File

@ -20,39 +20,40 @@ import ca.uhn.fhir.rest.client.PostClientInvocation;
import ca.uhn.fhir.rest.method.SearchMethodBinding.RequestType; import ca.uhn.fhir.rest.method.SearchMethodBinding.RequestType;
import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
public class DeleteMethodBinding extends BaseOutcomeReturningMethodBinding { public class DeleteMethodBinding extends BaseOutcomeReturningMethodBinding {
private String myResourceName; private String myResourceName;
private Integer myIdParameterIndex; private Integer myIdParameterIndex;
public DeleteMethodBinding(Method theMethod, FhirContext theContext, IResourceProvider theProvider) { public DeleteMethodBinding(Method theMethod, FhirContext theContext, IResourceProvider theProvider) {
super(theMethod, theContext, Delete.class); super(theMethod, theContext, Delete.class);
Delete deleteAnnotation = theMethod.getAnnotation(Delete.class); Delete deleteAnnotation = theMethod.getAnnotation(Delete.class);
Class<? extends IResource> resourceType = deleteAnnotation.resourceType(); Class<? extends IResource> resourceType = deleteAnnotation.resourceType();
if (resourceType != Delete.NotSpecified.class) { if (resourceType != Delete.NotSpecified.class) {
RuntimeResourceDefinition def = theContext.getResourceDefinition(resourceType); RuntimeResourceDefinition def = theContext.getResourceDefinition(resourceType);
myResourceName = def.getName(); myResourceName = def.getName();
} else { } else {
if (theProvider!=null) { if (theProvider != null) {
RuntimeResourceDefinition def = theContext.getResourceDefinition(theProvider.getResourceType()); RuntimeResourceDefinition def = theContext.getResourceDefinition(theProvider.getResourceType());
myResourceName = def.getName(); myResourceName = def.getName();
}else { } else {
throw new ConfigurationException("Can not determine resource type for method '"+theMethod.getName() + "' on type " + theMethod.getDeclaringClass().getCanonicalName() + " - Did you forget to include the resourceType() value on the @" + Delete.class.getSimpleName() + " method annotation?"); throw new ConfigurationException("Can not determine resource type for method '" + theMethod.getName() + "' on type " + theMethod.getDeclaringClass().getCanonicalName() + " - Did you forget to include the resourceType() value on the @"
+ Delete.class.getSimpleName() + " method annotation?");
} }
} }
myIdParameterIndex = Util.findIdParameterIndex(theMethod); myIdParameterIndex = Util.findIdParameterIndex(theMethod);
if (myIdParameterIndex == null) { if (myIdParameterIndex == null) {
throw new ConfigurationException("Method '" + theMethod.getName() + "' on type '" + theMethod.getDeclaringClass().getCanonicalName() + "' has no parameter annotated with the @" + IdParam.class.getSimpleName() + " annotation"); throw new ConfigurationException("Method '" + theMethod.getName() + "' on type '" + theMethod.getDeclaringClass().getCanonicalName() + "' has no parameter annotated with the @" + IdParam.class.getSimpleName() + " annotation");
} }
Integer versionIdParameterIndex = Util.findVersionIdParameterIndex(theMethod); Integer versionIdParameterIndex = Util.findVersionIdParameterIndex(theMethod);
if (versionIdParameterIndex!=null) { if (versionIdParameterIndex != null) {
throw new ConfigurationException("Method '" + theMethod.getName() + "' on type '" + theMethod.getDeclaringClass().getCanonicalName() + "' has a parameter annotated with the @" + VersionIdParam.class.getSimpleName() + " annotation but delete methods may not have this annotation"); throw new ConfigurationException("Method '" + theMethod.getName() + "' on type '" + theMethod.getDeclaringClass().getCanonicalName() + "' has a parameter annotated with the @" + VersionIdParam.class.getSimpleName()
+ " annotation but delete methods may not have this annotation");
} }
} }
@ -90,11 +91,11 @@ public class DeleteMethodBinding extends BaseOutcomeReturningMethodBinding {
return myResourceName; return myResourceName;
} }
// @Override // @Override
// public boolean matches(Request theRequest) { // public boolean matches(Request theRequest) {
// // TODO Auto-generated method stub // // TODO Auto-generated method stub
// return super.matches(theRequest); // return super.matches(theRequest);
// } // }
@Override @Override
public BaseClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException { public BaseClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException {
@ -109,4 +110,16 @@ public class DeleteMethodBinding extends BaseOutcomeReturningMethodBinding {
return retVal; return retVal;
} }
@Override
protected void addParametersForServerRequest(Request theRequest, Object[] theParams) {
String url = theRequest.getCompleteUrl();
int resNameIdx = url.indexOf(getResourceName());
String id = url.substring(resNameIdx+getResourceName().length() + 1);
if (id.contains("/")) {
throw new InvalidRequestException("Invalid request path for a DELETE operation: "+theRequest.getCompleteUrl());
}
theParams[myIdParameterIndex] = new IdDt(id);
}
} }

View File

@ -50,7 +50,7 @@ public class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithRe
} }
@Override @Override
protected void addAdditionalParams(Request theRequest, Object[] theParams) { protected void addParametersForServerRequest(Request theRequest, Object[] theParams) {
/* /*
* We are being a bit lenient here, since technically the client is * We are being a bit lenient here, since technically the client is
* supposed to include the version in the Content-Location header, but * supposed to include the version in the Content-Location header, but

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.rest.server; package ca.uhn.fhir.rest.server;
import java.util.HashMap;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.parser.IParser;
@ -21,6 +23,17 @@ public enum EncodingUtil {
; ;
private static HashMap<String, EncodingUtil> ourContentTypeToEncoding;
static {
ourContentTypeToEncoding = new HashMap<String, EncodingUtil>();
for (EncodingUtil next: values()) {
ourContentTypeToEncoding.put(next.getBundleContentType(), next);
ourContentTypeToEncoding.put(next.getResourceContentType(), next);
ourContentTypeToEncoding.put(next.getBrowserFriendlyBundleContentType(), next);
}
}
private String myResourceContentType; private String myResourceContentType;
private String myBundleContentType; private String myBundleContentType;
private String myBrowserFriendlyContentType; private String myBrowserFriendlyContentType;
@ -45,4 +58,9 @@ public enum EncodingUtil {
return myBrowserFriendlyContentType; return myBrowserFriendlyContentType;
} }
public static EncodingUtil forContentType(String theContentType) {
return ourContentTypeToEncoding.get(theContentType);
}
} }

View File

@ -23,7 +23,7 @@ public class CompleteExampleClient {
{ {
/** /**
* This translated into a URL similar to the following: * This is translated into a URL similar to the following:
* http://fhir.healthintersections.com.au/open/Patient?identifier=urn:oid:1.2.36.146.595.217.0.1%7C12345 * http://fhir.healthintersections.com.au/open/Patient?identifier=urn:oid:1.2.36.146.595.217.0.1%7C12345
*/ */
@Search @Search

View File

@ -62,7 +62,7 @@
</macro> </macro>
<p> <p>
You will probable wish to add more methods You will probably want to add more methods
to your client interface. See to your client interface. See
<a href="./doc_rest_operations.html">RESTful Operations</a> for <a href="./doc_rest_operations.html">RESTful Operations</a> for
lots more examples of how to add methods for various operations. lots more examples of how to add methods for various operations.

View File

@ -14,6 +14,7 @@ import org.apache.http.Header;
import org.apache.http.HttpResponse; import org.apache.http.HttpResponse;
import org.apache.http.ProtocolVersion; import org.apache.http.ProtocolVersion;
import org.apache.http.client.HttpClient; import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut; import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.client.methods.HttpUriRequest;
@ -32,6 +33,7 @@ import ca.uhn.fhir.model.api.PathSpecification;
import ca.uhn.fhir.model.dstu.composite.CodingDt; import ca.uhn.fhir.model.dstu.composite.CodingDt;
import ca.uhn.fhir.model.dstu.composite.IdentifierDt; import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
import ca.uhn.fhir.model.dstu.resource.Conformance; import ca.uhn.fhir.model.dstu.resource.Conformance;
import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
import ca.uhn.fhir.model.dstu.resource.Patient; import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum; import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
@ -115,7 +117,45 @@ public class ClientTest {
assertEquals("100", response.getId().getValue()); assertEquals("100", response.getId().getValue());
assertEquals("200", response.getVersionId().getValue()); assertEquals("200", response.getVersionId().getValue());
} }
@Test
public void testDelete() throws Exception {
OperationOutcome oo = new OperationOutcome();
oo.addIssue().setDetails("Hello");
String resp = new FhirContext().newXmlParser().encodeResourceToString(oo);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 201, "OK"));
when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(resp), Charset.forName("UTF-8")));
ITestClient client = ctx.newRestfulClient(ITestClient.class, "http://foo");
MethodOutcome response = client.deletePatient(new IdDt("1234"));
assertEquals(HttpDelete.class, capt.getValue().getClass());
assertEquals("http://foo/Patient/1234", capt.getValue().getURI().toString());
assertEquals("Hello", response.getOperationOutcome().getIssueFirstRep().getDetails().getValue());
}
@Test
public void testDeleteNoResponse() throws Exception {
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 204, "OK"));
when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_TEXT + "; charset=UTF-8"));
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8")));
ITestClient client = ctx.newRestfulClient(ITestClient.class, "http://foo");
client.deleteDiagnosticReport(new IdDt("1234"));
assertEquals(HttpDelete.class, capt.getValue().getClass());
assertEquals("http://foo/DiagnosticReport/1234", capt.getValue().getURI().toString());
}
@Test @Test
public void testUpdate() throws Exception { public void testUpdate() throws Exception {

View File

@ -5,10 +5,12 @@ import java.util.List;
import ca.uhn.fhir.model.api.Bundle; import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.PathSpecification; import ca.uhn.fhir.model.api.PathSpecification;
import ca.uhn.fhir.model.dstu.composite.IdentifierDt; import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
import ca.uhn.fhir.model.dstu.resource.DiagnosticReport;
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.model.primitive.StringDt; import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.rest.annotation.Create; import ca.uhn.fhir.rest.annotation.Create;
import ca.uhn.fhir.rest.annotation.Delete;
import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.IncludeParam; import ca.uhn.fhir.rest.annotation.IncludeParam;
import ca.uhn.fhir.rest.annotation.OptionalParam; import ca.uhn.fhir.rest.annotation.OptionalParam;
@ -29,6 +31,12 @@ public interface ITestClient extends IBasicClient {
@Read(type=Patient.class) @Read(type=Patient.class)
Patient getPatientById(@IdParam IdDt theId); Patient getPatientById(@IdParam IdDt theId);
@Delete(resourceType=Patient.class)
MethodOutcome deletePatient(@IdParam IdDt theId);
@Delete(resourceType=DiagnosticReport.class)
void deleteDiagnosticReport(@IdParam IdDt theId);
@Read(type=Patient.class) @Read(type=Patient.class)
Patient getPatientByVersionId(@IdParam IdDt theId, @VersionIdParam IdDt theVersionId); Patient getPatientByVersionId(@IdParam IdDt theId, @VersionIdParam IdDt theVersionId);

View File

@ -566,11 +566,26 @@ public class ResfulServerMethodTest {
assertEquals(200, status.getStatusLine().getStatusCode()); assertEquals(200, status.getStatusLine().getStatusCode());
OperationOutcome patient = (OperationOutcome) ourCtx.newXmlParser().parseBundle(responseContent).getEntries().get(0).getResource(); OperationOutcome patient = ourCtx.newXmlParser().parseResource(OperationOutcome.class, responseContent);
assertEquals("1234", patient.getIssueFirstRep().getDetails().getValue()); assertEquals("1234", patient.getIssueFirstRep().getDetails().getValue());
} }
@Test
public void testDeleteNoResponse() throws Exception {
// HttpPost httpPost = new HttpPost("http://localhost:" + ourPort +
// "/Patient/1");
// httpPost.setEntity(new StringEntity("test",
// ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
HttpDelete httpGet = new HttpDelete("http://localhost:" + ourPort + "/DiagnosticReport/1234");
HttpResponse status = ourClient.execute(httpGet);
assertEquals(204, status.getStatusLine().getStatusCode());
}
@Test @Test
public void testPrettyPrint() throws Exception { public void testPrettyPrint() throws Exception {