diff --git a/sdk-springboot/pom.xml b/sdk-springboot/pom.xml index 042c06580..cd6b2df50 100644 --- a/sdk-springboot/pom.xml +++ b/sdk-springboot/pom.xml @@ -88,6 +88,11 @@ compile true + + junit + junit + test + @@ -149,6 +154,9 @@ BUNDLE + + io.dapr.springboot.DaprBeanPostProcessor + LINE diff --git a/sdk-springboot/src/main/java/io/dapr/springboot/DaprBeanPostProcessor.java b/sdk-springboot/src/main/java/io/dapr/springboot/DaprBeanPostProcessor.java index 56cae1b29..cc591184a 100644 --- a/sdk-springboot/src/main/java/io/dapr/springboot/DaprBeanPostProcessor.java +++ b/sdk-springboot/src/main/java/io/dapr/springboot/DaprBeanPostProcessor.java @@ -23,9 +23,14 @@ import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.EmbeddedValueResolver; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import java.lang.annotation.Annotation; import java.lang.reflect.Method; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; /** @@ -80,15 +85,6 @@ public class DaprBeanPostProcessor implements BeanPostProcessor { continue; } - String route = topic.name(); - PostMapping mapping = method.getAnnotation(PostMapping.class); - - if (mapping != null && mapping.path() != null && mapping.path().length >= 1) { - route = mapping.path()[0]; - } else if (mapping != null && mapping.value() != null && mapping.value().length >= 1) { - route = mapping.value()[0]; - } - String topicName = embeddedValueResolver.resolveStringValue(topic.name()); String pubSubName = embeddedValueResolver.resolveStringValue(topic.pubsubName()); if ((topicName != null) && (topicName.length() > 0) && pubSubName != null && pubSubName.length() > 0) { @@ -96,11 +92,84 @@ public class DaprBeanPostProcessor implements BeanPostProcessor { TypeReference> typeRef = new TypeReference>() {}; Map metadata = MAPPER.readValue(topic.metadata(), typeRef); - DaprRuntime.getInstance().addSubscribedTopic(pubSubName, topicName, route, metadata); + List routes = getAllCompleteRoutesForPost(clazz, method, topicName); + for (String route : routes) { + DaprRuntime.getInstance().addSubscribedTopic(pubSubName, topicName, route, + metadata); + } + } catch (JsonProcessingException e) { throw new IllegalArgumentException("Error while parsing metadata: " + e.toString()); } } } } + + /** + * Method to provide all possible complete routes list fos this post method present in this controller class, + * for mentioned topic. + * + * @param clazz Controller class + * @param method Declared method for posting data + * @param topicName Associated topic name + * @return All possible routes for post mapping for this class and post method + */ + private static List getAllCompleteRoutesForPost(Class clazz, Method method, String topicName) { + List routesList = new ArrayList<>(); + RequestMapping clazzRequestMapping = + (RequestMapping) clazz.getAnnotation(RequestMapping.class); + String[] clazzLevelRoute = null; + if (clazzRequestMapping != null) { + clazzLevelRoute = clazzRequestMapping.value(); + } + String[] postValueArray = getRoutesForPost(method, topicName); + if (postValueArray != null && postValueArray.length >= 1) { + for (String postValue : postValueArray) { + if (clazzLevelRoute != null && clazzLevelRoute.length >= 1) { + for (String clazzLevelValue : clazzLevelRoute) { + String route = clazzLevelValue + confirmLeadingSlash(postValue); + routesList.add(route); + } + } else { + routesList.add(postValue); + } + } + } + return routesList; + } + + private static String[] getRoutesForPost(Method method, String topicName) { + String[] postValueArray = new String[] {topicName}; + PostMapping postMapping = method.getAnnotation(PostMapping.class); + if (postMapping != null) { + if (postMapping.path() != null && postMapping.path().length >= 1) { + postValueArray = postMapping.path(); + } else if (postMapping.value() != null && postMapping.value().length >= 1) { + postValueArray = postMapping.value(); + } + } else { + RequestMapping reqMapping = method.getAnnotation(RequestMapping.class); + for (RequestMethod reqMethod : reqMapping.method()) { + if (reqMethod == RequestMethod.POST) { + if (reqMapping.path() != null && reqMapping.path().length >= 1) { + postValueArray = reqMapping.path(); + } else if (reqMapping.value() != null && reqMapping.value().length >= 1) { + postValueArray = reqMapping.value(); + } + break; + } + } + } + return postValueArray; + } + + private static String confirmLeadingSlash(String path) { + if (path != null && path.length() >= 1) { + if (!path.substring(0, 1).equals("/")) { + return "/" + path; + } + } + return path; + } + } diff --git a/sdk-springboot/src/test/java/io/dapr/springboot/DaprBeanPostProcessorTest.java b/sdk-springboot/src/test/java/io/dapr/springboot/DaprBeanPostProcessorTest.java new file mode 100644 index 000000000..403d5e21c --- /dev/null +++ b/sdk-springboot/src/test/java/io/dapr/springboot/DaprBeanPostProcessorTest.java @@ -0,0 +1,69 @@ +package io.dapr.springboot; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +@RunWith(Parameterized.class) +public class DaprBeanPostProcessorTest { + + private final Class clazzToBeTested; + private final String methodToBeTested; + private final String[] expected; + private final boolean expectedResult; + private static final String TOPIC_NAME = "topicName1"; + + public DaprBeanPostProcessorTest(Class clazzToBeTested, String methodToBeTested, String[] expected, + boolean expectedResult) { + this.clazzToBeTested = clazzToBeTested; + this.methodToBeTested = methodToBeTested; + this.expected = expected; + this.expectedResult = expectedResult; + } + + @Parameterized.Parameters + public static Collection routesTester() { + return Arrays.asList(new Object[][] { + {MockController.class, "testMethod1", new String[] {"v1", "v2", "v1/page1", "v2/page1", "v1/page2", "v2/page2"}, + true}, + {MockController.class, "testMethod2", new String[] {"v1", "v2", "v1/page3", "v2/page3", "v1/page4", "v2/page4"}, + true}, + {MockController.class, "testMethod3", new String[] {"v1/foo", "v2/foo"}, true}, + {MockController.class, "testMethod4", new String[] {"v1/foo1", "v2/foo1", "v1/foo2", "v2/foo2"}, true}, + {MockController.class, "testMethod5", new String[] {"v1/" + TOPIC_NAME, "v2/" + TOPIC_NAME}, true}, + {MockControllerNoClazzAnnotation.class, "testMethod1", new String[] {"", "page1", "page2"}, true}, + {MockControllerNoClazzAnnotation.class, "testMethod2", new String[] {"", "page3", "page4"}, true}, + {MockControllerNoClazzAnnotation.class, "testMethod3", new String[] {"foo"}, true}, + {MockControllerNoClazzAnnotation.class, "testMethod4", new String[] {"foo1", "foo2"}, true}, + {MockControllerNoClazzAnnotation.class, "testMethod5", new String[] {TOPIC_NAME}, true} + }); + } + + @Test + public void testAllPostRoutesGeneration() throws NoSuchMethodException { + Method allPostRoutesMethod = DaprBeanPostProcessor.class. + getDeclaredMethod("getAllCompleteRoutesForPost", Class.class, Method.class, String.class); + allPostRoutesMethod.setAccessible(true); + List routesArrayTestMethod1 = null; + try { + routesArrayTestMethod1 = (List) allPostRoutesMethod.invoke(DaprBeanPostProcessor.class, clazzToBeTested, + clazzToBeTested.getMethod(methodToBeTested), TOPIC_NAME); + } catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + Assert.assertEquals(expectedResult, + testingListForOrderAgnosticEquality(Arrays.asList(expected), routesArrayTestMethod1)); + } + + private boolean testingListForOrderAgnosticEquality(List first, List second) { + return (first.size() == second.size() && first.containsAll(second) && second.containsAll(first)); + } + +} diff --git a/sdk-springboot/src/test/java/io/dapr/springboot/MockController.java b/sdk-springboot/src/test/java/io/dapr/springboot/MockController.java new file mode 100644 index 000000000..ee32c15cd --- /dev/null +++ b/sdk-springboot/src/test/java/io/dapr/springboot/MockController.java @@ -0,0 +1,35 @@ +package io.dapr.springboot; + +import org.springframework.web.bind.annotation.*; + +@RequestMapping(value = {"v1", "v2"}) +public class MockController { + + @RequestMapping(value = {"", "/page1", "page2"}, method = {RequestMethod.POST, RequestMethod.PUT}) + public void testMethod1() { + // Do nothing + } + + @PostMapping(path = {"", "/page3", "page4"}) + public void testMethod2() { + // Do nothing + } + + @PostMapping("foo") + public void testMethod3() { + // Do nothing + } + + @PostMapping({"/foo1", "foo2"}) + public void testMethod4() { + // Do nothing + } + + + @RequestMapping(path = {"/bar", "bar1"}, method = {RequestMethod.GET}) + public void testMethod5() { + // Do nothing + } + + +} \ No newline at end of file diff --git a/sdk-springboot/src/test/java/io/dapr/springboot/MockControllerNoClazzAnnotation.java b/sdk-springboot/src/test/java/io/dapr/springboot/MockControllerNoClazzAnnotation.java new file mode 100644 index 000000000..1a64920db --- /dev/null +++ b/sdk-springboot/src/test/java/io/dapr/springboot/MockControllerNoClazzAnnotation.java @@ -0,0 +1,36 @@ +package io.dapr.springboot; + +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +public class MockControllerNoClazzAnnotation { + + @RequestMapping(value = {"", "page1", "page2"}, method = {RequestMethod.POST, RequestMethod.PUT}) + public void testMethod1() { + // Do nothing + } + + @PostMapping(path = {"", "page3", "page4"}) + public void testMethod2() { + // Do nothing + } + + @PostMapping("foo") + public void testMethod3() { + // Do nothing + } + + @PostMapping({"foo1", "foo2"}) + public void testMethod4() { + // Do nothing + } + + + @RequestMapping(path = {"bar", "bar1"}, method = {RequestMethod.GET}) + public void testMethod5() { + // Do nothing + } + + +} \ No newline at end of file