diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcloud/GcfHandler.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcloud/GcfHandler.java new file mode 100644 index 000000000..5843a5f3b --- /dev/null +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcloud/GcfHandler.java @@ -0,0 +1,108 @@ +/* + * Copyright 2012-2019 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 + * + * https://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.gcloud; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import com.google.cloud.functions.Context; +import com.google.cloud.functions.HttpResponse; +import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; + +import org.springframework.cloud.function.context.AbstractSpringFunctionAdapterInitializer; +import org.springframework.messaging.Message; + +public class GcfHandler extends AbstractSpringFunctionAdapterInitializer { + + public GcfHandler(Class configurationClass) { + super(configurationClass); + init(); + } + + public GcfHandler() { + super(); + init(); + } + + public void init() { + Thread.currentThread().setContextClassLoader(GcfSpringBootHttpRequestHandler.class.getClassLoader()); + initialize(null); + } + + Object toOptionalIfEmpty(String requestBody) { + return requestBody.isEmpty() ? Optional.empty() : requestBody; + } + + protected boolean functionAcceptsMessage() { + return this.getInspector().isMessage(function()); + } + + @SuppressWarnings("unchecked") + protected T result(Object input, Publisher output, HttpResponse resp) { + List result = new ArrayList<>(); + for (Object value : Flux.from(output).toIterable()) { + result.add((T) convertOutputAndHeaders(value, resp)); + } + if (isSingleValue(input) && result.size() == 1) { + return result.get(0); + } + return (T) result; + } + + private boolean isSingleValue(Object input) { + return !(input instanceof Collection); + } + + Flux extract(Object input) { + if (input instanceof Collection) { + return Flux.fromIterable((Iterable) input); + } + return Flux.just(input); + } + + protected O convertOutputAndHeaders(Object output, HttpResponse resp) { + if (output instanceof Message) { + Message message = (Message) output; + for (Map.Entry entry : message.getHeaders().entrySet()) { + Object values = entry.getValue(); + if (values instanceof List) { + for (Object value : (List) values) { + if (value != null) { + resp.appendHeader(entry.getKey(), value.toString()); + } + } + } + else if (values != null) { + resp.appendHeader(entry.getKey(), values.toString()); + } + } + return (O) message.getPayload(); + } + else { + return (O) output; + } + } + + boolean returnsOutput() { + return !this.getInspector().getOutputType(function()).equals(Void.class); + } + +} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcloud/GcfSpringBootHttpRequestHandlerOriginal.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcloud/GcfSpringBootHttpRequestHandlerOriginal.java new file mode 100644 index 000000000..9d070b080 --- /dev/null +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcloud/GcfSpringBootHttpRequestHandlerOriginal.java @@ -0,0 +1,103 @@ +/* + * Copyright 2012-2019 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 + * + * https://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.gcloud; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.cloud.functions.HttpFunction; +import com.google.cloud.functions.HttpRequest; +import com.google.cloud.functions.HttpResponse; +import org.reactivestreams.Publisher; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.messaging.MessageHeaders; +import org.springframework.messaging.support.GenericMessage; + +/** + * Implementation of HttpFunction for Google Cloud Function. + * + * @param input type + * @author Dmitry Solomakha + */ +public class GcfSpringBootHttpRequestHandlerOriginal extends GcfHandler implements HttpFunction { + + public GcfSpringBootHttpRequestHandlerOriginal(Class configurationClass) { + super(configurationClass); + } + + public GcfSpringBootHttpRequestHandlerOriginal() { + super(); + } + + @Autowired + private ObjectMapper mapper; + + @Override + public void service(HttpRequest httpRequest, HttpResponse httpResponse) throws Exception { + Publisher output = apply(extract(convert(httpRequest))); + BufferedWriter writer = httpResponse.getWriter(); + Object result = result(httpRequest, output, httpResponse); + if (returnsOutput()) { + writer.write(mapper.writeValueAsString(result)); + writer.flush(); + } + httpResponse.setStatusCode(200); + } + + private Object convert(HttpRequest event) throws IOException { + BufferedReader br = event.getReader(); + StringBuilder sb = new StringBuilder(); + + char[] buffer = new char[1024 * 4]; + int n; + while (-1 != (n = br.read(buffer))) { + sb.append(buffer, 0, n); + } + + String requestBody = sb.toString(); + if (functionAcceptsMessage()) { + return new GenericMessage<>(toOptionalIfEmpty(requestBody), getHeaders(event)); + } + return toOptionalIfEmpty(requestBody); + } + + private MessageHeaders getHeaders(HttpRequest event) { + Map headers = new HashMap(); + + if (event.getHeaders() != null) { + headers.putAll(event.getHeaders()); + } + if (event.getQueryParameters() != null) { + headers.putAll(event.getQueryParameters()); + } + if (event.getUri() != null) { + headers.put("path", event.getPath()); + } + + if (event.getMethod() != null) { + headers.put("httpMethod", event.getMethod()); + } + + return new MessageHeaders(headers); + } + +} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcloud/GcfSpringBootPubSubFunctionHandler.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcloud/GcfSpringBootPubSubFunctionHandler.java new file mode 100644 index 000000000..bc4847160 --- /dev/null +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcloud/GcfSpringBootPubSubFunctionHandler.java @@ -0,0 +1,74 @@ +/* + * Copyright 2012-2019 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 + * + * https://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.gcloud; + +import java.util.Base64; +import java.util.HashMap; +import java.util.Map; + +import com.google.cloud.functions.BackgroundFunction; +import com.google.cloud.functions.Context; +import reactor.core.publisher.Flux; + +import org.springframework.messaging.MessageHeaders; +import org.springframework.messaging.support.GenericMessage; + +import static java.nio.charset.StandardCharsets.UTF_8; + +public class GcfSpringBootPubSubFunctionHandler extends GcfHandler + implements BackgroundFunction { + + public GcfSpringBootPubSubFunctionHandler(Class configurationClass) { + super(configurationClass); + } + + public GcfSpringBootPubSubFunctionHandler() { + super(); + } + + @Override + public void accept(PubSubMessage pubSubMessage, Context context) { + Flux.from(apply(extract(toMessageIfNeeded(pubSubMessage)))).blockLast(); + } + + private Object toMessageIfNeeded(PubSubMessage pubSubMessage) { + String data = new String(Base64.getDecoder().decode(pubSubMessage.getData()), UTF_8); + if (functionAcceptsMessage()) { + return new GenericMessage<>(toOptionalIfEmpty(data), getHeaders(pubSubMessage)); + } + return toOptionalIfEmpty(data); + } + + private Map getHeaders(PubSubMessage pubSubMessage) { + Map headers = new HashMap(); + + if (pubSubMessage.getAttributes() != null) { + headers.putAll(pubSubMessage.getAttributes()); + } + + if (pubSubMessage.getMessageId() != null) { + headers.put("messageId", pubSubMessage.getMessageId()); + } + + if (pubSubMessage.getPublishTime() != null) { + headers.put("publishTime", pubSubMessage.getPublishTime()); + } + + return new MessageHeaders(headers); + } + +} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcloud/PubSubMessage.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcloud/PubSubMessage.java new file mode 100644 index 000000000..ffbfdedef --- /dev/null +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcloud/PubSubMessage.java @@ -0,0 +1,73 @@ +/* + * Copyright 2012-2019 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 + * + * https://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.gcloud; + +import java.util.Map; + +public class PubSubMessage { + + String data; + + Map attributes; + + String messageId; + + String publishTime; + + public PubSubMessage() { + } + + PubSubMessage(String data, Map attributes, String messageId, String publishTime) { + this.data = data; + this.attributes = attributes; + this.messageId = messageId; + this.publishTime = publishTime; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public Map getAttributes() { + return attributes; + } + + public void setAttributes(Map attributes) { + this.attributes = attributes; + } + + public String getMessageId() { + return messageId; + } + + public void setMessageId(String messageId) { + this.messageId = messageId; + } + + public String getPublishTime() { + return publishTime; + } + + public void setPublishTime(String publishTime) { + this.publishTime = publishTime; + } + +} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/test/java/org/springframework/cloud/function/adapter/gcloud/GcfSpringBootHttpRequestHandlerOriginalTests.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/test/java/org/springframework/cloud/function/adapter/gcloud/GcfSpringBootHttpRequestHandlerOriginalTests.java new file mode 100644 index 000000000..29fea1466 --- /dev/null +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/test/java/org/springframework/cloud/function/adapter/gcloud/GcfSpringBootHttpRequestHandlerOriginalTests.java @@ -0,0 +1,313 @@ +/* + * Copyright 2019-2019 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 + * + * https://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.gcloud; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.io.StringReader; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Consumer; +import java.util.function.Function; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.cloud.functions.HttpRequest; +import com.google.cloud.functions.HttpResponse; +import com.google.gson.Gson; +import org.junit.After; +import org.junit.Test; +import org.mockito.Mockito; + +import org.springframework.cloud.function.context.config.ContextFunctionCatalogAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.messaging.Message; +import org.springframework.messaging.support.GenericMessage; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +/** + * @author Dmitry Solomakha + */ +public class GcfSpringBootHttpRequestHandlerOriginalTests { + + HttpRequest request = Mockito.mock(HttpRequest.class); + + private GcfSpringBootHttpRequestHandlerOriginal handler = null; + + public static final Gson GSON = new Gson(); + + GcfSpringBootHttpRequestHandlerOriginal handler(Class config) { + GcfSpringBootHttpRequestHandlerOriginal handler = new GcfSpringBootHttpRequestHandlerOriginal<>(config); + + this.handler = handler; + return handler; + } + + @Test + public void testWithBody() throws Exception { + GcfSpringBootHttpRequestHandlerOriginal handler = handler(FunctionMessageBodyConfig.class); + + StringReader foo = new StringReader(GSON.toJson(new Foo("foo"))); + when(request.getReader()).thenReturn(new BufferedReader(foo)); + + StringWriter writer = new StringWriter(); + handler.service(request, new HttpResponseImpl(new BufferedWriter(writer))); + + assertThat(GSON.fromJson(writer.toString(), Bar.class)).isEqualTo(new Bar("FOO")); + } + + @Test + public void testWithRequestParameters() throws Exception { + GcfSpringBootHttpRequestHandlerOriginal handler = handler(FunctionMessageEchoReqParametersConfig.class); + + when(request.getReader()).thenReturn(new BufferedReader(new StringReader(""))); + when(request.getUri()).thenReturn("http://localhost:8080/pathValue"); + when(request.getPath()).thenReturn("/pathValue"); + when(request.getHeaders()) + .thenReturn(Collections.singletonMap("test-header", Collections.singletonList("headerValue"))); + when(request.getMethod()).thenReturn("GET"); + + StringWriter writer = new StringWriter(); + HttpResponseImpl response = new HttpResponseImpl(new BufferedWriter(writer)); + handler.service(request, response); + + assertThat(response.statusCode).isEqualTo(200); + assertThat(response.headers.get("path")).containsExactly("/pathValue"); + assertThat(response.headers.get("test-header")).containsExactly("headerValue"); + assertThat(GSON.fromJson(writer.toString(), Bar.class)).isEqualTo(new Bar("body")); + } + + @Test + public void testWithEmptyBody() throws Exception { + GcfSpringBootHttpRequestHandlerOriginal handler = handler(FunctionMessageConsumerConfig.class); + + when(request.getReader()).thenReturn(new BufferedReader(new StringReader(""))); + + StringWriter writer = new StringWriter(); + HttpResponseImpl response = new HttpResponseImpl(new BufferedWriter(writer)); + handler.service(request, response); + + assertThat(response.statusCode).isEqualTo(200); + assertThat(writer.toString()).isEqualTo(""); + } + + @After + public void close() { + if (this.handler != null) { + this.handler.close(); + } + } + + @Configuration + @Import({ ContextFunctionCatalogAutoConfiguration.class }) + protected static class FunctionMessageBodyConfig { + + @Bean + public ObjectMapper objectMapper() { + return new ObjectMapper(); + } + + @Bean + public Function, Message> function() { + return (foo -> { + Map headers = new HashMap<>(); + return new GenericMessage<>(new Bar(foo.getPayload().getValue().toUpperCase()), headers); + }); + } + + } + + @Configuration + @Import({ ContextFunctionCatalogAutoConfiguration.class }) + protected static class FunctionMessageEchoReqParametersConfig { + + @Bean + public ObjectMapper objectMapper() { + return new ObjectMapper(); + } + + @Bean + public Function, Message> function() { + return (message -> { + Map 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 }) + protected static class FunctionMessageConsumerConfig { + + @Bean + public ObjectMapper objectMapper() { + return new ObjectMapper(); + } + + @Bean + public Consumer> function() { + return (foo -> { + }); + } + + } + + public static class Foo { + + private String value; + + Foo() { + } + + Foo(String value) { + this.value = value; + } + + public String getValue() { + return this.value; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Foo foo = (Foo) o; + return Objects.equals(value, foo.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } + + } + + private static class Bar { + + private String value; + + Bar() { + } + + Bar(String value) { + this.value = value; + } + + public String getValue() { + return this.value; + } + + public void setValue(String value) { + this.value = value; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Bar bar = (Bar) o; + return Objects.equals(getValue(), bar.getValue()); + } + + @Override + public int hashCode() { + return Objects.hash(getValue()); + } + + } + + private static class HttpResponseImpl implements HttpResponse { + + int statusCode; + + String contentType; + + BufferedWriter writer; + + HttpResponseImpl(BufferedWriter writer) { + this.writer = writer; + } + + Map> headers = new HashMap<>(); + + @Override + public void setStatusCode(int code) { + statusCode = code; + } + + @Override + public void setStatusCode(int code, String message) { + statusCode = code; + } + + @Override + public void setContentType(String contentType) { + this.contentType = contentType; + } + + @Override + public Optional getContentType() { + return Optional.ofNullable(contentType); + } + + @Override + public void appendHeader(String header, String value) { + headers.computeIfAbsent(header, x -> new ArrayList<>()).add(value); + } + + @Override + public Map> getHeaders() { + return headers; + } + + @Override + public OutputStream getOutputStream() throws IOException { + throw new RuntimeException("unsupported!"); + } + + @Override + public BufferedWriter getWriter() throws IOException { + return writer; + } + + } + +} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/test/java/org/springframework/cloud/function/adapter/gcloud/GcfSpringBootPubSubFunctionTests.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/test/java/org/springframework/cloud/function/adapter/gcloud/GcfSpringBootPubSubFunctionTests.java new file mode 100644 index 000000000..21720e0f5 --- /dev/null +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/test/java/org/springframework/cloud/function/adapter/gcloud/GcfSpringBootPubSubFunctionTests.java @@ -0,0 +1,121 @@ +/* + * Copyright 2019-2019 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 + * + * https://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.gcloud; + +import java.util.Base64; +import java.util.HashMap; +import java.util.function.Consumer; + +import com.google.gson.Gson; +import org.junit.After; +import org.junit.Test; + +import org.springframework.cloud.function.context.config.ContextFunctionCatalogAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.messaging.Message; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.cloud.function.adapter.gcloud.GcfSpringBootHttpRequestHandlerOriginalTests.Foo; + +/** + * @author Dmitry Solomakha + */ +public class GcfSpringBootPubSubFunctionTests { + + private GcfSpringBootPubSubFunctionHandler handler = null; + + public static final Gson GSON = new Gson(); + + GcfSpringBootPubSubFunctionHandler handler(Class config) { + GcfSpringBootPubSubFunctionHandler handler = new GcfSpringBootPubSubFunctionHandler<>(config); + + this.handler = handler; + return handler; + } + + @Test + public void testWithBody() { + GcfSpringBootPubSubFunctionHandler handler = handler(FunctionConfig.class); + + Foo foo = new Foo("foo"); + PubSubMessage psMessage = new PubSubMessage(toBase64EncodedJson(foo), null, null, null); + handler.accept(psMessage, null); + + assertThat(FunctionConfig.argument).isEqualTo(foo); + } + + @Test + public void testWithBodyMessage() { + GcfSpringBootPubSubFunctionHandler handler = handler(FunctionMessageConfig.class); + + Foo foo = new Foo("foo"); + HashMap attributes = new HashMap<>(); + attributes.put("attribute", "value"); + + PubSubMessage psMessage = new PubSubMessage(toBase64EncodedJson(foo), attributes, "id", "2020-02-01"); + handler.accept(psMessage, null); + + assertThat(FunctionMessageConfig.argument.getHeaders().get("messageId")).isEqualTo("id"); + assertThat(FunctionMessageConfig.argument.getHeaders().get("publishTime")).isEqualTo("2020-02-01"); + assertThat(FunctionMessageConfig.argument.getHeaders().get("attribute")).isEqualTo("value"); + assertThat(FunctionMessageConfig.argument.getPayload()).isEqualTo(foo); + } + + private String toBase64EncodedJson(Foo foo) { + return Base64.getEncoder().encodeToString(GSON.toJson(foo).getBytes()); + } + + @After + public void close() { + if (this.handler != null) { + this.handler.close(); + } + } + + @Configuration + @Import({ ContextFunctionCatalogAutoConfiguration.class }) + protected static class FunctionConfig { + + static Foo argument; + + @Bean + public Consumer function() { + return (foo -> { + argument = foo; + }); + } + + } + + @Configuration + @Import({ ContextFunctionCatalogAutoConfiguration.class }) + protected static class FunctionMessageConfig { + + static Message argument; + + @Bean + public Consumer> function() { + return (message -> { + argument = message; + }); + } + + } + +} diff --git a/spring-cloud-function-samples/function-sample-gcp/pom.xml b/spring-cloud-function-samples/function-sample-gcp/pom.xml index 5debf3b91..8bda6765b 100644 --- a/spring-cloud-function-samples/function-sample-gcp/pom.xml +++ b/spring-cloud-function-samples/function-sample-gcp/pom.xml @@ -28,6 +28,8 @@ 0.9.1 org.springframework.cloud.function.adapter.gcloud.GcfSpringBootHttpRequestHandler + + 8080 diff --git a/spring-cloud-function-samples/function-sample-gcp/src/main/java/com/example/CloudFunctionMain.java b/spring-cloud-function-samples/function-sample-gcp/src/main/java/com/example/CloudFunctionMain.java index dc5f213ab..41a692964 100644 --- a/spring-cloud-function-samples/function-sample-gcp/src/main/java/com/example/CloudFunctionMain.java +++ b/spring-cloud-function-samples/function-sample-gcp/src/main/java/com/example/CloudFunctionMain.java @@ -18,6 +18,9 @@ import java.util.function.Function; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; @@ -25,12 +28,18 @@ @SpringBootApplication public class CloudFunctionMain { + private final Logger logger = LoggerFactory.getLogger(CloudFunctionMain.class); + public static void main(String[] args) { SpringApplication.run(CloudFunctionMain.class, args); } @Bean public Function function() { - return value -> value.toUpperCase(); + return value -> { + logger.info("function was called with this argument: " + value); + return value.toUpperCase(); + }; } + }