Compare commits

...

118 Commits

Author SHA1 Message Date
Kohsuke Kawaguchi
553df7ac85 [maven-release-plugin] prepare release github-api-1.45 2013-11-09 15:19:23 -08:00
Kohsuke Kawaguchi
9648602252 Merge branch 'pull-51' 2013-11-09 15:17:21 -08:00
Kohsuke Kawaguchi
63d0d13330 Better and more secure to use OAuth. 2013-11-09 15:17:01 -08:00
Kohsuke Kawaguchi
ff9b538a49 doc touch up 2013-11-09 15:11:02 -08:00
Kohsuke Kawaguchi
6632da34c0 this dependency is for testing 2013-11-09 15:08:07 -08:00
evanchooly
178c9ff4d0 add support (most of) the release-related endpoints 2013-11-04 20:08:24 -05:00
Kohsuke Kawaguchi
bfefeae5c1 Merged pull request #45 with some changes 2013-11-02 18:15:31 -07:00
Kohsuke Kawaguchi
ebf953cbc4 Massaging the pull request.
- GHPerson really should be an abstract class
- static listPullRequests method does not fit the design of this
  library, although I'm very sympathetic as to why Luca wanted to do it.
  Nonetheless I'm removing this in favor of the lazy loading of the
  state to avoid unnecessary calls in the future.
2013-11-02 18:13:40 -07:00
Luca Milanesio
19ec3321ae Gets commit details of a pull request.
Retrieves the list of commits included in the pull request.
Commit object returned is not a full GHCommit and includes
just a small subset of information.
2013-10-09 16:59:18 +01:00
Kohsuke Kawaguchi
d82af9f1a0 Merge pull request #50 from pescuma/jackson
Updates Jackson to 2.2.3
2013-10-02 08:31:42 -07:00
Ricardo Pescuma Domenecci
4712d2c8ac Using version 2 of jackson 2013-09-28 14:25:23 -03:00
Ricardo Pescuma Domenecci
a2df4217fd Updated issue label with ifts fields 2013-09-28 12:43:01 -03:00
Luca Milanesio
096c96550b Allows fetching pull requests by knowing repo-name and owner. 2013-09-27 09:18:22 +01:00
Luca Milanesio
2fb3f31930 GHRepository owner is NOT a GHUser but more generically a GHPerson.
When GitHub repositories are associated to organisations,
the owner is NOT the user but the org itself.
2013-09-27 09:12:54 +01:00
Luca Milanesio
f52b4a2e11 Allows to define page size for repository lists.
Extension of the listRepositories() with the desired
pageSize. This allows to reduce the number of calls
to GitHub API for fetching the entire set of repositories
browsing all the pages.

