diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/function/FunctionEndpointInitializer.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/function/FunctionEndpointInitializer.java index 3912b2c98..52780ac53 100644 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/function/FunctionEndpointInitializer.java +++ b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/function/FunctionEndpointInitializer.java @@ -20,6 +20,7 @@ import java.lang.management.ManagementFactory; import java.time.Duration; import java.util.Set; import java.util.function.Function; +import java.util.function.Supplier; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -43,6 +44,7 @@ import org.springframework.cloud.function.web.BasicStringConverter; import org.springframework.cloud.function.web.RequestProcessor; import org.springframework.cloud.function.web.RequestProcessor.FunctionWrapper; import org.springframework.cloud.function.web.StringConverter; +import org.springframework.cloud.function.web.constants.WebRequestConstants; import org.springframework.cloud.function.web.util.FunctionWebUtils; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextInitializer; @@ -61,10 +63,12 @@ import org.springframework.web.reactive.function.server.HandlerStrategies; import org.springframework.web.reactive.function.server.RouterFunction; import org.springframework.web.reactive.function.server.RouterFunctions; import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; import org.springframework.web.server.WebExceptionHandler; import org.springframework.web.server.adapter.HttpWebHandlerAdapter; import org.springframework.web.server.adapter.WebHttpHandlerBuilder; +import static org.springframework.web.reactive.function.server.RequestPredicates.GET; import static org.springframework.web.reactive.function.server.RequestPredicates.POST; import static org.springframework.web.reactive.function.server.RouterFunctions.route; import static org.springframework.web.reactive.function.server.ServerResponse.status; @@ -209,9 +213,8 @@ class FunctionEndpointFactory { this.handler = handler; } - @SuppressWarnings("unchecked") - private Function, Flux> extract(ServerRequest request) { - Function, Flux> function; + private Object extract(ServerRequest request) { + Object function; if (handler != null) { logger.info("Configured function: " + handler); Set names = this.functionCatalog.getNames(Function.class); @@ -219,7 +222,7 @@ class FunctionEndpointFactory { function = this.functionCatalog.lookup(Function.class, handler); } else { - function = (Function, Flux>) FunctionWebUtils.findFunction(request.method(), functionCatalog, + function = FunctionWebUtils.findFunction(request.method(), functionCatalog, request.attributes(), request.path()); } return function; @@ -228,7 +231,7 @@ class FunctionEndpointFactory { @SuppressWarnings({ "unchecked" }) public RouterFunction functionEndpoints() { return route(POST("/**"), request -> { - Function, Flux> function = extract(request); + Function, Flux> function = (Function, Flux>) extract(request); Class outputType = (Class) this.inspector.getOutputType(function); FunctionWrapper wrapper = RequestProcessor.wrapper(function, null, null); Mono> stream = request.bodyToMono(String.class) @@ -237,7 +240,26 @@ class FunctionEndpointFactory { return status(entity.getStatusCode()).headers(headers -> headers.addAll(entity.getHeaders())) .body(Mono.just((T) entity.getBody()), outputType); }); + }) + .andRoute(GET("/**"), request -> { + Object functionComponent = extract(request); + if (functionComponent instanceof Supplier || functionComponent instanceof Function) { + Class outputType = (Class) this.inspector.getOutputType(functionComponent); + if (functionComponent instanceof Supplier) { + Supplier> supplier = (Supplier>) functionComponent; + FunctionWrapper wrapper = RequestProcessor.wrapper(null, null, supplier); + return ServerResponse.ok().body(wrapper.supplier().get(), outputType); + } + else if (functionComponent instanceof Function) { + Function, Flux> function = (Function, Flux>) functionComponent; + FunctionWrapper wrapper = RequestProcessor.wrapper(function, null, null); + wrapper.headers(request.headers().asHttpHeaders()); + String argument = (String) request.attribute(WebRequestConstants.ARGUMENT).get(); + wrapper.argument(Flux.just(argument)); + return ServerResponse.ok().body(wrapper.function().apply(wrapper.argument()), outputType); + } + } + throw new UnsupportedOperationException("Consumer is not supported for GET"); }); } - } diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/function/FunctionEndpointInitializerTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/function/FunctionEndpointInitializerTests.java index 48d204c3a..8f47ab6b4 100644 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/function/FunctionEndpointInitializerTests.java +++ b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/function/FunctionEndpointInitializerTests.java @@ -18,6 +18,7 @@ package org.springframework.cloud.function.web.function; import java.net.URI; import java.util.function.Function; +import java.util.function.Supplier; import org.junit.After; import org.junit.Before; @@ -90,11 +91,37 @@ public class FunctionEndpointInitializerTests { assertThat(response.getBody()).isEqualTo("desserts"); } + @Test + public void testGetWithtFunction() throws Exception { + FunctionalSpringApplication.run(ApplicationConfiguration.class); + TestRestTemplate testRestTemplate = new TestRestTemplate(); + String port = System.getProperty("server.port"); + Thread.sleep(200); + ResponseEntity response = testRestTemplate + .getForEntity(new URI("http://localhost:" + port + "/reverse/stressed"), String.class); + assertThat(response.getBody()).isEqualTo("desserts"); + } + + @Test + public void testGetWithtSupplier() throws Exception { + FunctionalSpringApplication.run(ApplicationConfiguration.class); + TestRestTemplate testRestTemplate = new TestRestTemplate(); + String port = System.getProperty("server.port"); + Thread.sleep(200); + ResponseEntity response = testRestTemplate + .getForEntity(new URI("http://localhost:" + port + "/supplier"), String.class); + assertThat(response.getBody()).isEqualTo("Jim Lahey"); + } + @SpringBootConfiguration protected static class ApplicationConfiguration implements ApplicationContextInitializer { + public Supplier supplier() { + return () -> "Jim Lahey"; + } + public Function uppercase() { return s -> s.toUpperCase(); } @@ -118,6 +145,9 @@ public class FunctionEndpointInitializerTests { applicationContext.registerBean("lowercase", FunctionRegistration.class, () -> new FunctionRegistration<>(lowercase()) .type(FunctionType.from(String.class).to(String.class))); + applicationContext.registerBean("supplier", FunctionRegistration.class, + () -> new FunctionRegistration<>(supplier()) + .type(FunctionType.supplier(String.class))); } }