Compare commits

...

241 Commits

Author SHA1 Message Date
Kohsuke Kawaguchi
db845850b2 [maven-release-plugin] prepare release github-api-1.58 2014-08-30 14:15:25 -07:00
Kohsuke Kawaguchi
d516597659 See if this will get me release with Maven 3.1 2014-08-30 14:13:45 -07:00
Kohsuke Kawaguchi
3af5a8145a Merge branch 'pull-117' 2014-08-30 14:03:58 -07:00
Kohsuke Kawaguchi
31bebd4a7a Design pattern in this library demands that these methods be on GHRef 2014-08-30 14:03:34 -07:00
Kohsuke Kawaguchi
ccb87258b0 Merge pull request #116 from DavidTanner/patch-1
Remove getPath()
2014-08-30 13:54:05 -07:00
Kohsuke Kawaguchi
2ab71e88bd Merge pull request #115 from bernd/add-missing-event-types
Add missing GitHub event types.
2014-08-30 13:53:23 -07:00
Kohsuke Kawaguchi
78bd7585bb Merge pull request #114 from ndeloof/master
get repository full name (including owner)
2014-08-30 13:53:02 -07:00
Kohsuke Kawaguchi
92cc81d9b8 Merge pull request #107 from msperisen/general-pagination
General pagination
2014-08-30 13:52:35 -07:00
Matt Farmer
97a1c741de Implement support for deleting a ref using the GitHub API. 2014-08-22 23:33:04 -04:00
Matt Farmer
077d693959 Add support for updating a ref using the API. 2014-08-22 23:30:22 -04:00
David Tanner
94831fc10a Remove getPath()
When getting the path and providing an enterprise url for the apiUrl, the /api/v3 portion gets duplicated.  Since they will be combined on line 231 of GitHub.java there is no point just grabbing the path.  See https://github.com/janinko/ghprb/issues/178, and https://issues.jenkins-ci.org/browse/JENKINS-24145?focusedCommentId=208270#comment-208270
2014-08-19 13:22:25 -06:00
Surya Gaddipati
1f298a88b0 [maven-release-plugin] prepare for next development iteration 2014-08-19 13:29:40 -05:00
Surya Gaddipati
7e49946bed [maven-release-plugin] prepare release github-api-1.57 2014-08-19 13:29:37 -05:00
Surya Gaddipati
1e81ab1017 Merge pull request #112 from lucamilanesio/master
Get all orgs/teams/permissions in a single GitHub API call
2014-08-19 12:57:39 -05:00
Bernd Ahlers
311180d12e Add missing DEPLOYMENT, DEPLOYMENT_STATUS, RELEASE and STATUS events. 2014-08-18 16:54:17 +02:00
Nicolas De Loof
0efb206881 get repository full name (including owner) 2014-08-06 04:48:46 +02:00
Luca Milanesio
0b92fa5615 Get all orgs/teams/permissions in a single GitHub API call
Exposes a new API call at GitHub root level to build the complete
set of organisations and teams that current user belongs to.

This change allows to massively reduce the number of calls to GitHub 
especially for people that belongs to multiple organisations with 
lots of teams and members.

Signed-off-by: Luca Milanesio <luca.milanesio@gmail.com>
2014-07-24 23:42:37 +01:00
t865095
a58a5b56b2 Merge branch 'master' of https://github.com/kohsuke/github-api 2014-07-08 14:14:59 +02:00
t865095
b9764c0a44 introduce pagination for all paged api calls and introduce cache invalidation 2014-07-08 13:52:53 +02:00
Kohsuke Kawaguchi
590f7ba511 Merge pull request #106 from lucamilanesio/master
Implement pagination on list of private+public repos of a user.
2014-07-04 19:13:04 -07:00
Luca Milanesio
449909b0e8 Implement pagination on list of private+public repos of a user.
The paginated version of listRepositories() was 
missing at GHMyself: as side-effect of this bug
when requesting a paginated list of repositories
the ones privately owned by a user were not shown.

This was caused by the missing override of the 
listRepositories(final int pageSize) at GHMyself
that caused the GHPerson implementation to invoked.

The GHPerson version uses the /users/:org/repos?per_page=x
URL which *works fine* for organisations but unfortunately
*does not return* private repositories for users.

IMHO GitHub API are quite inconsistent form this 
point of view, but they are documented in this way
so that work (inconsistently) as designed.
2014-07-03 10:05:17 +01:00
Kohsuke Kawaguchi
40780525f8 Bumping up the version in the hope of fixing site plugin problem 2014-07-02 21:39:13 -07:00
Kohsuke Kawaguchi
0e3707d1c3 [maven-release-plugin] prepare for next development iteration 2014-07-02 21:28:38 -07:00
Kohsuke Kawaguchi
8ddbef093b [maven-release-plugin] prepare release github-api-1.56 2014-07-02 21:28:34 -07:00
Kohsuke Kawaguchi
4ccdfccdf0 Oops forgot to commit this change 2014-07-02 21:27:11 -07:00
Kohsuke Kawaguchi
3c8aa0c630 Convention dictates that the method name be "listXyz"
getCollaborators() only fetching the first page is a bug, so we should fix that too.
2014-07-02 21:24:17 -07:00
Kohsuke Kawaguchi
ba519f996a Extracted non-formatting changes from pull request #98
The original commit has too many whitespace noise changes.

Reference: https://github.com/kohsuke/github-api/pull/98
2014-07-02 21:22:17 -07:00
Kohsuke Kawaguchi
62d9b92e6e Merge pull request #103 from rtyley/okhttp-2.0.0
Update to OkHttp 2.0.0, which has a new OkUrlFactory
2014-07-02 21:13:37 -07:00
Kohsuke Kawaguchi
d2c909584d Merge pull request #102 from jglick/clearer-FNFE
Better FNFE from delete()
2014-07-02 21:13:16 -07:00
Kohsuke Kawaguchi
219916eb14 Merge pull request #101 from farmdawgnation/final-answer
Un-finalize a handful of classes.
2014-07-02 21:12:59 -07:00
Roberto Tyley
0a37ac901f Update to OkHttp 2.0.0, which has a new OkUrlFactory
OkHttp changed API with v2.0.0, and the `client.open(url)` method no
longer exists:

"URLConnection support has moved to the okhttp-urlconnection module.
If you're upgrading from 1.x, this change will impact you. You will
need to add the okhttp-urlconnection module to your project and use
the OkUrlFactory to create new instances of HttpURLConnection"

https://github.com/square/okhttp/blob/master/CHANGELOG.md#version-200-rc1
2014-06-28 11:12:25 +01:00
Jesse Glick
4586baea27 Explain what a FNFE from delete() may just mean your token is lacking delete_repo. 2014-06-24 10:38:13 -04:00
Matt Farmer
d83961e85b Un-finalize a handful of classes. 2014-06-14 19:34:42 -04:00
Kohsuke Kawaguchi
7e35716ff0 [maven-release-plugin] prepare for next development iteration 2014-06-08 10:37:21 -07:00
Kohsuke Kawaguchi
d0cf1ac605 [maven-release-plugin] prepare release github-api-1.55 2014-06-08 10:37:17 -07:00
Kohsuke Kawaguchi
9a19d07ab8 I'm getting "the key is already in use" error.
Creating a new thro-away key
2014-06-08 10:36:05 -07:00
Kohsuke Kawaguchi
922be0b164 make sure the key gets deleted no matter what 2014-06-08 10:31:15 -07:00
Kohsuke Kawaguchi
d0b8e2e37e Merge pull request #97 from suryagaddipati/add_context_to_commit_status
Add support for adding context to commit status.
2014-06-08 10:21:43 -07:00
Surya Gaddipati
e3d6e08b6a Preserve api compatablity with previous version.
Support adding commitstatus without context.
2014-06-07 14:43:09 -05:00
Surya Gaddipati
16a6623095 Add support for adding context to commit status.
This groups statuses from multiple contexts to return single combined
status. More information here:
https://developer.github.com/changes/2014-03-27-combined-status-api/
2014-06-07 14:29:25 -05:00
Kohsuke Kawaguchi
7bf31ca149 [maven-release-plugin] prepare for next development iteration 2014-06-05 10:30:01 -07:00
Kohsuke Kawaguchi
030f2360ca [maven-release-plugin] prepare release github-api-1.54 2014-06-05 10:29:56 -07:00
Kohsuke Kawaguchi
08eafc4214 Merge pull request #94 from suryagaddipati/master
Add support for adding deploykeys to repo
2014-06-05 10:28:19 -07:00
Kohsuke Kawaguchi
134ece9385 Merge pull request #95 from suryagaddipati/ghref
Add support for retriving a single ref
2014-06-05 10:27:42 -07:00
Surya Gaddipati
3e4b06e959 Add support for retriving a single ref
Implements the following api method
https://developer.github.com/v3/git/refs/#get-a-reference
2014-06-04 15:11:27 -05:00
Surya Gaddipati
3e3c6f70ba Add support for adding deploykeys to repo
Implements https://developer.github.com/v3/repos/keys/
2014-06-03 15:26:04 -05:00
Kohsuke Kawaguchi
3097378a10 Merge pull request #93 from vr100/master
Upgrading to 1.12 version for bridge-method-annotation and bridge-method-injector - fix for #91
2014-05-28 20:52:07 -07:00
vr100
53f7a0f78a Upgrading to 1.12 version for bridge-method-annotation and bridge-method-injector - fix for #91 2014-05-28 12:41:19 +05:30
Kohsuke Kawaguchi
45a6841772 added a method to visit sub-directory 2014-05-27 20:57:42 -07:00
Kohsuke Kawaguchi
80b93a6e33 doc improvement 2014-05-27 20:44:44 -07:00
Kohsuke Kawaguchi
6aaab641be Using the latest 2014-05-10 15:57:56 -07:00
Kohsuke Kawaguchi
4999490c06 [maven-release-plugin] prepare for next development iteration 2014-05-10 15:52:17 -07:00
Kohsuke Kawaguchi
c7c1cd8bb3 [maven-release-plugin] prepare release github-api-1.53 2014-05-10 15:52:12 -07:00
Kohsuke Kawaguchi
8a1f116305 Renamed to avoid colliding with LifecycleTest 2014-05-10 15:49:31 -07:00
Kohsuke Kawaguchi
f8fe1dda6d Presence check needs to run on the same repo 2014-05-10 15:46:14 -07:00
Kohsuke Kawaguchi
bf44232d5c Updated test case 2014-05-10 15:43:15 -07:00
Kohsuke Kawaguchi
8c8ba47ef9 Fork into an org to simplify access control 2014-05-10 15:38:13 -07:00
Kohsuke Kawaguchi
b3580002d0 doc improvement 2014-05-10 15:37:21 -07:00
Kohsuke Kawaguchi
1249199b22 push fails with HTTPS 2014-05-10 15:34:57 -07:00
Kohsuke Kawaguchi
f39a47f354 This test seems to fail randomly 2014-05-10 15:32:58 -07:00
Kohsuke Kawaguchi
f58f15925f Fake a big rate limit
This fixes issue #78
2014-05-10 15:30:33 -07:00
Kohsuke Kawaguchi
a071f8bb86 Exposed more endpoints.
This fixes issue #64.
2014-05-10 15:21:59 -07:00
Kohsuke Kawaguchi
d33609ee57 Make sure getRepositories() for the user himself returns private repositories
This fixes issue #88
2014-05-10 15:16:12 -07:00
Kohsuke Kawaguchi
f2c08bc1e8 Added ability to create a pull request.
This fixes issue #79.
2014-05-10 15:05:48 -07:00
Kohsuke Kawaguchi
688d8ed7bc Added databinding for files 2014-05-10 14:11:41 -07:00
Kohsuke Kawaguchi
2ddb4018b8 added data binding for a file in gist 2014-05-10 14:07:53 -07:00
Kohsuke Kawaguchi
3ec25683f7 doc improvement 2014-05-10 14:03:49 -07:00
Kohsuke Kawaguchi
25d426f483 Bug fix based on tests 2014-05-10 14:03:12 -07:00
Kohsuke Kawaguchi
9d91ebc47a Not sure why tests weren't in the right package 2014-05-10 13:53:13 -07:00
Kohsuke Kawaguchi
c784ab6632 Added Gist support 2014-05-10 13:52:36 -07:00
Kohsuke Kawaguchi
ed5e3f13a1 exposed via getter.
This fixes issue #52.
2014-05-10 13:25:33 -07:00
Kohsuke Kawaguchi
3dcd82745e Resolved issue #54 2014-05-10 12:52:00 -07:00
Kohsuke Kawaguchi
c564dc5f29 All tests should use the secondary account 2014-05-10 12:47:28 -07:00
Kohsuke Kawaguchi
d64453b661 Fixing tests 2014-05-10 12:44:52 -07:00
Kohsuke Kawaguchi
834ed7db0e Merge branch 'pull-84' 2014-05-10 12:42:34 -07:00
Kohsuke Kawaguchi
db1dde533f Updated tests to use a separate account 2014-05-10 12:42:19 -07:00
Kohsuke Kawaguchi
9c66d9465e Added removal of release 2014-05-10 12:27:18 -07:00
Kohsuke Kawaguchi
8c193d004e Updated test cases to reflect my changes 2014-05-10 12:11:20 -07:00
Kohsuke Kawaguchi
7cbd7f999a Pointless to define a builder that doesn't take parameters 2014-05-10 12:08:17 -07:00
Kohsuke Kawaguchi
4340c8cb00 Support pagination in releases just like tags 2014-05-10 12:00:44 -07:00
Kohsuke Kawaguchi
6f6583772d Should support pagination 2014-05-10 11:59:46 -07:00
Kohsuke Kawaguchi
c62bc8d5da subsumed by GHRepository.queryCommits() 2014-05-10 11:58:22 -07:00
Kohsuke Kawaguchi
f68bfcb3e2 [maven-release-plugin] prepare for next development iteration 2014-05-09 18:39:33 -07:00
Kohsuke Kawaguchi
a37ade13cd [maven-release-plugin] prepare release github-api-1.52 2014-05-09 18:39:29 -07:00
Kohsuke Kawaguchi
1e0129f2d8 A few more properties while we are at it 2014-05-09 18:36:46 -07:00
Kohsuke Kawaguchi
2e1d4381b0 looks like this property was renamed 2014-05-09 18:34:51 -07:00
Kohsuke Kawaguchi
a409b4f49c Merge branch 'pull-86'
Conflicts:
	src/main/java/org/kohsuke/github/GHOrganization.java
	src/main/java/org/kohsuke/github/Requester.java
	src/test/java/org/kohsuke/AppTest.java
2014-05-09 18:30:59 -07:00
Kohsuke Kawaguchi
926776204b Implemented the builder pattern to my liking 2014-05-09 18:28:34 -07:00
Kohsuke Kawaguchi
bba490127d Merge branch 'pull-87' 2014-05-09 17:59:30 -07:00
Kohsuke Kawaguchi
80672c43c0 Restored backward compatibility 2014-05-09 17:59:22 -07:00
Kohsuke Kawaguchi
926e0ea0e5 added more tests.
Looks like org APIs only work for owners of the org
2014-05-09 17:55:53 -07:00
Kohsuke Kawaguchi
6da169cd56 a convenience method 2014-05-09 17:54:24 -07:00
Kohsuke Kawaguchi
aaf10aa118 added a test 2014-05-09 17:53:08 -07:00
Kohsuke Kawaguchi
145bdacf13 Added a method to list up teams 2014-05-09 17:50:19 -07:00
Kohsuke Kawaguchi
51309efd46 Graceful error handling 2014-05-09 17:43:27 -07:00
Kohsuke Kawaguchi
60fe143b07 detect the common problem
If the caller tries to parse a pull event into a pull_request event, we'd choke with NPE. This change avoids that.
2014-04-30 13:27:37 -07:00
Kelly Campbell
3f921b69f6 Fix bug in GHMyself.getEmails due to API change 2014-04-25 22:55:45 -04:00
vr100
d2ac81d7ba Using builder pattern to list commits in a repo 2014-04-23 09:45:11 +05:30
Francois Berthault
2e5a9479a8 create a Release & Branch 2014-04-21 10:45:10 +02:00
Kohsuke Kawaguchi
ec664ab6a2 Merge pull request #83 from antonkrasov/master
add tarball_url and zipball_url to GHRelease
2014-04-19 11:50:39 -07:00
Anton Krasov
8818bf8cf0 add tarball_url and zipball_url to GHRelease 2014-04-18 17:28:51 +03:00
Kohsuke Kawaguchi
cc53e0c8ff [maven-release-plugin] prepare for next development iteration 2014-04-13 08:58:53 -07:00
Kohsuke Kawaguchi
8353499d18 [maven-release-plugin] prepare release github-api-1.51 2014-04-13 08:58:49 -07:00
Kohsuke Kawaguchi
d2c5994616 I always get this confused 2014-04-13 08:57:48 -07:00
Kohsuke Kawaguchi
500b9097f7 doc improvement 2014-04-13 08:53:00 -07:00
Kohsuke Kawaguchi
7993266e6e This is also useful for setting timeout 2014-04-13 08:51:58 -07:00
Kohsuke Kawaguchi
74090103a9 doc improvement 2014-04-13 08:48:16 -07:00
Kohsuke Kawaguchi
a18cde2340 Abstracted away HTTP connector and added OkHttp implementation for convenience 2014-04-13 08:46:21 -07:00
Kohsuke Kawaguchi
ee98e97876 Merge pull request #80 from rtyley/remove-member-of-org
Add support for removing a user from an Organisation
2014-04-13 08:22:23 -07:00
Kohsuke Kawaguchi
ff63bafd72 Merge pull request #82 from prazanna/master
Cast url.openConnection() to HttpURLConnection instead of HttpsURLConnec...
2014-04-13 08:22:00 -07:00
Prasanna Rajaperumal
3f8c8be77a Cast url.openConnection() to HttpURLConnection instead of HttpsURLConnection. Useful for enterprise githubs which does not have https set up. 2014-04-09 18:13:10 -07:00
Roberto Tyley
769cdc73e7 Add support for removing a user from an Organisation 2014-04-02 18:47:50 +01:00
Kohsuke Kawaguchi
cef8f74c00 Added LICENSE 2014-04-02 09:31:02 -07:00
Kohsuke Kawaguchi
9f5ab709de [maven-release-plugin] prepare for next development iteration 2014-03-28 10:59:51 -07:00
Kohsuke Kawaguchi
c6fdae3b3c [maven-release-plugin] prepare release github-api-1.50 2014-03-28 10:59:47 -07:00
Kohsuke Kawaguchi
320cf0fea2 Seeing a failure to create 2014-03-28 10:58:41 -07:00
Kohsuke Kawaguchi
0261f1262f fixed a test case 2014-03-28 10:58:41 -07:00
Kohsuke Kawaguchi
5278ae3662 Merge pull request #75 from rtyley/check-team-membership
Support the check-user-team-membership API call
2014-03-28 10:24:18 -07:00
Kohsuke Kawaguchi
2941c44de2 Merge pull request #73 from rtyley/fix-issue-labels-setter
Fix GHIssue.setLabels()
2014-03-28 10:23:15 -07:00
Kohsuke Kawaguchi
627222602e Merge pull request #72 from farmdawgnation/patch-1
Un-finalize GHContent.
2014-03-28 10:23:03 -07:00
Kohsuke Kawaguchi
e13b1ffc48 looks like the 'tire' repository is gone 2014-03-28 10:11:15 -07:00
Kohsuke Kawaguchi
10bafce217 fixed a test 2014-03-28 10:08:08 -07:00
Kohsuke Kawaguchi
4817969495 fixed a broken test 2014-03-28 10:06:50 -07:00
Kohsuke Kawaguchi
2feda55eb7 looks like we need to pick another user here 2014-03-28 10:01:59 -07:00
Kohsuke Kawaguchi
a74cfd453a Added support for getting issues by milestone and state.
rework AppTest to work better for people other than kohsuke

