GH-293 Enhanced endpoint mapping support for functional form context configuration

- Enhanced HTTP endpoint mapping support for 'functional form' context configuration ensuring it can register multiple endpoint to maintain the same behaviour as with regular application context
- Additional consolidation around Function Catalog
- Added identical test for functional and non-functional form endpoint configuration.

Resolves #293
This commit is contained in:
Oleg Zhurakousky
2019-03-19 07:51:24 +01:00
parent 3b4e9616ae
commit 87a878879c
11 changed files with 418 additions and 243 deletions

View File

@@ -47,6 +47,7 @@ import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
@@ -186,6 +187,28 @@ public abstract class AbstractComposableFunctionRegistry implements FunctionRegi
this.environment = environment;
}
@Override
public FunctionRegistration<?> getRegistration(Object function) {
String functionName = function == null ? null
: this.lookupFunctionName(function);
if (StringUtils.hasText(functionName)) {
FunctionRegistration<?> registration = new FunctionRegistration<Object>(
function, functionName);
FunctionType functionType = this.findType(registration);
return registration.type(functionType.getType());
}
return null;
}
@Override
public <T> void register(FunctionRegistration<T> functionRegistration) {
Assert.notEmpty(functionRegistration.getNames(),
"'registration' must contain at least one name before it is registered in catalog.");
register(functionRegistration, functionRegistration.getNames().iterator().next());
}
/**
* Registers function wrapped by the provided FunctionRegistration with
* this FunctionRegistry.
@@ -236,16 +259,13 @@ public abstract class AbstractComposableFunctionRegistry implements FunctionRegi
}
protected FunctionType findType(FunctionRegistration<?> functionRegistration) {
FunctionType functionType = functionRegistration.getType();
if (functionType != null) {
return functionType;
}
throw new IllegalStateException(
"Unless FunctionType is already available in FunctionRegistration, "
+ "this operation must be overriden "
+ "by the implementation of the FunctionRegistry.");
String name = this.lookupFunctionName(functionRegistration.getTarget());
return functionRegistration.getType() != null
? functionRegistration.getType()
: this.getFunctionType(name);
}
protected void addSupplier(String name, Supplier<?> supplier) {
this.suppliers.put(name, supplier);
}
@@ -327,8 +347,8 @@ public abstract class AbstractComposableFunctionRegistry implements FunctionRegi
}
else if (composedFunction instanceof Supplier) {
this.addSupplier(name, (Supplier<?>) composedFunction);
}
// this.register(composedRegistration);
}
}

View File

@@ -17,14 +17,10 @@
package org.springframework.cloud.function.context.catalog;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import org.springframework.cloud.function.context.FunctionRegistration;
import org.springframework.cloud.function.context.FunctionType;
import org.springframework.util.Assert;
/**
@@ -34,72 +30,21 @@ import org.springframework.util.Assert;
*/
public class InMemoryFunctionCatalog extends AbstractComposableFunctionRegistry {
private final Map<Object, FunctionRegistration<?>> registrations;
public InMemoryFunctionCatalog() {
this(Collections.emptySet());
}
public InMemoryFunctionCatalog(Set<FunctionRegistration<?>> registrations) {
Assert.notNull(registrations, "'registrations' must not be null");
this.registrations = new HashMap<>();
registrations.stream().forEach(reg -> register(reg));
}
@Override
public FunctionRegistration<?> getRegistration(Object function) {
return this.registrations.get(function);
protected FunctionType findType(FunctionRegistration<?> functionRegistration) {
FunctionType functionType = super.findType(functionRegistration);
if (functionType == null) {
functionType = new FunctionType(functionRegistration.getTarget().getClass());
}
return functionType;
}
@Override
public <T> void register(FunctionRegistration<T> functionRegistration) {
Assert.notEmpty(functionRegistration.getNames(),
"'registration' must contain at least one name before it is registered in catalog.");
// TODO should we just delegate to wrap(..)????
// wrap(functionRegistration, functionRegistration.getNames().iterator().next());
Class<?> type = Object.class;
if (functionRegistration.getTarget() instanceof Function) {
type = Function.class;
}
else if (functionRegistration.getTarget() instanceof Supplier) {
type = Supplier.class;
}
else if (functionRegistration.getTarget() instanceof Consumer) {
type = Consumer.class;
}
FunctionRegistrationEvent event = new FunctionRegistrationEvent(this, type,
functionRegistration.getNames());
this.registrations.put(functionRegistration.getTarget(), functionRegistration);
FunctionRegistration<T> wrapped = functionRegistration.wrap();
if (wrapped != functionRegistration) {
functionRegistration = wrapped;
this.registrations.put(wrapped.getTarget(), wrapped);
if (type == Consumer.class) {
type = Function.class;
}
}
for (String name : functionRegistration.getNames()) {
addType(name, functionRegistration.getType());
addName(functionRegistration.getTarget(), name);
if (functionRegistration.getTarget() instanceof Function) {
this.addFunction(name, (Function<?, ?>) functionRegistration.getTarget());
}
else if (functionRegistration.getTarget() instanceof Consumer) {
this.addConsumer(name, (Consumer<?>) functionRegistration.getTarget());
}
else {
this.addSupplier(name, (Supplier<?>) functionRegistration.getTarget());
}
}
this.publishEvent(event);
}
private void publishEvent(Object event) {
if (this.applicationEventPublisher != null) {
this.applicationEventPublisher.publishEvent(event);
}
}
}

View File

@@ -62,8 +62,6 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.type.StandardMethodMetadata;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* @author Dave Syer
@@ -93,26 +91,6 @@ public class ContextFunctionCatalogAutoConfiguration {
private ConfigurableListableBeanFactory beanFactory;
@Override
public FunctionRegistration<?> getRegistration(Object function) {
String functionName = function == null ? null
: this.lookupFunctionName(function);
if (StringUtils.hasText(functionName)) {
FunctionRegistration<?> registration = new FunctionRegistration<Object>(
function, functionName);
FunctionType functionType = this.findType(registration);
return registration.type(functionType.getType());
}
return null;
}
@Override
public <T> void register(FunctionRegistration<T> functionRegistration) {
Assert.notEmpty(functionRegistration.getNames(),
"'registration' must contain at least one name before it is registered in catalog.");
register(functionRegistration, functionRegistration.getNames().iterator().next());
}
/**
* Will collect all suppliers, functions, consumers and function registration as
* late as possible in the lifecycle.
@@ -160,13 +138,9 @@ public class ContextFunctionCatalogAutoConfiguration {
@Override
protected FunctionType findType(FunctionRegistration<?> functionRegistration) {
if (functionRegistration.getType() != null) {
return functionRegistration.getType();
}
String name = this.lookupFunctionName(functionRegistration.getTarget());
FunctionType functionType = this.getFunctionType(name);
FunctionType functionType = super.findType(functionRegistration);
if (functionType == null) {
String name = this.lookupFunctionName(functionRegistration.getTarget());
functionType = functionByNameExist(name)
? new FunctionType(functionRegistration.getTarget().getClass())
: new FunctionType(

View File

@@ -44,7 +44,7 @@ public class InMemoryFunctionCatalogTests {
InMemoryFunctionCatalog catalog = new InMemoryFunctionCatalog();
catalog.register(registration);
FunctionRegistration<?> registration2 = catalog.getRegistration(function);
assertThat(registration2).isSameAs(registration);
assertThat(registration2.getType()).isEqualTo(registration.getType());
}
@Test

View File

@@ -50,7 +50,7 @@ import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Dave Syer
*
* @author Oleg Zhurakousky
*/
public class ContextFunctionCatalogInitializerTests {
@@ -163,7 +163,7 @@ public class ContextFunctionCatalogInitializerTests {
assertThat(bean).isNotSameAs(function);
assertThat(this.inspector.getRegistration(function)).isNotNull();
assertThat(this.inspector.getRegistration(function).getType()).isEqualTo(
FunctionType.from(String.class).to(String.class).wrap(Flux.class));
FunctionType.from(String.class).to(String.class));
}
@Test