Compare commits

...

33 Commits

Author SHA1 Message Date
Kohsuke Kawaguchi
dafb50d6a9 [maven-release-plugin] prepare release github-api-1.30 2012-08-28 09:43:14 -07:00
Kohsuke Kawaguchi
13c59b6618 Merge branch 'pull-15' 2012-08-28 09:41:59 -07:00
Kohsuke Kawaguchi
8f95c4f179 Massaging the pull request 15.
- we need to maintain the binary compatibility, so I reverted
  getPullRequests and added listPullRequests that exposes PagedIterable.

- Made PagedIterator expose asList.
2012-08-28 09:39:49 -07:00
Aurélien Thieriot
9fd34aec7f Paging GHPullRequests getter and allow to transform iterator to a list 2012-08-12 13:01:42 +02:00
Kohsuke Kawaguchi
17c7a3e7c5 [maven-release-plugin] prepare for next development iteration 2012-06-18 12:51:29 -07:00
Kohsuke Kawaguchi
40a8c110bf [maven-release-plugin] prepare release github-api-1.29 2012-06-18 12:51:25 -07:00
Kohsuke Kawaguchi
c9cd0a4d1f added a simple CRUD test 2012-06-18 12:50:47 -07:00
Kohsuke Kawaguchi
45eae77f8f added missing repository delete operation 2012-06-18 12:50:40 -07:00
Kohsuke Kawaguchi
926202900c fixed a bug in editing the repository definition 2012-06-18 12:37:27 -07:00
Kohsuke Kawaguchi
21aa669503 redundant test case 2012-06-18 12:24:30 -07:00
Kohsuke Kawaguchi
dee28e7a7a Doc says this is asynchronous 2012-06-18 12:23:48 -07:00
Kohsuke Kawaguchi
c8f46a3666 needs to wrap up 2012-06-18 12:23:34 -07:00
Kohsuke Kawaguchi
ba7fe10a08 bug fix 2012-06-18 12:23:20 -07:00
Kohsuke Kawaguchi
9e9db72878 [maven-release-plugin] prepare for next development iteration 2012-06-13 08:27:58 -07:00
Kohsuke Kawaguchi
69a87e2ab7 [maven-release-plugin] prepare release github-api-1.28 2012-06-13 08:27:53 -07:00
Kohsuke Kawaguchi
8ec2686e72 getName() is null with shallow retrieval 2012-06-13 08:12:18 -07:00
Kohsuke Kawaguchi
d034ca4d1f removed unused V3 API 2012-06-13 08:10:01 -07:00
Kohsuke Kawaguchi
61cf71fd67 [maven-release-plugin] prepare for next development iteration 2012-06-12 14:26:30 -07:00
Kohsuke Kawaguchi
63dd1330e9 [maven-release-plugin] prepare release github-api-1.27 2012-06-12 14:26:25 -07:00
Kohsuke Kawaguchi
4411650c5a additional tweaks 2012-06-12 14:25:08 -07:00
Kohsuke Kawaguchi
1c15751949 Removing pointless '3' suffix in the method names. 2012-06-12 14:21:43 -07:00
Kohsuke Kawaguchi
82acf4f107 Now that everything is V3 API, there's no need for such enum. 2012-06-12 14:20:52 -07:00
Kohsuke Kawaguchi
5525ae8921 Removed unused JSON databinding classes 2012-06-12 14:16:59 -07:00
Kohsuke Kawaguchi
b5f7208b0d Removed v2 API usage and switched to v3.
https://github.com/kohsuke/github-api/issues/8
2012-06-12 14:07:27 -07:00
Kohsuke Kawaguchi
3b5bc98053 [maven-release-plugin] prepare for next development iteration 2012-06-04 10:09:15 -07:00
Kohsuke Kawaguchi
3a9ade667a [maven-release-plugin] prepare release github-api-1.26 2012-06-04 10:09:10 -07:00
Kohsuke Kawaguchi
057c32d410 added API to retrieve rate limit. 2012-06-04 10:08:31 -07:00
Kohsuke Kawaguchi
33657c9c92 made the authentication header optional.
This was needed now that GHPerson.getRepository() always try with authentication on, and it'll break if logged in anonymously
2012-06-04 10:05:03 -07:00
Kohsuke Kawaguchi
4c199256a5 [maven-release-plugin] prepare for next development iteration 2012-05-21 22:42:24 -07:00
Kohsuke Kawaguchi
9e62776905 [maven-release-plugin] prepare release github-api-1.25 2012-05-21 22:42:19 -07:00
Kohsuke Kawaguchi
9ba74b945d added permission check methods 2012-05-21 22:40:43 -07:00
Kohsuke Kawaguchi
66656ce612 when authenticated, repository returns additional information, so always send in a credential when one is available 2012-05-21 22:37:48 -07:00
Kohsuke Kawaguchi
73a20ad829 [maven-release-plugin] prepare for next development iteration 2012-05-21 22:16:52 -07:00
33 changed files with 386 additions and 656 deletions

View File

@@ -7,7 +7,7 @@
</parent>
<artifactId>github-api</artifactId>
<version>1.24</version>
<version>1.30</version>
<name>GitHub API for Java</name>
<url>http://github-api.kohsuke.org/</url>
<description>GitHub API for Java</description>

View File

@@ -1,24 +0,0 @@
package org.kohsuke.github;
/**
* Different API versions.
*
* @author Kohsuke Kawaguchi
*/
enum ApiVersion {
V2("https://?/api/v2/json"),
V3("https://api.?");
final String templateUrl;
ApiVersion(String templateUrl) {
this.templateUrl = templateUrl;
}
public String getApiVersionBaseUrl(String githubServer) {
return templateUrl.replaceFirst("\\?", githubServer);
}
}

View File