Additionally allows to match the UX paging with the
underlying GitHub API paging, increasing performance
and reducing hourly API allowance.
2013-09-16 00:41:10 +01:00
Kohsuke Kawaguchi
bbc78ffec6 [maven-release-plugin] prepare for next development iteration 2013-09-07 13:32:24 +01:00
Kohsuke Kawaguchi
3606f412b3 [maven-release-plugin] prepare release github-api-1.44 2013-09-07 13:32:15 +01:00
Kohsuke Kawaguchi
6e0e94094b Massaging the changes 2013-09-07 13:29:23 +01:00
Kohsuke Kawaguchi
976960f495 Merge pull request #42 from paulbutenko/master
Commit's short info model
2013-09-07 05:25:58 -07:00
Kohsuke Kawaguchi
d2f2f3b2d3 Merge pull request #44 from stephenc/is-anonymous
Provide a way to determine if the connection is anonymous
2013-09-07 05:18:29 -07:00
Kohsuke Kawaguchi
2da7c45840 Merge pull request #43 from stephenc/master
GHMyself should allow accessing the private repos and orgs too
2013-09-07 05:18:00 -07:00
Stephen Connolly
fb078de627 Provide a way to determine if the connection is anonymous 2013-08-29 14:33:04 +01:00
Stephen Connolly
da46b7fddb GHMyself should allow accessing the private repos and orgs too 2013-08-29 14:29:58 +01:00
Paul Butenko
c4e0729b7d GHCOmpare reverted, getCommit changed to getCommitShortInfo 2013-08-17 01:09:55 +03:00
Paul Butenko
eee9f0ace5 Original formating restored 2013-08-17 00:48:42 +03:00
Paul Butenko
d0692458a3 New model for short info of the commit was added 2013-08-17 00:41:45 +03:00
Kohsuke Kawaguchi
c96e6c7c92 [maven-release-plugin] prepare for next development iteration 2013-07-06 22:26:45 -07:00
Kohsuke Kawaguchi
4956278f17 [maven-release-plugin] prepare release github-api-1.43 2013-07-06 22:26:39 -07:00
Kohsuke Kawaguchi
5858a86624 using the latest 2013-07-06 22:24:21 -07:00
Kohsuke Kawaguchi
c87d178a6a added a method that matches the convention elsewhere. 2013-07-06 17:49:56 -07:00
Kohsuke Kawaguchi
6fc872b1fd this is no longer efficient 2013-07-06 17:49:46 -07:00
Kohsuke Kawaguchi
589c5783a0 "myself" only makes sense when there's a credential 2013-05-24 12:01:56 -07:00
Kohsuke Kawaguchi
40165628d6 [maven-release-plugin] prepare for next development iteration 2013-05-07 11:09:20 -07:00
Kohsuke Kawaguchi
435be77249 [maven-release-plugin] prepare release github-api-1.42 2013-05-07 11:09:13 -07:00
Kohsuke Kawaguchi
b932ba856d fixed NPE 2013-05-07 11:07:12 -07:00
Kohsuke Kawaguchi
094514f617 Merge pull request #36 from spiffxp/require-credentials-fix
Allow oauthToken to be used without login
2013-05-06 14:34:06 -07:00
Kohsuke Kawaguchi
fab96879d0 Merge pull request #37 from spiffxp/pr-comments-fix
Force issues-based API route for PR comments
2013-05-06 14:33:29 -07:00
Kohsuke Kawaguchi
367a5f0c57 Merge pull request #38 from janinko/fixPullRequestPayload
add repository to Pull Request payload and wrap the PR with the repository
2013-05-06 14:32:11 -07:00
Honza Brázdil
0d2ecfbc67 add repository to Pull Request payload and wrap the PR with the repository 2013-05-02 18:11:40 +02:00
Aaron Crickenberger
5410ba3b1d Force issues-based API route for PR comments
pulls/:number/comments is used for review_comments
2013-05-01 13:59:45 -07:00
Aaron Crickenberger
716bfd4611 requireCredential should allow for oauthToken with no login 2013-04-30 15:41:08 -07:00
Kohsuke Kawaguchi
3830a58493 [maven-release-plugin] prepare for next development iteration 2013-04-23 10:30:54 -07:00
Kohsuke Kawaguchi
31d5cf6129 [maven-release-plugin] prepare release github-api-1.41 2013-04-23 10:30:48 -07:00
Kohsuke Kawaguchi
8c78d20e6e doc improvement 2013-04-23 10:25:57 -07:00
Kohsuke Kawaguchi
abe78cf0bb Formatting only changes 2013-04-23 10:20:07 -07:00
Kohsuke Kawaguchi
eeebfd5f04 added a little helper code that generates an OAuth token for ~/.github 2013-04-23 10:19:50 -07:00
Kohsuke Kawaguchi
5e3d3dd023 In fact there's no point in looking up the 'token' property since API token is no longer supported by GitHub 2013-04-23 10:05:45 -07:00
Kohsuke Kawaguchi
60175ebfad if the user name is available, pass that in and save a lookup 2013-04-23 10:05:06 -07:00
Kohsuke Kawaguchi
c6fafe453f Cleaned up the authentication part of the code 2013-04-23 10:04:20 -07:00
Kohsuke Kawaguchi
838ecd0dd8 Merge branch 'pull-33' 2013-04-23 09:40:37 -07:00
Kohsuke Kawaguchi
beec605e2c [FIXED issue #34] API route for pull requests uses 'pulls' 2013-04-23 09:28:00 -07:00
watsonian
da1405a060 Remove apiToken completely. 2013-04-20 11:58:52 -07:00
watsonian
e38eeae533 Update constructor to use OAuth tokens rather than API tokens. 2013-04-20 11:43:54 -07:00
watsonian
8442e7e326 Declare the exception type. 2013-04-19 17:10:29 -07:00
watsonian
e6c82e2003 Stop using deprecated API tokens for Enterprise.
Authentication by API token is deprecated and doesn't work anymore.
Instead, authentication should be done via OAuth token now.
2013-04-19 16:27:09 -07:00
Kohsuke Kawaguchi
c4de972c53 [maven-release-plugin] prepare for next development iteration 2013-04-16 12:30:34 -07:00
Kohsuke Kawaguchi
d2adbaec89 [maven-release-plugin] prepare release github-api-1.40 2013-04-16 12:30:28 -07:00
Kohsuke Kawaguchi
72736588c9 release botched because of the Nexus staging deployer. rolling back to the parent POM 4 2013-04-16 12:28:38 -07:00
Kohsuke Kawaguchi
46e726363a [maven-release-plugin] prepare for next development iteration 2013-04-16 12:22:26 -07:00
Kohsuke Kawaguchi
f7e5292b8c [maven-release-plugin] prepare release github-api-1.39 2013-04-16 12:22:20 -07:00
Kohsuke Kawaguchi
f67954fa3e Merge branch 'pull-32' 2013-04-16 12:19:33 -07:00
Kohsuke Kawaguchi
ba6c6a17ae doc improvement 2013-04-16 12:18:50 -07:00
Kohsuke Kawaguchi
c21d61a70d TAB -> WS 2013-04-16 12:17:30 -07:00
Kohsuke Kawaguchi
df857d3a84 doc improvement 2013-04-16 12:14:06 -07:00
Honza Brázdil
e9564f101b implement retrieving of access token 2013-04-14 05:50:11 +02:00
Honza Brázdil
e8a2a69649 Implement GHEventPayload.IssueComment 2013-04-13 17:09:03 +02:00
Kohsuke Kawaguchi
ba416b1294 [maven-release-plugin] prepare for next development iteration 2013-04-06 19:06:44 -07:00
Kohsuke Kawaguchi
3024b598ad [maven-release-plugin] prepare release github-api-1.38 2013-04-06 19:06:37 -07:00
Kohsuke Kawaguchi
5ac7a34a13 using a newer POM 2013-04-06 19:03:47 -07:00
Kohsuke Kawaguchi
b63181d9b5 need to finish the initialization 2013-04-06 13:56:04 -07:00
Kohsuke Kawaguchi
a3119e2cc7 [maven-release-plugin] prepare for next development iteration 2013-03-14 19:06:11 -07:00
Kohsuke Kawaguchi
e8b8971b72 [maven-release-plugin] prepare release github-api-1.37 2013-03-14 19:06:05 -07:00
Kohsuke Kawaguchi
d5f5fa0e0a doc improvement 2013-03-14 19:02:05 -07:00
Kohsuke Kawaguchi
c284f90a1a adding some more type-safe overloads 2013-03-14 18:56:37 -07:00
Michael Clarke
349df60ce8 Adding in support for the refs command and filters in the API 2013-02-15 23:55:04 +00:00
Michael Clarke
5ccc3f4ccd Adding in support for the compare command in the API 2013-02-15 09:43:27 +00:00
johnou
8ba61bb3a6 Revert 2ef5dec466 and add source encoding to deter build warnings. 2013-01-24 17:31:43 +01:00
Johno Crawford
cb3d413e5e [maven-release-plugin] prepare for next development iteration 2013-01-24 17:23:07 +01:00
Johno Crawford
1837699d8c [maven-release-plugin] prepare release github-api-1.36 2013-01-24 17:22:58 +01:00
johnou
2ef5dec466 Temporary change for release. 2013-01-24 17:20:16 +01:00
Johno Crawford
a46c7acbd2 Merge branch 'master' of github.com:kohsuke/github-api 2013-01-24 16:58:42 +01:00
johnou
769d645237 Normalize api url for oauth constructor. 2013-01-24 16:57:51 +01:00
Kohsuke Kawaguchi
389330df2e [maven-release-plugin] prepare for next development iteration 2013-01-06 16:47:29 -08:00
Kohsuke Kawaguchi
9d75913005 [maven-release-plugin] prepare release github-api-1.35 2013-01-06 16:47:24 -08:00
Kohsuke Kawaguchi
887ca772e0 switching to Markdown 2013-01-06 16:45:17 -08:00
Kohsuke Kawaguchi
45286598aa adding OAuth support in ~/.github 2013-01-06 16:43:43 -08:00
Kohsuke Kawaguchi
2e074b5bc4 follow up fix to the pull request #28 2013-01-06 16:25:02 -08:00
Johno Crawford
560e3c257a Refactored test. 2013-01-06 16:25:02 -08:00
Johno Crawford
6e0202fa0b Added membership checks. 2013-01-06 16:25:01 -08:00
Kohsuke Kawaguchi
ef241b1a07 Merge pull request #27 from johnou/deprecated-password
Password is no longer required for api usage and fix for broken base64 encoding.
2013-01-06 16:14:15 -08:00
Johno Crawford
f80cb541d5 Fix base64 encoding. 2013-01-06 21:11:19 +01:00
Johno Crawford
1fe61f72e2 Password is no longer required for api usage. 2013-01-06 13:08:41 +01:00
Jerome Lacoste
bf1b0edfd2 Merge pull request #26 from johnou/remove-webclient
Removed web client and proprietary api usage.
2013-01-06 00:34:15 -08:00
johnou
f0ab946b88 Removed proprietary api usage. 2013-01-06 04:27:18 +01:00
Johno Crawford
975ef1a43d Removed last traces of web client. 2013-01-06 04:06:55 +01:00
Kohsuke Kawaguchi
7064865157 [maven-release-plugin] prepare for next development iteration 2013-01-05 17:18:17 -08:00
Kohsuke Kawaguchi
3dd738b0db [maven-release-plugin] prepare release github-api-1.34 2013-01-05 17:18:11 -08:00
Kohsuke Kawaguchi
6480dde247 oops test failures 2013-01-05 17:15:46 -08:00
Kohsuke Kawaguchi
555dab7403 Merge branch 'pull-22' 2013-01-05 17:11:58 -08:00
Kohsuke Kawaguchi
13158a28e1 turns out we never exposed the ability to specify the custom URL.
So no backward compatibility provision is needed.
Also in this change, I stopped exposin the password. See the code comment for more details
2013-01-05 17:10:24 -08:00
Kohsuke Kawaguchi
cbaca87bbc massaging this a bit to accept the full URL 2013-01-05 16:08:42 -08:00
Kohsuke Kawaguchi
5166202f67 follow-up fix and documenting the expected value 2013-01-05 15:59:02 -08:00
johnou
35d45ca47d JENKINS-13726: Github plugin should work with Guthub enterprise by allowing for overriding the github URL. 2013-01-06 00:47:44 +01:00
Honza Brázdil
b66ede98c7 Retrieve repository directly. 2012-10-20 23:24:56 +02:00
Kohsuke Kawaguchi
1ba8f2ccbf Update pom.xml
Added <repository> definition
2012-10-17 07:45:15 -07:00
Kohsuke Kawaguchi
82133c117a [maven-release-plugin] prepare for next development iteration 2012-09-13 16:31:58 -07:00
Kohsuke Kawaguchi
f71afca828 [maven-release-plugin] prepare release github-api-1.33 2012-09-13 16:31:52 -07:00
Kohsuke Kawaguchi
87f5231c9a updated a test 2012-09-13 16:28:50 -07:00
Kohsuke Kawaguchi
b17f506c20 clean up 2012-09-13 16:23:57 -07:00
Kohsuke Kawaguchi
7f15f12668 completed code to create a new issue 2012-09-13 16:23:06 -07:00
Kohsuke Kawaguchi
e53e62bfa0 added code to create a new issue 2012-09-13 15:56:05 -07:00
Kohsuke Kawaguchi
2e74517a4a fixed issue #20
PagedIterator is updated to cope with the base iterator returning array of length 0
2012-09-13 15:46:25 -07:00
Kohsuke Kawaguchi
aed888051e added a method to retrieve a single issue 2012-09-13 15:36:32 -07:00
Kohsuke Kawaguchi
bd584124bb Merge pull request #19 from janinko/Issues-19-PagedIterable_dosnt_use_authentication
PagedIterable dosn't use authentication
2012-09-13 15:32:56 -07:00
Honza Brázdil
e658a7fa6b send authentication header on all requests 2012-09-12 18:04:59 +02:00
Kohsuke Kawaguchi
52108707bb Merge pull request #18 from janinko/patch-1
When using lazy population, this is not deprecated
2012-09-07 09:52:12 -07:00
Honza Brázdil
0e226a8f78 When using lazy population, this is not deprecated 2012-09-06 14:23:51 +03:00
Kohsuke Kawaguchi
1bf3e025b8 [maven-release-plugin] prepare for next development iteration 2012-09-05 19:30:18 -07:00
32 changed files with 1844 additions and 264 deletions

1
.gitignore vendored
View File

@@ -1,4 +1,5 @@
target
.idea/
*.iml
*.ipr
*.iws

44
pom.xml
View File

@@ -3,11 +3,11 @@
<parent>
<groupId>org.kohsuke</groupId>
<artifactId>pom</artifactId>
<version>3</version>
<version>6</version>
</parent>
<artifactId>github-api</artifactId>
<version>1.32</version>
<version>1.45</version>
<name>GitHub API for Java</name>
<url>http://github-api.kohsuke.org/</url>
<description>GitHub API for Java</description>
@@ -25,6 +25,10 @@
</site>
</distributionManagement>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<plugin>
@@ -44,16 +48,14 @@
<dependencies>
<dependency>
<groupId>org.jvnet.hudson</groupId>
<artifactId>htmlunit</artifactId>
<version>2.6-hudson-2</version>
<exclusions>
<exclusion>
<!-- hides JDK DOM classes in Eclipse -->
<groupId>xml-apis</groupId>
<artifactId>xml-apis</artifactId>
</exclusion>
</exclusions>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.7</version>
</dependency>
<dependency>
<groupId>junit</groupId>
@@ -62,9 +64,9 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.9</version>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.2.3</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
@@ -83,7 +85,19 @@
<version>1.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit</artifactId>
<version>3.1.0.201310021548-r</version>
<scope>test</scope>
</dependency>
</dependencies>
<repositories>
<repository>
<id>repo.jenkins-ci.org</id>
<url>http://repo.jenkins-ci.org/public/</url>
</repository>
</repositories>
<reporting>
<plugins>

View File

@@ -0,0 +1,108 @@
package org.kohsuke.github;
import java.io.IOException;
import java.util.Date;
/**
* Asset in a release.
*
* @see GHRelease#getAssets()
*/
public class GHAsset {
GitHub root;
GHRepository owner;
private String url;
private String id;
private String name;
private String label;
private String state;
private String content_type;
private long size;
private long download_count;
private Date created_at;
private Date updated_at;
public String getContentType() {
return content_type;
}
public void setContentType(String contentType) throws IOException {
edit("content_type", contentType);
this.content_type = contentType;
}
public Date getCreatedAt() {
return created_at;
}
public long getDownloadCount() {
return download_count;
}
public String getId() {
return id;
}
public String getLabel() {
return label;
}
public void setLabel(String label) throws IOException {
edit("label", label);
this.label = label;
}
public String getName() {
return name;
}
public GHRepository getOwner() {
return owner;
}
public GitHub getRoot() {
return root;
}
public long getSize() {
return size;
}
public String getState() {
return state;
}
public Date getUpdatedAt() {
return updated_at;
}
public String getUrl() {
return url;
}
private void edit(String key, Object value) throws IOException {
new Requester(root)._with(key, value).method("PATCH").to(getApiRoute());
}
public void delete() throws IOException {
new Requester(root).method("DELETE").to(getApiRoute());
}
private String getApiRoute() {
return "/repos/" + owner.getOwnerName() + "/" + owner.getName() + "/releases/assets/" + id;
}
GHAsset wrap(GHRelease release) {
this.owner = release.getOwner();
this.root = owner.root;
return this;
}
public static GHAsset[] wrap(GHAsset[] assets, GHRelease release) {
for (GHAsset aTo : assets) {
aTo.wrap(release);
}
return assets;
}
}

View File

@@ -0,0 +1,94 @@
package org.kohsuke.github;
import java.net.URL;
import java.util.Collection;
import java.util.Date;
import java.util.List;
/**
* Generated OAuth token
*
* @author janinko
* @see GitHub#createToken(Collection, String, String)
* @see http://developer.github.com/v3/oauth/#create-a-new-authorization
*/
public class GHAuthorization {
public static final String USER = "user";
public static final String USER_EMAIL = "user:email";
public static final String USER_FOLLOW = "user:follow";
public static final String PUBLIC_REPO = "public_repo";
public static final String REPO = "repo";
public static final String REPO_STATUS = "repo:status";
public static final String DELETE_REPO = "delete_repo";
public static final String NOTIFICATIONS = "notifications";
public static final String GIST = "gist";
private GitHub root;
private int id;
private String url;
private List<String> scopes;
private String token;
private App app;
private String note;
private String note_url;
private String updated_at;
private String created_at;
public GitHub getRoot() {
return root;
}
public int getId() {
return id;
}
public List<String> getScopes() {
return scopes;
}
public String getToken(){
return token;
}
public URL getAppUrl(){
return GitHub.parseURL(app.url);
}
public String getAppName() {
return app.name;
}
public URL getApiURL(){
return GitHub.parseURL(url);
}
public String getNote() {
return note;
}
public URL getNoteUrl(){
return GitHub.parseURL(note_url);
}
public Date getCreatedAt() {
return GitHub.parseDate(created_at);
}
public Date getUpdatedAt() {
return GitHub.parseDate(updated_at);
}
/*package*/ GHAuthorization wrap(GitHub root) {
this.root = root;
return this;
}
private static class App{
private String url;
private String name;
}
}

View File

@@ -5,6 +5,7 @@ import java.net.URL;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
/**
@@ -16,6 +17,55 @@ import java.util.List;
*/
public class GHCommit {
private GHRepository owner;
private ShortInfo commit;
/**
* Short summary of this commit.
*/
public static class ShortInfo {
private GHAuthor author;
private GHAuthor committer;
private String message;
private int comment_count;
public GHAuthor getAuthor() {
return author;
}
public GHAuthor getCommitter() {
return committer;
}
/**
* Commit message.
*/
public String getMessage() {
return message;
}
public int getCommentCount() {
return comment_count;
}
}
public static class GHAuthor {
private String name,email,date;
public String getName() {
return name;
}
public String getEmail() {
return email;
}
public Date getDate() {
return GitHub.parseDate(date);
}
}
public static class Stats {
int total,additions,deletions;
@@ -110,8 +160,14 @@ public class GHCommit {
Stats stats;
List<Parent> parents;
User author,committer;
/**
public ShortInfo getCommitShortInfo() {
return commit;
}
/**
* The repository that contains the commit.
*/
public GHRepository getOwner() {
@@ -225,7 +281,6 @@ public class GHCommit {
.with("path",path)
.with("line",line)
.with("position",position)
.withCredential()
.to(String.format("/repos/%s/%s/commits/%s/comments",owner.getOwnerName(),owner.getName(),sha),GHCommitComment.class);
return r.wrap(owner);
}

View File

@@ -99,7 +99,7 @@ public class GHCommitComment {
public void update(String body) throws IOException {
GHCommitComment r = new Requester(owner.root)
.with("body", body)
.withCredential().method("PATCH").to(getApiTail(), GHCommitComment.class);
.method("PATCH").to(getApiTail(), GHCommitComment.class);
this.body = body;
}
@@ -107,7 +107,7 @@ public class GHCommitComment {
* Deletes this comment.
*/
public void delete() throws IOException {
new Requester(owner.root).withCredential().method("DELETE").to(getApiTail());
new Requester(owner.root).method("DELETE").to(getApiTail());
}
private String getApiTail() {

View File

@@ -0,0 +1,156 @@
package org.kohsuke.github;
import java.net.URL;
import java.util.Date;
/**
* The model user for comparing 2 commits in the GitHub API.
*
* @author Michael Clarke
*/
public class GHCompare {
private String url, html_url, permalink_url, diff_url, patch_url;
public Status status;
private int ahead_by, behind_by, total_commits;
private Commit base_commit, merge_base_commit;
private Commit[] commits;
private GHCommit.File[] files;
private GHRepository owner;
public URL getUrl() {
return GitHub.parseURL(url);
}
public URL getHtmlUrl() {
return GitHub.parseURL(html_url);
}
public URL getPermalinkUrl() {
return GitHub.parseURL(permalink_url);
}
public URL getDiffUrl() {
return GitHub.parseURL(diff_url);
}
public URL getPatchUrl() {
return GitHub.parseURL(patch_url);
}
public Status getStatus() {
return status;
}
public int getAheadBy() {
return ahead_by;
}
public int getBehindBy() {
return behind_by;
}
public int getTotalCommits() {
return total_commits;
}
public Commit getBaseCommit() {
return base_commit;
}
public Commit getMergeBaseCommit() {
return merge_base_commit;
}
public Commit[] getCommits() {
return commits;
}
public GHCompare wrap(GHRepository owner) {
this.owner = owner;
for (Commit commit : commits) {
commit.wrapUp(owner);
}
merge_base_commit.wrapUp(owner);
base_commit.wrapUp(owner);
return this;
}
/**
* Compare commits had a child commit element with additional details we want to capture.
* This extenstion of GHCommit provides that.
*/
public static class Commit extends GHCommit {
private InnerCommit commit;
public InnerCommit getCommit() {
return commit;
}
}
public static class InnerCommit {
private String url, sha, message;
private User author, committer;
private Tree tree;
public String getUrl() {
return url;
}
public String getSha() {
return sha;
}
public String getMessage() {
return message;
}
public User getAuthor() {
return author;
}
public User getCommitter() {
return committer;
}
public Tree getTree() {
return tree;
}
}
public static class Tree {
private String url, sha;
public String getUrl() {
return url;
}
public String getSha() {
return sha;
}
}
public static class User {
private String name, email, date;
public String getName() {
return name;
}
public String getEmail() {
return email;
}
public Date getDate() {
return GitHub.parseDate(date);
}
}
public static enum Status {
behind, ahead, identical;
}
}

View File

@@ -1,10 +1,10 @@
package org.kohsuke.github;
import org.codehaus.jackson.node.ObjectNode;
import java.io.IOException;
import java.util.Date;
import com.fasterxml.jackson.databind.node.ObjectNode;
/**
* Represents an event.
*

View File

@@ -18,10 +18,16 @@ public abstract class GHEventPayload {
this.root = root;
}
/**
* A pull request status has changed.
*
* @see http://developer.github.com/v3/activity/events/types/#pullrequestevent
*/
public static class PullRequest extends GHEventPayload {
private String action;
private int number;
GHPullRequest pull_request;
private GHPullRequest pull_request;
private GHRepository repository;
public String getAction() {
return action;
@@ -36,11 +42,67 @@ public abstract class GHEventPayload {
return pull_request;
}
public GHRepository getRepository() {
return repository;
}
@Override
void wrapUp(GitHub root) {
super.wrapUp(root);
pull_request.wrapUp(root);
if (repository!=null) {
repository.wrap(root);
pull_request.wrap(repository);
} else {
pull_request.wrapUp(root);
}
}
}
/**
* A comment was added to an issue
*
* @see http://developer.github.com/v3/activity/events/types/#issuecommentevent
*/
public static class IssueComment extends GHEventPayload {
private String action;
private GHIssueComment comment;
private GHIssue issue;
private GHRepository repository;
public String getAction() {
return action;
}
public GHIssueComment getComment() {
return comment;
}
public void setComment(GHIssueComment comment) {
this.comment = comment;
}
public GHIssue getIssue() {
return issue;
}
public void setIssue(GHIssue issue) {
this.issue = issue;
}
public GHRepository getRepository() {
return repository;
}
public void setRepository(GHRepository repository) {
this.repository = repository;
}
@Override
void wrapUp(GitHub root) {
super.wrapUp(root);
repository.wrap(root);
issue.wrap(repository);
comment.wrapUp(issue);
}
}
}

View File

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

View File

@@ -49,7 +49,7 @@ public class GHIssue {
protected String closed_at;
protected int comments;
protected String body;
protected List<String> labels;
protected List<Label> labels;
protected GHUser user;
protected String title, created_at, html_url;
protected GHIssue.PullRequest pull_request;
@@ -58,6 +58,24 @@ public class GHIssue {
protected int id;
protected GHUser closed_by;
public static class Label {
private String url;
private String name;
private String color;
public String getUrl() {
return url;
}
public String getName() {
return name;
}
public String getColor() {
return color;
}
}
/*package*/ GHIssue wrap(GHRepository owner) {
this.owner = owner;
this.root = owner.root;
@@ -111,7 +129,7 @@ public class GHIssue {
return Enum.valueOf(GHIssueState.class, state.toUpperCase(Locale.ENGLISH));
}
public Collection<String> getLabels() {
public Collection<Label> getLabels() {
if(labels == null){
return Collections.EMPTY_LIST;
}
@@ -138,11 +156,11 @@ public class GHIssue {
* Updates the issue by adding a comment.
*/
public void comment(String message) throws IOException {
new Requester(root).withCredential().with("body",message).to(getApiRoute() + "/comments");
new Requester(root).with("body",message).to(getIssuesApiRoute() + "/comments");
}
private void edit(String key, Object value) throws IOException {
new Requester(root).withCredential()._with(key, value).method("PATCH").to(getApiRoute());
new Requester(root)._with(key, value).method("PATCH").to(getApiRoute());
}
/**
@@ -190,7 +208,7 @@ public class GHIssue {
public PagedIterable<GHIssueComment> listComments() throws IOException {
return new PagedIterable<GHIssueComment>() {
public PagedIterator<GHIssueComment> iterator() {
return new PagedIterator<GHIssueComment>(root.retrieve().asIterator(getApiRoute() + "/comments", GHIssueComment[].class)) {
return new PagedIterator<GHIssueComment>(root.retrieve().asIterator(getIssuesApiRoute() + "/comments", GHIssueComment[].class)) {
protected void wrapUp(GHIssueComment[] page) {
for (GHIssueComment c : page)
c.wrapUp(GHIssue.this);
@@ -200,7 +218,11 @@ public class GHIssue {
};
}
private String getApiRoute() {
protected String getApiRoute() {
return getIssuesApiRoute();
}
private String getIssuesApiRoute() {
return "/repos/"+owner.getOwnerName()+"/"+owner.getName()+"/issues/"+number;
}
@@ -211,7 +233,6 @@ public class GHIssue {
/**
* User who submitted the issue.
*/
@Deprecated
public GHUser getUser() {
return user;
}
@@ -251,4 +272,4 @@ public class GHIssue {
return GitHub.parseURL(html_url);
}
}
}
}

View File

@@ -0,0 +1,59 @@
package org.kohsuke.github;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* @author Kohsuke Kawaguchi
*/
public class GHIssueBuilder {
private final GHRepository repo;
private final Requester builder;
private List<String> labels = new ArrayList<String>();
GHIssueBuilder(GHRepository repo, String title) {
this.repo = repo;
this.builder = new Requester(repo.root);
builder.with("title",title);
}
/**
* Sets the main text of an issue, which is arbitrary multi-line text.
*/
public GHIssueBuilder body(String str) {
builder.with("body",str);
return this;
}
public GHIssueBuilder assignee(GHUser user) {
if (user!=null)
builder.with("assignee",user.getLogin());
return this;
}
public GHIssueBuilder assignee(String user) {
if (user!=null)
builder.with("assignee",user);
return this;
}
public GHIssueBuilder milestone(GHMilestone milestone) {
if (milestone!=null)
builder.with("milestone",milestone.getNumber());
return this;
}
public GHIssueBuilder label(String label) {
if (label!=null)
labels.add(label);
return this;
}
/**
* Creates a new issue.
*/
public GHIssue create() throws IOException {
return builder.with("labels",labels).to(repo.getApiTailUrl("issues"),GHIssue.class).wrap(repo);
}
}

View File

@@ -3,7 +3,12 @@ package org.kohsuke.github;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
/**
* Represents the account that's logging into GitHub.
@@ -22,7 +27,7 @@ public class GHMyself extends GHUser {
* Always non-null.
*/
public List<String> getEmails() throws IOException {
String[] addresses = root.retrieve().withCredential().to("/user/emails", String[].class);
String[] addresses = root.retrieve().to("/user/emails", String[].class);
return Collections.unmodifiableList(Arrays.asList(addresses));
}
@@ -33,9 +38,52 @@ public class GHMyself extends GHUser {
* Always non-null.
*/
public List<GHKey> getPublicKeys() throws IOException {
return Collections.unmodifiableList(Arrays.asList(root.retrieve().withCredential().to("/user/keys", GHKey[].class)));
return Collections.unmodifiableList(Arrays.asList(root.retrieve().to("/user/keys", GHKey[].class)));
}
/**
* Gets the organization that this user belongs to.
*/
public GHPersonSet<GHOrganization> getAllOrganizations() throws IOException {
GHPersonSet<GHOrganization> orgs = new GHPersonSet<GHOrganization>();
Set<String> names = new HashSet<String>();
for (GHOrganization o : root.retrieve().to("/user/orgs", GHOrganization[].class)) {
if (names.add(o.getLogin())) // in case of rumoured duplicates in the data
orgs.add(root.getOrganization(o.getLogin()));
}
return orgs;
}
/**
* Gets the all repositories this user owns (public and private).
*/
public synchronized Map<String,GHRepository> getAllRepositories() throws IOException {
Map<String,GHRepository> repositories = new TreeMap<String, GHRepository>();
for (GHRepository r : listAllRepositories()) {
repositories.put(r.getName(),r);
}
return Collections.unmodifiableMap(repositories);
}
/**
* Lists up all repositories this user owns (public and private).
*
* Unlike {@link #getAllRepositories()}, this does not wait until all the repositories are returned.
*/
public PagedIterable<GHRepository> listAllRepositories() {
return new PagedIterable<GHRepository>() {
public PagedIterator<GHRepository> iterator() {
return new PagedIterator<GHRepository>(root.retrieve().asIterator("/user/repos", GHRepository[].class)) {
@Override
protected void wrapUp(GHRepository[] page) {
for (GHRepository c : page)
c.wrap(root);
}
};
}
};
}
// public void addEmails(Collection<String> emails) throws IOException {
//// new Requester(root,ApiVersion.V3).withCredential().to("/user/emails");
// root.retrieveWithAuth3()

View File

@@ -1,9 +1,5 @@
package org.kohsuke.github;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlAnchor;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import java.io.IOException;
import java.util.AbstractList;
import java.util.ArrayList;
@@ -33,7 +29,7 @@ public class GHOrganization extends GHPerson {
public GHRepository createRepository(String name, String description, String homepage, GHTeam team, boolean isPublic) throws IOException {
// such API doesn't exist, so fall back to HTML scraping
return new Requester(root).withCredential()
return new Requester(root)
.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);
}
@@ -42,7 +38,7 @@ public class GHOrganization extends GHPerson {
* Teams by their names.
*/
public Map<String,GHTeam> getTeams() throws IOException {
GHTeam[] teams = root.retrieve().withCredential().to("/orgs/" + login + "/teams", GHTeam[].class);
GHTeam[] teams = root.retrieve().to("/orgs/" + login + "/teams", GHTeam[].class);
Map<String,GHTeam> r = new TreeMap<String, GHTeam>();
for (GHTeam t : teams) {
r.put(t.getName(),t.wrapUp(this));
@@ -50,11 +46,35 @@ public class GHOrganization extends GHPerson {
return r;
}
/**
* Checks if this organization has the specified user as a member.
*/
public boolean hasMember(GHUser user) {
try {
root.retrieve().to("/orgs/" + login + "/members/" + user.getLogin());
return true;
} catch (IOException ignore) {
return false;
}
}
/**
* Checks if this organization has the specified user as a public member.
*/
public boolean hasPublicMember(GHUser user) {
try {
root.retrieve().to("/orgs/" + login + "/public_members/" + user.getLogin());
return true;
} catch (IOException ignore) {
return false;
}
}
/**
* Publicizes the membership.
*/
public void publicize(GHUser u) throws IOException {
root.retrieve().withCredential().method("PUT").to("/orgs/" + login + "/public_members/" + u.getLogin(), null);
root.retrieve().method("PUT").to("/orgs/" + login + "/public_members/" + u.getLogin(), null);
}
/**
@@ -64,7 +84,7 @@ public class GHOrganization extends GHPerson {
return new AbstractList<GHUser>() {
// these are shallow objects with only some limited values filled out
// TODO: it's better to allow objects to fill themselves in later when missing values are requested
final GHUser[] shallow = root.retrieve().withCredential().to("/orgs/" + login + "/members", GHUser[].class);
final GHUser[] shallow = root.retrieve().to("/orgs/" + login + "/members", GHUser[].class);
@Override
public GHUser get(int index) {
@@ -86,7 +106,7 @@ public class GHOrganization extends GHPerson {
* Conceals the membership.
*/
public void conceal(GHUser u) throws IOException {
root.retrieve().withCredential().method("DELETE").to("/orgs/" + login + "/public_members/" + u.getLogin(), null);
root.retrieve().method("DELETE").to("/orgs/" + login + "/public_members/" + u.getLogin(), null);
}
public enum Permission { ADMIN, PUSH, PULL }
@@ -95,7 +115,7 @@ 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 {
Requester post = new Requester(root).withCredential().with("name", name).with("permission", p.name().toLowerCase());
Requester post = new Requester(root).with("name", name).with("permission", p.name().toLowerCase());
List<String> repo_names = new ArrayList<String>();
for (GHRepository r : repositories) {
repo_names.add(r.getName());
@@ -110,15 +130,18 @@ public class GHOrganization extends GHPerson {
/**
* List up repositories that has some open pull requests.
*
* This used to be an efficient method that didn't involve traversing every repository, but now
* it doesn't do any optimization.
*/
public List<GHRepository> getRepositoriesWithOpenPullRequests() throws IOException {
WebClient wc = root.createWebClient();
HtmlPage pg = (HtmlPage)wc.getPage("https://github.com/organizations/"+login+"/dashboard/pulls");
List<GHRepository> r = new ArrayList<GHRepository>();
for (HtmlAnchor e : pg.getElementById("js-issue-list").<HtmlAnchor>selectNodes(".//UL[@class='smallnav']/LI[not(@class='zeroed')]/A")) {
String a = e.getHrefAttribute();
String name = a.substring(a.lastIndexOf('/')+1);
r.add(getRepository(name));
for (GHRepository repository : listRepositories()) {
repository.wrap(root);
List<GHPullRequest> pullRequests = repository.getPullRequests(GHIssueState.OPEN);
if (pullRequests.size() > 0) {
r.add(repository);
}
}
return r;
}

View File

@@ -54,6 +54,36 @@ public abstract class GHPerson {
return Collections.unmodifiableMap(repositories);
}
/**
* Lists up all the repositories using a 30 items page size.
*
* Unlike {@link #getRepositories()}, this does not wait until all the repositories are returned.
*/
public PagedIterable<GHRepository> listRepositories() {
return listRepositories(30);
}
/**
* Lists up all the repositories using the specified page size.
*
* @param pageSize size for each page of items returned by GitHub. Maximum page size is 100.
*
* Unlike {@link #getRepositories()}, this does not wait until all the repositories are returned.
*/
public PagedIterable<GHRepository> listRepositories(final int pageSize) {
return new PagedIterable<GHRepository>() {
public PagedIterator<GHRepository> iterator() {
return new PagedIterator<GHRepository>(root.retrieve().asIterator("/users/" + login + "/repos?per_page=" + pageSize, GHRepository[].class)) {
@Override
protected void wrapUp(GHRepository[] page) {
for (GHRepository c : page)
c.wrap(root);
}
};
}
};
}
/**
* Loads repository list in a pagenated fashion.
*
@@ -63,6 +93,9 @@ public abstract class GHPerson {
*
* Every {@link Iterator#next()} call results in I/O. Exceptions that occur during the processing is wrapped
* into {@link Error}.
*
* @deprecated
* Use {@link #listRepositories()}
*/
public synchronized Iterable<List<GHRepository>> iterateRepositories(final int pageSize) {
return new Iterable<List<GHRepository>>() {
@@ -96,7 +129,7 @@ public abstract class GHPerson {
*/
public GHRepository getRepository(String name) throws IOException {
try {
return root.retrieve().withCredential().to("/repos/" + login + '/' + name, GHRepository.class).wrap(root);
return root.retrieve().to("/repos/" + login + '/' + name, GHRepository.class).wrap(root);
} catch (FileNotFoundException e) {
return null;
}
@@ -202,5 +235,4 @@ public abstract class GHPerson {
populate();
return followers;
}
}

View File

@@ -27,6 +27,7 @@ import java.io.IOException;
import java.net.URL;
import java.util.Collection;
import java.util.Date;
import java.util.Locale;
/**
* A pull request.
@@ -63,7 +64,12 @@ public class GHPullRequest extends GHIssue {
if (merged_by != null) merged_by.wrapUp(root);
return this;
}
@Override
protected String getApiRoute() {
return "/repos/"+owner.getOwnerName()+"/"+owner.getName()+"/pulls/"+number;
}
/**
* The URL of the patch file.
* like https://github.com/jenkinsci/jenkins/pull/100.patch
@@ -113,7 +119,7 @@ public class GHPullRequest extends GHIssue {
}
@Override
public Collection<String> getLabels() {
public Collection<Label> getLabels() {
return super.getLabels();
}
@@ -181,4 +187,22 @@ public class GHPullRequest extends GHIssue {
root.retrieve().to(url, this);
}
/**
* Retrieves all the commits associated to this pull request.
*/
public PagedIterable<GHPullRequestCommitDetail> listCommits() {
return new PagedIterable<GHPullRequestCommitDetail>() {
public PagedIterator<GHPullRequestCommitDetail> iterator() {
return new PagedIterator<GHPullRequestCommitDetail>(root.retrieve().asIterator(
String.format("%s/commits", getApiURL().getPath()),
GHPullRequestCommitDetail[].class)) {
@Override
protected void wrapUp(GHPullRequestCommitDetail[] page) {
}
};
}
};
}
}

View File

@@ -0,0 +1,139 @@
/*
* The MIT License
*
* Copyright (c) 2013, Luca Milanesio
*
* 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.net.URL;
import java.util.Date;
/**
* Commit detail inside a {@link GHPullRequest}.
*
* @author Luca Milanesio
*/
public class GHPullRequestCommitDetail {
public static class Authorship {
String name;
String email;
String date;
public String getName() {
return name;
}
public String getEmail() {
return email;
}
public Date getDate() {
return GitHub.parseDate(date);
}
}
public static class Tree {
String sha;
String url;
public String getSha() {
return sha;
}
public URL getUrl() {
return GitHub.parseURL(url);
}
}
public static class Commit {
Authorship author;
Authorship committer;
String message;
Tree tree;
String url;
int comment_count;
public Authorship getAuthor() {
return author;
}
public Authorship getCommitter() {
return committer;
}
public String getMessage() {
return message;
}
public URL getUrl() {
return GitHub.parseURL(url);
}
public int getComment_count() {
return comment_count;
}
}
public static class CommitPointer {
String sha;
String url;
String html_url;
public URL getUrl() {
return GitHub.parseURL(url);
}
public URL getHtml_url() {
return GitHub.parseURL(html_url);
}
public String getSha() {
return sha;
}
}
String sha;
Commit commit;
String url;
String html_url;
String comments_url;
CommitPointer[] parents;
public String getSha() {
return sha;
}
public Commit getCommit() {
return commit;
}
public URL getApiUrl() {
return GitHub.parseURL(url);
}
public URL getUrl() {
return GitHub.parseURL(html_url);
}
public URL getCommentsUrl() {
return GitHub.parseURL(comments_url);
}
public CommitPointer[] getParents() {
return parents;
}
}

View File

@@ -0,0 +1,61 @@
package org.kohsuke.github;
import java.net.URL;
/**
* Provides information on a Git ref from GitHub.
*
* @author Michael Clarke
*/
public class GHRef {
private String ref, url;
private GHObject object;
/**
* Name of the ref, such as "refs/tags/abc"
*/
public String getRef() {
return ref;
}
/**
* The API URL of this tag, such as https://api.github.com/repos/jenkinsci/jenkins/git/refs/tags/1.312
*/
public URL getUrl() {
return GitHub.parseURL(url);
}
/**
* The object that this ref points to.
*/
public GHObject getObject() {
return object;
}
public static class GHObject {
private String type, sha, url;
/**
* Type of the object, such as "commit"
*/
public String getType() {
return type;
}
/**
* SHA1 of this object.
*/
public String getSha() {
return sha;
}
/**
* API URL to this Git data, such as https://api.github.com/repos/jenkinsci/jenkins/git/commits/b72322675eb0114363a9a86e9ad5a170d1d07ac0
*/
public URL getUrl() {
return GitHub.parseURL(url);
}
}
}

View File

@@ -0,0 +1,195 @@
package org.kohsuke.github;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import static java.lang.String.format;
/**
* Release in a github repository.
*
* @see GHRepository#getReleases()
* @see GHRepository#createRelease(String)
*/
public class GHRelease {
GitHub root;
GHRepository owner;
private String url;
private String html_url;
private String assets_url;
private String upload_url;
private long id;
private String tag_name;
private String target_commitish;
private String name;
private String body;
private boolean draft;
private boolean prerelease;
private Date created_at;
private Date published_at;
public String getAssetsUrl() {
return assets_url;
}
public void setAssetsUrl(String assets_url) {
this.assets_url = assets_url;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
public Date getCreatedAt() {
return created_at;
}
public void setCreatedAt(Date created_at) {
this.created_at = created_at;
}
public boolean isDraft() {
return draft;
}
public void setDraft(boolean draft) {
this.draft = draft;
}
public String getHtmlUrl() {
return html_url;
}
public void setHtmlUrl(String html_url) {
this.html_url = html_url;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public GHRepository getOwner() {
return owner;
}
public void setOwner(GHRepository owner) {
this.owner = owner;
}
public boolean isPrerelease() {
return prerelease;
}
public void setPrerelease(boolean prerelease) {
this.prerelease = prerelease;
}
public Date getPublished_at() {
return published_at;
}
public void setPublished_at(Date published_at) {
this.published_at = published_at;
}
public GitHub getRoot() {
return root;
}
public void setRoot(GitHub root) {
this.root = root;
}
public String getTagName() {
return tag_name;
}
public void setTagName(String tag_name) {
this.tag_name = tag_name;
}
public String getTargetCommitish() {
return target_commitish;
}
public void setTargetCommitish(String target_commitish) {
this.target_commitish = target_commitish;
}
public String getUploadUrl() {
return upload_url;
}
public void setUploadUrl(String upload_url) {
this.upload_url = upload_url;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
GHRelease wrap(GHRepository owner) {
this.owner = owner;
this.root = owner.root;
return this;
}
static GHRelease[] wrap(GHRelease[] releases, GHRepository owner) {
for (GHRelease release : releases) {
release.wrap(owner);
}
return releases;
}
/**
* Because github relies on SNI (http://en.wikipedia.org/wiki/Server_Name_Indication) this method will only work on
* Java 7 or greater. Options for fixing this for earlier JVMs can be found here
* http://stackoverflow.com/questions/12361090/server-name-indication-sni-on-java but involve more complicated
* handling of the HTTP requests to github's API.
*
* @throws IOException
*/
public GHAsset uploadAsset(File file, String contentType) throws IOException {
Requester builder = new Requester(owner.root);
String url = format("https://uploads.github.com%sreleases/%d/assets?name=%s",
owner.getApiTailUrl(""), getId(), file.getName());
return builder.contentType(contentType)
.with(new FileInputStream(file))
.to(url, GHAsset.class).wrap(this);
}
public List<GHAsset> getAssets() throws IOException {
Requester builder = new Requester(owner.root);
GHAsset[] assets = builder
.method("GET")
.to(owner.getApiTailUrl(format("releases/%d/assets", id)), GHAsset[].class);
return Arrays.asList(GHAsset.wrap(assets, this));
}
}

View File

@@ -0,0 +1,80 @@
package org.kohsuke.github;
import java.io.IOException;
/**
* Builder pattern for creating a {@link GHRelease}
*
* @see GHRepository#createRelease(String)
*/
public class GHReleaseBuilder {
private final GHRepository repo;
private final Requester builder;
public GHReleaseBuilder(GHRepository ghRepository, String tag) {
this.repo = ghRepository;
this.builder = new Requester(repo.root);
builder.with("tag_name", tag);
}
/**
* @param body The release notes body.
*/
public GHReleaseBuilder body(String body) {
if (body != null) {
builder.with("body", body);
}
return this;
}
/**
* Specifies the commitish value that determines where the Git tag is created from. Can be any branch or
* commit SHA.
*
* @param commitish Defaults to the repositorys default branch (usually "master"). Unused if the Git tag
* already exists.
* @return
*/
public GHReleaseBuilder commitish(String commitish) {
if (commitish != null) {
builder.with("target_commitish", commitish);
}
return this;
}
/**
* Optional.
*
* @param draft {@code true} to create a draft (unpublished) release, {@code false} to create a published one.
* Default is {@code false}.
*/
public GHReleaseBuilder draft(boolean draft) {
builder.with("draft", draft);
return this;
}
/**
* @param name the name of the release
*/
public GHReleaseBuilder name(String name) {
if (name != null) {
builder.with("name", name);
}
return this;
}
/**
* Optional
*
* @param prerelease {@code true} to identify the release as a prerelease. {@code false} to identify the release
* as a full release. Default is {@code false}.
*/
public GHReleaseBuilder prerelease(boolean prerelease) {
builder.with("prerelease", prerelease);
return this;
}
public GHRelease create() throws IOException {
return builder.to(repo.getApiTailUrl("releases"), GHRelease.class).wrap(repo);
}
}

View File

@@ -23,12 +23,6 @@
*/
package org.kohsuke.github;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlButton;
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;
@@ -140,10 +134,27 @@ public class GHRepository {
return root.getUser(owner.login); // because 'owner' isn't fully populated
}
public GHIssue getIssue(int id) throws IOException {
return root.retrieve().to("/repos/" + owner.login + "/" + name + "/issues/" + id, GHIssue.class).wrap(this);
}
public GHIssueBuilder createIssue(String title) {
return new GHIssueBuilder(this,title);
}
public List<GHIssue> getIssues(GHIssueState state) throws IOException {
return Arrays.asList(GHIssue.wrap(root.retrieve().to("/repos/" + owner.login + "/" + name + "/issues?state=" + state.toString().toLowerCase(), GHIssue[].class), this));
}
public GHReleaseBuilder createRelease(String tag) {
return new GHReleaseBuilder(this,tag);
}
public List<GHRelease> getReleases() throws IOException {
return Arrays.asList(GHRelease.wrap(root.retrieve().to("/repos/" + owner.login + "/" + name + "/releases",
GHRelease[].class), this));
}
protected String getOwnerName() {
return owner.login;
}
@@ -231,7 +242,7 @@ public class GHRepository {
* If this repository belongs to an organization, return a set of teams.
*/
public Set<GHTeam> getTeams() throws IOException {
return Collections.unmodifiableSet(new HashSet<GHTeam>(Arrays.asList(GHTeam.wrapUp(root.retrieve().withCredential().to("/repos/" + owner.login + "/" + name + "/teams", GHTeam[].class), root.getOrganization(owner.login)))));
return Collections.unmodifiableSet(new HashSet<GHTeam>(Arrays.asList(GHTeam.wrapUp(root.retrieve().to("/repos/" + owner.login + "/" + name + "/teams", GHTeam[].class), root.getOrganization(owner.login)))));
}
public void addCollaborators(GHUser... users) throws IOException {
@@ -253,24 +264,19 @@ public class GHRepository {
private void modifyCollaborators(Collection<GHUser> users, String method) throws IOException {
verifyMine();
for (GHUser user : users) {
new Requester(root).withCredential().method(method).to("/repos/" + owner.login + "/" + name + "/collaborators/" + user.getLogin());
new Requester(root).method(method).to("/repos/" + owner.login + "/" + name + "/collaborators/" + user.getLogin());
}
}
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");
email.setValueAttribute(address);
HtmlCheckBoxInput active = (HtmlCheckBoxInput)pg.getElementById("email[active]");
active.setChecked(true);
final HtmlForm f = email.getEnclosingFormOrDie();
f.submit((HtmlButton) f.getElementsByTagName("button").get(0));
Map<String, String> config = new HashMap<String, String>();
config.put("address", address);
new Requester(root).method("POST").with("name", "email").with("config", config).with("active", "true")
.to(String.format("/repos/%s/%s/hooks", owner.login, name));
}
private void edit(String key, String value) throws IOException {
Requester requester = new Requester(root).withCredential();
Requester requester = new Requester(root);
if (!key.equals("name"))
requester.with("name", name); // even when we don't change the name, we need to send it in
requester.with(key, value).method("PATCH").to("/repos/" + owner.login + "/" + name);
@@ -313,7 +319,7 @@ public class GHRepository {
* Deletes this repository.
*/
public void delete() throws IOException {
new Requester(root).withCredential().method("DELETE").to("/repos/" + owner.login + "/" + name);
new Requester(root).method("DELETE").to("/repos/" + owner.login + "/" + name);
}
/**
@@ -323,7 +329,7 @@ public class GHRepository {
* Newly forked repository that belong to you.
*/
public GHRepository fork() throws IOException {
return new Requester(root).withCredential().method("POST").to("/repos/" + owner.login + "/" + name + "/forks", GHRepository.class).wrap(root);
return new Requester(root).method("POST").to("/repos/" + owner.login + "/" + name + "/forks", GHRepository.class).wrap(root);
}
/**
@@ -333,7 +339,7 @@ public class GHRepository {
* Newly forked repository that belong to you.
*/
public GHRepository forkTo(GHOrganization org) throws IOException {
new Requester(root).withCredential().to(String.format("/repos/%s/%s/forks?org=%s",owner.login,name,org.getLogin()));
new Requester(root).to(String.format("/repos/%s/%s/forks?org=%s", owner.login, name, org.getLogin()));
// this API is asynchronous. we need to wait for a bit
for (int i=0; i<10; i++) {
@@ -352,7 +358,7 @@ public class GHRepository {
* Retrieves a specified pull request.
*/
public GHPullRequest getPullRequest(int i) throws IOException {
return root.retrieve().withCredential().to("/repos/" + owner.login + '/' + name + "/pulls/" + i, GHPullRequest.class).wrapUp(this);
return root.retrieve().to("/repos/" + owner.login + '/' + name + "/pulls/" + i, GHPullRequest.class).wrapUp(this);
}
/**
@@ -386,14 +392,54 @@ public class GHRepository {
*/
public List<GHHook> getHooks() throws IOException {
List<GHHook> list = new ArrayList<GHHook>(Arrays.asList(
root.retrieve().withCredential().to(String.format("/repos/%s/%s/hooks", owner.login, name), GHHook[].class)));
root.retrieve().to(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.retrieve().withCredential().to(String.format("/repos/%s/%s/hooks/%d", owner.login, name, id), GHHook.class).wrap(this);
return root.retrieve().to(String.format("/repos/%s/%s/hooks/%d", owner.login, name, id), GHHook.class).wrap(this);
}
/**
* Gets a comparison between 2 points in the repository. This would be similar
* to calling <tt>git log id1...id2</tt> against a local repository.
* @param id1 an identifier for the first point to compare from, this can be a sha1 ID (for a commit, tag etc) or a direct tag name
* @param id2 an identifier for the second point to compare to. Can be the same as the first point.
* @return the comparison output
* @throws IOException on failure communicating with GitHub
*/
public GHCompare getCompare(String id1, String id2) throws IOException {
GHCompare compare = root.retrieve().to(String.format("/repos/%s/%s/compare/%s...%s", owner.login, name, id1, id2), GHCompare.class);
return compare.wrap(this);
}
public GHCompare getCompare(GHCommit id1, GHCommit id2) throws IOException {
return getCompare(id1.getSHA1(),id2.getSHA1());
}
public GHCompare getCompare(GHBranch id1, GHBranch id2) throws IOException {
return getCompare(id1.getName(),id2.getName());
}
/**
* Retrieves all refs for the github repository.
* @return an array of GHRef elements coresponding with the refs in the remote repository.
* @throws IOException on failure communicating with GitHub
*/
public GHRef[] getRefs() throws IOException {
return root.retrieve().to(String.format("/repos/%s/%s/git/refs", owner.login, name), GHRef[].class);
}
/**
* Retrienved all refs of the given type for the current GitHub repository.
* @param refType the type of reg to search for e.g. <tt>tags</tt> or <tt>commits</tt>
* @return an array of all refs matching the request type
* @throws IOException on failure communicating with GitHub, potentially due to an invalid ref type being requested
*/
public GHRef[] getRefs(String refType) throws IOException {
return root.retrieve().to(String.format("/repos/%s/%s/git/refs/%s", owner.login, name, refType), GHRef[].class);
}
/**
@@ -476,8 +522,7 @@ public class GHRepository {
*/
public GHCommitStatus createCommitStatus(String sha1, GHCommitState state, String targetUrl, String description) throws IOException {
return new Requester(root)
.withCredential()
.with("state",state.name().toLowerCase(Locale.ENGLISH))
.with("state", state.name().toLowerCase(Locale.ENGLISH))
.with("target_url", targetUrl)
.with("description", description)
.to(String.format("/repos/%s/%s/statuses/%s",owner.login,this.name,sha1),GHCommitStatus.class).wrapUp(root);
@@ -505,8 +550,7 @@ public class GHRepository {
}
return new Requester(root)
.withCredential()
.with("name",name)
.with("name", name)
.with("active", active)
._with("config", config)
._with("events",ea)
@@ -610,28 +654,46 @@ public class GHRepository {
*/
public Map<String,GHBranch> getBranches() throws IOException {
Map<String,GHBranch> r = new TreeMap<String,GHBranch>();
for (GHBranch p : root.retrieve().to("/repos/" + owner.login + "/" + name + "/branches", GHBranch[].class)) {
for (GHBranch p : root.retrieve().to(getApiTailUrl("branches"), GHBranch[].class)) {
p.wrap(this);
r.put(p.getName(),p);
}
return r;
}
/**
* @deprecated
* Use {@link #listMilestones(GHIssueState)}
*/
public Map<Integer, GHMilestone> getMilestones() throws IOException {
Map<Integer,GHMilestone> milestones = new TreeMap<Integer, GHMilestone>();
GHMilestone[] ms = root.retrieve().to("/repos/" + owner.login + "/" + name + "/milestones", GHMilestone[].class);
for (GHMilestone m : ms) {
m.owner = this;
m.root = root;
for (GHMilestone m : listMilestones(GHIssueState.OPEN)) {
milestones.put(m.getNumber(), m);
}
return milestones;
}
/**
* Lists up all the milestones in this repository.
*/
public PagedIterable<GHMilestone> listMilestones(final GHIssueState state) {
return new PagedIterable<GHMilestone>() {
public PagedIterator<GHMilestone> iterator() {
return new PagedIterator<GHMilestone>(root.retrieve().asIterator(getApiTailUrl("milestones?state="+state.toString().toLowerCase(Locale.ENGLISH)), GHMilestone[].class)) {
@Override
protected void wrapUp(GHMilestone[] page) {
for (GHMilestone c : page)
c.wrap(GHRepository.this);
}
};
}
};
}
public GHMilestone getMilestone(int number) throws IOException {
GHMilestone m = milestones.get(number);
if (m == null) {
m = root.retrieve().to("/repos/" + owner.login + "/" + name + "/milestones/" + number, GHMilestone.class);
m = root.retrieve().to(getApiTailUrl("milestones/" + number), GHMilestone.class);
m.owner = this;
m.root = root;
milestones.put(m.getNumber(), m);
@@ -640,8 +702,8 @@ public class GHRepository {
}
public GHMilestone createMilestone(String title, String description) throws IOException {
return new Requester(root).withCredential()
.with("title", title).with("description", description).method("POST").to("/repos/" + owner.login + "/" + name + "/milestones", GHMilestone.class).wrap(this);
return new Requester(root)
.with("title", title).with("description", description).method("POST").to(getApiTailUrl("milestones"), GHMilestone.class).wrap(this);
}
@Override
@@ -663,4 +725,8 @@ public class GHRepository {
}
return false;
}
String getApiTailUrl(String tail) {
return "/repos/" + owner.login + "/" + name +'/'+tail;
}
}

View File

@@ -46,11 +46,11 @@ public class GHTeam {
* Retrieves the current members.
*/
public Set<GHUser> getMembers() throws IOException {
return new HashSet<GHUser>(Arrays.asList(GHUser.wrap(org.root.retrieve().withCredential().to(api("/members"), GHUser[].class), org.root)));
return new HashSet<GHUser>(Arrays.asList(GHUser.wrap(org.root.retrieve().to(api("/members"), GHUser[].class), org.root)));
}
public Map<String,GHRepository> getRepositories() throws IOException {
GHRepository[] repos = org.root.retrieve().withCredential().to(api("/repos"), GHRepository[].class);
GHRepository[] repos = org.root.retrieve().to(api("/repos"), GHRepository[].class);
Map<String,GHRepository> m = new TreeMap<String, GHRepository>();
for (GHRepository r : repos) {
m.put(r.getName(),r.wrap(org.root));
@@ -62,22 +62,22 @@ public class GHTeam {
* Adds a member to the team.
*/
public void add(GHUser u) throws IOException {
org.root.retrieve().withCredential().method("PUT").to(api("/members/" + u.getLogin()), null);
org.root.retrieve().method("PUT").to(api("/members/" + u.getLogin()), null);
}
/**
* Removes a member to the team.
*/
public void remove(GHUser u) throws IOException {
org.root.retrieve().withCredential().method("DELETE").to(api("/members/" + u.getLogin()), null);
org.root.retrieve().method("DELETE").to(api("/members/" + u.getLogin()), null);
}
public void add(GHRepository r) throws IOException {
org.root.retrieve().withCredential().method("PUT").to(api("/repos/" + r.getOwnerName() + '/' + r.getName()), null);
org.root.retrieve().method("PUT").to(api("/repos/" + r.getOwnerName() + '/' + r.getName()), null);
}
public void remove(GHRepository r) throws IOException {
org.root.retrieve().withCredential().method("DELETE").to(api("/repos/" + r.getOwnerName() + '/' + r.getName()), null);
org.root.retrieve().method("DELETE").to(api("/repos/" + r.getOwnerName() + '/' + r.getName()), null);
}
private String api(String tail) {

View File

@@ -41,14 +41,14 @@ public class GHUser extends GHPerson {
* Follow this user.
*/
public void follow() throws IOException {
new Requester(root).withCredential().method("PUT").to("/user/following/" + login);
new Requester(root).method("PUT").to("/user/following/" + login);
}
/**
* Unfollow this user.
*/
public void unfollow() throws IOException {
new Requester(root).withCredential().method("DELETE").to("/user/following/" + login);
new Requester(root).method("DELETE").to("/user/following/" + login);
}
/**
@@ -69,6 +69,20 @@ public class GHUser extends GHPerson {
return new GHPersonSet<GHUser>(Arrays.asList(wrap(followers,root)));
}
/**
* Returns true if this user belongs to the specified organization.
*/
public boolean isMemberOf(GHOrganization org) {
return org.hasMember(this);
}
/**
* Returns true if this user belongs to the specified organization as a public member.
*/
public boolean isPublicMemberOf(GHOrganization org) {
return org.hasPublicMember(this);
}
/*package*/ static GHUser[] wrap(GHUser[] users, GitHub root) {
for (GHUser f : users)
f.root = root;

View File

@@ -23,15 +23,7 @@
*/
package org.kohsuke.github;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlForm;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
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 com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.*;
import java.io.File;
import java.io.FileInputStream;
@@ -42,6 +34,7 @@ import java.net.URL;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
@@ -49,7 +42,13 @@ import java.util.Map;
import java.util.Properties;
import java.util.TimeZone;
import static org.codehaus.jackson.annotate.JsonAutoDetect.Visibility.*;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.introspect.VisibilityChecker.Std;
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
/**
* Root of the GitHub API.
@@ -58,51 +57,78 @@ import static org.codehaus.jackson.annotate.JsonAutoDetect.Visibility.*;
*/
public class GitHub {
/*package*/ final String login;
/**
* Value of the authorization header to be sent with the request.
*/
/*package*/ final String encodedAuthorization;
/*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>();
/*package*/ String oauthAccessToken;
private final String githubServer;
private GitHub(String login, String apiToken, String password) {
this ("github.com", login, apiToken, password);
}
private final String apiUrl;
/**
*
* @param githubServer
* The host name of the GitHub (or GitHub enterprise) server, such as "github.com".
* Connects to GitHub.com
*/
private GitHub(String githubServer, String login, String apiToken, String password) {
this.githubServer = githubServer;
this.login = login;
this.apiToken = apiToken;
this.password = password;
BASE64Encoder enc = new sun.misc.BASE64Encoder();
if (apiToken!=null || password!=null) {
String userpassword = password==null ? (login + "/token" + ":" + apiToken) : (login + ':'+password);
encodedAuthorization = enc.encode(userpassword.getBytes());
} else
encodedAuthorization = null;
private GitHub(String login, String oauthAccessToken, String password) throws IOException {
this (GITHUB_URL, login, oauthAccessToken, password);
}
private GitHub (String githubServer, String oauthAccessToken) throws IOException {
this.githubServer = githubServer;
this.password = null;
this.encodedAuthorization = null;
this.oauthAccessToken = oauthAccessToken;
this.apiToken = oauthAccessToken;
this.login = getMyself().getLogin();
/**
* Creates a client API root object.
*
* <p>
* Several different combinations of the login/oauthAccessToken/password parameters are allowed
* to represent different ways of authentication.
*
* <dl>
* <dt>Loging anonymously
* <dd>Leave all three parameters null and you will be making HTTP requests without any authentication.
*
* <dt>Log in with password
* <dd>Specify the login and password, then leave oauthAccessToken null.
* This will use the HTTP BASIC auth with the GitHub API.
*
* <dt>Log in with OAuth token
* <dd>Specify oauthAccessToken, and optionally specify the login. Leave password null.
* This will send OAuth token to the GitHub API. If the login parameter is null,
* The constructor makes an API call to figure out the user name that owns the token.
* </dl>
*
* @param apiUrl
* The URL of GitHub (or GitHub enterprise) API endpoint, such as "https://api.github.com" or
* "http://ghe.acme.com/api/v3". Note that GitHub Enterprise has <tt>/api/v3</tt> in the URL.
* For historical reasons, this parameter still accepts the bare domain name, but that's considered deprecated.
* Password is also considered deprecated as it is no longer required for api usage.
* @param login
* The use ID on GitHub that you are logging in as. Can be omitted if the OAuth token is
* provided or if logging in anonymously. Specifying this would save one API call.
* @param oauthAccessToken
* Secret OAuth token.
* @param password
* User's password. Always used in conjunction with the {@code login} parameter
*/
private GitHub(String apiUrl, String login, String oauthAccessToken, String password) throws IOException {
if (apiUrl.endsWith("/")) apiUrl = apiUrl.substring(0, apiUrl.length()-1); // normalize
this.apiUrl = apiUrl;
if (oauthAccessToken!=null) {
encodedAuthorization = "token "+oauthAccessToken;
} else {
if (password!=null) {
String authorization = (login + ':' + password);
encodedAuthorization = "Basic "+new String(Base64.encodeBase64(authorization.getBytes()));
} else {// anonymous access
encodedAuthorization = null;
}
}
if (login==null && encodedAuthorization!=null)
login = getMyself().getLogin();
this.login = login;
}
/**
* Obtains the credential from "~/.github"
*/
@@ -115,48 +141,81 @@ public class GitHub {
} finally {
IOUtils.closeQuietly(in);
}
return new GitHub(props.getProperty("login"),props.getProperty("token"),props.getProperty("password"));
return new GitHub(GITHUB_URL,props.getProperty("login"), props.getProperty("oauth"),props.getProperty("password"));
}
public static GitHub connect(String login, String apiToken){
return new GitHub(login,apiToken,null);
/**
* Version that connects to GitHub Enterprise.
*
* @param apiUrl
* The URL of GitHub (or GitHub enterprise) API endpoint, such as "https://api.github.com" or
* "http://ghe.acme.com/api/v3". Note that GitHub Enterprise has <tt>/api/v3</tt> in the URL.
* For historical reasons, this parameter still accepts the bare domain name, but that's considered deprecated.
*/
public static GitHub connectToEnterprise(String apiUrl, String oauthAccessToken) throws IOException {
return connectUsingOAuth(apiUrl, oauthAccessToken);
}
public static GitHub connect(String login, String apiToken, String password){
return new GitHub(login,apiToken,password);
public static GitHub connectToEnterprise(String apiUrl, String login, String password) throws IOException {
return new GitHub(apiUrl, login, null, password);
}
public static GitHub connectUsingOAuth (String accessToken) throws IOException {
return connectUsingOAuth("github.com", accessToken);
public static GitHub connect(String login, String oauthAccessToken) throws IOException {
return new GitHub(login,oauthAccessToken,null);
}
public static GitHub connectUsingOAuth (String githubServer, String accessToken) throws IOException {
return new GitHub(githubServer, accessToken);
/**
* @deprecated
* Either OAuth token or password is sufficient, so there's no point in passing both.
* Use {@link #connectUsingPassword(String, String)} or {@link #connectUsingOAuth(String)}.
*/
public static GitHub connect(String login, String oauthAccessToken, String password) throws IOException {
return new GitHub(login,oauthAccessToken,password);
}
public static GitHub connectUsingPassword(String login, String password) throws IOException {
return new GitHub(login,null,password);
}
public static GitHub connectUsingOAuth(String oauthAccessToken) throws IOException {
return new GitHub(null, oauthAccessToken, null);
}
public static GitHub connectUsingOAuth(String githubServer, String oauthAccessToken) throws IOException {
return new GitHub(githubServer,null, oauthAccessToken,null);
}
/**
* Connects to GitHub anonymously.
*
* All operations that requires authentication will fail.
*/
public static GitHub connectAnonymously() {
public static GitHub connectAnonymously() throws IOException {
return new GitHub(null,null,null);
}
/**
* Is this an anonymous connection
* @return {@code true} if operations that require authentication will fail.
*/
public boolean isAnonymous() {
return login==null && encodedAuthorization==null;
}
/*package*/ void requireCredential() {
if ((login==null || encodedAuthorization==null) && oauthAccessToken == null)
if (isAnonymous())
throw new IllegalStateException("This operation requires a credential but none is given to the GitHub constructor");
}
/*package*/ URL getApiURL(String tailApiUrl) throws IOException {
if (oauthAccessToken != null) {
// append the access token
tailApiUrl = tailApiUrl + (tailApiUrl.indexOf('?')>=0 ?'&':'?') + "access_token=" + oauthAccessToken;
}
if (tailApiUrl.startsWith("/"))
return new URL("https://api."+githubServer+tailApiUrl);
else
if (tailApiUrl.startsWith("/")) {
if ("github.com".equals(apiUrl)) {// backward compatibility
return new URL(GITHUB_URL + tailApiUrl);
} else {
return new URL(apiUrl + tailApiUrl);
}
} else {
return new URL(tailApiUrl);
}
}
/*package*/ Requester retrieve() {
@@ -167,7 +226,7 @@ public class GitHub {
* Gets the current rate limit.
*/
public GHRateLimit getRateLimit() throws IOException {
return retrieve().withCredential().to("/rate_limit", JsonRateLimit.class).rate;
return retrieve().to("/rate_limit", JsonRateLimit.class).rate;
}
/**
@@ -177,7 +236,7 @@ public class GitHub {
public GHMyself getMyself() throws IOException {
requireCredential();
GHMyself u = retrieve().withCredential().to("/user", GHMyself.class);
GHMyself u = retrieve().to("/user", GHMyself.class);
u.root = this;
users.put(u.getLogin(), u);
@@ -227,7 +286,7 @@ public class GitHub {
*/
public GHRepository getRepository(String name) throws IOException {
String[] tokens = name.split("/");
return getUser(tokens[0]).getRepository(tokens[1]);
return retrieve().to("/repos/" + tokens[0] + '/' + tokens[1], GHRepository.class).wrap(this);
}
/**
@@ -237,7 +296,7 @@ public class GitHub {
* TODO: make this automatic.
*/
public Map<String, GHOrganization> getMyOrganizations() throws IOException {
GHOrganization[] orgs = retrieve().withCredential().to("/user/orgs", GHOrganization[].class);
GHOrganization[] orgs = retrieve().to("/user/orgs", GHOrganization[].class);
Map<String, GHOrganization> r = new HashMap<String, GHOrganization>();
for (GHOrganization o : orgs) {
// don't put 'o' into orgs because they are shallow
@@ -250,7 +309,7 @@ public class GitHub {
* Public events visible to you. Equivalent of what's displayed on https://github.com/
*/
public List<GHEventInfo> getEvents() throws IOException {
// TODO: pagenation
// TODO: pagination
GHEventInfo[] events = retrieve().to("/events", GHEventInfo[].class);
for (GHEventInfo e : events)
e.wrapUp(this);
@@ -269,7 +328,7 @@ public class GitHub {
t.wrapUp(this);
return t;
}
/**
* Creates a new repository.
*
@@ -277,36 +336,40 @@ public class GitHub {
* Newly created repository.
*/
public GHRepository createRepository(String name, String description, String homepage, boolean isPublic) throws IOException {
Requester requester = new Requester(this).withCredential()
Requester requester = new Requester(this)
.with("name", name).with("description", description).with("homepage", homepage)
.with("public", isPublic ? 1 : 0);
return requester.method("POST").to("/user/repos", GHRepository.class).wrap(this);
}
/**
* Creates a new authorization.
*
* The token created can be then used for {@link GitHub#connectUsingOAuth(String)} in the future.
*
* @see <a href="http://developer.github.com/v3/oauth/#create-a-new-authorization">Documentation</a>
*/
public GHAuthorization createToken(Collection<String> scope, String note, String noteUrl) throws IOException{
Requester requester = new Requester(this)
.with("scopes", scope)
.with("note", note)
.with("note_url", noteUrl);
return requester.method("POST").to("/authorizations", GHAuthorization.class).wrap(this);
}
/**
* Ensures that the credential is valid.
*/
public boolean isCredentialValid() throws IOException {
try {
retrieve().withCredential().to("/user", GHUser.class);
retrieve().to("/user", GHUser.class);
return true;
} catch (IOException e) {
return false;
}
}
WebClient createWebClient() throws IOException {
WebClient wc = new WebClient();
wc.setJavaScriptEnabled(false);
wc.setCssEnabled(false);
HtmlPage pg = (HtmlPage)wc.getPage("https://github.com/login");
HtmlForm f = pg.getForms().get(0);
f.getInputByName("login").setValueAttribute(login);
f.getInputByName("password").setValueAttribute(password);
f.submit();
return wc;
}
/*package*/ static URL parseURL(String s) {
try {
return s==null ? null : new URL(s);
@@ -335,6 +398,8 @@ public class GitHub {
static {
MAPPER.setVisibilityChecker(new Std(NONE, NONE, NONE, NONE, ANY));
MAPPER.getDeserializationConfig().set(Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
private static final String GITHUB_URL = "https://api.github.com";
}

View File

@@ -3,6 +3,7 @@ package org.kohsuke.github;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
/**
* Iterator over a pagenated data source.
@@ -28,17 +29,24 @@ public abstract class PagedIterator<T> implements Iterator<T> {
protected abstract void wrapUp(T[] page);
public boolean hasNext() {
return (current!=null && pos<current.length) || base.hasNext();
fetch();
return current!=null;
}
public T next() {
fetch();
if (current==null) throw new NoSuchElementException();
return current[pos++];
}
private void fetch() {
while (current==null || current.length<=pos) {
if (!base.hasNext()) {// no more to retrieve
current = null;
pos = 0;
return;
}
current = base.next();
wrapUp(current);
pos = 0;

View File

@@ -25,6 +25,7 @@ package org.kohsuke.github;
import org.apache.commons.io.IOUtils;
import javax.net.ssl.HttpsURLConnection;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
@@ -39,13 +40,15 @@ import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.zip.GZIPInputStream;
import static org.kohsuke.github.GitHub.*;
import static org.kohsuke.github.GitHub.MAPPER;
/**
* A builder pattern for making HTTP call and parsing its output.
@@ -55,12 +58,13 @@ import static org.kohsuke.github.GitHub.*;
class Requester {
private final GitHub root;
private final List<Entry> args = new ArrayList<Entry>();
private boolean authenticate;
/**
* Request method.
*/
private String method = "POST";
private String contentType = "application/x-www-form-urlencoded";
private InputStream body;
private static class Entry {
String key;
@@ -79,10 +83,10 @@ class Requester {
/**
* Makes a request with authentication credential.
*/
@Deprecated
public Requester withCredential() {
// keeping it inline with retrieveWithAuth not to enforce the check
// root.requireCredential();
authenticate = true;
return this;
}
@@ -108,6 +112,15 @@ class Requester {
return _with(key, value);
}
public Requester with(String key, Map<String, String> value) {
return _with(key, value);
}
public Requester with(InputStream body) {
this.body = body;
return this;
}
public Requester _with(String key, Object value) {
if (value!=null) {
args.add(new Entry(key,value));
@@ -120,6 +133,11 @@ class Requester {
return this;
}
public Requester contentType(String contentType) {
this.contentType = contentType;
return this;
}
public void to(String tailApiUrl) throws IOException {
to(tailApiUrl,null);
}
@@ -157,13 +175,25 @@ class Requester {
if (!method.equals("GET")) {
uc.setDoOutput(true);
uc.setRequestProperty("Content-type","application/x-www-form-urlencoded");
uc.setRequestProperty("Content-type", contentType);
Map json = new HashMap();
for (Entry e : args) {
json.put(e.key, e.value);
if (body == null) {
Map json = new HashMap();
for (Entry e : args) {
json.put(e.key, e.value);
}
MAPPER.writeValue(uc.getOutputStream(), json);
} else {
try {
byte[] bytes = new byte[32768];
int read = 0;
while ((read = body.read(bytes)) != -1) {
uc.getOutputStream().write(bytes, 0, read);
}
} finally {
body.close();
}
}
MAPPER.writeValue(uc.getOutputStream(),json);
}
try {
@@ -264,13 +294,12 @@ class Requester {
private HttpURLConnection setupConnection(URL url) throws IOException {
HttpURLConnection uc = (HttpURLConnection) url.openConnection();
HttpsURLConnection uc = (HttpsURLConnection) url.openConnection();
// if the authentication is needed but no credential is given, try it anyway (so that some calls
// that do work with anonymous access in the reduced form should still work.)
// if OAuth token is present, it'll be set in the URL, so need to set the Authorization header
if (authenticate && root.encodedAuthorization!=null && root.oauthAccessToken == null)
uc.setRequestProperty("Authorization", "Basic " + root.encodedAuthorization);
if (root.encodedAuthorization!=null)
uc.setRequestProperty("Authorization", root.encodedAuthorization);
try {
uc.setRequestMethod(method);
@@ -342,4 +371,11 @@ class Requester {
IOUtils.closeQuietly(es);
}
}
private Set<String> toSet(String s) {
Set<String> r = new HashSet<String>();
for (String t : s.split(","))
r.add(t.trim());
return r;
}
}

View File

@@ -1,25 +0,0 @@
What is this?
This library defines an object oriented representation of the GitHub API. The library doesn't yet cover the entirety of the GitHub API, but it's implemented with the right abstractions and libraries to make it very easy to improve the coverage.
Sample Usage
------------------
GitHub github = GitHub.connect();
GHRepository repo = github.createRepository(
"new-repository","this is my new repository",
"http://www.kohsuke.org/",true/*public*/);
repo.addCollaborators(github.getUser("abayer"),github.getUser("rtyler"));
repo.delete();
------------------
Credential
This library allows the caller to supply the credential as parameters, but it also defines a common convention
so that applications using this library will look at the consistent location. In this convention, the library
looks at "~/.github" property file, which should have the following two values:
------------------
login=kohsuke
token=012345678
------------------

View File

@@ -0,0 +1,36 @@
What is this?
=====
This library defines an object oriented representation of the GitHub API. By "object oriented" we mean
there are classes that correspond to the domain model of GitHub (such as `GHUser` and `GHRepository`),
operations that act on them as defined as methods (such as `GHUser.follow()`), and those object references
are used in favor of using string handle (such as `GHUser.isMemberOf(GHOrganization)` instead of
`GHUser.isMemberOf(String)`)
There are some corners of the GitHub API that's not yet implemented, but
the library is implemented with the right abstractions and libraries to make it very easy to improve the coverage.
Sample Usage
-----
GitHub github = GitHub.connect();
GHRepository repo = github.createRepository(
"new-repository","this is my new repository",
"http://www.kohsuke.org/",true/*public*/);
repo.addCollaborators(github.getUser("abayer"),github.getUser("rtyler"));
repo.delete();
Credential
----
This library allows the caller to supply the credential as parameters, but it also defines a common convention
so that applications using this library will look at the consistent location. In this convention, the library
looks at `~/.github` property file, which should have the following two values:
login=kohsuke
password=012345678
Alternatively, you can have just the OAuth token in this file:
oauth=4d98173f7c075527cb64878561d1fe70

13
src/test/java/Foo.java Normal file
View File

@@ -0,0 +1,13 @@
import org.kohsuke.github.GitHub;
import java.util.Arrays;
/**
* @author Kohsuke Kawaguchi
*/
public class Foo {
public static void main(String[] args) throws Exception {
System.out.println(GitHub.connect().createToken(
Arrays.asList("user", "repo", "delete_repo", "notifications", "gist"), "GitHub API", null).getToken());
}
}

View File

@@ -4,7 +4,6 @@ import junit.framework.TestCase;
import org.kohsuke.github.GHCommit;
import org.kohsuke.github.GHCommit.File;
import org.kohsuke.github.GHCommitComment;
import org.kohsuke.github.GHCommitState;
import org.kohsuke.github.GHCommitStatus;
import org.kohsuke.github.GHEvent;
import org.kohsuke.github.GHEventInfo;
@@ -12,8 +11,10 @@ import org.kohsuke.github.GHEventPayload;
import org.kohsuke.github.GHHook;
import org.kohsuke.github.GHBranch;
import org.kohsuke.github.GHIssue;
import org.kohsuke.github.GHIssueComment;
import org.kohsuke.github.GHIssueState;
import org.kohsuke.github.GHKey;
import org.kohsuke.github.GHMilestone;
import org.kohsuke.github.GHMyself;
import org.kohsuke.github.GHOrganization;
import org.kohsuke.github.GHOrganization.Permission;
@@ -23,7 +24,6 @@ import org.kohsuke.github.GHTeam;
import org.kohsuke.github.GHUser;
import org.kohsuke.github.GitHub;
import org.kohsuke.github.PagedIterable;
import org.kohsuke.github.PagedIterator;
import java.io.IOException;
import java.net.URL;
@@ -36,42 +36,68 @@ import java.util.List;
* Unit test for simple App.
*/
public class AppTest extends TestCase {
private GitHub gitHub;
@Override
public void setUp() throws Exception {
super.setUp();
gitHub = GitHub.connect();
}
public void testRepoCRUD() throws Exception {
GitHub hub = GitHub.connect();
GHRepository r = hub.createRepository("github-api-test", "a test repository", "http://github-api.kohsuke.org/", true);
GHRepository r = gitHub.createRepository("github-api-test", "a test repository", "http://github-api.kohsuke.org/", true);
r.enableIssueTracker(false);
r.enableDownloads(false);
r.enableWiki(false);
r.renameTo("github-api-test2");
hub.getMyself().getRepository("github-api-test2").delete();
gitHub.getMyself().getRepository("github-api-test2").delete();
}
public void testCredentialValid() throws IOException {
assertTrue(GitHub.connect().isCredentialValid());
assertTrue(gitHub.isCredentialValid());
assertFalse(GitHub.connect("totally", "bogus").isCredentialValid());
}
public void testIssueWithNoComment() throws IOException {
GHRepository repository = gitHub.getRepository("kohsuke/test");
List<GHIssueComment> v = repository.getIssue(4).getComments();
System.out.println(v);
assertTrue(v.isEmpty());
v = repository.getIssue(3).getComments();
System.out.println(v);
assertTrue(v.size() == 3);
}
public void testCreateIssue() throws IOException {
GHUser u = gitHub.getUser("kohsuke");
GHRepository r = u.getRepository("test");
GHMilestone someMilestone = r.listMilestones(GHIssueState.CLOSED).iterator().next();
GHIssue o = r.createIssue("testing").body("this is body").assignee(u).label("bug").label("question").milestone(someMilestone).create();
System.out.println(o.getUrl());
o.close();
}
public void testRateLimit() throws IOException {
System.out.println(GitHub.connect().getRateLimit());
System.out.println(gitHub.getRateLimit());
}
public void testMyOrganizations() throws IOException {
Map<String, GHOrganization> org = GitHub.connect().getMyOrganizations();
Map<String, GHOrganization> org = gitHub.getMyOrganizations();
assertFalse(org.keySet().contains(null));
System.out.println(org);
}
public void testFetchPullRequest() throws Exception {
GitHub gh = GitHub.connect();
GHRepository r = gh.getOrganization("jenkinsci").getRepository("jenkins");
GHRepository r = gitHub.getOrganization("jenkinsci").getRepository("jenkins");
assertEquals("master",r.getMasterBranch());
r.getPullRequest(1);
r.getPullRequests(GHIssueState.OPEN);
}
public void testFetchPullRequestAsList() throws Exception {
GitHub gh = GitHub.connect();
GHRepository r = gh.getOrganization("symfony").getRepository("symfony-docs");
GHRepository r = gitHub.getOrganization("symfony").getRepository("symfony-docs");
assertEquals("master", r.getMasterBranch());
PagedIterable<GHPullRequest> i = r.listPullRequests(GHIssueState.CLOSED);
List<GHPullRequest> prs = i.asList();
@@ -80,19 +106,17 @@ public class AppTest extends TestCase {
}
public void testRepoPermissions() throws Exception {
GitHub gh = GitHub.connect();
GHRepository r = gh.getOrganization("jenkinsci").getRepository("jenkins");
GHRepository r = gitHub.getOrganization("jenkinsci").getRepository("jenkins");
assertTrue(r.hasPullAccess());
r = gh.getOrganization("github").getRepository("tire");
r = gitHub.getOrganization("github").getRepository("tire");
assertFalse(r.hasAdminAccess());
}
public void testGetMyself() throws Exception {
GitHub hub = GitHub.connect();
GHMyself me = hub.getMyself();
GHMyself me = gitHub.getMyself();
System.out.println(me);
GHUser u = hub.getUser("kohsuke2");
GHUser u = gitHub.getUser("kohsuke2");
System.out.println(u);
for (List<GHRepository> lst : me.iterateRepositories(100)) {
for (GHRepository r : lst) {
@@ -102,36 +126,30 @@ public class AppTest extends TestCase {
}
public void testPublicKeys() throws Exception {
GitHub gh = GitHub.connect();
List<GHKey> keys = gh.getMyself().getPublicKeys();
List<GHKey> keys = gitHub.getMyself().getPublicKeys();
System.out.println(keys);
}
public void tryOrgFork() throws Exception {
GitHub gh = GitHub.connect();
gh.getUser("kohsuke").getRepository("rubywm").forkTo(gh.getOrganization("jenkinsci"));
gitHub.getUser("kohsuke").getRepository("rubywm").forkTo(gitHub.getOrganization("jenkinsci"));
}
public void tryGetTeamsForRepo() throws Exception {
GitHub gh = GitHub.connect();
Set<GHTeam> o = gh.getOrganization("jenkinsci").getRepository("rubywm").getTeams();
Set<GHTeam> o = gitHub.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();
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 testCommit() throws Exception {
GitHub gitHub = GitHub.connect();
GHCommit commit = gitHub.getUser("jenkinsci").getRepository("jenkins").getCommit("08c1c9970af4d609ae754fbe803e06186e3206f7");
System.out.println(commit);
assertEquals(1, commit.getParents().size());
@@ -140,11 +158,10 @@ public class AppTest extends TestCase {
File f = commit.getFiles().get(0);
assertEquals(48,f.getLinesChanged());
assertEquals("modified",f.getStatus());
assertEquals("changelog.html",f.getFileName());
assertEquals("changelog.html", f.getFileName());
}
public void testListCommits() throws Exception {
GitHub gitHub = GitHub.connect();
List<String> sha1 = new ArrayList<String>();
for (GHCommit c : gitHub.getUser("kohsuke").getRepository("empty-commit").listCommits()) {
System.out.println(c.getSHA1());
@@ -155,14 +172,12 @@ public class AppTest extends TestCase {
}
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 testCommitComment() throws Exception {
GitHub gitHub = GitHub.connect();
GHRepository r = gitHub.getUser("jenkinsci").getRepository("jenkins");
PagedIterable<GHCommitComment> comments = r.listCommitComments();
List<GHCommitComment> batch = comments.iterator().nextPage();
@@ -173,7 +188,6 @@ public class AppTest extends TestCase {
}
public void testCreateCommitComment() throws Exception {
GitHub gitHub = GitHub.connect();
GHCommit commit = gitHub.getUser("kohsuke").getRepository("sandbox-ant").getCommit("8ae38db0ea5837313ab5f39d43a6f73de3bd9000");
GHCommitComment c = commit.createComment("[testing](http://kohsuse.org/)");
System.out.println(c);
@@ -183,7 +197,6 @@ public class AppTest extends TestCase {
}
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);
@@ -193,7 +206,6 @@ public class AppTest extends TestCase {
}
public void testEventApi() throws Exception {
GitHub gitHub = GitHub.connect();
for (GHEventInfo ev : gitHub.getEvents()) {
System.out.println(ev);
if (ev.getType()==GHEvent.PULL_REQUEST) {
@@ -205,10 +217,9 @@ public class AppTest extends TestCase {
}
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);
// GHRepository r = gitHub.getOrganization("jenkinsci").createRepository("kktest4", "Kohsuke's test", "http://kohsuke.org/", "Everyone", true);
// r.fork();
// tryDisablingIssueTrackers(gitHub);
@@ -235,7 +246,7 @@ public class AppTest extends TestCase {
// t.remove(gitHub.getMyself());
// System.out.println(t.getMembers());
// GHRepository r = GitHub.connect().getOrganization("HudsonLabs").createRepository("auto-test", "some description", "http://kohsuke.org/", "Plugin Developers", true);
// GHRepository r = gitHub.getOrganization("HudsonLabs").createRepository("auto-test", "some description", "http://kohsuke.org/", "Plugin Developers", true);
// r.
// GitHub hub = GitHub.connectAnonymously();
@@ -294,7 +305,6 @@ public class AppTest extends TestCase {
}
public void testOrgRepositories() throws IOException {
GitHub gitHub = GitHub.connect();
GHOrganization j = gitHub.getOrganization("jenkinsci");
long start = System.currentTimeMillis();
Map<String, GHRepository> repos = j.getRepositories();
@@ -303,7 +313,6 @@ public class AppTest extends TestCase {
}
public void testOrganization() throws IOException {
GitHub gitHub = GitHub.connect();
GHOrganization j = gitHub.getOrganization("jenkinsci");
GHTeam t = j.getTeams().get("Core Developers");
@@ -313,7 +322,6 @@ public class AppTest extends TestCase {
}
public void testCommitStatus() throws Exception {
GitHub gitHub = GitHub.connect();
GHRepository r = gitHub.getUser("kohsuke").getRepository("test");
GHCommitStatus state;
// state = r.createCommitStatus("edacdd76b06c5f3f0697a22ca75803169f25f296", GHCommitState.FAILURE, "http://jenkins-ci.org/", "oops!");
@@ -324,12 +332,29 @@ public class AppTest extends TestCase {
assertEquals("oops!",state.getDescription());
assertEquals("http://jenkins-ci.org/",state.getTargetUrl());
}
public void testCommitShortInfo() throws Exception {
GHCommit commit = gitHub.getUser("kohsuke").getRepository("test").getCommit("c77360d6f2ff2c2e6dd11828ad5dccf72419fa1b");
assertEquals(commit.getCommitShortInfo().getAuthor().getName(), "Kohsuke Kawaguchi");
assertEquals(commit.getCommitShortInfo().getMessage(), "Added a file");
}
public void testPullRequestPopulate() throws Exception {
GitHub gitHub = GitHub.connect();
GHRepository r = gitHub.getUser("kohsuke").getRepository("github-api");
GHPullRequest p = r.getPullRequest(17);
GHUser u = p.getUser();
assertNotNull(u.getName());
}
public void testCheckMembership() throws Exception {
GHOrganization j = gitHub.getOrganization("jenkinsci");
GHUser kohsuke = gitHub.getUser("kohsuke");
GHUser a = gitHub.getUser("a");
assertTrue(j.hasMember(kohsuke));
assertFalse(j.hasMember(a));
assertTrue(j.hasPublicMember(kohsuke));
assertFalse(j.hasPublicMember(a));
}
}

View File

@@ -0,0 +1,146 @@
package org.kohsuke;
import junit.framework.TestCase;
import org.apache.commons.io.IOUtils;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
import org.kohsuke.github.GHAsset;
import org.kohsuke.github.GHIssue;
import org.kohsuke.github.GHMilestone;
import org.kohsuke.github.GHMyself;
import org.kohsuke.github.GHRelease;
import org.kohsuke.github.GHRepository;
import org.kohsuke.github.GitHub;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.Properties;
public class LifecycleTest extends TestCase {
private GitHub gitHub;
@Override
public void setUp() throws Exception {
super.setUp();
gitHub = GitHub.connect();
}
public void testCreateRepository() throws IOException, GitAPIException {
GHMyself myself = gitHub.getMyself();
GHRepository repository = myself.getRepository("github-api-test");
if (repository != null) {
repository.delete();
}
repository = gitHub.createRepository("github-api-test",
"a test repository used to test kohsuke's github-api", "http://github-api.kohsuke.org/", true);
assertTrue(repository.getReleases().isEmpty());
try {
GHMilestone milestone = repository.createMilestone("Initial Release", "first one");
GHIssue issue = repository.createIssue("Test Issue")
.body("issue body just for grins")
.milestone(milestone)
.assignee(myself)
.label("bug")
.create();
File repoDir = new File(System.getProperty("java.io.tmpdir"), "github-api-test");
delete(repoDir);
Git origin = Git.cloneRepository()
.setBare(false)
.setURI(repository.gitHttpTransportUrl())
.setDirectory(repoDir)
.setCredentialsProvider(getCredentialsProvider(myself))
.call();
commitTestFile(myself, repoDir, origin);
GHRelease release = createRelease(repository);
GHAsset asset = uploadAsset(release);
updateAsset(release, asset);
deleteAsset(release, asset);
} finally {
repository.delete();
}
}
private void updateAsset(GHRelease release, GHAsset asset) throws IOException {
asset.setLabel("test label");
assertEquals("test label", release.getAssets().get(0).getLabel());
}
private void deleteAsset(GHRelease release, GHAsset asset) throws IOException {
asset.delete();
assertEquals(0, release.getAssets().size());
}
private GHAsset uploadAsset(GHRelease release) throws IOException {
GHAsset asset = release.uploadAsset(new File("pom.xml"), "application/text");
assertNotNull(asset);
List<GHAsset> assets = release.getAssets();
assertEquals(1, assets.size());
assertEquals("pom.xml", assets.get(0).getName());
return asset;
}
private GHRelease createRelease(GHRepository repository) throws IOException {
GHRelease builder = repository.createRelease("release_tag")
.name("Test Release")
.body("How exciting! To be able to programmatically create releases is a dream come true!")
.create();
List<GHRelease> releases = repository.getReleases();
assertEquals(1, releases.size());
GHRelease release = releases.get(0);
assertEquals("Test Release", release.getName());
return release;
}
private void commitTestFile(GHMyself myself, File repoDir, Git origin) throws IOException, GitAPIException {
File dummyFile = createDummyFile(repoDir);
DirCache cache = origin.add().addFilepattern(dummyFile.getName()).call();
origin.commit().setMessage("test commit").call();
origin.push().setCredentialsProvider(getCredentialsProvider(myself)).call();
}
private UsernamePasswordCredentialsProvider getCredentialsProvider(GHMyself myself) throws IOException {
Properties props = new Properties();
File homeDir = new File(System.getProperty("user.home"));
FileInputStream in = new FileInputStream(new File(homeDir, ".github"));
try {
props.load(in);
} finally {
IOUtils.closeQuietly(in);
}
return new UsernamePasswordCredentialsProvider(props.getProperty("login"), props.getProperty("oauth"));
}
private void delete(File toDelete) {
if (toDelete.isDirectory()) {
for (File file : toDelete.listFiles()) {
delete(file);
}
}
toDelete.delete();
}
private File createDummyFile(File repoDir) throws IOException {
File file = new File(repoDir, "testFile-" + System.currentTimeMillis());
PrintWriter writer = new PrintWriter(new FileWriter(file));
try {
writer.println("test file");
} finally {
writer.close();
}
return file;
}
}

View File

@@ -0,0 +1,24 @@
package org.kohsuke.github;
import junit.framework.TestCase;
/**
* Unit test for {@link GitHub}.
*/
public class GitHubTest extends TestCase {
public void testGitHubServerWithHttp() throws Exception {
GitHub hub = GitHub.connectToEnterprise("http://enterprise.kohsuke.org/api/v3", "bogus","bogus");
assertEquals("http://enterprise.kohsuke.org/api/v3/test", hub.getApiURL("/test").toString());
}
public void testGitHubServerWithHttps() throws Exception {
GitHub hub = GitHub.connectToEnterprise("https://enterprise.kohsuke.org/api/v3", "bogus","bogus");
assertEquals("https://enterprise.kohsuke.org/api/v3/test", hub.getApiURL("/test").toString());
}
public void testGitHubServerWithoutServer() throws Exception {
GitHub hub = GitHub.connectUsingPassword("kohsuke", "bogus");
assertEquals("https://api.github.com/test", hub.getApiURL("/test").toString());
}
}