Fix broken unit tests and begin preparing for non-resource specific items

This commit is contained in:
jamesagnew 2014-04-22 17:10:25 -04:00
parent 5da4698fab
commit 0b93dcc491
16 changed files with 952 additions and 844 deletions

View File

@ -3,6 +3,8 @@ package ca.uhn.fhir.rest.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import ca.uhn.fhir.model.api.IResource;
@Retention(RetentionPolicy.RUNTIME)
/**
@ -14,5 +16,15 @@ import java.lang.annotation.RetentionPolicy;
* </p>
*/
public @interface Create {
// nothing
/**
* The return type for this search method. This generally does not need
* to be populated for a server implementation, since servers will return
* only one resource per class, but generally does need to be populated
* for client implementations.
*/
// NB: Read, Search (maybe others) share this annotation, so update the javadocs everywhere
Class<? extends IResource> type() default IResource.class;
}

View File

@ -20,11 +20,12 @@ import ca.uhn.fhir.model.api.IResource;
@Target(value=ElementType.METHOD)
public @interface Delete {
Class<? extends IResource> resourceType() default NotSpecified.class;
interface NotSpecified extends IResource{
// nothing
}
/**
* The return type for this search method. This generally does not need
* to be populated for a server implementation, since servers will return
* only one resource per class, but generally does need to be populated
* for client implementations.
*/
// NB: Read, Search (maybe others) share this annotation, so update the javadocs everywhere
Class<? extends IResource> type() default IResource.class;
}

View File

@ -57,11 +57,6 @@ public @interface History {
* The resource type that this method applies to. See the {@link History History annotation type documentation}
* for information on usage patterns.
*/
Class<? extends IResource> resourceType() default AllResources.class;
interface AllResources extends IResource {
// nothing
}
Class<? extends IResource> type() default IResource.class;
}

View File

@ -5,6 +5,8 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import ca.uhn.fhir.model.api.IResource;
/**
* RESTful method annotation to be used for the FHIR
* <a href="http://hl7.org/implement/standards/fhir/http.html#update">update</a> method.
@ -18,4 +20,13 @@ import java.lang.annotation.Target;
@Target(value=ElementType.METHOD)
public @interface Update {
/**
* The return type for this search method. This generally does not need
* to be populated for a server implementation, since servers will return
* only one resource per class, but generally does need to be populated
* for client implementations.
*/
// NB: Read, Search (maybe others) share this annotation, so update the javadocs everywhere
Class<? extends IResource> type() default IResource.class;
}

View File

