diff --git a/spring-cloud-function-samples/function-sample-aws/pom.xml b/spring-cloud-function-samples/function-sample-aws/pom.xml index ee6ee3a41..5fc086c0f 100644 --- a/spring-cloud-function-samples/function-sample-aws/pom.xml +++ b/spring-cloud-function-samples/function-sample-aws/pom.xml @@ -14,7 +14,7 @@ org.springframework.boot spring-boot-starter-parent - 2.1.0.BUILD-SNAPSHOT + 2.1.0.RC1 diff --git a/spring-cloud-function-samples/function-sample-azure/pom.xml b/spring-cloud-function-samples/function-sample-azure/pom.xml index 79a74e3ea..9fc7534cf 100644 --- a/spring-cloud-function-samples/function-sample-azure/pom.xml +++ b/spring-cloud-function-samples/function-sample-azure/pom.xml @@ -13,7 +13,7 @@ org.springframework.boot spring-boot-starter-parent - 2.1.0.M4 + 2.1.0.RC1 diff --git a/spring-cloud-function-samples/function-sample-compiler/pom.xml b/spring-cloud-function-samples/function-sample-compiler/pom.xml index 86ca518eb..b0834d60b 100644 --- a/spring-cloud-function-samples/function-sample-compiler/pom.xml +++ b/spring-cloud-function-samples/function-sample-compiler/pom.xml @@ -13,7 +13,7 @@ org.springframework.boot spring-boot-starter-parent - 2.1.0.M1 + 2.1.0.RC1 @@ -22,7 +22,7 @@ 2.0.0.BUILD-SNAPSHOT Fishtown.BUILD-SNAPSHOT 3.1.2.RELEASE - 1.0.10.RELEASE + 1.0.17.RELEASE diff --git a/spring-cloud-function-samples/function-sample-pof/pom.xml b/spring-cloud-function-samples/function-sample-pof/pom.xml index 7313c6886..38b153c28 100644 --- a/spring-cloud-function-samples/function-sample-pof/pom.xml +++ b/spring-cloud-function-samples/function-sample-pof/pom.xml @@ -12,7 +12,7 @@ org.springframework.boot spring-boot-starter-parent - 2.1.0.M1 + 2.1.0.RC1 diff --git a/spring-cloud-function-samples/function-sample-pojo/src/test/java/com/example/SampleApplicationTests.java b/spring-cloud-function-samples/function-sample-pojo/src/test/java/com/example/SampleApplicationTests.java index 000cb1dda..112334157 100644 --- a/spring-cloud-function-samples/function-sample-pojo/src/test/java/com/example/SampleApplicationTests.java +++ b/spring-cloud-function-samples/function-sample-pojo/src/test/java/com/example/SampleApplicationTests.java @@ -18,6 +18,7 @@ package com.example; import java.net.URI; import java.util.Arrays; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; @@ -41,40 +42,38 @@ public class SampleApplicationTests { @LocalServerPort private int port; + private TestRestTemplate rest = new TestRestTemplate(); @Test public void words() { - assertThat(new TestRestTemplate() - .getForObject("http://localhost:" + port + "/words", String.class)) + assertThat(rest.getForObject("http://localhost:" + port + "/words", String.class)) .isEqualTo("[{\"value\":\"foo\"},{\"value\":\"bar\"}]"); } @Test public void uppercase() { - assertThat(new TestRestTemplate().postForObject( - "http://localhost:" + port + "/uppercase", "[{\"value\":\"foo\"}]", - String.class)).isEqualTo("[{\"value\":\"FOO\"}]"); + assertThat(rest.postForObject("http://localhost:" + port + "/uppercase", + "[{\"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\"}]"); + assertThat(rest.getForObject("http://localhost:" + port + "/words,uppercase", + String.class)).isEqualTo("[{\"value\":\"FOO\"},{\"value\":\"BAR\"}]"); } @Test public void single() { - assertThat(new TestRestTemplate().postForObject( - "http://localhost:" + port + "/uppercase", "{\"value\":\"foo\"}", - String.class)).isEqualTo("{\"value\":\"FOO\"}"); + assertThat(rest.postForObject("http://localhost:" + port + "/uppercase", + "{\"value\":\"foo\"}", String.class)).isEqualTo("{\"value\":\"FOO\"}"); } @Test public void lowercase() { - assertThat(new TestRestTemplate().postForObject( - "http://localhost:" + port + "/lowercase", "[{\"value\":\"Foo\"}]", - String.class)).isEqualTo("[{\"value\":\"foo\"}]"); + assertThat(rest.postForObject("http://localhost:" + port + "/lowercase", + "[{\"value\":\"Foo\"}]", String.class)) + .isEqualTo("[{\"value\":\"foo\"}]"); } @Test @@ -85,10 +84,25 @@ public class SampleApplicationTests { map.put("A", Arrays.asList("1", "2", "3")); map.put("B", Arrays.asList("5", "6")); - assertThat(new TestRestTemplate().exchange(RequestEntity.post(new URI("http://localhost:" + port + "/sum")) - .accept(MediaType.APPLICATION_JSON).contentType(MediaType.APPLICATION_FORM_URLENCODED) - .body(map), String.class).getBody()) - .isEqualTo("[{\"A\":6,\"B\":11}]"); + assertThat(rest.exchange( + RequestEntity.post(new URI("http://localhost:" + port + "/sum")) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_FORM_URLENCODED).body(map), + String.class).getBody()).isEqualTo("[{\"A\":6,\"B\":11}]"); } + @Test + @Ignore + public void multipart() throws Exception { + + LinkedMultiValueMap map = new LinkedMultiValueMap<>(); + + map.put("A", Arrays.asList("1", "2", "3")); + map.put("B", Arrays.asList("5", "6")); + + assertThat(rest.exchange( + RequestEntity.post(new URI("http://localhost:" + port + "/sum")).accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.MULTIPART_FORM_DATA).body(map), + String.class).getBody()).isEqualTo("[{\"A\":6,\"B\":11}]"); + } } diff --git a/spring-cloud-function-samples/function-sample/pom.xml b/spring-cloud-function-samples/function-sample/pom.xml index 810a4f5cb..710e8dff8 100644 --- a/spring-cloud-function-samples/function-sample/pom.xml +++ b/spring-cloud-function-samples/function-sample/pom.xml @@ -13,14 +13,14 @@ org.springframework.boot spring-boot-starter-parent - 2.1.0.M1 + 2.1.0.RC1 1.8 2.0.0.BUILD-SNAPSHOT - 1.0.13.RELEASE + 1.0.17.RELEASE diff --git a/spring-cloud-function-web/pom.xml b/spring-cloud-function-web/pom.xml index 1fa994faa..c138ba438 100644 --- a/spring-cloud-function-web/pom.xml +++ b/spring-cloud-function-web/pom.xml @@ -60,6 +60,11 @@ spring-boot-configuration-processor true + + org.synchronoss.cloud + nio-multipart-parser + test + diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/RequestProcessor.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/RequestProcessor.java index ae38f3bfa..b4132949f 100644 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/RequestProcessor.java +++ b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/RequestProcessor.java @@ -16,6 +16,8 @@ package org.springframework.cloud.function.web; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -97,15 +99,14 @@ public class RequestProcessor { boolean stream) { Object function = wrapper.handler(); Class inputType = inspector.getInputType(function); + Type itemType = getItemType(function); Object input = null; if (StringUtils.hasText(body)) { if (body.startsWith("[")) { - input = Collection.class.isAssignableFrom(inputType) - ? mapper.toObject(body, inputType) - : mapper.toObject(body, - ResolvableType.forClassWithGenerics(ArrayList.class, - (Class) inputType).getType()); + input = mapper.toObject(body, ResolvableType + .forClassWithGenerics(ArrayList.class, (Class) itemType) + .getType()); } else { if (inputType == String.class) { @@ -146,8 +147,8 @@ public class RequestProcessor { form.putAll(params); } - boolean inputIsCollection = - Collection.class.isAssignableFrom(inspector.getInputType(wrapper.handler())); + boolean inputIsCollection = Collection.class + .isAssignableFrom(inspector.getInputType(wrapper.handler())); Flux flux = body == null ? Flux.just(form) : inputIsCollection ? Flux.just(body) : Flux.fromIterable(iterable); if (inspector.isMessage(function)) { @@ -254,6 +255,42 @@ public class RequestProcessor { return Mono.from(function.apply(input)); } + private Object getTargetFunction(Object function) { + // we need to get the actual un-fluxed function so we can interrogate for types + Object target = inspector.getRegistration(function).getTarget(); + if (target instanceof FluxWrapper) { + target = ((FluxWrapper) target).getTarget(); + } + return target; + } + + private Type getItemType(Object function) { + Class inputType = inspector.getInputType(function); + if (!Collection.class.isAssignableFrom(inputType)) { + return inputType; + } + Type type = inspector.getRegistration(this.getTargetFunction(function)).getType() + .getType(); + if (type instanceof ParameterizedType) { + type = ((ParameterizedType) type).getActualTypeArguments()[0]; + } + else { + for (Type iface : ((Class) type).getGenericInterfaces()) { + if (iface.getTypeName().startsWith("java.util.function")) { + type = ((ParameterizedType) iface).getActualTypeArguments()[0]; + break; + } + } + } + if (type instanceof ParameterizedType) { + type = ((ParameterizedType) type).getActualTypeArguments()[0]; + } + else { + type = inputType; + } + return type; + } + public static class FunctionWrapper { private final Function, Publisher> function; diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/FunctionalWithInputCollectionTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/FunctionalWithInputCollectionTests.java index 3cf0eddf4..9367b43a5 100644 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/FunctionalWithInputCollectionTests.java +++ b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/FunctionalWithInputCollectionTests.java @@ -18,6 +18,7 @@ package org.springframework.cloud.function.test; import java.util.List; import java.util.function.Function; +import java.util.stream.Collectors; import org.junit.Test; import org.junit.runner.RunWith; @@ -45,15 +46,43 @@ public class FunctionalWithInputCollectionTests { @Test public void words() throws Exception { - client.post().uri("/").body(Mono.just("[\"foo\", \"bar\"]"), String.class).exchange() - .expectStatus().isOk().expectBody(String.class).isEqualTo("[FOO, BAR]"); + client.post().uri("/").body(Mono.just("[{\"value\":\"foo\"}, {\"value\":\"bar\"}]"), String.class) + .exchange().expectStatus().isOk().expectBody(String.class) + .isEqualTo("{\"value\":\"FOOBAR\"}"); } @SpringBootConfiguration - protected static class TestConfiguration implements Function, String> { + protected static class TestConfiguration implements Function, Foo> { @Override - public String apply(List value) { - return value.toString().toUpperCase(); + public Foo apply(List value) { + return new Foo(value.stream().map(foo -> foo.getValue().toUpperCase()) + .collect(Collectors.joining())); } } + + public static class Foo { + + private String value; + + public Foo() { + } + + public Foo(String value) { + this.value = value; + } + + public String getValue() { + return this.value; + } + + public void setValue(String value) { + this.value = value; + } + + @Override + public String toString() { + return "Foo [value=" + this.value + "]"; + } + + } }