Compare commits

...

61 Commits

Author SHA1 Message Date
Kohsuke Kawaguchi
74b3902d5f [maven-release-plugin] prepare release github-api-1.22 2012-04-12 11:36:49 -07:00
Kohsuke Kawaguchi
7433ed968e monkey test code 2012-04-12 11:05:18 -07:00
Kohsuke Kawaguchi
ddf2d69a68 this method can return null 2012-04-12 11:05:04 -07:00
Kohsuke Kawaguchi
f5b34861bd avoid NPE 2012-04-12 11:01:43 -07:00
Kohsuke Kawaguchi
f0ff31a1af [maven-release-plugin] prepare for next development iteration 2012-04-11 18:16:16 -07:00
Kohsuke Kawaguchi
8ce76fba62 [maven-release-plugin] prepare release github-api-1.21 2012-04-11 18:16:10 -07:00
Kohsuke Kawaguchi
a947672320 doc improvement 2012-04-11 16:34:37 -07:00
Kohsuke Kawaguchi
5496e2b553 added pagenation support 2012-04-11 16:30:31 -07:00
Kohsuke Kawaguchi
2f9edc65dd support gzip for better performance 2012-04-11 15:45:08 -07:00
Kohsuke Kawaguchi
6372337456 Use gzip to improve transfer performance 2012-04-11 15:44:04 -07:00
Kohsuke Kawaguchi
b843606ebe [maven-release-plugin] prepare for next development iteration 2012-04-11 09:32:51 -07:00
Kohsuke Kawaguchi
7cda083b31 [maven-release-plugin] prepare release github-api-1.20 2012-04-11 09:32:46 -07:00
Kohsuke Kawaguchi
095604d4ca updated the parent version 2012-04-11 09:32:22 -07:00
Kohsuke Kawaguchi
7e799d02e3 Added test-ish 2012-04-11 09:29:28 -07:00
Kohsuke Kawaguchi
7f4612f872 exposed the size 2012-04-11 09:27:47 -07:00
Kohsuke Kawaguchi
6f6ff56338 exposed master_branch 2012-04-11 09:26:26 -07:00
Kohsuke Kawaguchi
27701a75d9 Migrating to the V3 API 2012-04-10 13:03:55 -07:00
Kohsuke Kawaguchi
3717d2ed32 emulate the value for V2 API 2012-04-10 12:57:25 -07:00
Kohsuke Kawaguchi
3a9b6665ee exposed avatar_url 2012-04-10 12:43:59 -07:00
Kohsuke Kawaguchi
bbc42f8898 [maven-release-plugin] prepare for next development iteration 2012-04-06 08:25:49 -07:00
Kohsuke Kawaguchi
f6aea47c2c [maven-release-plugin] prepare release github-api-1.19 2012-04-06 08:25:43 -07:00
Kohsuke Kawaguchi
23069ac2fc Using map to enable lookup by name. 2012-04-06 08:24:41 -07:00
Kohsuke Kawaguchi
0b2fdc598b cleaning up 2012-04-06 08:22:44 -07:00
Frederik Fix
aac30b923c implement listing of branches 2012-04-05 16:21:24 +02:00
Kohsuke Kawaguchi
318caf6123 [maven-release-plugin] prepare for next development iteration 2012-03-08 12:51:59 -08:00
Kohsuke Kawaguchi
6898893ffb [maven-release-plugin] prepare release github-api-1.18 2012-03-08 12:51:54 -08:00
Kohsuke Kawaguchi
cb34f4e91f Merge branch 'pull-6' 2012-03-08 12:50:09 -08:00
Kohsuke Kawaguchi
0da4b320f1 As per Sun coding convention, using upper case letters for enum constants 2012-03-08 12:49:36 -08:00
Kohsuke Kawaguchi
facb5bd13e Added API to handle emails. 2012-03-08 12:47:11 -08:00
Kohsuke Kawaguchi
22bc768868 [maven-release-plugin] prepare for next development iteration 2012-02-12 09:30:37 -08:00
Kohsuke Kawaguchi
a9b6f7bc9c [maven-release-plugin] prepare release github-api-1.17 2012-02-12 09:30:06 -08:00
Kohsuke Kawaguchi
830fb7192d added additional convenience method 2012-02-12 08:20:10 -08:00
Kohsuke Kawaguchi
e8ff7a4ae8 updated to use V3 API 2012-02-12 08:11:15 -08:00
Kohsuke Kawaguchi
dac2a56671 fully populate the root field upon deserialization 2012-02-12 07:43:06 -08:00
Kohsuke Kawaguchi
2718cf5ccb support both v2 and v3 2012-02-12 07:27:39 -08:00
Michael O'Cleirigh
202a5d435c Add original GitHub.connectUsingOAuth(token) back
This adds the original API back which defaults to the 'github.com' server.
2012-02-01 21:24:19 -05:00
Michael O'Cleirigh
58af8d0e90 Add support for github enterprise different base url
This changes how the ApiVersion enumeration works so that each of the V2 and V3  Url schemes is actually a template.

Then the serverName/hostname to use is passed in and used to generate the full url.

This will allow the github-api project to be used against users running the enterprise version of github.

