GH-1052 Fix collection/array processing for AWS invocations with Publisher input type functions
Resolves #1052
This commit is contained in:
@@ -39,6 +39,90 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*/
|
||||
public class CustomRuntimeEventLoopTest {
|
||||
|
||||
private String API_EVENT = "{\n"
|
||||
+ " \"version\": \"1.0\",\n"
|
||||
+ " \"resource\": \"$default\",\n"
|
||||
+ " \"path\": \"/question\",\n"
|
||||
+ " \"httpMethod\": \"POST\",\n"
|
||||
+ " \"headers\": {\n"
|
||||
+ " \"Content-Length\": \"40\",\n"
|
||||
+ " \"Content-Type\": \"application/json\",\n"
|
||||
+ " \"Host\": \"emcdxu5ijj.execute-api.us-east-2.amazonaws.com\",\n"
|
||||
+ " \"User-Agent\": \"curl/7.88.1\",\n"
|
||||
+ " \"X-Amzn-Trace-Id\": \"Root=1-64ad9787-4c89d5af7607eb9e522e01d5\",\n"
|
||||
+ " \"X-Forwarded-For\": \"109.210.252.44\",\n"
|
||||
+ " \"X-Forwarded-Port\": \"443\",\n"
|
||||
+ " \"X-Forwarded-Proto\": \"https\",\n"
|
||||
+ " \"accept\": \"*/*\"\n"
|
||||
+ " },\n"
|
||||
+ " \"multiValueHeaders\": {\n"
|
||||
+ " \"Content-Length\": [\n"
|
||||
+ " \"40\"\n"
|
||||
+ " ],\n"
|
||||
+ " \"Content-Type\": [\n"
|
||||
+ " \"application/json\"\n"
|
||||
+ " ],\n"
|
||||
+ " \"Host\": [\n"
|
||||
+ " \"emcdxu5ijj.execute-api.us-east-2.amazonaws.com\"\n"
|
||||
+ " ],\n"
|
||||
+ " \"User-Agent\": [\n"
|
||||
+ " \"curl/7.88.1\"\n"
|
||||
+ " ],\n"
|
||||
+ " \"X-Amzn-Trace-Id\": [\n"
|
||||
+ " \"Root=1-64ad9787-4c89d5af7607eb9e522e01d5\"\n"
|
||||
+ " ],\n"
|
||||
+ " \"X-Forwarded-For\": [\n"
|
||||
+ " \"109.210.252.44\"\n"
|
||||
+ " ],\n"
|
||||
+ " \"X-Forwarded-Port\": [\n"
|
||||
+ " \"443\"\n"
|
||||
+ " ],\n"
|
||||
+ " \"X-Forwarded-Proto\": [\n"
|
||||
+ " \"https\"\n"
|
||||
+ " ],\n"
|
||||
+ " \"accept\": [\n"
|
||||
+ " \"*/*\"\n"
|
||||
+ " ]\n"
|
||||
+ " },\n"
|
||||
+ " \"queryStringParameters\": null,\n"
|
||||
+ " \"multiValueQueryStringParameters\": null,\n"
|
||||
+ " \"requestContext\": {\n"
|
||||
+ " \"accountId\": \"313369169943\",\n"
|
||||
+ " \"apiId\": \"emcdxu5ijj\",\n"
|
||||
+ " \"domainName\": \"emcdxu5ijj.execute-api.us-east-2.amazonaws.com\",\n"
|
||||
+ " \"domainPrefix\": \"emcdxu5ijj\",\n"
|
||||
+ " \"extendedRequestId\": \"H6SdPgXtiYcEP1w=\",\n"
|
||||
+ " \"httpMethod\": \"POST\",\n"
|
||||
+ " \"identity\": {\n"
|
||||
+ " \"accessKey\": null,\n"
|
||||
+ " \"accountId\": null,\n"
|
||||
+ " \"caller\": null,\n"
|
||||
+ " \"cognitoAmr\": null,\n"
|
||||
+ " \"cognitoAuthenticationProvider\": null,\n"
|
||||
+ " \"cognitoAuthenticationType\": null,\n"
|
||||
+ " \"cognitoIdentityId\": null,\n"
|
||||
+ " \"cognitoIdentityPoolId\": null,\n"
|
||||
+ " \"principalOrgId\": null,\n"
|
||||
+ " \"sourceIp\": \"109.210.252.44\",\n"
|
||||
+ " \"user\": null,\n"
|
||||
+ " \"userAgent\": \"curl/7.88.1\",\n"
|
||||
+ " \"userArn\": null\n"
|
||||
+ " },\n"
|
||||
+ " \"path\": \"/question\",\n"
|
||||
+ " \"protocol\": \"HTTP/1.1\",\n"
|
||||
+ " \"requestId\": \"H6SdPgXtiYcEP1w=\",\n"
|
||||
+ " \"requestTime\": \"11/Jul/2023:17:55:19 +0000\",\n"
|
||||
+ " \"requestTimeEpoch\": 1689098119662,\n"
|
||||
+ " \"resourceId\": \"$default\",\n"
|
||||
+ " \"resourcePath\": \"$default\",\n"
|
||||
+ " \"stage\": \"$default\"\n"
|
||||
+ " },\n"
|
||||
+ " \"pathParameters\": null,\n"
|
||||
+ " \"stageVariables\": null,\n"
|
||||
+ " \"body\": \"[{\\\"latitude\\\": 41.34, \\\"longitude\\\": 2.78},{\\\"latitude\\\": 43.24, \\\"longitude\\\": 3.78}]\",\n"
|
||||
+ " \"isBase64Encoded\": false\n"
|
||||
+ "}";
|
||||
|
||||
@Test
|
||||
public void testDefaultFunctionLookup() throws Exception {
|
||||
testDefaultFunctionLookup("uppercase", SingleFunctionConfiguration.class);
|
||||
@@ -98,6 +182,21 @@ public class CustomRuntimeEventLoopTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_HANDLERWithApiGatewayRequestAndFlux() throws Exception {
|
||||
try (ConfigurableApplicationContext userContext =
|
||||
new SpringApplicationBuilder(MultipleFunctionConfiguration.class, AWSCustomRuntime.class)
|
||||
.web(WebApplicationType.SERVLET)
|
||||
.properties("_HANDLER=echoFlux", "server.port=0")
|
||||
.run()) {
|
||||
|
||||
AWSCustomRuntime aws = userContext.getBean(AWSCustomRuntime.class);
|
||||
String response = aws.exchange(API_EVENT).getPayload();
|
||||
assertThat(response).contains("{\\\"latitude\\\":2.78,\\\"longitude\\\":41.34}");
|
||||
assertThat(response).contains("{\\\"latitude\\\":3.78,\\\"longitude\\\":43.24}");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@DirtiesContext
|
||||
public void test_definitionLookupAndComposition() throws Exception {
|
||||
@@ -149,6 +248,13 @@ public class CustomRuntimeEventLoopTest {
|
||||
public Function<Person, Person> uppercasePerson() {
|
||||
return p -> new Person(p.getName().toUpperCase());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Function<Flux<GeoLocation>, Flux<GeoLocation>> echoFlux() {
|
||||
return flux -> flux.map(g -> {
|
||||
return new GeoLocation(g.longitude(), g.latitude());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@@ -185,4 +291,7 @@ public class CustomRuntimeEventLoopTest {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public record GeoLocation(Float latitude, Float longitude) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,6 +74,8 @@ public class FunctionInvokerTests {
|
||||
|
||||
String jsonCollection = "[\"Ricky\",\"Julien\",\"Bubbles\"]";
|
||||
|
||||
String jsonPojoCollection = "[{\"name\":\"Ricky\"},{\"name\":\"Julien\"},{\"name\":\"Julien\"}]";
|
||||
|
||||
String dynamoDbEvent = "{\n"
|
||||
+ " \"Records\": [\n"
|
||||
+ " {\n"
|
||||
@@ -610,6 +612,84 @@ public class FunctionInvokerTests {
|
||||
" \"isBase64Encoded\": false\n" +
|
||||
"}";
|
||||
|
||||
String apiGatewayEventWithArray = "{\n" +
|
||||
" \"resource\": \"/uppercase2\",\n" +
|
||||
" \"path\": \"/uppercase2\",\n" +
|
||||
" \"httpMethod\": \"POST\",\n" +
|
||||
" \"headers\": {\n" +
|
||||
" \"accept\": \"*/*\",\n" +
|
||||
" \"content-type\": \"application/json\",\n" +
|
||||
" \"Host\": \"fhul32ccy2.execute-api.eu-west-3.amazonaws.com\",\n" +
|
||||
" \"User-Agent\": \"curl/7.54.0\",\n" +
|
||||
" \"X-Amzn-Trace-Id\": \"Root=1-5ece339e-e0595766066d703ec70f1522\",\n" +
|
||||
" \"X-Forwarded-For\": \"90.37.8.133\",\n" +
|
||||
" \"X-Forwarded-Port\": \"443\",\n" +
|
||||
" \"X-Forwarded-Proto\": \"https\"\n" +
|
||||
" },\n" +
|
||||
" \"multiValueHeaders\": {\n" +
|
||||
" \"accept\": [\n" +
|
||||
" \"*/*\"\n" +
|
||||
" ],\n" +
|
||||
" \"content-type\": [\n" +
|
||||
" \"application/json\"\n" +
|
||||
" ],\n" +
|
||||
" \"Host\": [\n" +
|
||||
" \"fhul32ccy2.execute-api.eu-west-3.amazonaws.com\"\n" +
|
||||
" ],\n" +
|
||||
" \"User-Agent\": [\n" +
|
||||
" \"curl/7.54.0\"\n" +
|
||||
" ],\n" +
|
||||
" \"X-Amzn-Trace-Id\": [\n" +
|
||||
" \"Root=1-5ece339e-e0595766066d703ec70f1522\"\n" +
|
||||
" ],\n" +
|
||||
" \"X-Forwarded-For\": [\n" +
|
||||
" \"90.37.8.133\"\n" +
|
||||
" ],\n" +
|
||||
" \"X-Forwarded-Port\": [\n" +
|
||||
" \"443\"\n" +
|
||||
" ],\n" +
|
||||
" \"X-Forwarded-Proto\": [\n" +
|
||||
" \"https\"\n" +
|
||||
" ]\n" +
|
||||
" },\n" +
|
||||
" \"queryStringParameters\": null,\n" +
|
||||
" \"multiValueQueryStringParameters\": null,\n" +
|
||||
" \"pathParameters\": null,\n" +
|
||||
" \"stageVariables\": null,\n" +
|
||||
" \"requestContext\": {\n" +
|
||||
" \"resourceId\": \"qf0io6\",\n" +
|
||||
" \"resourcePath\": \"/uppercase2\",\n" +
|
||||
" \"httpMethod\": \"POST\",\n" +
|
||||
" \"extendedRequestId\": \"NL0A1EokCGYFZOA=\",\n" +
|
||||
" \"requestTime\": \"27/May/2020:09:32:14 +0000\",\n" +
|
||||
" \"path\": \"/test/uppercase2\",\n" +
|
||||
" \"accountId\": \"123456789098\",\n" +
|
||||
" \"protocol\": \"HTTP/1.1\",\n" +
|
||||
" \"stage\": \"test\",\n" +
|
||||
" \"domainPrefix\": \"fhul32ccy2\",\n" +
|
||||
" \"requestTimeEpoch\": 1590571934872,\n" +
|
||||
" \"requestId\": \"b96500aa-f92a-43c3-9360-868ba4053a00\",\n" +
|
||||
" \"identity\": {\n" +
|
||||
" \"cognitoIdentityPoolId\": null,\n" +
|
||||
" \"accountId\": null,\n" +
|
||||
" \"cognitoIdentityId\": null,\n" +
|
||||
" \"caller\": null,\n" +
|
||||
" \"sourceIp\": \"90.37.8.133\",\n" +
|
||||
" \"principalOrgId\": null,\n" +
|
||||
" \"accessKey\": null,\n" +
|
||||
" \"cognitoAuthenticationType\": null,\n" +
|
||||
" \"cognitoAuthenticationProvider\": null,\n" +
|
||||
" \"userArn\": null,\n" +
|
||||
" \"userAgent\": \"curl/7.54.0\",\n" +
|
||||
" \"user\": null\n" +
|
||||
" },\n" +
|
||||
" \"domainName\": \"fhul32ccy2.execute-api.eu-west-3.amazonaws.com\",\n" +
|
||||
" \"apiId\": \"fhul32ccy2\"\n" +
|
||||
" },\n" +
|
||||
" \"body\":[{\"name\":\"Jim Lahey\"},{\"name\":\"Ricky\"}],\n" +
|
||||
" \"isBase64Encoded\": false\n" +
|
||||
"}";
|
||||
|
||||
String gwAuthorizerEvent = "{\n"
|
||||
+ " \"type\":\"TOKEN\",\n"
|
||||
+ " \"authorizationToken\":\"allow\",\n"
|
||||
@@ -650,6 +730,19 @@ public class FunctionInvokerTests {
|
||||
assertThat(result).isEqualTo(this.jsonCollection);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCollectionPojo() throws Exception {
|
||||
System.setProperty("MAIN_CLASS", SampleConfiguration.class.getName());
|
||||
System.setProperty("spring.cloud.function.definition", "echoPojoReactive");
|
||||
FunctionInvoker invoker = new FunctionInvoker();
|
||||
|
||||
InputStream targetStream = new ByteArrayInputStream(this.jsonPojoCollection.getBytes());
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
invoker.handleRequest(targetStream, output, null);
|
||||
String result = new String(output.toByteArray(), StandardCharsets.UTF_8);
|
||||
assertThat(result).isEqualTo(this.jsonPojoCollection);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKinesisStringEvent() throws Exception {
|
||||
System.setProperty("MAIN_CLASS", KinesisConfiguration.class.getName());
|
||||
@@ -1019,6 +1112,23 @@ public class FunctionInvokerTests {
|
||||
assertThat(person.getName()).isEqualTo("JIM LAHEY");
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Test
|
||||
public void testApiGatewayPojoReturninPojoReactive() throws Exception {
|
||||
System.setProperty("MAIN_CLASS", ApiGatewayConfiguration.class.getName());
|
||||
System.setProperty("spring.cloud.function.definition", "uppercasePojoReturnPojoReactive");
|
||||
FunctionInvoker invoker = new FunctionInvoker();
|
||||
|
||||
InputStream targetStream = new ByteArrayInputStream(this.apiGatewayEventWithArray.getBytes());
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
invoker.handleRequest(targetStream, output, null);
|
||||
|
||||
Map response = mapper.readValue(output.toByteArray(), Map.class);
|
||||
System.out.println(response);
|
||||
// Person person = mapper.readValue((String) response.get("body"), Person.class);
|
||||
// assertThat(person.getName()).isEqualTo("JIM LAHEY");
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Test
|
||||
public void testApiGatewayPojoEventBody() throws Exception {
|
||||
@@ -1339,6 +1449,11 @@ public class FunctionInvokerTests {
|
||||
public Function<Flux<String>, Flux<String>> echoStringReactive() {
|
||||
return v -> v;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Function<Flux<Person>, Flux<Person>> echoPojoReactive() {
|
||||
return v -> v;
|
||||
}
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@@ -1602,6 +1717,15 @@ public class FunctionInvokerTests {
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Function<Flux<Person>, Flux<Person>> uppercasePojoReturnPojoReactive() {
|
||||
return flux -> flux.map(v -> {
|
||||
Person p = new Person();
|
||||
p.setName(v.getName().toUpperCase());
|
||||
return p;
|
||||
});
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Function<APIGatewayProxyRequestEvent, String> inputApiEvent() {
|
||||
return v -> {
|
||||
|
||||
Reference in New Issue
Block a user