mirror of
https://github.com/jlengrand/github-api.git
synced 2026-03-11 00:11:25 +00:00
Compare commits
45 Commits
github-api
...
github-api
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2b9d47cea8 | ||
|
|
5db90d3fc4 | ||
|
|
372d5ff758 | ||
|
|
ebf39eaea1 | ||
|
|
556786f2e1 | ||
|
|
0f64994537 | ||
|
|
ac64c2022b | ||
|
|
9802132b6f | ||
|
|
4d6c5c14f1 | ||
|
|
d228a5fb93 | ||
|
|
7b46ef10c8 | ||
|
|
8eb9fba051 | ||
|
|
7b58182683 | ||
|
|
95fbf9274b | ||
|
|
09557dfa0f | ||
|
|
4029fcc1ca | ||
|
|
d6627b1e34 | ||
|
|
86d75fd767 | ||
|
|
e2220bb3b3 | ||
|
|
1a9b8bd1da | ||
|
|
9eda2d3f77 | ||
|
|
e6d59df705 | ||
|
|
db845850b2 | ||
|
|
d516597659 | ||
|
|
3af5a8145a | ||
|
|
31bebd4a7a | ||
|
|
ccb87258b0 | ||
|
|
2ab71e88bd | ||
|
|
78bd7585bb | ||
|
|
92cc81d9b8 | ||
|
|
97a1c741de | ||
|
|
077d693959 | ||
|
|
94831fc10a | ||
|
|
1f298a88b0 | ||
|
|
7e49946bed | ||
|
|
1e81ab1017 | ||
|
|
311180d12e | ||
|
|
0efb206881 | ||
|
|
0b92fa5615 | ||
|
|
a58a5b56b2 | ||
|
|
b9764c0a44 | ||
|
|
590f7ba511 | ||
|
|
449909b0e8 | ||
|
|
40780525f8 | ||
|
|
0e3707d1c3 |
6
pom.xml
6
pom.xml
@@ -3,11 +3,11 @@
|
||||
<parent>
|
||||
<groupId>org.kohsuke</groupId>
|
||||
<artifactId>pom</artifactId>
|
||||
<version>8</version>
|
||||
<version>10</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>github-api</artifactId>
|
||||
<version>1.56</version>
|
||||
<version>1.59</version>
|
||||
<name>GitHub API for Java</name>
|
||||
<url>http://github-api.kohsuke.org/</url>
|
||||
<description>GitHub API for Java</description>
|
||||
@@ -16,7 +16,7 @@
|
||||
<connection>scm:git:git@github.com/kohsuke/${project.artifactId}.git</connection>
|
||||
<developerConnection>scm:git:ssh://git@github.com/kohsuke/${project.artifactId}.git</developerConnection>
|
||||
<url>http://${project.artifactId}.kohsuke.org/</url>
|
||||
<tag>github-api-1.56</tag>
|
||||
<tag>HEAD</tag>
|
||||
</scm>
|
||||
|
||||
<distributionManagement>
|
||||
|
||||
@@ -21,6 +21,7 @@ public class GHAsset {
|
||||
private long download_count;
|
||||
private Date created_at;
|
||||
private Date updated_at;
|
||||
private String browser_download_url;
|
||||
|
||||
public String getContentType() {
|
||||
return content_type;
|
||||
@@ -80,6 +81,10 @@ public class GHAsset {
|
||||
return url;
|
||||
}
|
||||
|
||||
public String getBrowserDownloadUrl() {
|
||||
return browser_download_url;
|
||||
}
|
||||
|
||||
private void edit(String key, Object value) throws IOException {
|
||||
new Requester(root)._with(key, value).method("PATCH").to(getApiRoute());
|
||||
}
|
||||
|
||||
@@ -22,6 +22,15 @@ public class GHAuthorization {
|
||||
public static final String DELETE_REPO = "delete_repo";
|
||||
public static final String NOTIFICATIONS = "notifications";
|
||||
public static final String GIST = "gist";
|
||||
public static final String READ_HOOK = "read:repo_hook";
|
||||
public static final String WRITE_HOOK = "write:repo_hook";
|
||||
public static final String AMIN_HOOK = "admin:repo_hook";
|
||||
public static final String READ_ORG = "read:org";
|
||||
public static final String WRITE_ORG = "write:org";
|
||||
public static final String ADMIN_ORG = "admin:org";
|
||||
public static final String READ_KEY = "read:public_key";
|
||||
public static final String WRITE_KEY = "write:public_key";
|
||||
public static final String ADMIN_KEY = "admin:public_key";
|
||||
|
||||
private GitHub root;
|
||||
private int id;
|
||||
|
||||
@@ -68,9 +68,12 @@ public class GHCompare {
|
||||
public Commit[] getCommits() {
|
||||
return commits;
|
||||
}
|
||||
|
||||
public GHCommit.File[] getFiles() {
|
||||
return files;
|
||||
}
|
||||
|
||||
|
||||
public GHCompare wrap(GHRepository owner) {
|
||||
public GHCompare wrap(GHRepository owner) {
|
||||
this.owner = owner;
|
||||
for (Commit commit : commits) {
|
||||
commit.wrapUp(owner);
|
||||
|
||||
@@ -12,6 +12,8 @@ public enum GHEvent {
|
||||
COMMIT_COMMENT,
|
||||
CREATE,
|
||||
DELETE,
|
||||
DEPLOYMENT,
|
||||
DEPLOYMENT_STATUS,
|
||||
DOWNLOAD,
|
||||
FOLLOW,
|
||||
FORK,
|
||||
@@ -25,6 +27,8 @@ public enum GHEvent {
|
||||
PULL_REQUEST,
|
||||
PULL_REQUEST_REVIEW_COMMENT,
|
||||
PUSH,
|
||||
RELEASE,
|
||||
STATUS,
|
||||
TEAM_ADD,
|
||||
WATCH
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ public class GHIssue {
|
||||
return Enum.valueOf(GHIssueState.class, state.toUpperCase(Locale.ENGLISH));
|
||||
}
|
||||
|
||||
public Collection<Label> getLabels() {
|
||||
public Collection<Label> getLabels() throws IOException {
|
||||
if(labels == null){
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
@@ -163,6 +163,10 @@ public class GHIssue {
|
||||
new Requester(root)._with(key, value).method("PATCH").to(getApiRoute());
|
||||
}
|
||||
|
||||
private void editIssue(String key, Object value) throws IOException {
|
||||
new Requester(root)._with(key, value).method("PATCH").to(getIssuesApiRoute());
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes this issue.
|
||||
*/
|
||||
@@ -186,11 +190,11 @@ public class GHIssue {
|
||||
}
|
||||
|
||||
public void assignTo(GHUser user) throws IOException {
|
||||
edit("assignee",user.getLogin());
|
||||
editIssue("assignee",user.getLogin());
|
||||
}
|
||||
|
||||
public void setLabels(String... labels) throws IOException {
|
||||
edit("labels",labels);
|
||||
editIssue("labels",labels);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -222,7 +226,7 @@ public class GHIssue {
|
||||
return getIssuesApiRoute();
|
||||
}
|
||||
|
||||
private String getIssuesApiRoute() {
|
||||
protected String getIssuesApiRoute() {
|
||||
return "/repos/"+owner.getOwnerName()+"/"+owner.getName()+"/issues/"+number;
|
||||
}
|
||||
|
||||
|
||||
@@ -101,12 +101,24 @@ public class GHMyself extends GHUser {
|
||||
* Lists up all repositories this user owns (public and private).
|
||||
*
|
||||
* Unlike {@link #getAllRepositories()}, this does not wait until all the repositories are returned.
|
||||
* Repositories are returned by GitHub API with a 30 items per page.
|
||||
*/
|
||||
@Override
|
||||
public PagedIterable<GHRepository> listRepositories() {
|
||||
return listRepositories(30);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists up all the repositories this user owns (public and private) using the specified page size.
|
||||
*
|
||||
* @param pageSize size for each page of items returned by GitHub. Maximum page size is 100.
|
||||
*
|
||||
* Unlike {@link #getRepositories()}, this does not wait until all the repositories are returned.
|
||||
*/
|
||||
public PagedIterable<GHRepository> listRepositories(final int pageSize) {
|
||||
return new PagedIterable<GHRepository>() {
|
||||
public PagedIterator<GHRepository> iterator() {
|
||||
return new PagedIterator<GHRepository>(root.retrieve().asIterator("/user/repos", GHRepository[].class)) {
|
||||
return new PagedIterator<GHRepository>(root.retrieve().asIterator("/user/repos?per_page=" + pageSize, GHRepository[].class)) {
|
||||
@Override
|
||||
protected void wrapUp(GHRepository[] page) {
|
||||
for (GHRepository c : page)
|
||||
|
||||
@@ -52,6 +52,13 @@ public class GHPullRequest extends GHIssue {
|
||||
private String mergeable_state;
|
||||
private int changed_files;
|
||||
|
||||
/**
|
||||
* GitHub doesn't return some properties of {@link GHIssue} when requesting the GET on the 'pulls' API
|
||||
* route as opposed to 'issues' API route. This flag remembers whether we made the GET call on the 'issues' route
|
||||
* on this object to fill in those missing details
|
||||
*/
|
||||
private transient boolean fetchedIssueDetails;
|
||||
|
||||
|
||||
GHPullRequest wrapUp(GHRepository owner) {
|
||||
this.wrap(owner);
|
||||
@@ -120,11 +127,12 @@ public class GHPullRequest extends GHIssue {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Label> getLabels() {
|
||||
public Collection<Label> getLabels() throws IOException {
|
||||
fetchIssue();
|
||||
return super.getLabels();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public GHUser getClosedBy() {
|
||||
return null;
|
||||
}
|
||||
@@ -196,7 +204,7 @@ public class GHPullRequest extends GHIssue {
|
||||
return new PagedIterable<GHPullRequestCommitDetail>() {
|
||||
public PagedIterator<GHPullRequestCommitDetail> iterator() {
|
||||
return new PagedIterator<GHPullRequestCommitDetail>(root.retrieve().asIterator(
|
||||
String.format("%s/commits", getApiURL().getPath()),
|
||||
String.format("%s/commits", getApiURL()),
|
||||
GHPullRequestCommitDetail[].class)) {
|
||||
@Override
|
||||
protected void wrapUp(GHPullRequestCommitDetail[] page) {
|
||||
@@ -218,4 +226,10 @@ public class GHPullRequest extends GHIssue {
|
||||
new Requester(root).method("PUT").with("commit_message",msg).to(getApiRoute()+"/merge");
|
||||
}
|
||||
|
||||
private void fetchIssue() throws IOException {
|
||||
if (!fetchedIssueDetails) {
|
||||
new Requester(root).to(getIssuesApiRoute(), this);
|
||||
fetchedIssueDetails = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
@@ -8,6 +9,7 @@ import java.net.URL;
|
||||
* @author Michael Clarke
|
||||
*/
|
||||
public class GHRef {
|
||||
/*package almost final*/ GitHub root;
|
||||
|
||||
private String ref, url;
|
||||
private GHObject object;
|
||||
@@ -33,6 +35,48 @@ public class GHRef {
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates this ref to the specified commit.
|
||||
*
|
||||
* @param sha
|
||||
* The SHA1 value to set this reference to
|
||||
*/
|
||||
public void updateTo(String sha) throws IOException {
|
||||
updateTo(sha, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates this ref to the specified commit.
|
||||
*
|
||||
* @param sha
|
||||
* The SHA1 value to set this reference to
|
||||
* @param force
|
||||
* Whether or not to force this ref update.
|
||||
*/
|
||||
public void updateTo(String sha, Boolean force) throws IOException {
|
||||
new Requester(root)
|
||||
.with("sha", sha).with("force", force).method("PATCH").to(url, GHRef.class).wrap(root);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes this ref from the repository using the GitHub API.
|
||||
*/
|
||||
public void delete() throws IOException {
|
||||
new Requester(root).method("DELETE").to(url);
|
||||
}
|
||||
|
||||
/*package*/ GHRef wrap(GitHub root) {
|
||||
this.root = root;
|
||||
return this;
|
||||
}
|
||||
|
||||
/*package*/ static GHRef[] wrap(GHRef[] in, GitHub root) {
|
||||
for (GHRef r : in) {
|
||||
r.wrap(root);
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
|
||||
public static class GHObject {
|
||||
private String type, sha, url;
|
||||
|
||||
@@ -57,7 +57,7 @@ import static java.util.Arrays.*;
|
||||
public class GHRepository {
|
||||
/*package almost final*/ GitHub root;
|
||||
|
||||
private String description, homepage, name;
|
||||
private String description, homepage, name, full_name;
|
||||
private String url; // this is the API url
|
||||
private String html_url; // this is the UI
|
||||
private String git_url, ssh_url, clone_url, svn_url;
|
||||
@@ -131,6 +131,13 @@ public class GHRepository {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Full repository name including the owner or organization. For example 'jenkinsci/jenkins' in case of http://github.com/jenkinsci/jenkins
|
||||
*/
|
||||
public String getFullName() {
|
||||
return full_name;
|
||||
}
|
||||
|
||||
public boolean hasPullAccess() {
|
||||
return permissions!=null && permissions.pull;
|
||||
}
|
||||
@@ -206,7 +213,7 @@ public class GHRepository {
|
||||
*/
|
||||
public GHRef createRef(String name, String sha) throws IOException {
|
||||
return new Requester(root)
|
||||
.with("ref", name).with("sha", sha).method("POST").to(getApiTailUrl("git/refs"), GHRef.class);
|
||||
.with("ref", name).with("sha", sha).method("POST").to(getApiTailUrl("git/refs"), GHRef.class).wrap(root);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -507,7 +514,7 @@ public class GHRepository {
|
||||
@Override
|
||||
protected void wrapUp(GHPullRequest[] page) {
|
||||
for (GHPullRequest pr : page)
|
||||
pr.wrap(GHRepository.this);
|
||||
pr.wrapUp(GHRepository.this);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -579,17 +586,17 @@ public class GHRepository {
|
||||
* @throws IOException on failure communicating with GitHub
|
||||
*/
|
||||
public GHRef[] getRefs() throws IOException {
|
||||
return root.retrieve().to(String.format("/repos/%s/%s/git/refs", owner.login, name), GHRef[].class);
|
||||
return GHRef.wrap(root.retrieve().to(String.format("/repos/%s/%s/git/refs", owner.login, name), GHRef[].class),root);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrienved all refs of the given type for the current GitHub repository.
|
||||
* Retrieves all refs of the given type for the current GitHub repository.
|
||||
* @param refType the type of reg to search for e.g. <tt>tags</tt> or <tt>commits</tt>
|
||||
* @return an array of all refs matching the request type
|
||||
* @throws IOException on failure communicating with GitHub, potentially due to an invalid ref type being requested
|
||||
*/
|
||||
public GHRef[] getRefs(String refType) throws IOException {
|
||||
return root.retrieve().to(String.format("/repos/%s/%s/git/refs/%s", owner.login, name, refType), GHRef[].class);
|
||||
return GHRef.wrap(root.retrieve().to(String.format("/repos/%s/%s/git/refs/%s", owner.login, name, refType), GHRef[].class),root);
|
||||
}
|
||||
/**
|
||||
* Retrive a ref of the given type for the current GitHub repository.
|
||||
@@ -602,7 +609,7 @@ public class GHRepository {
|
||||
* invalid ref type being requested
|
||||
*/
|
||||
public GHRef getRef(String refName) throws IOException {
|
||||
return root.retrieve().to(String.format("/repos/%s/%s/git/refs/%s", owner.login, name, refName), GHRef.class);
|
||||
return root.retrieve().to(String.format("/repos/%s/%s/git/refs/%s", owner.login, name, refName), GHRef.class).wrap(root);
|
||||
}
|
||||
/**
|
||||
* Gets a commit object in this repository.
|
||||
|
||||
@@ -15,6 +15,7 @@ import java.util.TreeMap;
|
||||
public class GHTeam {
|
||||
private String name,permission;
|
||||
private int id;
|
||||
private GHOrganization organization; // populated by GET /user/teams where Teams+Orgs are returned together
|
||||
|
||||
protected /*final*/ GHOrganization org;
|
||||
|
||||
@@ -23,6 +24,11 @@ public class GHTeam {
|
||||
return this;
|
||||
}
|
||||
|
||||
/*package*/ GHTeam wrapUp(GitHub root) { // auto-wrapUp when organization is known from GET /user/teams
|
||||
this.organization.wrapUp(root);
|
||||
return wrapUp(organization);
|
||||
}
|
||||
|
||||
/*package*/ static GHTeam[] wrapUp(GHTeam[] teams, GHOrganization owner) {
|
||||
for (GHTeam t : teams) {
|
||||
t.wrapUp(owner);
|
||||
@@ -72,9 +78,13 @@ public class GHTeam {
|
||||
|
||||
/**
|
||||
* Adds a member to the team.
|
||||
*
|
||||
* The user will be invited to the organization if required.
|
||||
*
|
||||
* @since 1.59
|
||||
*/
|
||||
public void add(GHUser u) throws IOException {
|
||||
org.root.retrieve().method("PUT").to(api("/members/" + u.getLogin()), null);
|
||||
org.root.retrieve().method("PUT").to(api("/memberships/" + u.getLogin()), null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,4 +105,8 @@ public class GHTeam {
|
||||
private String api(String tail) {
|
||||
return "/teams/"+id+tail;
|
||||
}
|
||||
|
||||
public GHOrganization getOrganization() {
|
||||
return org;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,10 +23,9 @@
|
||||
*/
|
||||
package org.kohsuke.github;
|
||||
|
||||
import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.*;
|
||||
import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.ANY;
|
||||
import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.NONE;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
@@ -38,13 +37,13 @@ import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
@@ -71,13 +70,6 @@ public class GitHub {
|
||||
|
||||
private HttpConnector connector = HttpConnector.DEFAULT;
|
||||
|
||||
/**
|
||||
* Connects to GitHub.com
|
||||
*/
|
||||
private GitHub(String login, String oauthAccessToken, String password) throws IOException {
|
||||
this (GITHUB_URL, login, oauthAccessToken, password);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a client API root object.
|
||||
*
|
||||
@@ -111,10 +103,13 @@ public class GitHub {
|
||||
* Secret OAuth token.
|
||||
* @param password
|
||||
* User's password. Always used in conjunction with the {@code login} parameter
|
||||
* @param connector
|
||||
* HttpConnector to use. Pass null to use default connector.
|
||||
*/
|
||||
private GitHub(String apiUrl, String login, String oauthAccessToken, String password) throws IOException {
|
||||
/* package */ GitHub(String apiUrl, String login, String oauthAccessToken, String password, HttpConnector connector) throws IOException {
|
||||
if (apiUrl.endsWith("/")) apiUrl = apiUrl.substring(0, apiUrl.length()-1); // normalize
|
||||
this.apiUrl = apiUrl;
|
||||
if (null != connector) this.connector = connector;
|
||||
|
||||
if (oauthAccessToken!=null) {
|
||||
encodedAuthorization = "token "+oauthAccessToken;
|
||||
@@ -133,18 +128,10 @@ public class GitHub {
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the credential from "~/.github"
|
||||
* Obtains the credential from "~/.github" or from the System Environment Properties.
|
||||
*/
|
||||
public static GitHub connect() throws IOException {
|
||||
Properties props = new Properties();
|
||||
File homeDir = new File(System.getProperty("user.home"));
|
||||
FileInputStream in = new FileInputStream(new File(homeDir, ".github"));
|
||||
try {
|
||||
props.load(in);
|
||||
} finally {
|
||||
IOUtils.closeQuietly(in);
|
||||
}
|
||||
return new GitHub(GITHUB_URL,props.getProperty("login"), props.getProperty("oauth"),props.getProperty("password"));
|
||||
return GitHubBuilder.fromCredentials().build();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -156,15 +143,15 @@ public class GitHub {
|
||||
* For historical reasons, this parameter still accepts the bare domain name, but that's considered deprecated.
|
||||
*/
|
||||
public static GitHub connectToEnterprise(String apiUrl, String oauthAccessToken) throws IOException {
|
||||
return connectUsingOAuth(apiUrl, oauthAccessToken);
|
||||
return new GitHubBuilder().withEndpoint(apiUrl).withOAuthToken(oauthAccessToken).build();
|
||||
}
|
||||
|
||||
public static GitHub connectToEnterprise(String apiUrl, String login, String password) throws IOException {
|
||||
return new GitHub(apiUrl, login, null, password);
|
||||
return new GitHubBuilder().withEndpoint(apiUrl).withPassword(login, password).build();
|
||||
}
|
||||
|
||||
public static GitHub connect(String login, String oauthAccessToken) throws IOException {
|
||||
return new GitHub(login,oauthAccessToken,null);
|
||||
return new GitHubBuilder().withOAuthToken(oauthAccessToken, login).build();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -173,19 +160,19 @@ public class GitHub {
|
||||
* Use {@link #connectUsingPassword(String, String)} or {@link #connectUsingOAuth(String)}.
|
||||
*/
|
||||
public static GitHub connect(String login, String oauthAccessToken, String password) throws IOException {
|
||||
return new GitHub(login,oauthAccessToken,password);
|
||||
return new GitHubBuilder().withOAuthToken(oauthAccessToken, login).withPassword(login, password).build();
|
||||
}
|
||||
|
||||
public static GitHub connectUsingPassword(String login, String password) throws IOException {
|
||||
return new GitHub(login,null,password);
|
||||
return new GitHubBuilder().withPassword(login, password).build();
|
||||
}
|
||||
|
||||
public static GitHub connectUsingOAuth(String oauthAccessToken) throws IOException {
|
||||
return new GitHub(null, oauthAccessToken, null);
|
||||
return new GitHubBuilder().withOAuthToken(oauthAccessToken).build();
|
||||
}
|
||||
|
||||
public static GitHub connectUsingOAuth(String githubServer, String oauthAccessToken) throws IOException {
|
||||
return new GitHub(githubServer,null, oauthAccessToken,null);
|
||||
return new GitHubBuilder().withEndpoint(githubServer).withOAuthToken(oauthAccessToken).build();
|
||||
}
|
||||
/**
|
||||
* Connects to GitHub anonymously.
|
||||
@@ -193,7 +180,7 @@ public class GitHub {
|
||||
* All operations that requires authentication will fail.
|
||||
*/
|
||||
public static GitHub connectAnonymously() throws IOException {
|
||||
return new GitHub(null,null,null);
|
||||
return new GitHubBuilder().build();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -280,6 +267,15 @@ public class GitHub {
|
||||
return u;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* clears all cached data in order for external changes (modifications and del
|
||||
*/
|
||||
public void refreshCache() {
|
||||
users.clear();
|
||||
orgs.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Interns the given {@link GHUser}.
|
||||
*/
|
||||
@@ -328,11 +324,32 @@ public class GitHub {
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets complete map of organizations/teams that current user belongs to.
|
||||
*
|
||||
* Leverages the new GitHub API /user/teams made available recently to
|
||||
* get in a single call the complete set of organizations, teams and permissions
|
||||
* in a single call.
|
||||
*/
|
||||
public Map<String, Set<GHTeam>> getMyTeams() throws IOException {
|
||||
Map<String, Set<GHTeam>> allMyTeams = new HashMap<String, Set<GHTeam>>();
|
||||
for (GHTeam team : retrieve().to("/user/teams", GHTeam[].class)) {
|
||||
team.wrapUp(this);
|
||||
String orgLogin = team.getOrganization().getLogin();
|
||||
Set<GHTeam> teamsPerOrg = allMyTeams.get(orgLogin);
|
||||
if (teamsPerOrg == null) {
|
||||
teamsPerOrg = new HashSet<GHTeam>();
|
||||
}
|
||||
teamsPerOrg.add(team);
|
||||
allMyTeams.put(orgLogin, teamsPerOrg);
|
||||
}
|
||||
return allMyTeams;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public events visible to you. Equivalent of what's displayed on https://github.com/
|
||||
*/
|
||||
public List<GHEventInfo> getEvents() throws IOException {
|
||||
// TODO: pagination
|
||||
GHEventInfo[] events = retrieve().to("/events", GHEventInfo[].class);
|
||||
for (GHEventInfo e : events)
|
||||
e.wrapUp(this);
|
||||
@@ -442,5 +459,5 @@ public class GitHub {
|
||||
MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
}
|
||||
|
||||
private static final String GITHUB_URL = "https://api.github.com";
|
||||
/* package */ static final String GITHUB_URL = "https://api.github.com";
|
||||
}
|
||||
|
||||
159
src/main/java/org/kohsuke/github/GitHubBuilder.java
Normal file
159
src/main/java/org/kohsuke/github/GitHubBuilder.java
Normal file
@@ -0,0 +1,159 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* @since 1.59
|
||||
*/
|
||||
public class GitHubBuilder {
|
||||
private String endpoint = GitHub.GITHUB_URL;
|
||||
|
||||
// default scoped so unit tests can read them.
|
||||
/* private */ String user;
|
||||
/* private */ String password;
|
||||
/* private */ String oauthToken;
|
||||
|
||||
private HttpConnector connector;
|
||||
|
||||
public GitHubBuilder() {
|
||||
}
|
||||
|
||||
/**
|
||||
* First check if the credentials are configured using the ~/.github properties file.
|
||||
*
|
||||
* If no user is specified it means there is no configuration present so check the environment instead.
|
||||
*
|
||||
* If there is still no user it means there are no credentials defined and throw an IOException.
|
||||
*
|
||||
* @return the configured Builder from credentials defined on the system or in the environment.
|
||||
*
|
||||
* @throws IOException If there are no credentials defined in the ~/.github properties file or the process environment.
|
||||
*/
|
||||
public static GitHubBuilder fromCredentials() throws IOException {
|
||||
|
||||
GitHubBuilder builder;
|
||||
try {
|
||||
builder = fromPropertyFile();
|
||||
|
||||
if (builder.user != null)
|
||||
return builder;
|
||||
else {
|
||||
|
||||
// this is the case where the ~/.github file exists but has no content.
|
||||
|
||||
builder = fromEnvironment();
|
||||
|
||||
if (builder.user != null)
|
||||
return builder;
|
||||
else
|
||||
throw new IOException("Failed to resolve credentials from ~/.github or the environment.");
|
||||
}
|
||||
|
||||
} catch (FileNotFoundException e) {
|
||||
builder = fromEnvironment();
|
||||
|
||||
if (builder.user != null)
|
||||
return builder;
|
||||
else
|
||||
throw new IOException("Failed to resolve credentials from ~/.github or the environment.", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static GitHubBuilder fromEnvironment(String loginVariableName, String passwordVariableName, String oauthVariableName) throws IOException {
|
||||
|
||||
|
||||
Properties env = new Properties();
|
||||
|
||||
Object loginValue = System.getenv(loginVariableName);
|
||||
|
||||
if (loginValue != null)
|
||||
env.put("login", loginValue);
|
||||
|
||||
Object passwordValue = System.getenv(passwordVariableName);
|
||||
|
||||
if (passwordValue != null)
|
||||
env.put("password", passwordValue);
|
||||
|
||||
Object oauthValue = System.getenv(oauthVariableName);
|
||||
|
||||
if (oauthValue != null)
|
||||
env.put("oauth", oauthValue);
|
||||
|
||||
return fromProperties(env);
|
||||
|
||||
}
|
||||
|
||||
public static GitHubBuilder fromEnvironment() throws IOException {
|
||||
|
||||
Properties props = new Properties();
|
||||
|
||||
Map<String, String> env = System.getenv();
|
||||
|
||||
for (Map.Entry<String, String> element : env.entrySet()) {
|
||||
|
||||
props.put(element.getKey(), element.getValue());
|
||||
}
|
||||
|
||||
return fromProperties(props);
|
||||
}
|
||||
|
||||
public static GitHubBuilder fromPropertyFile() throws IOException {
|
||||
File homeDir = new File(System.getProperty("user.home"));
|
||||
File propertyFile = new File(homeDir, ".github");
|
||||
return fromPropertyFile(propertyFile.getPath());
|
||||
}
|
||||
|
||||
public static GitHubBuilder fromPropertyFile(String propertyFileName) throws IOException {
|
||||
Properties props = new Properties();
|
||||
FileInputStream in = null;
|
||||
try {
|
||||
in = new FileInputStream(propertyFileName);
|
||||
props.load(in);
|
||||
} finally {
|
||||
IOUtils.closeQuietly(in);
|
||||
}
|
||||
|
||||
return fromProperties(props);
|
||||
}
|
||||
|
||||
public static GitHubBuilder fromProperties(Properties props) {
|
||||
GitHubBuilder self = new GitHubBuilder();
|
||||
self.withOAuthToken(props.getProperty("oauth"), props.getProperty("login"));
|
||||
self.withPassword(props.getProperty("login"), props.getProperty("password"));
|
||||
return self;
|
||||
}
|
||||
|
||||
public GitHubBuilder withEndpoint(String endpoint) {
|
||||
this.endpoint = endpoint;
|
||||
return this;
|
||||
}
|
||||
public GitHubBuilder withPassword(String user, String password) {
|
||||
this.user = user;
|
||||
this.password = password;
|
||||
return this;
|
||||
}
|
||||
public GitHubBuilder withOAuthToken(String oauthToken) {
|
||||
return withOAuthToken(oauthToken, null);
|
||||
}
|
||||
public GitHubBuilder withOAuthToken(String oauthToken, String user) {
|
||||
this.oauthToken = oauthToken;
|
||||
this.user = user;
|
||||
return this;
|
||||
}
|
||||
public GitHubBuilder withConnector(HttpConnector connector) {
|
||||
this.connector = connector;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GitHub build() throws IOException {
|
||||
return new GitHub(endpoint, user, oauthToken, password, connector);
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,7 @@ import java.io.InputStreamReader;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.io.Reader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
@@ -47,10 +48,14 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
|
||||
/**
|
||||
* A builder pattern for making HTTP call and parsing its output.
|
||||
*
|
||||
@@ -177,7 +182,26 @@ class Requester {
|
||||
buildRequest(uc);
|
||||
|
||||
try {
|
||||
return parse(uc,type,instance);
|
||||
T result = parse(uc, type, instance);
|
||||
if (type != null && type.isArray()) { // we might have to loop for pagination - done through recursion
|
||||
final String links = uc.getHeaderField("link");
|
||||
if (links != null && links.contains("rel=\"next\"")) {
|
||||
Pattern nextLinkPattern = Pattern.compile(".*<(.*)>; rel=\"next\"");
|
||||
Matcher nextLinkMatcher = nextLinkPattern.matcher(links);
|
||||
if (nextLinkMatcher.find()) {
|
||||
final String link = nextLinkMatcher.group(1);
|
||||
T nextResult = _to(link, type, instance);
|
||||
|
||||
final int resultLength = Array.getLength(result);
|
||||
final int nextResultLength = Array.getLength(nextResult);
|
||||
T concatResult = (T) Array.newInstance(type.getComponentType(), resultLength + nextResultLength);
|
||||
System.arraycopy(result, 0, concatResult, 0, resultLength);
|
||||
System.arraycopy(nextResult, 0, concatResult, resultLength, nextResultLength);
|
||||
result = concatResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
} catch (IOException e) {
|
||||
handleApiError(e,uc);
|
||||
}
|
||||
|
||||
@@ -3,19 +3,13 @@ package org.kohsuke.github;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import org.junit.Assume;
|
||||
import org.junit.Test;
|
||||
import org.kohsuke.github.GHCommit.File;
|
||||
@@ -24,8 +18,6 @@ import org.kohsuke.github.GHOrganization.Permission;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Unit test for simple App.
|
||||
*/
|
||||
@@ -170,6 +162,35 @@ public class AppTest extends AbstractGitHubApiTestBase {
|
||||
System.out.println(org);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMyTeamsContainsAllMyOrganizations() throws IOException {
|
||||
Map<String, Set<GHTeam>> teams = gitHub.getMyTeams();
|
||||
Map<String, GHOrganization> myOrganizations = gitHub.getMyOrganizations();
|
||||
assertEquals(teams.keySet(), myOrganizations.keySet());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMyTeamsShouldIncludeMyself() throws IOException {
|
||||
Map<String, Set<GHTeam>> teams = gitHub.getMyTeams();
|
||||
for (Entry<String, Set<GHTeam>> teamsPerOrg : teams.entrySet()) {
|
||||
String organizationName = teamsPerOrg.getKey();
|
||||
for (GHTeam team : teamsPerOrg.getValue()) {
|
||||
String teamName = team.getName();
|
||||
assertTrue("Team " + teamName + " in organization " + organizationName
|
||||
+ " does not contain myself",
|
||||
shouldBelongToTeam(organizationName, teamName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldBelongToTeam(String organizationName, String teamName) throws IOException {
|
||||
GHOrganization org = gitHub.getOrganization(organizationName);
|
||||
assertNotNull(org);
|
||||
GHTeam team = org.getTeamByName(teamName);
|
||||
assertNotNull(team);
|
||||
return team.hasMember(gitHub.getMyself());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFetchPullRequest() throws Exception {
|
||||
GHRepository r = gitHub.getOrganization("jenkinsci").getRepository("jenkins");
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
@@ -21,4 +28,85 @@ public class GitHubTest extends TestCase {
|
||||
GitHub hub = GitHub.connectUsingPassword("kohsuke", "bogus");
|
||||
assertEquals("https://api.github.com/test", hub.getApiURL("/test").toString());
|
||||
}
|
||||
|
||||
public void testGitHubBuilderFromEnvironment() throws IOException {
|
||||
|
||||
Map<String, String>props = new HashMap<String, String>();
|
||||
|
||||
props.put("login", "bogus");
|
||||
props.put("oauth", "bogus");
|
||||
props.put("password", "bogus");
|
||||
|
||||
setupEnvironment(props);
|
||||
|
||||
GitHubBuilder builder = GitHubBuilder.fromEnvironment();
|
||||
|
||||
assertEquals("bogus", builder.user);
|
||||
assertEquals("bogus", builder.oauthToken);
|
||||
assertEquals("bogus", builder.password);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Copied from StackOverflow: http://stackoverflow.com/a/7201825/2336755
|
||||
*
|
||||
* This allows changing the in memory process environment.
|
||||
*
|
||||
* Its used to wire in values for the github credentials to test that the GitHubBuilder works properly to resolve them.
|
||||
*/
|
||||
private void setupEnvironment(Map<String, String> newenv) {
|
||||
try
|
||||
{
|
||||
Class<?> processEnvironmentClass = Class.forName("java.lang.ProcessEnvironment");
|
||||
Field theEnvironmentField = processEnvironmentClass.getDeclaredField("theEnvironment");
|
||||
theEnvironmentField.setAccessible(true);
|
||||
Map<String, String> env = (Map<String, String>) theEnvironmentField.get(null);
|
||||
env.putAll(newenv);
|
||||
Field theCaseInsensitiveEnvironmentField = processEnvironmentClass.getDeclaredField("theCaseInsensitiveEnvironment");
|
||||
theCaseInsensitiveEnvironmentField.setAccessible(true);
|
||||
Map<String, String> cienv = (Map<String, String>) theCaseInsensitiveEnvironmentField.get(null);
|
||||
cienv.putAll(newenv);
|
||||
}
|
||||
catch (NoSuchFieldException e)
|
||||
{
|
||||
try {
|
||||
Class[] classes = Collections.class.getDeclaredClasses();
|
||||
Map<String, String> env = System.getenv();
|
||||
for(Class cl : classes) {
|
||||
if("java.util.Collections$UnmodifiableMap".equals(cl.getName())) {
|
||||
Field field = cl.getDeclaredField("m");
|
||||
field.setAccessible(true);
|
||||
Object obj = field.get(env);
|
||||
Map<String, String> map = (Map<String, String>) obj;
|
||||
map.clear();
|
||||
map.putAll(newenv);
|
||||
}
|
||||
}
|
||||
} catch (Exception e2) {
|
||||
e2.printStackTrace();
|
||||
}
|
||||
} catch (Exception e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void testGitHubBuilderFromCustomEnvironment() throws IOException {
|
||||
|
||||
Map<String, String>props = new HashMap<String, String>();
|
||||
|
||||
props.put("customLogin", "bogusLogin");
|
||||
props.put("customOauth", "bogusOauth");
|
||||
props.put("customPassword", "bogusPassword");
|
||||
|
||||
setupEnvironment(props);
|
||||
|
||||
GitHubBuilder builder = GitHubBuilder.fromEnvironment("customLogin", "customPassword", "customOauth");
|
||||
|
||||
assertEquals("bogusLogin", builder.user);
|
||||
assertEquals("bogusOauth", builder.oauthToken);
|
||||
assertEquals("bogusPassword", builder.password);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,18 +1,51 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
public class PullRequestTest extends AbstractGitHubApiTestBase {
|
||||
@Test
|
||||
public void createPullRequest() throws Exception {
|
||||
GHRepository j = gitHub.getOrganization("github-api-test-org").getRepository("jenkins");
|
||||
String name = rnd.next();
|
||||
GHPullRequest p = j.createPullRequest(name, "stable", "master", "## test");
|
||||
GHPullRequest p = getRepository().createPullRequest(name, "stable", "master", "## test");
|
||||
System.out.println(p.getUrl());
|
||||
assertEquals(name, p.getTitle());
|
||||
p.close();
|
||||
}
|
||||
|
||||
@Test // Requires push access to the test repo to pass
|
||||
public void setLabels() throws Exception {
|
||||
GHPullRequest p = getRepository().createPullRequest(rnd.next(), "stable", "master", "## test");
|
||||
String label = rnd.next();
|
||||
p.setLabels(label);
|
||||
|
||||
Collection<GHIssue.Label> labels = getRepository().getPullRequest(p.getNumber()).getLabels();
|
||||
assertEquals(1, labels.size());
|
||||
assertEquals(label, labels.iterator().next().getName());
|
||||
}
|
||||
|
||||
@Test // Requires push access to the test repo to pass
|
||||
public void setAssignee() throws Exception {
|
||||
GHPullRequest p = getRepository().createPullRequest(rnd.next(), "stable", "master", "## test");
|
||||
GHMyself user = gitHub.getMyself();
|
||||
p.assignTo(user);
|
||||
|
||||
assertEquals(user, getRepository().getPullRequest(p.getNumber()).getAssignee());
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanUp() throws Exception {
|
||||
for (GHPullRequest pr : getRepository().getPullRequests(GHIssueState.OPEN)) {
|
||||
pr.close();
|
||||
}
|
||||
}
|
||||
|
||||
private GHRepository getRepository() throws IOException {
|
||||
return gitHub.getOrganization("github-api-test-org").getRepository("jenkins");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user