Catering to all possible routes for Post Request (#700)

* Catering to all possible routes for Post Request

Signed-off-by: deepanshuagarwal <deepanshu.agarwal1984@gmail.com>

* Correcting checkstyle related violations

Signed-off-by: deepanshuagarwal <deepanshu.agarwal1984@gmail.com>

* Refactoring and Adding UTs for post method routes

Signed-off-by: deepanshuagarwal <deepanshu.agarwal1984@gmail.com>

* Using resolved topic name

Signed-off-by: deepanshuagarwal <deepanshu.agarwal1984@gmail.com>

* Shifting routesPostMethod to reduce its access

Signed-off-by: deepanshuagarwal <deepanshu.agarwal1984@gmail.com>

Co-authored-by: Mukundan Sundararajan <65565396+mukundansundar@users.noreply.github.com>
This commit is contained in:
deepanshuagarwal 2022-03-21 14:27:29 +05:30 committed by GitHub
parent 06d92dafca
commit 593da48bc4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 227 additions and 10 deletions

View File

@ -88,6 +88,11 @@
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
@ -149,6 +154,9 @@
<rules>
<rule>
<element>BUNDLE</element>
<includes>
<include>io.dapr.springboot.DaprBeanPostProcessor</include>
</includes>
<limits>
<limit>
<counter>LINE</counter>

View File

@ -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<HashMap<String, String>> typeRef
= new TypeReference<HashMap<String, String>>() {};
Map<String, String> metadata = MAPPER.readValue(topic.metadata(), typeRef);
DaprRuntime.getInstance().addSubscribedTopic(pubSubName, topicName, route, metadata);
List<String> 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<String> getAllCompleteRoutesForPost(Class clazz, Method method, String topicName) {
List<String> 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;
}
}

View File

@ -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<String> routesArrayTestMethod1 = null;
try {
routesArrayTestMethod1 = (List<String>) 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));
}
}

View File

@ -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
}
}

View File

@ -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
}
}