From 785997aade7e2b7cf3be16a4755db6d0b54ffe42 Mon Sep 17 00:00:00 2001 From: ttn Date: Thu, 30 Jan 2020 16:24:18 +0100 Subject: [PATCH] Resource methods in interfaces (default implementations) are now picked up when registering providers --- .../uhn/fhir/rest/server/RestfulServer.java | 20 +++ .../fhir/rest/server/RestfulServerTest.java | 124 ++++++++++++++++++ 2 files changed, 144 insertions(+) create mode 100644 hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/RestfulServerTest.java diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java index 08cd779442c..3c32507105a 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java @@ -360,11 +360,13 @@ public class RestfulServer extends HttpServlet implements IRestfulServer supertype = clazz.getSuperclass(); while (!Object.class.equals(supertype)) { count += findResourceMethods(theProvider, supertype); + count += findResourceMethodsOnInterfaces(theProvider, supertype.getInterfaces()); supertype = supertype.getSuperclass(); } try { count += findResourceMethods(theProvider, clazz); + count += findResourceMethodsOnInterfaces(theProvider, clazz.getInterfaces()); } catch (ConfigurationException e) { throw new ConfigurationException("Failure scanning class " + clazz.getSimpleName() + ": " + e.getMessage(), e); } @@ -373,6 +375,15 @@ public class RestfulServer extends HttpServlet implements IRestfulServer[] interfaces) { + int count = 0; + for (Class anInterface : interfaces) { + count += findResourceMethods(theProvider, anInterface); + count += findResourceMethodsOnInterfaces(theProvider, anInterface.getInterfaces()); + } + return count; + } + private int findResourceMethods(Object theProvider, Class clazz) throws ConfigurationException { int count = 0; @@ -1604,14 +1615,23 @@ public class RestfulServer extends HttpServlet implements IRestfulServer resourceNames = new ArrayList<>(); while (!Object.class.equals(supertype)) { removeResourceMethods(theProvider, supertype, resourceNames); + removeResourceMethodsOnInterfaces(theProvider, supertype.getInterfaces(), resourceNames); supertype = supertype.getSuperclass(); } removeResourceMethods(theProvider, clazz, resourceNames); + removeResourceMethodsOnInterfaces(theProvider, clazz.getInterfaces(), resourceNames); for (String resourceName : resourceNames) { myResourceNameToBinding.remove(resourceName); } } + private void removeResourceMethodsOnInterfaces(Object theProvider, Class[] interfaces, Collection resourceNames) { + for (Class anInterface : interfaces) { + removeResourceMethods(theProvider, anInterface, resourceNames); + removeResourceMethodsOnInterfaces(theProvider, anInterface.getInterfaces(), resourceNames); + } + } + /* * Collect the set of RESTful methods for a single class when it is being unregistered */ diff --git a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/RestfulServerTest.java b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/RestfulServerTest.java new file mode 100644 index 00000000000..315f18ff9b5 --- /dev/null +++ b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/RestfulServerTest.java @@ -0,0 +1,124 @@ +package ca.uhn.fhir.rest.server; + +import ca.uhn.fhir.context.ConfigurationException; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.fhir.rest.annotation.Create; +import ca.uhn.fhir.rest.annotation.Metadata; +import ca.uhn.fhir.rest.annotation.ResourceParam; +import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.rest.api.server.IFhirVersionServer; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import org.hl7.fhir.instance.model.api.IBaseConformance; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Answers; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import java.io.Serializable; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class RestfulServerTest { + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private FhirContext myCtx; + private RestfulServer restfulServer; + + @Before + public void setUp() throws ServletException { + when(myCtx.getVersion().getVersion()).thenReturn(FhirVersionEnum.DSTU3); + when(myCtx.getVersion().getServerVersion()).thenReturn(new MyFhirVersionServer()); + + restfulServer = new RestfulServer(myCtx); + restfulServer.init(); + } + + @Test + public void testRegisterProviders() { + //test register Plain Provider + restfulServer.registerProvider(new MyClassWithRestInterface()); + assertEquals(1, restfulServer.getPlainProviders().size()); + Object plainProvider = restfulServer.getPlainProviders().iterator().next(); + assertTrue(plainProvider instanceof MyClassWithRestInterface); + + //test register Resource Provider + restfulServer.registerProvider(new MyResourceProvider()); + assertEquals(1, restfulServer.getResourceProviders().size()); + IResourceProvider resourceProvider = restfulServer.getResourceProviders().iterator().next(); + assertTrue(resourceProvider instanceof MyResourceProvider); + + //test unregister providers + restfulServer.unregisterProvider(plainProvider); + assertTrue(restfulServer.getPlainProviders().isEmpty()); + restfulServer.unregisterProvider(resourceProvider); + assertTrue(restfulServer.getResourceProviders().isEmpty()); + } + + @Test(expected = ConfigurationException.class) + public void testFailRegisterInterfaceProviderWithoutRestfulMethod() { + restfulServer.registerProvider(new MyClassWithoutRestInterface()); + } + + + //--------- Scaffolding ---------// + private static class MyClassWithoutRestInterface implements Serializable { + } + + private static class MyClassWithRestInterface implements MyRestInterface { + } + + @SuppressWarnings("unused") + private interface MyRestInterface { + @Create + default MethodOutcome create(@ResourceParam IBaseResource theResource) { + return mock(MethodOutcome.class); + } + + } + + private static class MyFhirVersionServer implements IFhirVersionServer { + @Override + public IServerConformanceProvider createServerConformanceProvider(RestfulServer theRestfulServer) { + return new IServerConformanceProvider() { + + @Override + @Metadata + public IBaseConformance getServerConformance(HttpServletRequest theRequest, RequestDetails theRequestDetails) { + return mock(IBaseConformance.class); + } + + @Override + public void setRestfulServer(RestfulServer theRestfulServer) { + } + }; + } + + @Override + public IResourceProvider createServerProfilesProvider(RestfulServer theRestfulServer) { + return new MyResourceProvider(); + } + } + + @SuppressWarnings("unused") + private static class MyResourceProvider implements IResourceProvider { + @Create + public MethodOutcome create(@ResourceParam IBaseResource theResource) { + return mock(MethodOutcome.class); + } + + @Override + public Class getResourceType() { + return IBaseResource.class; + } + } + +}