This is needed to support github enterprise based authentication with the github-oauth-plugin.
2012-01-31 22:01:46 -05:00
Kohsuke Kawaguchi
b950be9626 bug fix in the event API. Turns out GHRepository isn't usable for this, because the name field is different.
/repos/:user/:repo returns just ":repo" portion in the name field, whereas in the event it has both ":user/:repo"
2012-01-14 11:53:53 -08:00
Kohsuke Kawaguchi
222277ae0a doc improvement 2012-01-14 11:42:09 -08:00
y.kokubo
fbcf3a17b4 implmented milestone api via v3 2012-01-13 14:36:00 +09:00
Kohsuke Kawaguchi
748cbdd03b doc improvement 2012-01-08 18:30:20 -08:00
Kohsuke Kawaguchi
57b26b6a21 doc improvement 2012-01-08 18:30:19 -08:00
Kohsuke Kawaguchi
a4026d46ce after an experiment, revisiting the design 2012-01-08 18:30:19 -08:00
Kohsuke Kawaguchi
696daf7387 adding the payload handling 2012-01-08 18:30:19 -08:00
Kohsuke Kawaguchi
4a507a5625 adding event support 2012-01-08 18:30:19 -08:00
Kohsuke Kawaguchi
ac1dd9a0ee 'Z' isn't parsed but it means GMT 2012-01-08 18:30:19 -08:00
Kohsuke Kawaguchi
44f1038516 [maven-release-plugin] prepare for next development iteration 2012-01-03 11:16:15 -08:00
Kohsuke Kawaguchi
3e2c9f86da [maven-release-plugin] prepare release github-api-1.16 2012-01-03 11:16:10 -08:00
Kohsuke Kawaguchi
44cc3c8556 added OAuth support to V3 API calls 2012-01-03 11:14:33 -08:00
Kohsuke Kawaguchi
bf65a746b0 inheriting javadoc and designating as overridden 2012-01-03 11:12:18 -08:00
Joshua Krall
4a4a469ca4 Fix for finding private repos on organizations,
requires authenticated API call
2012-01-02 19:45:58 -06:00
Kohsuke Kawaguchi
46b2e5dd32 bug fix 2012-01-02 10:46:54 -08:00
Kohsuke Kawaguchi
8a7adf876f added support for a hook 2012-01-02 10:39:16 -08:00
Kohsuke Kawaguchi
c16ebe4fec bug fix 2012-01-01 18:38:03 -08:00
Kohsuke Kawaguchi
4c61f79e5c bug fix 2012-01-01 18:07:27 -08:00
Kohsuke Kawaguchi
646dc17e5d typo 2012-01-01 18:07:07 -08:00
Kohsuke Kawaguchi
7977471458 Pulled up more properties to the base type 2012-01-01 17:43:44 -08:00
Kohsuke Kawaguchi
9ac2ad957b needs to send auth header 2012-01-01 13:11:24 -08:00
Kohsuke Kawaguchi
7fead8171d improving the usability by introducing a set with lookup-by-ID 2012-01-01 11:13:09 -08:00
Kohsuke Kawaguchi
70bc5c59e2 added a method to return organizations of the user 2012-01-01 09:01:33 -08:00
Kohsuke Kawaguchi
06ef6e841d [maven-release-plugin] prepare for next development iteration 2011-12-31 21:20:06 -08:00
26 changed files with 1196 additions and 286 deletions

33
pom.xml
View File

@@ -3,11 +3,11 @@
<parent>
<groupId>org.kohsuke</groupId>
<artifactId>pom</artifactId>
<version>1</version>
<version>3</version>
</parent>
<artifactId>github-api</artifactId>
<version>1.15</version>
<version>1.22</version>
<name>GitHub API for Java</name>
<url>http://github-api.kohsuke.org/</url>
<description>GitHub API for Java</description>
@@ -25,6 +25,23 @@
</site>
</distributionManagement>
<build>
<plugins>
<plugin>
<groupId>com.infradna.tool</groupId>
<artifactId>bridge-method-injector</artifactId>
<version>1.2</version>
<executions>
<execution>
<goals>
<goal>process</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.jvnet.hudson</groupId>
@@ -54,6 +71,18 @@
<artifactId>commons-io</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>com.infradna.tool</groupId>
<artifactId>bridge-method-annotation</artifactId>
<version>1.4</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.kohsuke.stapler</groupId>
<artifactId>stapler-jetty</artifactId>
<version>1.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<reporting>

View File

