diff --git a/src/main/java/org/kohsuke/github/GHLicense.java b/src/main/java/org/kohsuke/github/GHLicense.java
new file mode 100644
index 000000000..baeb6c3d7
--- /dev/null
+++ b/src/main/java/org/kohsuke/github/GHLicense.java
@@ -0,0 +1,110 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2016, Duncan Dickinson
+ *
+ * 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 org.kohsuke.github;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The GitHub Preview API's license information
+ *
+ * WARNING: This uses a PREVIEW API - you must use {@link org.kohsuke.github.extras.PreviewHttpConnector}
+ *
+ * @author Duncan Dickinson
+ * @see GitHub#getLicense(String)
+ * @see GHRepository#getFullLicense()
+ * @see https://developer.github.com/v3/licenses/
+ */
+@SuppressWarnings({"UnusedDeclaration"})
+@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
+ "NP_UNWRITTEN_FIELD"}, justification = "JSON API")
+public class GHLicense extends GHLicenseBase {
+
+ protected String html_url, description, category, implementation, body;
+
+ protected List required = new ArrayList();
+ protected List permitted = new ArrayList();
+ protected List forbidden = new ArrayList();
+
+ public URL getHtmlUrl() {
+ return GitHub.parseURL(html_url);
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public String getCategory() {
+ return category;
+ }
+
+ public String getImplementation() {
+ return implementation;
+ }
+
+ public List getRequired() {
+ return required;
+ }
+
+ public List getPermitted() {
+ return permitted;
+ }
+
+ public List getForbidden() {
+ return forbidden;
+ }
+
+ public String getBody() {
+ return body;
+ }
+
+ @Override
+ public String toString() {
+ return "GHLicense{" +
+ "html_url='" + html_url + '\'' +
+ ", description='" + description + '\'' +
+ ", category='" + category + '\'' +
+ ", implementation='" + implementation + '\'' +
+ ", body='" + body + '\'' +
+ ", required=" + required +
+ ", permitted=" + permitted +
+ ", forbidden=" + forbidden +
+ ", htmlUrl=" + getHtmlUrl() +
+ "} " + super.toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return super.equals(o);
+ }
+
+ @Override
+ public int hashCode() {
+ return super.hashCode();
+ }
+}
diff --git a/src/main/java/org/kohsuke/github/GHLicenseBase.java b/src/main/java/org/kohsuke/github/GHLicenseBase.java
new file mode 100644
index 000000000..e6c92359d
--- /dev/null
+++ b/src/main/java/org/kohsuke/github/GHLicenseBase.java
@@ -0,0 +1,107 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2016, Duncan Dickinson
+ *
+ * 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 org.kohsuke.github;
+
+import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+
+import java.net.URL;
+
+/**
+ * The basic information for GitHub API licenses - as use in a number of
+ * API calls that only return the basic details
+ *
+ * WARNING: This uses a PREVIEW API - you must use {@link org.kohsuke.github.extras.PreviewHttpConnector}
+ *
+ * @author Duncan Dickinson
+ * @see https://developer.github.com/v3/licenses/
+ * @see GitHub#listLicenses()
+ * @see GHRepository#getLicense()
+ * @see GHLicense GHLicense subclass for the more comprehensive listing of properties
+ */
+@SuppressWarnings({"UnusedDeclaration"})
+@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
+ "NP_UNWRITTEN_FIELD"}, justification = "JSON API")
+public class GHLicenseBase {
+
+ protected String key, name, url;
+ protected Boolean featured;
+
+ /**
+ * @return a mnemonic for the license
+ */
+ public String getKey() {
+ return key;
+ }
+
+ /**
+ * @return the license name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @return API URL of this object.
+ */
+ @WithBridgeMethods(value = String.class, adapterMethod = "urlToString")
+ public URL getUrl() {
+ return GitHub.parseURL(url);
+ }
+
+ /**
+ * Featured licenses are bold in the new repository drop-down
+ *
+ * @return True if the license is featured, false otherwise
+ */
+ public Boolean isFeatured() {
+ return featured;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof GHLicenseBase)) return false;
+
+ GHLicenseBase that = (GHLicenseBase) o;
+
+ return getUrl().toString().equals(that.getUrl().toString());
+ }
+
+ @Override
+ public int hashCode() {
+ return getUrl().toString().hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "GHLicenseBase{" +
+ "key='" + key + '\'' +
+ ", name='" + name + '\'' +
+ ", url='" + url + '\'' +
+ ", featured=" + featured +
+ '}';
+ }
+}
diff --git a/src/main/java/org/kohsuke/github/GHRepository.java b/src/main/java/org/kohsuke/github/GHRepository.java
index 41a44e9da..0b7424311 100644
--- a/src/main/java/org/kohsuke/github/GHRepository.java
+++ b/src/main/java/org/kohsuke/github/GHRepository.java
@@ -65,6 +65,16 @@ public class GHRepository extends GHObject {
private String description, homepage, name, full_name;
private String html_url; // this is the UI
+ /*
+ * The license information makes use of the preview API.
+ *
+ * See: https://developer.github.com/v3/licenses/
+ */
+ /**
+ * The basic license details as returned from {@link GitHub#getRepository(String)}
+ */
+ private GHLicenseBase license;
+
private String git_url, ssh_url, clone_url, svn_url, mirror_url;
private GHUser owner; // not fully populated. beware.
private boolean has_issues, has_wiki, fork, has_downloads;
@@ -839,6 +849,44 @@ public class GHRepository extends GHObject {
}
};
}
+ * Gets the basic license details for the repository.
+ *
+ * This is a preview item and requires you to use {@link org.kohsuke.github.extras.PreviewHttpConnector}
+ *
+ * Warning: Only returns the basic license details. Use {@link GitHub#getLicense(String)}
+ * to get the full license information (hint: pass it {@link GHLicenseBase#getKey()}).
+ *
+ * @throws IOException as usual but also if you don't use the preview connector
+ */
+ public GHLicenseBase getLicense() {
+ return license;
+ }
+
+ /**
+ * Access the full license details - makes an additional API call
+ *
+ * This is a preview item and requires you to use {@link org.kohsuke.github.extras.PreviewHttpConnector}
+ *
+ * @return the license details
+ * @throws IOException as usual but also if you don't use the preview connector
+ */
+ public GHLicense getFullLicense() throws IOException {
+ return root.getLicense(license.getKey());
+ }
+
+ /**
+ * Retrieves the contents of the repository's license file - makes an additional API call
+ *
+ * This is a preview item and requires you to use {@link org.kohsuke.github.extras.PreviewHttpConnector}
+ *
+ * @return details regarding the license contents
+ * @throws IOException as usual but also if you don't use the preview connector
+ */
+ public GHContent getLicenseContent() throws IOException {
+ return root.retrieve().to(getApiTailUrl("license"), GHContent.class).wrap(this);
+ }
+
+ /**
/**
* Lists all the commit statues attached to the given commit, newer ones first.
diff --git a/src/main/java/org/kohsuke/github/GitHub.java b/src/main/java/org/kohsuke/github/GitHub.java
index 297596d87..dd1e3593e 100644
--- a/src/main/java/org/kohsuke/github/GitHub.java
+++ b/src/main/java/org/kohsuke/github/GitHub.java
@@ -56,6 +56,7 @@ import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.introspect.VisibilityChecker.Std;
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
+
import java.util.logging.Logger;
/**
@@ -339,6 +340,33 @@ public class GitHub {
String[] tokens = name.split("/");
return retrieve().to("/repos/" + tokens[0] + '/' + tokens[1], GHRepository.class).wrap(this);
}
+ * Returns a list of popular open source licenses
+ *
+ * WARNING: This uses a PREVIEW API - you must use {@link org.kohsuke.github.extras.PreviewHttpConnector}
+ *
+ * @see GitHub API - Licenses
+ *
+ * @return a list of popular open source licenses
+ * @throws IOException if the HttpConnector doesn't pass in the preview header or other IO issue
+ */
+ public List listLicenses() throws IOException {
+ return Arrays.asList(retrieve().to("/licenses", GHLicenseBase[].class));
+ }
+
+ /**
+ * Returns the full details for a license
+ *
+ * WARNING: This uses a PREVIEW API - you must use {@link org.kohsuke.github.extras.PreviewHttpConnector}
+ *
+ * @param key The license key provided from the API
+ * @return The license details
+ * @throws IOException
+ */
+ public GHLicense getLicense(String key) throws IOException {
+ return retrieve().to("/licenses/" + key, GHLicense.class);
+ }
+
+ /**
/**
* This method returns a shallowly populated organizations.
diff --git a/src/main/java/org/kohsuke/github/extras/PreviewHttpConnector.java b/src/main/java/org/kohsuke/github/extras/PreviewHttpConnector.java
new file mode 100644
index 000000000..e1be7855f
--- /dev/null
+++ b/src/main/java/org/kohsuke/github/extras/PreviewHttpConnector.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright $year slavinson
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.kohsuke.github.extras;
+
+import org.kohsuke.github.HttpConnector;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+public class PreviewHttpConnector implements HttpConnector {
+ private final HttpConnector base;
+ private final int readTimeout, connectTimeout;
+
+ /**
+ * @param connectTimeout HTTP connection timeout in milliseconds
+ * @param readTimeout HTTP read timeout in milliseconds
+ */
+ public PreviewHttpConnector(HttpConnector base, int connectTimeout, int readTimeout) {
+ this.base = base;
+ this.connectTimeout = connectTimeout;
+ this.readTimeout = readTimeout;
+ }
+
+ public PreviewHttpConnector(HttpConnector base, int timeout) {
+ this(base, timeout, timeout);
+ }
+
+ public PreviewHttpConnector(HttpConnector base) {
+ this(base, ImpatientHttpConnector.CONNECT_TIMEOUT, ImpatientHttpConnector.READ_TIMEOUT);
+ }
+
+ public PreviewHttpConnector() {
+ this(new HttpConnector() {
+ public HttpURLConnection connect(URL url) throws IOException {
+ return (HttpURLConnection) url.openConnection();
+ }
+ }, ImpatientHttpConnector.CONNECT_TIMEOUT, ImpatientHttpConnector.READ_TIMEOUT);
+ }
+
+ public HttpURLConnection connect(URL url) throws IOException {
+ HttpURLConnection con = base.connect(url);
+ con.setConnectTimeout(connectTimeout);
+ con.setReadTimeout(readTimeout);
+ con.addRequestProperty("Accept", PREVIEW_MEDIA_TYPE);
+ return con;
+ }
+
+ /**
+ * Default connection timeout in milliseconds
+ */
+ public static final String PREVIEW_MEDIA_TYPE = "application/vnd.github.drax-preview+json";
+}
diff --git a/src/test/java/org/kohsuke/github/GHLicenseTest.java b/src/test/java/org/kohsuke/github/GHLicenseTest.java
new file mode 100644
index 000000000..e27f8057c
--- /dev/null
+++ b/src/test/java/org/kohsuke/github/GHLicenseTest.java
@@ -0,0 +1,220 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2016, Duncan Dickinson
+ *
+ * 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 org.kohsuke.github;
+
+import org.apache.commons.io.IOUtils;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.kohsuke.github.extras.PreviewHttpConnector;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.List;
+
+/**
+ * @author Duncan Dickinson
+ */
+public class GHLicenseTest extends Assert {
+ private GitHub gitHub;
+
+ @Before
+ public void setUp() throws Exception {
+ gitHub = new GitHubBuilder()
+ .fromCredentials()
+ .withConnector(new PreviewHttpConnector())
+ .build();
+ }
+
+ /**
+ * Basic test to ensure that the list of licenses from {@link GitHub#listLicenses()} is returned
+ *
+ * @throws IOException
+ */
+ @Test
+ public void listLicenses() throws IOException {
+ List licenses = gitHub.listLicenses();
+ assertTrue(licenses.size() > 0);
+ }
+
+ /**
+ * Tests that {@link GitHub#listLicenses()} returns the MIT license
+ * in the expected manner.
+ *
+ * @throws IOException
+ */
+ @Test
+ public void listLicensesCheckIndividualLicense() throws IOException {
+ List licenses = gitHub.listLicenses();
+ for (GHLicenseBase lic : licenses) {
+ if (lic.getKey().equals("mit")) {
+ assertTrue(lic.getUrl().equals(new URL("https://api.github.com/licenses/mit")));
+ return;
+ }
+ }
+ fail("The MIT license was not found");
+ }
+
+ /**
+ * Checks that the request for an individual license using {@link GitHub#getLicense(String)}
+ * returns expected values (not all properties are checked)
+ *
+ * @throws IOException
+ */
+ @Test
+ public void getLicense() throws IOException {
+ String key = "mit";
+ GHLicense license = gitHub.getLicense(key);
+ assertNotNull(license);
+ assertTrue("The name is correct", license.getName().equals("MIT License"));
+ assertTrue("The HTML URL is correct", license.getHtmlUrl().equals(new URL("http://choosealicense.com/licenses/mit/")));
+ }
+
+ /**
+ * Attempts to list the licenses with a non-preview connection
+ *
+ * @throws IOException is expected to be thrown
+ */
+ @Test(expected = IOException.class)
+ public void ListLicensesWithoutPreviewConnection() throws IOException {
+ GitHub.connect().listLicenses();
+ }
+
+ /**
+ * Accesses the 'kohsuke/github-api' repo using {@link GitHub#getRepository(String)}
+ * and checks that the license is correct
+ *
+ * @throws IOException
+ */
+ @Test
+ public void checkRepositoryLicense() throws IOException {
+ GHRepository repo = gitHub.getRepository("kohsuke/github-api");
+ GHLicenseBase license = repo.getLicense();
+ assertNotNull("The license is populated", license);
+ assertTrue("The key is correct", license.getKey().equals("mit"));
+ assertTrue("The name is correct", license.getName().equals("MIT License"));
+ assertTrue("The URL is correct", license.getUrl().equals(new URL("https://api.github.com/licenses/mit")));
+ }
+
+ /**
+ * Accesses the 'atom/atom' repo using {@link GitHub#getRepository(String)}
+ * and checks that the license is correct
+ *
+ * @throws IOException
+ */
+ @Test
+ public void checkRepositoryLicenseAtom() throws IOException {
+ GHRepository repo = gitHub.getRepository("atom/atom");
+ GHLicenseBase license = repo.getLicense();
+ assertNotNull("The license is populated", license);
+ assertTrue("The key is correct", license.getKey().equals("mit"));
+ assertTrue("The name is correct", license.getName().equals("MIT License"));
+ assertTrue("The URL is correct", license.getUrl().equals(new URL("https://api.github.com/licenses/mit")));
+ }
+
+ /**
+ * Accesses the 'pomes/pomes' repo using {@link GitHub#getRepository(String)}
+ * and checks that the license is correct
+ *
+ * @throws IOException
+ */
+ @Test
+ public void checkRepositoryLicensePomes() throws IOException {
+ GHRepository repo = gitHub.getRepository("pomes/pomes");
+ GHLicenseBase license = repo.getLicense();
+ assertNotNull("The license is populated", license);
+ assertTrue("The key is correct", license.getKey().equals("apache-2.0"));
+ assertTrue("The name is correct", license.getName().equals("Apache License 2.0"));
+ assertTrue("The URL is correct", license.getUrl().equals(new URL("https://api.github.com/licenses/apache-2.0")));
+ }
+
+ /**
+ * Accesses the 'dedickinson/test-repo' repo using {@link GitHub#getRepository(String)}
+ * and checks that *no* license is returned as the repo doesn't have one
+ *
+ * @throws IOException
+ */
+ @Test
+ public void checkRepositoryWithoutLicense() throws IOException {
+ GHRepository repo = gitHub.getRepository("dedickinson/test-repo");
+ GHLicenseBase license = repo.getLicense();
+ assertNull("There is no license", license);
+ }
+
+ /**
+ * Accesses the 'kohsuke/github-api' repo using {@link GitHub#getRepository(String)}
+ * and then calls {@link GHRepository#getFullLicense()} and checks that certain
+ * properties are correct
+ *
+ * @throws IOException
+ */
+ @Test
+ public void checkRepositoryFullLicense() throws IOException {
+ GHRepository repo = gitHub.getRepository("kohsuke/github-api");
+ GHLicense license = repo.getFullLicense();
+ assertNotNull("The license is populated", license);
+ assertTrue("The key is correct", license.getKey().equals("mit"));
+ assertTrue("The name is correct", license.getName().equals("MIT License"));
+ assertTrue("The URL is correct", license.getUrl().equals(new URL("https://api.github.com/licenses/mit")));
+ assertTrue("The HTML URL is correct", license.getHtmlUrl().equals(new URL("http://choosealicense.com/licenses/mit/")));
+ }
+
+ /**
+ * Accesses the 'pomes/pomes' repo using {@link GitHub#getRepository(String)}
+ * and then calls {@link GHRepository#getLicenseContent()} and checks that certain
+ * properties are correct
+ *
+ * @throws IOException
+ */
+ @Test
+ public void checkRepositoryLicenseContent() throws IOException {
+ GHRepository repo = gitHub.getRepository("pomes/pomes");
+ GHContent content = repo.getLicenseContent();
+ assertNotNull("The license content is populated", content);
+ assertTrue("The type is 'file'", content.getType().equals("file"));
+ assertTrue("The license file is 'LICENSE'", content.getName().equals("LICENSE"));
+
+ if (content.getEncoding().equals("base64")) {
+ String licenseText = new String(IOUtils.toByteArray(content.read()));
+ assertTrue("The license appears to be an Apache License", licenseText.contains("Apache License"));
+ } else {
+ fail("Expected the license to be Base64 encoded but instead it was " + content.getEncoding());
+ }
+ }
+
+ /**
+ * Accesses the 'kohsuke/github-api' repo using {@link GitHub#getRepository(String)}
+ * but without using {@link PreviewHttpConnector} and ensures that the {@link GHRepository#getLicense()}
+ * call just returns null rather than raising an exception. This should indicate that
+ * non-preview connection requests aren't affected by the change in functionality
+ *
+ * @throws IOException
+ */
+ @Test
+ public void checkRepositoryLicenseWithoutPreviewConnection() throws IOException {
+ GHRepository repo = GitHub.connect().getRepository("kohsuke/github-api");
+ assertNull(repo.getLicense());
+ }
+}