making changes to adapter, including tests and documentation

This commit is contained in:
Deepankar Dixit
2022-05-25 10:22:29 -04:00
parent 11666058ad
commit eecb373d50
3 changed files with 90 additions and 9 deletions

View File

@@ -136,6 +136,28 @@ Invoke the HTTP function:
curl https://REGION-PROJECT_ID.cloudfunctions.net/function-sample-gcp-http -d "hello" curl https://REGION-PROJECT_ID.cloudfunctions.net/function-sample-gcp-http -d "hello"
---- ----
Setting custom HTTP statusCode:
----
Users can specify the response statusCode that they want to set by wrapping "statusCode" in headers.
----
[source, java]
----
@Bean
public Function<String, Message<String>> function() {
String payload = "hello";
Message<String> message = MessageBuilder.withPayload(payload).setHeader("statusCode", 404).build();
return input -> message;
};
----
==== Background Functions ==== Background Functions
Google Cloud Functions also supports deploying https://cloud.google.com/functions/docs/writing/background[Background Functions] which are invoked indirectly in response to an event, such as a message on a https://cloud.google.com/pubsub[Cloud Pub/Sub] topic, a change in a https://cloud.google.com/storage[Cloud Storage] bucket, or a https://firebase.google.com/[Firebase] event. Google Cloud Functions also supports deploying https://cloud.google.com/functions/docs/writing/background[Background Functions] which are invoked indirectly in response to an event, such as a message on a https://cloud.google.com/pubsub[Cloud Pub/Sub] topic, a change in a https://cloud.google.com/storage[Cloud Storage] bucket, or a https://firebase.google.com/[Firebase] event.

View File

@@ -95,6 +95,8 @@ public class FunctionInvoker extends AbstractSpringFunctionAdapterInitializer<Ht
@Override @Override
public void service(HttpRequest httpRequest, HttpResponse httpResponse) throws Exception { public void service(HttpRequest httpRequest, HttpResponse httpResponse) throws Exception {
String httpStatusCode = "statusCode";
Function<Message<BufferedReader>, Message<byte[]>> function = lookupFunction(); Function<Message<BufferedReader>, Message<byte[]>> function = lookupFunction();
Message<BufferedReader> message = getInputType() == Void.class || getInputType() == null ? null Message<BufferedReader> message = getInputType() == Void.class || getInputType() == null ? null
@@ -123,8 +125,8 @@ public class FunctionInvoker extends AbstractSpringFunctionAdapterInitializer<Ht
} }
httpRequest.getContentType().ifPresent(contentType -> httpResponse.setContentType(contentType)); httpRequest.getContentType().ifPresent(contentType -> httpResponse.setContentType(contentType));
if (headers.containsKey("statusCode")) { if (headers.containsKey(httpStatusCode) && (headers.get(httpStatusCode) instanceof Integer)) {
httpResponse.setStatusCode((int) headers.get("statusCode")); httpResponse.setStatusCode((int) headers.get(httpStatusCode));
} }
} }
} }

View File

