Improve output type conversion handling
- Re-enable, clean and improve special handling for collection/array output type - Add tests to validate and demonstrate the differences in special handling of collection of Messages ve collection of non-Messages Resolves #464
This commit is contained in:
@@ -17,9 +17,11 @@
|
|||||||
package org.springframework.cloud.function.context.catalog;
|
package org.springframework.cloud.function.context.catalog;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.GenericArrayType;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.ParameterizedType;
|
import java.lang.reflect.ParameterizedType;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -28,6 +30,7 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
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;
|
||||||
@@ -688,34 +691,32 @@ public class BeanFactoryAwareFunctionRegistry
|
|||||||
else {
|
else {
|
||||||
for (int i = 0; i < acceptedContentTypes.size() && convertedValue == null; i++) {
|
for (int i = 0; i < acceptedContentTypes.size() && convertedValue == null; i++) {
|
||||||
MimeType acceptedContentType = acceptedContentTypes.get(i);
|
MimeType acceptedContentType = acceptedContentTypes.get(i);
|
||||||
if (value instanceof Message) {
|
/*
|
||||||
Message<?> message = (Message<?>) value;
|
* We need to treat Iterables differently since they may represent collection of Messages
|
||||||
if (message.getPayload() instanceof byte[]) {
|
* which should be converted individually
|
||||||
convertedValue = message;
|
*/
|
||||||
|
boolean convertIndividualItem = false;
|
||||||
|
if (value instanceof Iterable || (ObjectUtils.isArray(value) && !(value instanceof byte[]))) {
|
||||||
|
Type outputType = FunctionTypeUtils.getOutputType(functionType, 0);
|
||||||
|
if (outputType instanceof ParameterizedType) {
|
||||||
|
convertIndividualItem = FunctionTypeUtils.isMessage(FunctionTypeUtils.getImmediateGenericType(outputType, 0));
|
||||||
}
|
}
|
||||||
else {
|
else if (outputType instanceof GenericArrayType) {
|
||||||
convertedValue = this.convertValueToMessage(message, enricher, acceptedContentType);
|
convertIndividualItem = FunctionTypeUtils.isMessage(((GenericArrayType) outputType).getGenericComponentType());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//<<<<<<< HEAD
|
|
||||||
// else if (value instanceof byte[]) {
|
if (convertIndividualItem) {
|
||||||
// convertedValue = MessageBuilder.withPayload(value)
|
if (ObjectUtils.isArray(value)) {
|
||||||
// .setHeader(MessageHeaders.CONTENT_TYPE, acceptedContentType).build();
|
value = Arrays.asList((Object[]) value);
|
||||||
// }
|
}
|
||||||
// else if (value instanceof Iterable || ObjectUtils.isArray(value)) {
|
AtomicReference<List<Message>> messages = new AtomicReference<List<Message>>(new ArrayList<>());
|
||||||
// boolean isArray = ObjectUtils.isArray(value);
|
((Iterable) value).forEach(element ->
|
||||||
// if (isArray) {
|
messages.get()
|
||||||
// value = Arrays.asList((Object[]) value);
|
.add((Message) convertOutputValueIfNecessary(element, enricher, acceptedContentType
|
||||||
// }
|
.toString())));
|
||||||
// AtomicReference<List<Message>> messages = new AtomicReference<List<Message>>(new ArrayList<>());
|
convertedValue = messages.get();
|
||||||
// ((Iterable) value).forEach(element ->
|
}
|
||||||
// messages.get()
|
|
||||||
// .add((Message) convertOutputValueIfNecessary(element, enricher, acceptedContentType
|
|
||||||
// .toString())));
|
|
||||||
// convertedValue = messages.get();
|
|
||||||
// }
|
|
||||||
//=======
|
|
||||||
//>>>>>>> Don't treat byte[] or collections in a special way.
|
|
||||||
else {
|
else {
|
||||||
convertedValue = this.convertValueToMessage(value, enricher, acceptedContentType);
|
convertedValue = this.convertValueToMessage(value, enricher, acceptedContentType);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ 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.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -50,6 +51,7 @@ import org.springframework.messaging.converter.MessageConverter;
|
|||||||
import org.springframework.messaging.support.GenericMessage;
|
import org.springframework.messaging.support.GenericMessage;
|
||||||
import org.springframework.messaging.support.MessageBuilder;
|
import org.springframework.messaging.support.MessageBuilder;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
import org.springframework.util.MimeType;
|
import org.springframework.util.MimeType;
|
||||||
import org.springframework.util.ReflectionUtils;
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
|
||||||
@@ -336,6 +338,7 @@ public class BeanFactoryAwareFunctionRegistryTests {
|
|||||||
assertThat(composed).isFalse();
|
assertThat(composed).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
@Test
|
@Test
|
||||||
public void byteArrayNoSpecialHandling() throws Exception {
|
public void byteArrayNoSpecialHandling() throws Exception {
|
||||||
FunctionCatalog catalog = this.configureCatalog(ByteArrayFunction.class);
|
FunctionCatalog catalog = this.configureCatalog(ByteArrayFunction.class);
|
||||||
@@ -345,6 +348,21 @@ public class BeanFactoryAwareFunctionRegistryTests {
|
|||||||
assertThat(result.getPayload()).isEqualTo("\"b2xsZWg=\"".getBytes());
|
assertThat(result.getPayload()).isEqualTo("\"b2xsZWg=\"".getBytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
@Test
|
||||||
|
public void testMultipleValuesInOutputHandling() throws Exception {
|
||||||
|
FunctionCatalog catalog = this.configureCatalog(CollectionOutConfiguration.class);
|
||||||
|
FunctionInvocationWrapper function = catalog.lookup("parseToList", "application/json");
|
||||||
|
assertThat(function).isNotNull();
|
||||||
|
Object result = (Message) function.apply(MessageBuilder.withPayload("1, 2, 3".getBytes()).setHeader(MessageHeaders.CONTENT_TYPE, "text/plain").build());
|
||||||
|
assertThat(result instanceof Message).isTrue();
|
||||||
|
|
||||||
|
function = catalog.lookup("parseToListOfMessages", "application/json");
|
||||||
|
assertThat(function).isNotNull();
|
||||||
|
result = function.apply(MessageBuilder.withPayload("1, 2, 3".getBytes()).setHeader(MessageHeaders.CONTENT_TYPE, "text/plain").build());
|
||||||
|
assertThat(result instanceof Message).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Test
|
@Test
|
||||||
public void testSerializationWithCompatibleWildcardSubtypeAcceptHeader() {
|
public void testSerializationWithCompatibleWildcardSubtypeAcceptHeader() {
|
||||||
@@ -370,6 +388,25 @@ public class BeanFactoryAwareFunctionRegistryTests {
|
|||||||
assertThat(dateResult.getHeaders().get("accept")).isNull();
|
assertThat(dateResult.getHeaders().get("accept")).isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@EnableAutoConfiguration
|
||||||
|
public static class CollectionOutConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public Function<String, List<String>> parseToList() {
|
||||||
|
return v -> CollectionUtils.arrayToList(v.split(","));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public Function<String, List<Message<String>>> parseToListOfMessages() {
|
||||||
|
return v -> {
|
||||||
|
List<Message<String>> list = (List<Message<String>>) CollectionUtils.arrayToList(v.split(",")).stream()
|
||||||
|
.map(value -> MessageBuilder.withPayload(value).build()).collect(Collectors.toList());
|
||||||
|
return list;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@EnableAutoConfiguration
|
@EnableAutoConfiguration
|
||||||
public static class NegotiatingMessageConverterConfiguration {
|
public static class NegotiatingMessageConverterConfiguration {
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user