Compare commits

...

29 Commits

Author SHA1 Message Date
Kohsuke Kawaguchi
2bee34da59 [maven-release-plugin] prepare release github-api-1.15 2011-12-31 21:20:00 -08:00
Kohsuke Kawaguchi
74415b14be bug fix in the error handling 2011-12-19 17:47:08 -08:00
Kohsuke Kawaguchi
1e9a68a16b fixed the behaviour in case the repository doesn't exist. 2011-12-19 17:07:56 -08:00
Kohsuke Kawaguchi
7a3127ed65 added a method to list all members 2011-12-19 17:07:56 -08:00
Kohsuke Kawaguchi
11b0ac19fd PUT apparently requires some payload. 2011-12-19 17:07:56 -08:00
Michael O'Cleirigh
c8eab1f53e OAuth related changes to getMyself() and getUser (username)
Changed how getMyself() works so that in the OAuth case it will use the username less url.

Changed how getUser (username) works in the OAuth case so that you can lookup the specific username using the OAuth token.

Previously getUser (username) would always return the 'myself' user.
2011-11-19 01:08:01 -05:00
Kohsuke Kawaguchi
1c613c5f07 added a method to publicize/conceal 2011-11-15 15:26:47 -08:00
Kohsuke Kawaguchi
b8a63541e8 v3 API changed the meaning of the 'url' field. 2011-11-10 14:30:27 -08:00
Kohsuke Kawaguchi
f25083fde1 more test 2011-11-10 14:25:59 -08:00
Kohsuke Kawaguchi
c86c974400 GHOrganization.createRepository broke, so I fixed that by using V3 API.
That didn't quite work because v3 API has owner as an object, whereas v2 API has owner as string.

So then I fixed all the other retrieval points for GHRepository to use v3.
2011-11-10 14:23:36 -08:00
Kohsuke Kawaguchi
0ad419b026 [maven-release-plugin] prepare for next development iteration 2011-10-26 17:39:15 -07:00
Kohsuke Kawaguchi
a395697980 [maven-release-plugin] prepare release github-api-1.14 2011-10-26 17:39:10 -07:00
Kohsuke Kawaguchi
834a6eb865 GitHub has changed the element ID 2011-10-26 17:38:51 -07:00
Kohsuke Kawaguchi
e1c6927fd8 [maven-release-plugin] prepare for next development iteration 2011-09-15 11:44:31 -07:00
Kohsuke Kawaguchi
f02f2d7585 [maven-release-plugin] prepare release github-api-1.13 2011-09-15 11:44:20 -07:00
Kohsuke Kawaguchi
d4c099d447 Merge pull request #2 from lacostej/lacostej-master
expose issue_updated_at. It looks like a better representation of update
2011-09-06 13:28:30 -07:00
Jerome Lacoste (f19)
5756133840 expose issue_updated_at. It looks like a better representation of update time for an pull request than updated_at 2011-09-06 13:07:50 +02:00
Kohsuke Kawaguchi
6cc966adee [maven-release-plugin] prepare for next development iteration 2011-08-26 18:43:22 -07:00
Kohsuke Kawaguchi
107a1b83c0 [maven-release-plugin] prepare release github-api-1.12 2011-08-26 18:43:18 -07:00
Kohsuke Kawaguchi
1f8e732d3a added command to disable Wiki 2011-08-26 18:36:29 -07:00
Kohsuke Kawaguchi
d5e52a2468 [maven-release-plugin] prepare for next development iteration 2011-08-26 18:12:18 -07:00
Kohsuke Kawaguchi
231a9b11ae [maven-release-plugin] prepare release github-api-1.11 2011-08-26 18:12:14 -07:00
Kohsuke Kawaguchi
a272ab93ec test 2011-08-26 18:11:55 -07:00
Kohsuke Kawaguchi
a693328ec9 this needs credential 2011-08-26 18:06:00 -07:00
Kohsuke Kawaguchi
7d1a043495 fixed a bug in v3 authentication that doesn't support API token 2011-08-26 17:48:56 -07:00
Kohsuke Kawaguchi
0180f8c352 adding a demo code of org fork 2011-08-26 17:35:26 -07:00
Kohsuke Kawaguchi
7c630842b0 bug fix 2011-08-26 17:33:59 -07:00
Kohsuke Kawaguchi
c7f3c5ef8b Started adding mechanism to use v3 API. 2011-08-26 17:26:34 -07:00
Kohsuke Kawaguchi
2b23bfd296 [maven-release-plugin] prepare for next development iteration 2011-07-11 11:20:50 -07:00
11 changed files with 314 additions and 166 deletions

