Fix and test bug with generic input or output types
Without this fix a Function<Foo<Bar>,...> shows as having an input type of Bar - we need to only take the parameter if the raw type is Flux.
This commit is contained in:
@@ -396,21 +396,23 @@ public class ContextFunctionCatalogAutoConfiguration {
|
|||||||
String.class)));
|
String.class)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Class<?> findType(AbstractBeanDefinition definition, ParamType paramType) {
|
private Class<?> findType(AbstractBeanDefinition definition,
|
||||||
|
ParamType paramType) {
|
||||||
Object source = definition.getSource();
|
Object source = definition.getSource();
|
||||||
Type param;
|
Type param;
|
||||||
// Start by assuming output -> Function
|
// Start by assuming output -> Function
|
||||||
int index = paramType==ParamType.OUTPUT ? 1 : 0;
|
int index = paramType == ParamType.OUTPUT ? 1 : 0;
|
||||||
if (source instanceof StandardMethodMetadata) {
|
if (source instanceof StandardMethodMetadata) {
|
||||||
ParameterizedType type;
|
ParameterizedType type;
|
||||||
type = (ParameterizedType) ((StandardMethodMetadata) source)
|
type = (ParameterizedType) ((StandardMethodMetadata) source)
|
||||||
.getIntrospectedMethod().getGenericReturnType();
|
.getIntrospectedMethod().getGenericReturnType();
|
||||||
if (type.getActualTypeArguments().length==1) {
|
if (type.getActualTypeArguments().length == 1) {
|
||||||
// There's only one
|
// There's only one
|
||||||
index = 0;
|
index = 0;
|
||||||
}
|
}
|
||||||
Type typeArgumentAtIndex = type.getActualTypeArguments()[index];
|
Type typeArgumentAtIndex = type.getActualTypeArguments()[index];
|
||||||
if (typeArgumentAtIndex instanceof ParameterizedType) {
|
if (typeArgumentAtIndex instanceof ParameterizedType && Flux.class
|
||||||
|
.equals(((ParameterizedType) typeArgumentAtIndex).getRawType())) {
|
||||||
param = ((ParameterizedType) typeArgumentAtIndex)
|
param = ((ParameterizedType) typeArgumentAtIndex)
|
||||||
.getActualTypeArguments()[0];
|
.getActualTypeArguments()[0];
|
||||||
}
|
}
|
||||||
@@ -470,11 +472,12 @@ public class ContextFunctionCatalogAutoConfiguration {
|
|||||||
if (!registry.containsBeanDefinition(name)) {
|
if (!registry.containsBeanDefinition(name)) {
|
||||||
return Object.class;
|
return Object.class;
|
||||||
}
|
}
|
||||||
return findType((AbstractBeanDefinition) registry.getBeanDefinition(name), ParamType.INPUT);
|
return findType((AbstractBeanDefinition) registry.getBeanDefinition(name),
|
||||||
|
ParamType.INPUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Class<?> findOutputType(String name) {
|
private Class<?> findOutputType(String name) {
|
||||||
if (name==null || !registry.containsBeanDefinition(name)) {
|
if (name == null || !registry.containsBeanDefinition(name)) {
|
||||||
return Object.class;
|
return Object.class;
|
||||||
}
|
}
|
||||||
BeanDefinition definition = registry.getBeanDefinition(name);
|
BeanDefinition definition = registry.getBeanDefinition(name);
|
||||||
|
|||||||
@@ -18,9 +18,11 @@ package org.springframework.cloud.function.context;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -44,6 +46,7 @@ public class ContextFunctionCatalogAutoConfigurationTests {
|
|||||||
|
|
||||||
private ConfigurableApplicationContext context;
|
private ConfigurableApplicationContext context;
|
||||||
private InMemoryFunctionCatalog catalog;
|
private InMemoryFunctionCatalog catalog;
|
||||||
|
private FunctionInspector inspector;
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void close() {
|
public void close() {
|
||||||
@@ -59,6 +62,14 @@ public class ContextFunctionCatalogAutoConfigurationTests {
|
|||||||
assertThat(catalog.lookupFunction("function")).isInstanceOf(Function.class);
|
assertThat(catalog.lookupFunction("function")).isInstanceOf(Function.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void genericFunction() {
|
||||||
|
create(GenericConfiguration.class);
|
||||||
|
assertThat(context.getBean("function")).isInstanceOf(Function.class);
|
||||||
|
assertThat(catalog.lookupFunction("function")).isInstanceOf(Function.class);
|
||||||
|
assertThat(inspector.getInputType("function")).isAssignableFrom(Map.class);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void simpleSupplier() {
|
public void simpleSupplier() {
|
||||||
create(SimpleConfiguration.class);
|
create(SimpleConfiguration.class);
|
||||||
@@ -104,26 +115,40 @@ public class ContextFunctionCatalogAutoConfigurationTests {
|
|||||||
private void create(Class<?>... types) {
|
private void create(Class<?>... types) {
|
||||||
context = new SpringApplicationBuilder((Object[]) types).run();
|
context = new SpringApplicationBuilder((Object[]) types).run();
|
||||||
catalog = context.getBean(InMemoryFunctionCatalog.class);
|
catalog = context.getBean(InMemoryFunctionCatalog.class);
|
||||||
|
inspector = context.getBean(FunctionInspector.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@EnableAutoConfiguration
|
@EnableAutoConfiguration
|
||||||
@Configuration
|
@Configuration
|
||||||
protected static class SimpleConfiguration {
|
protected static class SimpleConfiguration {
|
||||||
private List<String> list = new ArrayList<>();
|
private List<String> list = new ArrayList<>();
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public Function<String, String> function() {
|
public Function<String, String> function() {
|
||||||
return value -> value.toUpperCase();
|
return value -> value.toUpperCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public Supplier<String> supplier() {
|
public Supplier<String> supplier() {
|
||||||
return () -> "hello";
|
return () -> "hello";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public Consumer<String> consumer() {
|
public Consumer<String> consumer() {
|
||||||
return value -> list.add(value);
|
return value -> list.add(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@EnableAutoConfiguration
|
||||||
|
@Configuration
|
||||||
|
protected static class GenericConfiguration {
|
||||||
|
@Bean
|
||||||
|
public Function<Map<String, String>, Map<String, String>> function() {
|
||||||
|
return m -> m.entrySet().stream().collect(Collectors.toMap(e -> e.getKey(),
|
||||||
|
e -> e.getValue().toString().toUpperCase()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@EnableAutoConfiguration
|
@EnableAutoConfiguration
|
||||||
@Configuration
|
@Configuration
|
||||||
protected static class QualifiedConfiguration {
|
protected static class QualifiedConfiguration {
|
||||||
@@ -137,7 +162,7 @@ public class ContextFunctionCatalogAutoConfigurationTests {
|
|||||||
@EnableAutoConfiguration
|
@EnableAutoConfiguration
|
||||||
@Configuration
|
@Configuration
|
||||||
protected static class AliasConfiguration {
|
protected static class AliasConfiguration {
|
||||||
@Bean({"function", "other"})
|
@Bean({ "function", "other" })
|
||||||
public Function<String, String> function() {
|
public Function<String, String> function() {
|
||||||
return value -> value.toUpperCase();
|
return value -> value.toUpperCase();
|
||||||
}
|
}
|
||||||
@@ -148,8 +173,10 @@ public class ContextFunctionCatalogAutoConfigurationTests {
|
|||||||
protected static class RegistrationConfiguration {
|
protected static class RegistrationConfiguration {
|
||||||
@Bean
|
@Bean
|
||||||
public FunctionRegistration<Function<String, String>> registration() {
|
public FunctionRegistration<Function<String, String>> registration() {
|
||||||
return new FunctionRegistration<Function<String, String>>(function()).name("other");
|
return new FunctionRegistration<Function<String, String>>(function())
|
||||||
|
.name("other");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public Function<String, String> function() {
|
public Function<String, String> function() {
|
||||||
return value -> value.toUpperCase();
|
return value -> value.toUpperCase();
|
||||||
|
|||||||
@@ -26,6 +26,9 @@ import javax.servlet.http.HttpServletRequest;
|
|||||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
import org.springframework.cloud.function.context.FunctionInspector;
|
import org.springframework.cloud.function.context.FunctionInspector;
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
import org.springframework.core.Ordered;
|
import org.springframework.core.Ordered;
|
||||||
@@ -46,6 +49,9 @@ import org.springframework.web.util.ContentCachingRequestWrapper;
|
|||||||
public class FluxHandlerMethodArgumentResolver
|
public class FluxHandlerMethodArgumentResolver
|
||||||
implements HandlerMethodArgumentResolver, Ordered {
|
implements HandlerMethodArgumentResolver, Ordered {
|
||||||
|
|
||||||
|
private static Log logger = LogFactory
|
||||||
|
.getLog(FluxHandlerMethodArgumentResolver.class);
|
||||||
|
|
||||||
public static final String HANDLER = FluxHandlerMethodArgumentResolver.class.getName()
|
public static final String HANDLER = FluxHandlerMethodArgumentResolver.class.getName()
|
||||||
+ ".HANDLER";
|
+ ".HANDLER";
|
||||||
|
|
||||||
@@ -76,16 +82,22 @@ public class FluxHandlerMethodArgumentResolver
|
|||||||
List<Object> body;
|
List<Object> body;
|
||||||
ContentCachingRequestWrapper nativeRequest = new ContentCachingRequestWrapper(
|
ContentCachingRequestWrapper nativeRequest = new ContentCachingRequestWrapper(
|
||||||
webRequest.getNativeRequest(HttpServletRequest.class));
|
webRequest.getNativeRequest(HttpServletRequest.class));
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Resolving request body into type: " + type);
|
||||||
|
}
|
||||||
if (isPlainText(webRequest) && CharSequence.class.isAssignableFrom(type)) {
|
if (isPlainText(webRequest) && CharSequence.class.isAssignableFrom(type)) {
|
||||||
body = Arrays.asList(StreamUtils.copyToString(nativeRequest.getInputStream(),
|
body = Arrays.asList(StreamUtils.copyToString(nativeRequest.getInputStream(),
|
||||||
Charset.forName("UTF-8")));
|
Charset.forName("UTF-8")));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
try {
|
try {
|
||||||
body = mapper.readValue(nativeRequest.getInputStream(), mapper
|
body = mapper.readValue(nativeRequest.getInputStream(),
|
||||||
.getTypeFactory().constructCollectionLikeType(ArrayList.class, type));
|
mapper.getTypeFactory()
|
||||||
} catch (JsonMappingException e) {
|
.constructCollectionLikeType(ArrayList.class, type));
|
||||||
body = Arrays.asList(mapper.readValue(nativeRequest.getContentAsByteArray(), type));
|
}
|
||||||
|
catch (JsonMappingException e) {
|
||||||
|
body = Arrays.asList(
|
||||||
|
mapper.readValue(nativeRequest.getContentAsByteArray(), type));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new FluxRequest<Object>(body);
|
return new FluxRequest<Object>(body);
|
||||||
|
|||||||
Reference in New Issue
Block a user