Ensure composite wrapper type reflects reality
When a Supplier<Flux<Foo>> is composed with a Function<Foo,Bar> the resulting handler (supplier) should have Flux as its output wrapper still (the most general output wrapper type in the chain).
This commit is contained in:
@@ -98,6 +98,9 @@ public class FunctionType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isWrapper(Type type) {
|
public static boolean isWrapper(Type type) {
|
||||||
|
if (type instanceof ParameterizedType) {
|
||||||
|
type = ((ParameterizedType)type).getRawType();
|
||||||
|
}
|
||||||
return Publisher.class.equals(type) || Flux.class.equals(type)
|
return Publisher.class.equals(type) || Flux.class.equals(type)
|
||||||
|| Mono.class.equals(type) || Optional.class.equals(type);
|
|| Mono.class.equals(type) || Optional.class.equals(type);
|
||||||
}
|
}
|
||||||
@@ -147,21 +150,34 @@ public class FunctionType {
|
|||||||
public static FunctionType compose(FunctionType input, FunctionType output) {
|
public static FunctionType compose(FunctionType input, FunctionType output) {
|
||||||
ResolvableType inputGeneric = input(input);
|
ResolvableType inputGeneric = input(input);
|
||||||
ResolvableType outputGeneric = output(output);
|
ResolvableType outputGeneric = output(output);
|
||||||
|
if (!isWrapper(outputGeneric.getType())) {
|
||||||
|
ResolvableType inputOutput = output(input);
|
||||||
|
if (isWrapper(inputOutput.getType())) {
|
||||||
|
outputGeneric = wrap(input,
|
||||||
|
extractClass(inputOutput.getType(), ParamType.OUTPUT_WRAPPER),
|
||||||
|
extractClass(outputGeneric.getType(), ParamType.OUTPUT));
|
||||||
|
}
|
||||||
|
}
|
||||||
return new FunctionType(ResolvableType
|
return new FunctionType(ResolvableType
|
||||||
.forClassWithGenerics(Function.class, inputGeneric, outputGeneric)
|
.forClassWithGenerics(Function.class, inputGeneric, outputGeneric)
|
||||||
.getType());
|
.getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
private ResolvableType wrap(Class<?> wrapper, Class<?> type) {
|
private ResolvableType wrap(Class<?> wrapper, Class<?> type) {
|
||||||
return isMessage() ? wrap(wrapper, message(type))
|
return wrap(this, wrapper, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ResolvableType wrap(FunctionType input, Class<?> wrapper,
|
||||||
|
Class<?> type) {
|
||||||
|
return input.isMessage() ? wrap(wrapper, message(type))
|
||||||
: ResolvableType.forClassWithGenerics(wrapper, type);
|
: ResolvableType.forClassWithGenerics(wrapper, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ResolvableType wrap(Class<?> wrapper, ResolvableType type) {
|
private static ResolvableType wrap(Class<?> wrapper, ResolvableType type) {
|
||||||
return ResolvableType.forClassWithGenerics(wrapper, type);
|
return ResolvableType.forClassWithGenerics(wrapper, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ResolvableType message(Class<?> type) {
|
private static ResolvableType message(Class<?> type) {
|
||||||
return ResolvableType.forClassWithGenerics(Message.class, type);
|
return ResolvableType.forClassWithGenerics(Message.class, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,7 +240,7 @@ public class FunctionType {
|
|||||||
return Object.class;
|
return Object.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Class<?> extractClass(Type param, ParamType paramType) {
|
private static Class<?> extractClass(Type param, ParamType paramType) {
|
||||||
if (param instanceof ParameterizedType) {
|
if (param instanceof ParameterizedType) {
|
||||||
ParameterizedType concrete = (ParameterizedType) param;
|
ParameterizedType concrete = (ParameterizedType) param;
|
||||||
param = concrete.getRawType();
|
param = concrete.getRawType();
|
||||||
|
|||||||
@@ -165,6 +165,18 @@ public class FunctionTypeTests {
|
|||||||
assertThat(function.isMessage()).isEqualTo(true);
|
assertThat(function.isMessage()).isEqualTo(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void compose() {
|
||||||
|
FunctionType input = FunctionType.from(Foo.class).to(Bar.class).wrap(Flux.class);
|
||||||
|
FunctionType output = FunctionType.from(Bar.class).to(String.class);
|
||||||
|
FunctionType function = FunctionType.compose(input, output);
|
||||||
|
assertThat(function.getInputType()).isEqualTo(Foo.class);
|
||||||
|
assertThat(function.getOutputType()).isEqualTo(String.class);
|
||||||
|
assertThat(function.getInputWrapper()).isEqualTo(Flux.class);
|
||||||
|
assertThat(function.getOutputWrapper()).isEqualTo(Flux.class);
|
||||||
|
assertThat(function.isMessage()).isEqualTo(false);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void idempotentMessage() {
|
public void idempotentMessage() {
|
||||||
FunctionType function = FunctionType.from(Foo.class).to(Bar.class).message()
|
FunctionType function = FunctionType.from(Foo.class).to(Bar.class).message()
|
||||||
|
|||||||
@@ -98,7 +98,8 @@ public class ContextFunctionPostProcessorTests {
|
|||||||
Function<Flux<Integer>, Flux<String>> foos = (Function<Flux<Integer>, Flux<String>>) processor
|
Function<Flux<Integer>, Flux<String>> foos = (Function<Flux<Integer>, Flux<String>>) processor
|
||||||
.lookupFunction("foos,bars");
|
.lookupFunction("foos,bars");
|
||||||
assertThat(foos.apply(Flux.just(2)).blockFirst()).isEqualTo("Hello 4");
|
assertThat(foos.apply(Flux.just(2)).blockFirst()).isEqualTo("Hello 4");
|
||||||
assertThat(processor.getRegistration(foos).getNames()).containsExactly("foos|bars");
|
assertThat(processor.getRegistration(foos).getNames())
|
||||||
|
.containsExactly("foos|bars");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -109,7 +110,21 @@ public class ContextFunctionPostProcessorTests {
|
|||||||
Function<Flux<Integer>, Flux<String>> foos = (Function<Flux<Integer>, Flux<String>>) processor
|
Function<Flux<Integer>, Flux<String>> foos = (Function<Flux<Integer>, Flux<String>>) processor
|
||||||
.lookupFunction("foos|bars");
|
.lookupFunction("foos|bars");
|
||||||
assertThat(foos.apply(Flux.just(2)).blockFirst()).isEqualTo("Hello 4");
|
assertThat(foos.apply(Flux.just(2)).blockFirst()).isEqualTo("Hello 4");
|
||||||
assertThat(processor.getRegistration(foos).getNames()).containsExactly("foos|bars");
|
assertThat(processor.getRegistration(foos).getNames())
|
||||||
|
.containsExactly("foos|bars");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void composeWrapper() {
|
||||||
|
processor.register(new FunctionRegistration<>(new WrappedSource()).names("ints"));
|
||||||
|
processor.register(new FunctionRegistration<>(new Foos()).names("foos"));
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Supplier<Flux<String>> foos = (Supplier<Flux<String>>) processor
|
||||||
|
.lookupSupplier("ints|foos");
|
||||||
|
assertThat(foos.get().blockFirst()).isEqualTo("8");
|
||||||
|
assertThat(processor.getRegistration(foos).getNames())
|
||||||
|
.containsExactly("ints|foos");
|
||||||
|
assertThat(processor.getRegistration(foos).getType().getOutputWrapper()).isEqualTo(Flux.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -127,7 +142,8 @@ public class ContextFunctionPostProcessorTests {
|
|||||||
public void isolatedSupplier() {
|
public void isolatedSupplier() {
|
||||||
contextClassLoader = ClassUtils
|
contextClassLoader = ClassUtils
|
||||||
.overrideThreadContextClassLoader(getClass().getClassLoader());
|
.overrideThreadContextClassLoader(getClass().getClassLoader());
|
||||||
processor.register(new FunctionRegistration<>(create(Source.class)).names("source"));
|
processor.register(
|
||||||
|
new FunctionRegistration<>(create(Source.class)).names("source"));
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
Supplier<Flux<Integer>> source = (Supplier<Flux<Integer>>) processor
|
Supplier<Flux<Integer>> source = (Supplier<Flux<Integer>>) processor
|
||||||
.lookupSupplier("source");
|
.lookupSupplier("source");
|
||||||
@@ -145,7 +161,8 @@ public class ContextFunctionPostProcessorTests {
|
|||||||
.lookupConsumer("sink");
|
.lookupConsumer("sink");
|
||||||
sink.accept(Flux.just("Hello"));
|
sink.accept(Flux.just("Hello"));
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
List<String> values = (List<String>) ReflectionTestUtils.getField(target, "values");
|
List<String> values = (List<String>) ReflectionTestUtils.getField(target,
|
||||||
|
"values");
|
||||||
assertThat(values).contains("Hello");
|
assertThat(values).contains("Hello");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,6 +218,15 @@ public class ContextFunctionPostProcessorTests {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class WrappedSource implements Supplier<Flux<Integer>> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Flux<Integer> get() {
|
||||||
|
return Flux.just(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public static class Foo {
|
public static class Foo {
|
||||||
private String value;
|
private String value;
|
||||||
|
|
||||||
@@ -239,5 +265,5 @@ public class ContextFunctionPostProcessorTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,8 +33,8 @@ public class SampleApplication {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public Supplier<Flux<Bar>> words() {
|
public Supplier<Flux<Foo>> words() {
|
||||||
return () -> Flux.fromArray(new Bar[] { new Bar("foo"), new Bar("bar") }).log();
|
return () -> Flux.fromArray(new Foo[] { new Foo("foo"), new Foo("bar") }).log();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
|
|||||||
@@ -51,6 +51,13 @@ public class SampleApplicationTests {
|
|||||||
String.class)).isEqualTo("[{\"value\":\"FOO\"}]");
|
String.class)).isEqualTo("[{\"value\":\"FOO\"}]");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void composite() {
|
||||||
|
assertThat(new TestRestTemplate()
|
||||||
|
.getForObject("http://localhost:" + port + "/words,uppercase", String.class))
|
||||||
|
.isEqualTo("[{\"value\":\"FOO\"},{\"value\":\"BAR\"}]");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void single() {
|
public void single() {
|
||||||
assertThat(new TestRestTemplate().postForObject(
|
assertThat(new TestRestTemplate().postForObject(
|
||||||
|
|||||||
Reference in New Issue
Block a user