Skip to content

Commit

Permalink
GH-1106 Fix RoutingFunction fail when "spring.cloud.function.definiti…
Browse files Browse the repository at this point in the history
…on" header contains a List value instead of a String value (GCP-specific)

Resolves #1106
Resolves #1146
  • Loading branch information
akenra authored and olegz committed Jun 5, 2024
1 parent 812c39e commit faeb0f7
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@

package org.springframework.cloud.function.context.config;

import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
Expand Down Expand Up @@ -193,11 +195,31 @@ else if (StringUtils.hasText(functionProperties.getRoutingExpression())) {

private FunctionInvocationWrapper locateFunctionFromDefinitionOrExpression(Message<?> message) {
for (Entry<String, Object> headerEntry : message.getHeaders().entrySet()) {
if (headerEntry.getKey().equalsIgnoreCase(FunctionProperties.FUNCTION_DEFINITION)) {
return functionFromDefinition((String) headerEntry.getValue());
String headerKey = headerEntry.getKey();
Object headerValue = headerEntry.getValue();

if (headerKey == null || headerValue == null) {
continue;
}

boolean isFunctionDefinition = FunctionProperties.FUNCTION_DEFINITION.equalsIgnoreCase(headerKey);
boolean isRoutingExpression = FunctionProperties.ROUTING_EXPRESSION.equalsIgnoreCase(headerKey);

if (isFunctionDefinition) {
if (headerValue instanceof String definition) {
return functionFromDefinition(definition);
}
else if (headerValue instanceof List<?> definitions && !definitions.isEmpty()) {
return functionFromDefinition(definitions.stream().map(Object::toString).collect(Collectors.joining(",")));
}
}
else if (headerEntry.getKey().equalsIgnoreCase(FunctionProperties.ROUTING_EXPRESSION)) {
return this.functionFromExpression((String) headerEntry.getValue(), message, true);
else if (isRoutingExpression) {
if (headerValue instanceof String expression) {
return functionFromExpression(expression, message, true);
}
else if (headerValue instanceof List<?> expressions && !expressions.isEmpty()) {
return functionFromExpression(expressions.get(0).toString(), message, true);
}
}
}
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package org.springframework.cloud.function.context.config;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

Expand Down Expand Up @@ -98,7 +99,7 @@ public void testDefaultRouting() {

@SuppressWarnings({ "unchecked", "rawtypes" })
@Test
public void testInvocationWithMessageAndHeader() {
public void testInvocationWithMessageAndStringHeader() {
FunctionCatalog functionCatalog = this.configureCatalog();
Function function = functionCatalog.lookup(RoutingFunction.FUNCTION_NAME);
assertThat(function).isNotNull();
Expand All @@ -107,6 +108,57 @@ public void testInvocationWithMessageAndHeader() {
assertThat(function.apply(message)).isEqualTo("olleh");
}

@SuppressWarnings({ "unchecked", "rawtypes" })
@Test
public void testInvocationWithMessageAndListOfSingleElementHeader() {
FunctionCatalog functionCatalog = this.configureCatalog();
Function function = functionCatalog.lookup(RoutingFunction.FUNCTION_NAME);
assertThat(function).isNotNull();
Message<String> message = MessageBuilder.withPayload("hello")
.setHeader(FunctionProperties.PREFIX + ".definition", List.of("reverse"))
.build();
assertThat(function.apply(message)).isEqualTo("olleh");
}

@SuppressWarnings({ "unchecked", "rawtypes" })
@Test
public void testCompositionWithMessageAndListOfMultipleElementsHeader() {
FunctionCatalog functionCatalog = this.configureCatalog();
Function function = functionCatalog.lookup(RoutingFunction.FUNCTION_NAME);
assertThat(function).isNotNull();
Message<String> message = MessageBuilder.withPayload("hello")
.setHeader(FunctionProperties.PREFIX + ".definition",
List.of("reverse", "uppercase"))
.build();
assertThat(function.apply(message)).isEqualTo("OLLEH");
}

@SuppressWarnings({ "unchecked", "rawtypes" })
@Test
public void testInvocationWithMessageAndListOfSingleRoutingExpression() {
FunctionCatalog functionCatalog = this.configureCatalog();
Function function = functionCatalog.lookup(RoutingFunction.FUNCTION_NAME);
assertThat(function).isNotNull();
Message<String> message = MessageBuilder.withPayload("hello")
.setHeader(FunctionProperties.PREFIX + ".routing-expression",
List.of("'reverse'"))
.build();
assertThat(function.apply(message)).isEqualTo("olleh");
}

@SuppressWarnings({ "unchecked", "rawtypes" })
@Test
public void testInvocationWithMessageAndListOfMultipleRoutingExpressions() {
FunctionCatalog functionCatalog = this.configureCatalog();
Function function = functionCatalog.lookup(RoutingFunction.FUNCTION_NAME);
assertThat(function).isNotNull();
Message<String> message = MessageBuilder.withPayload("hello")
.setHeader(FunctionProperties.PREFIX + ".routing-expression",
List.of("'uppercase'", "'reverse'"))
.build();
assertThat(function.apply(message)).isEqualTo("HELLO");
}

@SuppressWarnings({ "unchecked", "rawtypes" })
@Test
public void testRoutingSimpleInputWithReactiveFunctionWithMessageHeader() {
Expand Down

0 comments on commit faeb0f7

Please sign in to comment.