GH-891 Ensure HTTP Request params are mapped even when body is not present

Resolves #891
This commit is contained in:
Oleg Zhurakousky
2022-07-26 16:49:23 +02:00
parent f51279be48
commit e17f79d169
3 changed files with 66 additions and 17 deletions

View File

@@ -85,6 +85,9 @@ public final class FunctionWebRequestProcessingHelper {
@SuppressWarnings({ "rawtypes", "unchecked" })
public static Publisher<?> processRequest(FunctionWrapper wrapper, Object argument, boolean eventStream) {
if (argument == null) {
argument = "";
}
FunctionInvocationWrapper function = wrapper.getFunction();
if (function == null) {
@@ -95,21 +98,18 @@ public final class FunctionWebRequestProcessingHelper {
Message<?> inputMessage = null;
if (argument != null) {
MessageBuilder builder = MessageBuilder.withPayload(argument);
if (!CollectionUtils.isEmpty(wrapper.getParams())) {
builder = builder.setHeader(HeaderUtils.HTTP_REQUEST_PARAM, wrapper.getParams().toSingleValueMap());
}
inputMessage = builder.copyHeaders(headers.toSingleValueMap()).build();
MessageBuilder builder = MessageBuilder.withPayload(argument);
if (!CollectionUtils.isEmpty(wrapper.getParams())) {
builder = builder.setHeader(HeaderUtils.HTTP_REQUEST_PARAM, wrapper.getParams().toSingleValueMap());
}
inputMessage = builder.copyHeaders(headers.toSingleValueMap()).build();
if (function.isRoutingFunction()) {
function.setSkipOutputConversion(true);
}
Object input = argument == null ? "" : (argument instanceof Publisher ? Flux.from((Publisher) argument) : inputMessage);
Object result = function.apply(input);
Object result = function.apply(inputMessage);
if (function.isConsumer()) {
if (result instanceof Publisher) {
Mono.from((Publisher) result).subscribe();

View File

@@ -18,23 +18,34 @@ package org.springframework.cloud.function.web.function;
import java.net.URI;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import org.junit.jupiter.api.Test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.cloud.function.context.FunctionRegistration;
import org.springframework.cloud.function.context.FunctionalSpringApplication;
import org.springframework.cloud.function.context.catalog.FunctionTypeUtils;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.ResolvableType;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.util.UriComponentsBuilder;
import static org.assertj.core.api.Assertions.assertThat;
import static org.awaitility.Awaitility.await;
@@ -47,6 +58,28 @@ import static org.awaitility.Awaitility.await;
*/
public class FunctionEndpointInitializerTests {
@SuppressWarnings({ "rawtypes", "unchecked" })
@Test
public void testEmptyBodyRequestParameters() throws Exception {
int port = startServerAndWaitForPort(BeansConfiguration.class, false);
TestRestTemplate testRestTemplate = new TestRestTemplate();
Map<String, String> params = new HashMap<>();
params.put("fname", "Jim");
params.put("lname", "Lahey");
HttpHeaders headers = new HttpHeaders();
headers.set("Accept", "application/json");
HttpEntity entity = new HttpEntity(headers);
String urlTemplate = UriComponentsBuilder.fromHttpUrl("http://localhost:" + port + "/nullPayload")
.queryParam("fname", "Jim").queryParam("lname", "Lahey").encode().toUriString();
ResponseEntity<String> response = testRestTemplate.exchange(urlTemplate, HttpMethod.GET, entity, String.class);
String res = response.getBody();
assertThat(res).contains("Jim");
assertThat(res).contains("Lahey");
}
@Test
public void testNonExistingFunction() throws Exception {
int port = startServerAndWaitForPort(ApplicationConfiguration.class);
@@ -105,8 +138,10 @@ public class FunctionEndpointInitializerTests {
assertThat(response.getBody()).isEqualTo("Jim Lahey");
}
private int startServerAndWaitForPort(Class<?> primaryAppConfig) throws InterruptedException {
ConfigurableApplicationContext context = FunctionalSpringApplication.run(primaryAppConfig, "--server.port=0");
private int startServerAndWaitForPort(Class<?> primaryAppConfig, boolean functional) throws InterruptedException {
ConfigurableApplicationContext context = functional
? FunctionalSpringApplication.run(primaryAppConfig, "--server.port=0")
: SpringApplication.run(primaryAppConfig, "--server.port=0");
await()
.pollDelay(Duration.ofMillis(500))
.pollInterval(Duration.ofMillis(500))
@@ -118,6 +153,10 @@ public class FunctionEndpointInitializerTests {
return Integer.valueOf(context.getEnvironment().getProperty("local.server.port"));
}
private int startServerAndWaitForPort(Class<?> primaryAppConfig) throws InterruptedException {
return this.startServerAndWaitForPort(primaryAppConfig, true);
}
@SpringBootConfiguration
protected static class ConsumerConfiguration
implements ApplicationContextInitializer<GenericApplicationContext> {
@@ -136,6 +175,17 @@ public class FunctionEndpointInitializerTests {
}
@EnableAutoConfiguration
@Configuration
protected static class BeansConfiguration {
@Bean
public BiFunction<String, Map<String, Object>, Map<String, Object>> nullPayload() {
return (p, h) -> {
return h;
};
}
}
@SpringBootConfiguration
protected static class ApplicationConfiguration