GH-612: Better tests and implementation

Resolves #613
This commit is contained in:
Greg Eales
2020-11-23 10:55:37 +00:00
committed by Oleg Zhurakousky
parent a5e045731f
commit 995b3425a8
4 changed files with 37 additions and 103 deletions

View File

@@ -820,8 +820,11 @@ public class SimpleFunctionRegistry implements FunctionRegistry, FunctionInspect
if (messageNeedsConversion(rawType, type, (Message<?>) value)) {
boolean convertWithHint = false;
Type hint = FunctionTypeUtils.getGenericType(type);;
if (FunctionTypeUtils.isTypeCollection(type) || !rawType.equals(hint)) {
Type hint = FunctionTypeUtils.getGenericType(type);
if (FunctionTypeUtils.isTypeCollection(type)) {
convertWithHint = true;
}
else if (!FunctionTypeUtils.isPublisher(type) && !rawType.equals(type)) {
convertWithHint = true;
}

View File

@@ -29,7 +29,6 @@ import java.util.stream.Collectors;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.assertj.core.util.VisibleForTesting;
import org.springframework.cloud.function.context.catalog.FunctionTypeUtils;
import org.springframework.core.MethodParameter;
import org.springframework.core.ParameterizedTypeReference;
@@ -50,14 +49,13 @@ import org.springframework.util.ReflectionUtils;
* @author Gary Russell
* @since 2.0
*/
public class ApplicationJsonMessageMarshallingConverter extends MappingJackson2MessageConverter {
class ApplicationJsonMessageMarshallingConverter extends MappingJackson2MessageConverter {
private final Field headersField;
private final Map<Type, JavaType> typeCache = new ConcurrentHashMap<>();
@VisibleForTesting
public ApplicationJsonMessageMarshallingConverter(@Nullable ObjectMapper objectMapper) {
ApplicationJsonMessageMarshallingConverter(@Nullable ObjectMapper objectMapper) {
if (objectMapper != null) {
this.setObjectMapper(objectMapper);
}

View File

@@ -46,6 +46,7 @@ import org.springframework.cloud.function.context.FunctionRegistration;
import org.springframework.cloud.function.context.FunctionRegistry;
import org.springframework.cloud.function.context.FunctionType;
import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper;
import org.springframework.cloud.function.context.config.NegotiatingMessageConverterWrapper;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -53,13 +54,13 @@ import org.springframework.lang.Nullable;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.converter.AbstractMessageConverter;
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
import org.springframework.messaging.converter.MessageConverter;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.MimeType;
import org.springframework.util.MimeTypeUtils;
import org.springframework.util.ReflectionUtils;
import static java.util.Collections.singletonList;
@@ -531,12 +532,28 @@ public class BeanFactoryAwareFunctionRegistryTests {
result = (Mono<String>) f.apply(Flux.just("hello"));
assertThat(result.block()).isEqualTo("hello");
}
//
// @Test
//<<<<<<< HEAD
// public void testGH_611() {
// FunctionCatalog catalog = this.configureCatalog(NegotiatingMessageConverterConfiguration.class);
// Supplier<Message<Integer>> f = catalog.lookup("supplier", "text/*");
// assertThat(f.get().getHeaders().get(MessageHeaders.CONTENT_TYPE)).isEqualTo(MimeTypeUtils.parseMimeType("text/*"));
//=======
public void testGH_612() {
ApplicationContext context = new SpringApplicationBuilder(SampleFunctionConfiguration.class)
.run("--logging.level.org.springframework.cloud.function=DEBUG",
"--spring.main.lazy-initialization=true");
FunctionCatalog catalog = context.getBean(FunctionCatalog.class);
@Test
public void testGH_611() {
FunctionCatalog catalog = this.configureCatalog(NegotiatingMessageConverterConfiguration.class);
Supplier<Message<Integer>> f = catalog.lookup("supplier", "text/*");
assertThat(f.get().getHeaders().get(MessageHeaders.CONTENT_TYPE)).isEqualTo(MimeTypeUtils.parseMimeType("text/*"));
Consumer<Flux<Message<String>>> consumer = catalog.lookup("reactivePojoConsumer");
consumer.accept(Flux.just(MessageBuilder
.withPayload("{\"name\":\"Ricky\"}")
.setHeader(MessageHeaders.CONTENT_TYPE, MimeType.valueOf("application/json"))
.build()));
SampleFunctionConfiguration config = context.getBean(SampleFunctionConfiguration.class);
assertThat(((Person) config.consumerInputRef.get()).getName()).isEqualTo("Ricky");
//>>>>>>> GH-612: Better tests and implementation
}
@EnableAutoConfiguration
@@ -734,6 +751,11 @@ public class BeanFactoryAwareFunctionRegistryTests {
AtomicReference<Object> consumerInputRef = new AtomicReference<>();
@Bean
public MessageConverter mappingJackson2MessageConverter() {
return NegotiatingMessageConverterWrapper.wrap(new MappingJackson2MessageConverter());
}
@Bean
public Function<Person, Person> uppercasePerson() {
return person -> {

View File

@@ -39,7 +39,6 @@ import org.springframework.cloud.function.context.FunctionRegistry;
import org.springframework.cloud.function.context.FunctionType;
import org.springframework.cloud.function.context.HybridFunctionalRegistrationTests.UppercaseFunction;
import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper;
import org.springframework.cloud.function.context.config.ApplicationJsonMessageMarshallingConverter;
import org.springframework.cloud.function.context.config.JsonMessageConverter;
import org.springframework.cloud.function.context.config.NegotiatingMessageConverterWrapper;
import org.springframework.cloud.function.json.GsonMapper;
@@ -55,6 +54,7 @@ import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.converter.AbstractMessageConverter;
import org.springframework.messaging.converter.ByteArrayMessageConverter;
import org.springframework.messaging.converter.CompositeMessageConverter;
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
import org.springframework.messaging.converter.MessageConverter;
import org.springframework.messaging.converter.StringMessageConverter;
import org.springframework.messaging.support.MessageBuilder;
@@ -252,29 +252,6 @@ public class SimpleFunctionRegistryTests {
Assertions.assertIterableEquals(result.blockFirst(), Arrays.asList("item1", "item2"));
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Test
// @Ignore
public void testReactiveFunctionWithListMessagesJackson() {
FunctionRegistration<ReactiveFunctionWithList> registration = new FunctionRegistration<>(new ReactiveFunctionWithList(), "reactiveWithList")
.type(FunctionType.of(ReactiveFunctionWithList.class));
SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService,
new CompositeMessageConverter(Collections.singletonList(new ApplicationJsonMessageMarshallingConverter(null))));
catalog.register(registration);
Function lookedUpFunction = catalog.lookup("reactiveWithList");
assertThat(lookedUpFunction).isNotNull();
Flux<List<String>> result = (Flux<List<String>>) lookedUpFunction
.apply(Flux.just(MessageBuilder
.withPayload("[{\"name\":\"item1\"},{\"name\":\"item2\"}]")
.setHeader(MessageHeaders.CONTENT_TYPE, "application/json")
.build()
));
Assertions.assertIterableEquals(result.blockFirst(), Arrays.asList("item1", "item2"));
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Test
public void testReactiveFunctionMessage() {
@@ -303,7 +280,7 @@ public class SimpleFunctionRegistryTests {
.type(FunctionType.of(ReactiveFunction.class));
SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService,
new CompositeMessageConverter(Collections.singletonList(new ApplicationJsonMessageMarshallingConverter(null))));
new CompositeMessageConverter(Collections.singletonList(new MappingJackson2MessageConverter())));
catalog.register(registration);
Function lookedUpFunction = catalog.lookup("reactive");
@@ -318,49 +295,6 @@ public class SimpleFunctionRegistryTests {
Assertions.assertEquals(result.blockFirst(), "item");
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Test
public void testReactiveFunctionWithHolderMessage() {
FunctionRegistration<ReactiveFunctionWithHolder> registration = new FunctionRegistration<>(new ReactiveFunctionWithHolder(), "reactive")
.type(FunctionType.of(ReactiveFunctionWithHolder.class));
SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter);
catalog.register(registration);
Function lookedUpFunction = catalog.lookup("reactive");
assertThat(lookedUpFunction).isNotNull();
Flux<String> result = (Flux<String>) lookedUpFunction
.apply(Flux.just(MessageBuilder
.withPayload("{\"data\":{\"name\":\"item\"}}")
.setHeader(MessageHeaders.CONTENT_TYPE, "application/json")
.build()
));
Assertions.assertEquals(result.blockFirst(), "item");
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Test
public void testReactiveFunctionWithHolderMessageJackson() {
FunctionRegistration<ReactiveFunctionWithHolder> registration = new FunctionRegistration<>(new ReactiveFunctionWithHolder(), "reactive")
.type(FunctionType.of(ReactiveFunctionWithHolder.class));
SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService,
new CompositeMessageConverter(Collections.singletonList(new ApplicationJsonMessageMarshallingConverter(null))));
catalog.register(registration);
Function lookedUpFunction = catalog.lookup("reactive");
assertThat(lookedUpFunction).isNotNull();
Flux<String> result = (Flux<String>) lookedUpFunction
.apply(Flux.just(MessageBuilder
.withPayload("{\"data\":{\"name\":\"item\"}}")
.setHeader(MessageHeaders.CONTENT_TYPE, "application/json")
.build()
));
Assertions.assertEquals(result.blockFirst(), "item");
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Test
public void testWithCustomMessageConverter() {
@@ -511,27 +445,4 @@ public class SimpleFunctionRegistryTests {
.map(Person::getName);
}
}
private static class ReactiveFunctionWithHolder implements Function<Flux<Message<GenericDataHolder<Person>>>, Flux<String>> {
@Override
public Flux<String> apply(Flux<Message<GenericDataHolder<Person>>> flux) {
return flux
.map(Message::getPayload)
.map(GenericDataHolder::getData)
.map(Person::getName);
}
}
public static class GenericDataHolder<T> {
private T data;
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
}