Skip to content

Commit 547327e

Browse files
committed
GH-1236 Fix actuator's FunctionsEndpoint to handle ineligible functions
Given that we have ineligible function catalog.lookup(..) may return null if function is ineligible. Resolves #1236
1 parent 2fa8c2b commit 547327e

File tree

2 files changed

+90
-15
lines changed

2 files changed

+90
-15
lines changed

spring-cloud-function-context/src/main/java/org/springframework/cloud/function/actuator/FunctionsEndpoint.java

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -50,24 +50,25 @@ public Map<String, Map<String, Object>> listAll() {
5050
Set<String> names = functionCatalog.getNames(null);
5151
for (String name : names) {
5252
FunctionInvocationWrapper function = functionCatalog.lookup(name);
53-
Map<String, Object> functionMap = new LinkedHashMap<>();
54-
if (function.isFunction()) {
55-
functionMap.put("type", "FUNCTION");
56-
functionMap.put("input-type", this.toSimplePolyIn(function));
57-
functionMap.put("output-type", this.toSimplePolyOut(function));
53+
if (function != null) {
54+
Map<String, Object> functionMap = new LinkedHashMap<>();
55+
if (function.isFunction()) {
56+
functionMap.put("type", "FUNCTION");
57+
functionMap.put("input-type", this.toSimplePolyIn(function));
58+
functionMap.put("output-type", this.toSimplePolyOut(function));
59+
}
60+
else if (function.isConsumer()) {
61+
functionMap.put("type", "CONSUMER");
62+
functionMap.put("input-type", this.toSimplePolyIn(function));
63+
}
64+
else {
65+
functionMap.put("type", "SUPPLIER");
66+
functionMap.put("output-type", this.toSimplePolyOut(function));
67+
}
68+
allFunctions.put(name, functionMap);
5869
}
59-
else if (function.isConsumer()) {
60-
functionMap.put("type", "CONSUMER");
61-
functionMap.put("input-type", this.toSimplePolyIn(function));
62-
}
63-
else {
64-
functionMap.put("type", "SUPPLIER");
65-
functionMap.put("output-type", this.toSimplePolyOut(function));
66-
}
67-
allFunctions.put(name, functionMap);
6870
}
6971

70-
7172
return allFunctions;
7273
}
7374

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* Copyright 2021-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.cloud.function.actuator;
18+
19+
import java.util.Locale;
20+
import java.util.Map;
21+
import java.util.function.Function;
22+
23+
import org.junit.jupiter.api.Test;
24+
25+
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
26+
import org.springframework.boot.builder.SpringApplicationBuilder;
27+
import org.springframework.cloud.function.context.FunctionCatalog;
28+
import org.springframework.context.ApplicationContext;
29+
import org.springframework.context.annotation.Bean;
30+
import org.springframework.context.annotation.Configuration;
31+
32+
import static org.assertj.core.api.Assertions.assertThat;
33+
34+
/**
35+
*
36+
* @author Oleg Zhurakousky
37+
*/
38+
39+
public class FunctionsEndpointTests {
40+
41+
@Test
42+
public void ensureIneligibleFunctionWontCauseNPE() {
43+
ApplicationContext context = new SpringApplicationBuilder(SampleConfiguration.class)
44+
.run("--spring.cloud.function.ineligible-definitions=echo,uppercase",
45+
"--spring.main.lazy-initialization=true");
46+
FunctionCatalog catalog = context.getBean(FunctionCatalog.class);
47+
FunctionsEndpoint endpoint = new FunctionsEndpoint(catalog);
48+
Map<String, Map<String, Object>> allFunctionsinCatalog = endpoint.listAll();
49+
// implicit assertion - no NPE
50+
assertThat(allFunctionsinCatalog.size()).isEqualTo(2);
51+
assertThat(allFunctionsinCatalog.containsKey("functionRouter"));
52+
assertThat(allFunctionsinCatalog.containsKey("reverse"));
53+
}
54+
55+
@EnableAutoConfiguration
56+
@Configuration
57+
public static class SampleConfiguration {
58+
59+
@Bean
60+
public Function<String, String> echo() {
61+
return v -> v;
62+
}
63+
64+
@Bean
65+
public Function<String, String> uppercase() {
66+
return v -> v.toUpperCase(Locale.ROOT);
67+
}
68+
69+
@Bean
70+
public Function<String, String> reverse() {
71+
return v -> new StringBuilder(v).reverse().toString();
72+
}
73+
}
74+
}

0 commit comments

Comments
 (0)