Commit c306e031 authored by Phillip Webb's avatar Phillip Webb

Support '-' in endpoint names

Update the `EndpointId` constraints to allow '-' in names.

Closes gh-14773
parent d8b96856
...@@ -23,30 +23,44 @@ import org.springframework.util.Assert; ...@@ -23,30 +23,44 @@ import org.springframework.util.Assert;
/** /**
* An identifier for an actuator endpoint. Endpoint IDs may contain only letters, numbers * An identifier for an actuator endpoint. Endpoint IDs may contain only letters, numbers
* and {@code '.'}. They must begin with a lower-case letter. Case is ignored when * {@code '.'} and {@code '-'}. They must begin with a lower-case letter. Case and syntax
* comparing endpoint IDs. * characters are ignored when comparing endpoint IDs.
* *
* @author Phillip Webb * @author Phillip Webb
* @since 2.0.6 * @since 2.0.6
*/ */
public final class EndpointId { public final class EndpointId {
private static final Pattern VALID_CHARS = Pattern.compile("[a-zA-Z0-9\\.]+"); private static final Pattern VALID_CHARS = Pattern.compile("[a-zA-Z0-9\\.\\-]+");
private final String value; private final String value;
private final String lowerCaseValue; private final String lowerCaseValue;
private final String lowerCaseAlphaNumeric;
private EndpointId(String value) { private EndpointId(String value) {
Assert.hasText(value, "Value must not be empty"); Assert.hasText(value, "Value must not be empty");
Assert.isTrue(VALID_CHARS.matcher(value).matches(), Assert.isTrue(VALID_CHARS.matcher(value).matches(),
"Value must be alpha-numeric or '.'"); "Value must only contain valid chars");
Assert.isTrue(!Character.isDigit(value.charAt(0)), Assert.isTrue(!Character.isDigit(value.charAt(0)),
"Value must not start with a number"); "Value must not start with a number");
Assert.isTrue(!Character.isUpperCase(value.charAt(0)), Assert.isTrue(!Character.isUpperCase(value.charAt(0)),
"Value must not start with an uppercase letter"); "Value must not start with an uppercase letter");
this.value = value; this.value = value;
this.lowerCaseValue = value.toLowerCase(Locale.ENGLISH); this.lowerCaseValue = value.toLowerCase(Locale.ENGLISH);
this.lowerCaseAlphaNumeric = getAlphaNumerics(this.lowerCaseValue);
}
private String getAlphaNumerics(String value) {
StringBuilder result = new StringBuilder(value.length());
for (int i = 0; i < value.length(); i++) {
char ch = value.charAt(i);
if (ch >= 'a' && ch <= 'z' || ch >= '0' && ch <= '9') {
result.append(ch);
}
}
return result.toString();
} }
@Override @Override
...@@ -57,12 +71,13 @@ public final class EndpointId { ...@@ -57,12 +71,13 @@ public final class EndpointId {
if (obj == null || getClass() != obj.getClass()) { if (obj == null || getClass() != obj.getClass()) {
return false; return false;
} }
return toLowerCaseString().equals(((EndpointId) obj).toLowerCaseString()); return this.lowerCaseAlphaNumeric
.equals(((EndpointId) obj).lowerCaseAlphaNumeric);
} }
@Override @Override
public int hashCode() { public int hashCode() {
return toLowerCaseString().hashCode(); return this.lowerCaseAlphaNumeric.hashCode();
} }
/** /**
......
...@@ -47,16 +47,16 @@ public class EndpointIdTests { ...@@ -47,16 +47,16 @@ public class EndpointIdTests {
} }
@Test @Test
public void ofWhenContainsDashThrowsException() { public void ofWhenContainsInvalidCharThrowsException() {
this.thrown.expect(IllegalArgumentException.class); this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Value must be alpha-numeric"); this.thrown.expectMessage("Value must only contain valid chars");
EndpointId.of("foo-bar"); EndpointId.of("foo/bar");
} }
@Test @Test
public void ofWhenHasBadCharThrowsException() { public void ofWhenHasBadCharThrowsException() {
this.thrown.expect(IllegalArgumentException.class); this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Value must be alpha-numeric"); this.thrown.expectMessage("Value must only contain valid chars");
EndpointId.of("foo!bar"); EndpointId.of("foo!bar");
} }
...@@ -82,13 +82,25 @@ public class EndpointIdTests { ...@@ -82,13 +82,25 @@ public class EndpointIdTests {
assertThat(endpointId.toString()).isEqualTo("foo.bar"); assertThat(endpointId.toString()).isEqualTo("foo.bar");
} }
@Test
public void ofWhenContainsDashIsValid() {
// Ideally we wouldn't support this but there are existing endpoints using the
// pattern. See gh-14773
EndpointId endpointId = EndpointId.of("foo-bar");
assertThat(endpointId.toString()).isEqualTo("foo-bar");
}
@Test @Test
public void equalsAndHashCode() { public void equalsAndHashCode() {
EndpointId one = EndpointId.of("foobar"); EndpointId one = EndpointId.of("foobar1");
EndpointId two = EndpointId.of("fooBar"); EndpointId two = EndpointId.of("fooBar1");
EndpointId three = EndpointId.of("barfoo"); EndpointId three = EndpointId.of("foo-bar1");
EndpointId four = EndpointId.of("foo.bar1");
EndpointId five = EndpointId.of("barfoo1");
EndpointId six = EndpointId.of("foobar2");
assertThat(one.hashCode()).isEqualTo(two.hashCode()); assertThat(one.hashCode()).isEqualTo(two.hashCode());
assertThat(one).isEqualTo(one).isEqualTo(two).isNotEqualTo(three); assertThat(one).isEqualTo(one).isEqualTo(two).isEqualTo(two).isEqualTo(three)
.isEqualTo(four).isNotEqualTo(five).isNotEqualTo(six);
} }
@Test @Test
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment