Skip to content

Commit faeb0f7

Browse files
akenraolegz
authored andcommitted
GH-1106 Fix RoutingFunction fail when "spring.cloud.function.definition" header contains a List value instead of a String value (GCP-specific)
Resolves #1106 Resolves #1146
1 parent 812c39e commit faeb0f7

File tree

2 files changed

+79
-5
lines changed

2 files changed

+79
-5
lines changed

spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/RoutingFunction.java

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@
1616

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

19+
import java.util.List;
1920
import java.util.Map;
2021
import java.util.Map.Entry;
2122
import java.util.function.Function;
23+
import java.util.stream.Collectors;
2224

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

194196
private FunctionInvocationWrapper locateFunctionFromDefinitionOrExpression(Message<?> message) {
195197
for (Entry<String, Object> headerEntry : message.getHeaders().entrySet()) {
196-
if (headerEntry.getKey().equalsIgnoreCase(FunctionProperties.FUNCTION_DEFINITION)) {
197-
return functionFromDefinition((String) headerEntry.getValue());
198+
String headerKey = headerEntry.getKey();
199+
Object headerValue = headerEntry.getValue();
200+
201+
if (headerKey == null || headerValue == null) {
202+
continue;
203+
}
204+
205+
boolean isFunctionDefinition = FunctionProperties.FUNCTION_DEFINITION.equalsIgnoreCase(headerKey);
206+
boolean isRoutingExpression = FunctionProperties.ROUTING_EXPRESSION.equalsIgnoreCase(headerKey);
207+
208+
if (isFunctionDefinition) {
209+
if (headerValue instanceof String definition) {
210+
return functionFromDefinition(definition);
211+
}
212+
else if (headerValue instanceof List<?> definitions && !definitions.isEmpty()) {
213+
return functionFromDefinition(definitions.stream().map(Object::toString).collect(Collectors.joining(",")));
214+
}
198215
}
199-
else if (headerEntry.getKey().equalsIgnoreCase(FunctionProperties.ROUTING_EXPRESSION)) {
200-
return this.functionFromExpression((String) headerEntry.getValue(), message, true);
216+
else if (isRoutingExpression) {
217+
if (headerValue instanceof String expression) {
218+
return functionFromExpression(expression, message, true);
219+
}
220+
else if (headerValue instanceof List<?> expressions && !expressions.isEmpty()) {
221+
return functionFromExpression(expressions.get(0).toString(), message, true);
222+
}
201223
}
202224
}
203225
return null;

spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/RoutingFunctionTests.java

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.springframework.cloud.function.context.config;
1818

1919
import java.util.HashMap;
20+
import java.util.List;
2021
import java.util.Map;
2122
import java.util.function.Function;
2223

@@ -98,7 +99,7 @@ public void testDefaultRouting() {
9899

99100
@SuppressWarnings({ "unchecked", "rawtypes" })
100101
@Test
101-
public void testInvocationWithMessageAndHeader() {
102+
public void testInvocationWithMessageAndStringHeader() {
102103
FunctionCatalog functionCatalog = this.configureCatalog();
103104
Function function = functionCatalog.lookup(RoutingFunction.FUNCTION_NAME);
104105
assertThat(function).isNotNull();
@@ -107,6 +108,57 @@ public void testInvocationWithMessageAndHeader() {
107108
assertThat(function.apply(message)).isEqualTo("olleh");
108109
}
109110

111+
@SuppressWarnings({ "unchecked", "rawtypes" })
112+
@Test
113+
public void testInvocationWithMessageAndListOfSingleElementHeader() {
114+
FunctionCatalog functionCatalog = this.configureCatalog();
115+
Function function = functionCatalog.lookup(RoutingFunction.FUNCTION_NAME);
116+
assertThat(function).isNotNull();
117+
Message<String> message = MessageBuilder.withPayload("hello")
118+
.setHeader(FunctionProperties.PREFIX + ".definition", List.of("reverse"))
119+
.build();
120+
assertThat(function.apply(message)).isEqualTo("olleh");
121+
}
122+
123+
@SuppressWarnings({ "unchecked", "rawtypes" })
124+
@Test
125+
public void testCompositionWithMessageAndListOfMultipleElementsHeader() {
126+
FunctionCatalog functionCatalog = this.configureCatalog();
127+
Function function = functionCatalog.lookup(RoutingFunction.FUNCTION_NAME);
128+
assertThat(function).isNotNull();
129+
Message<String> message = MessageBuilder.withPayload("hello")
130+
.setHeader(FunctionProperties.PREFIX + ".definition",
131+
List.of("reverse", "uppercase"))
132+
.build();
133+
assertThat(function.apply(message)).isEqualTo("OLLEH");
134+
}
135+
136+
@SuppressWarnings({ "unchecked", "rawtypes" })
137+
@Test
138+
public void testInvocationWithMessageAndListOfSingleRoutingExpression() {
139+
FunctionCatalog functionCatalog = this.configureCatalog();
140+
Function function = functionCatalog.lookup(RoutingFunction.FUNCTION_NAME);
141+
assertThat(function).isNotNull();
142+
Message<String> message = MessageBuilder.withPayload("hello")
143+
.setHeader(FunctionProperties.PREFIX + ".routing-expression",
144+
List.of("'reverse'"))
145+
.build();
146+
assertThat(function.apply(message)).isEqualTo("olleh");
147+
}
148+
149+
@SuppressWarnings({ "unchecked", "rawtypes" })
150+
@Test
151+
public void testInvocationWithMessageAndListOfMultipleRoutingExpressions() {
152+
FunctionCatalog functionCatalog = this.configureCatalog();
153+
Function function = functionCatalog.lookup(RoutingFunction.FUNCTION_NAME);
154+
assertThat(function).isNotNull();
155+
Message<String> message = MessageBuilder.withPayload("hello")
156+
.setHeader(FunctionProperties.PREFIX + ".routing-expression",
157+
List.of("'uppercase'", "'reverse'"))
158+
.build();
159+
assertThat(function.apply(message)).isEqualTo("HELLO");
160+
}
161+
110162
@SuppressWarnings({ "unchecked", "rawtypes" })
111163
@Test
112164
public void testRoutingSimpleInputWithReactiveFunctionWithMessageHeader() {

0 commit comments

Comments
 (0)