This patch was originally from @evanchooly. Reworked to avoid whitespace
changes.
2014-03-28 09:57:04 -07:00
Kohsuke Kawaguchi
a360f65133 merging a portion of pull request #70 2014-03-28 09:49:38 -07:00
Kohsuke Kawaguchi
6a903d65a7 Fixed a bug in the databinding of the 'private' field.
Luca pointed out this bug in
ae7a616e92
2014-03-28 09:47:27 -07:00
Kohsuke Kawaguchi
d4b3166036 Merge branch 'pull-69' 2014-03-28 09:42:00 -07:00
Kohsuke Kawaguchi
21a54e2333 The parent commit appears to be totally untested.
The placement of the method makes no sense, and the API route is wrong.
The parameter is missing, and the expected response type is wrong.
2014-03-28 09:40:55 -07:00
Kohsuke Kawaguchi
37c58bffba indentation fix 2014-03-28 09:37:11 -07:00
Kohsuke Kawaguchi
4a42b7277c Merge branch 'pull-76' 2014-03-28 09:35:43 -07:00
Kohsuke Kawaguchi
071aea397b Keep the original signature intact for backward compatibility 2014-03-28 09:35:37 -07:00
vr100
9c02f918c1 In order to handle http based github enterprise vm url, changing https to http 2014-03-26 09:23:26 +05:30
vr100
15c5c9615e Added pagesize parameter to 'Get commits by author and branch' api 2014-03-20 15:00:34 +05:30
vr100
0b74692d62 change in comment 2014-03-19 11:38:03 +05:30
vr100
8f24f55eb7 Added apis to list commits by author and list the repos of an organization (including the private ones) 2014-03-19 11:06:11 +05:30
Kohsuke Kawaguchi
cd5ab1ae9b need to populate enum 2014-03-18 11:52:00 -07:00
Roberto Tyley
d29ea97948 Fix org.getMembers() to 'wrap' returned users for post-populating
This fixes an issue introduced with PR #66 in commit fee02a3d16 - the
`getMembers()` method switched to using paging, but lost the code that
populated the users with a full set of values.

This commit just applies 'wrapping' to all users returned, so that methods
like `user.getCreatedAt()` can be called on returned users without throwing
a NullPointerException - the reference to 'root' allows the user objects to
populate themselves.
2014-03-14 09:22:15 +00:00
Roberto Tyley
9930e7bb93 Add org public-members call, to complement the full members list
http://developer.github.com/v3/orgs/members/#public-members-list
2014-03-13 22:04:45 +00:00
Roberto Tyley
b1d37e4848 Support the check-user-team-membership API call
http://developer.github.com/v3/orgs/teams/#get-team-member
2014-03-12 21:46:14 +00:00
Roberto Tyley
893152cdf3 Fix GHIssue.setLabels() - *don't* set 'assignee' instead! 2014-03-08 16:05:33 +00:00
Matt Farmer
efbf98751d Un-finalize GHContent.
GHContent was declared as a final class, which makes it impossible to mock with libraries like Mockito.
2014-03-07 22:14:21 -05:00
Luca Milanesio
caac294e79 BugFix when listing repositories of a GitHub Organisation
Repositories of a GitHub Organisation are under 
/orgs/:org/repos as documented at:
http://developer.github.com/v3/repos/#list-organization-repositories

