Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[FIXED JENKINS-45436] Add an API to assist deriving human names from …
…URLs
  • Loading branch information
stephenc committed Jul 11, 2017
1 parent 850d9d2 commit ef9f853
Show file tree
Hide file tree
Showing 2 changed files with 199 additions and 0 deletions.
129 changes: 129 additions & 0 deletions src/main/java/jenkins/scm/api/SCMName.java
@@ -0,0 +1,129 @@
/*
* The MIT License
*
* Copyright (c) 2017 CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

package jenkins.scm.api;

import com.google.common.net.InternetDomainName;
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import java.net.IDN;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import org.apache.commons.lang.StringUtils;

/**
* Utility class to help with naming SCM related things.
*
* @since 2.2.0
*/
public final class SCMName {

/**
* Utility class
*/
private SCMName() {
throw new IllegalAccessError("Utility class");
}

/**
* Makes best effort to guess a "sensible" display name from the hostname in the URL.
*
* @param url the URL.
* @return the display name or {@code null}
*/
public static String fromUrl(@NonNull String url, @CheckForNull String... ignoredPrefixes) {
try {
return innerFromUrl(url, ignoredPrefixes.length == 0 ? null : Arrays.asList(ignoredPrefixes));
} catch (LinkageError e) {
return null;
}
}

/**
* Makes best effort to guess a "sensible" display name from the hostname in the URL.
*
* @param url the URL.
* @return the display name or {@code null}
*/
public static String fromUrl(@NonNull String url, @CheckForNull List<String> ignoredPrefixes) {
try {
return innerFromUrl(url, ignoredPrefixes);
} catch (LinkageError e) {
return null;
}
}

/**
* Makes best effort to guess a "sensible" display name from the hostname in the URL.
*
* @param url the URL.
* @return the display name or {@code null}
* @throws LinkageError if Guava changes their API that we have depended on.
*/
@CheckForNull
private static String innerFromUrl(@NonNull String url, List<String> ignoredPrefixes) throws LinkageError {
String hostName;
try {
URL serverUri = new URL(url);
hostName = serverUri.getHost();
if (StringUtils.isBlank(hostName)) {
return null;
}
// let's see if we can make this more "friendly"
InternetDomainName host = InternetDomainName.from(IDN.toASCII(hostName));
if (host.hasPublicSuffix()) {
String publicName = host.publicSuffix().name();
hostName = StringUtils.removeEnd(StringUtils.removeEnd(host.name(), publicName), ".")
.toLowerCase(Locale.ENGLISH);
} else {
hostName = StringUtils.removeEnd(host.name(), ".").toLowerCase(Locale.ENGLISH);
}
if (ignoredPrefixes != null) {
for (String prefix : ignoredPrefixes) {
if (prefix.endsWith(".")) {
if (hostName.startsWith(prefix)) {
hostName = hostName.substring(prefix.length());
break;
}
} else {
if (hostName.startsWith(prefix + ".")) {
hostName = hostName.substring(prefix.length() + 1);
break;
}
}
}
}
if (StringUtils.isNotBlank(hostName)) {
hostName = IDN.toUnicode(hostName).replace('.', ' ').replace('-', ' ');
}
} catch (MalformedURLException e) {
// ignore, best effort
hostName = null;
}
return hostName;
}
}
70 changes: 70 additions & 0 deletions src/test/java/jenkins/scm/api/SCMNameTest.java
@@ -0,0 +1,70 @@
/*
* The MIT License
*
* Copyright (c) 2017 CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

package jenkins.scm.api;

import org.junit.Test;

import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.*;

public class SCMNameTest {
@Test
public void given__url_without_hostname__when__naming__then__no_name_inferred() throws Exception {
assertThat(SCMName.fromUrl("file:///some/random/file"), is(nullValue()));
}

@Test
public void given__url_with_hostname__when__naming__then__public_tld_removed() throws Exception {
assertThat(SCMName.fromUrl("http://scm.example.com"), is("scm example"));
}

@Test
public void given__url_with_hostname__when__naming__then__public_sld_removed() throws Exception {
assertThat(SCMName.fromUrl("http://scm.example.co.uk"), is("scm example"));
}

@Test
public void given__url_with_hostname__when__naming__then__prefix_is_removed() throws Exception {
assertThat(SCMName.fromUrl("http://scm.example.ie", "scm"), is("example"));
}

@Test
public void given__url_with_punycode__when__naming__then__hostname_is_decoded() throws Exception {
assertThat(SCMName.fromUrl("http://xn--e1afmkfd.xn--p1ai/"), is("пример"));
}

@Test
public void given__url_with_idn__when__naming__then__punycode_is_roundtripped() throws Exception {
assertThat(SCMName.fromUrl("http://\u043F\u0440\u0438\u043C\u0435\u0440.\u0440\u0444" /*пример.рф*/),
is("\u043F\u0440\u0438\u043C\u0435\u0440" /*пример*/));
}

@Test
public void given__url_with_idn__when__naming__then__punycode_is_roundtripped2() throws Exception {
assertThat(SCMName.fromUrl("http://\u4F8B\u5B50.\u4E2D\u56FD/"), is("\u4F8B\u5B50"));
}

}

0 comments on commit ef9f853

Please sign in to comment.