Streamline and refactor the azure and the azure-web adapters.

- Add SCF/Azure Gradle sample and docs.
- Move the function-azure-di-samples into standalone projects.
 - Apply the name convetion and project structure for the SCF adaptes.
   E.g.  function-sample-azure-XXX projects under the spring-cloud-function-samples root.
 - Remove the redudant samples.
 - Improve the samples docs and the Adapter generic docs.
- Streamline docs.
- Add azure web adapter sample and README.
- Add Spring Azure Functions banner for azure and azure web adapters.
- azure-web adapter fixes:
  - Fix issues in serverles-web ProxyHttpServletResponse implementation.
  - Remove the custom FunctionClassUtils utils in favor of scf-context/util/FunctionClassUtils.
- Remove redundant files.
- Add FunctionInvoker deprecation annotations.
- Extend the time trigger sample with Retry policies example.
This commit is contained in:
Christian Tzolov
2023-07-14 18:46:42 +02:00
parent 64f57bcef8
commit 6299a5366b
88 changed files with 1834 additions and 1312 deletions

View File

@@ -22,10 +22,12 @@ import com.microsoft.azure.functions.spi.inject.FunctionInstanceInjector;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.ResourceBanner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType;
import org.springframework.cloud.function.utils.FunctionClassUtils;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
@@ -96,6 +98,8 @@ public class AzureFunctionInstanceInjector implements FunctionInstanceInjector {
SpringApplication application = new org.springframework.cloud.function.context.FunctionalSpringApplication(
configurationClass);
application.setWebApplicationType(WebApplicationType.NONE);
application.setBanner(new ResourceBanner(
new DefaultResourceLoader().getResource("classpath:/spring-azure-function-banner.txt")));
return application;
}
}

View File