We need then to override the GHPerson implementation
which would have just returned the user's repositories.
2014-02-28 17:45:36 +00:00
vanjikumaran
d145ad8f47 Created new method to automate the merge 2014-02-24 17:00:11 +05:30
lindseydew
628034ae8c add a filter param to the get members method 2014-02-11 18:07:01 +00:00
Ryan Kennedy
fee02a3d16 Support paging when fetching organization members
Organization members are a paged list in the V3 GitHub API. Previously the code would fetch only the first page of ~30 members and return that. This change switches the return to a PagedIterable<GHUser> so clients can fetch the entire organization's member list.
2014-01-22 09:56:06 -08:00
Kohsuke Kawaguchi
ce5ae13bf6 [maven-release-plugin] prepare for next development iteration 2014-01-06 08:56:38 -08:00
Kohsuke Kawaguchi
26b6a94e32 [maven-release-plugin] prepare release github-api-1.49 2014-01-06 08:56:34 -08:00
Kohsuke Kawaguchi
be6ef475ba [maven-release-plugin] prepare for next development iteration 2014-01-06 08:52:00 -08:00
Kohsuke Kawaguchi
55e218ac37 [maven-release-plugin] prepare release github-api-1.48 2014-01-06 08:51:56 -08:00
Kohsuke Kawaguchi
7d1f636cdd Recover from earlier test failures 2014-01-01 21:12:17 -08:00
Kohsuke Kawaguchi
a4b8b9b09b Merge pull request #61 from lucamilanesio/master
Fetching of user's verified keys through standard OAuth scope.
2014-01-01 15:26:58 -08:00
Kohsuke Kawaguchi
4e8e28d27c Merge pull request #59 from farmdawgnation/contents-api
Contents API
2014-01-01 15:26:23 -08:00
Luca Milanesio
4b52414435 Fetching of user's verified keys through standard OAuth scope.
Allow the fetching of a user's verified key list without
requiring to have an OAuth user scope (which implies
user's profile READ/WRITE permissions).
This would relax the requirements of GitHub OAuth scope
when fetching public information such as their SSH
public keys.
2013-12-11 00:30:27 +00:00
Matt Farmer
811b96bcbe Implement the ability to retrieve content from a particular ref. 2013-11-28 10:02:37 -05:00
Matt Farmer
f3207855ca Implement a CRUD integration test for the content api. 2013-11-28 09:45:54 -05:00
Matt Farmer
9f3c5b93e3 Store encoded content in class level variable on update. 2013-11-28 09:45:21 -05:00
Matt Farmer
a56357f0c1 Implement the ability to retrieve conent if not populated.
The create and update API calls don't send back the content you just
sent, so that field is null. If this GHContent was instantiated from one
of those calls, we want to make sure we can retrieve the content (and
updated sha, etc) on the fly.
2013-11-28 09:44:27 -05:00
Matt Farmer
f8d14d2e56 Implement the ability to delete content. 2013-11-28 00:37:40 -05:00
Matt Farmer
b0c30759c8 Properly wrap content on directory listing retrieval. 2013-11-28 00:33:07 -05:00
Matt Farmer
54037e6e10 Add the ability to specify a branch name with GHContent updates. 2013-11-28 00:29:49 -05:00
Matt Farmer
fc260d4a37 Implement the ability to create content via content api. 2013-11-28 00:21:25 -05:00
Matt Farmer
387d4e5bc9 Return the new GHContentUpdateResponse from content changes. 2013-11-28 00:12:30 -05:00
Matt Farmer
4ffd46b391 Implement the ability to update existing content. 2013-11-28 00:03:43 -05:00
Matt Farmer
3b49370c06 Add the ability to retrieve the sha of the content. 2013-11-27 23:28:40 -05:00
Alexandre COLLIGNON
925d26e54c Add GitHub Contents API read methods 2013-11-27 22:35:41 -05:00
Kohsuke Kawaguchi
1283006b53 [maven-release-plugin] prepare for next development iteration 2013-11-27 11:57:40 -08:00
Kohsuke Kawaguchi
83a718c9db [maven-release-plugin] prepare release github-api-1.47 2013-11-27 11:57:35 -08:00
Kohsuke Kawaguchi
2abf03ccb7 Minor doc improvement 2013-11-27 11:52:47 -08:00
Kohsuke Kawaguchi
a59810a487 Merge pull request #56 from endeavor85/master
Use `PagedIterator<GHIssue>` to retrieve repository issues
2013-11-27 11:49:44 -08:00
Kohsuke Kawaguchi
9cc9e06ad1 Merge pull request #58 from rtholmes/master
Add support for PULL_REQUEST_REVIEW_COMMENT event types.
2013-11-27 11:49:17 -08:00
reid holmes
0108a0c146 Add support for PULL_REQUEST_REVIEW_COMMENT event types. 2013-11-26 15:13:36 -05:00
endeavor85
4188758d84 Use PagedIterator<GHIssue> to retrieve repository issues. Overcomes
default 30 item page size limit.
2013-11-24 02:54:18 -06:00
Kohsuke Kawaguchi
2144100f81 [maven-release-plugin] prepare for next development iteration 2013-11-13 13:05:29 -08:00
Kohsuke Kawaguchi
8a61e04be6 [maven-release-plugin] prepare release github-api-1.46 2013-11-13 13:05:24 -08:00
Kohsuke Kawaguchi
eaeb083891 Perhaps this will help resolve? 2013-11-12 13:13:18 -08:00
Kohsuke Kawaguchi
25e117b412 oops 2013-11-12 12:47:37 -08:00
Kohsuke Kawaguchi
6b7ceed1a5 Short cut 2013-11-12 12:45:39 -08:00
Kohsuke Kawaguchi
0533fc98a2 bumped up to a newer version 2013-11-12 12:21:17 -08:00
Kohsuke Kawaguchi
edde3ab2fb added one more field based on observations 2013-11-12 12:17:57 -08:00
Kohsuke Kawaguchi
ec20e518be adding a new payload implementation for PushEvent 2013-11-12 12:14:34 -08:00
Kohsuke Kawaguchi
930a582afa Unified various different instances of the "author" class 2013-11-12 12:10:33 -08:00
Kohsuke Kawaguchi
7536eeea54 V2 code no longer needed 2013-11-12 12:01:49 -08:00
Kohsuke Kawaguchi
d940d43907 added more event support 2013-11-12 11:52:40 -08:00
Kohsuke Kawaguchi
3ad3dd9493 [maven-release-plugin] prepare for next development iteration 2013-11-09 15:19:27 -08:00
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
52 changed files with 3803 additions and 537 deletions

22
LICENSE.txt Normal file
View File

@@ -0,0 +1,22 @@
Copyright (c) 2011- Kohsuke Kawaguchi and other contributors
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.

56
pom.xml
View File

@@ -3,11 +3,11 @@
<parent>
<groupId>org.kohsuke</groupId>
<artifactId>pom</artifactId>
<version>4</version>
<version>10</version>
</parent>
<artifactId>github-api</artifactId>
<version>1.40</version>
<version>1.58</version>
<name>GitHub API for Java</name>
<url>http://github-api.kohsuke.org/</url>
<description>GitHub API for Java</description>
@@ -16,6 +16,7 @@
<connection>scm:git:git@github.com/kohsuke/${project.artifactId}.git</connection>
<developerConnection>scm:git:ssh://git@github.com/kohsuke/${project.artifactId}.git</developerConnection>
<url>http://${project.artifactId}.kohsuke.org/</url>
<tag>github-api-1.58</tag>
</scm>
<distributionManagement>
@@ -34,7 +35,7 @@
<plugin>
<groupId>com.infradna.tool</groupId>
<artifactId>bridge-method-injector</artifactId>
<version>1.2</version>
<version>1.12</version>
<executions>
<execution>
<goals>
@@ -60,13 +61,13 @@
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<version>4.11</version>
<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>
@@ -76,8 +77,7 @@
<dependency>
<groupId>com.infradna.tool</groupId>
<artifactId>bridge-method-annotation</artifactId>
<version>1.4</version>
<optional>true</optional>
<version>1.12</version>
</dependency>
<dependency>
<groupId>org.kohsuke.stapler</groupId>
@@ -85,14 +85,42 @@
<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>
<dependency>
<groupId>com.squareup.okhttp</groupId>
<artifactId>okhttp-urlconnection</artifactId>
<version>2.0.0</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.kohsuke</groupId>
<artifactId>wordnet-random-name</artifactId>
<version>1.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.5</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>repo.jenkins-ci.org</id>
<url>http://repo.jenkins-ci.org/public/</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>repo.jenkins-ci.org</id>
<url>http://repo.jenkins-ci.org/public/</url>
</pluginRepository>
</pluginRepositories>
<reporting>
<plugins>
@@ -102,4 +130,12 @@
</plugin>
</plugins>
</reporting>
<licenses>
<license>
<name>The MIT license</name>
<url>http://www.opensource.org/licenses/mit-license.php</url>
<distribution>repo</distribution>
</license>
</licenses>
</project>

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

@@ -1,5 +1,7 @@
package org.kohsuke.github;
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
import java.io.IOException;
import java.net.URL;
import java.util.AbstractList;
@@ -16,6 +18,47 @@ 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;
@WithBridgeMethods(value=GHAuthor.class,castRequired=true)
public GitUser getAuthor() {
return author;
}
@WithBridgeMethods(value=GHAuthor.class,castRequired=true)
public GitUser getCommitter() {
return committer;
}
/**
* Commit message.
*/
public String getMessage() {
return message;
}
public int getCommentCount() {
return comment_count;
}
}
/**
* @deprecated Use {@link GitUser} instead.
*/
public static class GHAuthor extends GitUser {
}
public static class Stats {
int total,additions,deletions;
@@ -110,8 +153,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() {

View File

@@ -31,11 +31,11 @@ package org.kohsuke.github;
public class GHCommitPointer {
private String ref, sha, label;
private GHUser user;
private GHRepository repository/*V2*/,repo/*V3*/;
private GHRepository repo;
/**
* This points to the user who owns
* the {@link #repository}.
* the {@link #getRepository()}.
*/
public GHUser getUser() {
return user;
@@ -45,7 +45,7 @@ public class GHCommitPointer {
* The repository that contains the commit.
*/
public GHRepository getRepository() {
return repo!=null ? repo : repository;
return repo;
}
/**
@@ -72,6 +72,5 @@ public class GHCommitPointer {
void wrapUp(GitHub root) {
if (user!=null) user.root = root;
if (repo!=null) repo.wrap(root);
if (repository!=null) repository.wrap(root);
}
}

View File

@@ -0,0 +1,105 @@
package org.kohsuke.github;
import java.util.Date;
/**
* Builds up query for listing commits.
*
* <p>
* Call various methods that set the filter criteria, then {@link #list()} method to actually list up the commit.
*
* <pre>
* GHRepository r = ...;
* for (GHCommit c : r.queryCommits().since(x).until(y).author("kohsuke")) {
* ...
* }
* </pre>
*
* @author Kohsuke Kawaguchi
* @see GHRepository#queryCommits()
*/
public class GHCommitQueryBuilder {
private final Requester req;
private final GHRepository repo;
/*package*/ GHCommitQueryBuilder(GHRepository repo) {
this.repo = repo;
this.req = repo.root.retrieve(); // requester to build up
}
/**
* GItHub login or email address by which to filter by commit author.
*/
public GHCommitQueryBuilder author(String author) {
req.with("author",author);
return this;
}
/**
* Only commits containing this file path will be returned.
*/
public GHCommitQueryBuilder path(String path) {
req.with("path",path);
return this;
}
/**
* Specifies the SHA1 commit / tag / branch / etc to start listing commits from.
*
*/
public GHCommitQueryBuilder from(String ref) {
req.with("sha",ref);
return this;
}
public GHCommitQueryBuilder pageSize(int pageSize) {
req.with("per_page",pageSize);
return this;
}
/**
* Only commits after this date will be returned
*/
public GHCommitQueryBuilder since(Date dt) {
req.with("since",GitHub.printDate(dt));
return this;
}
/**
* Only commits after this date will be returned
*/
public GHCommitQueryBuilder since(long timestamp) {
return since(new Date(timestamp));
}
/**
* Only commits before this date will be returned
*/
public GHCommitQueryBuilder until(Date dt) {
req.with("until",GitHub.printDate(dt));
return this;
}
/**
* Only commits before this date will be returned
*/
public GHCommitQueryBuilder until(long timestamp) {
return until(new Date(timestamp));
}
/**
* Lists up the commits with the criteria built so far.
*/
public PagedIterable<GHCommit> list() {
return new PagedIterable<GHCommit>() {
public PagedIterator<GHCommit> iterator() {
return new PagedIterator<GHCommit>(req.asIterator(repo.getApiTailUrl("commits"), GHCommit[].class)) {
protected void wrapUp(GHCommit[] page) {
for (GHCommit c : page)
c.wrapUp(repo);
}
};
}
};
}
}

View File

@@ -16,6 +16,7 @@ public class GHCommitStatus {
String target_url,description;
int id;
String url;
String context;
GHUser creator;
private GitHub root;
@@ -69,4 +70,8 @@ public class GHCommitStatus {
public GHUser getCreator() {
return creator;
}
public String getContext() {
return context;
}
}

View File

@@ -1,5 +1,7 @@
package org.kohsuke.github;
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
import java.net.URL;
import java.util.Date;
@@ -109,11 +111,13 @@ public class GHCompare {
return message;
}
public User getAuthor() {
@WithBridgeMethods(value=User.class,castRequired=true)
public GitUser getAuthor() {
return author;
}
public User getCommitter() {
@WithBridgeMethods(value=User.class,castRequired=true)
public GitUser getCommitter() {
return committer;
}
@@ -134,23 +138,13 @@ public class GHCompare {
}
}
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);
}
/**
* @deprecated use {@link GitUser} instead.
*/
public static class User extends GitUser {
}
public static enum Status {
behind, ahead, identical;
behind, ahead, identical
}
}

View File

@@ -0,0 +1,194 @@
package org.kohsuke.github;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import javax.xml.bind.DatatypeConverter;
/**
* A Content of a repository.
*
* @author Alexandre COLLIGNON
* @see GHRepository#getFileContent(String)
*/
@SuppressWarnings({"UnusedDeclaration"})
public class GHContent {
private GHRepository owner;
private String type;
private String encoding;
private long size;
private String sha;
private String name;
private String path;
private String content;
private String url; // this is the API url
private String git_url; // this is the Blob url
private String html_url; // this is the UI
public GHRepository getOwner() {
return owner;
}
public String getType() {
return type;
}
public String getEncoding() {
return encoding;
}
public long getSize() {
return size;
}
public String getSha() {
return sha;
}
public String getName() {
return name;
}
public String getPath() {
return path;
}
/**
* Retrieve the decoded content that is stored at this location.
*
* Due to the nature of GitHub's API, you're not guaranteed that
* the content will already be populated, so this may trigger
* network activity, and can throw an IOException.
**/
public String getContent() throws IOException {
return new String(DatatypeConverter.parseBase64Binary(getEncodedContent()));
}
/**
* Retrieve the raw content that is stored at this location.
*
* Due to the nature of GitHub's API, you're not guaranteed that
* the content will already be populated, so this may trigger
* network activity, and can throw an IOException.
**/
public String getEncodedContent() throws IOException {
if (content != null)
return content;
GHContent retrievedContent = owner.getFileContent(path);
this.size = retrievedContent.size;
this.sha = retrievedContent.sha;
this.content = retrievedContent.content;
this.url = retrievedContent.url;
this.git_url = retrievedContent.git_url;
this.html_url = retrievedContent.html_url;
return content;
}
public String getUrl() {
return url;
}
public String getGitUrl() {
return git_url;
}
public String getHtmlUrl() {
return html_url;
}
public boolean isFile() {
return "file".equals(type);
}
public boolean isDirectory() {
return "dir".equals(type);
}
/**
* List immediate children of this directory.
*/
public PagedIterable<GHContent> listDirectoryContent() throws IOException {
if (!isDirectory())
throw new IllegalStateException(path+" is not a directory");
return new PagedIterable<GHContent>() {
public PagedIterator<GHContent> iterator() {
return new PagedIterator<GHContent>(owner.root.retrieve().asIterator(url, GHContent[].class)) {
@Override
protected void wrapUp(GHContent[] page) {
GHContent.wrap(page,owner);
}
};
}
};
}
public GHContentUpdateResponse update(String newContent, String commitMessage) throws IOException {
return update(newContent, commitMessage, null);
}
public GHContentUpdateResponse update(String newContent, String commitMessage, String branch) throws IOException {
String encodedContent = DatatypeConverter.printBase64Binary(newContent.getBytes());
Requester requester = new Requester(owner.root)
.with("path", path)
.with("message", commitMessage)
.with("sha", sha)
.with("content", encodedContent)
.method("PUT");
if (branch != null) {
requester.with("branch", branch);
}
GHContentUpdateResponse response = requester.to(getApiRoute(), GHContentUpdateResponse.class);
response.getContent().wrap(owner);
response.getCommit().wrapUp(owner);
this.content = encodedContent;
return response;
}
public GHContentUpdateResponse delete(String message) throws IOException {
return delete(message, null);
}
public GHContentUpdateResponse delete(String commitMessage, String branch) throws IOException {
Requester requester = new Requester(owner.root)
.with("path", path)
.with("message", commitMessage)
.with("sha", sha)
.method("DELETE");
if (branch != null) {
requester.with("branch", branch);
}
GHContentUpdateResponse response = requester.to(getApiRoute(), GHContentUpdateResponse.class);
response.getCommit().wrapUp(owner);
return response;
}
private String getApiRoute() {
return "/repos/" + owner.getOwnerName() + "/" + owner.getName() + "/contents/" + path;
}
GHContent wrap(GHRepository owner) {
this.owner = owner;
return this;
}
public static GHContent[] wrap(GHContent[] contents, GHRepository repository) {
for (GHContent unwrappedContent : contents) {
unwrappedContent.wrap(repository);
}
return contents;
}
}

View File

@@ -0,0 +1,18 @@
package org.kohsuke.github;
/**
* The response that is returned when updating
* repository content.
**/
public class GHContentUpdateResponse {
private GHContent content;
private GHCommit commit;
public GHContent getContent() {
return content;
}
public GHCommit getCommit() {
return commit;
}
}

View File

@@ -0,0 +1,46 @@
package org.kohsuke.github;
import java.io.IOException;
import org.apache.commons.lang.builder.ToStringBuilder;
public class GHDeployKey {
protected String url, key, title;
protected boolean verified;
protected int id;
private GHRepository owner;
public int getId() {
return id;
}
public String getKey() {
return key;
}
public String getTitle() {
return title;
}
public String getUrl() {
return url;
}
public boolean isVerified() {
return verified;
}
public GHDeployKey wrap(GHRepository repo) {
this.owner = repo;
return this;
}
public String toString() {
return new ToStringBuilder(this).append("title",title).append("id",id).append("key",key).toString();
}
public void delete() throws IOException {
new Requester(owner.root).method("DELETE").to(String.format("/repos/%s/%s/keys/%d", owner.getOwnerName(), owner.getName(), id));
}
}

View File

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

View File

@@ -6,11 +6,14 @@ package org.kohsuke.github;
* See http://developer.github.com/v3/events/types/
*
* @author Kohsuke Kawaguchi
* @see GHEventInfo
*/
public enum GHEvent {
COMMIT_COMMENT,
CREATE,
DELETE,
DEPLOYMENT,
DEPLOYMENT_STATUS,
DOWNLOAD,
FOLLOW,
FORK,
@@ -22,7 +25,10 @@ public enum GHEvent {
MEMBER,
PUBLIC,
PULL_REQUEST,
PULL_REQUEST_REVIEW_COMMENT,
PUSH,
RELEASE,
STATUS,
TEAM_ADD,
WATCH
}

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.
*
@@ -63,6 +63,13 @@ public class GHEventInfo {
return root.getUser(actor.getLogin());
}
/**
* Quick way to just get the actor of the login.
*/
public String getActorLogin() throws IOException {
return actor.getLogin();
}
public GHOrganization getOrganization() throws IOException {
return (org==null || org.getLogin()==null) ? null : root.getOrganization(org.getLogin());
}

View File

@@ -1,6 +1,7 @@
package org.kohsuke.github;
import java.io.Reader;
import java.util.List;
/**
* Base type for types used in databinding of the event payload.
@@ -8,6 +9,7 @@ import java.io.Reader;
* @see GitHub#parseEventPayload(Reader, Class)
* @see GHEventInfo#getPayload(Class)
*/
@SuppressWarnings("UnusedDeclaration")
public abstract class GHEventPayload {
protected GitHub root;
@@ -21,12 +23,13 @@ public abstract class GHEventPayload {
/**
* A pull request status has changed.
*
* @see http://developer.github.com/v3/activity/events/types/#pullrequestevent
* @see <a href="http://developer.github.com/v3/activity/events/types/#pullrequestevent">authoritative source</a>
*/
public static class PullRequest extends GHEventPayload {
private String action;
private int number;
private GHPullRequest pull_request;
private GHRepository repository;
public String getAction() {
return action;
@@ -41,17 +44,28 @@ 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 (pull_request==null)
throw new IllegalStateException("Expected pull_request payload, but got something else. Maybe we've got another type of event?");
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
* @see <a href="http://developer.github.com/v3/activity/events/types/#issuecommentevent">authoritative source</a>
*/
public static class IssueComment extends GHEventPayload {
private String action;
@@ -95,4 +109,88 @@ public abstract class GHEventPayload {
comment.wrapUp(issue);
}
}
/**
* A commit was pushed.
*
* @see <a href="http://developer.github.com/v3/activity/events/types/#pushevent">authoritative source</a>
*/
public static class Push extends GHEventPayload {
private String head, before;
private String ref;
private int size;
private List<PushCommit> commits;
/**
* The SHA of the HEAD commit on the repository
*/
public String getHead() {
return head;
}
/**
* This is undocumented, but it looks like this captures the commit that the ref was pointing to
* before the push.
*/
public String getBefore() {
return before;
}
/**
* The full Git ref that was pushed. Example: “refs/heads/master”
*/
public String getRef() {
return ref;
}
/**
* The number of commits in the push.
* Is this always the same as {@code getCommits().size()}?
*/
public int getSize() {
return size;
}
/**
* The list of pushed commits.
*/
public List<PushCommit> getCommits() {
return commits;
}
/**
* Commit in a push
*/
public static class PushCommit {
private GitUser author;
private String url, sha, message;
private boolean distinct;
public GitUser getAuthor() {
return author;
}
/**
* Points to the commit API resource.
*/
public String getUrl() {
return url;
}
public String getSha() {
return sha;
}
public String getMessage() {
return message;
}
/**
* Whether this commit is distinct from any that have been pushed before.
*/
public boolean isDistinct() {
return distinct;
}
}
}
}

View File

@@ -0,0 +1,200 @@
package org.kohsuke.github;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.IOException;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
/**
* Gist
*
* @author Kohsuke Kawaguchi
* @see GHUser#listGists()
* @see GitHub#getGist(String)
* @see GitHub#createGist()
*/
public class GHGist {
/*package almost final*/ GHUser owner;
/*package almost final*/ GitHub root;
private String url, forks_url, commits_url, id, git_pull_url, git_push_url, html_url;
@JsonProperty("public")
private boolean _public;
private String created_at, updated_at, description;
private int comments;
private String comments_url;
private Map<String,GHGistFile> files = new HashMap<String, GHGistFile>();
/**
* User that owns this Gist.
*/
public GHUser getOwner() {
return owner;
}
/**
* API URL of this gist, such as 'https://api.github.com/gists/12345'
*/
public String getUrl() {
return url;
}
public String getForksUrl() {
return forks_url;
}
public String getCommitsUrl() {
return commits_url;
}
/**
* ID of this gist, such as '12345'
*/
public String getId() {
return id;
}
/**
* URL like https://gist.github.com/gists/12345.git
*/
public String getGitPullUrl() {
return git_pull_url;
}
public String getGitPushUrl() {
return git_push_url;
}
public String getHtmlUrl() {
return html_url;
}
public boolean isPublic() {
return _public;
}
public Date getCreatedAt() {
return GitHub.parseDate(created_at);
}
public Date getUpdatedAt() {
return GitHub.parseDate(updated_at);
}
public String getDescription() {
return description;
}
public int getCommentCount() {
return comments;
}
/**
* API URL of listing comments.
*/
public String getCommentsUrl() {
return comments_url;
}
public GHGistFile getFile(String name) {
return files.get(name);
}
public Map<String,GHGistFile> getFiles() {
return Collections.unmodifiableMap(files);
}
/*package*/ GHGist wrapUp(GHUser owner) {
this.owner = owner;
this.root = owner.root;
wrapUp();
return this;
}
/**
* Used when caller obtains {@link GHGist} without knowing its owner.
* A partially constructed owner object is interned.
*/
/*package*/ GHGist wrapUp(GitHub root) throws IOException {
this.owner = root.getUser(owner);
this.root = root;
wrapUp();
return this;
}
private void wrapUp() {
for (Entry<String, GHGistFile> e : files.entrySet()) {
e.getValue().fileName = e.getKey();
}
}
String getApiTailUrl(String tail) {
return "/gists/" + id + '/' + tail;
}
public void star() throws IOException {
new Requester(root).method("PUT").to(getApiTailUrl("star"));
}
public void unstar() throws IOException {
new Requester(root).method("DELETE").to(getApiTailUrl("star"));
}
public boolean isStarred() throws IOException {
return root.retrieve().asHttpStatusCode(getApiTailUrl("star"))/100==2;
}
/**
* Forks this gist into your own.
*/
public GHGist fork() throws IOException {
return new Requester(root).to(getApiTailUrl("forks"),GHGist.class).wrapUp(root);
}
public PagedIterable<GHGist> listForks() {
return new PagedIterable<GHGist>() {
public PagedIterator<GHGist> iterator() {
return new PagedIterator<GHGist>(root.retrieve().asIterator(getApiTailUrl("forks"), GHGist[].class)) {
@Override
protected void wrapUp(GHGist[] page) {
try {
for (GHGist c : page)
c.wrapUp(root);
} catch (IOException e) {
throw new Error(e);
}
}
};
}
};
}
/**
* Deletes this gist.
*/
public void delete() throws IOException {
new Requester(root).method("DELETE").to("/gists/" + id);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
GHGist ghGist = (GHGist) o;
return id.equals(ghGist.id);
}
@Override
public int hashCode() {
return id.hashCode();
}
}

View File

@@ -0,0 +1,48 @@
package org.kohsuke.github;
import java.io.IOException;
import java.util.Collections;
import java.util.LinkedHashMap;
/**
* Builder pattern for creating a new Gist.
*
* @author Kohsuke Kawaguchi
* @see GitHub#createGist()
*/
public class GHGistBuilder {
private final GitHub root;
private final Requester req;
private final LinkedHashMap<String,Object> files = new LinkedHashMap<String, Object>();
public GHGistBuilder(GitHub root) {
this.root = root;
req = new Requester(root);
}
public GHGistBuilder description(String desc) {
req.with("description",desc);
return this;
}
public GHGistBuilder public_(boolean v) {
req.with("public",v);
return this;
}
/**
* Adds a new file.
*/
public GHGistBuilder file(String fileName, String content) {
files.put(fileName, Collections.singletonMap("content", content));
return this;
}
/**
* Creates a Gist based on the parameters specified thus far.
*/
public GHGist create() throws IOException {
req._with("files",files);
return req.to("/gists",GHGist.class).wrapUp(root);
}
}

View File

@@ -0,0 +1,60 @@
package org.kohsuke.github;
/**
* A file inside {@link GHGist}
*
* @author Kohsuke Kawaguchi
* @see GHGist#getFile(String)
* @see GHGist#getFiles()
*/
public class GHGistFile {
/*package almost final*/ String fileName;
private int size;
private String raw_url, type, language, content;
private boolean truncated;
public String getFileName() {
return fileName;
}
/**
* File size in bytes.
*/
public int getSize() {
return size;
}
/**
* URL that serves this file as-is.
*/
public String getRawUrl() {
return raw_url;
}
/**
* Content type of this Gist, such as "text/plain"
*/
public String getType() {
return type;
}
public String getLanguage() {
return language;
}
/**
* Content of this file.
*/
public String getContent() {
return content;
}
/**
* (?) indicates if {@link #getContent()} contains a truncated content.
*/
public boolean isTruncated() {
return truncated;
}
}

View File

@@ -10,7 +10,7 @@ import java.util.Map;
/**
* @author Kohsuke Kawaguchi
*/
public final class GHHook {
public class GHHook {
/**
* Repository that the hook belongs to.
*/
@@ -34,7 +34,7 @@ public final class GHHook {
public EnumSet<GHEvent> getEvents() {
EnumSet<GHEvent> s = EnumSet.noneOf(GHEvent.class);
for (String e : events)
Enum.valueOf(GHEvent.class,e.toUpperCase(Locale.ENGLISH));
s.add(Enum.valueOf(GHEvent.class,e.toUpperCase(Locale.ENGLISH)));
return s;
}

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,7 +156,7 @@ public class GHIssue {
* Updates the issue by adding a comment.
*/
public void comment(String message) throws IOException {
new Requester(root).with("body",message).to(getApiRoute() + "/comments");
new Requester(root).with("body",message).to(getIssuesApiRoute() + "/comments");
}
private void edit(String key, Object value) throws IOException {
@@ -172,7 +190,7 @@ public class GHIssue {
}
public void setLabels(String... labels) throws IOException {
edit("assignee",labels);
edit("labels",labels);
}
/**
@@ -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;
}
@@ -250,4 +272,4 @@ public class GHIssue {
return GitHub.parseURL(html_url);
}
}
}
}

View File

@@ -10,9 +10,9 @@ import org.apache.commons.lang.builder.ToStringBuilder;
public class GHKey {
/*package almost final*/ GitHub root;
private String url, key, title;
private boolean verified;
private int id;
protected String url, key, title;
protected boolean verified;
protected int id;
public int getId() {
return id;

View File

@@ -1,5 +1,6 @@
package org.kohsuke.github;
import java.io.IOException;
import java.util.Date;
import java.util.Locale;
@@ -65,6 +66,21 @@ public class GHMilestone {
return Enum.valueOf(GHMilestoneState.class, state.toUpperCase(Locale.ENGLISH));
}
/**
* Closes this issue.
*/
public void close() throws IOException {
edit("state", "closed");
}
private void edit(String key, Object value) throws IOException {
new Requester(root)._with(key, value).method("PATCH").to(getApiRoute());
}
protected String getApiRoute() {
return "/repos/"+owner.getOwnerName()+"/"+owner.getName()+"/milestones/"+number;
}
public GHMilestone wrap(GHRepository repo) {
this.owner = repo;
this.root = repo.root;

View File

@@ -1,9 +1,15 @@
package org.kohsuke.github;
import java.io.IOException;
import java.util.ArrayList;
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.
@@ -11,31 +17,126 @@ import java.util.List;
* @author Kohsuke Kawaguchi
*/
public class GHMyself extends GHUser {
/**
* @deprecated
* Use {@link #getEmails2()}
*/
public List<String> getEmails() throws IOException {
List<GHEmail> src = getEmails2();
List<String> r = new ArrayList<String>(src.size());
for (GHEmail e : src) {
r.add(e.getEmail());
}
return r;
}
/**
* Returns the read-only list of e-mail addresses configured for you.
*
* This corresponds to the stuff you configure in https://github.com/settings/emails,
* and not to be confused with {@link #getEmail()} that shows your public e-mail address
* set in https://github.com/settings/profile
*
*
* @return
* Always non-null.
*/
public List<String> getEmails() throws IOException {
String[] addresses = root.retrieve().to("/user/emails", String[].class);
public List<GHEmail> getEmails2() throws IOException {
GHEmail[] addresses = root.retrieve().to("/user/emails", GHEmail[].class);
return Collections.unmodifiableList(Arrays.asList(addresses));
}
/**
* Returns the read-only list of all the pulic keys of the current user.
*
* NOTE: When using OAuth authenticaiton, the READ/WRITE User scope is
* required by the GitHub APIs, otherwise you will get a 404 NOT FOUND.
*
* @return
* Always non-null.
*/
public List<GHKey> getPublicKeys() throws IOException {
return Collections.unmodifiableList(Arrays.asList(root.retrieve().to("/user/keys", GHKey[].class)));
}
/**
* Returns the read-only list of all the public verified keys of the current user.
*
* Differently from the getPublicKeys() method, the retrieval of the user's
* verified public keys does not require any READ/WRITE OAuth Scope to the
* user's profile.
*
* @return
* Always non-null.
*/
public List<GHVerifiedKey> getPublicVerifiedKeys() throws IOException {
return Collections.unmodifiableList(Arrays.asList(root.retrieve().to(
"/users/" + getLogin() + "/keys", GHVerifiedKey[].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.
* Repositories are returned by GitHub API with a 30 items per page.
*/
@Override
public PagedIterable<GHRepository> listRepositories() {
return listRepositories(30);
}
/**
* Lists up all the repositories this user owns (public and private) 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("/user/repos?per_page=" + pageSize, GHRepository[].class)) {
@Override
protected void wrapUp(GHRepository[] page) {
for (GHRepository c : page)
c.wrap(root);
}
};
}
};
}
/**
* @deprecated
* Use {@link #listRepositories()}
*/
public PagedIterable<GHRepository> listAllRepositories() {
return listRepositories();
}
// public void addEmails(Collection<String> emails) throws IOException {
//// new Requester(root,ApiVersion.V3).withCredential().to("/user/emails");
// root.retrieveWithAuth3()

View File

@@ -1,7 +1,6 @@
package org.kohsuke.github;
import java.io.IOException;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -24,10 +23,15 @@ public class GHOrganization extends GHPerson {
* Newly created repository.
*/
public GHRepository createRepository(String name, String description, String homepage, String team, boolean isPublic) throws IOException {
return createRepository(name,description,homepage,getTeams().get(team),isPublic);
GHTeam t = getTeams().get(team);
if (t==null)
throw new IllegalArgumentException("No such team: "+team);
return createRepository(name,description,homepage,t,isPublic);
}
public GHRepository createRepository(String name, String description, String homepage, GHTeam team, boolean isPublic) throws IOException {
if (team==null)
throw new IllegalArgumentException("Invalid team");
// such API doesn't exist, so fall back to HTML scraping
return new Requester(root)
.with("name", name).with("description", description).with("homepage", homepage)
@@ -38,14 +42,41 @@ public class GHOrganization extends GHPerson {
* Teams by their names.
*/
public Map<String,GHTeam> getTeams() throws IOException {
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));
for (GHTeam t : listTeams()) {
r.put(t.getName(),t);
}
return r;
}
/**
* List up all the teams.
*/
public PagedIterable<GHTeam> listTeams() throws IOException {
return new PagedIterable<GHTeam>() {
public PagedIterator<GHTeam> iterator() {
return new PagedIterator<GHTeam>(root.retrieve().asIterator(String.format("/orgs/%s/teams", login), GHTeam[].class)) {
@Override
protected void wrapUp(GHTeam[] page) {
for (GHTeam c : page)
c.wrapUp(GHOrganization.this);
}
};
}
};
}
/**
* Finds a team that has the given name in its {@link GHTeam#getName()}
*/
public GHTeam getTeamByName(String name) throws IOException {
for (GHTeam t : listTeams()) {
if(t.getName().equals(name))
return t;
}
return null;
}
/**
* Checks if this organization has the specified user as a member.
*/
@@ -58,6 +89,14 @@ public class GHOrganization extends GHPerson {
}
}
/**
* Remove a member of the organisation - which will remove them from
* all teams, and remove their access to the organizations repositories.
*/
public void remove(GHUser user) throws IOException {
root.retrieve().method("DELETE").to("/orgs/" + login + "/members/" + user.getLogin());
}
/**
* Checks if this organization has the specified user as a public member.
*/
@@ -78,26 +117,44 @@ public class GHOrganization extends GHPerson {
}
/**
* All the members of this organization.
* @deprecated use {@link #listMembers()}
*/
public List<GHUser> getMembers() throws IOException {
return new AbstractList<GHUser>() {
// these are shallow objects with only some limited values filled out
// TODO: it's better to allow objects to fill themselves in later when missing values are requested
final GHUser[] shallow = root.retrieve().to("/orgs/" + login + "/members", GHUser[].class);
return listMembers().asList();
}
@Override
public GHUser get(int index) {
try {
return root.getUser(shallow[index].getLogin());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* All the members of this organization.
*/
public PagedIterable<GHUser> listMembers() throws IOException {
return listMembers("members");
}
@Override
public int size() {
return shallow.length;
/**
* All the public members of this organization.
*/
public PagedIterable<GHUser> listPublicMembers() throws IOException {
return listMembers("public_members");
}
private PagedIterable<GHUser> listMembers(String suffix) throws IOException {
return listMembers(suffix, null);
}
public PagedIterable<GHUser> listMembersWithFilter(String filter) throws IOException {
return listMembers("members", filter);
}
private PagedIterable<GHUser> listMembers(final String suffix, final String filter) throws IOException {
return new PagedIterable<GHUser>() {
public PagedIterator<GHUser> iterator() {
String filterParams = (filter == null) ? "" : ("?filter=" + filter);
return new PagedIterator<GHUser>(root.retrieve().asIterator(String.format("/orgs/%s/%s%s", login, suffix, filterParams), GHUser[].class)) {
@Override
protected void wrapUp(GHUser[] users) {
GHUser.wrap(users, root);
}
};
}
};
}
@@ -130,10 +187,13 @@ 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 {
List<GHRepository> r = new ArrayList<GHRepository>();
for (GHRepository repository : root.retrieve().to("/orgs/" + login + "/repos", GHRepository[].class)) {
for (GHRepository repository : listRepositories()) {
repository.wrap(root);
List<GHPullRequest> pullRequests = repository.getPullRequests(GHIssueState.OPEN);
if (pullRequests.size() > 0) {
@@ -153,4 +213,43 @@ public class GHOrganization extends GHPerson {
}
return all;
}
/**
* Lists events performed by a user (this includes private events if the caller is authenticated.
*/
public PagedIterable<GHEventInfo> listEvents() throws IOException {
return new PagedIterable<GHEventInfo>() {
public PagedIterator<GHEventInfo> iterator() {
return new PagedIterator<GHEventInfo>(root.retrieve().asIterator(String.format("/orgs/%s/events", login), GHEventInfo[].class)) {
@Override
protected void wrapUp(GHEventInfo[] page) {
for (GHEventInfo c : page)
c.wrapUp(root);
}
};
}
};
}
/**
* 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.
*/
@Override
public PagedIterable<GHRepository> listRepositories(final int pageSize) {
return new PagedIterable<GHRepository>() {
public PagedIterator<GHRepository> iterator() {
return new PagedIterator<GHRepository>(root.retrieve().asIterator("/orgs/" + login + "/repos?per_page=" + pageSize, GHRepository[].class)) {
@Override
protected void wrapUp(GHRepository[] page) {
for (GHRepository c : page)
c.wrap(root);
}
};
}
};
}
}

View File

@@ -43,17 +43,50 @@ public abstract class GHPerson {
}
/**
* Gets the repositories this user owns.
* Gets the public repositories this user owns.
*
* <p>
* To list your own repositories, including private repositories,
* use {@link GHMyself#listRepositories()}
*/
public synchronized Map<String,GHRepository> getRepositories() throws IOException {
Map<String,GHRepository> repositories = new TreeMap<String, GHRepository>();
for (List<GHRepository> batch : iterateRepositories(100)) {
for (GHRepository r : batch)
repositories.put(r.getName(),r);
for (GHRepository r : listRepositories()) {
repositories.put(r.getName(),r);
}
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 +96,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>>() {
@@ -102,6 +138,11 @@ public abstract class GHPerson {
}
}
/**
* Lists events for an organization or an user.
*/
public abstract PagedIterable<GHEventInfo> listEvents() throws IOException;
/**
* Gravatar ID of this user, like 0cb9832a01c22c083390f3c5dcb64105
*
@@ -168,6 +209,10 @@ public abstract class GHPerson {
return blog;
}
public String getHtmlUrl() {
return html_url;
}
/**
* Gets the e-mail address of the user.
*/
@@ -202,5 +247,4 @@ public abstract class GHPerson {
populate();
return followers;
}
}

View File

@@ -9,7 +9,7 @@ import java.util.HashSet;
*
* @author Kohsuke Kawaguchi
*/
public final class GHPersonSet<T extends GHPerson> extends HashSet<T> {
public class GHPersonSet<T extends GHPerson> extends HashSet<T> {
public GHPersonSet() {
}

View File

@@ -27,11 +27,13 @@ import java.io.IOException;
import java.net.URL;
import java.util.Collection;
import java.util.Date;
import java.util.Locale;
/**
* A pull request.
*
* @author Kohsuke Kawaguchi
* @see GHRepository#getPullRequest(int)
*/
@SuppressWarnings({"UnusedDeclaration"})
public class GHPullRequest extends GHIssue {
@@ -63,7 +65,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 +120,7 @@ public class GHPullRequest extends GHIssue {
}
@Override
public Collection<String> getLabels() {
public Collection<Label> getLabels() {
return super.getLabels();
}
@@ -181,4 +188,34 @@ 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()),
GHPullRequestCommitDetail[].class)) {
@Override
protected void wrapUp(GHPullRequestCommitDetail[] page) {
}
};
}
};
}
/**
* Merge this pull request.
*
* The equivalent of the big green "Merge pull request" button.
*
* @param msg
* Commit message. If null, the default one will be used.
*/
public void merge(String msg) throws IOException {
new Requester(root).method("PUT").with("commit_message",msg).to(getApiRoute()+"/merge");
}
}

View File

@@ -0,0 +1,129 @@
/*
* 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 com.infradna.tool.bridge_method_injector.WithBridgeMethods;
import java.net.URL;
/**
* Commit detail inside a {@link GHPullRequest}.
*
* @author Luca Milanesio
*/
public class GHPullRequestCommitDetail {
/**
* @deprecated Use {@link GitUser}
*/
public static class Authorship extends GitUser {}
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;
@WithBridgeMethods(value=Authorship.class,castRequired=true)
public GitUser getAuthor() {
return author;
}
@WithBridgeMethods(value=Authorship.class,castRequired=true)
public GitUser 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

@@ -1,5 +1,6 @@
package org.kohsuke.github;
import java.io.IOException;
import java.net.URL;
/**
@@ -8,6 +9,7 @@ import java.net.URL;
* @author Michael Clarke
*/
public class GHRef {
/*package almost final*/ GitHub root;
private String ref, url;
private GHObject object;
@@ -33,6 +35,48 @@ public class GHRef {
return object;
}
/**
* Updates this ref to the specified commit.
*
* @param sha
* The SHA1 value to set this reference to
*/
public void updateTo(String sha) throws IOException {
updateTo(sha, false);
}
/**
* Updates this ref to the specified commit.
*
* @param sha
* The SHA1 value to set this reference to
* @param force
* Whether or not to force this ref update.
*/
public void updateTo(String sha, Boolean force) throws IOException {
new Requester(root)
.with("sha", sha).with("force", force).method("PATCH").to(url, GHRef.class).wrap(root);
}
/**
* Deletes this ref from the repository using the GitHub API.
*/
public void delete() throws IOException {
new Requester(root).method("DELETE").to(url);
}
/*package*/ GHRef wrap(GitHub root) {
this.root = root;
return this;
}
/*package*/ static GHRef[] wrap(GHRef[] in, GitHub root) {
for (GHRef r : in) {
r.wrap(root);
}
return in;
}
public static class GHObject {
private String type, sha, url;

View File

@@ -0,0 +1,224 @@
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;
private String tarball_url;
private String zipball_url;
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;
}
public String getZipballUrl() {
return zipball_url;
}
public void setZipballUrl(String zipballUrl) {
this.zipball_url = zipballUrl;
}
public String getTarballUrl() {
return tarball_url;
}
public void setTarballUrl(String tarballUrl) {
this.tarball_url = tarballUrl;
}
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(getApiTailUrl("assets"), GHAsset[].class);
return Arrays.asList(GHAsset.wrap(assets, this));
}
/**
* Deletes this release.
*/
public void delete() throws IOException {
new Requester(root).method("DELETE").to(owner.getApiTailUrl("releases/"+id));
}
private String getApiTailUrl(String end) {
return owner.getApiTailUrl(format("releases/%s/%s",id,end));
}
}

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,8 +23,11 @@
*/
package org.kohsuke.github;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
import java.io.FileNotFoundException;
import javax.xml.bind.DatatypeConverter;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.URL;
@@ -54,16 +57,19 @@ import static java.util.Arrays.*;
public class GHRepository {
/*package almost final*/ GitHub root;
private String description, homepage, name;
private String description, homepage, name, full_name;
private String url; // this is the API url
private String html_url; // this is the UI
private String git_url, ssh_url, clone_url, svn_url;
private GHUser owner; // not fully populated. beware.
private boolean has_issues, has_wiki, fork, _private, has_downloads;
private int watchers,forks,open_issues,size;
private boolean has_issues, has_wiki, fork, has_downloads;
@JsonProperty("private")
private boolean _private;
private int watchers,forks,open_issues,size,network_count,subscribers_count;
private String created_at, pushed_at;
private Map<Integer,GHMilestone> milestones = new HashMap<Integer, GHMilestone>();
private String master_branch,language;
private String default_branch,language;
private Map<String,GHCommit> commits = new HashMap<String, GHCommit>();
private GHRepoPermission permissions;
@@ -93,7 +99,7 @@ public class GHRepository {
* This URL is read-only.
*/
public String getGitTransportUrl() {
return "git://github.com/"+getOwnerName()+"/"+name+".git";
return git_url;
}
/**
@@ -101,7 +107,21 @@ public class GHRepository {
* This URL is read-only.
*/
public String gitHttpTransportUrl() {
return "https://github.com/"+getOwnerName()+"/"+name+".git";
return clone_url;
}
/**
* Gets the Subversion URL to access this repository: https://github.com/rails/rails
*/
public String getSvnUrl() {
return svn_url;
}
/**
* Gets the SSH URL to access this repository, such as git@github.com:rails/rails.git
*/
public String getSshUrl() {
return ssh_url;
}
/**
@@ -111,6 +131,13 @@ public class GHRepository {
return name;
}
/**
* Full repository name including the owner or organization. For example 'jenkinsci/jenkins' in case of http://github.com/jenkinsci/jenkins
*/
public String getFullName() {
return full_name;
}
public boolean hasPullAccess() {
return permissions!=null && permissions.pull;
}
@@ -143,7 +170,86 @@ public class GHRepository {
}
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));
return listIssues(state).asList();
}
public List<GHIssue> getIssues(GHIssueState state, GHMilestone milestone) throws IOException {
return Arrays.asList(GHIssue.wrap(root.retrieve()
.to(String.format("/repos/%s/%s/issues?state=%s&milestone=%s", owner.login, name,
state.toString().toLowerCase(), milestone == null ? "none" : "" + milestone.getNumber()),
GHIssue[].class
), this));
}
/**
* Lists up all the issues in this repository.
*/
public PagedIterable<GHIssue> listIssues(final GHIssueState state) {
return new PagedIterable<GHIssue>() {
public PagedIterator<GHIssue> iterator() {
return new PagedIterator<GHIssue>(root.retrieve().asIterator(getApiTailUrl("issues?state="+state.toString().toLowerCase(Locale.ENGLISH)), GHIssue[].class)) {
@Override
protected void wrapUp(GHIssue[] page) {
for (GHIssue c : page)
c.wrap(GHRepository.this);
}
};
}
};
}
public GHReleaseBuilder createRelease(String tag) {
return new GHReleaseBuilder(this,tag);
}
/**
* Creates a named ref, such as tag, branch, etc.
*
* @param name
* The name of the fully qualified reference (ie: refs/heads/master).
* If it doesn't start with 'refs' and have at least two slashes, it will be rejected.
* @param sha
* The SHA1 value to set this reference to
*/
public GHRef createRef(String name, String sha) throws IOException {
return new Requester(root)
.with("ref", name).with("sha", sha).method("POST").to(getApiTailUrl("git/refs"), GHRef.class).wrap(root);
}
/**
* @deprecated
* use {@link #listReleases()}
*/
public List<GHRelease> getReleases() throws IOException {
return listReleases().asList();
}
public PagedIterable<GHRelease> listReleases() throws IOException {
return new PagedIterable<GHRelease>() {
public PagedIterator<GHRelease> iterator() {
return new PagedIterator<GHRelease>(root.retrieve().asIterator(getApiTailUrl("releases"), GHRelease[].class)) {
@Override
protected void wrapUp(GHRelease[] page) {
for (GHRelease c : page)
c.wrap(GHRepository.this);
}
};
}
};
}
public PagedIterable<GHTag> listTags() throws IOException {
return new PagedIterable<GHTag>() {
public PagedIterator<GHTag> iterator() {
return new PagedIterator<GHTag>(root.retrieve().asIterator(getApiTailUrl("tags"), GHTag[].class)) {
@Override
protected void wrapUp(GHTag[] page) {
for (GHTag c : page)
c.wrap(GHRepository.this);
}
};
}
};
}
protected String getOwnerName() {
@@ -182,6 +288,14 @@ public class GHRepository {
return open_issues;
}
public int getNetworkCount() {
return network_count;
}
public int getSubscribersCount() {
return subscribers_count;
}
/**
*
* @return
@@ -202,7 +316,7 @@ public class GHRepository {
* This field is null until the user explicitly configures the master branch.
*/
public String getMasterBranch() {
return master_branch;
return default_branch;
}
public int getSize() {
@@ -215,7 +329,32 @@ public class GHRepository {
*/
@WithBridgeMethods(Set.class)
public GHPersonSet<GHUser> getCollaborators() throws IOException {
return new GHPersonSet<GHUser>(GHUser.wrap(root.retrieve().to("/repos/" + owner.login + "/" + name + "/collaborators", GHUser[].class),root));
return new GHPersonSet<GHUser>(listCollaborators().asList());
}
/**
* Lists up the collaborators on this repository.
*
* @return Users
* @throws IOException
*/
public PagedIterable<GHUser> listCollaborators() throws IOException {
return new PagedIterable<GHUser>() {
public PagedIterator<GHUser> iterator() {
return new PagedIterator<GHUser>(root.retrieve().asIterator("/repos/" + owner.login + "/" + name + "/collaborators", GHUser[].class)) {
@Override
protected void wrapUp(GHUser[] users) {
for (GHUser user : users) {
user.wrapUp(root);
}
}
};
}
};
}
/**
@@ -310,7 +449,11 @@ public class GHRepository {
* Deletes this repository.
*/
public void delete() throws IOException {
new Requester(root).method("DELETE").to("/repos/" + owner.login + "/" + name);
try {
new Requester(root).method("DELETE").to("/repos/" + owner.login + "/" + name);
} catch (FileNotFoundException x) {
throw (FileNotFoundException) new FileNotFoundException("Failed to delete " + owner.login + "/" + name + "; might not exist, or you might need the delete_repo scope in your token: http://stackoverflow.com/a/19327004/12916").initCause(x);
}
}
/**
@@ -378,6 +521,29 @@ public class GHRepository {
};
}
/**
* Creates a new pull request.
*
* @param title
* Required. The title of the pull request.
* @param head
* Required. The name of the branch where your changes are implemented.
* For cross-repository pull requests in the same network,
* namespace head with a user like this: username:branch.
* @param base
* Required. The name of the branch you want your changes pulled into.
* This should be an existing branch on the current repository.
* @param body
* The contents of the pull request. This is the markdown description
* of a pull request.
*/
public GHPullRequest createPullRequest(String title, String head, String base, String body) throws IOException {
return new Requester(root).with("title",title)
.with("head",head)
.with("base",base)
.with("body",body).to(getApiTailUrl("pulls"),GHPullRequest.class).wrapUp(this);
}
/**
* Retrieves the currently configured hooks.
*/
@@ -407,7 +573,7 @@ public class GHRepository {
}
public GHCompare getCompare(GHCommit id1, GHCommit id2) throws IOException {
return getCompare(id1.getSHA1(),id2.getSHA1());
return getCompare(id1.getSHA1(), id2.getSHA1());
}
public GHCompare getCompare(GHBranch id1, GHBranch id2) throws IOException {
@@ -420,19 +586,31 @@ public class GHRepository {
* @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);
return GHRef.wrap(root.retrieve().to(String.format("/repos/%s/%s/git/refs", owner.login, name), GHRef[].class),root);
}
/**
* Retrienved all refs of the given type for the current GitHub repository.
* Retrieves 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);
return GHRef.wrap(root.retrieve().to(String.format("/repos/%s/%s/git/refs/%s", owner.login, name, refType), GHRef[].class),root);
}
/**
* Retrive a ref of the given type for the current GitHub repository.
*
* @param refName
* eg: heads/branch
* @return refs matching the request type
* @throws IOException
* on failure communicating with GitHub, potentially due to an
* invalid ref type being requested
*/
public GHRef getRef(String refName) throws IOException {
return root.retrieve().to(String.format("/repos/%s/%s/git/refs/%s", owner.login, name, refName), GHRef.class).wrap(root);
}
/**
* Gets a commit object in this repository.
*/
@@ -461,6 +639,13 @@ public class GHRepository {
};
}
/**
* Search commits by specifying filters through a builder pattern.
*/
public GHCommitQueryBuilder queryCommits() {
return new GHCommitQueryBuilder(this);
}
/**
* Lists up all the commit comments in this repository.
*/
@@ -510,15 +695,41 @@ public class GHRepository {
* Optional parameter that points to the URL that has more details.
* @param description
* Optional short description.
* @param context
* Optinal commit status context.
*/
public GHCommitStatus createCommitStatus(String sha1, GHCommitState state, String targetUrl, String description) throws IOException {
public GHCommitStatus createCommitStatus(String sha1, GHCommitState state, String targetUrl, String description, String context) throws IOException {
return new Requester(root)
.with("state", state.name().toLowerCase(Locale.ENGLISH))
.with("target_url", targetUrl)
.with("description", description)
.with("context", context)
.to(String.format("/repos/%s/%s/statuses/%s",owner.login,this.name,sha1),GHCommitStatus.class).wrapUp(root);
}
/**
* @see {@link #createCommitStatus(String, GHCommitState,String,String,String) createCommitStatus}
*/
public GHCommitStatus createCommitStatus(String sha1, GHCommitState state, String targetUrl, String description) throws IOException {
return createCommitStatus(sha1, state, targetUrl, description,null);
}
/**
* Lists repository events.
*/
public PagedIterable<GHEventInfo> listEvents() throws IOException {
return new PagedIterable<GHEventInfo>() {
public PagedIterator<GHEventInfo> iterator() {
return new PagedIterator<GHEventInfo>(root.retrieve().asIterator(String.format("/repos/%s/%s/events", owner.login, name), GHEventInfo[].class)) {
@Override
protected void wrapUp(GHEventInfo[] page) {
for (GHEventInfo c : page)
c.wrapUp(root);
}
};
}
};
}
/**
*
@@ -691,11 +902,86 @@ public class GHRepository {
}
return m;
}
public GHContent getFileContent(String path) throws IOException {
return getFileContent(path, null);
}
public GHContent getFileContent(String path, String ref) throws IOException {
Requester requester = root.retrieve();
String target = String.format("/repos/%s/%s/contents/%s", owner.login, name, path);
if (ref != null)
target = target + "?ref=" + ref;
return requester.to(target, GHContent.class).wrap(this);
}
public List<GHContent> getDirectoryContent(String path) throws IOException {
return getDirectoryContent(path, null);
}
public List<GHContent> getDirectoryContent(String path, String ref) throws IOException {
Requester requester = root.retrieve();
String target = String.format("/repos/%s/%s/contents/%s", owner.login, name, path);
if (ref != null)
target = target + "?ref=" + ref;
GHContent[] files = requester.to(target, GHContent[].class);
GHContent.wrap(files, this);
return Arrays.asList(files);
}
public GHContent getReadme() throws Exception {
return getFileContent("readme");
}
public GHContentUpdateResponse createContent(String content, String commitMessage, String path) throws IOException {
return createContent(content, commitMessage, path, null);
}
public GHContentUpdateResponse createContent(String content, String commitMessage, String path, String branch) throws IOException {
Requester requester = new Requester(root)
.with("path", path)
.with("message", commitMessage)
.with("content", DatatypeConverter.printBase64Binary(content.getBytes()))
.method("PUT");
if (branch != null) {
requester.with("branch", branch);
}
GHContentUpdateResponse response = requester.to(getApiTailUrl("contents/" + path), GHContentUpdateResponse.class);
response.getContent().wrap(this);
response.getCommit().wrapUp(this);
return response;
}
public GHMilestone createMilestone(String title, String description) throws IOException {
return new Requester(root)
.with("title", title).with("description", description).method("POST").to(getApiTailUrl("milestones"), GHMilestone.class).wrap(this);
}
public GHDeployKey addDeployKey(String title,String key) throws IOException {
return new Requester(root)
.with("title", title).with("key", key).method("POST").to(getApiTailUrl("keys"), GHDeployKey.class).wrap(this);
}
public List<GHDeployKey> getDeployKeys() throws IOException{
List<GHDeployKey> list = new ArrayList<GHDeployKey>(Arrays.asList(
root.retrieve().to(String.format("/repos/%s/%s/keys", owner.login, name), GHDeployKey[].class)));
for (GHDeployKey h : list)
h.wrap(this);
return list;
}
@Override
public String toString() {

View File

@@ -0,0 +1,36 @@
package org.kohsuke.github;
/**
* Represents a tag in {@link GHRepository}
*
* @see GHRepository#listTags()
*/
public class GHTag {
private GHRepository owner;
private GitHub root;
private String name;
private GHCommit commit;
/*package*/ GHTag wrap(GHRepository owner) {
this.owner = owner;
this.root = owner.root;
return this;
}
public GHRepository getOwner() {
return owner;
}
public GitHub getRoot() {
return root;
}
public String getName() {
return name;
}
public GHCommit getCommit() {
return commit;
}
}

View File

@@ -15,6 +15,7 @@ import java.util.TreeMap;
public class GHTeam {
private String name,permission;
private int id;
private GHOrganization organization; // populated by GET /user/teams where Teams+Orgs are returned together
protected /*final*/ GHOrganization org;
@@ -23,6 +24,11 @@ public class GHTeam {
return this;
}
/*package*/ GHTeam wrapUp(GitHub root) { // auto-wrapUp when organization is known from GET /user/teams
this.organization.wrapUp(root);
return wrapUp(organization);
}
/*package*/ static GHTeam[] wrapUp(GHTeam[] teams, GHOrganization owner) {
for (GHTeam t : teams) {
t.wrapUp(owner);
@@ -49,6 +55,18 @@ public class GHTeam {
return new HashSet<GHUser>(Arrays.asList(GHUser.wrap(org.root.retrieve().to(api("/members"), GHUser[].class), org.root)));
}
/**
* Checks if this team has the specified user as a member.
*/
public boolean hasMember(GHUser user) {
try {
org.root.retrieve().to("/teams/" + id + "/members/" + user.getLogin());
return true;
} catch (IOException ignore) {
return false;
}
}
public Map<String,GHRepository> getRepositories() throws IOException {
GHRepository[] repos = org.root.retrieve().to(api("/repos"), GHRepository[].class);
Map<String,GHRepository> m = new TreeMap<String, GHRepository>();
@@ -83,4 +101,8 @@ public class GHTeam {
private String api(String tail) {
return "/teams/"+id+tail;
}
public GHOrganization getOrganization() {
return org;
}
}

View File

@@ -76,6 +76,13 @@ public class GHUser extends GHPerson {
return org.hasMember(this);
}
/**
* Returns true if this user belongs to the specified team.
*/
public boolean isMemberOf(GHTeam team) {
return team.hasMember(this);
}
/**
* Returns true if this user belongs to the specified organization as a public member.
*/
@@ -103,6 +110,40 @@ public class GHUser extends GHPerson {
return orgs;
}
/**
* Lists events performed by a user (this includes private events if the caller is authenticated.
*/
public PagedIterable<GHEventInfo> listEvents() throws IOException {
return new PagedIterable<GHEventInfo>() {
public PagedIterator<GHEventInfo> iterator() {
return new PagedIterator<GHEventInfo>(root.retrieve().asIterator(String.format("/users/%s/events", login), GHEventInfo[].class)) {
@Override
protected void wrapUp(GHEventInfo[] page) {
for (GHEventInfo c : page)
c.wrapUp(root);
}
};
}
};
}
/**
* Lists Gists created by this user.
*/
public PagedIterable<GHGist> listGists() throws IOException {
return new PagedIterable<GHGist>() {
public PagedIterator<GHGist> iterator() {
return new PagedIterator<GHGist>(root.retrieve().asIterator(String.format("/users/%s/gists", login), GHGist[].class)) {
@Override
protected void wrapUp(GHGist[] page) {
for (GHGist c : page)
c.wrapUp(GHUser.this);
}
};
}
};
}
@Override
public String toString() {
return "User:"+login;

View File

@@ -0,0 +1,13 @@
package org.kohsuke.github;
public class GHVerifiedKey extends GHKey {
public GHVerifiedKey() {
this.verified = true;
}
@Override
public String getTitle() {
return (title == null ? "key-" + id : title);
}
}

View File

@@ -23,15 +23,12 @@
*/
package org.kohsuke.github;
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
import org.apache.commons.codec.binary.Base64;
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 static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.ANY;
import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.NONE;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.Reader;
import java.net.MalformedURLException;
@@ -49,7 +46,13 @@ import java.util.Properties;
import java.util.Set;
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.
@@ -59,52 +62,79 @@ 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 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 apiUrl;
private GitHub(String login, String apiToken, String password) {
this (GITHUB_URL, login, apiToken, password);
}
private final String apiUrl;
private HttpConnector connector = HttpConnector.DEFAULT;
/**
* Connects to GitHub.com
*/
private GitHub(String login, String oauthAccessToken, String password) throws IOException {
this (GITHUB_URL, login, oauthAccessToken, password);
}
/**
* 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 apiToken, String password) {
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;
this.login = login;
this.apiToken = apiToken;
if (apiToken!=null || password!=null) {
String authorization = password==null ? (login + "/token" + ":" + apiToken) : (login + ':'+password);
encodedAuthorization = new String(Base64.encodeBase64(authorization.getBytes()));
} else
encodedAuthorization = null;
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;
}
private GitHub (String apiUrl, String oauthAccessToken) throws IOException {
if (apiUrl.endsWith("/")) apiUrl = apiUrl.substring(0, apiUrl.length()-1); // normalize
this.apiUrl = apiUrl;
this.encodedAuthorization = null;
this.oauthAccessToken = oauthAccessToken;
this.apiToken = oauthAccessToken;
this.login = getMyself().getLogin();
}
/**
* Obtains the credential from "~/.github"
*/
@@ -117,11 +147,7 @@ public class GitHub {
} finally {
IOUtils.closeQuietly(in);
}
String oauth = props.getProperty("oauth");
if (oauth!=null)
return new GitHub(GITHUB_URL,oauth);
else
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"));
}
/**
@@ -132,45 +158,72 @@ public class GitHub {
* "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 login, String apiToken) {
return new GitHub(apiUrl,login,apiToken,null);
public static GitHub connectToEnterprise(String apiUrl, String oauthAccessToken) throws IOException {
return connectUsingOAuth(apiUrl, oauthAccessToken);
}
public static GitHub connect(String login, String apiToken){
return new GitHub(login,apiToken,null);
public static GitHub connectToEnterprise(String apiUrl, String login, String password) throws IOException {
return new GitHub(apiUrl, login, null, password);
}
public static GitHub connect(String login, String apiToken, String password){
return new GitHub(login,apiToken,password);
public static GitHub connect(String login, String oauthAccessToken) throws IOException {
return new GitHub(login,oauthAccessToken,null);
}
public static GitHub connectUsingOAuth (String accessToken) throws IOException {
return connectUsingOAuth("github.com", 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 connectUsingOAuth (String githubServer, String accessToken) throws IOException {
return new GitHub(githubServer, accessToken);
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;
}
public HttpConnector getConnector() {
return connector;
}
/**
* Sets the custom connector used to make requests to GitHub.
*/
public void setConnector(HttpConnector connector) {
this.connector = connector;
}
/*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("/")) {
if ("github.com".equals(apiUrl)) {// backward compatibility
return new URL(GITHUB_URL + tailApiUrl);
@@ -190,7 +243,16 @@ public class GitHub {
* Gets the current rate limit.
*/
public GHRateLimit getRateLimit() throws IOException {
return retrieve().to("/rate_limit", JsonRateLimit.class).rate;
try {
return retrieve().to("/rate_limit", JsonRateLimit.class).rate;
} catch (FileNotFoundException e) {
// GitHub Enterprise doesn't have the rate limit, so in that case
// return some big number that's not too big.
// see issue #78
GHRateLimit r = new GHRateLimit();
r.limit = r.remaining = 1000000;
return r;
}
}
/**
@@ -221,6 +283,15 @@ public class GitHub {
return u;
}
/**
* clears all cached data in order for external changes (modifications and del
*/
public void refreshCache() {
users.clear();
orgs.clear();
}
/**
* Interns the given {@link GHUser}.
*/
@@ -269,17 +340,49 @@ public class GitHub {
return r;
}
/**
* Gets complete map of organizations/teams that current user belongs to.
*
* Leverages the new GitHub API /user/teams made available recently to
* get in a single call the complete set of organizations, teams and permissions
* in a single call.
*/
public Map<String, Set<GHTeam>> getMyTeams() throws IOException {
Map<String, Set<GHTeam>> allMyTeams = new HashMap<String, Set<GHTeam>>();
for (GHTeam team : retrieve().to("/user/teams", GHTeam[].class)) {
team.wrapUp(this);
String orgLogin = team.getOrganization().getLogin();
Set<GHTeam> teamsPerOrg = allMyTeams.get(orgLogin);
if (teamsPerOrg == null) {
teamsPerOrg = new HashSet<GHTeam>();
}
teamsPerOrg.add(team);
allMyTeams.put(orgLogin, teamsPerOrg);
}
return allMyTeams;
}
/**
* Public events visible to you. Equivalent of what's displayed on https://github.com/
*/
public List<GHEventInfo> getEvents() throws IOException {
// TODO: pagination
GHEventInfo[] events = retrieve().to("/events", GHEventInfo[].class);
for (GHEventInfo e : events)
e.wrapUp(this);
return Arrays.asList(events);
}
/**
* Gets a sigle gist by ID.
*/
public GHGist getGist(String id) throws IOException {
return retrieve().to("/gists/"+id,GHGist.class).wrapUp(this);
}
public GHGistBuilder createGist() {
return new GHGistBuilder(this);
}
/**
* Parses the GitHub event object.
*
@@ -292,10 +395,13 @@ public class GitHub {
t.wrapUp(this);
return t;
}
/**
* Creates a new repository.
*
* To create a repository in an organization, see
* {@link GHOrganization#createRepository(String, String, String, GHTeam, boolean)}
*
* @return
* Newly created repository.
*/
@@ -311,7 +417,7 @@ public class GitHub {
*
* The token created can be then used for {@link GitHub#connectUsingOAuth(String)} in the future.
*
* @see http://developer.github.com/v3/oauth/#create-a-new-authorization
* @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)
@@ -356,13 +462,17 @@ public class GitHub {
throw new IllegalStateException("Unable to parse the timestamp: "+timestamp);
}
/*package*/ static String printDate(Date dt) {
return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").format(dt);
}
/*package*/ static final ObjectMapper MAPPER = new ObjectMapper();
private static final String[] TIME_FORMATS = {"yyyy/MM/dd HH:mm:ss ZZZZ","yyyy-MM-dd'T'HH:mm:ss'Z'"};
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

@@ -0,0 +1,38 @@
package org.kohsuke.github;
import java.util.Date;
/**
* Represents a user in Git who authors/commits a commit.
*
* In contrast, {@link GHUser} is an user of GitHub. Because Git allows a person to
* use multiple e-mail addresses and names when creating a commit, there's generally
* no meaningful mapping between {@link GHUser} and {@link GitUser}.
*
* @author Kohsuke Kawaguchi
*/
public class GitUser {
private String name, email, date;
/**
* Human readable name of the user, such as "Kohsuke Kawaguchi"
*/
public String getName() {
return name;
}
/**
* E-mail address, such as "foo@example.com"
*/
public String getEmail() {
return email;
}
/**
* This field doesn't appear to be consistently available in all the situations where this class
* is used.
*/
public Date getDate() {
return GitHub.parseDate(date);
}
}

View File

@@ -0,0 +1,29 @@
package org.kohsuke.github;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
/**
* Pluggability for customizing HTTP request behaviors or using altogether different library.
*
* <p>
* For example, you can implement this to st custom timeouts.
*
* @author Kohsuke Kawaguchi
*/
public interface HttpConnector {
/**
* Opens a connection to the given URL.
*/
HttpURLConnection connect(URL url) throws IOException;
/**
* Default implementation that uses {@link URL#openConnection()}.
*/
HttpConnector DEFAULT = new HttpConnector() {
public HttpURLConnection connect(URL url) throws IOException {
return (HttpURLConnection) url.openConnection();
}
};
}

View File

@@ -23,7 +23,7 @@
*/
package org.kohsuke.github;
import org.apache.commons.io.IOUtils;
import static org.kohsuke.github.GitHub.MAPPER;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -31,21 +31,30 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.InterruptedIOException;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.net.URLEncoder;
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.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.GZIPInputStream;
import static org.kohsuke.github.GitHub.*;
import org.apache.commons.io.IOUtils;
import javax.net.ssl.HttpsURLConnection;
/**
* A builder pattern for making HTTP call and parsing its output.
@@ -60,6 +69,8 @@ class Requester {
* Request method.
*/
private String method = "POST";
private String contentType = "application/x-www-form-urlencoded";
private InputStream body;
private static class Entry {
String key;
@@ -111,6 +122,11 @@ class Requester {
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));
@@ -123,6 +139,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);
}
@@ -158,21 +179,73 @@ class Requester {
while (true) {// loop while API rate limit is hit
HttpURLConnection uc = setupConnection(root.getApiURL(tailApiUrl));
if (!method.equals("GET")) {
uc.setDoOutput(true);
uc.setRequestProperty("Content-type","application/x-www-form-urlencoded");
buildRequest(uc);
try {
T result = parse(uc, type, instance);
if (type != null && type.isArray()) { // we might have to loop for pagination - done through recursion
final String links = uc.getHeaderField("link");
if (links != null && links.contains("rel=\"next\"")) {
Pattern nextLinkPattern = Pattern.compile(".*<(.*)>; rel=\"next\"");
Matcher nextLinkMatcher = nextLinkPattern.matcher(links);
if (nextLinkMatcher.find()) {
final String link = nextLinkMatcher.group(1);
T nextResult = _to(link, type, instance);
final int resultLength = Array.getLength(result);
final int nextResultLength = Array.getLength(nextResult);
T concatResult = (T) Array.newInstance(type.getComponentType(), resultLength + nextResultLength);
System.arraycopy(result, 0, concatResult, 0, resultLength);
System.arraycopy(nextResult, 0, concatResult, resultLength, nextResultLength);
result = concatResult;
}
}
}
return result;
} catch (IOException e) {
handleApiError(e,uc);
}
}
}
/**
* Makes a request and just obtains the HTTP status code.
*/
public int asHttpStatusCode(String tailApiUrl) throws IOException {
while (true) {// loop while API rate limit is hit
HttpURLConnection uc = setupConnection(root.getApiURL(tailApiUrl));
buildRequest(uc);
try {
return uc.getResponseCode();
} catch (IOException e) {
handleApiError(e,uc);
}
}
}
private void buildRequest(HttpURLConnection uc) throws IOException {
if (!method.equals("GET")) {
uc.setDoOutput(true);
uc.setRequestProperty("Content-type", contentType);
if (body == null) {
Map json = new HashMap();
for (Entry e : args) {
json.put(e.key, e.value);
}
MAPPER.writeValue(uc.getOutputStream(),json);
}
try {
return parse(uc,type,instance);
} catch (IOException e) {
handleApiError(e,uc);
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();
}
}
}
}
@@ -182,9 +255,23 @@ class Requester {
*
* Every iterator call reports a new batch.
*/
/*package*/ <T> Iterator<T> asIterator(final String tailApiUrl, final Class<T> type) {
/*package*/ <T> Iterator<T> asIterator(String _tailApiUrl, final Class<T> type) {
method("GET");
if (!args.isEmpty()) throw new IllegalStateException();
if (!args.isEmpty()) {
boolean first=true;
try {
for (Entry a : args) {
_tailApiUrl += first ? '?' : '&';
first = false;
_tailApiUrl += URLEncoder.encode(a.key,"UTF-8")+'='+URLEncoder.encode(a.value.toString(),"UTF-8");
}
} catch (UnsupportedEncodingException e) {
throw new AssertionError(e); // UTF-8 is mandatory
}
}
final String tailApiUrl = _tailApiUrl;
return new Iterator<T>() {
/**
@@ -267,13 +354,12 @@ class Requester {
private HttpURLConnection setupConnection(URL url) throws IOException {
HttpURLConnection uc = (HttpURLConnection) url.openConnection();
HttpURLConnection uc = root.getConnector().connect(url);
// 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 (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);
@@ -345,4 +431,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

@@ -0,0 +1,32 @@
package org.kohsuke.github.extras;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.OkUrlFactory;
import org.kohsuke.github.HttpConnector;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
/**
* {@link HttpConnector} for {@link OkHttpClient}.
*
* Unlike {@link #DEFAULT}, OkHttp does response caching.
* Making a conditional request against GitHubAPI and receiving a 304
* response does not count against the rate limit.
* See http://developer.github.com/v3/#conditional-requests
*
* @author Roberto Tyley
* @author Kohsuke Kawaguchi
*/
public class OkHttpConnector implements HttpConnector {
private final OkUrlFactory urlFactory;
public OkHttpConnector(OkUrlFactory urlFactory) {
this.urlFactory = urlFactory;
}
public HttpURLConnection connect(URL url) throws IOException {
return urlFactory.open(url);
}
}

View File

@@ -7,6 +7,8 @@ operations that act on them as defined as methods (such as `GHUser.follow()`), a
are used in favor of using string handle (such as `GHUser.isMemberOf(GHOrganization)` instead of
`GHUser.isMemberOf(String)`)
The library supports both github.com and GitHub Enterprise.
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.
@@ -34,3 +36,10 @@ Alternatively, you can have just the OAuth token in this file:
oauth=4d98173f7c075527cb64878561d1fe70
OkHttp
----
This library comes with a pluggable connector to use different HTTP client implementations
through `HttpConnector`. In particular, this means you can use [OkHttp](http://square.github.io/okhttp/),
so we can make use of it's HTTP response cache.
Making a conditional request against the GitHub API and receiving a 304 response
[does not count against the rate limit](http://developer.github.com/v3/#conditional-requests).

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

@@ -0,0 +1,23 @@
import org.kohsuke.github.GHOrganization;
import org.kohsuke.github.GHTeam;
import org.kohsuke.github.GitHub;
import java.util.Arrays;
import java.util.Map;
/**
* @author Kohsuke Kawaguchi
*/
public class Foo {
public static void main(String[] args) throws Exception {
GHOrganization org = GitHub.connect().getOrganization("jenkinsci");
Map<String, GHTeam> teams = org.getTeams();
System.out.println(teams.size());
int sz = 0;
for (GHTeam t : org.listTeams()) {
sz++;
}
System.out.println(sz);
}
}

View File

@@ -1,354 +0,0 @@
package org.kohsuke;
import junit.framework.TestCase;
import org.kohsuke.github.GHCommit;
import org.kohsuke.github.GHCommit.File;
import org.kohsuke.github.GHCommitComment;
import org.kohsuke.github.GHCommitStatus;
import org.kohsuke.github.GHEvent;
import org.kohsuke.github.GHEventInfo;
import org.kohsuke.github.GHEventPayload;
import org.kohsuke.github.GHHook;
import org.kohsuke.github.GHBranch;
import org.kohsuke.github.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;
import org.kohsuke.github.GHPullRequest;
import org.kohsuke.github.GHRepository;
import org.kohsuke.github.GHTeam;
import org.kohsuke.github.GHUser;
import org.kohsuke.github.GitHub;
import org.kohsuke.github.PagedIterable;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Map;
import java.util.Set;
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 {
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");
gitHub.getMyself().getRepository("github-api-test2").delete();
}
public void testCredentialValid() throws IOException {
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.getRateLimit());
}
public void testMyOrganizations() throws IOException {
Map<String, GHOrganization> org = gitHub.getMyOrganizations();
assertFalse(org.keySet().contains(null));
System.out.println(org);
}
public void testFetchPullRequest() throws Exception {
GHRepository r = gitHub.getOrganization("jenkinsci").getRepository("jenkins");
assertEquals("master",r.getMasterBranch());
r.getPullRequest(1);
r.getPullRequests(GHIssueState.OPEN);
}
public void testFetchPullRequestAsList() throws Exception {
GHRepository r = gitHub.getOrganization("symfony").getRepository("symfony-docs");
assertEquals("master", r.getMasterBranch());
PagedIterable<GHPullRequest> i = r.listPullRequests(GHIssueState.CLOSED);
List<GHPullRequest> prs = i.asList();
assertNotNull(prs);
assertTrue(prs.size() > 0);
}
public void testRepoPermissions() throws Exception {
GHRepository r = gitHub.getOrganization("jenkinsci").getRepository("jenkins");
assertTrue(r.hasPullAccess());
r = gitHub.getOrganization("github").getRepository("tire");
assertFalse(r.hasAdminAccess());
}
public void testGetMyself() throws Exception {
GHMyself me = gitHub.getMyself();
System.out.println(me);
GHUser u = gitHub.getUser("kohsuke2");
System.out.println(u);
for (List<GHRepository> lst : me.iterateRepositories(100)) {
for (GHRepository r : lst) {
System.out.println(r.getPushedAt());
}
}
}
public void testPublicKeys() throws Exception {
List<GHKey> keys = gitHub.getMyself().getPublicKeys();
System.out.println(keys);
}
public void tryOrgFork() throws Exception {
gitHub.getUser("kohsuke").getRepository("rubywm").forkTo(gitHub.getOrganization("jenkinsci"));
}
public void tryGetTeamsForRepo() throws Exception {
Set<GHTeam> o = gitHub.getOrganization("jenkinsci").getRepository("rubywm").getTeams();
System.out.println(o);
}
public void testMembership() throws Exception {
Set<String> members = gitHub.getOrganization("jenkinsci").getRepository("violations-plugin").getCollaboratorNames();
System.out.println(members.contains("kohsuke"));
}
public void testMemberOrgs() throws Exception {
Set<GHOrganization> o = gitHub.getUser("kohsuke").getOrganizations();
System.out.println(o);
}
public void testCommit() throws Exception {
GHCommit commit = gitHub.getUser("jenkinsci").getRepository("jenkins").getCommit("08c1c9970af4d609ae754fbe803e06186e3206f7");
System.out.println(commit);
assertEquals(1, commit.getParents().size());
assertEquals(1,commit.getFiles().size());
File f = commit.getFiles().get(0);
assertEquals(48,f.getLinesChanged());
assertEquals("modified",f.getStatus());
assertEquals("changelog.html", f.getFileName());
}
public void testListCommits() throws Exception {
List<String> sha1 = new ArrayList<String>();
for (GHCommit c : gitHub.getUser("kohsuke").getRepository("empty-commit").listCommits()) {
System.out.println(c.getSHA1());
sha1.add(c.getSHA1());
}
assertEquals("fdfad6be4db6f96faea1f153fb447b479a7a9cb7",sha1.get(0));
assertEquals(1,sha1.size());
}
public void testBranches() throws Exception {
Map<String,GHBranch> b =
gitHub.getUser("jenkinsci").getRepository("jenkins").getBranches();
System.out.println(b);
}
public void testCommitComment() throws Exception {
GHRepository r = gitHub.getUser("jenkinsci").getRepository("jenkins");
PagedIterable<GHCommitComment> comments = r.listCommitComments();
List<GHCommitComment> batch = comments.iterator().nextPage();
for (GHCommitComment comment : batch) {
System.out.println(comment.getBody());
assertSame(comment.getOwner(), r);
}
}
public void testCreateCommitComment() throws Exception {
GHCommit commit = gitHub.getUser("kohsuke").getRepository("sandbox-ant").getCommit("8ae38db0ea5837313ab5f39d43a6f73de3bd9000");
GHCommitComment c = commit.createComment("[testing](http://kohsuse.org/)");
System.out.println(c);
c.update("updated text");
System.out.println(c);
c.delete();
}
public void tryHook() throws Exception {
GHRepository r = gitHub.getMyself().getRepository("test2");
GHHook hook = r.createWebHook(new URL("http://www.google.com/"));
System.out.println(hook);
for (GHHook h : r.getHooks())
h.delete();
}
public void testEventApi() throws Exception {
for (GHEventInfo ev : gitHub.getEvents()) {
System.out.println(ev);
if (ev.getType()==GHEvent.PULL_REQUEST) {
GHEventPayload.PullRequest pr = ev.getPayload(GHEventPayload.PullRequest.class);
System.out.println(pr.getNumber());
System.out.println(pr.getPullRequest());
}
}
}
public void testApp() throws IOException {
System.out.println(gitHub.getMyself().getEmails());
// GHRepository r = gitHub.getOrganization("jenkinsci").createRepository("kktest4", "Kohsuke's test", "http://kohsuke.org/", "Everyone", true);
// r.fork();
// tryDisablingIssueTrackers(gitHub);
// tryDisablingWiki(gitHub);
// GHPullRequest i = gitHub.getOrganization("jenkinsci").getRepository("sandbox").getPullRequest(1);
// for (GHIssueComment c : i.getComments())
// System.out.println(c);
// System.out.println(i);
// gitHub.getMyself().getRepository("perforce-plugin").setEmailServiceHook("kk@kohsuke.org");
// tryRenaming(gitHub);
// tryOrgFork(gitHub);
// testOrganization(gitHub);
// testPostCommitHook(gitHub);
// tryTeamCreation(gitHub);
// t.add(gitHub.getMyself());
// System.out.println(t.getMembers());
// t.remove(gitHub.getMyself());
// System.out.println(t.getMembers());
// GHRepository r = gitHub.getOrganization("HudsonLabs").createRepository("auto-test", "some description", "http://kohsuke.org/", "Plugin Developers", true);
// r.
// GitHub hub = GitHub.connectAnonymously();
//// hub.createRepository("test","test repository",null,true);
//// hub.getUser("kohsuke").getRepository("test").delete();
//
// System.out.println(hub.getUser("kohsuke").getRepository("hudson").getCollaborators());
}
private void tryDisablingIssueTrackers(GitHub gitHub) throws IOException {
for (GHRepository r : gitHub.getOrganization("jenkinsci").getRepositories().values()) {
if (r.hasIssues()) {
if (r.getOpenIssueCount()==0) {
System.out.println("DISABLED "+r.getName());
r.enableIssueTracker(false);
} else {
System.out.println("UNTOUCHED "+r.getName());
}
}
}
}
private void tryDisablingWiki(GitHub gitHub) throws IOException {
for (GHRepository r : gitHub.getOrganization("jenkinsci").getRepositories().values()) {
if (r.hasWiki()) {
System.out.println("DISABLED "+r.getName());
r.enableWiki(false);
}
}
}
private void tryUpdatingIssueTracker(GitHub gitHub) throws IOException {
GHRepository r = gitHub.getOrganization("jenkinsci").getRepository("lib-task-reactor");
System.out.println(r.hasIssues());
System.out.println(r.getOpenIssueCount());
r.enableIssueTracker(false);
}
private void tryRenaming(GitHub gitHub) throws IOException {
gitHub.getUser("kohsuke").getRepository("test").renameTo("test2");
}
private void tryTeamCreation(GitHub gitHub) throws IOException {
GHOrganization o = gitHub.getOrganization("HudsonLabs");
GHTeam t = o.createTeam("auto team", Permission.PUSH);
t.add(o.getRepository("auto-test"));
}
private void testPostCommitHook(GitHub gitHub) throws IOException {
GHRepository r = gitHub.getMyself().getRepository("foo");
Set<URL> hooks = r.getPostCommitHooks();
hooks.add(new URL("http://kohsuke.org/test"));
System.out.println(hooks);
hooks.remove(new URL("http://kohsuke.org/test"));
System.out.println(hooks);
}
public void testOrgRepositories() throws IOException {
GHOrganization j = gitHub.getOrganization("jenkinsci");
long start = System.currentTimeMillis();
Map<String, GHRepository> repos = j.getRepositories();
long end = System.currentTimeMillis();
System.out.printf("%d repositories in %dms\n",repos.size(),end-start);
}
public void testOrganization() throws IOException {
GHOrganization j = gitHub.getOrganization("jenkinsci");
GHTeam t = j.getTeams().get("Core Developers");
assertNotNull(j.getRepository("jenkins"));
// t.add(labs.getRepository("xyz"));
}
public void testCommitStatus() throws Exception {
GHRepository r = gitHub.getUser("kohsuke").getRepository("test");
GHCommitStatus state;
// state = r.createCommitStatus("edacdd76b06c5f3f0697a22ca75803169f25f296", GHCommitState.FAILURE, "http://jenkins-ci.org/", "oops!");
List<GHCommitStatus> lst = r.listCommitStatuses("edacdd76b06c5f3f0697a22ca75803169f25f296").asList();
state = lst.get(0);
System.out.println(state);
assertEquals("oops!",state.getDescription());
assertEquals("http://jenkins-ci.org/",state.getTargetUrl());
}
public void testPullRequestPopulate() throws Exception {
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,36 @@
package org.kohsuke.github;
import org.apache.commons.io.IOUtils;
import org.junit.Assert;
import org.junit.Before;
import org.kohsuke.randname.RandomNameGenerator;
import java.io.FileInputStream;
import java.util.Properties;
/**
* @author Kohsuke Kawaguchi
*/
public abstract class AbstractGitHubApiTestBase extends Assert {
protected GitHub gitHub;
@Before
public void setUp() throws Exception {
Properties props = new Properties();
java.io.File f = new java.io.File(System.getProperty("user.home"), ".github.kohsuke2");
if (f.exists()) {
FileInputStream in = new FileInputStream(f);
try {
props.load(in);
gitHub = GitHub.connect(props.getProperty("login"),props.getProperty("oauth"));
} finally {
IOUtils.closeQuietly(in);
}
} else {
gitHub = GitHub.connect();
}
}
protected static final RandomNameGenerator rnd = new RandomNameGenerator();
}

View File

@@ -0,0 +1,607 @@
package org.kohsuke.github;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
import org.junit.Assume;
import org.junit.Test;
import org.kohsuke.github.GHCommit.File;
import org.kohsuke.github.GHOrganization.Permission;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
/**
* Unit test for simple App.
*/
public class AppTest extends AbstractGitHubApiTestBase {
private String getTestRepositoryName() throws IOException {
return getUser().getLogin() + "/github-api-test";
}
@Test
public void testRepoCRUD() throws Exception {
String targetName = "github-api-test-rename2";
deleteRepository("github-api-test-rename");
deleteRepository(targetName);
GHRepository r = gitHub.createRepository("github-api-test-rename", "a test repository", "http://github-api.kohsuke.org/", true);
r.enableIssueTracker(false);
r.enableDownloads(false);
r.enableWiki(false);
r.renameTo(targetName);
getUser().getRepository(targetName).delete();
}
private void deleteRepository(final String name) throws IOException {
GHRepository repository = getUser().getRepository(name);
if(repository != null) {
repository.delete();
}
}
@Test
public void testCredentialValid() throws IOException {
assertTrue(gitHub.isCredentialValid());
GitHub connect = GitHub.connect("totally", "bogus");
assertFalse(connect.isCredentialValid());
}
@Test
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);
}
@Test
public void testCreateIssue() throws IOException {
GHUser u = getUser();
GHRepository repository = getTestRepository();
GHMilestone milestone = repository.createMilestone(System.currentTimeMillis() + "", "Test Milestone");
GHIssue o = repository.createIssue("testing")
.body("this is body")
.assignee(u)
.label("bug")
.label("question")
.milestone(milestone)
.create();
assertNotNull(o);
o.close();
}
@Test
public void testGetIssues() throws Exception {
List<GHIssue> closedIssues = gitHub.getUser("kohsuke").getRepository("github-api").getIssues(GHIssueState.CLOSED);
// prior to using PagedIterable GHRepository.getIssues(GHIssueState) would only retrieve 30 issues
assertTrue(closedIssues.size() > 30);
}
private GHRepository getTestRepository() throws IOException {
GHRepository repository;
try {
repository = gitHub.getRepository(getTestRepositoryName());
} catch (IOException e) {
repository = gitHub.createRepository("github-api-test", "A test repository for testing" +
"the github-api project", "http://github-api.kohsuke.org/", true);
try {
Thread.sleep(2000);
} catch (InterruptedException e1) {
throw new RuntimeException(e.getMessage(), e);
}
repository.enableIssueTracker(true);
repository.enableDownloads(true);
repository.enableWiki(true);
}
return repository;
}
private GHUser getUser() {
try {
return gitHub.getMyself();
} catch (IOException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
@Test
public void testListIssues() throws IOException {
GHUser u = getUser();
GHRepository repository = getTestRepository();
GHMilestone milestone = repository.createMilestone(System.currentTimeMillis() + "", "Test Milestone");
milestone.close();
GHIssue unhomed = null;
GHIssue homed = null;
try {
unhomed = repository.createIssue("testing").body("this is body")
.assignee(u)
.label("bug")
.label("question")
.create();
assertEquals(unhomed.getNumber(), repository.getIssues(GHIssueState.OPEN, null).get(0).getNumber());
homed = repository.createIssue("testing").body("this is body")
.assignee(u)
.label("bug")
.label("question")
.milestone(milestone)
.create();
assertEquals(homed.getNumber(), repository.getIssues(GHIssueState.OPEN, milestone).get(0).getNumber());
} finally {
if (unhomed != null) {
unhomed.close();
}
if (homed != null) {
homed.close();
}
}
}
@Test
public void testRateLimit() throws IOException {
System.out.println(gitHub.getRateLimit());
}
@Test
public void testMyOrganizations() throws IOException {
Map<String, GHOrganization> org = gitHub.getMyOrganizations();
assertFalse(org.keySet().contains(null));
System.out.println(org);
}
@Test
public void testMyTeamsContainsAllMyOrganizations() throws IOException {
Map<String, Set<GHTeam>> teams = gitHub.getMyTeams();
Map<String, GHOrganization> myOrganizations = gitHub.getMyOrganizations();
assertEquals(teams.keySet(), myOrganizations.keySet());
}
@Test
public void testMyTeamsShouldIncludeMyself() throws IOException {
Map<String, Set<GHTeam>> teams = gitHub.getMyTeams();
for (Entry<String, Set<GHTeam>> teamsPerOrg : teams.entrySet()) {
String organizationName = teamsPerOrg.getKey();
for (GHTeam team : teamsPerOrg.getValue()) {
String teamName = team.getName();
assertTrue("Team " + teamName + " in organization " + organizationName
+ " does not contain myself",
shouldBelongToTeam(organizationName, teamName));
}
}
}
private boolean shouldBelongToTeam(String organizationName, String teamName) throws IOException {
GHOrganization org = gitHub.getOrganization(organizationName);
assertNotNull(org);
GHTeam team = org.getTeamByName(teamName);
assertNotNull(team);
return team.hasMember(gitHub.getMyself());
}
@Test
public void testFetchPullRequest() throws Exception {
GHRepository r = gitHub.getOrganization("jenkinsci").getRepository("jenkins");
assertEquals("master",r.getMasterBranch());
r.getPullRequest(1);
r.getPullRequests(GHIssueState.OPEN);
}
@Test
public void testFetchPullRequestAsList() throws Exception {
GHRepository r = gitHub.getRepository("kohsuke/github-api");
assertEquals("master", r.getMasterBranch());
PagedIterable<GHPullRequest> i = r.listPullRequests(GHIssueState.CLOSED);
List<GHPullRequest> prs = i.asList();
assertNotNull(prs);
assertTrue(prs.size() > 0);
}
@Test
public void testRepoPermissions() throws Exception {
kohsuke();
GHRepository r = gitHub.getOrganization("jenkinsci").getRepository("jenkins");
assertTrue(r.hasPullAccess());
r = gitHub.getOrganization("github").getRepository("hub");
assertFalse(r.hasAdminAccess());
}
@Test
public void testGetMyself() throws Exception {
GHMyself me = gitHub.getMyself();
assertNotNull(me);
assertNotNull(gitHub.getUser("kohsuke2"));
PagedIterable<GHRepository> ghRepositories = me.listRepositories();
assertTrue(ghRepositories.iterator().hasNext());
}
@Test
public void testPublicKeys() throws Exception {
List<GHKey> keys = gitHub.getMyself().getPublicKeys();
assertFalse(keys.isEmpty());
}
@Test
public void testOrgFork() throws Exception {
kohsuke();
gitHub.getRepository("kohsuke/rubywm").forkTo(gitHub.getOrganization("github-api-test-org"));
}
@Test
public void testGetTeamsForRepo() throws Exception {
kohsuke();
assertEquals(1,gitHub.getOrganization("github-api-test-org").getRepository("testGetTeamsForRepo").getTeams().size());
}
@Test
public void testMembership() throws Exception {
Set<String> members = gitHub.getOrganization("jenkinsci").getRepository("violations-plugin").getCollaboratorNames();
System.out.println(members.contains("kohsuke"));
}
@Test
public void testMemberOrgs() throws Exception {
Set<GHOrganization> o = gitHub.getUser("kohsuke").getOrganizations();
System.out.println(o);
}
@Test
public void testOrgTeams() throws Exception {
kohsuke();
int sz=0;
for (GHTeam t : gitHub.getOrganization("github-api-test-org").listTeams()) {
assertNotNull(t.getName());
sz++;
}
assertTrue(sz<100);
}
@Test
public void testOrgTeamByName() throws Exception {
kohsuke();
GHTeam e = gitHub.getOrganization("github-api-test-org").getTeamByName("Core Developers");
assertNotNull(e);
}
@Test
public void testCommit() throws Exception {
GHCommit commit = gitHub.getUser("jenkinsci").getRepository("jenkins").getCommit("08c1c9970af4d609ae754fbe803e06186e3206f7");
System.out.println(commit);
assertEquals(1, commit.getParents().size());
assertEquals(1,commit.getFiles().size());
File f = commit.getFiles().get(0);
assertEquals(48,f.getLinesChanged());
assertEquals("modified",f.getStatus());
assertEquals("changelog.html", f.getFileName());
}
@Test
public void testListCommits() throws Exception {
List<String> sha1 = new ArrayList<String>();
for (GHCommit c : gitHub.getUser("kohsuke").getRepository("empty-commit").listCommits()) {
System.out.println(c.getSHA1());
sha1.add(c.getSHA1());
}
assertEquals("fdfad6be4db6f96faea1f153fb447b479a7a9cb7", sha1.get(0));
assertEquals(1, sha1.size());
}
public void testQueryCommits() throws Exception {
List<String> sha1 = new ArrayList<String>();
for (GHCommit c : gitHub.getUser("jenkinsci").getRepository("jenkins").queryCommits()
.since(new Date(1199174400000L)).until(1201852800000L).path("pom.xml").list()) {
System.out.println(c.getSHA1());
sha1.add(c.getSHA1());
}
assertEquals("1cccddb22e305397151b2b7b87b4b47d74ca337b",sha1.get(0));
assertEquals(29,sha1.size());
}
@Test
public void testBranches() throws Exception {
Map<String,GHBranch> b =
gitHub.getUser("jenkinsci").getRepository("jenkins").getBranches();
System.out.println(b);
}
@Test
public void testCommitComment() throws Exception {
GHRepository r = gitHub.getUser("jenkinsci").getRepository("jenkins");
PagedIterable<GHCommitComment> comments = r.listCommitComments();
List<GHCommitComment> batch = comments.iterator().nextPage();
for (GHCommitComment comment : batch) {
System.out.println(comment.getBody());
assertSame(comment.getOwner(), r);
}
}
@Test
public void testCreateCommitComment() throws Exception {
GHCommit commit = gitHub.getUser("kohsuke").getRepository("sandbox-ant").getCommit("8ae38db0ea5837313ab5f39d43a6f73de3bd9000");
GHCommitComment c = commit.createComment("[testing](http://kohsuse.org/)");
System.out.println(c);
c.update("updated text");
System.out.println(c);
c.delete();
}
@Test
public void tryHook() throws Exception {
kohsuke();
GHRepository r = gitHub.getMyself().getRepository("test2");
GHHook hook = r.createWebHook(new URL("http://www.google.com/"));
System.out.println(hook);
for (GHHook h : r.getHooks())
h.delete();
}
@Test
public void testEventApi() throws Exception {
for (GHEventInfo ev : gitHub.getEvents()) {
System.out.println(ev);
if (ev.getType()==GHEvent.PULL_REQUEST) {
GHEventPayload.PullRequest pr = ev.getPayload(GHEventPayload.PullRequest.class);
System.out.println(pr.getNumber());
System.out.println(pr.getPullRequest());
}
}
}
@Test
public void testApp() throws IOException {
System.out.println(gitHub.getMyself().getEmails());
// GHRepository r = gitHub.getOrganization("jenkinsci").createRepository("kktest4", "Kohsuke's test", "http://kohsuke.org/", "Everyone", true);
// r.fork();
// tryDisablingIssueTrackers(gitHub);
// tryDisablingWiki(gitHub);
// GHPullRequest i = gitHub.getOrganization("jenkinsci").getRepository("sandbox").getPullRequest(1);
// for (GHIssueComment c : i.getComments())
// System.out.println(c);
// System.out.println(i);
// gitHub.getMyself().getRepository("perforce-plugin").setEmailServiceHook("kk@kohsuke.org");
// tryRenaming(gitHub);
// tryOrgFork(gitHub);
// testOrganization(gitHub);
// testPostCommitHook(gitHub);
// tryTeamCreation(gitHub);
// t.add(gitHub.getMyself());
// System.out.println(t.getMembers());
// t.remove(gitHub.getMyself());
// System.out.println(t.getMembers());
// GHRepository r = gitHub.getOrganization("HudsonLabs").createRepository("auto-test", "some description", "http://kohsuke.org/", "Plugin Developers", true);
// r.
// GitHub hub = GitHub.connectAnonymously();
//// hub.createRepository("test","test repository",null,true);
//// hub.getUser("kohsuke").getRepository("test").delete();
//
// System.out.println(hub.getUser("kohsuke").getRepository("hudson").getCollaborators());
}
private void tryDisablingIssueTrackers(GitHub gitHub) throws IOException {
for (GHRepository r : gitHub.getOrganization("jenkinsci").getRepositories().values()) {
if (r.hasIssues()) {
if (r.getOpenIssueCount()==0) {
System.out.println("DISABLED "+r.getName());
r.enableIssueTracker(false);
} else {
System.out.println("UNTOUCHED "+r.getName());
}
}
}
}
private void tryDisablingWiki(GitHub gitHub) throws IOException {
for (GHRepository r : gitHub.getOrganization("jenkinsci").getRepositories().values()) {
if (r.hasWiki()) {
System.out.println("DISABLED "+r.getName());
r.enableWiki(false);
}
}
}
private void tryUpdatingIssueTracker(GitHub gitHub) throws IOException {
GHRepository r = gitHub.getOrganization("jenkinsci").getRepository("lib-task-reactor");
System.out.println(r.hasIssues());
System.out.println(r.getOpenIssueCount());
r.enableIssueTracker(false);
}
private void tryRenaming(GitHub gitHub) throws IOException {
gitHub.getUser("kohsuke").getRepository("test").renameTo("test2");
}
private void tryTeamCreation(GitHub gitHub) throws IOException {
GHOrganization o = gitHub.getOrganization("HudsonLabs");
GHTeam t = o.createTeam("auto team", Permission.PUSH);
t.add(o.getRepository("auto-test"));
}
private void testPostCommitHook(GitHub gitHub) throws IOException {
GHRepository r = gitHub.getMyself().getRepository("foo");
Set<URL> hooks = r.getPostCommitHooks();
hooks.add(new URL("http://kohsuke.org/test"));
System.out.println(hooks);
hooks.remove(new URL("http://kohsuke.org/test"));
System.out.println(hooks);
}
@Test
public void testOrgRepositories() throws IOException {
kohsuke();
GHOrganization j = gitHub.getOrganization("jenkinsci");
long start = System.currentTimeMillis();
Map<String, GHRepository> repos = j.getRepositories();
long end = System.currentTimeMillis();
System.out.printf("%d repositories in %dms\n",repos.size(),end-start);
}
@Test
public void testOrganization() throws IOException {
kohsuke();
GHOrganization j = gitHub.getOrganization("github-api-test-org");
GHTeam t = j.getTeams().get("Core Developers");
assertNotNull(j.getRepository("jenkins"));
// t.add(labs.getRepository("xyz"));
}
@Test
public void testCommitStatus() throws Exception {
GHRepository r = gitHub.getRepository("kohsuke/github-api");
GHCommitStatus state;
// state = r.createCommitStatus("ecbfdd7315ef2cf04b2be7f11a072ce0bd00c396", GHCommitState.FAILURE, "http://kohsuke.org/", "testing!");
List<GHCommitStatus> lst = r.listCommitStatuses("ecbfdd7315ef2cf04b2be7f11a072ce0bd00c396").asList();
state = lst.get(0);
System.out.println(state);
assertEquals("testing!",state.getDescription());
assertEquals("http://kohsuke.org/",state.getTargetUrl());
}
@Test
public void testCommitShortInfo() throws Exception {
GHRepository r = gitHub.getRepository("kohsuke/github-api");
GHCommit commit = r.getCommit("86a2e245aa6d71d54923655066049d9e21a15f23");
assertEquals(commit.getCommitShortInfo().getAuthor().getName(), "Kohsuke Kawaguchi");
assertEquals(commit.getCommitShortInfo().getMessage(), "doc");
}
@Test
public void testPullRequestPopulate() throws Exception {
GHRepository r = gitHub.getUser("kohsuke").getRepository("github-api");
GHPullRequest p = r.getPullRequest(17);
GHUser u = p.getUser();
assertNotNull(u.getName());
}
@Test
public void testCheckMembership() throws Exception {
kohsuke();
GHOrganization j = gitHub.getOrganization("jenkinsci");
GHUser kohsuke = gitHub.getUser("kohsuke");
GHUser b = gitHub.getUser("b");
assertTrue(j.hasMember(kohsuke));
assertFalse(j.hasMember(b));
assertTrue(j.hasPublicMember(kohsuke));
assertFalse(j.hasPublicMember(b));
}
@Test
public void testCreateRelease() throws Exception {
kohsuke();
GHRepository r = gitHub.getRepository("kohsuke2/testCreateRelease");
String tagName = UUID.randomUUID().toString();
String releaseName = "release-" + tagName;
GHRelease rel = r.createRelease(tagName)
.name(releaseName)
.prerelease(false)
.create();
try {
for (GHTag tag : r.listTags()) {
if (tagName.equals(tag.getName())) {
String ash = tag.getCommit().getSHA1();
GHRef ref = r.createRef("refs/heads/"+releaseName, ash);
assertEquals(ref.getRef(),"refs/heads/"+releaseName);
for (Map.Entry<String, GHBranch> entry : r.getBranches().entrySet()) {
System.out.println(entry.getKey() + "/" + entry.getValue());
if (releaseName.equals(entry.getValue().getName())) {
return;
}
}
fail("branch not found");
}
}
fail("release creation failed! tag not found");
} finally {
rel.delete();
}
}
@Test
public void testRef() throws IOException {
GHRef masterRef = gitHub.getRepository("jenkinsci/jenkins").getRef("heads/master");
assertEquals("https://api.github.com/repos/jenkinsci/jenkins/git/refs/heads/master", masterRef.getUrl().toString());
}
@Test
public void directoryListing() throws IOException {
List<GHContent> children = gitHub.getRepository("jenkinsci/jenkins").getDirectoryContent("core");
for (GHContent c : children) {
System.out.println(c.getName());
if (c.isDirectory()) {
for (GHContent d : c.listDirectoryContent()) {
System.out.println(" "+d.getName());
}
}
}
}
@Test
public void testAddDeployKey() throws IOException {
GHRepository myRepository = Iterables.get(gitHub.getMyself().getRepositories().values(),0);
final GHDeployKey newDeployKey = myRepository.addDeployKey("test", "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDUt0RAycC5cS42JKh6SecfFZBR1RrF+2hYMctz4mk74/arBE+wFb7fnSHGzdGKX2h5CFOWODifRCJVhB7hlVxodxe+QkQQYAEL/x1WVCJnGgTGQGOrhOMj95V3UE5pQKhsKD608C+u5tSofcWXLToP1/wZ7U4/AHjqYi08OLsWToHCax55TZkvdt2jo0hbIoYU+XI9Q8Uv4ONDN1oabiOdgeKi8+crvHAuvNleiBhWVBzFh8KdfzaH5uNdw7ihhFjEd1vzqACsjCINCjdMfzl6jD9ExuWuE92nZJnucls2cEoNC6k2aPmrZDg9hA32FXVpyseY+bDUWFU6LO2LG6PB kohsuke@atlas");
try {
assertNotNull(newDeployKey.getId());
GHDeployKey k = Iterables.find(myRepository.getDeployKeys(), new Predicate<GHDeployKey>() {
public boolean apply(GHDeployKey deployKey) {
return newDeployKey.getId() == deployKey.getId();
}
});
assertNotNull(k);
} finally {
newDeployKey.delete();
}
}
@Test
public void testCommitStatusContext() throws IOException {
GHRepository myRepository = Iterables.get(gitHub.getMyself().getRepositories().values(), 0);
GHRef masterRef = myRepository.getRef("heads/master");
GHCommitStatus commitStatus = myRepository.createCommitStatus(masterRef.getObject().getSha(), GHCommitState.SUCCESS, "http://www.example.com", "test", "test/context");
assertEquals("test/context", commitStatus.getContext());
}
private void kohsuke() {
String login = getUser().getLogin();
Assume.assumeTrue(login.equals("kohsuke") || login.equals("kohsuke2"));
}
}

View File

@@ -0,0 +1,68 @@
package org.kohsuke.github;
import org.junit.Before;
import org.junit.Test;
import java.util.List;
/**
* Integration test for {@link GHContent}.
*/
public class GHContentIntegrationTest extends AbstractGitHubApiTestBase {
private GHRepository repo;
private String createdFilename = rnd.next();
@Before
@Override
public void setUp() throws Exception {
super.setUp();
repo = gitHub.getRepository("github-api-test-org/GHContentIntegrationTest").fork();
}
@Test
public void testGetFileContent() throws Exception {
GHContent content = repo.getFileContent("ghcontent-ro/a-file-with-content");
assertTrue(content.isFile());
assertEquals("thanks for reading me\n", content.getContent());
}
@Test
public void testGetEmptyFileContent() throws Exception {
GHContent content = repo.getFileContent("ghcontent-ro/an-empty-file");
assertTrue(content.isFile());
assertEquals("", content.getContent());
}
@Test
public void testGetDirectoryContent() throws Exception {
List<GHContent> entries = repo.getDirectoryContent("ghcontent-ro/a-dir-with-3-entries");
assertTrue(entries.size() == 3);
}
@Test
public void testCRUDContent() throws Exception {
GHContentUpdateResponse created = repo.createContent("this is an awesome file I created\n", "Creating a file for integration tests.", createdFilename);
GHContent createdContent = created.getContent();
assertNotNull(created.getCommit());
assertNotNull(created.getContent());
assertNotNull(createdContent.getContent());
assertEquals("this is an awesome file I created\n", createdContent.getContent());
GHContentUpdateResponse updatedContentResponse = createdContent.update("this is some new content\n", "Updated file for integration tests.");
GHContent updatedContent = updatedContentResponse.getContent();
assertNotNull(updatedContentResponse.getCommit());
assertNotNull(updatedContentResponse.getContent());
assertEquals("this is some new content\n", updatedContent.getContent());
GHContentUpdateResponse deleteResponse = updatedContent.delete("Enough of this foolishness!");
assertNotNull(deleteResponse.getCommit());
assertNull(deleteResponse.getContent());
}
}

View File

@@ -0,0 +1,73 @@
package org.kohsuke.github;
import org.junit.Test;
/**
* @author Kohsuke Kawaguchi
*/
public class GistTest extends AbstractGitHubApiTestBase {
/**
* CRUD operation.
*/
@Test
public void lifecycleTest() throws Exception {
GHGist gist = gitHub.createGist()
.public_(false)
.description("Test Gist")
.file("abc.txt","abc")
.file("def.txt","def")
.create();
assertNotNull(gist.getCreatedAt());
assertNotNull(gist.getUpdatedAt());
assertNotNull(gist.getCommentsUrl());
assertNotNull(gist.getCommitsUrl());
assertNotNull(gist.getGitPullUrl());
assertNotNull(gist.getGitPushUrl());
assertNotNull(gist.getHtmlUrl());
gist.delete();
}
@Test
public void starTest() throws Exception {
GHGist gist = gitHub.getGist("9903708");
assertEquals("rtyler",gist.getOwner().getLogin());
gist.star();
assertTrue(gist.isStarred());
gist.unstar();
assertFalse(gist.isStarred());
GHGist newGist = gist.fork();
try {
for (GHGist g : gist.listForks()) {
if (g.equals(newGist)) {
// expected to find it in the clone list
return;
}
}
fail("Expected to find a newly cloned gist");
} finally {
newGist.delete();
}
}
@Test
public void gistFile() throws Exception {
GHGist gist = gitHub.getGist("9903708");
assertTrue(gist.isPublic());
assertEquals(1,gist.getFiles().size());
GHGistFile f = gist.getFile("keybase.md");
assertEquals("text/plain", f.getType());
assertEquals("Markdown", f.getLanguage());
assertTrue(f.getContent().contains("### Keybase proof"));
assertNotNull(f.getContent());
}
}

View File

@@ -8,17 +8,17 @@ import junit.framework.TestCase;
public class GitHubTest extends TestCase {
public void testGitHubServerWithHttp() throws Exception {
GitHub hub = GitHub.connectToEnterprise("http://enterprise.kohsuke.org/api/v3", "kohsuke", "token");
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", "kohsuke", "token");
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.connect("kohsuke", "token", "password");
GitHub hub = GitHub.connectUsingPassword("kohsuke", "bogus");
assertEquals("https://api.github.com/test", hub.getApiURL("/test").toString());
}
}

View File

@@ -0,0 +1,135 @@
package org.kohsuke.github;
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.junit.Test;
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 AbstractGitHubApiTestBase {
@Test
public void testCreateRepository() throws IOException, GitAPIException, InterruptedException {
GHMyself myself = gitHub.getMyself();
GHOrganization org = gitHub.getOrganization("github-api-test-org");
GHRepository repository = org.getRepository("github-api-test");
if (repository != null) {
repository.delete();
Thread.sleep(1000);
}
repository = org.createRepository("github-api-test",
"a test repository used to test kohsuke's github-api", "http://github-api.kohsuke.org/", "Core Developers", true);
Thread.sleep(1000); // wait for the repository to become ready
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.getSshUrl())
.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,18 @@
package org.kohsuke.github;
import org.junit.Test;
/**
* @author Kohsuke Kawaguchi
*/
public class PullRequestTest extends AbstractGitHubApiTestBase {
@Test
public void createPullRequest() throws Exception {
GHRepository j = gitHub.getOrganization("github-api-test-org").getRepository("jenkins");
String name = rnd.next();
GHPullRequest p = j.createPullRequest(name, "stable", "master", "## test");
System.out.println(p.getUrl());
assertEquals(name, p.getTitle());
p.close();
}
}

View File

@@ -0,0 +1,91 @@
package org.kohsuke.github;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.util.Iterator;
import static org.mockito.Mockito.when;
/**
* @author Luciano P. Sabenca (luciano.sabenca [at] movile [com] | lucianosabenca [at] gmail [dot] com
*/
public class RepositoryTest {
@Mock
GitHub mockGitHub;
@Mock
Iterator<GHUser[]> iterator;
@Mock
GHRepository mockRepository;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
@Test
public void listCollaborators() throws Exception {
GHUser user1 = new GHUser();
user1.login = "login1";
GHUser user2 = new GHUser();
user2.login = "login2";
when(iterator.hasNext()).thenReturn(true, false, true);
when(iterator.next()).thenReturn(new GHUser[]{user1}, new GHUser[]{user2});
Requester requester = Mockito.mock(Requester.class);
when(mockGitHub.retrieve()).thenReturn(requester);
when(requester.asIterator("/repos/*/*/collaborators",
GHUser[].class)).thenReturn(iterator, iterator);
PagedIterable<GHUser> pagedIterable = Mockito.mock(PagedIterable.class);
when(mockRepository.listCollaborators()).thenReturn(pagedIterable);
PagedIterator<GHUser> userPagedIterator = new PagedIterator<GHUser>(iterator) {
@Override
protected void wrapUp(GHUser[] page) {
}
};
PagedIterator<GHUser> userPagedIterator2 = new PagedIterator<GHUser>(iterator) {
@Override
protected void wrapUp(GHUser[] page) {
}
};
when(pagedIterable.iterator()).thenReturn(userPagedIterator, userPagedIterator2);
Iterator<GHUser> returnIterator1 = mockRepository.listCollaborators().iterator();
Assert.assertTrue(returnIterator1.hasNext());
GHUser user = returnIterator1.next();
Assert.assertEquals(user, user1);
Assert.assertFalse(returnIterator1.hasNext());
Iterator returnIterator2 = mockRepository.listCollaborators().iterator();
Assert.assertTrue(returnIterator2.hasNext());
user = returnIterator1.next();
Assert.assertEquals(user, user2);
}
}