GH-249, Add full HTTP support to SpringBootApiGatewayRequestHandler

* Add support for handling APIGatewayProxyRequestEvent objects with
empty body (should apply to all GET and DELETE requests, which
comply with recommandation of RFC-7231)

* Add support ReSTful APIs: path and query string parameters as well
as the HTTP method are now extracted from the
APIGatewayProxyRequestEvent object and forwarded to the function
as message headers

* Extend unit tests accordingly
This commit is contained in:
Markus Gulden
2019-06-16 16:39:31 +02:00
committed by Oleg Zhurakousky
parent 570bbb23b5
commit 01e813ec52
2 changed files with 99 additions and 5 deletions

View File

@@ -18,6 +18,7 @@ package org.springframework.cloud.function.adapter.aws;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
@@ -36,6 +37,7 @@ import org.springframework.messaging.support.GenericMessage;
* @author Dave Syer
* @author Oleg Zhurakousky
* @author Semyon Fishman
* @author Markus Gulden
*/
public class SpringBootApiGatewayRequestHandler extends
SpringBootRequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {
@@ -56,12 +58,22 @@ public class SpringBootApiGatewayRequestHandler extends
@Override
protected Object convertEvent(APIGatewayProxyRequestEvent event) {
Object body = deserializeBody(event.getBody());
if (functionAcceptsMessage()) {
return new GenericMessage<>(body, getHeaders(event));
if (event.getBody() != null) {
if (functionAcceptsMessage()) {
return new GenericMessage<>(deserializeBody(event.getBody()), getHeaders(event));
}
else {
return deserializeBody(event.getBody());
}
}
else {
return body;
if (functionAcceptsMessage()) {
return new GenericMessage<Optional<Void>>(Optional.empty(), getHeaders(event));
}
else {
return Optional.empty();
}
}
}
@@ -83,6 +95,13 @@ public class SpringBootApiGatewayRequestHandler extends
if (event.getHeaders() != null) {
headers.putAll(event.getHeaders());
}
if (event.getQueryStringParameters() != null) {
headers.putAll(event.getQueryStringParameters());
}
if (event.getPathParameters() != null) {
headers.putAll(event.getPathParameters());
}
headers.put("httpMethod", event.getHttpMethod());
headers.put("request", event);
return new MessageHeaders(headers);
}

View File

@@ -17,6 +17,7 @@
package org.springframework.cloud.function.adapter.aws;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -37,7 +38,8 @@ import org.springframework.messaging.support.GenericMessage;
import static org.assertj.core.api.Assertions.assertThat;
/**
*
* @author Dimitry Declercq
* @author Markus Gulden
*/
public class SpringBootApiGatewayRequestHandlerTests {
@@ -102,6 +104,47 @@ public class SpringBootApiGatewayRequestHandlerTests {
.isEqualTo("{\"value\":\"FOO\"}");
}
@Test
public void functionMessageBeanWithRequestParameters() {
this.handler = new SpringBootApiGatewayRequestHandler(
FunctionMessageEchoReqParametersConfig.class);
APIGatewayProxyRequestEvent request = new APIGatewayProxyRequestEvent();
request.setPathParameters(Collections.singletonMap("path", "pathValue"));
request.setQueryStringParameters(Collections.singletonMap("query", "queryValue"));
request.setHeaders(Collections.singletonMap("test-header", "headerValue"));
request.setHttpMethod("GET");
Object output = this.handler.handleRequest(request, null);
assertThat(output).isInstanceOf(APIGatewayProxyResponseEvent.class);
assertThat(((APIGatewayProxyResponseEvent) output).getStatusCode())
.isEqualTo(200);
assertThat(((APIGatewayProxyResponseEvent) output).getHeaders().get("path"))
.isEqualTo("pathValue");
assertThat(((APIGatewayProxyResponseEvent) output).getHeaders().get("query"))
.isEqualTo("queryValue");
assertThat(
((APIGatewayProxyResponseEvent) output).getHeaders().get("test-header"))
.isEqualTo("headerValue");
assertThat(((APIGatewayProxyResponseEvent) output).getHeaders().get("httpMethod"))
.isEqualTo("GET");
assertThat(((APIGatewayProxyResponseEvent) output).getBody())
.isEqualTo("{\"value\":\"body\"}");
}
@Test
public void functionMessageBeanWithEmptyResponse() {
this.handler = new SpringBootApiGatewayRequestHandler(
FunctionMessageConsumerConfig.class);
APIGatewayProxyRequestEvent request = new APIGatewayProxyRequestEvent();
Object output = this.handler.handleRequest(request, null);
assertThat(output).isInstanceOf(APIGatewayProxyResponseEvent.class);
assertThat(((APIGatewayProxyResponseEvent) output).getStatusCode())
.isEqualTo(200);
assertThat(((APIGatewayProxyResponseEvent) output).getBody()).isNull();
}
@Configuration
@Import({ ContextFunctionCatalogAutoConfiguration.class,
JacksonAutoConfiguration.class })
@@ -140,6 +183,38 @@ public class SpringBootApiGatewayRequestHandlerTests {
}
@Configuration
@Import({ ContextFunctionCatalogAutoConfiguration.class,
JacksonAutoConfiguration.class })
protected static class FunctionMessageEchoReqParametersConfig {
@Bean
public Function<Message<Foo>, Message<Bar>> function() {
return (message -> {
Map<String, Object> headers = new HashMap<>();
headers.put("path", message.getHeaders().get("path"));
headers.put("query", message.getHeaders().get("query"));
headers.put("test-header", message.getHeaders().get("test-header"));
headers.put("httpMethod", message.getHeaders().get("httpMethod"));
return new GenericMessage<>(new Bar("body"), headers);
});
}
}
@Configuration
@Import({ ContextFunctionCatalogAutoConfiguration.class,
JacksonAutoConfiguration.class })
protected static class FunctionMessageConsumerConfig {
@Bean
public Consumer<Message<Foo>> function() {
return (foo -> {
});
}
}
protected static class Foo {
private String value;