binding,
- ExecutionContext context) {
- O result = handleRequest(input, context);
- binding.setValue(result);
- }
-
- private FunctionInvocationWrapper discoverFunction(String functionDefinition) {
- FunctionInvocationWrapper function = FUNCTION_CATALOG.lookup(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)) {
- this.registerFunction(functionDefinition);
- function = FUNCTION_CATALOG.lookup(functionDefinition);
- }
- return function;
- }
-
- public O handleRequest(I input, ExecutionContext executionContext) {
- String functionDefinition = executionContext.getFunctionName();
- FunctionInvocationWrapper function = this.discoverFunction(functionDefinition);
- Object enhancedInput = enhanceInputIfNecessary(input, executionContext);
-
- Object functionResult = function.apply(enhancedInput);
-
- if (functionResult instanceof Publisher) {
- 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.
- *
- *
- * 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 functionResult the result from the function invocation
- * @param function the invoked function
- * @param executionContext the Azure execution context
- * @return the possibly modified function results
- */
- @SuppressWarnings("unchecked")
- protected O postProcessImperativeFunctionResult(I rawInputs, Object functionInputs, Object functionResult,
- 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
- * {@link #postProcessFluxFunctionResult} based on the result type.
- *
- *
- * 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 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) {
- if (FunctionTypeUtils.isMono(function.getOutputType())) {
- return postProcessMonoFunctionResult(rawInputs, functionInputs, Mono.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.
- *
- *
- * 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 functionResult the Mono result from the function invocation
- * @param function the invoked function
- * @param executionContext the Azure execution context
- * @return the possibly modified function results
- */
- @SuppressWarnings("unchecked")
- protected O postProcessMonoFunctionResult(I rawInputs, Object functionInputs, Mono> functionResult,
- 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.
- *
- *
- * 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 functionResult the Mono result from the function invocation
- * @param function the invoked function
- * @param executionContext the Azure execution context
- * @return the possibly modified function results
- */
- @SuppressWarnings({ "rawtypes", "unchecked" })
- protected O postProcessFluxFunctionResult(I rawInputs, Object functionInputs, Flux> functionResult,
- 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()))) {
- return (O) this.convertOutputIfNecessary(rawInputs, resultItem);
- }
- else {
- resultList.add(resultItem);
- }
- }
- }
- return (O) this.convertOutputIfNecessary(rawInputs, resultList);
- }
-
- @SuppressWarnings({ "unchecked", "rawtypes" })
- private void registerFunction(String functionDefinition) {
- if (APPLICATION_CONTEXT.containsBean(functionDefinition)) {
- FunctionRegistration functionRegistration = new FunctionRegistration(
- APPLICATION_CONTEXT.getBean(functionDefinition), functionDefinition);
-
- Type type = FunctionContextUtils.findType(functionDefinition, APPLICATION_CONTEXT.getBeanFactory());
-
- functionRegistration = functionRegistration.type(type);
-
- ((FunctionRegistry) FUNCTION_CATALOG).register(functionRegistration);
- }
- }
-
- @SuppressWarnings({ "unchecked", "rawtypes" })
- private Object enhanceInputIfNecessary(Object input, ExecutionContext executionContext) {
- if (input == null) { // Supplier
- return input;
- }
- if (input instanceof Publisher) {
- return Flux.from((Publisher) input).map(item -> {
- if (item instanceof Message) {
- return MessageBuilder.fromMessage((Message) item)
- .setHeaderIfAbsent(EXECUTION_CONTEXT, executionContext).build();
- }
- else {
- return constructInputMessageFromItem(input, executionContext);
- }
- });
- }
- else if (input instanceof Message) {
- return MessageBuilder.fromMessage((Message) input)
- .setHeaderIfAbsent(EXECUTION_CONTEXT, executionContext).build();
- }
- else if (input instanceof Iterable) {
- return Flux.fromIterable((Iterable) input).map(item -> {
- return constructInputMessageFromItem(item, executionContext);
- });
- }
- return constructInputMessageFromItem(input, executionContext);
- }
-
- @SuppressWarnings("unchecked")
- private Object convertOutputIfNecessary(Object input, Object output) {
- if (input instanceof HttpRequestMessage) {
- HttpRequestMessage requestMessage = (HttpRequestMessage) input;
- Map headers = null;
- if (output instanceof Message) {
- headers = ((Message) output).getHeaders();
- output = ((Message) output).getPayload();
- }
- Builder responseBuilder = requestMessage.createResponseBuilder(HttpStatus.OK).body(output);
- if (headers != null) {
- for (Entry headersEntry : headers.entrySet()) {
- if (headersEntry.getValue() != null) {
- responseBuilder.header(headersEntry.getKey(), headersEntry.getValue().toString());
- }
- }
- }
- return responseBuilder.build();
- }
- return output;
- }
-
- @SuppressWarnings("unchecked")
- private Message> constructInputMessageFromItem(Object input, ExecutionContext executionContext) {
- MessageBuilder> messageBuilder = null;
- if (input instanceof HttpRequestMessage) {
- HttpRequestMessage requestMessage = (HttpRequestMessage) input;
- Object payload = requestMessage.getHttpMethod() != null
- && requestMessage.getHttpMethod().equals(HttpMethod.GET)
- ? requestMessage.getQueryParameters()
- : requestMessage.getBody();
-
- if (payload == null) {
- payload = Optional.empty();
- }
- messageBuilder = MessageBuilder.withPayload(payload).copyHeaders(this.getHeaders(requestMessage));
- }
- else {
- messageBuilder = MessageBuilder.withPayload(input);
- }
- return messageBuilder.setHeaderIfAbsent(EXECUTION_CONTEXT, executionContext).build();
- }
-
- private MessageHeaders getHeaders(HttpRequestMessage 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.getUri().getPath());
- }
-
- if (event.getHttpMethod() != null) {
- headers.put("httpMethod", event.getHttpMethod().toString());
- }
-
- headers.put("request", event.getBody());
- return new MessageHeaders(headers);
- }
-
- /**
- * Double-Checked Locking Optimization was used to avoid unnecessary locking overhead.
- */
- private static void initialize(Class> configurationClass) {
- if (FUNCTION_CATALOG == null) {
- try {
- globalLock.lock();
- if (FUNCTION_CATALOG == null) {
- logger.info("Initializing: " + configurationClass);
- SpringApplication builder = springApplication(configurationClass);
- APPLICATION_CONTEXT = builder.run();
-
- Map mf = APPLICATION_CONTEXT.getBeansOfType(FunctionCatalog.class);
- 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(),
- messageConverter, OBJECT_MAPPER);
- }
- else {
- OBJECT_MAPPER = APPLICATION_CONTEXT.getBean(JsonMapper.class);
- FUNCTION_CATALOG = mf.values().iterator().next();
- }
- }
- }
- finally {
- globalLock.unlock();
- }
- }
- }
-
- private static SpringApplication springApplication(Class> configurationClass) {
- SpringApplication application = new org.springframework.cloud.function.context.FunctionalSpringApplication(
- configurationClass);
- application.setWebApplicationType(WebApplicationType.NONE);
- return application;
- }
-}
diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/java/org/springframework/cloud/function/adapter/azure/HttpFunctionInvoker.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/java/org/springframework/cloud/function/adapter/azure/HttpFunctionInvoker.java
deleted file mode 100644
index c2af61fb3..000000000
--- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/main/java/org/springframework/cloud/function/adapter/azure/HttpFunctionInvoker.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2021-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.
- * 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.azure;
-
-import com.microsoft.azure.functions.HttpRequestMessage;
-import com.microsoft.azure.functions.HttpResponseMessage;
-
-
-/**
- * Implementation of HTTP Request Handler for Azure which supports
- * HttpRequestMessage and HttpResponseMessage the types required by
- * Azure Functions for HTTP-triggered functions.
- *
- * @param input type
- * @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 extends
- FunctionInvoker, HttpResponseMessage> {
-
- public HttpFunctionInvoker(Class> configurationClass) {
- super(configurationClass);
- }
-
- public HttpFunctionInvoker() {
- super();
- }
-}
diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/CustomFunctionInvokerTests.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/CustomFunctionInvokerTests.java
deleted file mode 100644
index ce639cf95..000000000
--- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/CustomFunctionInvokerTests.java
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * Copyright 2022-2022 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.azure;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.Locale;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-
-import com.microsoft.azure.functions.ExecutionContext;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.Test;
-import org.mockito.ArgumentCaptor;
-import org.reactivestreams.Publisher;
-import reactor.core.publisher.Flux;
-import reactor.core.publisher.Mono;
-
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.cloud.function.adapter.azure.helper.TestExecutionContext;
-import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.messaging.support.GenericMessage;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.util.Lists.list;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyList;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.same;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-
-/**
- * Unit tests for {@link FunctionInvoker} custom result handling.
- *
- * @author Chris Bono
- */
-class CustomFunctionInvokerTests {
-
- private FunctionInvoker, ?> currentInvoker;
-
- @AfterEach
- void closeCurrentInvoker() {
- if (this.currentInvoker != null) {
- this.currentInvoker.close();
- }
- }
-
- /**
- * Verifies custom result handling and proper post-process callback invocation for an imperative function.
- */
- @Test
- void customImperativeResultHandling() {
- FunctionInvoker invoker = new FunctionInvoker(TestFunctionsConfig.class) {
- @Override
- protected String postProcessImperativeFunctionResult(String rawInputs, Object functionInputs,
- Object functionResult, FunctionInvocationWrapper function, ExecutionContext executionContext
- ) {
- return functionResult + "+imperative";
- }
- };
- invoker = spyOnAndCloseAfterTest(invoker);
- ExecutionContext executionContext = new TestExecutionContext("imperativeUppercase");
- String result = invoker.handleRequest("foo", executionContext);
- assertThat(result).isEqualTo("FOO+imperative");
-
- // Below here verifies that the expected callback(s) were invoked w/ the expected arguments
-
- // Only imperative post-process callback should be called
- verify(invoker, never()).postProcessReactiveFunctionResult(anyString(), any(), any(Publisher.class), any(), same(executionContext));
- verify(invoker, never()).postProcessMonoFunctionResult(anyString(), any(), any(Mono.class), any(), same(executionContext));
- verify(invoker, never()).postProcessFluxFunctionResult(anyString(), any(), any(Flux.class), any(), same(executionContext));
-
- // Only sniff-test the payload of the input message (the other fields are problematic to verify and no value doing that here)
- ArgumentCaptor functionInputsCaptor = ArgumentCaptor.forClass(GenericMessage.class);
- verify(invoker).postProcessImperativeFunctionResult(eq("foo"), functionInputsCaptor.capture(), eq("FOO"), any(), same(executionContext));
- assertThat(functionInputsCaptor.getValue()).extracting(GenericMessage::getPayload).isEqualTo("foo");
- }
-
- /**
- * Verifies custom result handling and proper post-process callback invocation for a reactive Mono function.
- */
- @Test
- void customReactiveMonoResultHandling() {
- FunctionInvoker invoker = new FunctionInvoker(TestFunctionsConfig.class) {
- @Override
- protected String postProcessMonoFunctionResult(String rawInputs, Object functionInputs, Mono> functionResult,
- FunctionInvocationWrapper function, ExecutionContext executionContext
- ) {
- return functionResult.block().toString() + "+mono";
- }
- };
- invoker = spyOnAndCloseAfterTest(invoker);
- ExecutionContext executionContext = new TestExecutionContext("reactiveMonoUppercase");
- String result = invoker.handleRequest("foo", executionContext);
- assertThat(result).isEqualTo("FOO+mono");
-
- // Below here verifies that the expected callback(s) were invoked w/ the expected arguments
-
- // Only publisher->mono post-process callbacks should be called
- verify(invoker, never()).postProcessImperativeFunctionResult(anyString(), any(), any(), any(), same(executionContext));
- verify(invoker, never()).postProcessFluxFunctionResult(anyString(), any(), any(Flux.class), any(), same(executionContext));
-
- // Only sniff-test the payload of the input message and the mono (the other fields are problematic to verify and no value doing that here)
- ArgumentCaptor functionInputsCaptor = ArgumentCaptor.forClass(GenericMessage.class);
- ArgumentCaptor functionResultCaptor = ArgumentCaptor.forClass(Mono.class);
- verify(invoker).postProcessReactiveFunctionResult(eq("foo"), functionInputsCaptor.capture(), functionResultCaptor.capture(), any(), same(executionContext));
- verify(invoker).postProcessMonoFunctionResult(eq("foo"), functionInputsCaptor.capture(), functionResultCaptor.capture(), any(), same(executionContext));
- // NOTE: The captors get called twice as the args are just delegated from publisher->mono callback
- assertThat(functionInputsCaptor.getAllValues()).extracting(GenericMessage::getPayload).containsExactly("foo", "foo");
- assertThat(functionResultCaptor.getAllValues()).extracting(Mono::block).containsExactly("FOO", "FOO");
- }
-
- /**
- * Verifies custom result handling and proper post-process callback invocation for a reactive Flux function.
- */
- @Test
- void customReactiveFluxResultHandling() {
- FunctionInvoker, String> invoker = new FunctionInvoker, String>(TestFunctionsConfig.class) {
- @Override
- protected String postProcessFluxFunctionResult(List rawInputs, Object functionInputs,
- Flux> functionResult, FunctionInvocationWrapper function, ExecutionContext executionContext
- ) {
- return functionResult.map(o -> o.toString() + "+flux").collectList().block().stream().collect(Collectors.joining("/"));
- }
- };
- invoker = spyOnAndCloseAfterTest(invoker);
- ExecutionContext executionContext = new TestExecutionContext("reactiveFluxUppercase");
- List rawInputs = Arrays.asList("foo", "bar");
- String result = invoker.handleRequest(rawInputs, executionContext);
- assertThat(result).isEqualTo("FOO+flux/BAR+flux");
-
- // Below here verifies that the expected callback(s) were invoked w/ the expected arguments
-
- // Only publisher->flux post-process callbacks should be called
- verify(invoker, never()).postProcessImperativeFunctionResult(anyList(), any(), any(), any(), same(executionContext));
- verify(invoker, never()).postProcessMonoFunctionResult(anyList(), any(), any(Mono.class), any(), same(executionContext));
-
- // Only sniff-test the payload of the input message and the mono (the other fields are problematic to verify and no value doing that here)
- ArgumentCaptor> functionInputsCaptor = ArgumentCaptor.forClass(Flux.class);
- ArgumentCaptor functionResultCaptor = ArgumentCaptor.forClass(Flux.class);
- verify(invoker).postProcessReactiveFunctionResult(same(rawInputs), functionInputsCaptor.capture(), functionResultCaptor.capture(), any(), same(executionContext));
- verify(invoker).postProcessFluxFunctionResult(same(rawInputs), functionInputsCaptor.capture(), functionResultCaptor.capture(), any(), same(executionContext));
-
- // NOTE: The captors get called twice as the args are just delegated from publisher->flux callback
-
- // The functionInputs for each call is Flux with 2 items - one for 'foo' and one for 'bar'
- assertThat(functionInputsCaptor.getAllValues())
- .extracting(Flux::collectList).extracting(Mono::block)
- .flatExtracting(fluxAsList -> fluxAsList.stream().collect(Collectors.toList()))
- .extracting(GenericMessage::getPayload).containsExactlyInAnyOrder("foo", "bar", "foo", "bar");
-
- // The functionResult for each call is a Flux w/ 2 items { "FOO", "BAR" }
- assertThat(functionResultCaptor.getAllValues())
- .extracting(Flux::collectList).extracting(Mono::block)
- .containsExactlyInAnyOrder(list("FOO", "BAR"), list("FOO", "BAR"));
- }
-
- private FunctionInvoker spyOnAndCloseAfterTest(FunctionInvoker invoker) {
- this.currentInvoker = invoker;
- return spy(invoker);
- }
-
- @Configuration
- @EnableAutoConfiguration
- static class TestFunctionsConfig {
-
- @Bean
- public Function imperativeUppercase() {
- return (s) -> s.toUpperCase(Locale.ROOT);
- }
-
- @Bean
- public Function, Mono> reactiveMonoUppercase() {
- return (m) -> m.map(String::toUpperCase);
- }
-
- @Bean
- public Function, Flux> reactiveFluxUppercase() {
- return (f) -> f.map(String::toUpperCase);
- }
-
- }
-}
diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/FunctionInvokerTests.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/FunctionInvokerTests.java
deleted file mode 100644
index 5ed105fb4..000000000
--- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/FunctionInvokerTests.java
+++ /dev/null
@@ -1,370 +0,0 @@
-/*
- * 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.azure;
-
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Locale;
-import java.util.function.Consumer;
-import java.util.function.Function;
-import java.util.function.Supplier;
-import java.util.stream.Collectors;
-
-import com.microsoft.azure.functions.ExecutionContext;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.Test;
-import reactor.core.publisher.Flux;
-import reactor.core.publisher.Mono;
-
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.cloud.function.adapter.azure.helper.TestExecutionContext;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.messaging.Message;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-
-/**
- * @author Dave Syer
- * @author Oleg Zhurakousky
- */
-public class FunctionInvokerTests {
-
- private FunctionInvoker, ?> handler = null;
-
- FunctionInvoker handler(Class> config) {
- FunctionInvoker handler = new FunctionInvoker(
- config);
- this.handler = handler;
- return handler;
- }
-
-// @Test // this is wrong too since function is Flux, Flux and while input may be single value, the output can still be multiple
- public void bareConfig() {
- FunctionInvoker handler = handler(BareConfig.class);
- Bar bar = handler.handleRequest(new Foo("bar"),
- new TestExecutionContext("uppercase"));
- assertThat(bar.getValue()).isEqualTo("BAR");
- }
-
- @Test
- public void autoConfig() {
- FunctionInvoker handler = handler(AutoConfig.class);
- Bar bar = handler.handleRequest(new Foo("bar"),
- new TestExecutionContext("uppercase"));
- assertThat(bar.getValue()).isEqualTo("BAR");
- }
-
- @Test
- public void multiConfig() {
- FunctionInvoker handler = handler(MultiConfig.class);
- Bar bar = handler.handleRequest(new Foo("bar"),
- new TestExecutionContext("uppercase"));
- assertThat(bar.getValue()).isEqualTo("BAR");
- }
-
- @Test
- public void implicitListConfig() {
- FunctionInvoker, List> handler = handler(
- AutoConfig.class);
- List bar = handler.handleRequest(Arrays.asList(new Foo("bar"), new Foo("baz")),
- new TestExecutionContext("uppercase"));
- assertThat(bar).hasSize(2);
- assertThat(bar.get(0).getValue()).isEqualTo("BAR");
- assertThat(bar.get(1).getValue()).isEqualTo("BAZ");
- }
-
- @Test
- public void listToListConfig() {
- FunctionInvoker, List> handler = handler(
- ListConfig.class);
- List bar = handler.handleRequest(
- Arrays.asList(new Foo("bar"), new Foo("baz")),
- new TestExecutionContext("uppercase"));
- assertThat(bar).hasSize(2);
- assertThat(bar.get(0).getValue()).isEqualTo("BAR");
- }
-
- @Test
- public void listToListSingleConfig() {
- FunctionInvoker, List> handler = handler(
- ListConfig.class);
- List bar = handler.handleRequest(Arrays.asList(new Foo("bar")),
- new TestExecutionContext("uppercase"));
- assertThat(bar).hasSize(1);
- assertThat(bar.get(0).getValue()).isEqualTo("BAR");
- }
-
- @Test
- public void collectConfig() {
- FunctionInvoker, Bar> handler = handler(
- CollectConfig.class);
- Bar bar = handler.handleRequest(Arrays.asList(new Foo("bar")),
- new TestExecutionContext("uppercase"));
- assertThat(bar.getValue()).isEqualTo("BAR");
- }
-
- @Test
- public void functionNonFluxBean() {
- FunctionInvoker handler = handler(NonFluxFunctionConfig.class);
- Bar bar = handler.handleRequest(new Foo("bar"), new TestExecutionContext("function"));
- assertThat(bar).isNotNull();
- }
-
- @Test
- public void supplierNonFluxBean() {
- FunctionInvoker> handler = handler(NonFluxSupplierConfig.class);
- List result = handler.handleRequest(new TestExecutionContext("supplier"));
-
- assertThat(result).isNotEmpty();
- assertThat(result.toString()).isEqualTo("[foo1, foo2]");
- }
-
- @Test
- public void supplierPublisherBean() {
- FunctionInvoker handler = handler(ReactiveSupplierConfig.class);
- Foo resultSingle = (Foo) handler.handleRequest(new TestExecutionContext("suppliermono"));
- assertThat(resultSingle.getValue()).isEqualTo("hello");
-
- List resultList = (List) handler.handleRequest(new TestExecutionContext("supplierflux"));
- assertThat(resultList.size()).isEqualTo(2);
- }
-
- private static String consumerResult;
-
- @Test
- public void consumerNonFluxBean() {
- FunctionInvoker handler = handler(NonFluxConsumerConfig.class);
- Object result = handler.handleRequest("foo1", new TestExecutionContext("consumer"));
-
- assertThat(result).isNull();
- assertThat(consumerResult).isEqualTo("foo1");
- }
-
- @Test
- public void testReactiveFunctions() {
- FunctionInvoker handler = handler(ReactiveFunctionConfiguration.class);
- String result = handler.handleRequest("hello", new TestExecutionContext("uppercaseMono"));
-
- System.out.println(result);
-
-// assertThat(result).isNull();
-// assertThat(consumerResult).isEqualTo("foo1");
- }
-
- @AfterEach
- public void close() throws IOException {
- if (this.handler != null) {
- this.handler.close();
- }
- }
-
- @Configuration
- protected static class NonFluxFunctionConfig {
-
- @Bean
- public Function function() {
- return foo -> new Bar();
- }
-
- }
-
- @Configuration
- @EnableAutoConfiguration
- protected static class ReactiveFunctionConfiguration {
-
- @Bean
- public Function, Flux> echoStream() {
- return f -> f;
- }
-
- @Bean
- public Function, Mono> uppercaseMono() {
- return f -> f.map(v -> v.toUpperCase(Locale.ROOT));
- }
-
- }
-
- @Configuration
- protected static class NonFluxSupplierConfig {
-
- @Bean
- public Supplier> supplier() {
- return () -> Arrays.asList("foo1", "foo2");
- }
-
- }
-
- @Configuration
- protected static class NonFluxConsumerConfig {
-
- @Bean
- public Consumer consumer() {
- return (v) -> consumerResult = v;
- }
-
- }
-
- @Configuration
-// @EnableAutoConfiguration
- protected static class ReactiveSupplierConfig {
-
- @Bean
- public Supplier> suppliermono() {
- return () -> Mono.just(new Foo("hello"));
- }
-
- @Bean
- public Supplier> supplierflux() {
- return () -> Flux.just(new Foo("hello"), new Foo("bye"));
- }
-
- }
-
- @Configuration
- protected static class BareConfig {
-
- @Bean("uppercase")
- public Function, Flux> function() {
- return foos -> foos.map(foo -> new Bar(foo.getValue().toUpperCase(Locale.ROOT)));
- }
-
- }
-
- @Configuration
- @EnableAutoConfiguration
- protected static class AutoConfig {
-
- @Bean
- public Function, Bar> uppercase() {
- return message -> {
- Foo foo = message.getPayload();
- ExecutionContext targetContext = (ExecutionContext) message.getHeaders().get("executionContext");
- targetContext.getLogger().info("Invoking 'uppercase' on " + foo.getValue());
- return new Bar(foo.getValue().toUpperCase(Locale.ROOT));
- };
- }
-
- }
-
- @Configuration
- @EnableAutoConfiguration
- protected static class ListConfig {
-
- @Bean
- public Function, List> uppercase() {
- return foos -> {
- List bars = foos.stream().map(foo -> new Bar(foo.getValue().toUpperCase(Locale.ROOT)))
- .collect(Collectors.toList());
- return bars;
- };
- }
-
- }
-
- @Configuration
- @EnableAutoConfiguration
- protected static class CollectConfig {
-
- @Bean
- public Function, Bar> uppercase() {
- return foos -> new Bar(foos.stream().map(foo -> foo.getValue().toUpperCase(Locale.ROOT))
- .collect(Collectors.joining(",")));
- }
-
- }
-
- @Configuration
- @EnableAutoConfiguration
- protected static class MultiConfig {
-
- @Bean
- public Function, Bar> uppercase() {
-
- return message -> {
- ExecutionContext context = (ExecutionContext) message.getHeaders().get("executionContext");
- Foo foo = message.getPayload();
- context.getLogger().info("Executing uppercase function");
- return new Bar(foo.getValue().toUpperCase(Locale.ROOT));
- };
- }
-
- @Bean
- public Function, Foo> lowercase() {
- return message -> {
- ExecutionContext context = (ExecutionContext) message.getHeaders().get("executionContext");
- Bar bar = message.getPayload();
- context.getLogger().info("Executing lowercase function");
- return new Foo(bar.getValue().toLowerCase(Locale.ROOT));
- };
- }
-
- }
-
-}
-
-class Foo {
-
- private String value;
-
- Foo() {
- }
-
- Foo(String value) {
- this.value = value;
- }
-
- public String lowercase() {
- return this.value.toLowerCase(Locale.ROOT);
- }
-
- public String uppercase() {
- return this.value.toUpperCase(Locale.ROOT);
- }
-
- public String getValue() {
- return this.value;
- }
-
- public void setValue(String value) {
- this.value = value;
- }
-
-}
-
-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;
- }
-
-}
diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/HttpFunctionInvokerTests.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/HttpFunctionInvokerTests.java
deleted file mode 100644
index 82a3dfed0..000000000
--- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/HttpFunctionInvokerTests.java
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * 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.azure;
-
-import java.io.IOException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-import java.util.function.Consumer;
-import java.util.function.Function;
-
-import com.microsoft.azure.functions.HttpMethod;
-import com.microsoft.azure.functions.HttpResponseMessage;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.Test;
-
-import org.springframework.cloud.function.adapter.azure.helper.HttpRequestMessageStub;
-import org.springframework.cloud.function.adapter.azure.helper.TestExecutionContext;
-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;
-
-/**
- * @author Markus Gulden
- */
-public class HttpFunctionInvokerTests {
-
- private HttpFunctionInvoker> handler = null;
-
- HttpFunctionInvoker handler(Class> config) {
- HttpFunctionInvoker handler = new HttpFunctionInvoker(
- config);
- this.handler = handler;
- return handler;
- }
-
- @Test
- public void testWithBody() {
- HttpFunctionInvoker handler = handler(
- FunctionMessageBodyConfig.class);
- HttpRequestMessageStub request = new HttpRequestMessageStub();
- request.setBody(new Foo("foo"));
-
- HttpResponseMessage response = handler.handleRequest(request,
- new TestExecutionContext("uppercase"));
-
- assertThat(response.getBody()).isInstanceOf(Bar.class);
- assertThat(response.getStatusCode()).isEqualTo(200);
- Bar body = (Bar) response.getBody();
- assertThat(body.getValue()).isEqualTo("FOO");
- }
-
- @Test
- public void testWithRequestParameters() throws URISyntaxException {
- HttpFunctionInvoker handler = handler(
- FunctionMessageEchoReqParametersConfig.class);
- HttpRequestMessageStub request = new HttpRequestMessageStub();
- request.setUri(new URI("http://localhost:8080/pathValue"));
- request.setHeaders(Collections.singletonMap("test-header", "headerValue"));
- request.setQueryParameters(Collections.singletonMap("query", "queryValue"));
- request.setHttpMethod(HttpMethod.GET);
-
- HttpResponseMessage response = handler.handleRequest(request,
- new TestExecutionContext("uppercase"));
-
- assertThat(response.getStatusCode()).isEqualTo(200);
- assertThat(response.getHeader("path")).isEqualTo("/pathValue");
- assertThat(response.getHeader("query")).isEqualTo("queryValue");
- assertThat(response.getHeader("test-header")).isEqualTo("headerValue");
- Bar body = (Bar) response.getBody();
- assertThat(body.getValue()).isEqualTo("body");
- }
-
- @Test
- public void testWithEmptyBody() {
- HttpFunctionInvoker handler = handler(
- FunctionMessageConsumerConfig.class);
- HttpRequestMessageStub request = new HttpRequestMessageStub();
-
- HttpResponseMessage response = handler.handleRequest(request,
- new TestExecutionContext("uppercase"));
-
- assertThat(response.getStatusCode()).isEqualTo(200);
- Bar body = (Bar) response.getBody();
- assertThat(body).isNull();
- }
-
- @AfterEach
- public void close() throws IOException {
- if (this.handler != null) {
- this.handler.close();
- }
- }
-
- @Configuration
- @Import({ ContextFunctionCatalogAutoConfiguration.class })
- protected static class FunctionMessageBodyConfig {
-
- @Bean
- public Function, Message> function() {
- return (foo -> {
- Map headers = new HashMap<>();
- return new GenericMessage<>(
- new Bar(foo.getPayload().getValue().toUpperCase(Locale.ROOT)), headers);
- });
- }
-
- }
-
- @Configuration
- @Import({ ContextFunctionCatalogAutoConfiguration.class })
- protected static class FunctionMessageEchoReqParametersConfig {
-
- @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 Consumer> function() {
- return (foo -> {
- });
- }
-
- }
-}
diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/helper/BuilderStub.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/helper/BuilderStub.java
index 94cd39f6d..ee22219d5 100644
--- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/helper/BuilderStub.java
+++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/helper/BuilderStub.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2023-2023 the original author or authors.
+ * Copyright 2023-present 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.
diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/helper/HttpRequestMessageStub.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/helper/HttpRequestMessageStub.java
index b7db2b7d1..5a95d23c4 100644
--- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/helper/HttpRequestMessageStub.java
+++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/helper/HttpRequestMessageStub.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2023-2023 the original author or authors.
+ * Copyright 2023-present 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.
diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/helper/HttpResponseMessageStub.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/helper/HttpResponseMessageStub.java
index ffd939071..3948a5c4a 100644
--- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/helper/HttpResponseMessageStub.java
+++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/helper/HttpResponseMessageStub.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2023-2023 the original author or authors.
+ * Copyright 2023-present 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.
diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/helper/TestExecutionContext.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/helper/TestExecutionContext.java
index e7cb7934a..21fd440d8 100644
--- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/helper/TestExecutionContext.java
+++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/helper/TestExecutionContext.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2019 the original author or authors.
+ * Copyright 2012-present 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.
diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/injector/AzureFunctionInstanceInjectorTest.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/injector/AzureFunctionInstanceInjectorTest.java
index c4147e742..97051ead9 100644
--- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/injector/AzureFunctionInstanceInjectorTest.java
+++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/injector/AzureFunctionInstanceInjectorTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2022-2022 the original author or authors.
+ * Copyright 2022-present 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.
diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/injector/FunctionInstanceInjectorServiceLoadingTest.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/injector/FunctionInstanceInjectorServiceLoadingTest.java
index b21eb3900..f9a6efaae 100644
--- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/injector/FunctionInstanceInjectorServiceLoadingTest.java
+++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/injector/FunctionInstanceInjectorServiceLoadingTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2022-2022 the original author or authors.
+ * Copyright 2022-present 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.
diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/pom.xml b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/pom.xml
index b95fc3f17..959b458e8 100644
--- a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/pom.xml
+++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/pom.xml
@@ -11,7 +11,7 @@
spring-cloud-function-adapter-parent
org.springframework.cloud
- 4.3.1-SNAPSHOT
+ 5.0.2-SNAPSHOT
@@ -40,14 +40,12 @@
org.springframework.boot
spring-boot-loader-tools
-
-
- org.springframework.boot
- spring-boot-loader-classic
+ ${spring-boot.version}
org.springframework.boot
spring-boot-loader
+ ${spring-boot.version}
@@ -61,13 +59,28 @@
spring-web
test