Skip to content

Commit 644509f

Browse files
committed
Change LinksEnhancer to user endpoint name
If endpoint name is not available fallback to endpoint path. Allow multiple hrefs per rel if path is different. See spring-projectsgh-7132
1 parent 7352d8e commit 644509f

File tree

2 files changed

+167
-11
lines changed

2 files changed

+167
-11
lines changed

spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/LinksEnhancer.java

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,14 @@
1616

1717
package org.springframework.boot.actuate.autoconfigure;
1818

19-
import java.util.HashSet;
20-
import java.util.Set;
19+
import java.util.ArrayList;
20+
import java.util.HashMap;
21+
import java.util.List;
22+
import java.util.Map;
2123

2224
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
2325
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoints;
26+
import org.springframework.boot.actuate.endpoint.mvc.NamedMvcEndpoint;
2427
import org.springframework.hateoas.ResourceSupport;
2528
import org.springframework.util.StringUtils;
2629

@@ -47,23 +50,31 @@ public void addEndpointLinks(ResourceSupport resource, String self) {
4750
resource.add(linkTo(LinksEnhancer.class).slash(this.rootPath + self)
4851
.withSelfRel());
4952
}
50-
Set<String> added = new HashSet<String>();
53+
Map<String, List<String>> added = new HashMap<String, List<String>>();
5154
for (MvcEndpoint endpoint : this.endpoints.getEndpoints()) {
52-
if (!endpoint.getPath().equals(self) && !added.contains(endpoint.getPath())) {
53-
addEndpointLink(resource, endpoint);
55+
56+
String rel = getRel(endpoint);
57+
List<String> pathsForRel = added.get(rel) == null ? new ArrayList<String>() : added.get(rel);
58+
59+
if (!endpoint.getPath().equals(self) && !pathsForRel.contains(endpoint.getPath())) {
60+
addEndpointLink(resource, endpoint, rel);
61+
pathsForRel.add(endpoint.getPath());
62+
added.put(rel, pathsForRel);
5463
}
55-
added.add(endpoint.getPath());
5664
}
5765
}
5866

