mirror of
https://github.com/jlengrand/github-api.git
synced 2026-03-11 08:21:23 +00:00
Compare commits
15 Commits
github-api
...
github-api
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8ce76fba62 | ||
|
|
a947672320 | ||
|
|
5496e2b553 | ||
|
|
2f9edc65dd | ||
|
|
6372337456 | ||
|
|
b843606ebe | ||
|
|
7cda083b31 | ||
|
|
095604d4ca | ||
|
|
7e799d02e3 | ||
|
|
7f4612f872 | ||
|
|
6f6ff56338 | ||
|
|
27701a75d9 | ||
|
|
3717d2ed32 | ||
|
|
3a9b6665ee | ||
|
|
bbc42f8898 |
4
pom.xml
4
pom.xml
@@ -3,11 +3,11 @@
|
||||
<parent>
|
||||
<groupId>org.kohsuke</groupId>
|
||||
<artifactId>pom</artifactId>
|
||||
<version>1</version>
|
||||
<version>3</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>github-api</artifactId>
|
||||
<version>1.19</version>
|
||||
<version>1.21</version>
|
||||
<name>GitHub API for Java</name>
|
||||
<url>http://github-api.kohsuke.org/</url>
|
||||
<description>GitHub API for Java</description>
|
||||
|
||||
@@ -2,10 +2,15 @@ package org.kohsuke.github;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import static org.kohsuke.github.ApiVersion.*;
|
||||
|
||||
/**
|
||||
* Common part of {@link GHUser} and {@link GHOrganization}.
|
||||
*
|
||||
@@ -31,18 +36,48 @@ public abstract class GHPerson {
|
||||
*/
|
||||
public synchronized Map<String,GHRepository> getRepositories() throws IOException {
|
||||
Map<String,GHRepository> repositories = new TreeMap<String, GHRepository>();
|
||||
for (int i=1; ; i++) {
|
||||
GHRepository[] array = root.retrieve3("/users/" + login + "/repos?per_page=100&page=" + i, GHRepository[].class);
|
||||
for (GHRepository r : array) {
|
||||
r.root = root;
|
||||
for (List<GHRepository> batch : iterateRepositories(100)) {
|
||||
for (GHRepository r : batch)
|
||||
repositories.put(r.getName(),r);
|
||||
}
|
||||
if (array.length==0) break;
|
||||
}
|
||||
|
||||
return Collections.unmodifiableMap(repositories);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads repository list in a pagenated fashion.
|
||||
*
|
||||
* <p>
|
||||
* For a person with a lot of repositories, GitHub returns the list of repositories in a pagenated fashion.
|
||||
* Unlike {@link #getRepositories()}, this method allows the caller to start processing data as it arrives.
|
||||
*
|
||||
* Every {@link Iterator#next()} call results in I/O. Exceptions that occur during the processing is wrapped
|
||||
* into {@link Error}.
|
||||
*/
|
||||
public synchronized Iterable<List<GHRepository>> iterateRepositories(final int pageSize) {
|
||||
return new Iterable<List<GHRepository>>() {
|
||||
public Iterator<List<GHRepository>> iterator() {
|
||||
final Iterator<GHRepository[]> pager = root.retrievePaged("/users/" + login + "/repos?per_page="+pageSize,GHRepository[].class,false, V3);
|
||||
|
||||
return new Iterator<List<GHRepository>>() {
|
||||
public boolean hasNext() {
|
||||
return pager.hasNext();
|
||||
}
|
||||
|
||||
public List<GHRepository> next() {
|
||||
GHRepository[] batch = pager.next();
|
||||
for (GHRepository r : batch)
|
||||
r.root = root;
|
||||
return Arrays.asList(batch);
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
@@ -58,11 +93,26 @@ public abstract class GHPerson {
|
||||
|
||||
/**
|
||||
* Gravatar ID of this user, like 0cb9832a01c22c083390f3c5dcb64105
|
||||
*
|
||||
* @deprecated
|
||||
* No longer available in the v3 API.
|
||||
*/
|
||||
public String getGravatarId() {
|
||||
return gravatar_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string like 'https://secure.gravatar.com/avatar/0cb9832a01c22c083390f3c5dcb64105'
|
||||
* that indicates the avatar image URL.
|
||||
*/
|
||||
public String getAvatarUrl() {
|
||||
if (avatar_url!=null)
|
||||
return avatar_url;
|
||||
if (gravatar_id!=null)
|
||||
return "https://secure.gravatar.com/avatar/"+gravatar_id;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the login ID of this user, like 'kohsuke'
|
||||
*/
|
||||
|
||||
@@ -68,9 +68,11 @@ public class GHRepository {
|
||||
private String html_url; // this is the UI
|
||||
private GHUser owner; // not fully populated. beware.
|
||||
private boolean has_issues, has_wiki, fork, _private, has_downloads;
|
||||
private int watchers,forks,open_issues;
|
||||
private int watchers,forks,open_issues,size;
|
||||
private String created_at, pushed_at;
|
||||
private Map<Integer,GHMilestone> milestones = new HashMap<Integer, GHMilestone>();
|
||||
|
||||
private String master_branch;
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
@@ -162,6 +164,19 @@ public class GHRepository {
|
||||
return GitHub.parseDate(created_at);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the primary branch you'll configure in the "Admin > Options" config page.
|
||||
*
|
||||
* @return
|
||||
* This field is null until the user explicitly configures the master branch.
|
||||
*/
|
||||
public String getMasterBranch() {
|
||||
return master_branch;
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the collaborators on this repository.
|
||||
|
||||
@@ -44,13 +44,17 @@ import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Properties;
|
||||
import java.util.TimeZone;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.codehaus.jackson.JsonParseException;
|
||||
import org.codehaus.jackson.map.DeserializationConfig.Feature;
|
||||
import org.codehaus.jackson.map.ObjectMapper;
|
||||
import org.codehaus.jackson.map.introspect.VisibilityChecker.Std;
|
||||
@@ -170,7 +174,7 @@ public class GitHub {
|
||||
}
|
||||
|
||||
/*package*/ <T> T retrieveWithAuth(String tailApiUrl, Class<T> type) throws IOException {
|
||||
return retrieveWithAuth(tailApiUrl,type,"GET");
|
||||
return retrieveWithAuth(tailApiUrl, type, "GET");
|
||||
}
|
||||
|
||||
/*package*/ <T> T retrieveWithAuth3(String tailApiUrl, Class<T> type) throws IOException {
|
||||
@@ -187,32 +191,141 @@ public class GitHub {
|
||||
|
||||
private <T> T _retrieve(String tailApiUrl, Class<T> type, String method, boolean withAuth, ApiVersion v) throws IOException {
|
||||
while (true) {// loop while API rate limit is hit
|
||||
|
||||
HttpURLConnection uc = (HttpURLConnection) getApiURL(v,tailApiUrl).openConnection();
|
||||
|
||||
if (withAuth && this.oauthAccessToken == null)
|
||||
uc.setRequestProperty("Authorization", "Basic " + encodedAuthorization);
|
||||
|
||||
uc.setRequestMethod(method);
|
||||
if (method.equals("PUT")) {
|
||||
uc.setDoOutput(true);
|
||||
uc.setRequestProperty("Content-Length","0");
|
||||
uc.getOutputStream().close();
|
||||
}
|
||||
|
||||
HttpURLConnection uc = setupConnection(method, withAuth, getApiURL(v, tailApiUrl));
|
||||
try {
|
||||
InputStreamReader r = new InputStreamReader(uc.getInputStream(), "UTF-8");
|
||||
if (type==null) {
|
||||
String data = IOUtils.toString(r);
|
||||
return null;
|
||||
}
|
||||
return MAPPER.readValue(r,type);
|
||||
return parse(uc,type);
|
||||
} catch (IOException e) {
|
||||
handleApiError(e,uc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads pagenated resources.
|
||||
*
|
||||
* Every iterator call reports a new batch.
|
||||
*/
|
||||
/*package*/ <T> Iterator<T> retrievePaged(final String tailApiUrl, final Class<T> type, final boolean withAuth, final ApiVersion v) {
|
||||
return new Iterator<T>() {
|
||||
/**
|
||||
* The next batch to be returned from {@link #next()}.
|
||||
*/
|
||||
T next;
|
||||
/**
|
||||
* URL of the next resource to be retrieved, or null if no more data is available.
|
||||
*/
|
||||
URL url;
|
||||
|
||||
{
|
||||
try {
|
||||
url = getApiURL(v, tailApiUrl);
|
||||
} catch (IOException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
fetch();
|
||||
return next!=null;
|
||||
}
|
||||
|
||||
public T next() {
|
||||
fetch();
|
||||
T r = next;
|
||||
if (r==null) throw new NoSuchElementException();
|
||||
next = null;
|
||||
return r;
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private void fetch() {
|
||||
if (next!=null) return; // already fetched
|
||||
if (url==null) return; // no more data to fetch
|
||||
|
||||
try {
|
||||
while (true) {// loop while API rate limit is hit
|
||||
HttpURLConnection uc = setupConnection("GET", withAuth, url);
|
||||
try {
|
||||
next = parse(uc,type);
|
||||
assert next!=null;
|
||||
findNextURL(uc);
|
||||
return;
|
||||
} catch (IOException e) {
|
||||
handleApiError(e,uc);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Locate the next page from the pagination "Link" tag.
|
||||
*/
|
||||
private void findNextURL(HttpURLConnection uc) throws MalformedURLException {
|
||||
url = null; // start defensively
|
||||
String link = uc.getHeaderField("Link");
|
||||
if (link==null) return;
|
||||
|
||||
for (String token : link.split(", ")) {
|
||||
if (token.endsWith("rel=\"next\"")) {
|
||||
// found the next page. This should look something like
|
||||
// <https://api.github.com/repos?page=3&per_page=100>; rel="next"
|
||||
int idx = token.indexOf('>');
|
||||
url = new URL(token.substring(1,idx));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// no more "next" link. we are done.
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private HttpURLConnection setupConnection(String method, boolean withAuth, URL url) throws IOException {
|
||||
HttpURLConnection uc = (HttpURLConnection) url.openConnection();
|
||||
|
||||
if (withAuth && this.oauthAccessToken == null)
|
||||
uc.setRequestProperty("Authorization", "Basic " + encodedAuthorization);
|
||||
|
||||
uc.setRequestMethod(method);
|
||||
if (method.equals("PUT")) {
|
||||
uc.setDoOutput(true);
|
||||
uc.setRequestProperty("Content-Length","0");
|
||||
uc.getOutputStream().close();
|
||||
}
|
||||
uc.setRequestProperty("Accept-Encoding", "gzip");
|
||||
return uc;
|
||||
}
|
||||
|
||||
private <T> T parse(HttpURLConnection uc, Class<T> type) throws IOException {
|
||||
InputStreamReader r = null;
|
||||
try {
|
||||
r = new InputStreamReader(wrapStream(uc, uc.getInputStream()), "UTF-8");
|
||||
if (type==null) {
|
||||
String data = IOUtils.toString(r);
|
||||
return null;
|
||||
}
|
||||
return MAPPER.readValue(r,type);
|
||||
} finally {
|
||||
IOUtils.closeQuietly(r);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the "Content-Encoding" header.
|
||||
*/
|
||||
private InputStream wrapStream(HttpURLConnection uc, InputStream in) throws IOException {
|
||||
String encoding = uc.getContentEncoding();
|
||||
if (encoding==null) return in;
|
||||
if (encoding.equals("gzip")) return new GZIPInputStream(in);
|
||||
|
||||
throw new UnsupportedOperationException("Unexpected Content-Encoding: "+encoding);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the error is because of the API limit, wait 10 sec and return normally.
|
||||
* Otherwise throw an exception reporting an error.
|
||||
@@ -231,11 +344,15 @@ public class GitHub {
|
||||
if (e instanceof FileNotFoundException)
|
||||
throw e; // pass through 404 Not Found to allow the caller to handle it intelligently
|
||||
|
||||
InputStream es = uc.getErrorStream();
|
||||
if (es!=null)
|
||||
throw (IOException)new IOException(IOUtils.toString(es,"UTF-8")).initCause(e);
|
||||
else
|
||||
throw e;
|
||||
InputStream es = wrapStream(uc, uc.getErrorStream());
|
||||
try {
|
||||
if (es!=null)
|
||||
throw (IOException)new IOException(IOUtils.toString(es,"UTF-8")).initCause(e);
|
||||
else
|
||||
throw e;
|
||||
} finally {
|
||||
IOUtils.closeQuietly(es);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -245,7 +362,7 @@ public class GitHub {
|
||||
public GHMyself getMyself() throws IOException {
|
||||
requireCredential();
|
||||
|
||||
GHMyself u = retrieveWithAuth("/user/show", JsonMyself.class).user;
|
||||
GHMyself u = retrieveWithAuth3("/user", GHMyself.class);
|
||||
|
||||
u.root = this;
|
||||
users.put(u.getLogin(), u);
|
||||
@@ -259,17 +376,9 @@ public class GitHub {
|
||||
public GHUser getUser(String login) throws IOException {
|
||||
GHUser u = users.get(login);
|
||||
if (u == null) {
|
||||
|
||||
if (oauthAccessToken != null) {
|
||||
u = retrieve("/user/show/" + login, JsonUser.class).user;
|
||||
u.root = this;
|
||||
users.put(u.getLogin(), u);
|
||||
} else {
|
||||
u = retrieve("/user/show/" + login, JsonUser.class).user;
|
||||
u.root = this;
|
||||
users.put(login, u);
|
||||
}
|
||||
|
||||
u = retrieve3("/users/" + login, GHUser.class);
|
||||
u.root = this;
|
||||
users.put(u.getLogin(), u);
|
||||
}
|
||||
return u;
|
||||
}
|
||||
@@ -352,7 +461,7 @@ public class GitHub {
|
||||
*/
|
||||
public boolean isCredentialValid() throws IOException {
|
||||
try {
|
||||
retrieveWithAuth("/user/show",JsonUser.class);
|
||||
retrieveWithAuth3("/user",GHUser.class);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010, Kohsuke Kawaguchi
|
||||
*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
class JsonUser {
|
||||
public GHUser user;
|
||||
}
|
||||
@@ -7,10 +7,12 @@ import org.kohsuke.github.GHEventPayload;
|
||||
import org.kohsuke.github.GHHook;
|
||||
import org.kohsuke.github.GHBranch;
|
||||
import org.kohsuke.github.GHIssueState;
|
||||
import org.kohsuke.github.GHMyself;
|
||||
import org.kohsuke.github.GHOrganization;
|
||||
import org.kohsuke.github.GHOrganization.Permission;
|
||||
import org.kohsuke.github.GHRepository;
|
||||
import org.kohsuke.github.GHTeam;
|
||||
import org.kohsuke.github.GHUser;
|
||||
import org.kohsuke.github.GitHub;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -31,9 +33,18 @@ public class AppTest extends TestCase {
|
||||
public void testFetchPullRequest() throws Exception {
|
||||
GitHub gh = GitHub.connect();
|
||||
GHRepository r = gh.getOrganization("jenkinsci").getRepository("jenkins");
|
||||
assertEquals("master",r.getMasterBranch());
|
||||
r.getPullRequest(1);
|
||||
r.getPullRequests(GHIssueState.OPEN);
|
||||
}
|
||||
|
||||
public void tryGetMyself() throws Exception {
|
||||
GitHub hub = GitHub.connect();
|
||||
GHMyself me = hub.getMyself();
|
||||
System.out.println(me);
|
||||
GHUser u = hub.getUser("kohsuke2");
|
||||
System.out.println(u);
|
||||
}
|
||||
|
||||
public void tryOrgFork() throws Exception {
|
||||
GitHub gh = GitHub.connect();
|
||||
@@ -181,6 +192,15 @@ public class AppTest extends TestCase {
|
||||
System.out.println(hooks);
|
||||
}
|
||||
|
||||
public void testOrgRepositories() throws IOException {
|
||||
GitHub gitHub = GitHub.connect();
|
||||
GHOrganization j = gitHub.getOrganization("jenkinsci");
|
||||
long start = System.currentTimeMillis();
|
||||
Map<String, GHRepository> repos = j.getRepositories();
|
||||
long end = System.currentTimeMillis();
|
||||
System.out.printf("%d repositories in %dms\n",repos.size(),end-start);
|
||||
}
|
||||
|
||||
public void testOrganization() throws IOException {
|
||||
GitHub gitHub = GitHub.connect();
|
||||
GHOrganization j = gitHub.getOrganization("jenkinsci");
|
||||
|
||||
Reference in New Issue
Block a user