diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/java/org/springframework/cloud/function/adapter/azure/FunctionInvoker.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/java/org/springframework/cloud/function/adapter/azure/FunctionInvoker.java index ee1f1dd07..787f4a6dd 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/java/org/springframework/cloud/function/adapter/azure/FunctionInvoker.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/java/org/springframework/cloud/function/adapter/azure/FunctionInvoker.java @@ -106,9 +106,7 @@ public class FunctionInvoker { binding.setValue(result); } - @SuppressWarnings({ "unchecked", "rawtypes" }) - public O handleRequest(I input, ExecutionContext executionContext) { - String functionDefinition = executionContext.getFunctionName(); + private FunctionInvocationWrapper discoverFunction(String functionDefinition) { FunctionInvocationWrapper function = FUNCTION_CATALOG.lookup(functionDefinition); if (function != null && StringUtils.hasText(functionDefinition) && !function.getFunctionDefinition().equals(functionDefinition)) { this.registerFunction(functionDefinition); @@ -118,6 +116,13 @@ public class FunctionInvoker { this.registerFunction(functionDefinition); function = FUNCTION_CATALOG.lookup(functionDefinition); } + return function; + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + public O handleRequest(I input, ExecutionContext executionContext) { + String functionDefinition = executionContext.getFunctionName(); + FunctionInvocationWrapper function = this.discoverFunction(functionDefinition); Object enhancedInput = enhanceInputIfNecessary(input, executionContext); Object output = function.apply(enhancedInput); diff --git a/spring-cloud-function-samples/function-sample-azure/README.adoc b/spring-cloud-function-samples/function-sample-azure/README.adoc index 66cc7dbad..024fbd244 100644 --- a/spring-cloud-function-samples/function-sample-azure/README.adoc +++ b/spring-cloud-function-samples/function-sample-azure/README.adoc @@ -2,32 +2,19 @@ You can run this Azure function locally, similar to other Spring Cloud Function this time by using the Azure Maven plugin, as the Microsoft Azure functions execution context must be available. ---- -# Build and package +# Build and run package $ mvn clean package - -# Previously, for other examples -$ mvn spring-boot:run - -# For Azure Functions -$ mvn clean package azure-functions:run - -or - $ mvn azure-functions:run ---- -The `uppercase` function takes `Function uppercase()` and its expected input is JSON map, therefore we need to -provide the appropriate content-type (in this case `application/json`). The function iterates then over each element -and returns its `uppercase` mapped value. +The `uppercase` function is of the following signature `Function, String> uppercase()`. Its expected input is JSON, +therefore we need t0 provide the appropriate content-type (in this case `application/json`). -Test the function using cURL or HTTPie and notice that the URL is formed by concatenating `/api/` +Test the function using _curl_ and notice that the URL is formed by concatenating `/api/` ---- # testing with cURL $ curl -H "Content-Type: application/json" localhost:7071/api/uppercase -d '{"greeting": "hello", "name": "your name"}' -# testing with HTTPie -$ http POST localhost:7071/api/uppercase greeting=hello name='your name' - # result { "greeting": "HELLO", @@ -35,7 +22,27 @@ $ http POST localhost:7071/api/uppercase greeting=hello name='your name' } ---- -The same is for `echo` function, however it will take any input since all it does is just echos it back. +The HTTP headers of the incoming request will be copied into input Message's MessageHeaders, so they become accessible if need to. +It is done in implementation of `UppercaseHandler` which extends `FunctionInvoker`. + +NOTE: Implementation of FunctionInvoker, should contain the least amount of code. Everything should be delegated to the base FunctionInvoker. +These implementations of FunctionInvoker are really a type-safe way to define and configure function to be recognized as Azure Function. +Look at it as _configuration with the callback_ (e.g., `this.handleRequest(..)`). +---- +@FunctionName("uppercase") +public String execute(@HttpTrigger(name = "req", methods = {HttpMethod.GET, + HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, + ExecutionContext context) { + Message message = MessageBuilder.withPayload(request.getBody().get()).copyHeaders(request.getHeaders()).build(); + return handleRequest(message, context); +} +---- + + +The `echo` function does the same as the `uppercase` less the actual uppercasing. However, the important difference to notice is that function itself +takes primitive `String` as its input while the actual handler passes instance of `Message` the same way as with `uppercase`. The framework recognizes that +you only care about the payload and extracts it from the message before calling the function. + There is also a reactive version of 'uppercase' - `uppercaseReactive` which will produce the same result, but demonstrates and validates the ability to use reactive functions with Azure. @@ -73,10 +80,6 @@ curl https:///api/uppercase -d '{"greeting": "h # testing with cURL $ curl -H "Content-Type: application/json" https://function-sample-azure.azurewebsites.net/api/uppercase -d '{"greeting": "hello", "name": "your name"}' - -# testing with HTTPie -$ http POST https://function-sample-azure.azurewebsites.net/api/uppercase greeting=hello name='your name' - # result { "greeting": "HELLO", diff --git a/spring-cloud-function-samples/function-sample-azure/src/main/java/example/Config.java b/spring-cloud-function-samples/function-sample-azure/src/main/java/example/Config.java index beafa72af..8bfd1c78c 100644 --- a/spring-cloud-function-samples/function-sample-azure/src/main/java/example/Config.java +++ b/spring-cloud-function-samples/function-sample-azure/src/main/java/example/Config.java @@ -37,8 +37,8 @@ public class Config { } @Bean - public Function, String> echo() { - return message -> message.getPayload(); + public Function echo() { + return payload -> payload; } @Bean diff --git a/spring-cloud-function-samples/function-sample-azure/src/main/java/example/EchoHandler.java b/spring-cloud-function-samples/function-sample-azure/src/main/java/example/EchoHandler.java index 44c8e3336..784f0e86c 100644 --- a/spring-cloud-function-samples/function-sample-azure/src/main/java/example/EchoHandler.java +++ b/spring-cloud-function-samples/function-sample-azure/src/main/java/example/EchoHandler.java @@ -26,17 +26,21 @@ import com.microsoft.azure.functions.annotation.HttpTrigger; import java.util.Optional; import org.springframework.cloud.function.adapter.azure.FunctionInvoker; +import org.springframework.messaging.Message; +import org.springframework.messaging.support.MessageBuilder; /** * @author Soby Chacko + * @author Oleg Zhurakousky */ -public class EchoHandler extends FunctionInvoker { +public class EchoHandler extends FunctionInvoker, String> { @FunctionName("echo") public String execute(@HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, ExecutionContext context) { - return handleRequest(request.getBody().get(), context); + Message message = MessageBuilder.withPayload(request.getBody().get()).copyHeaders(request.getHeaders()).build(); + return handleRequest(message, context); } } diff --git a/spring-cloud-function-samples/function-sample-azure/src/main/java/example/UppercaseHandler.java b/spring-cloud-function-samples/function-sample-azure/src/main/java/example/UppercaseHandler.java index cc7f6d1b4..983c4b350 100644 --- a/spring-cloud-function-samples/function-sample-azure/src/main/java/example/UppercaseHandler.java +++ b/spring-cloud-function-samples/function-sample-azure/src/main/java/example/UppercaseHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2018-2021 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. @@ -26,17 +26,21 @@ import com.microsoft.azure.functions.annotation.HttpTrigger; import java.util.Optional; import org.springframework.cloud.function.adapter.azure.FunctionInvoker; +import org.springframework.messaging.Message; +import org.springframework.messaging.support.MessageBuilder; /** * @author Soby Chacko + * @author Oleg Zhurakousky */ -public class UppercaseHandler extends FunctionInvoker { +public class UppercaseHandler extends FunctionInvoker, String> { @FunctionName("uppercase") public String execute(@HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, ExecutionContext context) { - return handleRequest(request.getBody().get(), context); + Message message = MessageBuilder.withPayload(request.getBody().get()).copyHeaders(request.getHeaders()).build(); + return handleRequest(message, context); } }