59-
private void addEndpointLink(ResourceSupport resource, MvcEndpoint endpoint) {
67+
private String getRel(MvcEndpoint endpoint) {
68+
String name = endpoint instanceof NamedMvcEndpoint ? ((NamedMvcEndpoint) endpoint).getName() : endpoint.getPath();
69+
return (name.startsWith("/") ? name.substring(1) : name);
70+
}
71+
72+
private void addEndpointLink(ResourceSupport resource, MvcEndpoint endpoint, String rel) {
6073
Class<?> type = endpoint.getEndpointType();
6174
type = (type == null ? Object.class : type);
62-
String path = endpoint.getPath();
63-
String rel = (path.startsWith("/") ? path.substring(1) : path);
6475
if (StringUtils.hasText(rel)) {
65-
String fullPath = this.rootPath + endpoint.getPath();
66-
resource.add(linkTo(type).slash(fullPath).withRel(rel));
76+
String href = this.rootPath + endpoint.getPath();
77+
resource.add(linkTo(type).slash(href).withRel(rel));
6778
}
6879
}
6980

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/*
2+
* Copyright 2012-2016 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+
* http://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.boot.actuate.autoconfigure;
18+
19+
import java.util.Arrays;
20+
import java.util.Collections;
21+
import java.util.List;
22+
23+
import org.assertj.core.api.Condition;
24+
import org.junit.Before;
25+
import org.junit.Test;
26+
27+
import org.springframework.boot.actuate.endpoint.AbstractEndpoint;
28+
import org.springframework.boot.actuate.endpoint.mvc.AbstractMvcEndpoint;
29+
import org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter;
30+
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
31+
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoints;
32+
import org.springframework.hateoas.Link;
33+
import org.springframework.hateoas.ResourceSupport;
34+
import org.springframework.mock.web.MockHttpServletRequest;
35+
import org.springframework.web.context.request.RequestContextHolder;
36+
import org.springframework.web.context.request.ServletRequestAttributes;
37+
import org.springframework.web.context.support.StaticWebApplicationContext;
38+
39+
import static org.assertj.core.api.Assertions.assertThat;
40+
41+
/**
42+
* Tests for {@link LinksEnhancer}.
43+
*
44+
* @author Madhura Bhave
45+
*/
46+
public class LinksEnhancerTests {
47+
48+
@Before
49+
public void setup() {
50+
MockHttpServletRequest request = new MockHttpServletRequest();
51+
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request));
52+
}
53+
54+
@Test
55+
public void useNameAsRelIfAvailable() throws Exception {
56+
TestMvcEndpoint endpoint = new TestMvcEndpoint(new TestEndpoint("a"));
57+
endpoint.setPath("something-else");
58+
LinksEnhancer enhancer = getLinksEnhancer(Collections.singletonList((MvcEndpoint) endpoint));
59+
ResourceSupport support = new ResourceSupport();
60+
enhancer.addEndpointLinks(support, "");
61+
assertThat(support.getLink("a").getHref()).contains("/something-else");
62+
}
63+
64+
@Test
65+
public void usePathAsRelIfNameNotAvailable() throws Exception {
66+
MvcEndpoint endpoint = new NoNameTestMvcEndpoint("/a", false);
67+
LinksEnhancer enhancer = getLinksEnhancer(Collections.singletonList(endpoint));
68+
ResourceSupport support = new ResourceSupport();
69+
enhancer.addEndpointLinks(support, "");
70+
assertThat(support.getLink("a").getHref()).contains("/a");
71+
}
72+
73+
@Test
74+
public void hrefNotAddedToRelTwice() throws Exception {
75+
MvcEndpoint endpoint = new TestMvcEndpoint(new TestEndpoint("a"));
76+
MvcEndpoint otherEndpoint = new TestMvcEndpoint(new TestEndpoint("a"));
77+
LinksEnhancer enhancer = getLinksEnhancer(Arrays.asList(endpoint, otherEndpoint));
78+
ResourceSupport support = new ResourceSupport();
79+
enhancer.addEndpointLinks(support, "");
80+
assertThat(support.getLinks()).haveExactly(1, getCondition("a", "a"));
81+
}
82+
83+
@Test
84+
public void multipleHrefsForSameRelWhenPathIsDifferent() throws Exception {
85+
TestMvcEndpoint endpoint = new TestMvcEndpoint(new TestEndpoint("a"));
86+
endpoint.setPath("endpoint");
87+
TestMvcEndpoint otherEndpoint = new TestMvcEndpoint(new TestEndpoint("a"));
88+
otherEndpoint.setPath("other-endpoint");
89+
LinksEnhancer enhancer = getLinksEnhancer(Arrays.asList((MvcEndpoint) endpoint, otherEndpoint));
90+
ResourceSupport support = new ResourceSupport();
91+
enhancer.addEndpointLinks(support, "");
92+
assertThat(support.getLinks()).haveExactly(1, getCondition("a", "endpoint"));
93+
assertThat(support.getLinks()).haveExactly(1, getCondition("a", "other-endpoint"));
94+
}
95+
96+
private Condition<Link> getCondition(final String rel, final String href) {
97+
return new Condition<Link>() {
98+
@Override
99+
public boolean matches(Link link) {
100+
return link.getRel().equals(rel) && link.getHref().equals("http://localhost/" + href);
101+
}
102+
};
103+
}
104+
105+
private LinksEnhancer getLinksEnhancer(List<MvcEndpoint> endpoints) throws Exception {
106+
StaticWebApplicationContext context = new StaticWebApplicationContext();
107+
for (MvcEndpoint endpoint : endpoints) {
108+
context.getDefaultListableBeanFactory().registerSingleton(endpoint.toString(),
109+
endpoint);
110+
}
111+
MvcEndpoints mvcEndpoints = new MvcEndpoints();
112+
mvcEndpoints.setApplicationContext(context);
113+
mvcEndpoints.afterPropertiesSet();
114+
return new LinksEnhancer("", mvcEndpoints);
115+
}
116+
117+
private static class TestEndpoint extends AbstractEndpoint<Object> {
118+
119+
TestEndpoint(String id) {
120+
super(id);
121+
}
122+
123+
@Override
124+
public Object invoke() {
125+
return null;
126+
}
127+
128+
}
129+
130+
private static class TestMvcEndpoint extends EndpointMvcAdapter {
131+
132+
TestMvcEndpoint(TestEndpoint delegate) {
133+
super(delegate);
134+
}
135+
136+
}
137+
138+
private static class NoNameTestMvcEndpoint extends AbstractMvcEndpoint {
139+
140+
NoNameTestMvcEndpoint(String path, boolean sensitive) {
141+
super(path, sensitive);
142+
}
143+
}
144+
145+
}

0 commit comments

Comments
 (0)