@ -13,10 +13,8 @@ import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.rest.client.api.IRestfulClient;
import ca.uhn.fhir.rest.method.BaseMethodBinding;
import ca.uhn.fhir.util.ReflectionUtil;
public class RestfulClientFactory implements IRestfulClientFactory {
@ -51,7 +49,6 @@ public class RestfulClientFactory implements IRestfulClientFactory {
* @throws ConfigurationException
* If the interface type is not an interface
*/
@SuppressWarnings("unchecked")
@Override
public synchronized <T extends IRestfulClient> T newClient(Class<T> theClientType, String theServerBase) {
if (!theClientType.isInterface()) {
@ -68,20 +65,8 @@ public class RestfulClientFactory implements IRestfulClientFactory {
ClientInvocationHandler invocationHandler = myInvocationHandlers.get(theClientType);
if (invocationHandler == null) {
invocationHandler = new ClientInvocationHandler(client, myContext, serverBase, theClientType);
for (Method nextMethod : theClientType.getMethods()) {
Class<? extends IResource> resReturnType = null;
Class<?> returnType = nextMethod.getReturnType();
if (IResource.class.isAssignableFrom(returnType)) {
resReturnType = (Class<? extends IResource>) returnType;
} else if (java.util.Collection.class.isAssignableFrom(returnType)) {
Class<?> returnTypeColl = ReflectionUtil.getGenericCollectionTypeOfMethodReturnType(nextMethod);
if (!IResource.class.isAssignableFrom(returnTypeColl)) {
throw new ConfigurationException("Generic type of collection for method '" + nextMethod + "' is not a subclass of IResource");
}
resReturnType = (Class<? extends IResource>) returnTypeColl;
}
BaseMethodBinding binding = BaseMethodBinding.bindMethod(resReturnType, nextMethod, myContext,null);
BaseMethodBinding binding = BaseMethodBinding.bindMethod(nextMethod, myContext, null);
invocationHandler.addBinding(nextMethod, binding);
}
myInvocationHandlers.put(theClientType, invocationHandler);

View File

@ -3,6 +3,7 @@ package ca.uhn.fhir.rest.method;
import java.io.IOException;
import java.io.Reader;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@ -14,7 +15,6 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.Validate;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
@ -30,6 +30,7 @@ import ca.uhn.fhir.rest.annotation.Metadata;
import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.annotation.Update;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.client.BaseClientInvocation;
import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException;
import ca.uhn.fhir.rest.server.Constants;
@ -55,6 +56,13 @@ public abstract class BaseMethodBinding {
myProvider = theProvider;
}
/**
* Returns the name of the resource this method handles, or <code>null</code> if this
* method is not resource specific
*/
public abstract String getResourceName();
public Object getProvider() {
return myProvider;
}
@ -87,6 +95,7 @@ public abstract class BaseMethodBinding {
return parser;
}
@SuppressWarnings("unchecked")
public static BaseMethodBinding bindMethod(Method theMethod, FhirContext theContext, Object theProvider) {
Read read = theMethod.getAnnotation(Read.class);
Search search = theMethod.getAnnotation(Search.class);
@ -100,53 +109,84 @@ public abstract class BaseMethodBinding {
return null;
}
Class<? extends IResource> returnType;
Class<? extends IResource> returnTypeFromRp = null;
if (theProvider instanceof IResourceProvider) {
returnTypeFromRp = ((IResourceProvider) theProvider).getResourceType();
if (!verifyIsValidResourceReturnType(returnTypeFromRp)) {
throw new ConfigurationException("getResourceType() from " + IResourceProvider.class.getSimpleName() + " type " + theMethod.getDeclaringClass().getCanonicalName() + " returned " + toLogString(returnTypeFromRp) + " - Must return a resource type");
throw new ConfigurationException("getResourceType() from " + IResourceProvider.class.getSimpleName() + " type " + theMethod.getDeclaringClass().getCanonicalName() + " returned "
+ toLogString(returnTypeFromRp) + " - Must return a resource type");
}
}
Class<?> returnTypeFromMethod = theMethod.getReturnType();
if (Collection.class.isAssignableFrom(returnTypeFromMethod)) {
if (MethodOutcome.class.equals(returnTypeFromMethod)) {
// returns a method outcome
}else if (Bundle.class.equals(returnTypeFromMethod)) {
// returns a bundle
}else if (void.class.equals(returnTypeFromMethod)) {
// returns a bundle
} else if (Collection.class.isAssignableFrom(returnTypeFromMethod)) {
returnTypeFromMethod = ReflectionUtil.getGenericCollectionTypeOfMethodReturnType(theMethod);
if (!verifyIsValidResourceReturnType(returnTypeFromMethod)) {
throw new ConfigurationException("Method '" + theMethod.getName() + "' from " + IResourceProvider.class.getSimpleName() + " type " + theMethod.getDeclaringClass().getCanonicalName()
+ " returns a collection with generic type " + toLogString(returnTypeFromMethod) + " - Must return a resource type or a collection (List, Set) of a resource type");
}
} else {
if (!verifyIsValidResourceReturnType(returnTypeFromMethod)) {
throw new ConfigurationException("Method '" + theMethod.getName() + "' from " + IResourceProvider.class.getSimpleName() + " type " + theMethod.getDeclaringClass().getCanonicalName()
+ " returns " + toLogString(returnTypeFromMethod) + " - Must return a resource type");
}
}
if (Bundle.class.isAssignableFrom(returnTypeFromMethod)) {
returnTypeFromMethod = returnTypeFromRp;
}
if (!IResource.class.isAssignableFrom(returnTypeFromMethod)) {
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type " + theMethod.getDeclaringClass().getCanonicalName() + " returns type " + returnTypeFromMethod.getCanonicalName() + " - Must return a resource type");
Class<? extends IResource> returnTypeFromAnnotation = IResource.class;
if (read != null) {
returnTypeFromAnnotation = read.type();
} else if (search != null) {
returnTypeFromAnnotation = search.type();
} else if (history != null) {
returnTypeFromAnnotation = history.type();
} else if (delete != null) {
returnTypeFromAnnotation = delete.type();
} else if (create != null) {
returnTypeFromAnnotation = create.type();
} else if (update != null) {
returnTypeFromAnnotation = update.type();
}
if (returnTypeFromRp != null) {
if (returnTypeFromRp.equals(returnTypeFromMethod) == false) {
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type " + theMethod.getDeclaringClass().getCanonicalName() + " returns type " + returnTypeFromMethod.getCanonicalName() + " but must return " + returnTypeFromRp.getCanonicalName());
if (returnTypeFromAnnotation != null && returnTypeFromAnnotation != IResource.class) {
if (!returnTypeFromRp.isAssignableFrom(returnTypeFromAnnotation)) {
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type " + theMethod.getDeclaringClass().getCanonicalName() + " returns type "
+ returnTypeFromMethod.getCanonicalName() + " - Must return " + returnTypeFromRp.getCanonicalName() + " (or a subclass of it) per IResourceProvider contract");
}
if (!returnTypeFromRp.isAssignableFrom(returnTypeFromAnnotation)) {
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type " + theMethod.getDeclaringClass().getCanonicalName() + " claims to return type "
+ returnTypeFromAnnotation.getCanonicalName() + " per method annotation - Must return " + returnTypeFromRp.getCanonicalName()
+ " (or a subclass of it) per IResourceProvider contract");
}
returnType = returnTypeFromAnnotation;
} else {
returnType = returnTypeFromRp;
}
} else {
if (returnTypeFromAnnotation != IResource.class) {
if (!verifyIsValidResourceReturnType(returnTypeFromAnnotation)) {
throw new ConfigurationException("Method '"+theMethod.getName() + "' from " + IResourceProvider.class.getSimpleName() + " type " + theMethod.getDeclaringClass().getCanonicalName() + " returns "
+ toLogString(returnTypeFromAnnotation) + " according to annotation - Must return a resource type");
}
returnType=returnTypeFromAnnotation;
} else {
returnType = (Class<? extends IResource>) returnTypeFromMethod;
}
}
if (returnTypeFromMethod == null) {
if (read != null) {
returnTypeFromMethod = read.type();
} else if (search != null) {
returnTypeFromMethod = search.type();
}
if (returnTypeFromMethod == IResource.class) {
throw new ConfigurationException("Could not determine return type for method '" + theMethod.getName() + "'. Try explicitly specifying one in the operation annotation.");
}
}
@SuppressWarnings("unchecked")
Class<? extends IResource> retType = (Class<? extends IResource>) returnTypeFromMethod;
if (read != null) {
return new ReadMethodBinding(retType, theMethod, theContext, theProvider);
return new ReadMethodBinding(returnType, theMethod, theContext, theProvider);
} else if (search != null) {
String queryName = search.queryName();
return new SearchMethodBinding(retType, theMethod, queryName, theContext, theProvider);
return new SearchMethodBinding(returnType, theMethod, queryName, theContext, theProvider);
} else if (conformance != null) {
return new ConformanceMethodBinding(theMethod, theContext, theProvider);
} else if (create != null) {
@ -184,16 +224,18 @@ public abstract class BaseMethodBinding {
}
private static boolean verifyIsValidResourceReturnType(Class<?> theReturnType) {
if (theReturnType==null) {
if (theReturnType == null) {
return false;
}
if (!IResource.class.isAssignableFrom(theReturnType)) {
return false;
}
boolean retVal = Modifier.isAbstract(theReturnType.getModifiers()) == false;
return retVal;
}
private static String toLogString(Class<? extends IResource> theType) {
if (theType==null) {
private static String toLogString(Class<?> theType) {
if (theType == null) {
return null;
}
return theType.getCanonicalName();
@ -206,8 +248,8 @@ public abstract class BaseMethodBinding {
if (obj1 == null) {
obj1 = object;
} else {
throw new ConfigurationException("Method " + theNextMethod.getName() + " on type '" + theNextMethod.getDeclaringClass().getSimpleName() + " has annotations @" + obj1.getClass().getSimpleName() + " and @" + object.getClass().getSimpleName()
+ ". Can not have both.");
throw new ConfigurationException("Method " + theNextMethod.getName() + " on type '" + theNextMethod.getDeclaringClass().getSimpleName() + " has annotations @"
+ obj1.getClass().getSimpleName() + " and @" + object.getClass().getSimpleName() + ". Can not have both.");
}
}
@ -263,11 +305,7 @@ public abstract class BaseMethodBinding {
public abstract void invokeServer(RestfulServer theServer, Request theRequest, HttpServletResponse theResponse) throws BaseServerResponseException, IOException;
public abstract Object invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException;
public static BaseMethodBinding bindSystemMethod(Method theMethod, FhirContext theContext) {
return bindMethod(null, theMethod, theContext, null);
}
public abstract Object invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException,
BaseServerResponseException;
}

View File

@ -32,8 +32,8 @@ public class DeleteMethodBinding extends BaseOutcomeReturningMethodBinding {
theProvider);
Delete deleteAnnotation = theMethod.getAnnotation(Delete.class);
Class<? extends IResource> resourceType = deleteAnnotation.resourceType();
if (resourceType != Delete.NotSpecified.class) {
Class<? extends IResource> resourceType = deleteAnnotation.type();
if (resourceType != IResource.class) {
RuntimeResourceDefinition def = theContext.getResourceDefinition(resourceType);
myResourceName = def.getName();
} else {

View File

@ -43,8 +43,8 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
myCountParamIndex = Util.findCountParameterIndex(theMethod);
History historyAnnotation = theMethod.getAnnotation(History.class);
Class<? extends IResource> type = historyAnnotation.resourceType();
if (type == History.AllResources.class) {
Class<? extends IResource> type = historyAnnotation.type();
if (type == IResource.class) {
if (theProvider instanceof IResourceProvider) {
type = ((IResourceProvider) theProvider).getResourceType();
if (myIdParamIndex != null) {
@ -66,7 +66,7 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
mySystemOperationType = null;
}
if (type != History.AllResources.class) {
if (type != IResource.class) {
myResourceName = theConetxt.getResourceDefinition(type).getName();
} else {
myResourceName = null;
@ -79,8 +79,8 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
return ((IResourceProvider) theProvider).getResourceType();
}
History historyAnnotation = theMethod.getAnnotation(History.class);
Class<? extends IResource> type = historyAnnotation.resourceType();
if (type != History.AllResources.class) {
Class<? extends IResource> type = historyAnnotation.type();
if (type != IResource.class) {
return type;
}
return null;

View File

@ -4,6 +4,7 @@ import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import ca.uhn.fhir.context.ConfigurationException;
@ -26,7 +27,7 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding {
private int myParameterCount;
public ReadMethodBinding(Class<? extends IResource> theAnnotatedResourceType, Method theMethod, FhirContext theContext, Object theProvider) {
super( theAnnotatedResourceType, theMethod, theContext, theProvider);
super(theAnnotatedResourceType, theMethod, theContext, theProvider);
Validate.notNull(theMethod, "Method must not be null");
@ -39,10 +40,10 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding {
Class<?>[] parameterTypes = theMethod.getParameterTypes();
if (!IdDt.class.equals(parameterTypes[myIdIndex])) {
throw new ConfigurationException("ID parameter must be of type: " + IdDt.class.getCanonicalName() + " - Found: "+parameterTypes[myIdIndex]);
throw new ConfigurationException("ID parameter must be of type: " + IdDt.class.getCanonicalName() + " - Found: " + parameterTypes[myIdIndex]);
}
if (myVersionIdIndex != null && !IdDt.class.equals(parameterTypes[myVersionIdIndex])) {
throw new ConfigurationException("Version ID parameter must be of type: " + IdDt.class.getCanonicalName()+ " - Found: "+parameterTypes[myVersionIdIndex]);
throw new ConfigurationException("Version ID parameter must be of type: " + IdDt.class.getCanonicalName() + " - Found: " + parameterTypes[myVersionIdIndex]);
}
}
@ -71,6 +72,13 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding {
ourLog.trace("Method {} doesn't match because request type is not GET: {}", theRequest.getId(), theRequest.getRequestType());
return false;
}
if (Constants.PARAM_HISTORY.equals(theRequest.getOperation())) {
if (myVersionIdIndex == null) {
return false;
}
} else if (!StringUtils.isBlank(theRequest.getOperation())) {
return false;
}
return true;
}
@ -80,8 +88,7 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding {
}
@Override
public List<IResource> invokeServer(Object theResourceProvider, IdDt theId, IdDt theVersionId, Map<String, String[]> theParameterValues) throws InvalidRequestException,
InternalErrorException {
public List<IResource> invokeServer(Object theResourceProvider, IdDt theId, IdDt theVersionId, Map<String, String[]> theParameterValues) throws InvalidRequestException, InternalErrorException {
Object[] params = new Object[myParameterCount];
params[myIdIndex] = theId;
if (myVersionIdIndex != null) {
@ -92,7 +99,7 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding {
try {
response = getMethod().invoke(theResourceProvider, params);
} catch (Exception e) {
throw new InternalErrorException("Failed to call access method",e);
throw new InternalErrorException("Failed to call access method", e);
}
return toResourceList(response);
@ -100,11 +107,11 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding {
@Override
public GetClientInvocation invokeClient(Object[] theArgs) {
String id = ((IdDt)theArgs[myIdIndex]).getValue();
String id = ((IdDt) theArgs[myIdIndex]).getValue();
if (myVersionIdIndex == null) {
return new GetClientInvocation(getResourceName(), id);
}else {
String vid = ((IdDt)theArgs[myVersionIdIndex]).getValue();
} else {
String vid = ((IdDt) theArgs[myVersionIdIndex]).getValue();
return new GetClientInvocation(getResourceName(), id, Constants.URL_TOKEN_HISTORY, vid);
}
}

View File

@ -129,11 +129,11 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
ourLog.trace("Method {} doesn't match because ID or Version are not null: {} - {}", theRequest.getId(), theRequest.getVersion());
return false;
}
if (theRequest.getRequestType() == RequestType.GET && theRequest.getOperation() != null) {
if (theRequest.getRequestType() == RequestType.GET && theRequest.getOperation() != null && !Constants.PARAM_SEARCH.equals(theRequest.getOperation())) {
ourLog.trace("Method {} doesn't match because request type is GET but operation is not null: {}", theRequest.getId(), theRequest.getOperation());
return false;
}
if (theRequest.getRequestType() == RequestType.POST && !"_search".equals(theRequest.getOperation())) {
if (theRequest.getRequestType() == RequestType.POST && !Constants.PARAM_SEARCH.equals(theRequest.getOperation())) {
ourLog.trace("Method {} doesn't match because request type is POST but operation is not _search: {}", theRequest.getId(), theRequest.getOperation());
return false;
}

View File

@ -36,6 +36,7 @@ public class Constants {
public static final int STATUS_HTTP_204_NO_CONTENT = 204;
public static final String PARAM_COUNT = "_count";
public static final String PARAM_SINCE = "_since";
public static final String PARAM_SEARCH = "_search";
static {
Map<String, EncodingUtil> valToEncoding = new HashMap<String, EncodingUtil>();

View File

@ -1,6 +1,7 @@
package ca.uhn.fhir.rest.server;
import java.io.IOException;
import java.io.StringReader;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
@ -292,7 +293,7 @@ public class RestfulServer extends HttpServlet {
BaseMethodBinding foundMethodBinding = BaseMethodBinding.bindMethod(m, myFhirContext, theProvider);
if (foundMethodBinding != null) {
RuntimeResourceDefinition definition = myFhirContext.getResourceDefinition(retType);
RuntimeResourceDefinition definition = myFhirContext.getResourceDefinition(foundMethodBinding.getResourceName());
ResourceBinding resourceBinding;
if (myResourceNameToProvider.containsKey(definition.getName())) {
resourceBinding = myResourceNameToProvider.get(definition.getName());
@ -318,7 +319,7 @@ public class RestfulServer extends HttpServlet {
if (Modifier.isPublic(m.getModifiers())) {
ourLog.debug("Scanning public method: {}#{}", theSystemProvider.getClass(), m.getName());
BaseMethodBinding foundMethodBinding = BaseMethodBinding.bindSystemMethod(m, myFhirContext);
BaseMethodBinding foundMethodBinding = BaseMethodBinding.bindMethod(m, myFhirContext, theSystemProvider);
if (foundMethodBinding != null) {
if (foundMethodBinding instanceof ConformanceMethodBinding) {
myServerConformanceMethod = foundMethodBinding;
@ -476,7 +477,11 @@ public class RestfulServer extends HttpServlet {
r.setOperation(operation);
r.setParameters(params);
r.setRequestType(requestType);
if ("application/x-www-form-urlencoded".equals(request.getContentType())) {
r.setInputReader(new StringReader(""));
} else {
r.setInputReader(request.getReader());
}
r.setFhirServerBase(fhirServerBase);
r.setCompleteUrl(completeUrl);
r.setServletRequest(request);

View File

@ -375,11 +375,11 @@ public interface HistoryClient {
Bundle getHistoryServer();
// Type level (history of all resources of a given type)
@History(resourceType=Patient.class)
@History(type=Patient.class)
Bundle getHistoryPatientType();
// Instance level (history of a specific resource instance by type and ID)
@History(resourceType=Patient.class)
@History(type=Patient.class)
Bundle getHistoryPatientInstance(@IdParam IdDt theId);
//END SNIPPET: historyClient

View File

@ -56,10 +56,10 @@ public interface ITestClient extends IBasicClient {
@Update
public MethodOutcome updatePatient(@IdParam IdDt theId, @ResourceParam Patient thePatient);
@Delete(resourceType=DiagnosticReport.class)
@Delete(type=DiagnosticReport.class)
void deleteDiagnosticReport(@IdParam IdDt theId);
@Delete(resourceType=Patient.class)
@Delete(type=Patient.class)
MethodOutcome deletePatient(@IdParam IdDt theId);
@Search(type=Patient.class)
@ -68,10 +68,10 @@ public interface ITestClient extends IBasicClient {
@Search(type=Patient.class)
Bundle findPatientByName(@RequiredParam(name = Patient.SP_FAMILY) StringDt theId, @OptionalParam(name=Patient.SP_GIVEN) StringDt theGiven);
@History(resourceType=Patient.class)
@History(type=Patient.class)
Bundle getHistoryPatientInstance(@IdParam IdDt theId);
@History(resourceType=Patient.class)
@History(type=Patient.class)
Bundle getHistoryPatientType();
@History

View File

@ -1,7 +1,11 @@
package ca.uhn.fhir.rest.server;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import javax.servlet.ServletException;
import org.hamcrest.core.StringContains;
import org.junit.Test;
import ca.uhn.fhir.model.api.BaseResource;
@ -10,15 +14,20 @@ import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.rest.server.exceptions.ConfigurationException;
public class ServerInvalidDefinitionTest {
@Test(expected=ConfigurationException.class)
public void testNonInstantiableTypeForResourceProvider() throws ServletException {
@Test
public void testNonInstantiableTypeForResourceProvider() {
RestfulServer srv = new RestfulServer();
srv.setResourceProviders(new NonInstantiableTypeForResourceProvider());
try {
srv.init();
fail();
} catch (ServletException e) {
assertThat(e.getCause().toString(), StringContains.containsString("ConfigurationException"));
}
}
/**