Removed v2 API usage and switched to v3.

https://github.com/kohsuke/github-api/issues/8
This commit is contained in:
Kohsuke Kawaguchi
2012-06-12 14:06:49 -07:00
parent 3b5bc98053
commit b5f7208b0d
11 changed files with 169 additions and 119 deletions

View File

@@ -7,7 +7,6 @@ package org.kohsuke.github;
*/
enum ApiVersion {
V2("https://?/api/v2/json"),
V3("https://api.?");
final String templateUrl;

View File

@@ -26,11 +26,14 @@ 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;
import java.util.List;
import static org.kohsuke.github.ApiVersion.V3;
/**
* Represents an issue on GitHub.
*
@@ -46,6 +49,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 +114,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, V3).withCredential().with("body",message).to(getApiRoute()+"/comments",null,"POST");
}
private void edit(String key, Object value) throws IOException {
new Poster(root,V3).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.retrieve3(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

@@ -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,6 +11,7 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import static org.kohsuke.github.ApiVersion.*;
@@ -19,6 +19,10 @@ import static org.kohsuke.github.ApiVersion.*;
* @author Kohsuke Kawaguchi
*/
public class GHOrganization extends GHPerson {
/*package*/ GHOrganization wrapUp(GitHub root) {
return (GHOrganization)super.wrapUp(root);
}
/**
* Creates a new repository.
*
@@ -40,7 +44,12 @@ 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);
GHTeam[] teams = root.retrieveWithAuth3("/orgs/" + login + "/teams", GHTeam[].class);
Map<String,GHTeam> r = new TreeMap<String, GHTeam>();
for (GHTeam t : teams) {
r.put(t.getName(),t);
}
return r;
}
/**
@@ -88,11 +97,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,V3).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

@@ -33,6 +33,11 @@ public abstract class GHPerson {
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.
*/

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

@@ -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,6 @@ 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.net.URL;
import java.util.AbstractSet;
import java.util.ArrayList;
@@ -144,7 +141,7 @@ public class GHRepository {
}
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.retrieve3("/repos/" + owner.login + "/" + name + "/issues?state=" + state.toString().toLowerCase(), GHIssue[].class), this));
}
protected String getOwnerName() {
@@ -216,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.retrieve3("/repos/"+owner.login+"/"+name+"/collaborators",GHUser[].class),root));
}
/**
@@ -227,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.retrieve3("/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.retrieveWithAuth3("/repos/" + owner.login + "/" + name + "/teams", GHTeam[].class), root.getOrganization(owner.login)))));
}
public void addCollaborators(GHUser... users) throws IOException {
@@ -244,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 {
@@ -252,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,V3).withCredential().to("/repos/"+owner.login+"/"+name+"/collaborators/"+user.getLogin(),null,method);
}
}
@@ -274,31 +269,54 @@ public class GHRepository {
f.submit((HtmlButton) f.getElementsByTagName("button").get(0));
}
private void edit(String key, String value) throws IOException {
new Poster(root,V3).withCredential().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);
throw new UnsupportedOperationException(); // doesn't appear to be available in V3
// 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);
}
/**
@@ -322,39 +340,6 @@ public class GHRepository {
return org.getRepository(name);
}
/**
* 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;
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
}
}
throw new IllegalArgumentException("Either you don't have the privilege to rename "+owner.login+'/'+name+" or there's a bug in HTML scraping");
}
/**
* Retrieves a specified pull request.
*/

View File

@@ -1,6 +1,8 @@
package org.kohsuke.github;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@@ -15,6 +17,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,7 +45,7 @@ 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.retrieveWithAuth3(api("/members"),GHUser[].class),org.root)));
}
public Map<String,GHRepository> getRepositories() throws IOException {
@@ -42,22 +56,22 @@ public class GHTeam {
* 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.retrieveWithAuth3(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.retrieveWithAuth3(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.retrieveWithAuth3(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.retrieveWithAuth3(api("/repos/"+r.getOwnerName()+'/'+r.getName()),null, "DELETE");
}
private String api(String tail) {

View File

@@ -27,10 +27,13 @@ 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;
import static org.kohsuke.github.ApiVersion.V3;
/**
* Represents an user of GitHub.
*
@@ -42,14 +45,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, V3).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,V3).withCredential().to("/user/following/"+login,null,"DELETE");
}
/**
@@ -57,7 +60,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.retrieve3("/users/" + login + "/following", GHUser[].class);
return new GHPersonSet<GHUser>(Arrays.asList(wrap(followers,root)));
}
/**
@@ -65,7 +69,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.retrieve3("/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;
}
/**

View File

@@ -25,7 +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;
@@ -165,26 +164,14 @@ public class GitHub {
return new URL(v.getApiVersionBaseUrl(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);
}
/*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);
}
/*package*/ <T> T retrieveWithAuth(String tailApiUrl, Class<T> type, String method) throws IOException {
return _retrieve(tailApiUrl, type, method, true, V2);
}
/*package*/ <T> T retrieveWithAuth3(String tailApiUrl, Class<T> type, String method) throws IOException {
return _retrieve(tailApiUrl, type, method, true, V3);
}
@@ -409,8 +396,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 = retrieve3("/orgs/"+name,GHOrganization.class).wrapUp(this);
orgs.put(name,o);
}
return o;
@@ -427,7 +413,12 @@ public class GitHub {
}
public Map<String, GHOrganization> getMyOrganizations() throws IOException {
return retrieveWithAuth("/organizations",JsonOrganizations.class).wrap(this);
GHOrganization[] orgs = retrieveWithAuth3("/user/orgs", GHOrganization[].class);
Map<String, GHOrganization> r = new HashMap<String, GHOrganization>();
for (GHOrganization o : orgs) {
r.put(o.name,o.wrapUp(this));
}
return r;
}
/**

View File

@@ -38,6 +38,7 @@ 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;
@@ -63,10 +64,6 @@ 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) {
@@ -74,10 +71,6 @@ class Poster {
this.v = v;
}
Poster(GitHub root) {
this(root,ApiVersion.V2);
}
public Poster withCredential() {
root.requireCredential();
authenticate = true;
@@ -102,6 +95,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));
@@ -158,23 +155,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");