Try to detect single-valued returns in AWS
Lambdas generally only deal with single values anyway, but for consistency we should return similar results from a Lambda handler as from an MVC handler in s-c-f-web.
This commit is contained in:
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.function.adapter.aws;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Date;
|
||||
|
||||
import com.amazonaws.services.lambda.runtime.events.KinesisEvent;
|
||||
import com.amazonaws.services.lambda.runtime.events.KinesisEvent.KinesisEventRecord;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class FunctionInvokingKinesisEventHandler extends SpringBootRequestHandler<KinesisEvent, String> {
|
||||
|
||||
public FunctionInvokingKinesisEventHandler(Class<?> configurationClass) {
|
||||
super(configurationClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String convertEvent(KinesisEvent event) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (KinesisEventRecord record : event.getRecords()) {
|
||||
String id = record.getEventID();
|
||||
String name = record.getEventName();
|
||||
String source = record.getEventSource();
|
||||
Date timestamp = record.getKinesis().getApproximateArrivalTimestamp();
|
||||
String partitionKey = record.getKinesis().getPartitionKey();
|
||||
String sequenceNumber = record.getKinesis().getSequenceNumber();
|
||||
ByteBuffer data = record.getKinesis().getData();
|
||||
String dataString = new String(data.array(), Charset.forName("UTF-8"));
|
||||
result.append(String.format("id=%s,name=%s,source=%s,timestamp=%s,partitionKey=%s,sequenceNumber=%s,data=%s",
|
||||
id, name, source, timestamp, partitionKey, sequenceNumber, dataString));
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
}
|
||||
@@ -16,19 +16,28 @@
|
||||
|
||||
package org.springframework.cloud.function.adapter.aws;
|
||||
|
||||
import com.amazonaws.services.lambda.runtime.events.S3Event;
|
||||
import java.util.List;
|
||||
|
||||
import com.amazonaws.services.lambda.runtime.events.KinesisEvent;
|
||||
import com.amazonaws.services.lambda.runtime.events.KinesisEvent.KinesisEventRecord;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class FunctionInvokingS3EventHandler extends SpringBootRequestHandler<S3Event, String> {
|
||||
public class SpringBootKinesisEventHandler
|
||||
extends SpringBootRequestHandler<KinesisEvent, String> {
|
||||
|
||||
public FunctionInvokingS3EventHandler(Class<?> configurationClass) {
|
||||
public SpringBootKinesisEventHandler() {
|
||||
super();
|
||||
}
|
||||
|
||||
public SpringBootKinesisEventHandler(Class<?> configurationClass) {
|
||||
super(configurationClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String convertEvent(S3Event event) {
|
||||
return event.toJson();
|
||||
protected List<KinesisEventRecord> convertEvent(KinesisEvent event) {
|
||||
// TODO: maybe convert to List<Message>
|
||||
return event.getRecords();
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ import reactor.core.publisher.Flux;
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class SpringBootRequestHandler<E, O> extends SpringFunctionInitializer implements RequestHandler<E, List<O>> {
|
||||
public class SpringBootRequestHandler<E, O> extends SpringFunctionInitializer implements RequestHandler<E, Object> {
|
||||
|
||||
public SpringBootRequestHandler(Class<?> configurationClass) {
|
||||
super(configurationClass);
|
||||
@@ -39,24 +39,26 @@ public class SpringBootRequestHandler<E, O> extends SpringFunctionInitializer im
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<O> handleRequest(E event, Context context) {
|
||||
public Object handleRequest(E event, Context context) {
|
||||
initialize();
|
||||
Object input = convertEvent(event);
|
||||
Flux<?> output = apply(extract(input));
|
||||
return result(output);
|
||||
return result(input, output);
|
||||
}
|
||||
|
||||
private List<O> result(Flux<?> output) {
|
||||
private Object result(Object input, Flux<?> output) {
|
||||
List<Object> result = new ArrayList<>();
|
||||
for (Object value : output.toIterable()) {
|
||||
result.add(value);
|
||||
}
|
||||
return convertResult(result);
|
||||
if (isSingleValue(input) && result.size()==1) {
|
||||
return result.get(0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected List<O> convertResult(List<Object> value) {
|
||||
return (List<O>) value;
|
||||
private boolean isSingleValue(Object input) {
|
||||
return !(input instanceof Collection);
|
||||
}
|
||||
|
||||
private Flux<?> extract(Object input) {
|
||||
|
||||
@@ -54,17 +54,24 @@ public class SpringBootStreamHandler extends SpringFunctionInitializer
|
||||
initialize();
|
||||
Object value = convertStream(input);
|
||||
Flux<?> flux = apply(extract(value));
|
||||
mapper.writeValue(output, result(flux));
|
||||
mapper.writeValue(output, result(value, flux));
|
||||
}
|
||||
|
||||
private Object result(Flux<?> flux) {
|
||||
private Object result(Object input, Flux<?> flux) {
|
||||
List<Object> result = new ArrayList<>();
|
||||
for (Object value : flux.toIterable()) {
|
||||
result.add(value);
|
||||
}
|
||||
if (isSingleValue(input) && result.size()==1) {
|
||||
return result.get(0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean isSingleValue(Object input) {
|
||||
return !(input instanceof Collection);
|
||||
}
|
||||
|
||||
private Flux<?> extract(Object input) {
|
||||
if (input instanceof Collection) {
|
||||
return Flux.fromIterable((Iterable<?>) input);
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2016-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.function.adapter.aws;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
|
||||
import org.springframework.cloud.function.context.ContextFunctionCatalogAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class SpringBootRequestHandlerTests {
|
||||
|
||||
private SpringBootRequestHandler<Foo, Bar> handler;
|
||||
|
||||
@Test
|
||||
public void functionBean() throws Exception {
|
||||
handler = new SpringBootRequestHandler<Foo, Bar>(FunctionConfig.class);
|
||||
handler.initialize();
|
||||
Object output = handler.handleRequest(new Foo("foo"), null);
|
||||
assertThat(output).isInstanceOf(Bar.class);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import({ ContextFunctionCatalogAutoConfiguration.class,
|
||||
JacksonAutoConfiguration.class })
|
||||
protected static class FunctionConfig {
|
||||
@Bean
|
||||
public Function<Foo, Bar> function() {
|
||||
return foo -> new Bar(foo.getValue().toUpperCase());
|
||||
}
|
||||
}
|
||||
|
||||
protected static class Foo {
|
||||
private String value;
|
||||
|
||||
public Foo(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
protected static class Bar {
|
||||
private String value;
|
||||
|
||||
public Bar() {
|
||||
}
|
||||
|
||||
public Bar(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2016-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.function.adapter.aws;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
|
||||
import org.springframework.cloud.function.context.ContextFunctionCatalogAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class SpringBootStreamHandlerTests {
|
||||
|
||||
private SpringBootStreamHandler handler;
|
||||
|
||||
@Test
|
||||
public void functionBean() throws Exception {
|
||||
handler = new SpringBootStreamHandler(FunctionConfig.class);
|
||||
handler.initialize();
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
handler.handleRequest(new ByteArrayInputStream("{\"value\":\"foo\"}".getBytes()),
|
||||
output, null);
|
||||
assertThat(output.toString()).isEqualTo("{\"value\":\"FOO\"}");
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import({ ContextFunctionCatalogAutoConfiguration.class,
|
||||
JacksonAutoConfiguration.class })
|
||||
protected static class FunctionConfig {
|
||||
@Bean
|
||||
public Function<Foo, Bar> function() {
|
||||
return foo -> new Bar(foo.getValue().toUpperCase());
|
||||
}
|
||||
}
|
||||
|
||||
protected static class Foo {
|
||||
private String value;
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
protected static class Bar {
|
||||
private String value;
|
||||
|
||||
public Bar() {
|
||||
}
|
||||
|
||||
public Bar(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user