@@ -6,12 +6,19 @@ package org.kohsuke.github;
* @author Kohsuke Kawaguchi
*/
enum ApiVersion {
V2("https://github.com/api/v2/json"),
V3("https://api.github.com");
V2("https://?/api/v2/json"),
V3("https://api.?");
final String url;
final String templateUrl;
ApiVersion(String url) {
this.url = url;
ApiVersion(String templateUrl) {
this.templateUrl = templateUrl;
}
public String getApiVersionBaseUrl(String githubServer) {
return templateUrl.replaceFirst("\\?", githubServer);
}
}

View File

@@ -0,0 +1,51 @@
package org.kohsuke.github;
/**
* A branch in a repository.
*
* @author Yusuke Kokubo
*/
public class GHBranch {
private GitHub root;
private GHRepository owner;
private String name;
private Commit commit;
public static class Commit {
String sha,url;
}
public GitHub getRoot() {
return root;
}
/**
* Repository that this branch is in.
*/
public GHRepository getOwner() {
return owner;
}
public String getName() {
return name;
}
/**
* The commit that this branch currently points to.
*/
public String getSHA1() {
return commit.sha;
}
@Override
public String toString() {
return "Branch:" + name + " in " + owner.getUrl();
}
/*package*/ GHBranch wrap(GHRepository repo) {
this.owner = repo;
this.root = repo.root;
return this;
}
}

View File

@@ -31,7 +31,7 @@ package org.kohsuke.github;
public class GHCommitPointer {
private String ref, sha, label;
private GHUser user;
private GHRepository repository;
private GHRepository repository/*V2*/,repo/*V3*/;
/**
* This points to the user who owns
@@ -45,11 +45,11 @@ public class GHCommitPointer {
* The repository that contains the commit.
*/
public GHRepository getRepository() {
return repository;
return repo!=null ? repo : repository;
}
/**
* Named ref to the commit.
* Named ref to the commit. This appears to be a "short ref" that doesn't include "refs/heads/" portion.
*/
public String getRef() {
return ref;
@@ -68,4 +68,10 @@ public class GHCommitPointer {
public String getLabel() {
return label;
}
void wrapUp(GitHub root) {
if (user!=null) user.root = root;
if (repo!=null) repo.wrap(root);
if (repository!=null) repository.wrap(root);
}
}

View File

@@ -0,0 +1,28 @@
package org.kohsuke.github;
/**
* Hook event type.
*
* See http://developer.github.com/v3/events/types/
*
* @author Kohsuke Kawaguchi
*/
public enum GHEvent {
COMMIT_COMMENT,
CREATE,
DELETE,
DOWNLOAD,
FOLLOW,
FORK,
FORK_APPLY,
GIST,
GOLLUM,
ISSUE_COMMENT,
ISSUES,
MEMBER,
PUBLIC,
PULL_REQUEST,
PUSH,
TEAM_ADD,
WATCH
}

View File

@@ -0,0 +1,82 @@
package org.kohsuke.github;
import org.codehaus.jackson.node.ObjectNode;
import java.io.IOException;
import java.util.Date;
/**
* Represents an event.
*
* @author Kohsuke Kawaguchi
*/
public class GHEventInfo {
private GitHub root;
// we don't want to expose Jackson dependency to the user. This needs databinding
private ObjectNode payload;
private String created_at;
private String type;
// these are all shallow objects
private GHEventRepository repo;
private GHUser actor;
private GHOrganization org;
/**
* Inside the event JSON model, GitHub uses a slightly different format.
*/
public static class GHEventRepository {
private int id;
private String url; // repository API URL
private String name; // owner/repo
}
public GHEvent getType() {
String t = type;
if (t.endsWith("Event")) t=t.substring(0,t.length()-5);
for (GHEvent e : GHEvent.values()) {
if (e.name().replace("_","").equalsIgnoreCase(t))
return e;
}
return null; // unknown event type
}
/*package*/ GHEventInfo wrapUp(GitHub root) {
this.root = root;
return this;
}
public Date getCreatedAt() {
return GitHub.parseDate(created_at);
}
/**
* Repository where the change was made.
*/
public GHRepository getRepository() throws IOException {
return root.getRepository(repo.name);
}
public GHUser getActor() throws IOException {
return root.getUser(actor.getLogin());
}
public GHOrganization getOrganization() throws IOException {
return (org==null || org.getLogin()==null) ? null : root.getOrganization(org.getLogin());
}
/**
* Retrieves the payload.
*
* @param type
* Specify one of the {@link GHEventPayload} subtype that defines a type-safe access to the payload.
* This must match the {@linkplain #getType() event type}.
*/
public <T extends GHEventPayload> T getPayload(Class<T> type) throws IOException {
T v = GitHub.MAPPER.readValue(payload.traverse(), type);
v.wrapUp(root);
return v;
}
}

View File

@@ -0,0 +1,46 @@
package org.kohsuke.github;
import java.io.Reader;
/**
* Base type for types used in databinding of the event payload.
*
* @see GitHub#parseEventPayload(Reader, Class)
* @see GHEventInfo#getPayload(Class)
*/
public abstract class GHEventPayload {
protected GitHub root;
/*package*/ GHEventPayload() {
}
/*package*/ void wrapUp(GitHub root) {
this.root = root;
}
public static class PullRequest extends GHEventPayload {
private String action;
private int number;
GHPullRequest pull_request;
public String getAction() {
return action;
}
public int getNumber() {
return number;
}
public GHPullRequest getPullRequest() {
pull_request.root = root;
return pull_request;
}
@Override
void wrapUp(GitHub root) {
super.wrapUp(root);
pull_request.wrapUp(root);
}
}
}

View File

@@ -0,0 +1,60 @@
package org.kohsuke.github;
import java.io.IOException;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
/**
* @author Kohsuke Kawaguchi
*/
public final class GHHook {
/**
* Repository that the hook belongs to.
*/
/*package*/ transient GHRepository repository;
String created_at, updated_at, name;
List<String> events;
boolean active;
Map<String,String> config;
int id;
/*package*/ GHHook wrap(GHRepository owner) {
this.repository = owner;
return this;
}
public String getName() {
return name;
}
public EnumSet<GHEvent> getEvents() {
EnumSet<GHEvent> s = EnumSet.noneOf(GHEvent.class);
for (String e : events)
Enum.valueOf(GHEvent.class,e.toUpperCase(Locale.ENGLISH));
return s;
}
public boolean isActive() {
return active;
}
public Map<String, String> getConfig() {
return Collections.unmodifiableMap(config);
}
public int getId() {
return id;
}
/**
* Deletes this hook.
*/
public void delete() throws IOException {
new Poster(repository.root,ApiVersion.V3).withCredential()
.to(String.format("/repos/%s/%s/hooks/%d",repository.getOwnerName(),repository.getName(),id),null,"DELETE");
}
}

View File

@@ -0,0 +1,73 @@
package org.kohsuke.github;
import java.util.Date;
import java.util.Locale;
/**
*
* @author Yusuke Kokubo
*
*/
public class GHMilestone {
GitHub root;
GHRepository owner;
GHUser creator;
private String state, due_on, title, url, created_at, description;
private int closed_issues, open_issues, number;
public GitHub getRoot() {
return root;
}
public GHRepository getOwner() {
return owner;
}
public GHUser getCreator() {
return creator;
}
public Date getDueOn() {
if (due_on == null) return null;
return GitHub.parseDate(due_on);
}
public String getTitle() {
return title;
}
public String getUrl() {
return url;
}
public Date getCreatedAt() {
return GitHub.parseDate(created_at);
}
public String getDescription() {
return description;
}
public int getClosedIssues() {
return closed_issues;
}
public int getOpenIssues() {
return open_issues;
}
public int getNumber() {
return number;
}
public GHMilestoneState getState() {
return Enum.valueOf(GHMilestoneState.class, state.toUpperCase(Locale.ENGLISH));
}
public GHMilestone wrap(GHRepository repo) {
this.owner = repo;
this.root = repo.root;
return this;
}
}

View File

@@ -0,0 +1,11 @@
package org.kohsuke.github;
/**
*
* @author Yusuke Kokubo
*
*/
public enum GHMilestoneState {
OPEN,
CLOSED
}

View File

@@ -0,0 +1,34 @@
package org.kohsuke.github;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* Represents the account that's logging into GitHub.
*
* @author Kohsuke Kawaguchi
*/
public class GHMyself extends GHUser {
/**
* Returns the read-only list of e-mail addresses configured for you.
*
* This corresponds to the stuff you configure in https://github.com/settings/emails,
* and not to be confused with {@link #getEmail()} that shows your public e-mail address
* set in https://github.com/settings/profile
*
* @return
* Always non-null.
*/
public List<String> getEmails() throws IOException {
String[] addresses = root.retrieveWithAuth3("/user/emails",String[].class);
return Collections.unmodifiableList(Arrays.asList(addresses));
}
// public void addEmails(Collection<String> emails) throws IOException {
//// new Poster(root,ApiVersion.V3).withCredential().to("/user/emails");
// root.retrieveWithAuth3()
// }
}

View File

@@ -2,9 +2,9 @@ package org.kohsuke.github;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlAnchor;
import com.gargoylesoftware.htmlunit.html.HtmlForm;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.AbstractList;
import java.util.ArrayList;
@@ -13,7 +13,7 @@ import java.util.Collection;
import java.util.List;
import java.util.Map;
import static org.kohsuke.github.ApiVersion.V3;
import static org.kohsuke.github.ApiVersion.*;
/**
* @author Kohsuke Kawaguchi
@@ -43,6 +43,15 @@ public class GHOrganization extends GHPerson {
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;
}
}
/**
* Publicizes the membership.
*/

View File

@@ -2,10 +2,15 @@ package org.kohsuke.github;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import static org.kohsuke.github.ApiVersion.*;
/**
* Common part of {@link GHUser} and {@link GHOrganization}.
*
@@ -14,24 +19,65 @@ import java.util.TreeMap;
public abstract class GHPerson {
/*package almost final*/ GitHub root;
protected String gravatar_id,login;
// common
protected String login,location,blog,email,name,created_at,company;
protected int id;
protected String gravatar_id; // appears in V3 as well but presumably subsumed by avatar_url?
protected int public_gist_count,public_repo_count,following_count,id;
// 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;
/**
* Gets the repositories this user owns.
*/
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.retrieve3("/user/" + login + "/repos?per_page=100&page=" + i, JsonRepositories.class).wrap(root);
repositories.putAll(map);
if (map.isEmpty()) break;
for (List<GHRepository> batch : iterateRepositories(100)) {
for (GHRepository r : batch)
repositories.put(r.getName(),r);
}
return Collections.unmodifiableMap(repositories);
}
/**
* Loads repository list in a pagenated fashion.
*
* <p>
* For a person with a lot of repositories, GitHub returns the list of repositories in a pagenated fashion.
* Unlike {@link #getRepositories()}, this method allows the caller to start processing data as it arrives.
*
* Every {@link Iterator#next()} call results in I/O. Exceptions that occur during the processing is wrapped
* into {@link Error}.
*/
public synchronized Iterable<List<GHRepository>> iterateRepositories(final int pageSize) {
return new Iterable<List<GHRepository>>() {
public Iterator<List<GHRepository>> iterator() {
final Iterator<GHRepository[]> pager = root.retrievePaged("/users/" + login + "/repos?per_page="+pageSize,GHRepository[].class,false, V3);
return new Iterator<List<GHRepository>>() {
public boolean hasNext() {
return pager.hasNext();
}
public List<GHRepository> next() {
GHRepository[] batch = pager.next();
for (GHRepository r : batch)
r.root = root;
return Arrays.asList(batch);
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
};
}
/**
*
* @return
@@ -47,12 +93,93 @@ public abstract class GHPerson {
/**
* Gravatar ID of this user, like 0cb9832a01c22c083390f3c5dcb64105
*
* @deprecated
* No longer available in the v3 API.
*/
public String getGravatarId() {
return gravatar_id;
}
/**
* Returns a string like 'https://secure.gravatar.com/avatar/0cb9832a01c22c083390f3c5dcb64105'
* that indicates the avatar image URL.
*/
public String getAvatarUrl() {
if (avatar_url!=null)
return avatar_url;
if (gravatar_id!=null)
return "https://secure.gravatar.com/avatar/"+gravatar_id;
return null;
}
/**
* Gets the login ID of this user, like 'kohsuke'
*/
public String getLogin() {
return login;
}
/**
* Gets the human-readable name of the user, like "Kohsuke Kawaguchi"
*/
public String getName() {
return name;
}
/**
* Gets the company name of this user, like "Sun Microsystems, Inc."
*/
public String getCompany() {
return company;
}
/**
* Gets the location of this user, like "Santa Clara, California"
*/
public String getLocation() {
return location;
}
public String getCreatedAt() {
return created_at;
}
/**
* Gets the blog URL of this user.
*/
public String getBlog() {
return blog;
}
/**
* Gets the e-mail address of the user.
*/
public String getEmail() {
return email;
}
public int getPublicGistCount() {
return Math.max(public_gist_count,public_gists);
}
public int getPublicRepoCount() {
return Math.max(public_repo_count,public_repos);
}
public int getFollowingCount() {
return Math.max(following_count,following);
}
/**
* What appears to be a GitHub internal unique number that identifies this user.
*/
public int getId() {
return id;
}
public int getFollowersCount() {
return Math.max(followers_count,followers);
}
}

View File

@@ -0,0 +1,36 @@
package org.kohsuke.github;
import java.util.Collection;
import java.util.HashSet;
/**
* Set of {@link GHPerson} with helper lookup methods.
*
* @author Kohsuke Kawaguchi
*/
public final class GHPersonSet<T extends GHPerson> extends HashSet<T> {
public GHPersonSet() {
}
public GHPersonSet(Collection<? extends T> c) {
super(c);
}
public GHPersonSet(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
}
public GHPersonSet(int initialCapacity) {
super(initialCapacity);
}
/**
* Finds the item by its login.
*/
public T byLogin(String login) {
for (T t : this)
if (t.getLogin().equals(login))
return t;
return null;
}
}

View File

@@ -25,7 +25,6 @@ package org.kohsuke.github;
import java.net.URL;
import java.util.Date;
import java.util.Locale;
/**
* A pull request.
@@ -64,7 +63,7 @@ public class GHPullRequest extends GHIssue {
}
/**
* The change that should be pulled.
* The change that should be pulled. The tip of the commits to merge.
*/
public GHCommitPointer getHead() {
return head;
@@ -93,4 +92,19 @@ public class GHPullRequest extends GHIssue {
public Date getClosedAt() {
return GitHub.parseDate(closed_at);
}
GHPullRequest wrapUp(GHRepository owner) {
this.owner = owner;
return wrapUp(owner.root);
}
GHPullRequest wrapUp(GitHub root) {
this.root = root;
if (owner!=null) owner.wrap(root);
if (issue_user!=null) issue_user.root=root;
if (user!=null) user.root=root;
if (base!=null) base.wrapUp(root);
if (head!=null) head.wrapUp(root);
return this;
}
}

View File

@@ -30,22 +30,26 @@ import com.gargoylesoftware.htmlunit.html.HtmlCheckBoxInput;
import com.gargoylesoftware.htmlunit.html.HtmlForm;
import com.gargoylesoftware.htmlunit.html.HtmlInput;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
import java.io.IOException;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import static java.util.Arrays.*;
import static org.kohsuke.github.ApiVersion.V3;
@@ -64,8 +68,11 @@ public class GHRepository {
private String html_url; // this is the UI
private GHUser owner; // not fully populated. beware.
private boolean has_issues, has_wiki, fork, _private, has_downloads;
private int watchers,forks,open_issues;
private int watchers,forks,open_issues,size;
private String created_at, pushed_at;
private Map<Integer,GHMilestone> milestones = new HashMap<Integer, GHMilestone>();
private String master_branch;
public String getDescription() {
return description;
@@ -76,12 +83,31 @@ public class GHRepository {
}
/**
* URL of this repository, like 'http://github.com/kohsuke/hudson'
* URL of this repository, like 'http://github.com/kohsuke/jenkins'
*/
public String getUrl() {
return html_url;
}
/**
* Gets the git:// URL to this repository, such as "git://github.com/kohsuke/jenkins.git"
* This URL is read-only.
*/
public String getGitTransportUrl() {
return "git://github.com/"+getOwnerName()+"/"+name+".git";
}
/**
* Gets the HTTPS URL to this repository, such as "https://github.com/kohsuke/jenkins.git"
* This URL is read-only.
*/
public String gitHttpTransportUrl() {
return "https://github.com/"+getOwnerName()+"/"+name+".git";
}
/**
* Short repository name without the owner. For example 'jenkins' in case of http://github.com/jenkinsci/jenkins
*/
public String getName() {
return name;
}
@@ -130,6 +156,11 @@ public class GHRepository {
return open_issues;
}
/**
*
* @return
* null if the repository was never pushed at.
*/
public Date getPushedAt() {
return GitHub.parseDate(pushed_at);
}
@@ -138,16 +169,30 @@ public class GHRepository {
return GitHub.parseDate(created_at);
}
/**
* Returns the primary branch you'll configure in the "Admin > Options" config page.
*
* @return
* This field is null until the user explicitly configures the master branch.
*/
public String getMasterBranch() {
return master_branch;
}
public int getSize() {
return size;
}
/**
* Gets the collaborators on this repository.
* This set always appear to include the owner.
*/
public Set<GHUser> getCollaborators() throws IOException {
Set<GHUser> r = new HashSet<GHUser>();
@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 Collections.unmodifiableSet(r);
return r;
}
/**
@@ -287,14 +332,69 @@ public class GHRepository {
* Retrieves a specified pull request.
*/
public GHPullRequest getPullRequest(int i) throws IOException {
return root.retrieveWithAuth("/pulls/" + owner.login + '/' + name + "/" + i, JsonPullRequest.class).wrap(this);
return root.retrieveWithAuth3("/repos/" + owner.login + '/' + name + "/pulls/" + i, GHPullRequest.class).wrapUp(this);
}
/**
* Retrieves all the pull requests of a particular state.
*/
public List<GHPullRequest> getPullRequests(GHIssueState state) throws IOException {
return root.retrieveWithAuth("/pulls/"+owner.login+'/'+name+"/"+state.name().toLowerCase(Locale.ENGLISH),JsonPullRequests.class).wrap(this);
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));
}
/**
* Retrieves the currently configured hooks.
*/
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)));
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);
}
/**
*
* See https://api.github.com/hooks for possible names and their configuration scheme.
* TODO: produce type-safe binding
*
* @param name
* Type of the hook to be created. See https://api.github.com/hooks for possible names.
* @param config
* The configuration hash.
* @param events
* Can be null. Types of events to hook into.
*/
public GHHook createHook(String name, Map<String,String> config, Collection<GHEvent> events, boolean active) throws IOException {
List<String> ea = null;
if (events!=null) {
ea = new ArrayList<String>();
for (GHEvent e : events)
ea.add(e.name().toLowerCase(Locale.ENGLISH));
}
return new Poster(root,ApiVersion.V3)
.withCredential()
.with("name",name)
.with("active", active)
._with("config", config)
._with("events",ea)
.to(String.format("/repos/%s/%s/hooks",owner.login,this.name),GHHook.class).wrap(this);
}
public GHHook createWebHook(URL url, Collection<GHEvent> events) throws IOException {
return createHook("web",Collections.singletonMap("url",url.toExternalForm()),events,true);
}
public GHHook createWebHook(URL url) throws IOException {
return createWebHook(url,null);
}
// this is no different from getPullRequests(OPEN)
@@ -313,6 +413,9 @@ public class GHRepository {
/**
* Returns a set that represents the post-commit hook URLs.
* The returned set is live, and changes made to them are reflected to GitHub.
*
* @deprecated
* Use {@link #getHooks()} and {@link #createHook(String, Map, Collection, boolean)}
*/
public Set<URL> getPostCommitHooks() {
return postCommitHooks;
@@ -324,15 +427,11 @@ public class GHRepository {
private final Set<URL> postCommitHooks = new AbstractSet<URL>() {
private List<URL> getPostCommitHooks() {
try {
verifyMine();
HtmlForm f = getForm();
List<URL> r = new ArrayList<URL>();
for (HtmlInput i : f.getInputsByName("urls[]")) {
String v = i.getValueAttribute();
if (v.length()==0) continue;
r.add(new URL(v));
for (GHHook h : getHooks()) {
if (h.getName().equals("web")) {
r.add(new URL(h.getConfig().get("url")));
}
}
return r;
} catch (IOException e) {
@@ -353,22 +452,7 @@ public class GHRepository {
@Override
public boolean add(URL url) {
try {
String u = url.toExternalForm();
verifyMine();
HtmlForm f = getForm();
List<HtmlInput> controls = f.getInputsByName("urls[]");
for (HtmlInput i : controls) {
String v = i.getValueAttribute();
if (v.length()==0) continue;
if (v.equals(u))
return false; // already there
}
controls.get(controls.size()-1).setValueAttribute(u);
f.submit(null);
createWebHook(url);
return true;
} catch (IOException e) {
throw new GHException("Failed to update post-commit hooks",e);
@@ -376,37 +460,20 @@ public class GHRepository {
}
@Override
public boolean remove(Object o) {
public boolean remove(Object url) {
try {
String u = ((URL)o).toExternalForm();
verifyMine();
HtmlForm f = getForm();
List<HtmlInput> controls = f.getInputsByName("urls[]");
for (HtmlInput i : controls) {
String v = i.getValueAttribute();
if (v.length()==0) continue;
if (v.equals(u)) {
i.setValueAttribute("");
f.submit(null);
String _url = ((URL)url).toExternalForm();
for (GHHook h : getHooks()) {
if (h.getName().equals("web") && h.getConfig().get("url").equals(_url)) {
h.delete();
return true;
}
}
return false;
} catch (IOException e) {
throw new GHException("Failed to update post-commit hooks",e);
}
}
private HtmlForm getForm() throws IOException {
WebClient wc = root.createWebClient();
HtmlPage pg = (HtmlPage)wc.getPage(getUrl()+"/admin");
HtmlForm f = (HtmlForm) pg.getElementById("new_service");
return f;
}
};
/*package*/ GHRepository wrap(GitHub root) {
@@ -414,6 +481,46 @@ public class GHRepository {
return this;
}
/**
* Gets branches by {@linkplain GHBranch#getName() their names}.
*/
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)) {
p.wrap(this);
r.put(p.getName(),p);
}
return r;
}
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);
for (GHMilestone m : ms) {
m.owner = this;
m.root = root;
milestones.put(m.getNumber(), m);
}
return milestones;
}
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.owner = this;
m.root = root;
milestones.put(m.getNumber(), m);
}
return m;
}
public GHMilestone createMilestone(String title, String description) throws IOException {
return new Poster(root,V3).withCredential()
.with("title", title).with("description", description)
.to("/repos/"+owner.login+"/"+name+"/milestones", GHMilestone.class,"POST").wrap(this);
}
@Override
public String toString() {
return "Repository:"+owner.login+":"+name;

View File

@@ -23,7 +23,12 @@
*/
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.HashSet;
import java.util.Map;
import java.util.Set;
/**
@@ -32,77 +37,6 @@ import java.util.Set;
* @author Kohsuke Kawaguchi
*/
public class GHUser extends GHPerson {
private String name,company,location,created_at,blog,email;
private int followers_count;
/**
* Gets the human-readable name of the user, like "Kohsuke Kawaguchi"
*/
public String getName() {
return name;
}
/**
* Gets the company name of this user, like "Sun Microsystems, Inc."
*/
public String getCompany() {
return company;
}
/**
* Gets the location of this user, like "Santa Clara, California"
*/
public String getLocation() {
return location;
}
public String getCreatedAt() {
return created_at;
}
/**
* Gets the blog URL of this user.
*/
public String getBlog() {
return blog;
}
/**
* Gets the login ID of this user, like 'kohsuke'
*/
public String getLogin() {
return login;
}
/**
* Gets the e-mail address of the user.
*/
public String getEmail() {
return email;
}
public int getPublicGistCount() {
return public_gist_count;
}
public int getPublicRepoCount() {
return public_repo_count;
}
public int getFollowingCount() {
return following_count;
}
/**
* What appears to be a GitHub internal unique number that identifies this user.
*/
public int getId() {
return id;
}
public int getFollowersCount() {
return followers_count;
}
/**
* Follow this user.
@@ -121,17 +55,33 @@ public class GHUser extends GHPerson {
/**
* Lists the users that this user is following
*/
public Set<GHUser> getFollows() throws IOException {
@WithBridgeMethods(Set.class)
public GHPersonSet<GHUser> getFollows() throws IOException {
return root.retrieve("/user/show/"+login+"/following",JsonUsers.class).toSet(root);
}
/**
* Lists the users who are following this user.
*/
public Set<GHUser> getFollowers() throws IOException {
@WithBridgeMethods(Set.class)
public GHPersonSet<GHUser> getFollowers() throws IOException {
return root.retrieve("/user/show/"+login+"/followers",JsonUsers.class).toSet(root);
}
/**
* Gets the organization that this user belongs to publicly.
*/
@WithBridgeMethods(Set.class)
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)) {
if (names.add(o.getLogin())) // I've seen some duplicates in the data
orgs.add(root.getOrganization(o.getLogin()));
}
return orgs;
}
@Override
public String toString() {
return "User:"+login;

View File

@@ -23,14 +23,10 @@
*/
package org.kohsuke.github;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlForm;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import org.apache.commons.io.IOUtils;
import org.codehaus.jackson.map.DeserializationConfig.Feature;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.introspect.VisibilityChecker.Std;
import sun.misc.BASE64Encoder;
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;
@@ -39,18 +35,35 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.InterruptedIOException;
import java.io.Reader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Properties;
import java.util.TimeZone;
import java.util.zip.GZIPInputStream;
import static org.codehaus.jackson.annotate.JsonAutoDetect.Visibility.*;
import static org.kohsuke.github.ApiVersion.*;
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;
import sun.misc.BASE64Encoder;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlForm;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
/**
* Root of the GitHub API.
@@ -65,10 +78,17 @@ public class GitHub {
private final Map<String,GHUser> users = new HashMap<String, GHUser>();
private final Map<String,GHOrganization> orgs = new HashMap<String, GHOrganization>();
private String oauthAccessToken;
/*package*/ String oauthAccessToken;
private final String githubServer;
private GitHub(String login, String apiToken, String password) {
this.login = login;
private GitHub(String login, String apiToken, String password) {
this ("github.com", login, apiToken, password);
}
private GitHub(String githubServer, String login, String apiToken, String password) {
this.githubServer = githubServer;
this.login = login;
this.apiToken = apiToken;
this.password = password;
@@ -80,8 +100,9 @@ public class GitHub {
encodedAuthorization = null;
}
private GitHub (String oauthAccessToken) throws IOException {
private GitHub (String githubServer, String oauthAccessToken) throws IOException {
this.githubServer = githubServer;
this.password = null;
this.encodedAuthorization = null;
@@ -115,7 +136,11 @@ public class GitHub {
}
public static GitHub connectUsingOAuth (String accessToken) throws IOException {
return new GitHub(accessToken);
return connectUsingOAuth("github.com", accessToken);
}
public static GitHub connectUsingOAuth (String githubServer, String accessToken) throws IOException {
return new GitHub(githubServer, accessToken);
}
/**
* Connects to GitHub anonymously.
@@ -134,10 +159,10 @@ public class GitHub {
/*package*/ URL getApiURL(ApiVersion v, String tailApiUrl) throws IOException {
if (oauthAccessToken != null) {
// append the access token
tailApiUrl = tailApiUrl + "?access_token=" + oauthAccessToken;
tailApiUrl = tailApiUrl + (tailApiUrl.indexOf('?')>=0 ?'&':'?') + "access_token=" + oauthAccessToken;
}
return new URL(v.url+tailApiUrl);
return new URL(v.getApiVersionBaseUrl(githubServer)+tailApiUrl);
}
/*package*/ <T> T retrieve(String tailApiUrl, Class<T> type) throws IOException {
@@ -149,7 +174,7 @@ public class GitHub {
}
/*package*/ <T> T retrieveWithAuth(String tailApiUrl, Class<T> type) throws IOException {
return retrieveWithAuth(tailApiUrl,type,"GET");
return retrieveWithAuth(tailApiUrl, type, "GET");
}
/*package*/ <T> T retrieveWithAuth3(String tailApiUrl, Class<T> type) throws IOException {
@@ -166,33 +191,141 @@ public class GitHub {
private <T> T _retrieve(String tailApiUrl, Class<T> type, String method, boolean withAuth, ApiVersion v) throws IOException {
while (true) {// loop while API rate limit is hit
HttpURLConnection uc = (HttpURLConnection) getApiURL(v,tailApiUrl).openConnection();
if (withAuth && this.oauthAccessToken == null)
uc.setRequestProperty("Authorization", "Basic " + encodedAuthorization);
uc.setRequestMethod(method);
if (method.equals("PUT")) {
uc.setDoOutput(true);
uc.setRequestProperty("Content-Length","0");
uc.getOutputStream().close();
}
HttpURLConnection uc = setupConnection(method, withAuth, getApiURL(v, tailApiUrl));
try {
InputStreamReader r = new InputStreamReader(uc.getInputStream(), "UTF-8");
if (type==null) {
String data = IOUtils.toString(r);
return null;
}
return MAPPER.readValue(r,type);
return parse(uc,type);
} catch (IOException e) {
handleApiError(e,uc);
}
}
}
/**
* Loads pagenated resources.
*
* Every iterator call reports a new batch.
*/
/*package*/ <T> Iterator<T> retrievePaged(final String tailApiUrl, final Class<T> type, final boolean withAuth, final ApiVersion v) {
return new Iterator<T>() {
/**
* The next batch to be returned from {@link #next()}.
*/
T next;
/**
* URL of the next resource to be retrieved, or null if no more data is available.
*/
URL url;
{
try {
url = getApiURL(v, tailApiUrl);
} catch (IOException e) {
throw new Error(e);
}
}
public boolean hasNext() {
fetch();
return next!=null;
}
public T next() {
fetch();
T r = next;
if (r==null) throw new NoSuchElementException();
next = null;
return r;
}
public void remove() {
throw new UnsupportedOperationException();
}
private void fetch() {
if (next!=null) return; // already fetched
if (url==null) return; // no more data to fetch
try {
while (true) {// loop while API rate limit is hit
HttpURLConnection uc = setupConnection("GET", withAuth, url);
try {
next = parse(uc,type);
assert next!=null;
findNextURL(uc);
return;
} catch (IOException e) {
handleApiError(e,uc);
}
}
} catch (IOException e) {
throw new Error(e);
}
}
/**
* Locate the next page from the pagination "Link" tag.
*/
private void findNextURL(HttpURLConnection uc) throws MalformedURLException {
url = null; // start defensively
String link = uc.getHeaderField("Link");
if (link==null) return;
for (String token : link.split(", ")) {
if (token.endsWith("rel=\"next\"")) {
// found the next page. This should look something like
// <https://api.github.com/repos?page=3&per_page=100>; rel="next"
int idx = token.indexOf('>');
url = new URL(token.substring(1,idx));
return;
}
}
// no more "next" link. we are done.
}
};
}
private HttpURLConnection setupConnection(String method, boolean withAuth, URL url) throws IOException {
HttpURLConnection uc = (HttpURLConnection) url.openConnection();
if (withAuth && this.oauthAccessToken == null)
uc.setRequestProperty("Authorization", "Basic " + encodedAuthorization);
uc.setRequestMethod(method);
if (method.equals("PUT")) {
uc.setDoOutput(true);
uc.setRequestProperty("Content-Length","0");
uc.getOutputStream().close();
}
uc.setRequestProperty("Accept-Encoding", "gzip");
return uc;
}
private <T> T parse(HttpURLConnection uc, Class<T> type) throws IOException {
InputStreamReader r = null;
try {
r = new InputStreamReader(wrapStream(uc, uc.getInputStream()), "UTF-8");
if (type==null) {
String data = IOUtils.toString(r);
return null;
}
return MAPPER.readValue(r,type);
} finally {
IOUtils.closeQuietly(r);
}
}
/**
* Handles the "Content-Encoding" header.
*/
private InputStream wrapStream(HttpURLConnection uc, InputStream in) throws IOException {
String encoding = uc.getContentEncoding();
if (encoding==null) return in;
if (encoding.equals("gzip")) return new GZIPInputStream(in);
throw new UnsupportedOperationException("Unexpected Content-Encoding: "+encoding);
}
/**
* If the error is because of the API limit, wait 10 sec and return normally.
* Otherwise throw an exception reporting an error.
@@ -211,36 +344,30 @@ public class GitHub {
if (e instanceof FileNotFoundException)
throw e; // pass through 404 Not Found to allow the caller to handle it intelligently
InputStream es = uc.getErrorStream();
if (es!=null)
throw (IOException)new IOException(IOUtils.toString(es,"UTF-8")).initCause(e);
else
throw e;
InputStream es = wrapStream(uc, uc.getErrorStream());
try {
if (es!=null)
throw (IOException)new IOException(IOUtils.toString(es,"UTF-8")).initCause(e);
else
throw e;
} finally {
IOUtils.closeQuietly(es);
}
}
/**
* Gets the {@link GHUser} that represents yourself.
*/
public GHUser getMyself() throws IOException {
@WithBridgeMethods(GHUser.class)
public GHMyself 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);
}
GHMyself u = retrieveWithAuth3("/user", GHMyself.class);
u.root = this;
users.put(u.getLogin(), u);
return u;
}
/**
@@ -249,17 +376,9 @@ public class GitHub {
public GHUser getUser(String login) throws IOException {
GHUser u = users.get(login);
if (u == null) {
if (oauthAccessToken != null) {
u = retrieve("/user/show/" + login, JsonUser.class).user;
u.root = this;
users.put(u.getLogin(), u);
} else {
u = retrieve("/user/show/" + login, JsonUser.class).user;
u.root = this;
users.put(login, u);
}
u = retrieve3("/users/" + login, GHUser.class);
u.root = this;
users.put(u.getLogin(), u);
}
return u;
}
@@ -287,9 +406,42 @@ public class GitHub {
return o;
}
/**
* Gets the repository object from 'user/reponame' string that GitHub calls as "repository name"
*
* @see GHRepository#getName()
*/
public GHRepository getRepository(String name) throws IOException {
String[] tokens = name.split("/");
return getUser(tokens[0]).getRepository(tokens[1]);
}
public Map<String, GHOrganization> getMyOrganizations() throws IOException {
return retrieveWithAuth("/organizations",JsonOrganizations.class).wrap(this);
}
/**
* Public events visible to you. Equivalent of what's displayed on https://github.com/
*/
public List<GHEventInfo> getEvents() throws IOException {
// TODO: pagenation
GHEventInfo[] events = retrieve3("/events", GHEventInfo[].class);
for (GHEventInfo e : events)
e.wrapUp(this);
return Arrays.asList(events);
}
/**
* Parses the GitHub event object.
*
* This is primarily intended for receiving a POST HTTP call from a hook.
* Unfortunately, hook script payloads aren't self-descriptive, so you need
* to know the type of the payload you are expecting.
*/
public <T extends GHEventPayload> T parseEventPayload(Reader r, Class<T> type) throws IOException {
T t = MAPPER.readValue(r, type);
t.wrapUp(this);
return t;
}
/**
@@ -309,7 +461,7 @@ public class GitHub {
*/
public boolean isCredentialValid() throws IOException {
try {
retrieveWithAuth("/user/show",JsonUser.class);
retrieveWithAuth3("/user",GHUser.class);
return true;
} catch (IOException e) {
return false;
@@ -337,9 +489,12 @@ public class GitHub {
}
/*package*/ static Date parseDate(String timestamp) {
if (timestamp==null) return null;
for (String f : TIME_FORMATS) {
try {
return new SimpleDateFormat(f).parse(timestamp);
SimpleDateFormat df = new SimpleDateFormat(f);
df.setTimeZone(TimeZone.getTimeZone("GMT"));
return df.parse(timestamp);
} catch (ParseException e) {
// try next
}

View File

@@ -1,7 +1,7 @@
/*
* The MIT License
*
* Copyright (c) 2010, Kohsuke Kawaguchi
* 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
@@ -21,11 +21,16 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.kohsuke.github;
/**
* @author Kohsuke Kawaguchi
* @author Eric Maupin
*/
class JsonUser {
public GHUser user;
class JsonBranch {
GHBranch branch;
GHBranch wrap(GHRepository r) {
return branch.wrap(r);
}
}

View File

@@ -1,7 +1,7 @@
/*
* The MIT License
*
* Copyright (c) 2010, Kohsuke Kawaguchi
* 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
@@ -21,17 +21,20 @@
* 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 JsonPullRequest {
public GHPullRequest pull;
import java.util.List;
public GHPullRequest wrap(GHRepository owner) {
pull.owner = owner;
pull.root = owner.root;
return pull;
/**
* @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

@@ -0,0 +1,8 @@
package org.kohsuke.github;
/**
* @author Kohsuke Kawaguchi
*/
class JsonMyself {
GHMyself user;
}

View File

@@ -1,41 +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 JsonPullRequests {
public List<GHPullRequest> pulls;
public List<GHPullRequest> wrap(GHRepository owner) {
for (GHPullRequest pull : pulls) {
pull.owner = owner;
pull.root = owner.root;
}
return pulls;
}
}

View File

@@ -34,8 +34,8 @@ import java.util.Set;
class JsonUsers {
public List<String> users;
public Set<GHUser> toSet(GitHub root) throws IOException {
Set<GHUser> r = new HashSet<GHUser>();
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

@@ -94,7 +94,7 @@ class Poster {
return _with(key, value);
}
private Poster _with(String key, Object value) {
public Poster _with(String key, Object value) {
if (value!=null) {
args.add(new Entry(key,value));
}
@@ -125,9 +125,13 @@ class Poster {
uc.setRequestProperty("Content-type","application/x-www-form-urlencoded");
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);
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);
}
} else {
uc.setRequestProperty("Authorization", "Basic " + root.encodedAuthorization);
}

View File

@@ -1,9 +1,15 @@
package org.kohsuke;
import junit.framework.TestCase;
import org.kohsuke.github.GHEvent;
import org.kohsuke.github.GHEventInfo;
import org.kohsuke.github.GHEventPayload;
import org.kohsuke.github.GHHook;
import org.kohsuke.github.GHBranch;
import org.kohsuke.github.GHIssueState;
import org.kohsuke.github.GHMyself;
import org.kohsuke.github.GHOrganization;
import org.kohsuke.github.GHOrganization.Permission;
import org.kohsuke.github.GHPullRequest;
import org.kohsuke.github.GHRepository;
import org.kohsuke.github.GHTeam;
import org.kohsuke.github.GHUser;
@@ -11,7 +17,9 @@ import org.kohsuke.github.GitHub;
import java.io.IOException;
import java.net.URL;
import java.util.Map;
import java.util.Set;
import java.util.List;
/**
* Unit test for simple App.
@@ -22,6 +30,27 @@ public class AppTest extends TestCase {
assertFalse(GitHub.connect("totally","bogus").isCredentialValid());
}
public void testFetchPullRequest() throws Exception {
GitHub gh = GitHub.connect();
GHRepository r = gh.getOrganization("jenkinsci").getRepository("jenkins");
assertEquals("master",r.getMasterBranch());
r.getPullRequest(1);
r.getPullRequests(GHIssueState.OPEN);
}
public void tryGetMyself() throws Exception {
GitHub hub = GitHub.connect();
GHMyself me = hub.getMyself();
System.out.println(me);
GHUser u = hub.getUser("kohsuke2");
System.out.println(u);
for (List<GHRepository> lst : me.iterateRepositories(100)) {
for (GHRepository r : lst) {
System.out.println(r.getPushedAt());
}
}
}
public void tryOrgFork() throws Exception {
GitHub gh = GitHub.connect();
gh.getUser("kohsuke").getRepository("rubywm").forkTo(gh.getOrganization("jenkinsci"));
@@ -38,9 +67,45 @@ public class AppTest extends TestCase {
Set<String> members = gitHub.getOrganization("jenkinsci").getRepository("violations-plugin").getCollaboratorNames();
System.out.println(members.contains("kohsuke"));
}
public void testMemberOrgs() throws Exception {
GitHub gitHub = GitHub.connect();
Set<GHOrganization> o = gitHub.getUser("kohsuke").getOrganizations();
System.out.println(o);
}
public void testBranches() throws Exception {
GitHub gitHub = GitHub.connect();
Map<String,GHBranch> b =
gitHub.getUser("jenkinsci").getRepository("jenkins").getBranches();
System.out.println(b);
}
public void tryHook() throws Exception {
GitHub gitHub = GitHub.connect();
GHRepository r = gitHub.getMyself().getRepository("test2");
GHHook hook = r.createWebHook(new URL("http://www.google.com/"));
System.out.println(hook);
for (GHHook h : r.getHooks())
h.delete();
}
public void testEventApi() throws Exception {
GitHub gitHub = GitHub.connect();
for (GHEventInfo ev : gitHub.getEvents()) {
System.out.println(ev);
if (ev.getType()==GHEvent.PULL_REQUEST) {
GHEventPayload.PullRequest pr = ev.getPayload(GHEventPayload.PullRequest.class);
System.out.println(pr.getNumber());
System.out.println(pr.getPullRequest());
}
}
}
public void testApp() throws IOException {
GitHub gitHub = GitHub.connect();
System.out.println(gitHub.getMyself().getEmails());
// GHRepository r = gitHub.connect().getOrganization("jenkinsci").createRepository("kktest4", "Kohsuke's test", "http://kohsuke.org/", "Everyone", true);
// r.fork();
@@ -132,6 +197,15 @@ public class AppTest extends TestCase {
System.out.println(hooks);
}
public void testOrgRepositories() throws IOException {
GitHub gitHub = GitHub.connect();
GHOrganization j = gitHub.getOrganization("jenkinsci");
long start = System.currentTimeMillis();
Map<String, GHRepository> repos = j.getRepositories();
long end = System.currentTimeMillis();
System.out.printf("%d repositories in %dms\n",repos.size(),end-start);
}
public void testOrganization() throws IOException {
GitHub gitHub = GitHub.connect();
GHOrganization j = gitHub.getOrganization("jenkinsci");

View File

@@ -0,0 +1,32 @@
package org.kohsuke;
import org.kohsuke.github.GHEventPayload;
import org.kohsuke.github.GitHub;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.jetty.JettyRunner;
import java.io.IOException;
import java.io.StringReader;
/**
* App to test the hook script. You need some internet-facing server that can forward the request to you
* (typically via SSH reverse port forwarding.)
*
* @author Kohsuke Kawaguchi
*/
public class HookApp {
public static void main(String[] args) throws Exception {
// GitHub.connect().getMyself().getRepository("sandbox").createWebHook(
// new URL("http://173.203.118.45:18080/"), EnumSet.of(GHEvent.PULL_REQUEST));
JettyRunner jr = new JettyRunner(new HookApp());
jr.addHttpListener(8080);
jr.start();
}
public void doIndex(StaplerRequest req) throws IOException {
String str = req.getParameter("payload");
System.out.println(str);
GHEventPayload.PullRequest o = GitHub.connect().parseEventPayload(new StringReader(str),GHEventPayload.PullRequest.class);
System.out.println(o);
}
}