From f94d672dc4d71aa6085c4b3fdea44764929a94b1 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Wed, 28 Jun 2017 11:00:07 +0100 Subject: [PATCH] Add test and fix type inspection for scanned functions Fixes gh-73 --- ...ntextFunctionCatalogAutoConfiguration.java | 14 ++- ...FunctionCatalogAutoConfigurationTests.java | 16 ++++ .../cloud/function/scan/ScannedFunction.java | 37 ++++++++ .../cloud/function/scan/ComponentTests.java | 87 +++++++++++++++++++ 4 files changed, 152 insertions(+), 2 deletions(-) create mode 100644 spring-cloud-function-context/src/test/java/org/springframework/cloud/function/scan/ScannedFunction.java create mode 100644 spring-cloud-function-web/src/test/java/org/springframework/cloud/function/scan/ComponentTests.java diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/ContextFunctionCatalogAutoConfiguration.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/ContextFunctionCatalogAutoConfiguration.java index dd6691b42..e1870b7e4 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/ContextFunctionCatalogAutoConfiguration.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/ContextFunctionCatalogAutoConfiguration.java @@ -409,8 +409,18 @@ public class ContextFunctionCatalogAutoConfiguration { } else if (source instanceof FileSystemResource) { try { - Type type = ClassUtils.forName(definition.getBeanClassName(), null); - param = extractType(type, paramType, index); + Class beanType = ClassUtils.forName(definition.getBeanClassName(), + null); + for (Type type : beanType.getGenericInterfaces()) { + if (type.getTypeName().startsWith("java.util.function")) { + param = extractType(type, paramType, index); + break; + } + } + if (param == null) { + // Last chance + param = beanType; + } } catch (ClassNotFoundException e) { throw new IllegalStateException( diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/ContextFunctionCatalogAutoConfigurationTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/ContextFunctionCatalogAutoConfigurationTests.java index 69ecca78b..d8f623edc 100644 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/ContextFunctionCatalogAutoConfigurationTests.java +++ b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/ContextFunctionCatalogAutoConfigurationTests.java @@ -32,6 +32,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.function.compiler.CompiledFunctionFactory; import org.springframework.cloud.function.compiler.FunctionCompiler; +import org.springframework.cloud.function.scan.ScannedFunction; import org.springframework.cloud.function.test.GenericFunction; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; @@ -120,6 +121,15 @@ public class ContextFunctionCatalogAutoConfigurationTests { assertThat(inspector.getInputWrapper("function")).isAssignableFrom(Map.class); } + @Test + public void componentScanBeanFunction() { + create(ComponentScanBeanConfiguration.class); + assertThat(context.getBean("function")).isInstanceOf(Function.class); + assertThat(catalog.lookupFunction("function")).isInstanceOf(Function.class); + assertThat(inspector.getInputType("function")).isAssignableFrom(Map.class); + assertThat(inspector.getInputWrapper("function")).isAssignableFrom(Map.class); + } + @Test public void componentScanFunction() { create(ComponentScanConfiguration.class); @@ -285,6 +295,12 @@ public class ContextFunctionCatalogAutoConfigurationTests { @EnableAutoConfiguration @Configuration @ComponentScan(basePackageClasses = GenericFunction.class) + protected static class ComponentScanBeanConfiguration { + } + + @EnableAutoConfiguration + @Configuration + @ComponentScan(basePackageClasses = ScannedFunction.class) protected static class ComponentScanConfiguration { } diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/scan/ScannedFunction.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/scan/ScannedFunction.java new file mode 100644 index 000000000..ffc70a99f --- /dev/null +++ b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/scan/ScannedFunction.java @@ -0,0 +1,37 @@ +/* + * Copyright 2016-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.function.scan; + +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +import org.springframework.stereotype.Component; + +/** + * @author Dave Syer + * + */ +@Component("function") +public class ScannedFunction + implements Function, Map> { + @Override + public Map apply(Map m) { + return m.entrySet().stream().collect(Collectors.toMap(e -> e.getKey(), + e -> e.getValue().toString().toUpperCase())); + } +} diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/scan/ComponentTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/scan/ComponentTests.java new file mode 100644 index 000000000..e05833d47 --- /dev/null +++ b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/scan/ComponentTests.java @@ -0,0 +1,87 @@ +/* + * Copyright 2012-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.function.scan; + +import java.net.URI; +import java.util.function.Function; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.embedded.LocalServerPort; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.RequestEntity; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +import reactor.core.publisher.Flux; + +/** + * @author Dave Syer + * + */ +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +public class ComponentTests { + + @LocalServerPort + private int port; + @Autowired + private Greeter greeter; + @Autowired + private TestRestTemplate rest; + + @Test + public void contextLoads() throws Exception { + assertThat(greeter).isNotNull(); + } + + @Test + public void greeter() throws Exception { + ResponseEntity result = rest + .exchange( + RequestEntity.post(new URI("/greeter")) + .contentType(MediaType.TEXT_PLAIN).body("World"), + String.class); + assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(result.getBody()).isEqualTo("Hello World"); + } + + @SpringBootApplication + @ComponentScan + protected static class TestConfiguration { + } + + @Component("greeter") + protected static class Greeter implements Function, Flux> { + @Override + public Flux apply(Flux flux) { + return flux.map(name -> "Hello " + name); + } + } + +}