Extract common code from BeanFactoryAwareFunctionRegistry

. . . to ensure that we can have the version of FunctionRegistry that is not dependent on BeanFactory.
This commit is contained in:
Oleg Zhurakousky
2020-04-16 10:00:25 +02:00
parent adbbbac677
commit f1c15bf950
16 changed files with 1030 additions and 776 deletions

View File

@@ -91,6 +91,7 @@ public class SpringFunctionAdapterInitializerTests {
};
this.initializer.initialize(null);
Flux<?> result = Flux.from(this.initializer.apply(Flux.just(new Foo())));
Object o = result.blockFirst();
assertThat(result.blockFirst()).isInstanceOf(Bar.class);
}

View File

@@ -40,8 +40,7 @@ import reactor.util.function.Tuples;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.function.context.FunctionCatalog;
import org.springframework.cloud.function.context.catalog.BeanFactoryAwareFunctionRegistry.FunctionInvocationWrapper;
import org.springframework.cloud.function.context.catalog.FunctionTypeUtilsTests.ReactiveFunction;
import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

View File

@@ -16,17 +16,31 @@
package org.springframework.cloud.function.context.catalog;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.function.Supplier;
import com.google.gson.Gson;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import reactor.core.publisher.Flux;
import org.springframework.cloud.function.context.FunctionRegistration;
import org.springframework.cloud.function.context.FunctionType;
import org.springframework.cloud.function.core.FluxFunction;
import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper;
import org.springframework.cloud.function.context.config.JsonMessageConverter;
import org.springframework.cloud.function.context.config.NegotiatingMessageConverterWrapper;
import org.springframework.cloud.function.json.GsonMapper;
import org.springframework.cloud.function.json.JsonMapper;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.messaging.Message;
import org.springframework.messaging.converter.ByteArrayMessageConverter;
import org.springframework.messaging.converter.CompositeMessageConverter;
import org.springframework.messaging.converter.MessageConverter;
import org.springframework.messaging.converter.StringMessageConverter;
import org.springframework.messaging.support.MessageBuilder;
import static org.assertj.core.api.Assertions.assertThat;
@@ -35,37 +49,40 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Oleg Zhurakousky
*
*/
public class InMemoryFunctionCatalogTests {
public class SimpleFunctionRegistryTests {
@Test
@Ignore // we no longer have a need to register the actual target function as it is contained within wrapper
public void testFunctionRegistration() {
TestFunction function = new TestFunction();
FunctionRegistration<TestFunction> registration = new FunctionRegistration<>(
function, "foo").type(FunctionType.of(TestFunction.class));
InMemoryFunctionCatalog catalog = new InMemoryFunctionCatalog();
catalog.register(registration);
FunctionRegistration<?> registration2 = catalog.getRegistration(function);
assertThat(registration2.getType()).isEqualTo(registration.getType());
private CompositeMessageConverter messageConverter;
private ConversionService conversionService;
@Before
public void before() {
List<MessageConverter> messageConverters = new ArrayList<>();
JsonMapper jsonMapper = new GsonMapper(new Gson());
messageConverters.add(NegotiatingMessageConverterWrapper.wrap(new JsonMessageConverter(jsonMapper)));
messageConverters.add(NegotiatingMessageConverterWrapper.wrap(new ByteArrayMessageConverter()));
messageConverters.add(NegotiatingMessageConverterWrapper.wrap(new StringMessageConverter()));
this.messageConverter = new CompositeMessageConverter(messageConverters);
this.conversionService = new DefaultConversionService();
}
@Test
public void testFunctionLookup() {
TestFunction function = new TestFunction();
FunctionRegistration<TestFunction> registration = new FunctionRegistration<>(
function, "foo").type(FunctionType.of(TestFunction.class));
InMemoryFunctionCatalog catalog = new InMemoryFunctionCatalog();
SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter);
catalog.register(registration);
Object lookedUpFunction = catalog.lookup("hello");
FunctionInvocationWrapper lookedUpFunction = catalog.lookup("hello");
assertThat(lookedUpFunction).isNotNull(); // becouse we only have one and can look it up with any name
FunctionRegistration<TestFunction> registration2 = new FunctionRegistration<>(
function, "foo2").type(FunctionType.of(TestFunction.class));
catalog.register(registration2);
lookedUpFunction = catalog.lookup("hello");
assertThat(lookedUpFunction).isNull();
lookedUpFunction = catalog.lookup("foo");
assertThat(lookedUpFunction).isNotNull();
assertThat(catalog.lookupFunctionName(lookedUpFunction)).isEqualTo("foo");
assertThat(catalog.getFunctionType("foo").getOutputType())
.isEqualTo(String.class);
assertThat(lookedUpFunction instanceof FluxFunction).isTrue();
}
@Test
@@ -74,50 +91,48 @@ public class InMemoryFunctionCatalogTests {
new UpperCase(), "uppercase").type(FunctionType.of(UpperCase.class));
FunctionRegistration<Reverse> reverseRegistration = new FunctionRegistration<>(
new Reverse(), "reverse").type(FunctionType.of(Reverse.class));
InMemoryFunctionCatalog catalog = new InMemoryFunctionCatalog();
SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter);
catalog.register(upperCaseRegistration);
catalog.register(reverseRegistration);
Function<Flux<String>, Flux<String>> lookedUpFunction = catalog
.lookup("uppercase|reverse");
assertThat(catalog.getFunctionType("uppercase|reverse").isMessage()).isFalse();
assertThat(lookedUpFunction).isNotNull();
assertThat(lookedUpFunction.apply(Flux.just("star")).blockFirst())
.isEqualTo("RATS");
}
@Test
@Ignore
public void testFunctionCompositionImplicit() {
FunctionRegistration<Words> wordsRegistration = new FunctionRegistration<>(
new Words(), "words").type(FunctionType.of(Words.class));
FunctionRegistration<Reverse> reverseRegistration = new FunctionRegistration<>(
new Reverse(), "reverse").type(FunctionType.of(Reverse.class));
InMemoryFunctionCatalog catalog = new InMemoryFunctionCatalog();
SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter);
catalog.register(wordsRegistration);
catalog.register(reverseRegistration);
// There's only one function, we should be able to leave that blank
Supplier<Flux<String>> lookedUpFunction = catalog.lookup("words|");
assertThat(catalog.getFunctionType("words|").isMessage()).isFalse();
assertThat(lookedUpFunction).isNotNull();
assertThat(lookedUpFunction.get().blockFirst()).isEqualTo("olleh");
}
@Test
@Ignore
public void testFunctionCompletelyImplicitComposition() {
FunctionRegistration<Words> wordsRegistration = new FunctionRegistration<>(
new Words(), "words").type(FunctionType.of(Words.class));
FunctionRegistration<Reverse> reverseRegistration = new FunctionRegistration<>(
new Reverse(), "reverse").type(FunctionType.of(Reverse.class));
InMemoryFunctionCatalog catalog = new InMemoryFunctionCatalog();
SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter);
catalog.register(wordsRegistration);
catalog.register(reverseRegistration);
// There's only one function, we should be able to leave that blank
Supplier<Flux<String>> lookedUpFunction = catalog.lookup("|");
assertThat(catalog.getFunctionType("|").isMessage()).isFalse();
assertThat(lookedUpFunction).isNotNull();
assertThat(lookedUpFunction.get().blockFirst()).isEqualTo("olleh");
@@ -129,15 +144,14 @@ public class InMemoryFunctionCatalogTests {
new Words(), "words").type(FunctionType.of(Words.class));
FunctionRegistration<Reverse> reverseRegistration = new FunctionRegistration<>(
new Reverse(), "reverse").type(FunctionType.of(Reverse.class));
InMemoryFunctionCatalog catalog = new InMemoryFunctionCatalog();
SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter);
catalog.register(wordsRegistration);
catalog.register(reverseRegistration);
Supplier<Flux<String>> lookedUpFunction = catalog.lookup("words|reverse");
assertThat(catalog.getFunctionType("words|reverse").isMessage()).isFalse();
Supplier<String> lookedUpFunction = catalog.lookup("words|reverse");
assertThat(lookedUpFunction).isNotNull();
assertThat(lookedUpFunction.get().blockFirst()).isEqualTo("olleh");
assertThat(lookedUpFunction.get()).isEqualTo("olleh");
}
@Test
@@ -148,15 +162,12 @@ public class InMemoryFunctionCatalogTests {
FunctionRegistration<ReverseMessage> reverseRegistration = new FunctionRegistration<>(
new ReverseMessage(), "reverse")
.type(FunctionType.of(ReverseMessage.class));
InMemoryFunctionCatalog catalog = new InMemoryFunctionCatalog();
SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter);
catalog.register(upperCaseRegistration);
catalog.register(reverseRegistration);
Function<Flux<Message<String>>, Flux<Message<String>>> lookedUpFunction = catalog
.lookup("uppercase|reverse");
assertThat(catalog.getFunctionType("uppercase|reverse").isMessage()).isTrue();
assertThat(catalog.lookupFunctionName(lookedUpFunction))
.isEqualTo("uppercase|reverse");
assertThat(lookedUpFunction).isNotNull();
assertThat(lookedUpFunction
@@ -171,20 +182,16 @@ public class InMemoryFunctionCatalogTests {
.type(FunctionType.of(UpperCaseMessage.class));
FunctionRegistration<Reverse> reverseRegistration = new FunctionRegistration<>(
new Reverse(), "reverse").type(FunctionType.of(Reverse.class));
InMemoryFunctionCatalog catalog = new InMemoryFunctionCatalog();
SimpleFunctionRegistry catalog = new SimpleFunctionRegistry(this.conversionService, this.messageConverter);
catalog.register(upperCaseRegistration);
catalog.register(reverseRegistration);
Function<Flux<Message<String>>, Flux<Message<String>>> lookedUpFunction = catalog
Function<Message<String>, String> lookedUpFunction = catalog
.lookup("uppercase|reverse");
assertThat(catalog.getFunctionType("uppercase|reverse").isMessage()).isTrue();
assertThat(lookedUpFunction).isNotNull();
Message<String> message = lookedUpFunction.apply(Flux
.just(MessageBuilder.withPayload("star").setHeader("foo", "bar").build()))
.blockFirst();
assertThat(message.getPayload()).isEqualTo("RATS");
assertThat(message.getHeaders().get("foo")).isEqualTo("bar");
String result = lookedUpFunction.apply(MessageBuilder.withPayload("star").setHeader("foo", "bar").build());
assertThat(result).isEqualTo("RATS");
}
private static class Words implements Supplier<String> {

View File

@@ -158,13 +158,13 @@ public class ContextFunctionCatalogInitializerTests {
create(SimpleConfiguration.class);
Object bean = this.context.getBean("function");
assertThat(bean).isInstanceOf(FunctionRegistration.class);
Function<Flux<String>, Flux<String>> function = this.catalog
.lookup(Function.class, "function");
assertThat(function.apply(Flux.just("foo")).blockFirst()).isEqualTo("FOO");
Function<Flux<String>, Flux<Person>> function
= this.catalog.lookup(Function.class, "function");
assertThat(function.apply(Flux.just("{\"name\":\"foo\"}")).blockFirst().getName()).isEqualTo("FOO");
assertThat(bean).isNotSameAs(function);
assertThat(this.inspector.getRegistration(function)).isNotNull();
assertThat(this.inspector.getRegistration(function).getType())
.isEqualTo(FunctionType.from(String.class).to(String.class));
.isEqualTo(FunctionType.from(Person.class).to(Person.class));
}
@Test
@@ -187,8 +187,8 @@ public class ContextFunctionCatalogInitializerTests {
create(SimpleConfiguration.class);
assertThat(this.context.getBean("supplier"))
.isInstanceOf(FunctionRegistration.class);
Supplier<Flux<String>> supplier = this.catalog.lookup(Supplier.class, "supplier");
assertThat(supplier.get().blockFirst()).isEqualTo("hello");
Supplier<String> supplier = this.catalog.lookup(Supplier.class, "supplier");
assertThat(supplier.get()).isEqualTo("hello");
}
@Test
@@ -276,7 +276,7 @@ public class ContextFunctionCatalogInitializerTests {
public void initialize(GenericApplicationContext context) {
context.registerBean("function", FunctionRegistration.class,
() -> new FunctionRegistration<>(function()).type(
FunctionType.from(String.class).to(String.class).getType()));
FunctionType.from(Person.class).to(Person.class).getType()));
context.registerBean("supplier", FunctionRegistration.class,
() -> new FunctionRegistration<>(supplier())
.type(FunctionType.supplier(String.class).getType()));
@@ -287,8 +287,12 @@ public class ContextFunctionCatalogInitializerTests {
}
@Bean
public Function<String, String> function() {
return value -> value.toUpperCase();
public Function<Person, Person> function() {
return person -> {
Person p = new Person();
p.setName(person.getName().toUpperCase());
return p;
};
}
@Bean
@@ -300,7 +304,6 @@ public class ContextFunctionCatalogInitializerTests {
public Consumer<String> consumer() {
return value -> this.list.add(value);
}
}
@ConfigurationProperties("app")
@@ -440,4 +443,15 @@ public class ContextFunctionCatalogInitializerTests {
}
private static class Person {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}