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:
committed by
Oleg Zhurakousky
parent
570bbb23b5
commit
01e813ec52
@@ -18,6 +18,7 @@ package org.springframework.cloud.function.adapter.aws;
|
|||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import com.amazonaws.services.lambda.runtime.Context;
|
import com.amazonaws.services.lambda.runtime.Context;
|
||||||
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
|
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
|
||||||
@@ -36,6 +37,7 @@ import org.springframework.messaging.support.GenericMessage;
|
|||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
* @author Oleg Zhurakousky
|
* @author Oleg Zhurakousky
|
||||||
* @author Semyon Fishman
|
* @author Semyon Fishman
|
||||||
|
* @author Markus Gulden
|
||||||
*/
|
*/
|
||||||
public class SpringBootApiGatewayRequestHandler extends
|
public class SpringBootApiGatewayRequestHandler extends
|
||||||
SpringBootRequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {
|
SpringBootRequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {
|
||||||
@@ -56,12 +58,22 @@ public class SpringBootApiGatewayRequestHandler extends
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Object convertEvent(APIGatewayProxyRequestEvent event) {
|
protected Object convertEvent(APIGatewayProxyRequestEvent event) {
|
||||||
Object body = deserializeBody(event.getBody());
|
|
||||||
if (functionAcceptsMessage()) {
|
if (event.getBody() != null) {
|
||||||
return new GenericMessage<>(body, getHeaders(event));
|
if (functionAcceptsMessage()) {
|
||||||
|
return new GenericMessage<>(deserializeBody(event.getBody()), getHeaders(event));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return deserializeBody(event.getBody());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
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) {
|
if (event.getHeaders() != null) {
|
||||||
headers.putAll(event.getHeaders());
|
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);
|
headers.put("request", event);
|
||||||
return new MessageHeaders(headers);
|
return new MessageHeaders(headers);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package org.springframework.cloud.function.adapter.aws;
|
package org.springframework.cloud.function.adapter.aws;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
@@ -37,7 +38,8 @@ import org.springframework.messaging.support.GenericMessage;
|
|||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* @author Dimitry Declercq
|
||||||
|
* @author Markus Gulden
|
||||||
*/
|
*/
|
||||||
public class SpringBootApiGatewayRequestHandlerTests {
|
public class SpringBootApiGatewayRequestHandlerTests {
|
||||||
|
|
||||||
@@ -102,6 +104,47 @@ public class SpringBootApiGatewayRequestHandlerTests {
|
|||||||
.isEqualTo("{\"value\":\"FOO\"}");
|
.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
|
@Configuration
|
||||||
@Import({ ContextFunctionCatalogAutoConfiguration.class,
|
@Import({ ContextFunctionCatalogAutoConfiguration.class,
|
||||||
JacksonAutoConfiguration.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 {
|
protected static class Foo {
|
||||||
|
|
||||||
private String value;
|
private String value;
|
||||||
|
|||||||
Reference in New Issue
Block a user