@@ -65,8 +65,14 @@ import org.springframework.util.StringUtils;
* @param <O> result type
* @author Oleg Zhurakousky
* @author Chris Bono
* @author Christian Tzolov
*
* @since 3.2
*
* @deprecated since 4.0.0 in favor of the dependency injection implementation {@link AzureFunctionInstanceInjector}.
* Follow the official documentation for further information.
*/
@Deprecated
public class FunctionInvoker<I, O> {
private static Log logger = LogFactory.getLog(FunctionInvoker.class);
@@ -109,11 +115,13 @@ public class FunctionInvoker<I, O> {
private FunctionInvocationWrapper discoverFunction(String functionDefinition) {
FunctionInvocationWrapper function = FUNCTION_CATALOG.lookup(functionDefinition);
if (function != null && StringUtils.hasText(functionDefinition) && !function.getFunctionDefinition().equals(functionDefinition)) {
if (function != null && StringUtils.hasText(functionDefinition)
&& !function.getFunctionDefinition().equals(functionDefinition)) {
this.registerFunction(functionDefinition);
function = FUNCTION_CATALOG.lookup(functionDefinition);
}
else if (function == null && StringUtils.hasText(functionDefinition) && APPLICATION_CONTEXT.containsBean(functionDefinition)) {
else if (function == null && StringUtils.hasText(functionDefinition)
&& APPLICATION_CONTEXT.containsBean(functionDefinition)) {
this.registerFunction(functionDefinition);
function = FUNCTION_CATALOG.lookup(functionDefinition);
}
@@ -128,20 +136,22 @@ public class FunctionInvoker<I, O> {
Object functionResult = function.apply(enhancedInput);
if (functionResult instanceof Publisher) {
return postProcessReactiveFunctionResult(input, enhancedInput, (Publisher<?>) functionResult, function, executionContext);
return postProcessReactiveFunctionResult(input, enhancedInput, (Publisher<?>) functionResult, function,
executionContext);
}
return postProcessImperativeFunctionResult(input, enhancedInput, functionResult, function, executionContext);
}
/**
* Post-processes the result from a non-reactive function invocation before returning it to the Azure
* runtime. The default behavior is to {@link #convertOutputIfNecessary possibly convert} the result.
* Post-processes the result from a non-reactive function invocation before returning it to the Azure runtime. The
* default behavior is to {@link #convertOutputIfNecessary possibly convert} the result.
*
* <p>Provides a hook for custom function invokers to extend/modify the function results handling.
* <p>
* Provides a hook for custom function invokers to extend/modify the function results handling.
*
* @param rawInputs the inputs passed in from the Azure runtime
* @param functionInputs the actual inputs used for the function invocation; may be
* {@link #enhanceInputIfNecessary different} from the {@literal rawInputs}
* @param functionInputs the actual inputs used for the function invocation; may be {@link #enhanceInputIfNecessary
* different} from the {@literal rawInputs}
* @param functionResult the result from the function invocation
* @param function the invoked function
* @param executionContext the Azure execution context
@@ -149,44 +159,47 @@ public class FunctionInvoker<I, O> {
*/
@SuppressWarnings("unchecked")
protected O postProcessImperativeFunctionResult(I rawInputs, Object functionInputs, Object functionResult,
FunctionInvocationWrapper function, ExecutionContext executionContext
) {
FunctionInvocationWrapper function, ExecutionContext executionContext) {
return (O) this.convertOutputIfNecessary(rawInputs, functionResult);
}
/**
* Post-processes the result from a reactive function invocation before returning it to the Azure
* runtime. The default behavior is to delegate to {@link #postProcessMonoFunctionResult} or
* Post-processes the result from a reactive function invocation before returning it to the Azure runtime. The
* default behavior is to delegate to {@link #postProcessMonoFunctionResult} or
* {@link #postProcessFluxFunctionResult} based on the result type.
*
* <p>Provides a hook for custom function invokers to extend/modify the function results handling.
* <p>
* Provides a hook for custom function invokers to extend/modify the function results handling.
*
* @param rawInputs the inputs passed in from the Azure runtime
* @param functionInputs the actual inputs used for the function invocation; may be
* {@link #enhanceInputIfNecessary different} from the {@literal rawInputs}
* @param functionInputs the actual inputs used for the function invocation; may be {@link #enhanceInputIfNecessary
* different} from the {@literal rawInputs}
* @param functionResult the result from the function invocation
* @param function the invoked function
* @param executionContext the Azure execution context
* @return the possibly modified function results
*/
protected O postProcessReactiveFunctionResult(I rawInputs, Object functionInputs, Publisher<?> functionResult,
FunctionInvocationWrapper function, ExecutionContext executionContext
) {
FunctionInvocationWrapper function, ExecutionContext executionContext) {
if (FunctionTypeUtils.isMono(function.getOutputType())) {
return postProcessMonoFunctionResult(rawInputs, functionInputs, Mono.from(functionResult), function, executionContext);
return postProcessMonoFunctionResult(rawInputs, functionInputs, Mono.from(functionResult), function,
executionContext);
}
return postProcessFluxFunctionResult(rawInputs, functionInputs, Flux.from(functionResult), function, executionContext);
return postProcessFluxFunctionResult(rawInputs, functionInputs, Flux.from(functionResult), function,
executionContext);
}
/**
* Post-processes the {@code Mono} result from a reactive function invocation before returning it to the Azure
* runtime. The default behavior is to {@link Mono#blockOptional()} and {@link #convertOutputIfNecessary possibly convert} the result.
* runtime. The default behavior is to {@link Mono#blockOptional()} and {@link #convertOutputIfNecessary possibly
* convert} the result.
*
* <p>Provides a hook for custom function invokers to extend/modify the function results handling.
* <p>
* Provides a hook for custom function invokers to extend/modify the function results handling.
*
* @param rawInputs the inputs passed in from the Azure runtime
* @param functionInputs the actual inputs used for the function invocation; may be
* {@link #enhanceInputIfNecessary different} from the {@literal rawInputs}
* @param functionInputs the actual inputs used for the function invocation; may be {@link #enhanceInputIfNecessary
* different} from the {@literal rawInputs}
* @param functionResult the Mono result from the function invocation
* @param function the invoked function
* @param executionContext the Azure execution context
@@ -194,20 +207,21 @@ public class FunctionInvoker<I, O> {
*/
@SuppressWarnings("unchecked")
protected O postProcessMonoFunctionResult(I rawInputs, Object functionInputs, Mono<?> functionResult,
FunctionInvocationWrapper function, ExecutionContext executionContext
) {
FunctionInvocationWrapper function, ExecutionContext executionContext) {
return (O) this.convertOutputIfNecessary(rawInputs, functionResult.blockOptional().get());
}
/**
* Post-processes the {@code Flux} result from a reactive function invocation before returning it to the Azure
* runtime. The default behavior is to {@link Flux#toIterable() block} and {@link #convertOutputIfNecessary possibly convert} the results.
* runtime. The default behavior is to {@link Flux#toIterable() block} and {@link #convertOutputIfNecessary possibly
* convert} the results.
*
* <p>Provides a hook for custom function invokers to extend/modify the function results handling.
* <p>
* Provides a hook for custom function invokers to extend/modify the function results handling.
*
* @param rawInputs the inputs passed in from the Azure runtime
* @param functionInputs the actual inputs used for the function invocation; may be
* {@link #enhanceInputIfNecessary different} from the {@literal rawInputs}
* @param functionInputs the actual inputs used for the function invocation; may be {@link #enhanceInputIfNecessary
* different} from the {@literal rawInputs}
* @param functionResult the Mono result from the function invocation
* @param function the invoked function
* @param executionContext the Azure execution context
@@ -215,16 +229,16 @@ public class FunctionInvoker<I, O> {
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
protected O postProcessFluxFunctionResult(I rawInputs, Object functionInputs, Flux<?> functionResult,
FunctionInvocationWrapper function, ExecutionContext executionContext
) {
FunctionInvocationWrapper function, ExecutionContext executionContext) {
List resultList = new ArrayList<>();
for (Object resultItem : functionResult.toIterable()) {
if (resultItem instanceof Collection) {
resultList.addAll((Collection) resultItem);
}
else {
if (!function.isSupplier() && Collection.class.isAssignableFrom(FunctionTypeUtils.getRawType(function.getInputType()))
&& !Collection.class.isAssignableFrom(FunctionTypeUtils.getRawType(function.getOutputType()))) {
if (!function.isSupplier()
&& Collection.class.isAssignableFrom(FunctionTypeUtils.getRawType(function.getInputType()))
&& !Collection.class.isAssignableFrom(FunctionTypeUtils.getRawType(function.getOutputType()))) {
return (O) this.convertOutputIfNecessary(rawInputs, resultItem);
}
else {
@@ -238,11 +252,10 @@ public class FunctionInvoker<I, O> {
@SuppressWarnings({ "unchecked", "rawtypes" })
private void registerFunction(String functionDefinition) {
if (APPLICATION_CONTEXT.containsBean(functionDefinition)) {
FunctionRegistration functionRegistration =
new FunctionRegistration(APPLICATION_CONTEXT.getBean(functionDefinition), functionDefinition);
FunctionRegistration functionRegistration = new FunctionRegistration(
APPLICATION_CONTEXT.getBean(functionDefinition), functionDefinition);
Type type = FunctionContextUtils.
findType(functionDefinition, APPLICATION_CONTEXT.getBeanFactory());
Type type = FunctionContextUtils.findType(functionDefinition, APPLICATION_CONTEXT.getBeanFactory());
functionRegistration = functionRegistration.type(type);
@@ -305,8 +318,9 @@ public class FunctionInvoker<I, O> {
MessageBuilder<?> messageBuilder = null;
if (input instanceof HttpRequestMessage) {
HttpRequestMessage<I> requestMessage = (HttpRequestMessage<I>) input;
Object payload = requestMessage.getHttpMethod() != null && requestMessage.getHttpMethod().equals(HttpMethod.GET)
? requestMessage.getQueryParameters()
Object payload = requestMessage.getHttpMethod() != null
&& requestMessage.getHttpMethod().equals(HttpMethod.GET)
? requestMessage.getQueryParameters()
: requestMessage.getBody();
if (payload == null) {
@@ -352,8 +366,10 @@ public class FunctionInvoker<I, O> {
if (CollectionUtils.isEmpty(mf)) {
OBJECT_MAPPER = new JacksonMapper(new ObjectMapper());
JsonMessageConverter jsonConverter = new JsonMessageConverter(OBJECT_MAPPER);
SmartCompositeMessageConverter messageConverter = new SmartCompositeMessageConverter(Collections.singletonList(jsonConverter));
FUNCTION_CATALOG = new SimpleFunctionRegistry(APPLICATION_CONTEXT.getBeanFactory().getConversionService(),
SmartCompositeMessageConverter messageConverter = new SmartCompositeMessageConverter(
Collections.singletonList(jsonConverter));
FUNCTION_CATALOG = new SimpleFunctionRegistry(
APPLICATION_CONTEXT.getBeanFactory().getConversionService(),
messageConverter, OBJECT_MAPPER);
}
else {

View File

@@ -29,7 +29,11 @@ import com.microsoft.azure.functions.HttpResponseMessage;
* @author Oleg Zhurakousky
*
* @since 3.2
*
* @deprecated since 4.0.0 in favor of the dependency injection implementation {@link AzureFunctionInstanceInjector}.
* Follow the official documentation for further information.
*/
@Deprecated
public class HttpFunctionInvoker<I> extends
FunctionInvoker<HttpRequestMessage<I>, HttpResponseMessage> {

View File

@@ -0,0 +1,8 @@
____ _ _ _____ _ _
/ ___| _ __ _ __(_)_ __ __ _ / \ _____ _ _ __ ___ | ___| _ _ __ ___| |_(_) ___ _ __ ___
\___ \| '_ \| '__| | '_ \ / _` | / _ \ |_ / | | | '__/ _ \ | |_ | | | | '_ \ / __| __| |/ _ \| '_ \/ __|
___) | |_) | | | | | | | (_| | / ___ \ / /| |_| | | | __/ | _|| |_| | | | | (__| |_| | (_) | | | \__ \
|____/| .__/|_| |_|_| |_|\__, | /_/ \_\/___|\__,_|_| \___| |_| \__,_|_| |_|\___|\__|_|\___/|_| |_|___/
|_| |___/
${application.title} ${application.version}
Powered by Spring Boot ${spring-boot.version} and Azure Functions