View File

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

View File

@@ -0,0 +1,17 @@
package org.kohsuke.github;
/**
* Different API versions.
*
* @author Kohsuke Kawaguchi
*/
enum ApiVersion {
V2("https://github.com/api/v2/json"),
V3("https://api.github.com");
final String url;
ApiVersion(String url) {
this.url = url;
}
}

View File

@@ -6,12 +6,15 @@ import com.gargoylesoftware.htmlunit.html.HtmlForm;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import java.io.IOException;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import static org.kohsuke.github.ApiVersion.V3;
/**
* @author Kohsuke Kawaguchi
*/
@@ -23,23 +26,14 @@ public class GHOrganization extends GHPerson {
* Newly created repository.
*/
public GHRepository createRepository(String name, String description, String homepage, String team, boolean isPublic) throws IOException {
return createRepository(name,description,homepage,getTeams().get(team),isPublic);
}
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
WebClient wc = root.createWebClient();
HtmlPage pg = (HtmlPage)wc.getPage("https://github.com/organizations/"+login+"/repositories/new");
HtmlForm f = pg.getForms().get(1);
f.getInputByName("repository[name]").setValueAttribute(name);
f.getInputByName("repository[description]").setValueAttribute(description);
f.getInputByName("repository[homepage]").setValueAttribute(homepage);
f.getSelectByName("team_id").getOptionByText(team).setSelected(true);
f.submit(f.getButtonByCaption("Create Repository"));
return getRepository(name);
// GHRepository r = new Poster(root).withCredential()
// .with("name", name).with("description", description).with("homepage", homepage)
// .with("public", isPublic ? 1 : 0).to(root.getApiURL("/organizations/"+login+"/repos/create"), JsonRepository.class).repository;
// r.root = root;
// return r;
return new Poster(root,V3).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);
}
/**
@@ -49,6 +43,45 @@ public class GHOrganization extends GHPerson {
return root.retrieveWithAuth("/organizations/"+login+"/teams",JsonTeams.class).toMap(this);
}
/**
* Publicizes the membership.
*/
public void publicize(GHUser u) throws IOException {
root.retrieveWithAuth3("/orgs/" + login + "/public_members/" + u.getLogin(), null, "PUT");
}
/**
* All the members of this organization.
*/
public List<GHUser> getMembers() throws IOException {
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);
@Override
public GHUser get(int index) {
try {
return root.getUser(shallow[index].getLogin());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public int size() {
return shallow.length;
}
};
}
/**
* Conceals the membership.
*/
public void conceal(GHUser u) throws IOException {
root.retrieveWithAuth3("/orgs/" + login + "/public_members/" + u.getLogin(), null, "DELETE");
}
public enum Permission { ADMIN, PUSH, PULL }
/**

View File

@@ -1,5 +1,6 @@
package org.kohsuke.github;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Collections;
import java.util.Map;
@@ -23,7 +24,7 @@ public abstract class GHPerson {
public synchronized Map<String,GHRepository> getRepositories() throws IOException {
Map<String,GHRepository> repositories = new TreeMap<String, GHRepository>();
for (int i=1; ; i++) {
Map<String, GHRepository> map = root.retrieve("/repos/show/" + login + "?page=" + i, JsonRepositories.class).wrap(root);
Map<String, GHRepository> map = root.retrieve3("/user/" + login + "/repos?per_page=100&page=" + i, JsonRepositories.class).wrap(root);
repositories.putAll(map);
if (map.isEmpty()) break;
}
@@ -31,8 +32,17 @@ public abstract class GHPerson {
return Collections.unmodifiableMap(repositories);
}
/**
*
* @return
* null if the repository was not found
*/
public GHRepository getRepository(String name) throws IOException {
return root.retrieve("/repos/show/" + login + '/' + name, JsonRepository.class).wrap(root);
try {
return root.retrieve3("/repos/" + login + '/' + name, GHRepository.class).wrap(root);
} catch (FileNotFoundException e) {
return null;
}
}
/**

View File

@@ -70,6 +70,10 @@ public class GHPullRequest extends GHIssue {
return head;
}
public Date getIssueUpdatedAt() {
return GitHub.parseDate(issue_updated_at);
}
/**
* The HTML page of this pull request,
* like https://github.com/jenkinsci/jenkins/pull/100

View File

@@ -32,6 +32,7 @@ import com.gargoylesoftware.htmlunit.html.HtmlInput;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import java.io.IOException;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.URL;
@@ -47,6 +48,7 @@ import java.util.Locale;
import java.util.Set;
import static java.util.Arrays.*;
import static org.kohsuke.github.ApiVersion.V3;
/**
* A repository on GitHub.
@@ -57,7 +59,10 @@ import static java.util.Arrays.*;
public class GHRepository {
/*package almost final*/ GitHub root;
private String description, homepage, url, name, owner;
private String description, homepage, name;
private String url; // this is the API url
private String html_url; // this is the UI
private GHUser owner; // not fully populated. beware.
private boolean has_issues, has_wiki, fork, _private, has_downloads;
private int watchers,forks,open_issues;
private String created_at, pushed_at;
@@ -74,7 +79,7 @@ public class GHRepository {
* URL of this repository, like 'http://github.com/kohsuke/hudson'
*/
public String getUrl() {
return url;
return html_url;
}
public String getName() {
@@ -82,15 +87,15 @@ public class GHRepository {
}
public GHUser getOwner() throws IOException {
return root.getUser(owner);
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 + "/" + name + "/" + state.toString().toLowerCase(), JsonIssues.class).wrap(this);
return root.retrieve("/issues/list/" + owner.login + "/" + name + "/" + state.toString().toLowerCase(), JsonIssues.class).wrap(this);
}
protected String getOwnerName() {
return owner;
return owner.login;
}
public boolean hasIssues() {
@@ -140,7 +145,7 @@ public class GHRepository {
*/
public Set<GHUser> getCollaborators() throws IOException {
Set<GHUser> r = new HashSet<GHUser>();
for (String u : root.retrieve("/repos/show/"+owner+"/"+name+"/collaborators",JsonCollaborators.class).collaborators)
for (String u : root.retrieve("/repos/show/"+owner.login+"/"+name+"/collaborators",JsonCollaborators.class).collaborators)
r.add(root.getUser(u));
return Collections.unmodifiableSet(r);
}
@@ -150,7 +155,7 @@ 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+"/"+name+"/collaborators",JsonCollaborators.class).collaborators);
Set<String> r = new HashSet<String>(root.retrieve("/repos/show/"+owner.login+"/"+name+"/collaborators",JsonCollaborators.class).collaborators);
return Collections.unmodifiableSet(r);
}
@@ -158,8 +163,8 @@ public class GHRepository {
* If this repository belongs to an organization, return a set of teams.
*/
public Set<GHTeam> getTeams() throws IOException {
return Collections.unmodifiableSet(root.retrieve("/repos/show/"+owner+"/"+name+"/teams",JsonTeams.class).toSet(
root.getOrganization(owner)));
return Collections.unmodifiableSet(root.retrieveWithAuth("/repos/show/"+owner.login+"/"+name+"/teams",JsonTeams.class).toSet(
root.getOrganization(owner.login)));
}
public void addCollaborators(GHUser... users) throws IOException {
@@ -188,9 +193,9 @@ public class GHRepository {
public void setEmailServiceHook(String address) throws IOException {
WebClient wc = root.createWebClient();
HtmlPage pg = (HtmlPage)wc.getPage(getUrl()+"/admin");
HtmlInput email = (HtmlInput)pg.getElementById("Email_address");
HtmlInput email = (HtmlInput)pg.getElementById("email_address");
email.setValueAttribute(address);
HtmlCheckBoxInput active = (HtmlCheckBoxInput)pg.getElementById("Email[active]");
HtmlCheckBoxInput active = (HtmlCheckBoxInput)pg.getElementById("email[active]");
active.setChecked(true);
final HtmlForm f = email.getEnclosingFormOrDie();
@@ -202,7 +207,15 @@ public class GHRepository {
*/
public void enableIssueTracker(boolean v) throws IOException {
new Poster(root).withCredential().with("values[has_issues]",String.valueOf(v))
.to("/repos/show/" + owner + "/" + name);
.to("/repos/show/" + owner.login + "/" + name);
}
/**
* 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);
}
/**
@@ -210,7 +223,7 @@ public class GHRepository {
*/
public void delete() throws IOException {
Poster poster = new Poster(root).withCredential();
String url = "/repos/delete/" + owner +"/"+name;
String url = "/repos/delete/" + owner.login +"/"+name;
DeleteToken token = poster.to(url, DeleteToken.class);
poster.with("delete_token",token.delete_token).to(url);
@@ -223,7 +236,7 @@ public class GHRepository {
* Newly forked repository that belong to you.
*/
public GHRepository fork() throws IOException {
return new Poster(root).withCredential().to("/repos/fork/" + owner + "/" + name, JsonRepository.class).wrap(root);
return new Poster(root,V3).withCredential().to("/repos/" + owner.login + "/" + name + "/forks", GHRepository.class, "POST").wrap(root);
}
/**
@@ -233,22 +246,8 @@ public class GHRepository {
* Newly forked repository that belong to you.
*/
public GHRepository forkTo(GHOrganization org) throws IOException {
WebClient wc = root.createWebClient();
HtmlPage pg = (HtmlPage)wc.getPage(getUrl());
for (HtmlForm f : pg.getForms()) {
if (!f.getActionAttribute().endsWith("/fork")) continue;
try {
if (org.getLogin().equals(f.getInputByName("organization").getValueAttribute())) {
// found it
f.submit((HtmlButton)f.getElementsByTagName("button").get(0));
return org.getRepository(name);
}
} catch (ElementNotFoundException e) {
// continue
}
}
throw new IllegalArgumentException("Either you don't have the privilege to fork into "+org.getLogin()+" or there's a bug in HTML scraping");
new Poster(root, V3).withCredential().to(String.format("/repos/%s/%s/forks?org=%s",owner.login,name,org.getLogin()));
return org.getRepository(name);
}
/**
@@ -281,21 +280,21 @@ public class GHRepository {
}
}
throw new IllegalArgumentException("Either you don't have the privilege to rename "+owner+'/'+name+" or there's a bug in HTML scraping");
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.
*/
public GHPullRequest getPullRequest(int i) throws IOException {
return root.retrieveWithAuth("/pulls/" + owner + '/' + name + "/" + i, JsonPullRequest.class).wrap(this);
return root.retrieveWithAuth("/pulls/" + owner.login + '/' + name + "/" + i, JsonPullRequest.class).wrap(this);
}
/**
* Retrieves all the pull requests of a particular state.
*/
public List<GHPullRequest> getPullRequests(GHIssueState state) throws IOException {
return root.retrieveWithAuth("/pulls/"+owner+'/'+name+"/"+state.name().toLowerCase(Locale.ENGLISH),JsonPullRequests.class).wrap(this);
return root.retrieveWithAuth("/pulls/"+owner.login+'/'+name+"/"+state.name().toLowerCase(Locale.ENGLISH),JsonPullRequests.class).wrap(this);
}
// this is no different from getPullRequests(OPEN)
@@ -307,8 +306,8 @@ public class GHRepository {
// }
private void verifyMine() throws IOException {
if (!root.login.equals(owner))
throw new IOException("Operation not applicable to a repository owned by someone else: "+owner);
if (!root.login.equals(owner.login))
throw new IOException("Operation not applicable to a repository owned by someone else: "+owner.login);
}
/**
@@ -410,10 +409,14 @@ public class GHRepository {
}
};
/*package*/ GHRepository wrap(GitHub root) {
this.root = root;
return this;
}
@Override
public String toString() {
return "Repository:"+owner+":"+name;
return "Repository:"+owner.login+":"+name;
}
@Override
@@ -425,7 +428,7 @@ public class GHRepository {
public boolean equals(Object obj) {
if (obj instanceof GHRepository) {
GHRepository that = (GHRepository) obj;
return this.owner.equals(that.owner)
return this.owner.login.equals(that.owner.login)
&& this.name.equals(that.name);
}
return false;

View File

@@ -35,7 +35,7 @@ public class GHTeam {
}
public Map<String,GHRepository> getRepositories() throws IOException {
return org.root.retrieveWithAuth(api("/repositories"),JsonRepositories.class).wrap(org.root);
return org.root.retrieveWithAuth3(api("/repos"),JsonRepositories.class).wrap(org.root);
}
/**

View File

@@ -34,7 +34,9 @@ import sun.misc.BASE64Encoder;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.InterruptedIOException;
import java.net.HttpURLConnection;
@@ -42,15 +44,13 @@ import java.net.MalformedURLException;
import java.net.URL;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import static org.codehaus.jackson.annotate.JsonAutoDetect.Visibility.*;
import static org.kohsuke.github.ApiVersion.*;
/**
* Root of the GitHub API.
@@ -60,7 +60,8 @@ import static org.codehaus.jackson.annotate.JsonAutoDetect.Visibility.*;
public class GitHub {
/*package*/ final String login;
/*package*/ final String encodedAuthorization;
final String password;
/*package*/ final String password;
/*package*/ final String apiToken;
private final Map<String,GHUser> users = new HashMap<String, GHUser>();
private final Map<String,GHOrganization> orgs = new HashMap<String, GHOrganization>();
@@ -68,11 +69,12 @@ public class GitHub {
private GitHub(String login, String apiToken, String password) {
this.login = login;
this.apiToken = apiToken;
this.password = password;
BASE64Encoder enc = new sun.misc.BASE64Encoder();
if (apiToken!=null || password!=null) {
String userpassword = apiToken!=null ? (login + "/token" + ":" + apiToken) : (login + ':'+password);
String userpassword = password==null ? (login + "/token" + ":" + apiToken) : (login + ':'+password);
encodedAuthorization = enc.encode(userpassword.getBytes());
} else
encodedAuthorization = null;
@@ -84,11 +86,11 @@ public class GitHub {
this.encodedAuthorization = null;
this.oauthAccessToken = oauthAccessToken;
this.apiToken = oauthAccessToken;
this.login = getMyself().getLogin();
}
/**
* Obtains the credential from "~/.github"
*/
@@ -129,40 +131,54 @@ public class GitHub {
throw new IllegalStateException("This operation requires a credential but none is given to the GitHub constructor");
}
/*package*/ URL getApiURL(String tailApiUrl) throws IOException {
/*package*/ URL getApiURL(ApiVersion v, String tailApiUrl) throws IOException {
if (oauthAccessToken != null) {
// append the access token
tailApiUrl = tailApiUrl + "?access_token=" + oauthAccessToken;
}
return new URL("https://github.com/api/v2/json"+tailApiUrl);
return new URL(v.url+tailApiUrl);
}
/*package*/ <T> T retrieve(String tailApiUrl, Class<T> type) throws IOException {
return _retrieve(tailApiUrl, type, "GET", false);
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 retrieveWithAuth(String tailApiUrl, Class<T> type, String method) throws IOException {
return _retrieve(tailApiUrl, type, method, true);
/*package*/ <T> T retrieveWithAuth3(String tailApiUrl, Class<T> type) throws IOException {
return _retrieve(tailApiUrl, type, "GET", true, V3);
}
private <T> T _retrieve(String tailApiUrl, Class<T> type, String method, boolean withAuth) throws IOException {
/*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);
}
private <T> T _retrieve(String tailApiUrl, Class<T> type, String method, boolean withAuth, ApiVersion v) throws IOException {
while (true) {// loop while API rate limit is hit
HttpURLConnection uc = (HttpURLConnection) getApiURL(tailApiUrl).openConnection();
HttpURLConnection uc = (HttpURLConnection) getApiURL(v,tailApiUrl).openConnection();
if (withAuth && this.oauthAccessToken == null)
uc.setRequestProperty("Authorization", "Basic " + encodedAuthorization);
uc.setRequestMethod(method);
if (method.equals("PUT")) {
uc.setDoOutput(true);
uc.setRequestProperty("Content-Length","0");
uc.getOutputStream().close();
}
try {
InputStreamReader r = new InputStreamReader(uc.getInputStream(), "UTF-8");
@@ -192,30 +208,61 @@ public class GitHub {
}
}
throw (IOException)new IOException(IOUtils.toString(uc.getErrorStream(),"UTF-8")).initCause(e);
if (e instanceof FileNotFoundException)
throw e; // pass through 404 Not Found to allow the caller to handle it intelligently
InputStream es = uc.getErrorStream();
if (es!=null)
throw (IOException)new IOException(IOUtils.toString(es,"UTF-8")).initCause(e);
else
throw e;
}
/**
* Obtains the object that represents the named user.
*/
public GHUser getUser(String login) throws IOException {
GHUser u = users.get(login);
if (u==null) {
if (oauthAccessToken != null) {
u = retrieve("/user/show",JsonUser.class).user;
u.root = this;
users.put(u.getLogin(),u);
}
else {
u = retrieve("/user/show/"+login,JsonUser.class).user;
u.root = this;
users.put(login,u);
}
}
return u;
}
* Gets the {@link GHUser} that represents yourself.
*/
public GHUser getMyself() throws IOException {
requireCredential();
GHUser u = null;
if (oauthAccessToken != null) {
u = retrieve("/user/show", JsonUser.class).user;
u.root = this;
users.put(u.getLogin(), u);
return u;
}
else {
return getUser(login);
}
}
/**
* Obtains the object that represents the named user.
*/
public GHUser getUser(String login) throws IOException {
GHUser u = users.get(login);
if (u == null) {
if (oauthAccessToken != null) {
u = retrieve("/user/show/" + login, JsonUser.class).user;
u.root = this;
users.put(u.getLogin(), u);
} else {
u = retrieve("/user/show/" + login, JsonUser.class).user;
u.root = this;
users.put(login, u);
}
}
return u;
}
/**
* Interns the given {@link GHUser}.
@@ -244,14 +291,7 @@ public class GitHub {
return retrieveWithAuth("/organizations",JsonOrganizations.class).wrap(this);
}
/**
* Gets the {@link GHUser} that represents yourself.
*/
public GHUser getMyself() throws IOException {
requireCredential();
return getUser(login);
}
/**
* Creates a new repository.
*
@@ -259,9 +299,9 @@ public class GitHub {
* Newly created repository.
*/
public GHRepository createRepository(String name, String description, String homepage, boolean isPublic) throws IOException {
return new Poster(this).withCredential()
return new Poster(this,V3).withCredential()
.with("name", name).with("description", description).with("homepage", homepage)
.with("public", isPublic ? 1 : 0).to("/repos/create", JsonRepository.class).wrap(this);
.with("public", isPublic ? 1 : 0).to("/user/repos", GHRepository.class,"POST").wrap(this);
}
/**

View File

@@ -1,36 +0,0 @@
/*
* The MIT License
*
* Copyright (c) 2010, Kohsuke Kawaguchi
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.kohsuke.github;
/**
* @author Kohsuke Kawaguchi
*/
class JsonRepository {
public GHRepository repository;
public GHRepository wrap(GitHub root) {
repository.root = root;
return repository;
}
}

View File

@@ -24,6 +24,9 @@
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;
@@ -33,7 +36,9 @@ import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.kohsuke.github.GitHub.*;
@@ -43,11 +48,32 @@ import static org.kohsuke.github.GitHub.*;
*/
class Poster {
private final GitHub root;
private final List<String> args = new ArrayList<String>();
private final List<Entry> args = new ArrayList<Entry>();
private boolean authenticate;
Poster(GitHub root) {
private final ApiVersion v;
private static class Entry {
String key;
Object value;
private Entry(String key, Object value) {
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);
}
public Poster withCredential() {
@@ -57,16 +83,20 @@ class Poster {
}
public Poster with(String key, int value) {
return with(key,String.valueOf(value));
return _with(key, value);
}
public Poster with(String key, boolean value) {
return _with(key, value);
}
public Poster with(String key, String value) {
return _with(key, value);
}
private Poster _with(String key, Object value) {
if (value!=null) {
try {
args.add(URLEncoder.encode(key,"UTF-8")+'='+URLEncoder.encode(value,"UTF-8"));
} catch (UnsupportedEncodingException e) {
throw new Error(e); // impossible
}
args.add(new Entry(key,value));
}
return this;
}
@@ -89,32 +119,47 @@ 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(tailApiUrl).openConnection();
HttpURLConnection uc = (HttpURLConnection) root.getApiURL(v,tailApiUrl).openConnection();
uc.setDoOutput(true);
uc.setRequestProperty("Content-type","application/x-www-form-urlencoded");
if (authenticate)
uc.setRequestProperty("Authorization", "Basic " + root.encodedAuthorization);
if (authenticate) {
if (v==ApiVersion.V3) {
if (root.password==null)
throw new IllegalArgumentException("V3 API doesn't support API token");
uc.setRequestProperty("Authorization", "Basic " + root.encodedAuthorization);
} else {
uc.setRequestProperty("Authorization", "Basic " + root.encodedAuthorization);
}
}
uc.setRequestMethod(method);
StringBuilder body = new StringBuilder();
for (String e : args) {
if (body.length()>0) body.append('&');
body.append(e);
}
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();
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);
}
try {
InputStreamReader r = new InputStreamReader(uc.getInputStream(), "UTF-8");
String data = IOUtils.toString(r);
if (type==null) {
String data = IOUtils.toString(r);
return null;
}
return MAPPER.readValue(r,type);
return MAPPER.readValue(data,type);
} catch (IOException e) {
root.handleApiError(e,uc);
}

View File

@@ -3,6 +3,7 @@ package org.kohsuke;
import junit.framework.TestCase;
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;
@@ -21,6 +22,17 @@ public class AppTest extends TestCase {
assertFalse(GitHub.connect("totally","bogus").isCredentialValid());
}
public void tryOrgFork() throws Exception {
GitHub gh = GitHub.connect();
gh.getUser("kohsuke").getRepository("rubywm").forkTo(gh.getOrganization("jenkinsci"));
}
public void tryGetTeamsForRepo() throws Exception {
GitHub gh = GitHub.connect();
Set<GHTeam> o = gh.getOrganization("jenkinsci").getRepository("rubywm").getTeams();
System.out.println(o);
}
public void testMembership() throws Exception {
GitHub gitHub = GitHub.connect();
Set<String> members = gitHub.getOrganization("jenkinsci").getRepository("violations-plugin").getCollaboratorNames();
@@ -29,6 +41,14 @@ public class AppTest extends TestCase {
public void testApp() throws IOException {
GitHub gitHub = GitHub.connect();
// GHRepository r = gitHub.connect().getOrganization("jenkinsci").createRepository("kktest4", "Kohsuke's test", "http://kohsuke.org/", "Everyone", true);
// r.fork();
// tryDisablingIssueTrackers(gitHub);
// tryDisablingWiki(gitHub);
// GHPullRequest i = gitHub.getOrganization("jenkinsci").getRepository("sandbox").getPullRequest(1);
// for (GHIssueComment c : i.getComments())
// System.out.println(c);
@@ -72,6 +92,15 @@ public class AppTest extends TestCase {
}
}
private void tryDisablingWiki(GitHub gitHub) throws IOException {
for (GHRepository r : gitHub.getOrganization("jenkinsci").getRepositories().values()) {
if (r.hasWiki()) {
System.out.println("DISABLED "+r.getName());
r.enableWiki(false);
}
}
}
private void tryUpdatingIssueTracker(GitHub gitHub) throws IOException {
GHRepository r = gitHub.getOrganization("jenkinsci").getRepository("lib-task-reactor");
System.out.println(r.hasIssues());
@@ -103,10 +132,13 @@ public class AppTest extends TestCase {
System.out.println(hooks);
}
private void testOrganization(GitHub gitHub) throws IOException {
GHOrganization labs = gitHub.getOrganization("HudsonLabs");
GHTeam t = labs.getTeams().get("Core Developers");
public void testOrganization() throws IOException {
GitHub gitHub = GitHub.connect();
GHOrganization j = gitHub.getOrganization("jenkinsci");
GHTeam t = j.getTeams().get("Core Developers");
t.add(labs.getRepository("xyz"));
assertNotNull(j.getRepository("jenkins"));
// t.add(labs.getRepository("xyz"));
}
}