@@ -20,6 +20,8 @@ import java.io.BufferedReader;
import java.io.BufferedWriter; import java.io.BufferedWriter;
import java.io.StringReader; import java.io.StringReader;
import java.io.StringWriter; import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
@@ -37,7 +39,9 @@ import org.springframework.context.annotation.Import;
import org.springframework.messaging.Message; import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder; import org.springframework.messaging.support.MessageBuilder;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
/** /**
@@ -52,27 +56,38 @@ public class FunctionInvokerHttpTests {
@Test @Test
public void testHelloWorldSupplier() throws Exception { public void testHelloWorldSupplier() throws Exception {
testHttpFunction(HelloWorldSupplier.class, null, "Hello World!"); testHttpFunction(HelloWorldSupplier.class, null, "Hello World!", false, false);
} }
@Test @Test
public void testJsonInputFunction() throws Exception { public void testJsonInputFunction() throws Exception {
testHttpFunction(JsonInputFunction.class, new IncomingRequest("hello"), testHttpFunction(JsonInputFunction.class, new IncomingRequest("hello"),
"Thank you for sending the message: hello"); "Thank you for sending the message: hello", false, false);
} }
@Test @Test
public void testJsonInputOutputFunction() throws Exception { public void testJsonInputOutputFunction() throws Exception {
testHttpFunction(JsonInputOutputFunction.class, new IncomingRequest("hello"), testHttpFunction(JsonInputOutputFunction.class, new IncomingRequest("hello"),
new OutgoingResponse("Thank you for sending the message: hello")); new OutgoingResponse("Thank you for sending the message: hello"), false, false);
} }
@Test @Test
public void testJsonInputConsumer_Background() throws Exception { public void testJsonInputConsumer_Background() throws Exception {
testHttpFunction(JsonInputConsumer.class, new IncomingRequest("hello"), null); testHttpFunction(JsonInputConsumer.class, new IncomingRequest("hello"), null, false, false);
} }
private <I, O> void testHttpFunction(Class<?> configurationClass, I input, O expectedOutput) throws Exception { @Test
public void testStatusCodeSet() throws Exception {
testHttpFunction(statusCodeSupplier.class, "hello", "hello", true, false);
}
@Test
public void testMultiValueHeaderSupplied() throws Exception {
testHttpFunction(multiValueHeaderSupplier.class, "hello", "hello", false, true);
}
private <I, O> void testHttpFunction(Class<?> configurationClass, I input, O expectedOutput, boolean statusCodeCheck, boolean multiHeaderCheck) throws Exception {
try (FunctionInvoker handler = new FunctionInvoker(configurationClass);) { try (FunctionInvoker handler = new FunctionInvoker(configurationClass);) {
HttpRequest request = Mockito.mock(HttpRequest.class); HttpRequest request = Mockito.mock(HttpRequest.class);
@@ -88,6 +103,14 @@ public class FunctionInvokerHttpTests {
when(response.getWriter()).thenReturn(bufferedWriter); when(response.getWriter()).thenReturn(bufferedWriter);
handler.service(request, response); handler.service(request, response);
if (statusCodeCheck) {
verify(response).setStatusCode(404);
}
if (multiHeaderCheck) {
verify(response).appendHeader("multiValueHeader", "123,headerThing");
}
// Closing the writer is done by the Framework/caller. // Closing the writer is done by the Framework/caller.
bufferedWriter.close(); bufferedWriter.close();
@@ -108,6 +131,41 @@ public class FunctionInvokerHttpTests {
} }
@Configuration
@Import({ ContextFunctionCatalogAutoConfiguration.class })
protected static class statusCodeSupplier {
@Bean
public Function<String, Message<String>> function() {
String payload = "hello";
Message<String> msg = MessageBuilder.withPayload(payload).setHeader("statusCode", 404)
.build();
return x -> msg;
};
}
@Configuration
@Import({ ContextFunctionCatalogAutoConfiguration.class })
protected static class multiValueHeaderSupplier {
@Bean
public Function<String, Message<String>> function() {
String payload = "hello";
List<Object> li = new ArrayList<Object>(asList(123, "headerThing"));
Message<String> msg = MessageBuilder.withPayload(payload).setHeader("multiValueHeader", li)
.build();
return x -> msg;
};
}
@Configuration @Configuration
@Import({ ContextFunctionCatalogAutoConfiguration.class }) @Import({ ContextFunctionCatalogAutoConfiguration.class })
protected static class JsonInputFunction { protected static class JsonInputFunction {
@@ -128,10 +186,9 @@ public class FunctionInvokerHttpTests {
return (in) -> { return (in) -> {
return MessageBuilder return MessageBuilder
.withPayload(new OutgoingResponse("Thank you for sending the message: " + in.message)) .withPayload(new OutgoingResponse("Thank you for sending the message: " + in.message))
.setHeader("foo", "bar").build(); .setHeader("statusCode", "bar").build();
}; };
} }
} }
@Configuration @Configuration