@@ -7,8 +7,6 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static org.kohsuke.github.ApiVersion.V3;
/**
* A commit in a repository.
*
@@ -205,7 +203,7 @@ public class GHCommit {
public PagedIterable<GHCommitComment> listComments() {
return new PagedIterable<GHCommitComment>() {
public PagedIterator<GHCommitComment> iterator() {
return new PagedIterator<GHCommitComment>(owner.root.retrievePaged(String.format("/repos/%s/%s/commits/%s/comments",owner.getOwnerName(),owner.getName(),sha),GHCommitComment[].class,false,V3)) {
return new PagedIterator<GHCommitComment>(owner.root.retrievePaged(String.format("/repos/%s/%s/commits/%s/comments",owner.getOwnerName(),owner.getName(),sha),GHCommitComment[].class,false)) {
@Override
protected void wrapUp(GHCommitComment[] page) {
for (GHCommitComment c : page)
@@ -222,7 +220,7 @@ public class GHCommit {
* I'm not sure how path/line/position parameters interact with each other.
*/
public GHCommitComment createComment(String body, String path, Integer line, Integer position) throws IOException {
GHCommitComment r = new Poster(owner.root,V3)
GHCommitComment r = new Poster(owner.root)
.with("body",body)
.with("path",path)
.with("line",line)

View File

@@ -4,8 +4,6 @@ import java.io.IOException;
import java.net.URL;
import java.util.Date;
import static org.kohsuke.github.ApiVersion.V3;
/**
* A comment attached to a commit (or a specific line in a specific file of a commit.)
*
@@ -99,7 +97,7 @@ public class GHCommitComment {
* Updates the body of the commit message.
*/
public void update(String body) throws IOException {
GHCommitComment r = new Poster(owner.root,V3)
GHCommitComment r = new Poster(owner.root)
.with("body",body)
.withCredential()
.to(getApiTail(),GHCommitComment.class,"PATCH");
@@ -110,7 +108,7 @@ public class GHCommitComment {
* Deletes this comment.
*/
public void delete() throws IOException {
new Poster(owner.root,V3).withCredential().to(getApiTail(),null,"DELETE");
new Poster(owner.root).withCredential().to(getApiTail(),null,"DELETE");
}
private String getApiTail() {

View File

@@ -54,7 +54,7 @@ public final class GHHook {
* Deletes this hook.
*/
public void delete() throws IOException {
new Poster(repository.root,ApiVersion.V3).withCredential()
new Poster(repository.root).withCredential()
.to(String.format("/repos/%s/%s/hooks/%d",repository.getOwnerName(),repository.getName(),id),null,"DELETE");
}
}

View File

@@ -26,6 +26,7 @@ package org.kohsuke.github;
import java.io.IOException;
import java.net.URL;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
@@ -46,6 +47,18 @@ public class GHIssue {
private int number,votes,comments;
private int position;
/*package*/ GHIssue wrap(GHRepository owner) {
this.owner = owner;
this.root = owner.root;
return this;
}
/*package*/ static GHIssue[] wrap(GHIssue[] issues, GHRepository owner) {
for (GHIssue i : issues)
i.wrap(owner);
return issues;
}
/**
* Repository to which the issue belongs.
*/
@@ -99,31 +112,55 @@ public class GHIssue {
* Updates the issue by adding a comment.
*/
public void comment(String message) throws IOException {
new Poster(root).withCredential().with("comment",message).to(getApiRoute("comment"));
new Poster(root).withCredential().with("body",message).to(getApiRoute()+"/comments",null,"POST");
}
private void edit(String key, Object value) throws IOException {
new Poster(root).withCredential()._with(key, value)
.to(getApiRoute(),null,"PATCH");
}
/**
* Closes this issue.
*/
public void close() throws IOException {
new Poster(root).withCredential().to(getApiRoute("close"));
edit("state", "closed");
}
/**
* Reopens this issue.
*/
public void reopen() throws IOException {
new Poster(root).withCredential().to(getApiRoute("reopen"));
edit("state", "open");
}
public void setTitle(String title) throws IOException {
edit("title",title);
}
public void setBody(String body) throws IOException {
edit("body",body);
}
public void assignTo(GHUser user) throws IOException {
edit("assignee",user.getLogin());
}
public void setLabels(String... labels) throws IOException {
edit("assignee",labels);
}
/**
* Obtains all the comments associated with this issue.
*/
public List<GHIssueComment> getComments() throws IOException {
return root.retrieve(getApiRoute("comments"), JsonIssueComments.class).wrap(this);
GHIssueComment[] r = root.retrieve(getApiRoute() + "/comments", GHIssueComment[].class);
for (GHIssueComment c : r)
c.wrapUp(this);
return Arrays.asList(r);
}
private String getApiRoute(String verb) {
return "/issues/"+verb+"/"+owner.getOwnerName()+"/"+owner.getName()+"/"+number;
private String getApiRoute() {
return "/repos/"+owner.getOwnerName()+"/"+owner.getName()+"/issues/"+number;
}
}

View File

@@ -37,6 +37,11 @@ public class GHIssueComment {
private String body, gravatar_id, user, created_at, updated_at;
private int id;
/*package*/ GHIssueComment wrapUp(GHIssue owner) {
this.owner = owner;
return this;
}
/**
* Gets the issue to which this comment is associated.
*/

View File

@@ -2,7 +2,6 @@ package org.kohsuke.github;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@@ -23,7 +22,7 @@ public class GHMyself extends GHUser {
* Always non-null.
*/
public List<String> getEmails() throws IOException {
String[] addresses = root.retrieveWithAuth3("/user/emails",String[].class);
String[] addresses = root.retrieveWithAuth("/user/emails", String[].class);
return Collections.unmodifiableList(Arrays.asList(addresses));
}
@@ -34,7 +33,7 @@ public class GHMyself extends GHUser {
* Always non-null.
*/
public List<GHKey> getPublicKeys() throws IOException {
return Collections.unmodifiableList(Arrays.asList(root.retrieveWithAuth3("/user/keys",GHKey[].class)));
return Collections.unmodifiableList(Arrays.asList(root.retrieveWithAuth("/user/keys", GHKey[].class)));
}
// public void addEmails(Collection<String> emails) throws IOException {

View File

@@ -4,7 +4,6 @@ import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlAnchor;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.AbstractList;
import java.util.ArrayList;
@@ -12,13 +11,16 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import static org.kohsuke.github.ApiVersion.*;
import java.util.TreeMap;
/**
* @author Kohsuke Kawaguchi
*/
public class GHOrganization extends GHPerson {
/*package*/ GHOrganization wrapUp(GitHub root) {
return (GHOrganization)super.wrapUp(root);
}
/**
* Creates a new repository.
*
@@ -31,7 +33,7 @@ public class GHOrganization extends GHPerson {
public GHRepository createRepository(String name, String description, String homepage, GHTeam team, boolean isPublic) throws IOException {
// such API doesn't exist, so fall back to HTML scraping
return new Poster(root,V3).withCredential()
return new Poster(root).withCredential()
.with("name", name).with("description", description).with("homepage", homepage)
.with("public", isPublic).with("team_id",team.getId()).to("/orgs/"+login+"/repos", GHRepository.class).wrap(root);
}
@@ -40,23 +42,19 @@ public class GHOrganization extends GHPerson {
* Teams by their names.
*/
public Map<String,GHTeam> getTeams() throws IOException {
return root.retrieveWithAuth("/organizations/"+login+"/teams",JsonTeams.class).toMap(this);
}
@Override
public GHRepository getRepository(String name) throws IOException {
try {
return root.retrieveWithAuth3("/repos/" + login + '/' + name, GHRepository.class).wrap(root);
} catch (FileNotFoundException e) {
return null;
GHTeam[] teams = root.retrieveWithAuth("/orgs/" + login + "/teams", GHTeam[].class);
Map<String,GHTeam> r = new TreeMap<String, GHTeam>();
for (GHTeam t : teams) {
r.put(t.getName(),t.wrapUp(this));
}
return r;
}
/**
* Publicizes the membership.
*/
public void publicize(GHUser u) throws IOException {
root.retrieveWithAuth3("/orgs/" + login + "/public_members/" + u.getLogin(), null, "PUT");
root.retrieveWithAuth("/orgs/" + login + "/public_members/" + u.getLogin(), null, "PUT");
}
/**
@@ -66,7 +64,7 @@ public class GHOrganization extends GHPerson {
return new AbstractList<GHUser>() {
// these are shallow objects with only some limited values filled out
// TODO: it's better to allow objects to fill themselves in later when missing values are requested
final GHUser[] shallow = root.retrieveWithAuth3("/orgs/" + login + "/members", GHUser[].class);
final GHUser[] shallow = root.retrieveWithAuth("/orgs/" + login + "/members", GHUser[].class);
@Override
public GHUser get(int index) {
@@ -88,7 +86,7 @@ public class GHOrganization extends GHPerson {
* Conceals the membership.
*/
public void conceal(GHUser u) throws IOException {
root.retrieveWithAuth3("/orgs/" + login + "/public_members/" + u.getLogin(), null, "DELETE");
root.retrieveWithAuth("/orgs/" + login + "/public_members/" + u.getLogin(), null, "DELETE");
}
public enum Permission { ADMIN, PUSH, PULL }
@@ -97,11 +95,13 @@ public class GHOrganization extends GHPerson {
* Creates a new team and assigns the repositories.
*/
public GHTeam createTeam(String name, Permission p, Collection<GHRepository> repositories) throws IOException {
Poster post = new Poster(root).withCredential().with("team[name]", name).with("team[permission]", p.name().toLowerCase());
Poster post = new Poster(root).withCredential().with("name", name).with("permission", p.name().toLowerCase());
List<String> repo_names = new ArrayList<String>();
for (GHRepository r : repositories) {
post.with("team[repo_names][]",r.getOwnerName()+'/'+r.getName());
repo_names.add(r.getName());
}
return post.to("/organizations/"+login+"/teams",JsonTeam.class).wrap(this);
post.with("repo_names",repo_names);
return post.to("/orgs/"+login+"/teams",GHTeam.class,"POST").wrapUp(this);
}
public GHTeam createTeam(String name, Permission p, GHRepository... repositories) throws IOException {

View File

@@ -1,7 +1,5 @@
package org.kohsuke.github;
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Arrays;
@@ -11,8 +9,6 @@ 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}.
*
@@ -26,13 +22,15 @@ public abstract class GHPerson {
protected int id;
protected String gravatar_id; // appears in V3 as well but presumably subsumed by avatar_url?
// V2
protected int public_gist_count,public_repo_count,followers_count,following_count;
// V3
protected String avatar_url,html_url;
protected int followers,following,public_repos,public_gists;
/*package*/ GHPerson wrapUp(GitHub root) {
this.root = root;
return this;
}
/**
* Gets the repositories this user owns.
*/
@@ -58,7 +56,7 @@ public abstract class GHPerson {
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);
final Iterator<GHRepository[]> pager = root.retrievePaged("/users/" + login + "/repos?per_page="+pageSize,GHRepository[].class,false);
return new Iterator<List<GHRepository>>() {
public boolean hasNext() {
@@ -87,7 +85,7 @@ public abstract class GHPerson {
*/
public GHRepository getRepository(String name) throws IOException {
try {
return root.retrieve3("/repos/" + login + '/' + name, GHRepository.class).wrap(root);
return root.retrieveWithAuth("/repos/" + login + '/' + name, GHRepository.class).wrap(root);
} catch (FileNotFoundException e) {
return null;
}
@@ -162,15 +160,15 @@ public abstract class GHPerson {
}
public int getPublicGistCount() {
return Math.max(public_gist_count,public_gists);
return public_gists;
}
public int getPublicRepoCount() {
return Math.max(public_repo_count,public_repos);
return public_repos;
}
public int getFollowingCount() {
return Math.max(following_count,following);
return following;
}
/**
@@ -181,7 +179,7 @@ public abstract class GHPerson {
}
public int getFollowersCount() {
return Math.max(followers_count,followers);
return followers;
}
}

View File

@@ -1,5 +1,6 @@
package org.kohsuke.github;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
@@ -16,6 +17,10 @@ public final class GHPersonSet<T extends GHPerson> extends HashSet<T> {
super(c);
}
public GHPersonSet(T... c) {
super(Arrays.asList(c));
}
public GHPersonSet(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
}

View File

@@ -0,0 +1,21 @@
package org.kohsuke.github;
/**
* Rate limit.
* @author Kohsuke Kawaguchi
*/
public class GHRateLimit {
/**
* Remaining calls that can be made.
*/
public int remaining;
/**
* Alotted API call per hour.
*/
public int limit;
@Override
public String toString() {
return remaining+"/"+limit;
}
}

View File

@@ -23,7 +23,6 @@
*/
package org.kohsuke.github;
import com.gargoylesoftware.htmlunit.ElementNotFoundException;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlButton;
import com.gargoylesoftware.htmlunit.html.HtmlCheckBoxInput;
@@ -33,8 +32,7 @@ import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.io.InterruptedIOException;
import java.net.URL;
import java.util.AbstractSet;
import java.util.ArrayList;
@@ -52,7 +50,6 @@ import java.util.Set;
import java.util.TreeMap;
import static java.util.Arrays.*;
import static org.kohsuke.github.ApiVersion.V3;
/**
* A repository on GitHub.
@@ -72,9 +69,16 @@ public class GHRepository {
private String created_at, pushed_at;
private Map<Integer,GHMilestone> milestones = new HashMap<Integer, GHMilestone>();
private String master_branch;
private String master_branch,language;
private Map<String,GHCommit> commits = new HashMap<String, GHCommit>();
private GHRepoPermission permissions;
private static class GHRepoPermission {
boolean pull,push,admin;
}
public String getDescription() {
return description;
}
@@ -113,12 +117,31 @@ public class GHRepository {
return name;
}
public boolean hasPullAccess() {
return permissions!=null && permissions.pull;
}
public boolean hasPushAccess() {
return permissions!=null && permissions.push;
}
public boolean hasAdminAccess() {
return permissions!=null && permissions.admin;
}
/**
* Gets the primary programming language.
*/
public String getLanguage() {
return language;
}
public GHUser getOwner() throws IOException {
return root.getUser(owner.login); // because 'owner' isn't fully populated
}
public List<GHIssue> getIssues(GHIssueState state) throws IOException {
return root.retrieve("/issues/list/" + owner.login + "/" + name + "/" + state.toString().toLowerCase(), JsonIssues.class).wrap(this);
return Arrays.asList(GHIssue.wrap(root.retrieve("/repos/" + owner.login + "/" + name + "/issues?state=" + state.toString().toLowerCase(), GHIssue[].class), this));
}
protected String getOwnerName() {
@@ -190,10 +213,7 @@ public class GHRepository {
*/
@WithBridgeMethods(Set.class)
public GHPersonSet<GHUser> getCollaborators() throws IOException {
GHPersonSet<GHUser> r = new GHPersonSet<GHUser>();
for (String u : root.retrieve("/repos/show/"+owner.login+"/"+name+"/collaborators",JsonCollaborators.class).collaborators)
r.add(root.getUser(u));
return r;
return new GHPersonSet<GHUser>(GHUser.wrap(root.retrieve("/repos/" + owner.login + "/" + name + "/collaborators", GHUser[].class),root));
}
/**
@@ -201,16 +221,17 @@ public class GHRepository {
* This method deviates from the principle of this library but it works a lot faster than {@link #getCollaborators()}.
*/
public Set<String> getCollaboratorNames() throws IOException {
Set<String> r = new HashSet<String>(root.retrieve("/repos/show/"+owner.login+"/"+name+"/collaborators",JsonCollaborators.class).collaborators);
return Collections.unmodifiableSet(r);
Set<String> r = new HashSet<String>();
for (GHUser u : GHUser.wrap(root.retrieve("/repos/" + owner.login + "/" + name + "/collaborators", GHUser[].class),root))
r.add(u.login);
return r;
}
/**
* If this repository belongs to an organization, return a set of teams.
*/
public Set<GHTeam> getTeams() throws IOException {
return Collections.unmodifiableSet(root.retrieveWithAuth("/repos/show/"+owner.login+"/"+name+"/teams",JsonTeams.class).toSet(
root.getOrganization(owner.login)));
return Collections.unmodifiableSet(new HashSet<GHTeam>(Arrays.asList(GHTeam.wrapUp(root.retrieveWithAuth("/repos/" + owner.login + "/" + name + "/teams", GHTeam[].class), root.getOrganization(owner.login)))));
}
public void addCollaborators(GHUser... users) throws IOException {
@@ -218,7 +239,7 @@ public class GHRepository {
}
public void addCollaborators(Collection<GHUser> users) throws IOException {
modifyCollaborators(users, "/add/");
modifyCollaborators(users, "PUT");
}
public void removeCollaborators(GHUser... users) throws IOException {
@@ -226,13 +247,13 @@ public class GHRepository {
}
public void removeCollaborators(Collection<GHUser> users) throws IOException {
modifyCollaborators(users, "/remove/");
modifyCollaborators(users, "DELETE");
}
private void modifyCollaborators(Collection<GHUser> users, String op) throws IOException {
private void modifyCollaborators(Collection<GHUser> users, String method) throws IOException {
verifyMine();
for (GHUser user : users) {
new Poster(root).withCredential().to("/repos/collaborators/"+name+ op +user.getLogin());
new Poster(root).withCredential().to("/repos/"+owner.login+"/"+name+"/collaborators/"+user.getLogin(),null,method);
}
}
@@ -248,31 +269,52 @@ public class GHRepository {
f.submit((HtmlButton) f.getElementsByTagName("button").get(0));
}
private void edit(String key, String value) throws IOException {
Poster poster = new Poster(root).withCredential();
if (!key.equals("name"))
poster.with("name", name); // even when we don't change the name, we need to send it in
poster.with(key, value)
.to("/repos/" + owner.login + "/" + name, null, "PATCH");
}
/**
* Enables or disables the issue tracker for this repository.
*/
public void enableIssueTracker(boolean v) throws IOException {
new Poster(root).withCredential().with("values[has_issues]",String.valueOf(v))
.to("/repos/show/" + owner.login + "/" + name);
edit("has_issues", String.valueOf(v));
}
/**
* Enables or disables Wiki for this repository.
*/
public void enableWiki(boolean v) throws IOException {
new Poster(root).withCredential().with("values[has_wiki]",String.valueOf(v))
.to("/repos/show/" + owner.login + "/" + name);
edit("has_wiki", String.valueOf(v));
}
public void enableDownloads(boolean v) throws IOException {
edit("has_downloads",String.valueOf(v));
}
/**
* Rename this repository.
*/
public void renameTo(String name) throws IOException {
edit("name",name);
}
public void setDescription(String value) throws IOException {
edit("description",value);
}
public void setHomepage(String value) throws IOException {
edit("homepage",value);
}
/**
* Deletes this repository.
*/
public void delete() throws IOException {
Poster poster = new Poster(root).withCredential();
String url = "/repos/delete/" + owner.login +"/"+name;
DeleteToken token = poster.to(url, DeleteToken.class);
poster.with("delete_token", token.delete_token).to(url);
new Poster(root).withCredential().to("/repos/" + owner.login +"/"+name, null, "DELETE");
}
/**
@@ -282,7 +324,7 @@ public class GHRepository {
* Newly forked repository that belong to you.
*/
public GHRepository fork() throws IOException {
return new Poster(root,V3).withCredential().to("/repos/" + owner.login + "/" + name + "/forks", GHRepository.class, "POST").wrap(root);
return new Poster(root).withCredential().to("/repos/" + owner.login + "/" + name + "/forks", GHRepository.class, "POST").wrap(root);
}
/**
@@ -292,58 +334,52 @@ public class GHRepository {
* Newly forked repository that belong to you.
*/
public GHRepository forkTo(GHOrganization org) throws IOException {
new Poster(root, V3).withCredential().to(String.format("/repos/%s/%s/forks?org=%s",owner.login,name,org.getLogin()));
return org.getRepository(name);
}
new Poster(root).withCredential().to(String.format("/repos/%s/%s/forks?org=%s",owner.login,name,org.getLogin()));
/**
* Rename this repository.
*/
public void renameTo(String newName) throws IOException {
WebClient wc = root.createWebClient();
HtmlPage pg = (HtmlPage)wc.getPage(getUrl()+"/admin");
for (HtmlForm f : pg.getForms()) {
if (!f.getActionAttribute().endsWith("/rename")) continue;
// this API is asynchronous. we need to wait for a bit
for (int i=0; i<10; i++) {
GHRepository r = org.getRepository(name);
if (r!=null) return r;
try {
f.getInputByName("name").setValueAttribute(newName);
f.submit((HtmlButton)f.getElementsByTagName("button").get(0));
// overwrite fields
final GHRepository r = getOwner().getRepository(newName);
for (Field fi : getClass().getDeclaredFields()) {
if (Modifier.isStatic(fi.getModifiers())) continue;
fi.setAccessible(true);
try {
fi.set(this,fi.get(r));
} catch (IllegalAccessException e) {
throw (IllegalAccessError)new IllegalAccessError().initCause(e);
}
}
return;
} catch (ElementNotFoundException e) {
// continue
Thread.sleep(3000);
} catch (InterruptedException e) {
throw (IOException)new InterruptedIOException().initCause(e);
}
}
throw new IllegalArgumentException("Either you don't have the privilege to rename "+owner.login+'/'+name+" or there's a bug in HTML scraping");
throw new IOException(this+" was forked into "+org.getLogin()+" but can't find the new repository");
}
/**
* Retrieves a specified pull request.
*/
public GHPullRequest getPullRequest(int i) throws IOException {
return root.retrieveWithAuth3("/repos/" + owner.login + '/' + name + "/pulls/" + i, GHPullRequest.class).wrapUp(this);
return root.retrieveWithAuth("/repos/" + owner.login + '/' + name + "/pulls/" + i, GHPullRequest.class).wrapUp(this);
}
/**
* Retrieves all the pull requests of a particular state.
*
* @see #listPullRequests(GHIssueState)
*/
public List<GHPullRequest> getPullRequests(GHIssueState state) throws IOException {
return listPullRequests(state).asList();
}
/**
* Retrieves all the pull requests of a particular state.
*/
public List<GHPullRequest> getPullRequests(GHIssueState state) throws IOException {
GHPullRequest[] r = root.retrieveWithAuth3("/repos/" + owner.login + '/' + name + "/pulls?state=" + state.name().toLowerCase(Locale.ENGLISH), GHPullRequest[].class);
for (GHPullRequest p : r)
p.wrapUp(this);
return new ArrayList<GHPullRequest>(Arrays.asList(r));
public PagedIterable<GHPullRequest> listPullRequests(final GHIssueState state) {
return new PagedIterable<GHPullRequest>() {
public PagedIterator<GHPullRequest> iterator() {
return new PagedIterator<GHPullRequest>(root.retrievePaged(String.format("/repos/%s/%s/pulls?state=%s", owner.login,name,state.name().toLowerCase(Locale.ENGLISH)), GHPullRequest[].class, false)) {
@Override
protected void wrapUp(GHPullRequest[] page) {
for (GHPullRequest pr : page)
pr.wrap(GHRepository.this);
}
};
}
};
}
/**
@@ -351,14 +387,14 @@ public class GHRepository {
*/
public List<GHHook> getHooks() throws IOException {
List<GHHook> list = new ArrayList<GHHook>(Arrays.asList(
root.retrieveWithAuth3(String.format("/repos/%s/%s/hooks",owner.login,name),GHHook[].class)));
root.retrieveWithAuth(String.format("/repos/%s/%s/hooks", owner.login, name), GHHook[].class)));
for (GHHook h : list)
h.wrap(this);
return list;
}
public GHHook getHook(int id) throws IOException {
return root.retrieveWithAuth3(String.format("/repos/%s/%s/hooks/%d",owner.login,name,id),GHHook.class).wrap(this);
return root.retrieveWithAuth(String.format("/repos/%s/%s/hooks/%d", owner.login, name, id), GHHook.class).wrap(this);
}
/**
@@ -367,7 +403,7 @@ public class GHRepository {
public GHCommit getCommit(String sha1) throws IOException {
GHCommit c = commits.get(sha1);
if (c==null) {
c = root.retrieve3(String.format("/repos/%s/%s/commits/%s",owner.login,name,sha1),GHCommit.class).wrapUp(this);
c = root.retrieve(String.format("/repos/%s/%s/commits/%s", owner.login, name, sha1), GHCommit.class).wrapUp(this);
commits.put(sha1,c);
}
return c;
@@ -379,7 +415,7 @@ public class GHRepository {
public PagedIterable<GHCommit> listCommits() {
return new PagedIterable<GHCommit>() {
public PagedIterator<GHCommit> iterator() {
return new PagedIterator<GHCommit>(root.retrievePaged(String.format("/repos/%s/%s/commits",owner.login,name),GHCommit[].class,false,V3)) {
return new PagedIterator<GHCommit>(root.retrievePaged(String.format("/repos/%s/%s/commits",owner.login,name),GHCommit[].class,false)) {
protected void wrapUp(GHCommit[] page) {
for (GHCommit c : page)
c.wrapUp(GHRepository.this);
@@ -395,7 +431,7 @@ public class GHRepository {
public PagedIterable<GHCommitComment> listCommitComments() {
return new PagedIterable<GHCommitComment>() {
public PagedIterator<GHCommitComment> iterator() {
return new PagedIterator<GHCommitComment>(root.retrievePaged(String.format("/repos/%s/%s/comments",owner.login,name),GHCommitComment[].class,false,V3)) {
return new PagedIterator<GHCommitComment>(root.retrievePaged(String.format("/repos/%s/%s/comments",owner.login,name),GHCommitComment[].class,false)) {
@Override
protected void wrapUp(GHCommitComment[] page) {
for (GHCommitComment c : page)
@@ -426,7 +462,7 @@ public class GHRepository {
ea.add(e.name().toLowerCase(Locale.ENGLISH));
}
return new Poster(root,ApiVersion.V3)
return new Poster(root)
.withCredential()
.with("name",name)
.with("active", active)
@@ -532,7 +568,7 @@ public class GHRepository {
*/
public Map<String,GHBranch> getBranches() throws IOException {
Map<String,GHBranch> r = new TreeMap<String,GHBranch>();
for (GHBranch p : root.retrieve3("/repos/"+owner.login+"/"+name+"/branches", GHBranch[].class)) {
for (GHBranch p : root.retrieve("/repos/" + owner.login + "/" + name + "/branches", GHBranch[].class)) {
p.wrap(this);
r.put(p.getName(),p);
}
@@ -541,7 +577,7 @@ public class GHRepository {
public Map<Integer, GHMilestone> getMilestones() throws IOException {
Map<Integer,GHMilestone> milestones = new TreeMap<Integer, GHMilestone>();
GHMilestone[] ms = root.retrieve3("/repos/"+owner.login+"/"+name+"/milestones", GHMilestone[].class);
GHMilestone[] ms = root.retrieve("/repos/" + owner.login + "/" + name + "/milestones", GHMilestone[].class);
for (GHMilestone m : ms) {
m.owner = this;
m.root = root;
@@ -553,7 +589,7 @@ public class GHRepository {
public GHMilestone getMilestone(int number) throws IOException {
GHMilestone m = milestones.get(number);
if (m == null) {
m = root.retrieve3("/repos/"+owner.login+"/"+name+"/milestones/"+number, GHMilestone.class);
m = root.retrieve("/repos/" + owner.login + "/" + name + "/milestones/" + number, GHMilestone.class);
m.owner = this;
m.root = root;
milestones.put(m.getNumber(), m);
@@ -562,7 +598,7 @@ public class GHRepository {
}
public GHMilestone createMilestone(String title, String description) throws IOException {
return new Poster(root,V3).withCredential()
return new Poster(root).withCredential()
.with("title", title).with("description", description)
.to("/repos/"+owner.login+"/"+name+"/milestones", GHMilestone.class,"POST").wrap(this);
}

View File

@@ -1,8 +1,11 @@
package org.kohsuke.github;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
/**
* A team in GitHub organization.
@@ -15,6 +18,18 @@ public class GHTeam {
protected /*final*/ GHOrganization org;
/*package*/ GHTeam wrapUp(GHOrganization owner) {
this.org = owner;
return this;
}
/*package*/ static GHTeam[] wrapUp(GHTeam[] teams, GHOrganization owner) {
for (GHTeam t : teams) {
t.wrapUp(owner);
}
return teams;
}
public String getName() {
return name;
}
@@ -31,33 +46,38 @@ public class GHTeam {
* Retrieves the current members.
*/
public Set<GHUser> getMembers() throws IOException {
return org.root.retrieveWithAuth(api("/members"),JsonUsersWithDetails.class).toSet(org.root);
return new HashSet<GHUser>(Arrays.asList(GHUser.wrap(org.root.retrieveWithAuth(api("/members"), GHUser[].class), org.root)));
}
public Map<String,GHRepository> getRepositories() throws IOException {
return org.root.retrieveWithAuth3(api("/repos"),JsonRepositories.class).wrap(org.root);
GHRepository[] repos = org.root.retrieveWithAuth(api("/repos"), GHRepository[].class);
Map<String,GHRepository> m = new TreeMap<String, GHRepository>();
for (GHRepository r : repos) {
m.put(r.getName(),r.wrap(org.root));
}
return m;
}
/**
* Adds a member to the team.
*/
public void add(GHUser u) throws IOException {
org.root.retrieveWithAuth(api("/members?name="+u.getLogin()),null, "POST");
org.root.retrieveWithAuth(api("/members/" + u.getLogin()), null, "PUT");
}
/**
* Removes a member to the team.
*/
public void remove(GHUser u) throws IOException {
org.root.retrieveWithAuth(api("/members?name="+u.getLogin()),null, "DELETE");
org.root.retrieveWithAuth(api("/members/" + u.getLogin()), null, "DELETE");
}
public void add(GHRepository r) throws IOException {
org.root.retrieveWithAuth(api("/repositories?name="+r.getOwnerName()+'/'+r.getName()),null, "POST");
org.root.retrieveWithAuth(api("/repos/" + r.getOwnerName() + '/' + r.getName()), null, "PUT");
}
public void remove(GHRepository r) throws IOException {
org.root.retrieveWithAuth(api("/repositories?name="+r.getOwnerName()+'/'+r.getName()),null, "DELETE");
org.root.retrieveWithAuth(api("/repos/" + r.getOwnerName() + '/' + r.getName()), null, "DELETE");
}
private String api(String tail) {

View File

@@ -23,12 +23,11 @@
*/
package org.kohsuke.github;
import com.infradna.tool.bridge_method_injector.BridgeMethodsAdded;
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
@@ -42,14 +41,14 @@ public class GHUser extends GHPerson {
* Follow this user.
*/
public void follow() throws IOException {
new Poster(root).withCredential().to("/user/follow/"+login);
new Poster(root).withCredential().to("/user/following/"+login,null,"PUT");
}
/**
* Unfollow this user.
*/
public void unfollow() throws IOException {
new Poster(root).withCredential().to("/user/unfollow/"+login);
new Poster(root).withCredential().to("/user/following/"+login,null,"DELETE");
}
/**
@@ -57,7 +56,8 @@ public class GHUser extends GHPerson {
*/
@WithBridgeMethods(Set.class)
public GHPersonSet<GHUser> getFollows() throws IOException {
return root.retrieve("/user/show/"+login+"/following",JsonUsers.class).toSet(root);
GHUser[] followers = root.retrieve("/users/" + login + "/following", GHUser[].class);
return new GHPersonSet<GHUser>(Arrays.asList(wrap(followers,root)));
}
/**
@@ -65,7 +65,14 @@ public class GHUser extends GHPerson {
*/
@WithBridgeMethods(Set.class)
public GHPersonSet<GHUser> getFollowers() throws IOException {
return root.retrieve("/user/show/"+login+"/followers",JsonUsers.class).toSet(root);
GHUser[] followers = root.retrieve("/users/" + login + "/followers", GHUser[].class);
return new GHPersonSet<GHUser>(Arrays.asList(wrap(followers,root)));
}
/*package*/ static GHUser[] wrap(GHUser[] users, GitHub root) {
for (GHUser f : users)
f.root = root;
return users;
}
/**
@@ -75,7 +82,7 @@ public class GHUser extends GHPerson {
public GHPersonSet<GHOrganization> getOrganizations() throws IOException {
GHPersonSet<GHOrganization> orgs = new GHPersonSet<GHOrganization>();
Set<String> names = new HashSet<String>();
for (GHOrganization o : root.retrieve3("/users/"+login+"/orgs",GHOrganization[].class)) {
for (GHOrganization o : root.retrieve("/users/" + login + "/orgs", GHOrganization[].class)) {
if (names.add(o.getLogin())) // I've seen some duplicates in the data
orgs.add(root.getOrganization(o.getLogin()));
}

View File

@@ -25,8 +25,6 @@ package org.kohsuke.github;
import static org.codehaus.jackson.annotate.JsonAutoDetect.Visibility.ANY;
import static org.codehaus.jackson.annotate.JsonAutoDetect.Visibility.NONE;
import static org.kohsuke.github.ApiVersion.V2;
import static org.kohsuke.github.ApiVersion.V3;
import java.io.File;
import java.io.FileInputStream;
@@ -54,7 +52,6 @@ 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;
@@ -85,7 +82,12 @@ public class GitHub {
private GitHub(String login, String apiToken, String password) {
this ("github.com", login, apiToken, password);
}
/**
*
* @param githubServer
* The host name of the GitHub (or GitHub enterprise) server, such as "github.com".
*/
private GitHub(String githubServer, String login, String apiToken, String password) {
this.githubServer = githubServer;
this.login = login;
@@ -156,42 +158,30 @@ public class GitHub {
throw new IllegalStateException("This operation requires a credential but none is given to the GitHub constructor");
}
/*package*/ URL getApiURL(ApiVersion v, String tailApiUrl) throws IOException {
/*package*/ URL getApiURL(String tailApiUrl) throws IOException {
if (oauthAccessToken != null) {
// append the access token
tailApiUrl = tailApiUrl + (tailApiUrl.indexOf('?')>=0 ?'&':'?') + "access_token=" + oauthAccessToken;
}
return new URL(v.getApiVersionBaseUrl(githubServer)+tailApiUrl);
return new URL("https://api."+githubServer+tailApiUrl);
}
/*package*/ <T> T retrieve(String tailApiUrl, Class<T> type) throws IOException {
return _retrieve(tailApiUrl, type, "GET", false, V2);
}
/*package*/ <T> T retrieve3(String tailApiUrl, Class<T> type) throws IOException {
return _retrieve(tailApiUrl, type, "GET", false, V3);
return _retrieve(tailApiUrl, type, "GET", false);
}
/*package*/ <T> T retrieveWithAuth(String tailApiUrl, Class<T> type) throws IOException {
return retrieveWithAuth(tailApiUrl, type, "GET");
}
/*package*/ <T> T retrieveWithAuth3(String tailApiUrl, Class<T> type) throws IOException {
return _retrieve(tailApiUrl, type, "GET", true, V3);
return _retrieve(tailApiUrl, type, "GET", true);
}
/*package*/ <T> T retrieveWithAuth(String tailApiUrl, Class<T> type, String method) throws IOException {
return _retrieve(tailApiUrl, type, method, true, V2);
return _retrieve(tailApiUrl, type, method, true);
}
/*package*/ <T> T retrieveWithAuth3(String tailApiUrl, Class<T> type, String method) throws IOException {
return _retrieve(tailApiUrl, type, method, true, V3);
}
private <T> T _retrieve(String tailApiUrl, Class<T> type, String method, boolean withAuth, ApiVersion v) throws IOException {
private <T> T _retrieve(String tailApiUrl, Class<T> type, String method, boolean withAuth) throws IOException {
while (true) {// loop while API rate limit is hit
HttpURLConnection uc = setupConnection(method, withAuth, getApiURL(v, tailApiUrl));
HttpURLConnection uc = setupConnection(method, withAuth, getApiURL(tailApiUrl));
try {
return parse(uc,type);
} catch (IOException e) {
@@ -205,7 +195,7 @@ public class GitHub {
*
* 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) {
/*package*/ <T> Iterator<T> retrievePaged(final String tailApiUrl, final Class<T> type, final boolean withAuth) {
return new Iterator<T>() {
/**
* The next batch to be returned from {@link #next()}.
@@ -218,7 +208,7 @@ public class GitHub {
{
try {
url = getApiURL(v, tailApiUrl);
url = getApiURL(tailApiUrl);
} catch (IOException e) {
throw new Error(e);
}
@@ -288,16 +278,19 @@ public class GitHub {
private HttpURLConnection setupConnection(String method, boolean withAuth, URL url) throws IOException {
HttpURLConnection uc = (HttpURLConnection) url.openConnection();
if (withAuth && this.oauthAccessToken == null)
// if the authentication is needed but no credential is given, try it anyway (so that some calls
// that do work with anonymous access in the reduced form should still work.)
// if OAuth token is present, it'll be set in the URL, so need to set the Authorization header
if (withAuth && encodedAuthorization!=null && this.oauthAccessToken == null)
uc.setRequestProperty("Authorization", "Basic " + encodedAuthorization);
uc.setRequestMethod(method);
uc.setRequestProperty("Accept-Encoding", "gzip");
if (method.equals("PUT")) {
uc.setDoOutput(true);
uc.setRequestProperty("Content-Length","0");
uc.getOutputStream().close();
}
uc.setRequestProperty("Accept-Encoding", "gzip");
return uc;
}
@@ -355,6 +348,13 @@ public class GitHub {
}
}
/**
* Gets the current rate limit.
*/
public GHRateLimit getRateLimit() throws IOException {
return retrieveWithAuth("/rate_limit", JsonRateLimit.class).rate;
}
/**
* Gets the {@link GHUser} that represents yourself.
*/
@@ -362,7 +362,7 @@ public class GitHub {
public GHMyself getMyself() throws IOException {
requireCredential();
GHMyself u = retrieveWithAuth3("/user", GHMyself.class);
GHMyself u = retrieveWithAuth("/user", GHMyself.class);
u.root = this;
users.put(u.getLogin(), u);
@@ -376,7 +376,7 @@ public class GitHub {
public GHUser getUser(String login) throws IOException {
GHUser u = users.get(login);
if (u == null) {
u = retrieve3("/users/" + login, GHUser.class);
u = retrieve("/users/" + login, GHUser.class);
u.root = this;
users.put(u.getLogin(), u);
}
@@ -399,8 +399,7 @@ public class GitHub {
public GHOrganization getOrganization(String name) throws IOException {
GHOrganization o = orgs.get(name);
if (o==null) {
o = retrieve("/organizations/"+name,JsonOrganization.class).organization;
o.root = this;
o = retrieve("/orgs/" + name, GHOrganization.class).wrapUp(this);
orgs.put(name,o);
}
return o;
@@ -416,8 +415,20 @@ public class GitHub {
return getUser(tokens[0]).getRepository(tokens[1]);
}
/**
* This method returns a shallowly populated organizations.
*
* To retrieve full organization details, you need to call {@link #getOrganization(String)}
* TODO: make this automatic.
*/
public Map<String, GHOrganization> getMyOrganizations() throws IOException {
return retrieveWithAuth("/organizations",JsonOrganizations.class).wrap(this);
GHOrganization[] orgs = retrieveWithAuth("/user/orgs", GHOrganization[].class);
Map<String, GHOrganization> r = new HashMap<String, GHOrganization>();
for (GHOrganization o : orgs) {
// don't put 'o' into orgs because they are shallow
r.put(o.getLogin(),o.wrapUp(this));
}
return r;
}
/**
@@ -425,7 +436,7 @@ public class GitHub {
*/
public List<GHEventInfo> getEvents() throws IOException {
// TODO: pagenation
GHEventInfo[] events = retrieve3("/events", GHEventInfo[].class);
GHEventInfo[] events = retrieve("/events", GHEventInfo[].class);
for (GHEventInfo e : events)
e.wrapUp(this);
return Arrays.asList(events);
@@ -451,7 +462,7 @@ public class GitHub {
* Newly created repository.
*/
public GHRepository createRepository(String name, String description, String homepage, boolean isPublic) throws IOException {
return new Poster(this,V3).withCredential()
return new Poster(this).withCredential()
.with("name", name).with("description", description).with("homepage", homepage)
.with("public", isPublic ? 1 : 0).to("/user/repos", GHRepository.class,"POST").wrap(this);
}
@@ -461,7 +472,7 @@ public class GitHub {
*/
public boolean isCredentialValid() throws IOException {
try {
retrieveWithAuth3("/user",GHUser.class);
retrieveWithAuth("/user", GHUser.class);
return true;
} catch (IOException e) {
return false;

View File

@@ -1,36 +0,0 @@
/*
* The MIT License
*
* Copyright (c) 2011, Eric Maupin
*
* 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 Eric Maupin
*/
class JsonBranch {
GHBranch branch;
GHBranch wrap(GHRepository r) {
return branch.wrap(r);
}
}

View File

@@ -1,40 +0,0 @@
/*
* The MIT License
*
* Copyright (c) 2011, Eric Maupin
*
* 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 java.util.List;
/**
* @author Eric Maupin
*/
class JsonBranches {
List<GHBranch> branches;
public List<GHBranch> wrap(GHRepository owner) {
for (GHBranch branch : branches)
branch.wrap(owner);
return branches;
}
}

View File

@@ -1,33 +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;
import java.util.List;
/**
* @author Kohsuke Kawaguchi
*/
class JsonCollaborators {
List<String> collaborators;
}

View File

@@ -1,38 +0,0 @@
/*
* The MIT License
*
* Copyright (c) 2011, Eric Maupin
*
* 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 Eric Maupin
*/
class JsonIssue {
GHIssue issue;
GHIssue wrap(GHRepository r) {
issue.owner = r;
issue.root = r.root;
return issue;
}
}

View File

@@ -1,16 +0,0 @@
package org.kohsuke.github;
import java.util.List;
/**
* @author Kohsuke Kawaguchi
*/
class JsonIssueComments {
List<GHIssueComment> comments;
List<GHIssueComment> wrap(GHIssue owner) {
for (GHIssueComment c : comments)
c.owner = owner;
return comments;
}
}

View File

@@ -1,38 +0,0 @@
/*
* The MIT License
*
* Copyright (c) 2011, Eric Maupin
*
* 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 java.util.List;
class JsonIssues {
List<GHIssue> issues;
public List<GHIssue> wrap(GHRepository owner) {
for (GHIssue issue : issues) {
issue.owner = owner;
issue.root = owner.root;
}
return issues;
}
}

View File

@@ -1,8 +0,0 @@
package org.kohsuke.github;
/**
* @author Kohsuke Kawaguchi
*/
class JsonOrganization {
public GHOrganization organization;
}

View File

@@ -1,44 +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;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
/**
*
*/
class JsonOrganizations {
public List<GHOrganization> organizations;
public Map<String,GHOrganization> wrap(GitHub root) {
Map<String,GHOrganization> map = new TreeMap<String, GHOrganization>();
for (GHOrganization o : organizations) {
o.root = root;
map.put(o.getLogin(),o);
}
return map;
}
}

View File

@@ -3,6 +3,6 @@ package org.kohsuke.github;
/**
* @author Kohsuke Kawaguchi
*/
class JsonMyself {
GHMyself user;
class JsonRateLimit {
GHRateLimit rate;
}

View File

@@ -1,44 +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;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
/**
* @author Kohsuke Kawaguchi
*/
class JsonRepositories {
public List<GHRepository> repositories;
public Map<String,GHRepository> wrap(GitHub root) {
Map<String,GHRepository> map = new TreeMap<String, GHRepository>();
for (GHRepository r : repositories) {
r.root = root;
map.put(r.getName(),r);
}
return map;
}
}

View File

@@ -1,13 +0,0 @@
package org.kohsuke.github;
/**
* @author Kohsuke Kawaguchi
*/
class JsonTeam {
public GHTeam team;
GHTeam wrap(GHOrganization org) {
team.org = org;
return team;
}
}

View File

@@ -1,32 +0,0 @@
package org.kohsuke.github;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
/**
* @author Kohsuke Kawaguchi
*/
class JsonTeams {
public List<GHTeam> teams;
Map<String, GHTeam> toMap(GHOrganization org) {
Map<String, GHTeam> r = new TreeMap<String, GHTeam>();
for (GHTeam t : teams) {
t.org = org;
r.put(t.getName(),t);
}
return r;
}
Set<GHTeam> toSet(GHOrganization org) {
Set<GHTeam> r = new HashSet<GHTeam>();
for (GHTeam t : teams) {
t.org = org;
r.add(t);
}
return r;
}
}

View File

@@ -1,43 +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;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* @author Kohsuke Kawaguchi
*/
class JsonUsers {
public List<String> users;
public GHPersonSet<GHUser> toSet(GitHub root) throws IOException {
GHPersonSet<GHUser> r = new GHPersonSet<GHUser>();
for (String u : users)
r.add(root.getUser(u));
return r;
}
}

View File

@@ -1,20 +0,0 @@
package org.kohsuke.github;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* @author Kohsuke Kawaguchi
*/
class JsonUsersWithDetails {
public List<GHUser> users;
public Set<GHUser> toSet(GitHub root) throws IOException {
Set<GHUser> r = new HashSet<GHUser>();
for (GHUser u : users)
r.add(root.getUser(u));
return r;
}
}

View File

@@ -1,8 +1,24 @@
package org.kohsuke.github;
import java.util.ArrayList;
import java.util.List;
/**
* {@link Iterable} that returns {@link PagedIterator}
*
* @author Kohsuke Kawaguchi
*/
public interface PagedIterable<T> extends Iterable<T> {
PagedIterator<T> iterator();
public abstract class PagedIterable<T> implements Iterable<T> {
public abstract PagedIterator<T> iterator();
/**
* Eagerly walk {@link Iterable} and return the result in a list.
*/
public List<T> asList() {
List<T> r = new ArrayList<T>();
for(PagedIterator<T> i = iterator(); i.hasNext();) {
r.addAll(i.nextPage());
}
return r;
}
}

View File

@@ -24,20 +24,15 @@
package org.kohsuke.github;
import org.apache.commons.io.IOUtils;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.impl.WriterBasedGenerator;
import org.codehaus.jackson.node.ObjectNode;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.net.HttpURLConnection;
import java.net.ProtocolException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -53,8 +48,6 @@ class Poster {
private final List<Entry> args = new ArrayList<Entry>();
private boolean authenticate;
private final ApiVersion v;
private static class Entry {
String key;
Object value;
@@ -63,19 +56,10 @@ class Poster {
this.key = key;
this.value = value;
}
public String toFormArg() throws UnsupportedEncodingException {
return URLEncoder.encode(key,"UTF-8")+'='+URLEncoder.encode(value.toString(),"UTF-8");
}
}
Poster(GitHub root, ApiVersion v) {
this.root = root;
this.v = v;
}
Poster(GitHub root) {
this(root,ApiVersion.V2);
this.root = root;
}
public Poster withCredential() {
@@ -102,6 +86,10 @@ class Poster {
return _with(key, value);
}
public Poster with(String key, Collection<String> value) {
return _with(key, value);
}
public Poster _with(String key, Object value) {
if (value!=null) {
args.add(new Entry(key,value));
@@ -127,20 +115,16 @@ class Poster {
public <T> T to(String tailApiUrl, Class<T> type, String method) throws IOException {
while (true) {// loop while API rate limit is hit
HttpURLConnection uc = (HttpURLConnection) root.getApiURL(v,tailApiUrl).openConnection();
HttpURLConnection uc = (HttpURLConnection) root.getApiURL(tailApiUrl).openConnection();
uc.setDoOutput(true);
uc.setRequestProperty("Content-type","application/x-www-form-urlencoded");
if (authenticate) {
if (v==ApiVersion.V3) {
if (root.oauthAccessToken!=null) {
uc.setRequestProperty("Authorization", "token " + root.oauthAccessToken);
} else {
if (root.password==null)
throw new IllegalArgumentException("V3 API doesn't support API token");
uc.setRequestProperty("Authorization", "Basic " + root.encodedAuthorization);
}
if (root.oauthAccessToken!=null) {
uc.setRequestProperty("Authorization", "token " + root.oauthAccessToken);
} else {
if (root.password==null)
throw new IllegalArgumentException("V3 API doesn't support API token");
uc.setRequestProperty("Authorization", "Basic " + root.encodedAuthorization);
}
}
@@ -158,23 +142,11 @@ class Poster {
}
if (v==ApiVersion.V2) {
StringBuilder body = new StringBuilder();
for (Entry e : args) {
if (body.length()>0) body.append('&');
body.append(e.toFormArg());
}
OutputStreamWriter o = new OutputStreamWriter(uc.getOutputStream(), "UTF-8");
o.write(body.toString());
o.close();
} else {
Map json = new HashMap();
for (Entry e : args) {
json.put(e.key, e.value);
}
MAPPER.writeValue(uc.getOutputStream(),json);
Map json = new HashMap();
for (Entry e : args) {
json.put(e.key, e.value);
}
MAPPER.writeValue(uc.getOutputStream(),json);
try {
InputStreamReader r = new InputStreamReader(uc.getInputStream(), "UTF-8");

View File

@@ -14,11 +14,13 @@ import org.kohsuke.github.GHKey;
import org.kohsuke.github.GHMyself;
import org.kohsuke.github.GHOrganization;
import org.kohsuke.github.GHOrganization.Permission;
import org.kohsuke.github.GHPullRequest;
import org.kohsuke.github.GHRepository;
import org.kohsuke.github.GHTeam;
import org.kohsuke.github.GHUser;
import org.kohsuke.github.GitHub;
import org.kohsuke.github.PagedIterable;
import org.kohsuke.github.PagedIterator;
import java.io.IOException;
import java.net.URL;
@@ -31,11 +33,31 @@ import java.util.List;
* Unit test for simple App.
*/
public class AppTest extends TestCase {
public void testRepoCRUD() throws Exception {
GitHub hub = GitHub.connect();
GHRepository r = hub.createRepository("github-api-test", "a test repository", "http://github-api.kohsuke.org/", true);
r.enableIssueTracker(false);
r.enableDownloads(false);
r.enableWiki(false);
r.renameTo("github-api-test2");
hub.getMyself().getRepository("github-api-test2").delete();
}
public void testCredentialValid() throws IOException {
assertTrue(GitHub.connect().isCredentialValid());
assertFalse(GitHub.connect("totally","bogus").isCredentialValid());
}
public void testRateLimit() throws IOException {
System.out.println(GitHub.connect().getRateLimit());
}
public void testMyOrganizations() throws IOException {
Map<String, GHOrganization> org = GitHub.connect().getMyOrganizations();
assertFalse(org.keySet().contains(null));
System.out.println(org);
}
public void testFetchPullRequest() throws Exception {
GitHub gh = GitHub.connect();
GHRepository r = gh.getOrganization("jenkinsci").getRepository("jenkins");
@@ -43,6 +65,25 @@ public class AppTest extends TestCase {
r.getPullRequest(1);
r.getPullRequests(GHIssueState.OPEN);
}
public void testFetchPullRequestAsList() throws Exception {
GitHub gh = GitHub.connect();
GHRepository r = gh.getOrganization("symfony").getRepository("symfony-docs");
assertEquals("master", r.getMasterBranch());
PagedIterable<GHPullRequest> i = r.listPullRequests(GHIssueState.CLOSED);
List<GHPullRequest> prs = i.asList();
assertNotNull(prs);
assertTrue(prs.size() > 0);
}
public void testRepoPermissions() throws Exception {
GitHub gh = GitHub.connect();
GHRepository r = gh.getOrganization("jenkinsci").getRepository("jenkins");
assertTrue(r.hasPullAccess());
r = gh.getOrganization("github").getRepository("tire");
assertFalse(r.hasAdminAccess());
}
public void tryGetMyself() throws Exception {
GitHub hub = GitHub.connect();
@@ -128,7 +169,7 @@ public class AppTest extends TestCase {
}
}
public void tryCreateCommitComment() throws Exception {
public void testCreateCommitComment() throws Exception {
GitHub gitHub = GitHub.connect();
GHCommit commit = gitHub.getUser("kohsuke").getRepository("sandbox-ant").getCommit("8ae38db0ea5837313ab5f39d43a6f73de3bd9000");
GHCommitComment c = commit.createComment("[testing](http://kohsuse.org/)");
@@ -234,11 +275,6 @@ public class AppTest extends TestCase {
gitHub.getUser("kohsuke").getRepository("test").renameTo("test2");
}
private void tryOrgFork(GitHub gitHub) throws IOException {
GHOrganization o = gitHub.getOrganization("HudsonLabs");
System.out.println(gitHub.getUser("rtyler").getRepository("memcache-ada").forkTo(o).getUrl());
}
private void tryTeamCreation(GitHub gitHub) throws IOException {
GHOrganization o = gitHub.getOrganization("HudsonLabs");
GHTeam t = o.createTeam("auto team", Permission.PUSH);