Compare commits

..

229 Commits

Author SHA1 Message Date
Kohsuke Kawaguchi
31d5cf6129 [maven-release-plugin] prepare release github-api-1.41 2013-04-23 10:30:48 -07:00
Kohsuke Kawaguchi
8c78d20e6e doc improvement 2013-04-23 10:25:57 -07:00
Kohsuke Kawaguchi
abe78cf0bb Formatting only changes 2013-04-23 10:20:07 -07:00
Kohsuke Kawaguchi
eeebfd5f04 added a little helper code that generates an OAuth token for ~/.github 2013-04-23 10:19:50 -07:00
Kohsuke Kawaguchi
5e3d3dd023 In fact there's no point in looking up the 'token' property since API token is no longer supported by GitHub 2013-04-23 10:05:45 -07:00
Kohsuke Kawaguchi
60175ebfad if the user name is available, pass that in and save a lookup 2013-04-23 10:05:06 -07:00
Kohsuke Kawaguchi
c6fafe453f Cleaned up the authentication part of the code 2013-04-23 10:04:20 -07:00
Kohsuke Kawaguchi
838ecd0dd8 Merge branch 'pull-33' 2013-04-23 09:40:37 -07:00
Kohsuke Kawaguchi
beec605e2c [FIXED issue #34] API route for pull requests uses 'pulls' 2013-04-23 09:28:00 -07:00
watsonian
da1405a060 Remove apiToken completely. 2013-04-20 11:58:52 -07:00
watsonian
e38eeae533 Update constructor to use OAuth tokens rather than API tokens. 2013-04-20 11:43:54 -07:00
watsonian
8442e7e326 Declare the exception type. 2013-04-19 17:10:29 -07:00
watsonian
e6c82e2003 Stop using deprecated API tokens for Enterprise.
Authentication by API token is deprecated and doesn't work anymore.
Instead, authentication should be done via OAuth token now.
2013-04-19 16:27:09 -07:00
Kohsuke Kawaguchi
c4de972c53 [maven-release-plugin] prepare for next development iteration 2013-04-16 12:30:34 -07:00
Kohsuke Kawaguchi
d2adbaec89 [maven-release-plugin] prepare release github-api-1.40 2013-04-16 12:30:28 -07:00
Kohsuke Kawaguchi
72736588c9 release botched because of the Nexus staging deployer. rolling back to the parent POM 4 2013-04-16 12:28:38 -07:00
Kohsuke Kawaguchi
46e726363a [maven-release-plugin] prepare for next development iteration 2013-04-16 12:22:26 -07:00
Kohsuke Kawaguchi
f7e5292b8c [maven-release-plugin] prepare release github-api-1.39 2013-04-16 12:22:20 -07:00
Kohsuke Kawaguchi
f67954fa3e Merge branch 'pull-32' 2013-04-16 12:19:33 -07:00
Kohsuke Kawaguchi
ba6c6a17ae doc improvement 2013-04-16 12:18:50 -07:00
Kohsuke Kawaguchi
c21d61a70d TAB -> WS 2013-04-16 12:17:30 -07:00
Kohsuke Kawaguchi
df857d3a84 doc improvement 2013-04-16 12:14:06 -07:00
Honza Brázdil
e9564f101b implement retrieving of access token 2013-04-14 05:50:11 +02:00
Honza Brázdil
e8a2a69649 Implement GHEventPayload.IssueComment 2013-04-13 17:09:03 +02:00
Kohsuke Kawaguchi
ba416b1294 [maven-release-plugin] prepare for next development iteration 2013-04-06 19:06:44 -07:00
Kohsuke Kawaguchi
3024b598ad [maven-release-plugin] prepare release github-api-1.38 2013-04-06 19:06:37 -07:00
Kohsuke Kawaguchi
5ac7a34a13 using a newer POM 2013-04-06 19:03:47 -07:00
Kohsuke Kawaguchi
b63181d9b5 need to finish the initialization 2013-04-06 13:56:04 -07:00
Kohsuke Kawaguchi
a3119e2cc7 [maven-release-plugin] prepare for next development iteration 2013-03-14 19:06:11 -07:00
Kohsuke Kawaguchi
e8b8971b72 [maven-release-plugin] prepare release github-api-1.37 2013-03-14 19:06:05 -07:00
Kohsuke Kawaguchi
d5f5fa0e0a doc improvement 2013-03-14 19:02:05 -07:00
Kohsuke Kawaguchi
c284f90a1a adding some more type-safe overloads 2013-03-14 18:56:37 -07:00
Michael Clarke
349df60ce8 Adding in support for the refs command and filters in the API 2013-02-15 23:55:04 +00:00
Michael Clarke
5ccc3f4ccd Adding in support for the compare command in the API 2013-02-15 09:43:27 +00:00
johnou
8ba61bb3a6 Revert 2ef5dec466 and add source encoding to deter build warnings. 2013-01-24 17:31:43 +01:00
Johno Crawford
cb3d413e5e [maven-release-plugin] prepare for next development iteration 2013-01-24 17:23:07 +01:00
Johno Crawford
1837699d8c [maven-release-plugin] prepare release github-api-1.36 2013-01-24 17:22:58 +01:00
johnou
2ef5dec466 Temporary change for release. 2013-01-24 17:20:16 +01:00
Johno Crawford
a46c7acbd2 Merge branch 'master' of github.com:kohsuke/github-api 2013-01-24 16:58:42 +01:00
johnou
769d645237 Normalize api url for oauth constructor. 2013-01-24 16:57:51 +01:00
Kohsuke Kawaguchi
389330df2e [maven-release-plugin] prepare for next development iteration 2013-01-06 16:47:29 -08:00
Kohsuke Kawaguchi
9d75913005 [maven-release-plugin] prepare release github-api-1.35 2013-01-06 16:47:24 -08:00
Kohsuke Kawaguchi
887ca772e0 switching to Markdown 2013-01-06 16:45:17 -08:00
Kohsuke Kawaguchi
45286598aa adding OAuth support in ~/.github 2013-01-06 16:43:43 -08:00
Kohsuke Kawaguchi
2e074b5bc4 follow up fix to the pull request #28 2013-01-06 16:25:02 -08:00
Johno Crawford
560e3c257a Refactored test. 2013-01-06 16:25:02 -08:00
Johno Crawford
6e0202fa0b Added membership checks. 2013-01-06 16:25:01 -08:00
Kohsuke Kawaguchi
ef241b1a07 Merge pull request #27 from johnou/deprecated-password
Password is no longer required for api usage and fix for broken base64 encoding.
2013-01-06 16:14:15 -08:00
Johno Crawford
f80cb541d5 Fix base64 encoding. 2013-01-06 21:11:19 +01:00
Johno Crawford
1fe61f72e2 Password is no longer required for api usage. 2013-01-06 13:08:41 +01:00
Jerome Lacoste
bf1b0edfd2 Merge pull request #26 from johnou/remove-webclient
Removed web client and proprietary api usage.
2013-01-06 00:34:15 -08:00
johnou
f0ab946b88 Removed proprietary api usage. 2013-01-06 04:27:18 +01:00
Johno Crawford
975ef1a43d Removed last traces of web client. 2013-01-06 04:06:55 +01:00
Kohsuke Kawaguchi
7064865157 [maven-release-plugin] prepare for next development iteration 2013-01-05 17:18:17 -08:00
Kohsuke Kawaguchi
3dd738b0db [maven-release-plugin] prepare release github-api-1.34 2013-01-05 17:18:11 -08:00
Kohsuke Kawaguchi
6480dde247 oops test failures 2013-01-05 17:15:46 -08:00
Kohsuke Kawaguchi
555dab7403 Merge branch 'pull-22' 2013-01-05 17:11:58 -08:00
Kohsuke Kawaguchi
13158a28e1 turns out we never exposed the ability to specify the custom URL.
So no backward compatibility provision is needed.
Also in this change, I stopped exposin the password. See the code comment for more details
2013-01-05 17:10:24 -08:00
Kohsuke Kawaguchi
cbaca87bbc massaging this a bit to accept the full URL 2013-01-05 16:08:42 -08:00
Kohsuke Kawaguchi
5166202f67 follow-up fix and documenting the expected value 2013-01-05 15:59:02 -08:00
johnou
35d45ca47d JENKINS-13726: Github plugin should work with Guthub enterprise by allowing for overriding the github URL. 2013-01-06 00:47:44 +01:00
Honza Brázdil
b66ede98c7 Retrieve repository directly. 2012-10-20 23:24:56 +02:00
Kohsuke Kawaguchi
1ba8f2ccbf Update pom.xml
Added <repository> definition
2012-10-17 07:45:15 -07:00
Kohsuke Kawaguchi
82133c117a [maven-release-plugin] prepare for next development iteration 2012-09-13 16:31:58 -07:00
Kohsuke Kawaguchi
f71afca828 [maven-release-plugin] prepare release github-api-1.33 2012-09-13 16:31:52 -07:00
Kohsuke Kawaguchi
87f5231c9a updated a test 2012-09-13 16:28:50 -07:00
Kohsuke Kawaguchi
b17f506c20 clean up 2012-09-13 16:23:57 -07:00
Kohsuke Kawaguchi
7f15f12668 completed code to create a new issue 2012-09-13 16:23:06 -07:00
Kohsuke Kawaguchi
e53e62bfa0 added code to create a new issue 2012-09-13 15:56:05 -07:00
Kohsuke Kawaguchi
2e74517a4a fixed issue #20
PagedIterator is updated to cope with the base iterator returning array of length 0
2012-09-13 15:46:25 -07:00
Kohsuke Kawaguchi
aed888051e added a method to retrieve a single issue 2012-09-13 15:36:32 -07:00
Kohsuke Kawaguchi
bd584124bb Merge pull request #19 from janinko/Issues-19-PagedIterable_dosnt_use_authentication
PagedIterable dosn't use authentication
2012-09-13 15:32:56 -07:00
Honza Brázdil
e658a7fa6b send authentication header on all requests 2012-09-12 18:04:59 +02:00
Kohsuke Kawaguchi
52108707bb Merge pull request #18 from janinko/patch-1
When using lazy population, this is not deprecated
2012-09-07 09:52:12 -07:00
Honza Brázdil
0e226a8f78 When using lazy population, this is not deprecated 2012-09-06 14:23:51 +03:00
Kohsuke Kawaguchi
1bf3e025b8 [maven-release-plugin] prepare for next development iteration 2012-09-05 19:30:18 -07:00
Kohsuke Kawaguchi
a0fdcca129 [maven-release-plugin] prepare release github-api-1.32 2012-09-05 19:30:04 -07:00
Kohsuke Kawaguchi
3e75e96718 adding an assertion 2012-09-05 19:26:25 -07:00
Kohsuke Kawaguchi
1dd875adac adding lazy population to GHUser and got rid of GHSmallUser 2012-09-05 19:25:56 -07:00
Kohsuke Kawaguchi
2341f789ab Merge branch 'master' into pull-17 2012-09-05 19:13:32 -07:00
Kohsuke Kawaguchi
8a95847b0a Renaming to better represent what it does. 2012-09-05 19:13:10 -07:00
Kohsuke Kawaguchi
ea9f3eacbc bug fix 2012-09-05 19:12:18 -07:00
Kohsuke Kawaguchi
435363a246 moved the pagenation API over to Poster 2012-09-05 19:08:29 -07:00
Kohsuke Kawaguchi
b6520cb6f9 got rid of all retrieveXYZ methods in favor of Poster 2012-09-05 18:55:44 -07:00
Kohsuke Kawaguchi
d6d73f5165 A step toward using Poster in place of the retrieve* methods.
I was trying to add  the flavor of the retrieve method that reads into an existing instance, when I realized that there are just too many orthogonal axes here to rely on overloaded methods.

That calls for a builder pattern, which we already have --- it's called Poster, but it can actually already handle GET and other HTTP requests.

So I'm retiring the retrieveXYZ methods and moving the code into Poster. This is the first step.
2012-09-05 18:53:06 -07:00
Kohsuke Kawaguchi
f58dbceec7 Massaging the pull request.
In various places of the GitHub API, there's often a full object and
then there's a shallow object. I think the client API would be a lot
easier to use if a single class represents both and retrieve additional
fields on the fly as needed.
2012-09-05 18:15:29 -07:00
Kohsuke Kawaguchi
3f1bb1a214 completed the commit status API 2012-09-05 18:00:50 -07:00
Kohsuke Kawaguchi
892d2acaa2 Added the commit status API, first cut. 2012-09-04 10:40:32 -07:00
Honza Brázdil
4e27d1b5a0 method names should by camelCase 2012-08-31 15:31:41 +02:00
Honza Brázdil
fff3272e42 make url methods return URL and add getApiUrl 2012-08-31 15:27:06 +02:00
Honza Brázdil
803198620d Nested GHIssue.PullRequest must be static 2012-08-31 15:24:14 +02:00
Honza Brázdil
9017fe70d5 We need to preserve api 2012-08-31 14:50:08 +02:00
Honza Brázdil
ce47762fbf Added milestone attribute to GHIssue 2012-08-31 14:37:02 +02:00
Honza Brázdil
b40677a3ca changed return value of GHRepository.getPullRequest()
I'm not sure whether this influence something or not, but I think it should be OK.
2012-08-31 14:30:00 +02:00
Honza Brázdil
c283c4e595 added method retrieving detailed pull request 2012-08-31 14:23:48 +02:00
Honza Brázdil
6aabaea96c Clean up GHIssue and GHPullRequest and make them relevant to api v3.
Added SmallUser representing reference to user.
Added DetailedPullRequest - when retrieving pull request by id, it has more attributes then pull requests obtained by GHRepository.getPullRequests()
2012-08-31 12:37:00 +02:00
Kohsuke Kawaguchi
65adb2f2b4 [maven-release-plugin] prepare for next development iteration 2012-08-28 11:03:11 -07:00
Kohsuke Kawaguchi
dcaf926a95 [maven-release-plugin] prepare release github-api-1.31 2012-08-28 11:02:59 -07:00
Honza Brázdil
587278f282 we need to maintain the binary compatibility, so I reverted getComments and added listComments that exposes PagedIterable.
inspired by 8f95c4f179
2012-08-28 19:09:18 +02:00
Honza Brázdil
cc3793cbcd Comments are paged 2012-08-28 18:59:09 +02:00
Honza Brázdil
c268a5dd07 removed unused throws statement 2012-08-28 18:59:09 +02:00
Honza Brázdil
58d10df5e3 Fix GHIssue.getState() No enum constant 2012-08-28 18:59:09 +02:00
Honza Brázdil
ae2d01a878 fix GHPullRequest.getLabels() NPE 2012-08-28 18:59:09 +02:00
Honza Brázdil
dbc5b0b742 user is not just username; added url 2012-08-28 18:59:09 +02:00
Kohsuke Kawaguchi
6af12c2335 [maven-release-plugin] prepare for next development iteration 2012-08-28 09:43:22 -07:00
Kohsuke Kawaguchi
dafb50d6a9 [maven-release-plugin] prepare release github-api-1.30 2012-08-28 09:43:14 -07:00
Kohsuke Kawaguchi
13c59b6618 Merge branch 'pull-15' 2012-08-28 09:41:59 -07:00
Kohsuke Kawaguchi
8f95c4f179 Massaging the pull request 15.
- we need to maintain the binary compatibility, so I reverted
  getPullRequests and added listPullRequests that exposes PagedIterable.

- Made PagedIterator expose asList.
2012-08-28 09:39:49 -07:00
Aurélien Thieriot
9fd34aec7f Paging GHPullRequests getter and allow to transform iterator to a list 2012-08-12 13:01:42 +02:00
Kohsuke Kawaguchi
17c7a3e7c5 [maven-release-plugin] prepare for next development iteration 2012-06-18 12:51:29 -07:00
Kohsuke Kawaguchi
40a8c110bf [maven-release-plugin] prepare release github-api-1.29 2012-06-18 12:51:25 -07:00
Kohsuke Kawaguchi
c9cd0a4d1f added a simple CRUD test 2012-06-18 12:50:47 -07:00
Kohsuke Kawaguchi
45eae77f8f added missing repository delete operation 2012-06-18 12:50:40 -07:00
Kohsuke Kawaguchi
926202900c fixed a bug in editing the repository definition 2012-06-18 12:37:27 -07:00
Kohsuke Kawaguchi
21aa669503 redundant test case 2012-06-18 12:24:30 -07:00
Kohsuke Kawaguchi
dee28e7a7a Doc says this is asynchronous 2012-06-18 12:23:48 -07:00
Kohsuke Kawaguchi
c8f46a3666 needs to wrap up 2012-06-18 12:23:34 -07:00
Kohsuke Kawaguchi
ba7fe10a08 bug fix 2012-06-18 12:23:20 -07:00
Kohsuke Kawaguchi
9e9db72878 [maven-release-plugin] prepare for next development iteration 2012-06-13 08:27:58 -07:00
Kohsuke Kawaguchi
69a87e2ab7 [maven-release-plugin] prepare release github-api-1.28 2012-06-13 08:27:53 -07:00
Kohsuke Kawaguchi
8ec2686e72 getName() is null with shallow retrieval 2012-06-13 08:12:18 -07:00
Kohsuke Kawaguchi
d034ca4d1f removed unused V3 API 2012-06-13 08:10:01 -07:00
Kohsuke Kawaguchi
61cf71fd67 [maven-release-plugin] prepare for next development iteration 2012-06-12 14:26:30 -07:00
Kohsuke Kawaguchi
63dd1330e9 [maven-release-plugin] prepare release github-api-1.27 2012-06-12 14:26:25 -07:00
Kohsuke Kawaguchi
4411650c5a additional tweaks 2012-06-12 14:25:08 -07:00
Kohsuke Kawaguchi
1c15751949 Removing pointless '3' suffix in the method names. 2012-06-12 14:21:43 -07:00
Kohsuke Kawaguchi
82acf4f107 Now that everything is V3 API, there's no need for such enum. 2012-06-12 14:20:52 -07:00
Kohsuke Kawaguchi
5525ae8921 Removed unused JSON databinding classes 2012-06-12 14:16:59 -07:00
Kohsuke Kawaguchi
b5f7208b0d Removed v2 API usage and switched to v3.
https://github.com/kohsuke/github-api/issues/8
2012-06-12 14:07:27 -07:00
Kohsuke Kawaguchi
3b5bc98053 [maven-release-plugin] prepare for next development iteration 2012-06-04 10:09:15 -07:00
Kohsuke Kawaguchi
3a9ade667a [maven-release-plugin] prepare release github-api-1.26 2012-06-04 10:09:10 -07:00
Kohsuke Kawaguchi
057c32d410 added API to retrieve rate limit. 2012-06-04 10:08:31 -07:00
Kohsuke Kawaguchi
33657c9c92 made the authentication header optional.
This was needed now that GHPerson.getRepository() always try with authentication on, and it'll break if logged in anonymously
2012-06-04 10:05:03 -07:00
Kohsuke Kawaguchi
4c199256a5 [maven-release-plugin] prepare for next development iteration 2012-05-21 22:42:24 -07:00
Kohsuke Kawaguchi
9e62776905 [maven-release-plugin] prepare release github-api-1.25 2012-05-21 22:42:19 -07:00
Kohsuke Kawaguchi
9ba74b945d added permission check methods 2012-05-21 22:40:43 -07:00
Kohsuke Kawaguchi
66656ce612 when authenticated, repository returns additional information, so always send in a credential when one is available 2012-05-21 22:37:48 -07:00
Kohsuke Kawaguchi
73a20ad829 [maven-release-plugin] prepare for next development iteration 2012-05-21 22:16:52 -07:00
Kohsuke Kawaguchi
6fc9a546cb [maven-release-plugin] prepare release github-api-1.24 2012-05-21 22:16:47 -07:00
Kohsuke Kawaguchi
3f6c225948 updated URL 2012-05-21 22:16:09 -07:00
Kohsuke Kawaguchi
c7e9650a39 added code to list up public keys 2012-05-21 22:15:34 -07:00
Kohsuke Kawaguchi
fd28a36b74 [maven-release-plugin] prepare for next development iteration 2012-04-24 17:22:19 -07:00
Kohsuke Kawaguchi
86543c84db [maven-release-plugin] prepare release github-api-1.23 2012-04-24 17:22:14 -07:00
Kohsuke Kawaguchi
706cdac4cc bug fix 2012-04-24 17:21:33 -07:00
Kohsuke Kawaguchi
0b69743792 added comment deletion 2012-04-24 17:17:35 -07:00
Kohsuke Kawaguchi
dd54a00172 added update 2012-04-24 17:13:48 -07:00
Kohsuke Kawaguchi
b5514b891a added a method to create a new commit comment 2012-04-24 17:06:23 -07:00
Kohsuke Kawaguchi
31a6eca97f added commit comment support 2012-04-24 16:54:05 -07:00
Kohsuke Kawaguchi
8557676561 making names consistent with GHBranch 2012-04-24 16:36:18 -07:00
Kohsuke Kawaguchi
39631461ae added list commits 2012-04-24 16:30:01 -07:00
Kohsuke Kawaguchi
a124d7f714 added some tests 2012-04-24 16:16:13 -07:00
Kohsuke Kawaguchi
fff07bf70b fixed a bug in error handling 2012-04-24 16:14:13 -07:00
Kohsuke Kawaguchi
53d09bb5d8 added object representation for GitHub commit 2012-04-24 16:11:55 -07:00
Kohsuke Kawaguchi
a8ecf0bef0 [maven-release-plugin] prepare for next development iteration 2012-04-12 11:36:57 -07:00
Kohsuke Kawaguchi
74b3902d5f [maven-release-plugin] prepare release github-api-1.22 2012-04-12 11:36:49 -07:00
Kohsuke Kawaguchi
7433ed968e monkey test code 2012-04-12 11:05:18 -07:00
Kohsuke Kawaguchi
ddf2d69a68 this method can return null 2012-04-12 11:05:04 -07:00
Kohsuke Kawaguchi
f5b34861bd avoid NPE 2012-04-12 11:01:43 -07:00
Kohsuke Kawaguchi
f0ff31a1af [maven-release-plugin] prepare for next development iteration 2012-04-11 18:16:16 -07:00
Kohsuke Kawaguchi
8ce76fba62 [maven-release-plugin] prepare release github-api-1.21 2012-04-11 18:16:10 -07:00
Kohsuke Kawaguchi
a947672320 doc improvement 2012-04-11 16:34:37 -07:00
Kohsuke Kawaguchi
5496e2b553 added pagenation support 2012-04-11 16:30:31 -07:00
Kohsuke Kawaguchi
2f9edc65dd support gzip for better performance 2012-04-11 15:45:08 -07:00
Kohsuke Kawaguchi
6372337456 Use gzip to improve transfer performance 2012-04-11 15:44:04 -07:00
Kohsuke Kawaguchi
b843606ebe [maven-release-plugin] prepare for next development iteration 2012-04-11 09:32:51 -07:00
Kohsuke Kawaguchi
7cda083b31 [maven-release-plugin] prepare release github-api-1.20 2012-04-11 09:32:46 -07:00
Kohsuke Kawaguchi
095604d4ca updated the parent version 2012-04-11 09:32:22 -07:00
Kohsuke Kawaguchi
7e799d02e3 Added test-ish 2012-04-11 09:29:28 -07:00
Kohsuke Kawaguchi
7f4612f872 exposed the size 2012-04-11 09:27:47 -07:00
Kohsuke Kawaguchi
6f6ff56338 exposed master_branch 2012-04-11 09:26:26 -07:00
Kohsuke Kawaguchi
27701a75d9 Migrating to the V3 API 2012-04-10 13:03:55 -07:00
Kohsuke Kawaguchi
3717d2ed32 emulate the value for V2 API 2012-04-10 12:57:25 -07:00
Kohsuke Kawaguchi
3a9b6665ee exposed avatar_url 2012-04-10 12:43:59 -07:00
Kohsuke Kawaguchi
bbc42f8898 [maven-release-plugin] prepare for next development iteration 2012-04-06 08:25:49 -07:00
Kohsuke Kawaguchi
f6aea47c2c [maven-release-plugin] prepare release github-api-1.19 2012-04-06 08:25:43 -07:00
Kohsuke Kawaguchi
23069ac2fc Using map to enable lookup by name. 2012-04-06 08:24:41 -07:00
Kohsuke Kawaguchi
0b2fdc598b cleaning up 2012-04-06 08:22:44 -07:00
Frederik Fix
aac30b923c implement listing of branches 2012-04-05 16:21:24 +02:00
Kohsuke Kawaguchi
318caf6123 [maven-release-plugin] prepare for next development iteration 2012-03-08 12:51:59 -08:00
Kohsuke Kawaguchi
6898893ffb [maven-release-plugin] prepare release github-api-1.18 2012-03-08 12:51:54 -08:00
Kohsuke Kawaguchi
cb34f4e91f Merge branch 'pull-6' 2012-03-08 12:50:09 -08:00
Kohsuke Kawaguchi
0da4b320f1 As per Sun coding convention, using upper case letters for enum constants 2012-03-08 12:49:36 -08:00
Kohsuke Kawaguchi
facb5bd13e Added API to handle emails. 2012-03-08 12:47:11 -08:00
Kohsuke Kawaguchi
22bc768868 [maven-release-plugin] prepare for next development iteration 2012-02-12 09:30:37 -08:00
Kohsuke Kawaguchi
a9b6f7bc9c [maven-release-plugin] prepare release github-api-1.17 2012-02-12 09:30:06 -08:00
Kohsuke Kawaguchi
830fb7192d added additional convenience method 2012-02-12 08:20:10 -08:00
Kohsuke Kawaguchi
e8ff7a4ae8 updated to use V3 API 2012-02-12 08:11:15 -08:00
Kohsuke Kawaguchi
dac2a56671 fully populate the root field upon deserialization 2012-02-12 07:43:06 -08:00
Kohsuke Kawaguchi
2718cf5ccb support both v2 and v3 2012-02-12 07:27:39 -08:00
Michael O'Cleirigh
202a5d435c Add original GitHub.connectUsingOAuth(token) back
This adds the original API back which defaults to the 'github.com' server.
2012-02-01 21:24:19 -05:00
Michael O'Cleirigh
58af8d0e90 Add support for github enterprise different base url
This changes how the ApiVersion enumeration works so that each of the V2 and V3  Url schemes is actually a template.

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

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

This is needed to support github enterprise based authentication with the github-oauth-plugin.
2012-01-31 22:01:46 -05:00
Kohsuke Kawaguchi
b950be9626 bug fix in the event API. Turns out GHRepository isn't usable for this, because the name field is different.
/repos/:user/:repo returns just ":repo" portion in the name field, whereas in the event it has both ":user/:repo"
2012-01-14 11:53:53 -08:00
Kohsuke Kawaguchi
222277ae0a doc improvement 2012-01-14 11:42:09 -08:00
y.kokubo
fbcf3a17b4 implmented milestone api via v3 2012-01-13 14:36:00 +09:00
Kohsuke Kawaguchi
748cbdd03b doc improvement 2012-01-08 18:30:20 -08:00
Kohsuke Kawaguchi
57b26b6a21 doc improvement 2012-01-08 18:30:19 -08:00
Kohsuke Kawaguchi
a4026d46ce after an experiment, revisiting the design 2012-01-08 18:30:19 -08:00
Kohsuke Kawaguchi
696daf7387 adding the payload handling 2012-01-08 18:30:19 -08:00
Kohsuke Kawaguchi
4a507a5625 adding event support 2012-01-08 18:30:19 -08:00
Kohsuke Kawaguchi
ac1dd9a0ee 'Z' isn't parsed but it means GMT 2012-01-08 18:30:19 -08:00
Kohsuke Kawaguchi
44f1038516 [maven-release-plugin] prepare for next development iteration 2012-01-03 11:16:15 -08:00
Kohsuke Kawaguchi
3e2c9f86da [maven-release-plugin] prepare release github-api-1.16 2012-01-03 11:16:10 -08:00
Kohsuke Kawaguchi
44cc3c8556 added OAuth support to V3 API calls 2012-01-03 11:14:33 -08:00
Kohsuke Kawaguchi
bf65a746b0 inheriting javadoc and designating as overridden 2012-01-03 11:12:18 -08:00
Joshua Krall
4a4a469ca4 Fix for finding private repos on organizations,
requires authenticated API call
2012-01-02 19:45:58 -06:00
Kohsuke Kawaguchi
46b2e5dd32 bug fix 2012-01-02 10:46:54 -08:00
Kohsuke Kawaguchi
8a7adf876f added support for a hook 2012-01-02 10:39:16 -08:00
Kohsuke Kawaguchi
c16ebe4fec bug fix 2012-01-01 18:38:03 -08:00
Kohsuke Kawaguchi
4c61f79e5c bug fix 2012-01-01 18:07:27 -08:00
Kohsuke Kawaguchi
646dc17e5d typo 2012-01-01 18:07:07 -08:00
Kohsuke Kawaguchi
7977471458 Pulled up more properties to the base type 2012-01-01 17:43:44 -08:00
Kohsuke Kawaguchi
9ac2ad957b needs to send auth header 2012-01-01 13:11:24 -08:00
Kohsuke Kawaguchi
7fead8171d improving the usability by introducing a set with lookup-by-ID 2012-01-01 11:13:09 -08:00
Kohsuke Kawaguchi
70bc5c59e2 added a method to return organizations of the user 2012-01-01 09:01:33 -08:00
Kohsuke Kawaguchi
06ef6e841d [maven-release-plugin] prepare for next development iteration 2011-12-31 21:20:06 -08:00
Kohsuke Kawaguchi
2bee34da59 [maven-release-plugin] prepare release github-api-1.15 2011-12-31 21:20:00 -08:00
Kohsuke Kawaguchi
74415b14be bug fix in the error handling 2011-12-19 17:47:08 -08:00
Kohsuke Kawaguchi
1e9a68a16b fixed the behaviour in case the repository doesn't exist. 2011-12-19 17:07:56 -08:00
Kohsuke Kawaguchi
7a3127ed65 added a method to list all members 2011-12-19 17:07:56 -08:00
Kohsuke Kawaguchi
11b0ac19fd PUT apparently requires some payload. 2011-12-19 17:07:56 -08:00
Michael O'Cleirigh
c8eab1f53e OAuth related changes to getMyself() and getUser (username)
Changed how getMyself() works so that in the OAuth case it will use the username less url.

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

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

So then I fixed all the other retrieval points for GHRepository to use v3.
2011-11-10 14:23:36 -08:00
Kohsuke Kawaguchi
0ad419b026 [maven-release-plugin] prepare for next development iteration 2011-10-26 17:39:15 -07:00
Kohsuke Kawaguchi
a395697980 [maven-release-plugin] prepare release github-api-1.14 2011-10-26 17:39:10 -07:00
Kohsuke Kawaguchi
834a6eb865 GitHub has changed the element ID 2011-10-26 17:38:51 -07:00
Kohsuke Kawaguchi
e1c6927fd8 [maven-release-plugin] prepare for next development iteration 2011-09-15 11:44:31 -07:00
58 changed files with 3489 additions and 1118 deletions

1
.gitignore vendored
View File

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

2
README
View File

@@ -1,3 +1,3 @@
Java API for GitHub
See http://kohsuke.org/github-api/ for more details
See http://github-api.kohsuke.org/ for more details

64
pom.xml
View File

@@ -3,11 +3,11 @@
<parent>
<groupId>org.kohsuke</groupId>
<artifactId>pom</artifactId>
<version>1</version>
<version>4</version>
</parent>
<artifactId>github-api</artifactId>
<version>1.13</version>
<version>1.41</version>
<name>GitHub API for Java</name>
<url>http://github-api.kohsuke.org/</url>
<description>GitHub API for Java</description>
@@ -25,18 +25,37 @@
</site>
</distributionManagement>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<plugin>
<groupId>com.infradna.tool</groupId>
<artifactId>bridge-method-injector</artifactId>
<version>1.2</version>
<executions>
<execution>
<goals>
<goal>process</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.jvnet.hudson</groupId>
<artifactId>htmlunit</artifactId>
<version>2.6-hudson-2</version>
<exclusions>
<exclusion>
<!-- hides JDK DOM classes in Eclipse -->
<groupId>xml-apis</groupId>
<artifactId>xml-apis</artifactId>
</exclusion>
</exclusions>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.7</version>
</dependency>
<dependency>
<groupId>junit</groupId>
@@ -47,15 +66,34 @@
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.5.0</version>
<version>1.9.9</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>com.infradna.tool</groupId>
<artifactId>bridge-method-annotation</artifactId>
<version>1.4</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.kohsuke.stapler</groupId>
<artifactId>stapler-jetty</artifactId>
<version>1.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<repositories>
<repository>
<id>repo.jenkins-ci.org</id>
<url>http://repo.jenkins-ci.org/public/</url>
</repository>
</repositories>
<reporting>
<plugins>
<plugin>

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,254 @@
package org.kohsuke.github;
import java.io.IOException;
import java.net.URL;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* A commit in a repository.
*
* @author Kohsuke Kawaguchi
* @see GHRepository#getCommit(String)
* @see GHCommitComment#getCommit()
*/
public class GHCommit {
private GHRepository owner;
public static class Stats {
int total,additions,deletions;
}
/**
* A file that was modified.
*/
public static class File {
String status;
int changes,additions,deletions;
String raw_url, blob_url, filename, sha, patch;
/**
* Number of lines added + removed.
*/
public int getLinesChanged() {
return changes;
}
/**
* Number of lines added.
*/
public int getLinesAdded() {
return additions;
}
/**
* Number of lines removed.
*/
public int getLinesDeleted() {
return deletions;
}
/**
* "modified", "added", or "deleted"
*/
public String getStatus() {
return status;
}
/**
* Just the base name and the extension without any directory name.
*/
public String getFileName() {
return filename;
}
/**
* The actual change.
*/
public String getPatch() {
return patch;
}
/**
* URL like 'https://raw.github.com/jenkinsci/jenkins/4eb17c197dfdcf8ef7ff87eb160f24f6a20b7f0e/core/pom.xml'
* that resolves to the actual content of the file.
*/
public URL getRawUrl() {
return GitHub.parseURL(raw_url);
}
/**
* URL like 'https://github.com/jenkinsci/jenkins/blob/1182e2ebb1734d0653142bd422ad33c21437f7cf/core/pom.xml'
* that resolves to the HTML page that describes this file.
*/
public URL getBlobUrl() {
return GitHub.parseURL(blob_url);
}
/**
* [0-9a-f]{40} SHA1 checksum.
*/
public String getSha() {
return sha;
}
}
public static class Parent {
String url,sha;
}
static class User {
// TODO: what if someone who doesn't have an account on GitHub makes a commit?
String url,avatar_url,login,gravatar_id;
int id;
}
String url,sha;
List<File> files;
Stats stats;
List<Parent> parents;
User author,committer;
/**
* The repository that contains the commit.
*/
public GHRepository getOwner() {
return owner;
}
/**
* Number of lines added + removed.
*/
public int getLinesChanged() {
return stats.total;
}
/**
* Number of lines added.
*/
public int getLinesAdded() {
return stats.additions;
}
/**
* Number of lines removed.
*/
public int getLinesDeleted() {
return stats.deletions;
}
/**
* [0-9a-f]{40} SHA1 checksum.
*/
public String getSHA1() {
return sha;
}
/**
* List of files changed/added/removed in this commit.
*
* @return
* Can be empty but never null.
*/
public List<File> getFiles() {
return files!=null ? Collections.unmodifiableList(files) : Collections.<File>emptyList();
}
/**
* Returns the SHA1 of parent commit objects.
*/
public List<String> getParentSHA1s() {
if (parents==null) return Collections.emptyList();
return new AbstractList<String>() {
@Override
public String get(int index) {
return parents.get(index).sha;
}
@Override
public int size() {
return parents.size();
}
};
}
/**
* Resolves the parent commit objects and return them.
*/
public List<GHCommit> getParents() throws IOException {
List<GHCommit> r = new ArrayList<GHCommit>();
for (String sha1 : getParentSHA1s())
r.add(owner.getCommit(sha1));
return r;
}
public GHUser getAuthor() throws IOException {
return resolveUser(author);
}
public GHUser getCommitter() throws IOException {
return resolveUser(committer);
}
private GHUser resolveUser(User author) throws IOException {
if (author==null || author.login==null) return null;
return owner.root.getUser(author.login);
}
/**
* Lists up all the commit comments in this repository.
*/
public PagedIterable<GHCommitComment> listComments() {
return new PagedIterable<GHCommitComment>() {
public PagedIterator<GHCommitComment> iterator() {
return new PagedIterator<GHCommitComment>(owner.root.retrieve().asIterator(String.format("/repos/%s/%s/commits/%s/comments", owner.getOwnerName(), owner.getName(), sha), GHCommitComment[].class)) {
@Override
protected void wrapUp(GHCommitComment[] page) {
for (GHCommitComment c : page)
c.wrap(owner);
}
};
}
};
}
/**
* Creates a commit comment.
*
* I'm not sure how path/line/position parameters interact with each other.
*/
public GHCommitComment createComment(String body, String path, Integer line, Integer position) throws IOException {
GHCommitComment r = new Requester(owner.root)
.with("body",body)
.with("path",path)
.with("line",line)
.with("position",position)
.to(String.format("/repos/%s/%s/commits/%s/comments",owner.getOwnerName(),owner.getName(),sha),GHCommitComment.class);
return r.wrap(owner);
}
public GHCommitComment createComment(String body) throws IOException {
return createComment(body,null,null,null);
}
/**
* Gets the status of this commit, newer ones first.
*/
public PagedIterable<GHCommitStatus> listStatuses() throws IOException {
return owner.listCommitStatuses(sha);
}
/**
* Gets the last status of this commit, which is what gets shown in the UI.
*/
public GHCommitStatus getLastStatus() throws IOException {
return owner.getLastCommitStatus(sha);
}
GHCommit wrapUp(GHRepository owner) {
this.owner = owner;
return this;
}
}

View File

@@ -0,0 +1,122 @@
package org.kohsuke.github;
import java.io.IOException;
import java.net.URL;
import java.util.Date;
/**
* A comment attached to a commit (or a specific line in a specific file of a commit.)
*
* @author Kohsuke Kawaguchi
* @see GHRepository#listCommitComments()
* @see GHCommit#listComments()
* @see GHCommit#createComment(String, String, Integer, Integer)
*/
public class GHCommitComment {
private GHRepository owner;
String updated_at, created_at;
String body, url, html_url, commit_id;
Integer line;
int id;
String path;
User user;
static class User {
// TODO: what if someone who doesn't have an account on GitHub makes a commit?
String url,avatar_url,login,gravatar_id;
int id;
}
public GHRepository getOwner() {
return owner;
}
public Date getCreatedAt() {
return GitHub.parseDate(created_at);
}
public Date getUpdatedAt() {
return GitHub.parseDate(updated_at);
}
/**
* URL like 'https://github.com/kohsuke/sandbox-ant/commit/8ae38db0ea5837313ab5f39d43a6f73de3bd9000#commitcomment-1252827' to
* show this commit comment in a browser.
*/
public URL getHtmlUrl() {
return GitHub.parseURL(html_url);
}
public String getSHA1() {
return commit_id;
}
/**
* Commit comment in the GitHub flavored markdown format.
*/
public String getBody() {
return body;
}
/**
* A commit comment can be on a specific line of a specific file, if so, this field points to a file.
* Otherwise null.
*/
public String getPath() {
return path;
}
/**
* A commit comment can be on a specific line of a specific file, if so, this field points to the line number in the file.
* Otherwise -1.
*/
public int getLine() {
return line!=null ? line : -1;
}
public int getId() {
return id;
}
/**
* Gets the user who put this comment.
*/
public GHUser getUser() throws IOException {
return owner.root.getUser(user.login);
}
/**
* Gets the commit to which this comment is associated with.
*/
public GHCommit getCommit() throws IOException {
return getOwner().getCommit(getSHA1());
}
/**
* Updates the body of the commit message.
*/
public void update(String body) throws IOException {
GHCommitComment r = new Requester(owner.root)
.with("body", body)
.method("PATCH").to(getApiTail(), GHCommitComment.class);
this.body = body;
}
/**
* Deletes this comment.
*/
public void delete() throws IOException {
new Requester(owner.root).method("DELETE").to(getApiTail());
}
private String getApiTail() {
return String.format("/repos/%s/%s/comments/%s",owner.getOwnerName(),owner.getName(),id);
}
GHCommitComment wrap(GHRepository owner) {
this.owner = owner;
return this;
}
}

View File

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

View File

@@ -0,0 +1,11 @@
package org.kohsuke.github;
/**
* Represents the state of commit
*
* @author Kohsuke Kawaguchi
* @see GHCommitStatus
*/
public enum GHCommitState {
PENDING, SUCCESS, ERROR, FAILURE
}

View File

@@ -0,0 +1,72 @@
package org.kohsuke.github;
import java.io.IOException;
import java.util.Date;
/**
* Represents a status of a commit.
*
* @author Kohsuke Kawaguchi
* @see GHRepository#getCommitStatus(String)
* @see GHCommit#getStatus()
*/
public class GHCommitStatus {
String created_at, updated_at;
String state;
String target_url,description;
int id;
String url;
GHUser creator;
private GitHub root;
/*package*/ GHCommitStatus wrapUp(GitHub root) {
if (creator!=null) creator.wrapUp(root);
this.root = root;
return this;
}
public Date getCreatedAt() {
return GitHub.parseDate(created_at);
}
public Date getUpdatedAt() {
return GitHub.parseDate(updated_at);
}
public GHCommitState getState() {
for (GHCommitState s : GHCommitState.values()) {
if (s.name().equalsIgnoreCase(state))
return s;
}
throw new IllegalStateException("Unexpected state: "+state);
}
/**
* The URL that this status is linked to.
*
* This is the URL specified when creating a commit status.
*/
public String getTargetUrl() {
return target_url;
}
public String getDescription() {
return description;
}
public int getId() {
return id;
}
/**
* API URL of this commit status.
*/
public String getUrl() {
return url;
}
public GHUser getCreator() {
return creator;
}
}

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,98 @@
package org.kohsuke.github;
import java.io.Reader;
/**
* Base type for types used in databinding of the event payload.
*
* @see GitHub#parseEventPayload(Reader, Class)
* @see GHEventInfo#getPayload(Class)
*/
public abstract class GHEventPayload {
protected GitHub root;
/*package*/ GHEventPayload() {
}
/*package*/ void wrapUp(GitHub root) {
this.root = root;
}
/**
* A pull request status has changed.
*
* @see http://developer.github.com/v3/activity/events/types/#pullrequestevent
*/
public static class PullRequest extends GHEventPayload {
private String action;
private int number;
private GHPullRequest pull_request;
public String getAction() {
return action;
}
public int getNumber() {
return number;
}
public GHPullRequest getPullRequest() {
pull_request.root = root;
return pull_request;
}
@Override
void wrapUp(GitHub root) {
super.wrapUp(root);
pull_request.wrapUp(root);
}
}
/**
* A comment was added to an issue
*
* @see http://developer.github.com/v3/activity/events/types/#issuecommentevent
*/
public static class IssueComment extends GHEventPayload {
private String action;
private GHIssueComment comment;
private GHIssue issue;
private GHRepository repository;
public String getAction() {
return action;
}
public GHIssueComment getComment() {
return comment;
}
public void setComment(GHIssueComment comment) {
this.comment = comment;
}
public GHIssue getIssue() {
return issue;
}
public void setIssue(GHIssue issue) {
this.issue = issue;
}
public GHRepository getRepository() {
return repository;
}
public void setRepository(GHRepository repository) {
this.repository = repository;
}
@Override
void wrapUp(GitHub root) {
super.wrapUp(root);
repository.wrap(root);
issue.wrap(repository);
comment.wrapUp(issue);
}
}
}

View File

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

View File

@@ -30,6 +30,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Locale;
/**
* Represents an issue on GitHub.
@@ -40,11 +41,38 @@ import java.util.List;
public class GHIssue {
GitHub root;
GHRepository owner;
// API v3
protected GHUser assignee;
protected String state;
protected int number;
protected String closed_at;
protected int comments;
protected String body;
protected List<String> labels;
protected GHUser user;
protected String title, created_at, html_url;
protected GHIssue.PullRequest pull_request;
protected GHMilestone milestone;
protected String url, updated_at;
protected int id;
protected GHUser closed_by;
private String gravatar_id,body,title,state,created_at,updated_at,html_url;
private List<String> labels;
private int number,votes,comments;
private int position;
/*package*/ GHIssue wrap(GHRepository owner) {
this.owner = owner;
this.root = owner.root;
if(milestone != null) milestone.wrap(owner);
if(assignee != null) assignee.wrapUp(root);
if(user != null) user.wrapUp(root);
if(closed_by != null) closed_by.wrapUp(root);
return this;
}
/*package*/ static GHIssue[] wrap(GHIssue[] issues, GHRepository owner) {
for (GHIssue i : issues)
i.wrap(owner);
return issues;
}
/**
* Repository to which the issue belongs.
@@ -80,10 +108,13 @@ public class GHIssue {
}
public GHIssueState getState() {
return Enum.valueOf(GHIssueState.class, state);
return Enum.valueOf(GHIssueState.class, state.toUpperCase(Locale.ENGLISH));
}
public Collection<String> getLabels() {
if(labels == null){
return Collections.EMPTY_LIST;
}
return Collections.unmodifiableList(labels);
}
@@ -95,35 +126,128 @@ public class GHIssue {
return GitHub.parseDate(updated_at);
}
public Date getClosedAt() {
return GitHub.parseDate(closed_at);
}
public URL getApiURL(){
return GitHub.parseURL(url);
}
/**
* Updates the issue by adding a comment.
*/
public void comment(String message) throws IOException {
new Poster(root).withCredential().with("comment",message).to(getApiRoute("comment"));
new Requester(root).with("body",message).to(getApiRoute() + "/comments");
}
private void edit(String key, Object value) throws IOException {
new Requester(root)._with(key, value).method("PATCH").to(getApiRoute());
}
/**
* Closes this issue.
*/
public void close() throws IOException {
new Poster(root).withCredential().to(getApiRoute("close"));
edit("state", "closed");
}
/**
* Reopens this issue.
*/
public void reopen() throws IOException {
new Poster(root).withCredential().to(getApiRoute("reopen"));
edit("state", "open");
}
public void setTitle(String title) throws IOException {
edit("title",title);
}
public void setBody(String body) throws IOException {
edit("body",body);
}
public void assignTo(GHUser user) throws IOException {
edit("assignee",user.getLogin());
}
public void setLabels(String... labels) throws IOException {
edit("assignee",labels);
}
/**
* Obtains all the comments associated with this issue.
*
* @see #listComments()
*/
public List<GHIssueComment> getComments() throws IOException {
return root.retrieve(getApiRoute("comments"), JsonIssueComments.class).wrap(this);
public List<GHIssueComment> getComments() throws IOException {
return listComments().asList();
}
/**
* Obtains all the comments associated with this issue.
*/
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)) {
protected void wrapUp(GHIssueComment[] page) {
for (GHIssueComment c : page)
c.wrapUp(GHIssue.this);
}
};
}
};
}
private String getApiRoute(String verb) {
return "/issues/"+verb+"/"+owner.getOwnerName()+"/"+owner.getName()+"/"+number;
protected String getApiRoute() {
return "/repos/"+owner.getOwnerName()+"/"+owner.getName()+"/issues/"+number;
}
public GHUser getAssignee() {
return assignee;
}
/**
* User who submitted the issue.
*/
public GHUser getUser() {
return user;
}
public GHUser getClosedBy() {
if(!"closed".equals(state)) return null;
if(closed_by != null) return closed_by;
//TODO closed_by = owner.getIssue(number).getClosed_by();
return closed_by;
}
public int getCommentsCount(){
return comments;
}
public PullRequest getPullRequest() {
return pull_request;
}
public GHMilestone getMilestone() {
return milestone;
}
public static class PullRequest{
private String diff_url, patch_url, html_url;
public URL getDiffUrl() {
return GitHub.parseURL(diff_url);
}
public URL getPatchUrl() {
return GitHub.parseURL(patch_url);
}
public URL getUrl() {
return GitHub.parseURL(html_url);
}
}
}

View File

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

View File

@@ -24,6 +24,7 @@
package org.kohsuke.github;
import java.io.IOException;
import java.net.URL;
import java.util.Date;
/**
@@ -34,8 +35,15 @@ import java.util.Date;
public class GHIssueComment {
GHIssue owner;
private String body, gravatar_id, user, created_at, updated_at;
private String body, gravatar_id, created_at, updated_at;
private URL url;
private int id;
private GHUser user;
/*package*/ GHIssueComment wrapUp(GHIssue owner) {
this.owner = owner;
return this;
}
/**
* Gets the issue to which this comment is associated.
@@ -63,17 +71,22 @@ public class GHIssueComment {
return id;
}
public URL getUrl() {
return url;
}
/**
* Gets the ID of the user who posted this comment.
*/
@Deprecated
public String getUserName() {
return user;
return user.getLogin();
}
/**
* Gets the user who posted this comment.
*/
public GHUser getUser() throws IOException {
return owner.root.getUser(user);
return owner.root.getUser(user.getLogin());
}
}

View File

@@ -0,0 +1,48 @@
package org.kohsuke.github;
import org.apache.commons.lang.builder.ToStringBuilder;
/**
* SSH public key.
*
* @author Kohsuke Kawaguchi
*/
public class GHKey {
/*package almost final*/ GitHub root;
private String url, key, title;
private boolean verified;
private int id;
public int getId() {
return id;
}
public String getKey() {
return key;
}
public String getTitle() {
return title;
}
/**
* Something like "https://api.github.com/user/keys/73593"
*/
public String getUrl() {
return url;
}
public boolean isVerified() {
return verified;
}
/*package*/ GHKey wrap(GitHub root) {
this.root = root;
return this;
}
public String toString() {
return new ToStringBuilder(this).append("title",title).append("id",id).append("key",key).toString();
}
}

View File

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

View File

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

View File

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

View File

@@ -1,21 +1,22 @@
package org.kohsuke.github;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlAnchor;
import com.gargoylesoftware.htmlunit.html.HtmlForm;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import java.io.IOException;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
/**
* @author Kohsuke Kawaguchi
*/
public class GHOrganization extends GHPerson {
/*package*/ GHOrganization wrapUp(GitHub root) {
return (GHOrganization)super.wrapUp(root);
}
/**
* Creates a new repository.
*
@@ -23,30 +24,89 @@ public class GHOrganization extends GHPerson {
* Newly created repository.
*/
public GHRepository createRepository(String name, String description, String homepage, String team, boolean isPublic) throws IOException {
return createRepository(name,description,homepage,getTeams().get(team),isPublic);
}
public GHRepository createRepository(String name, String description, String homepage, GHTeam team, boolean isPublic) throws IOException {
// such API doesn't exist, so fall back to HTML scraping
WebClient wc = root.createWebClient();
HtmlPage pg = (HtmlPage)wc.getPage("https://github.com/organizations/"+login+"/repositories/new");
HtmlForm f = pg.getForms().get(1);
f.getInputByName("repository[name]").setValueAttribute(name);
f.getInputByName("repository[description]").setValueAttribute(description);
f.getInputByName("repository[homepage]").setValueAttribute(homepage);
f.getSelectByName("team_id").getOptionByText(team).setSelected(true);
f.submit(f.getButtonByCaption("Create Repository"));
return getRepository(name);
// GHRepository r = new Poster(root).withCredential()
// .with("name", name).with("description", description).with("homepage", homepage)
// .with("public", isPublic ? 1 : 0).to(root.getApiURL("/organizations/"+login+"/repos/create"), JsonRepository.class).repository;
// r.root = root;
// return r;
return new Requester(root)
.with("name", name).with("description", description).with("homepage", homepage)
.with("public", isPublic).with("team_id",team.getId()).to("/orgs/"+login+"/repos", GHRepository.class).wrap(root);
}
/**
* Teams by their names.
*/
public Map<String,GHTeam> getTeams() throws IOException {
return root.retrieveWithAuth("/organizations/"+login+"/teams",JsonTeams.class).toMap(this);
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));
}
return r;
}
/**
* Checks if this organization has the specified user as a member.
*/
public boolean hasMember(GHUser user) {
try {
root.retrieve().to("/orgs/" + login + "/members/" + user.getLogin());
return true;
} catch (IOException ignore) {
return false;
}
}
/**
* Checks if this organization has the specified user as a public member.
*/
public boolean hasPublicMember(GHUser user) {
try {
root.retrieve().to("/orgs/" + login + "/public_members/" + user.getLogin());
return true;
} catch (IOException ignore) {
return false;
}
}
/**
* Publicizes the membership.
*/
public void publicize(GHUser u) throws IOException {
root.retrieve().method("PUT").to("/orgs/" + login + "/public_members/" + u.getLogin(), null);
}
/**
* All the members of this organization.
*/
public List<GHUser> getMembers() throws IOException {
return new AbstractList<GHUser>() {
// these are shallow objects with only some limited values filled out
// TODO: it's better to allow objects to fill themselves in later when missing values are requested
final GHUser[] shallow = root.retrieve().to("/orgs/" + login + "/members", GHUser[].class);
@Override
public GHUser get(int index) {
try {
return root.getUser(shallow[index].getLogin());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public int size() {
return shallow.length;
}
};
}
/**
* Conceals the membership.
*/
public void conceal(GHUser u) throws IOException {
root.retrieve().method("DELETE").to("/orgs/" + login + "/public_members/" + u.getLogin(), null);
}
public enum Permission { ADMIN, PUSH, PULL }
@@ -55,11 +115,13 @@ public class GHOrganization extends GHPerson {
* Creates a new team and assigns the repositories.
*/
public GHTeam createTeam(String name, Permission p, Collection<GHRepository> repositories) throws IOException {
Poster post = new Poster(root).withCredential().with("team[name]", name).with("team[permission]", p.name().toLowerCase());
Requester post = new Requester(root).with("name", name).with("permission", p.name().toLowerCase());
List<String> repo_names = new ArrayList<String>();
for (GHRepository r : repositories) {
post.with("team[repo_names][]",r.getOwnerName()+'/'+r.getName());
repo_names.add(r.getName());
}
return post.to("/organizations/"+login+"/teams",JsonTeam.class).wrap(this);
post.with("repo_names",repo_names);
return post.method("POST").to("/orgs/" + login + "/teams", GHTeam.class).wrapUp(this);
}
public GHTeam createTeam(String name, Permission p, GHRepository... repositories) throws IOException {
@@ -70,13 +132,13 @@ public class GHOrganization extends GHPerson {
* List up repositories that has some open pull requests.
*/
public List<GHRepository> getRepositoriesWithOpenPullRequests() throws IOException {
WebClient wc = root.createWebClient();
HtmlPage pg = (HtmlPage)wc.getPage("https://github.com/organizations/"+login+"/dashboard/pulls");
List<GHRepository> r = new ArrayList<GHRepository>();
for (HtmlAnchor e : pg.getElementById("js-issue-list").<HtmlAnchor>selectNodes(".//UL[@class='smallnav']/LI[not(@class='zeroed')]/A")) {
String a = e.getHrefAttribute();
String name = a.substring(a.lastIndexOf('/')+1);
r.add(getRepository(name));
for (GHRepository repository : root.retrieve().to("/orgs/" + login + "/repos", GHRepository[].class)) {
repository.wrap(root);
List<GHPullRequest> pullRequests = repository.getPullRequests(GHIssueState.OPEN);
if (pullRequests.size() > 0) {
r.add(repository);
}
}
return r;
}

View File

@@ -1,7 +1,11 @@
package org.kohsuke.github;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
@@ -13,36 +17,190 @@ import java.util.TreeMap;
public abstract class GHPerson {
/*package almost final*/ GitHub root;
protected String gravatar_id,login;
// core data fields that exist even for "small" user data (such as the user info in pull request)
protected String login, avatar_url, url, gravatar_id;
protected int id;
protected int public_gist_count,public_repo_count,following_count,id;
// other fields (that only show up in full data)
protected String location,blog,email,name,created_at,company;
protected String html_url;
protected int followers,following,public_repos,public_gists;
/*package*/ GHPerson wrapUp(GitHub root) {
this.root = root;
return this;
}
/**
* Fully populate the data by retrieving missing data.
*
* Depending on the original API call where this object is created, it may not contain everything.
*/
protected void populate() throws IOException {
if (created_at!=null) return; // already populated
root.retrieve().to(url, this);
}
/**
* Gets the repositories this user owns.
*/
public synchronized Map<String,GHRepository> getRepositories() throws IOException {
Map<String,GHRepository> repositories = new TreeMap<String, GHRepository>();
for (int i=1; ; i++) {
Map<String, GHRepository> map = root.retrieve("/repos/show/" + login + "?page=" + i, JsonRepositories.class).wrap(root);
repositories.putAll(map);
if (map.isEmpty()) break;
for (List<GHRepository> batch : iterateRepositories(100)) {
for (GHRepository r : batch)
repositories.put(r.getName(),r);
}
return Collections.unmodifiableMap(repositories);
}
/**
* Loads repository list in a pagenated fashion.
*
* <p>
* For a person with a lot of repositories, GitHub returns the list of repositories in a pagenated fashion.
* Unlike {@link #getRepositories()}, this method allows the caller to start processing data as it arrives.
*
* Every {@link Iterator#next()} call results in I/O. Exceptions that occur during the processing is wrapped
* into {@link Error}.
*/
public synchronized Iterable<List<GHRepository>> iterateRepositories(final int pageSize) {
return new Iterable<List<GHRepository>>() {
public Iterator<List<GHRepository>> iterator() {
final Iterator<GHRepository[]> pager = root.retrieve().asIterator("/users/" + login + "/repos?per_page="+pageSize,GHRepository[].class);
return new Iterator<List<GHRepository>>() {
public boolean hasNext() {
return pager.hasNext();
}
public List<GHRepository> next() {
GHRepository[] batch = pager.next();
for (GHRepository r : batch)
r.root = root;
return Arrays.asList(batch);
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
};
}
/**
*
* @return
* null if the repository was not found
*/
public GHRepository getRepository(String name) throws IOException {
return root.retrieve("/repos/show/" + login + '/' + name, JsonRepository.class).wrap(root);
try {
return root.retrieve().to("/repos/" + login + '/' + name, GHRepository.class).wrap(root);
} catch (FileNotFoundException e) {
return null;
}
}
/**
* Gravatar ID of this user, like 0cb9832a01c22c083390f3c5dcb64105
*
* @deprecated
* No longer available in the v3 API.
*/
public String getGravatarId() {
return gravatar_id;
}
/**
* Returns a string like 'https://secure.gravatar.com/avatar/0cb9832a01c22c083390f3c5dcb64105'
* that indicates the avatar image URL.
*/
public String getAvatarUrl() {
if (avatar_url!=null)
return avatar_url;
if (gravatar_id!=null)
return "https://secure.gravatar.com/avatar/"+gravatar_id;
return null;
}
/**
* Gets the login ID of this user, like 'kohsuke'
*/
public String getLogin() {
return login;
}
/**
* Gets the human-readable name of the user, like "Kohsuke Kawaguchi"
*/
public String getName() throws IOException {
populate();
return name;
}
/**
* Gets the company name of this user, like "Sun Microsystems, Inc."
*/
public String getCompany() throws IOException {
populate();
return company;
}
/**
* Gets the location of this user, like "Santa Clara, California"
*/
public String getLocation() throws IOException {
populate();
return location;
}
public String getCreatedAt() throws IOException {
populate();
return created_at;
}
/**
* Gets the blog URL of this user.
*/
public String getBlog() throws IOException {
populate();
return blog;
}
/**
* Gets the e-mail address of the user.
*/
public String getEmail() throws IOException {
populate();
return email;
}
public int getPublicGistCount() throws IOException {
populate();
return public_gists;
}
public int getPublicRepoCount() throws IOException {
populate();
return public_repos;
}
public int getFollowingCount() throws IOException {
populate();
return following;
}
/**
* What appears to be a GitHub internal unique number that identifies this user.
*/
public int getId() {
return id;
}
public int getFollowersCount() throws IOException {
populate();
return followers;
}
}

View File

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

View File

@@ -23,9 +23,10 @@
*/
package org.kohsuke.github;
import java.io.IOException;
import java.net.URL;
import java.util.Collection;
import java.util.Date;
import java.util.Locale;
/**
* A pull request.
@@ -34,11 +35,39 @@ import java.util.Locale;
*/
@SuppressWarnings({"UnusedDeclaration"})
public class GHPullRequest extends GHIssue {
private String closed_at, patch_url, issue_updated_at;
private GHUser issue_user, user;
// labels??
private GHCommitPointer base, head;
private String mergeable, diff_url;
private String patch_url, diff_url, issue_url;
private GHCommitPointer base;
private String merged_at;
private GHCommitPointer head;
// details that are only available when obtained from ID
private GHUser merged_by;
private int review_comments, additions;
private boolean merged;
private Boolean mergeable;
private int deletions;
private String mergeable_state;
private int changed_files;
GHPullRequest wrapUp(GHRepository owner) {
this.wrap(owner);
return wrapUp(owner.root);
}
GHPullRequest wrapUp(GitHub root) {
if (owner!=null) owner.wrap(root);
if (base!=null) base.wrapUp(root);
if (head!=null) head.wrapUp(root);
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.
@@ -47,12 +76,13 @@ public class GHPullRequest extends GHIssue {
public URL getPatchUrl() {
return GitHub.parseURL(patch_url);
}
/**
* User who submitted a pull request.
/**
* The URL of the patch file.
* like https://github.com/jenkinsci/jenkins/pull/100.patch
*/
public GHUser getUser() {
return user;
public URL getIssueUrl() {
return GitHub.parseURL(issue_url);
}
/**
@@ -64,22 +94,15 @@ public class GHPullRequest extends GHIssue {
}
/**
* The change that should be pulled.
* The change that should be pulled. The tip of the commits to merge.
*/
public GHCommitPointer getHead() {
return head;
}
@Deprecated
public Date getIssueUpdatedAt() {
return GitHub.parseDate(issue_updated_at);
}
/**
* The HTML page of this pull request,
* like https://github.com/jenkinsci/jenkins/pull/100
*/
public URL getUrl() {
return super.getUrl();
return super.getUpdatedAt();
}
/**
@@ -90,7 +113,77 @@ public class GHPullRequest extends GHIssue {
return GitHub.parseURL(diff_url);
}
public Date getClosedAt() {
return GitHub.parseDate(closed_at);
public Date getMergedAt() {
return GitHub.parseDate(merged_at);
}
@Override
public Collection<String> getLabels() {
return super.getLabels();
}
@Override
public GHUser getClosedBy() {
return null;
}
@Override
public PullRequest getPullRequest() {
return null;
}
//
// details that are only available via get with ID
//
//
public GHUser getMergedBy() throws IOException {
populate();
return merged_by;
}
public int getReviewComments() throws IOException {
populate();
return review_comments;
}
public int getAdditions() throws IOException {
populate();
return additions;
}
public boolean isMerged() throws IOException {
populate();
return merged;
}
public Boolean getMergeable() throws IOException {
populate();
return mergeable;
}
public int getDeletions() throws IOException {
populate();
return deletions;
}
public String getMergeableState() throws IOException {
populate();
return mergeable_state;
}
public int getChangedFiles() throws IOException {
populate();
return changed_files;
}
/**
* Fully populate the data by retrieving missing data.
*
* Depending on the original API call where this object is created, it may not contain everything.
*/
private void populate() throws IOException {
if (merged_by!=null) return; // already populated
root.retrieve().to(url, this);
}
}

View File

@@ -0,0 +1,21 @@
package org.kohsuke.github;
/**
* Rate limit.
* @author Kohsuke Kawaguchi
*/
public class GHRateLimit {
/**
* Remaining calls that can be made.
*/
public int remaining;
/**
* Alotted API call per hour.
*/
public int limit;
@Override
public String toString() {
return remaining+"/"+limit;
}
}

View File

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

View File

@@ -23,32 +23,27 @@
*/
package org.kohsuke.github;
import com.gargoylesoftware.htmlunit.ElementNotFoundException;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlButton;
import com.gargoylesoftware.htmlunit.html.HtmlCheckBoxInput;
import com.gargoylesoftware.htmlunit.html.HtmlForm;
import com.gargoylesoftware.htmlunit.html.HtmlInput;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
import java.io.IOException;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.io.InterruptedIOException;
import java.net.URL;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import static java.util.Arrays.*;
import static org.kohsuke.github.ApiVersion.V3;
/**
* A repository on GitHub.
@@ -59,10 +54,24 @@ import static org.kohsuke.github.ApiVersion.V3;
public class GHRepository {
/*package almost final*/ GitHub root;
private String description, homepage, url, name, owner;
private String description, homepage, name;
private String url; // this is the API url
private String html_url; // this is the UI
private GHUser owner; // not fully populated. beware.
private boolean has_issues, has_wiki, fork, _private, has_downloads;
private int watchers,forks,open_issues;
private int watchers,forks,open_issues,size;
private String created_at, pushed_at;
private Map<Integer,GHMilestone> milestones = new HashMap<Integer, GHMilestone>();
private String master_branch,language;
private Map<String,GHCommit> commits = new HashMap<String, GHCommit>();
private GHRepoPermission permissions;
private static class GHRepoPermission {
boolean pull,push,admin;
}
public String getDescription() {
return description;
@@ -73,26 +82,72 @@ public class GHRepository {
}
/**
* URL of this repository, like 'http://github.com/kohsuke/hudson'
* URL of this repository, like 'http://github.com/kohsuke/jenkins'
*/
public String getUrl() {
return url;
return html_url;
}
/**
* Gets the git:// URL to this repository, such as "git://github.com/kohsuke/jenkins.git"
* This URL is read-only.
*/
public String getGitTransportUrl() {
return "git://github.com/"+getOwnerName()+"/"+name+".git";
}
/**
* Gets the HTTPS URL to this repository, such as "https://github.com/kohsuke/jenkins.git"
* This URL is read-only.
*/
public String gitHttpTransportUrl() {
return "https://github.com/"+getOwnerName()+"/"+name+".git";
}
/**
* Short repository name without the owner. For example 'jenkins' in case of http://github.com/jenkinsci/jenkins
*/
public String getName() {
return name;
}
public boolean hasPullAccess() {
return permissions!=null && permissions.pull;
}
public boolean hasPushAccess() {
return permissions!=null && permissions.push;
}
public boolean hasAdminAccess() {
return permissions!=null && permissions.admin;
}
/**
* Gets the primary programming language.
*/
public String getLanguage() {
return language;
}
public GHUser getOwner() throws IOException {
return root.getUser(owner);
return root.getUser(owner.login); // because 'owner' isn't fully populated
}
public GHIssue getIssue(int id) throws IOException {
return root.retrieve().to("/repos/" + owner.login + "/" + name + "/issues/" + id, GHIssue.class).wrap(this);
}
public GHIssueBuilder createIssue(String title) {
return new GHIssueBuilder(this,title);
}
public List<GHIssue> getIssues(GHIssueState state) throws IOException {
return root.retrieve("/issues/list/" + owner + "/" + name + "/" + state.toString().toLowerCase(), JsonIssues.class).wrap(this);
return Arrays.asList(GHIssue.wrap(root.retrieve().to("/repos/" + owner.login + "/" + name + "/issues?state=" + state.toString().toLowerCase(), GHIssue[].class), this));
}
protected String getOwnerName() {
return owner;
return owner.login;
}
public boolean hasIssues() {
@@ -127,6 +182,11 @@ public class GHRepository {
return open_issues;
}
/**
*
* @return
* null if the repository was never pushed at.
*/
public Date getPushedAt() {
return GitHub.parseDate(pushed_at);
}
@@ -135,16 +195,27 @@ public class GHRepository {
return GitHub.parseDate(created_at);
}
/**
* Returns the primary branch you'll configure in the "Admin > Options" config page.
*
* @return
* This field is null until the user explicitly configures the master branch.
*/
public String getMasterBranch() {
return master_branch;
}
public int getSize() {
return size;
}
/**
* Gets the collaborators on this repository.
* This set always appear to include the owner.
*/
public Set<GHUser> getCollaborators() throws IOException {
Set<GHUser> r = new HashSet<GHUser>();
for (String u : root.retrieve("/repos/show/"+owner+"/"+name+"/collaborators",JsonCollaborators.class).collaborators)
r.add(root.getUser(u));
return Collections.unmodifiableSet(r);
@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));
}
/**
@@ -152,16 +223,17 @@ public class GHRepository {
* This method deviates from the principle of this library but it works a lot faster than {@link #getCollaborators()}.
*/
public Set<String> getCollaboratorNames() throws IOException {
Set<String> r = new HashSet<String>(root.retrieve("/repos/show/"+owner+"/"+name+"/collaborators",JsonCollaborators.class).collaborators);
return Collections.unmodifiableSet(r);
Set<String> r = new HashSet<String>();
for (GHUser u : GHUser.wrap(root.retrieve().to("/repos/" + owner.login + "/" + name + "/collaborators", GHUser[].class),root))
r.add(u.login);
return r;
}
/**
* If this repository belongs to an organization, return a set of teams.
*/
public Set<GHTeam> getTeams() throws IOException {
return Collections.unmodifiableSet(root.retrieveWithAuth("/repos/show/"+owner+"/"+name+"/teams",JsonTeams.class).toSet(
root.getOrganization(owner)));
return Collections.unmodifiableSet(new HashSet<GHTeam>(Arrays.asList(GHTeam.wrapUp(root.retrieve().to("/repos/" + owner.login + "/" + name + "/teams", GHTeam[].class), root.getOrganization(owner.login)))));
}
public void addCollaborators(GHUser... users) throws IOException {
@@ -169,7 +241,7 @@ public class GHRepository {
}
public void addCollaborators(Collection<GHUser> users) throws IOException {
modifyCollaborators(users, "/add/");
modifyCollaborators(users, "PUT");
}
public void removeCollaborators(GHUser... users) throws IOException {
@@ -177,53 +249,68 @@ public class GHRepository {
}
public void removeCollaborators(Collection<GHUser> users) throws IOException {
modifyCollaborators(users, "/remove/");
modifyCollaborators(users, "DELETE");
}
private void modifyCollaborators(Collection<GHUser> users, String op) throws IOException {
private void modifyCollaborators(Collection<GHUser> users, String method) throws IOException {
verifyMine();
for (GHUser user : users) {
new Poster(root).withCredential().to("/repos/collaborators/"+name+ op +user.getLogin());
new Requester(root).method(method).to("/repos/" + owner.login + "/" + name + "/collaborators/" + user.getLogin());
}
}
public void setEmailServiceHook(String address) throws IOException {
WebClient wc = root.createWebClient();
HtmlPage pg = (HtmlPage)wc.getPage(getUrl()+"/admin");
HtmlInput email = (HtmlInput)pg.getElementById("Email_address");
email.setValueAttribute(address);
HtmlCheckBoxInput active = (HtmlCheckBoxInput)pg.getElementById("Email[active]");
active.setChecked(true);
Map<String, String> config = new HashMap<String, String>();
config.put("address", address);
new Requester(root).method("POST").with("name", "email").with("config", config).with("active", "true")
.to(String.format("/repos/%s/%s/hooks", owner.login, name));
}
final HtmlForm f = email.getEnclosingFormOrDie();
f.submit((HtmlButton) f.getElementsByTagName("button").get(0));
private void edit(String key, String value) throws IOException {
Requester requester = new Requester(root);
if (!key.equals("name"))
requester.with("name", name); // even when we don't change the name, we need to send it in
requester.with(key, value).method("PATCH").to("/repos/" + owner.login + "/" + name);
}
/**
* Enables or disables the issue tracker for this repository.
*/
public void enableIssueTracker(boolean v) throws IOException {
new Poster(root).withCredential().with("values[has_issues]",String.valueOf(v))
.to("/repos/show/" + owner + "/" + name);
edit("has_issues", String.valueOf(v));
}
/**
* Enables or disables Wiki for this repository.
*/
public void enableWiki(boolean v) throws IOException {
new Poster(root).withCredential().with("values[has_wiki]",String.valueOf(v))
.to("/repos/show/" + owner + "/" + name);
edit("has_wiki", String.valueOf(v));
}
public void enableDownloads(boolean v) throws IOException {
edit("has_downloads",String.valueOf(v));
}
/**
* Rename this repository.
*/
public void renameTo(String name) throws IOException {
edit("name",name);
}
public void setDescription(String value) throws IOException {
edit("description",value);
}
public void setHomepage(String value) throws IOException {
edit("homepage",value);
}
/**
* Deletes this repository.
*/
public void delete() throws IOException {
Poster poster = new Poster(root).withCredential();
String url = "/repos/delete/" + owner +"/"+name;
DeleteToken token = poster.to(url, DeleteToken.class);
poster.with("delete_token",token.delete_token).to(url);
new Requester(root).method("DELETE").to("/repos/" + owner.login + "/" + name);
}
/**
@@ -233,7 +320,7 @@ public class GHRepository {
* Newly forked repository that belong to you.
*/
public GHRepository fork() throws IOException {
return new Poster(root).withCredential().to("/repos/fork/" + owner + "/" + name, JsonRepository.class).wrap(root);
return new Requester(root).method("POST").to("/repos/" + owner.login + "/" + name + "/forks", GHRepository.class).wrap(root);
}
/**
@@ -243,55 +330,230 @@ public class GHRepository {
* Newly forked repository that belong to you.
*/
public GHRepository forkTo(GHOrganization org) throws IOException {
new Poster(root, V3).withCredential().to(String.format("/repos/%s/%s/forks?org=%s",owner,name,org.getLogin()));
return org.getRepository(name);
}
new Requester(root).to(String.format("/repos/%s/%s/forks?org=%s", owner.login, name, org.getLogin()));
/**
* Rename this repository.
*/
public void renameTo(String newName) throws IOException {
WebClient wc = root.createWebClient();
HtmlPage pg = (HtmlPage)wc.getPage(getUrl()+"/admin");
for (HtmlForm f : pg.getForms()) {
if (!f.getActionAttribute().endsWith("/rename")) continue;
// this API is asynchronous. we need to wait for a bit
for (int i=0; i<10; i++) {
GHRepository r = org.getRepository(name);
if (r!=null) return r;
try {
f.getInputByName("name").setValueAttribute(newName);
f.submit((HtmlButton)f.getElementsByTagName("button").get(0));
// overwrite fields
final GHRepository r = getOwner().getRepository(newName);
for (Field fi : getClass().getDeclaredFields()) {
if (Modifier.isStatic(fi.getModifiers())) continue;
fi.setAccessible(true);
try {
fi.set(this,fi.get(r));
} catch (IllegalAccessException e) {
throw (IllegalAccessError)new IllegalAccessError().initCause(e);
}
}
return;
} catch (ElementNotFoundException e) {
// continue
Thread.sleep(3000);
} catch (InterruptedException e) {
throw (IOException)new InterruptedIOException().initCause(e);
}
}
throw new IllegalArgumentException("Either you don't have the privilege to rename "+owner+'/'+name+" or there's a bug in HTML scraping");
throw new IOException(this+" was forked into "+org.getLogin()+" but can't find the new repository");
}
/**
* Retrieves a specified pull request.
*/
public GHPullRequest getPullRequest(int i) throws IOException {
return root.retrieveWithAuth("/pulls/" + owner + '/' + name + "/" + i, JsonPullRequest.class).wrap(this);
return root.retrieve().to("/repos/" + owner.login + '/' + name + "/pulls/" + i, GHPullRequest.class).wrapUp(this);
}
/**
* Retrieves all the pull requests of a particular state.
*
* @see #listPullRequests(GHIssueState)
*/
public List<GHPullRequest> getPullRequests(GHIssueState state) throws IOException {
return listPullRequests(state).asList();
}
/**
* Retrieves all the pull requests of a particular state.
*/
public List<GHPullRequest> getPullRequests(GHIssueState state) throws IOException {
return root.retrieveWithAuth("/pulls/"+owner+'/'+name+"/"+state.name().toLowerCase(Locale.ENGLISH),JsonPullRequests.class).wrap(this);
public PagedIterable<GHPullRequest> listPullRequests(final GHIssueState state) {
return new PagedIterable<GHPullRequest>() {
public PagedIterator<GHPullRequest> iterator() {
return new PagedIterator<GHPullRequest>(root.retrieve().asIterator(String.format("/repos/%s/%s/pulls?state=%s", owner.login, name, state.name().toLowerCase(Locale.ENGLISH)), GHPullRequest[].class)) {
@Override
protected void wrapUp(GHPullRequest[] page) {
for (GHPullRequest pr : page)
pr.wrap(GHRepository.this);
}
};
}
};
}
/**
* Retrieves the currently configured hooks.
*/
public List<GHHook> getHooks() throws IOException {
List<GHHook> list = new ArrayList<GHHook>(Arrays.asList(
root.retrieve().to(String.format("/repos/%s/%s/hooks", owner.login, name), GHHook[].class)));
for (GHHook h : list)
h.wrap(this);
return list;
}
public GHHook getHook(int id) throws IOException {
return root.retrieve().to(String.format("/repos/%s/%s/hooks/%d", owner.login, name, id), GHHook.class).wrap(this);
}
/**
* Gets a comparison between 2 points in the repository. This would be similar
* to calling <tt>git log id1...id2</tt> against a local repository.
* @param id1 an identifier for the first point to compare from, this can be a sha1 ID (for a commit, tag etc) or a direct tag name
* @param id2 an identifier for the second point to compare to. Can be the same as the first point.
* @return the comparison output
* @throws IOException on failure communicating with GitHub
*/
public GHCompare getCompare(String id1, String id2) throws IOException {
GHCompare compare = root.retrieve().to(String.format("/repos/%s/%s/compare/%s...%s", owner.login, name, id1, id2), GHCompare.class);
return compare.wrap(this);
}
public GHCompare getCompare(GHCommit id1, GHCommit id2) throws IOException {
return getCompare(id1.getSHA1(),id2.getSHA1());
}
public GHCompare getCompare(GHBranch id1, GHBranch id2) throws IOException {
return getCompare(id1.getName(),id2.getName());
}
/**
* Retrieves all refs for the github repository.
* @return an array of GHRef elements coresponding with the refs in the remote repository.
* @throws IOException on failure communicating with GitHub
*/
public GHRef[] getRefs() throws IOException {
return root.retrieve().to(String.format("/repos/%s/%s/git/refs", owner.login, name), GHRef[].class);
}
/**
* Retrienved all refs of the given type for the current GitHub repository.
* @param refType the type of reg to search for e.g. <tt>tags</tt> or <tt>commits</tt>
* @return an array of all refs matching the request type
* @throws IOException on failure communicating with GitHub, potentially due to an invalid ref type being requested
*/
public GHRef[] getRefs(String refType) throws IOException {
return root.retrieve().to(String.format("/repos/%s/%s/git/refs/%s", owner.login, name, refType), GHRef[].class);
}
/**
* Gets a commit object in this repository.
*/
public GHCommit getCommit(String sha1) throws IOException {
GHCommit c = commits.get(sha1);
if (c==null) {
c = root.retrieve().to(String.format("/repos/%s/%s/commits/%s", owner.login, name, sha1), GHCommit.class).wrapUp(this);
commits.put(sha1,c);
}
return c;
}
/**
* Lists all the commits.
*/
public PagedIterable<GHCommit> listCommits() {
return new PagedIterable<GHCommit>() {
public PagedIterator<GHCommit> iterator() {
return new PagedIterator<GHCommit>(root.retrieve().asIterator(String.format("/repos/%s/%s/commits", owner.login, name), GHCommit[].class)) {
protected void wrapUp(GHCommit[] page) {
for (GHCommit c : page)
c.wrapUp(GHRepository.this);
}
};
}
};
}
/**
* Lists up all the commit comments in this repository.
*/
public PagedIterable<GHCommitComment> listCommitComments() {
return new PagedIterable<GHCommitComment>() {
public PagedIterator<GHCommitComment> iterator() {
return new PagedIterator<GHCommitComment>(root.retrieve().asIterator(String.format("/repos/%s/%s/comments", owner.login, name), GHCommitComment[].class)) {
@Override
protected void wrapUp(GHCommitComment[] page) {
for (GHCommitComment c : page)
c.wrap(GHRepository.this);
}
};
}
};
}
/**
* Lists all the commit statues attached to the given commit, newer ones first.
*/
public PagedIterable<GHCommitStatus> listCommitStatuses(final String sha1) throws IOException {
return new PagedIterable<GHCommitStatus>() {
public PagedIterator<GHCommitStatus> iterator() {
return new PagedIterator<GHCommitStatus>(root.retrieve().asIterator(String.format("/repos/%s/%s/statuses/%s", owner.login, name, sha1), GHCommitStatus[].class)) {
@Override
protected void wrapUp(GHCommitStatus[] page) {
for (GHCommitStatus c : page)
c.wrapUp(root);
}
};
}
};
}
/**
* Gets the last status of this commit, which is what gets shown in the UI.
*/
public GHCommitStatus getLastCommitStatus(String sha1) throws IOException {
List<GHCommitStatus> v = listCommitStatuses(sha1).asList();
return v.isEmpty() ? null : v.get(0);
}
/**
* Creates a commit status
*
* @param targetUrl
* Optional parameter that points to the URL that has more details.
* @param description
* Optional short description.
*/
public GHCommitStatus createCommitStatus(String sha1, GHCommitState state, String targetUrl, String description) throws IOException {
return new Requester(root)
.with("state", state.name().toLowerCase(Locale.ENGLISH))
.with("target_url", targetUrl)
.with("description", description)
.to(String.format("/repos/%s/%s/statuses/%s",owner.login,this.name,sha1),GHCommitStatus.class).wrapUp(root);
}
/**
*
* See https://api.github.com/hooks for possible names and their configuration scheme.
* TODO: produce type-safe binding
*
* @param name
* Type of the hook to be created. See https://api.github.com/hooks for possible names.
* @param config
* The configuration hash.
* @param events
* Can be null. Types of events to hook into.
*/
public GHHook createHook(String name, Map<String,String> config, Collection<GHEvent> events, boolean active) throws IOException {
List<String> ea = null;
if (events!=null) {
ea = new ArrayList<String>();
for (GHEvent e : events)
ea.add(e.name().toLowerCase(Locale.ENGLISH));
}
return new Requester(root)
.with("name", name)
.with("active", active)
._with("config", config)
._with("events",ea)
.to(String.format("/repos/%s/%s/hooks",owner.login,this.name),GHHook.class).wrap(this);
}
public GHHook createWebHook(URL url, Collection<GHEvent> events) throws IOException {
return createHook("web",Collections.singletonMap("url",url.toExternalForm()),events,true);
}
public GHHook createWebHook(URL url) throws IOException {
return createWebHook(url,null);
}
// this is no different from getPullRequests(OPEN)
@@ -303,13 +565,16 @@ public class GHRepository {
// }
private void verifyMine() throws IOException {
if (!root.login.equals(owner))
throw new IOException("Operation not applicable to a repository owned by someone else: "+owner);
if (!root.login.equals(owner.login))
throw new IOException("Operation not applicable to a repository owned by someone else: "+owner.login);
}
/**
* Returns a set that represents the post-commit hook URLs.
* The returned set is live, and changes made to them are reflected to GitHub.
*
* @deprecated
* Use {@link #getHooks()} and {@link #createHook(String, Map, Collection, boolean)}
*/
public Set<URL> getPostCommitHooks() {
return postCommitHooks;
@@ -321,15 +586,11 @@ public class GHRepository {
private final Set<URL> postCommitHooks = new AbstractSet<URL>() {
private List<URL> getPostCommitHooks() {
try {
verifyMine();
HtmlForm f = getForm();
List<URL> r = new ArrayList<URL>();
for (HtmlInput i : f.getInputsByName("urls[]")) {
String v = i.getValueAttribute();
if (v.length()==0) continue;
r.add(new URL(v));
for (GHHook h : getHooks()) {
if (h.getName().equals("web")) {
r.add(new URL(h.getConfig().get("url")));
}
}
return r;
} catch (IOException e) {
@@ -350,22 +611,7 @@ public class GHRepository {
@Override
public boolean add(URL url) {
try {
String u = url.toExternalForm();
verifyMine();
HtmlForm f = getForm();
List<HtmlInput> controls = f.getInputsByName("urls[]");
for (HtmlInput i : controls) {
String v = i.getValueAttribute();
if (v.length()==0) continue;
if (v.equals(u))
return false; // already there
}
controls.get(controls.size()-1).setValueAttribute(u);
f.submit(null);
createWebHook(url);
return true;
} catch (IOException e) {
throw new GHException("Failed to update post-commit hooks",e);
@@ -373,43 +619,87 @@ public class GHRepository {
}
@Override
public boolean remove(Object o) {
public boolean remove(Object url) {
try {
String u = ((URL)o).toExternalForm();
verifyMine();
HtmlForm f = getForm();
List<HtmlInput> controls = f.getInputsByName("urls[]");
for (HtmlInput i : controls) {
String v = i.getValueAttribute();
if (v.length()==0) continue;
if (v.equals(u)) {
i.setValueAttribute("");
f.submit(null);
String _url = ((URL)url).toExternalForm();
for (GHHook h : getHooks()) {
if (h.getName().equals("web") && h.getConfig().get("url").equals(_url)) {
h.delete();
return true;
}
}
return false;
} catch (IOException e) {
throw new GHException("Failed to update post-commit hooks",e);
}
}
private HtmlForm getForm() throws IOException {
WebClient wc = root.createWebClient();
HtmlPage pg = (HtmlPage)wc.getPage(getUrl()+"/admin");
HtmlForm f = (HtmlForm) pg.getElementById("new_service");
return f;
}
};
/*package*/ GHRepository wrap(GitHub root) {
this.root = root;
return this;
}
/**
* Gets branches by {@linkplain GHBranch#getName() their names}.
*/
public Map<String,GHBranch> getBranches() throws IOException {
Map<String,GHBranch> r = new TreeMap<String,GHBranch>();
for (GHBranch p : root.retrieve().to(getApiTailUrl("branches"), GHBranch[].class)) {
p.wrap(this);
r.put(p.getName(),p);
}
return r;
}
/**
* @deprecated
* Use {@link #listMilestones(GHIssueState)}
*/
public Map<Integer, GHMilestone> getMilestones() throws IOException {
Map<Integer,GHMilestone> milestones = new TreeMap<Integer, GHMilestone>();
for (GHMilestone m : listMilestones(GHIssueState.OPEN)) {
milestones.put(m.getNumber(), m);
}
return milestones;
}
/**
* Lists up all the milestones in this repository.
*/
public PagedIterable<GHMilestone> listMilestones(final GHIssueState state) {
return new PagedIterable<GHMilestone>() {
public PagedIterator<GHMilestone> iterator() {
return new PagedIterator<GHMilestone>(root.retrieve().asIterator(getApiTailUrl("milestones?state="+state.toString().toLowerCase(Locale.ENGLISH)), GHMilestone[].class)) {
@Override
protected void wrapUp(GHMilestone[] page) {
for (GHMilestone c : page)
c.wrap(GHRepository.this);
}
};
}
};
}
public GHMilestone getMilestone(int number) throws IOException {
GHMilestone m = milestones.get(number);
if (m == null) {
m = root.retrieve().to(getApiTailUrl("milestones/" + number), GHMilestone.class);
m.owner = this;
m.root = root;
milestones.put(m.getNumber(), m);
}
return m;
}
public GHMilestone createMilestone(String title, String description) throws IOException {
return new Requester(root)
.with("title", title).with("description", description).method("POST").to(getApiTailUrl("milestones"), GHMilestone.class).wrap(this);
}
@Override
public String toString() {
return "Repository:"+owner+":"+name;
return "Repository:"+owner.login+":"+name;
}
@Override
@@ -421,9 +711,13 @@ public class GHRepository {
public boolean equals(Object obj) {
if (obj instanceof GHRepository) {
GHRepository that = (GHRepository) obj;
return this.owner.equals(that.owner)
return this.owner.login.equals(that.owner.login)
&& this.name.equals(that.name);
}
return false;
}
String getApiTailUrl(String tail) {
return "/repos/" + owner.login + "/" + name +'/'+tail;
}
}

View File

@@ -1,8 +1,11 @@
package org.kohsuke.github;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
/**
* A team in GitHub organization.
@@ -15,6 +18,18 @@ public class GHTeam {
protected /*final*/ GHOrganization org;
/*package*/ GHTeam wrapUp(GHOrganization owner) {
this.org = owner;
return this;
}
/*package*/ static GHTeam[] wrapUp(GHTeam[] teams, GHOrganization owner) {
for (GHTeam t : teams) {
t.wrapUp(owner);
}
return teams;
}
public String getName() {
return name;
}
@@ -31,33 +46,38 @@ public class GHTeam {
* Retrieves the current members.
*/
public Set<GHUser> getMembers() throws IOException {
return org.root.retrieveWithAuth(api("/members"),JsonUsersWithDetails.class).toSet(org.root);
return new HashSet<GHUser>(Arrays.asList(GHUser.wrap(org.root.retrieve().to(api("/members"), GHUser[].class), org.root)));
}
public Map<String,GHRepository> getRepositories() throws IOException {
return org.root.retrieveWithAuth(api("/repositories"),JsonRepositories.class).wrap(org.root);
GHRepository[] repos = org.root.retrieve().to(api("/repos"), GHRepository[].class);
Map<String,GHRepository> m = new TreeMap<String, GHRepository>();
for (GHRepository r : repos) {
m.put(r.getName(),r.wrap(org.root));
}
return m;
}
/**
* Adds a member to the team.
*/
public void add(GHUser u) throws IOException {
org.root.retrieveWithAuth(api("/members?name="+u.getLogin()),null, "POST");
org.root.retrieve().method("PUT").to(api("/members/" + u.getLogin()), null);
}
/**
* Removes a member to the team.
*/
public void remove(GHUser u) throws IOException {
org.root.retrieveWithAuth(api("/members?name="+u.getLogin()),null, "DELETE");
org.root.retrieve().method("DELETE").to(api("/members/" + u.getLogin()), null);
}
public void add(GHRepository r) throws IOException {
org.root.retrieveWithAuth(api("/repositories?name="+r.getOwnerName()+'/'+r.getName()),null, "POST");
org.root.retrieve().method("PUT").to(api("/repos/" + r.getOwnerName() + '/' + r.getName()), null);
}
public void remove(GHRepository r) throws IOException {
org.root.retrieveWithAuth(api("/repositories?name="+r.getOwnerName()+'/'+r.getName()),null, "DELETE");
org.root.retrieve().method("DELETE").to(api("/repos/" + r.getOwnerName() + '/' + r.getName()), null);
}
private String api(String tail) {

View File

@@ -23,7 +23,11 @@
*/
package org.kohsuke.github;
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/**
@@ -32,104 +36,71 @@ import java.util.Set;
* @author Kohsuke Kawaguchi
*/
public class GHUser extends GHPerson {
private String name,company,location,created_at,blog,email;
private int followers_count;
/**
* Gets the human-readable name of the user, like "Kohsuke Kawaguchi"
*/
public String getName() {
return name;
}
/**
* Gets the company name of this user, like "Sun Microsystems, Inc."
*/
public String getCompany() {
return company;
}
/**
* Gets the location of this user, like "Santa Clara, California"
*/
public String getLocation() {
return location;
}
public String getCreatedAt() {
return created_at;
}
/**
* Gets the blog URL of this user.
*/
public String getBlog() {
return blog;
}
/**
* Gets the login ID of this user, like 'kohsuke'
*/
public String getLogin() {
return login;
}
/**
* Gets the e-mail address of the user.
*/
public String getEmail() {
return email;
}
public int getPublicGistCount() {
return public_gist_count;
}
public int getPublicRepoCount() {
return public_repo_count;
}
public int getFollowingCount() {
return following_count;
}
/**
* What appears to be a GitHub internal unique number that identifies this user.
*/
public int getId() {
return id;
}
public int getFollowersCount() {
return followers_count;
}
/**
* Follow this user.
*/
public void follow() throws IOException {
new Poster(root).withCredential().to("/user/follow/"+login);
new Requester(root).method("PUT").to("/user/following/" + login);
}
/**
* Unfollow this user.
*/
public void unfollow() throws IOException {
new Poster(root).withCredential().to("/user/unfollow/"+login);
new Requester(root).method("DELETE").to("/user/following/" + login);
}
/**
* Lists the users that this user is following
*/
public Set<GHUser> getFollows() throws IOException {
return root.retrieve("/user/show/"+login+"/following",JsonUsers.class).toSet(root);
@WithBridgeMethods(Set.class)
public GHPersonSet<GHUser> getFollows() throws IOException {
GHUser[] followers = root.retrieve().to("/users/" + login + "/following", GHUser[].class);
return new GHPersonSet<GHUser>(Arrays.asList(wrap(followers,root)));
}
/**
* Lists the users who are following this user.
*/
public Set<GHUser> getFollowers() throws IOException {
return root.retrieve("/user/show/"+login+"/followers",JsonUsers.class).toSet(root);
@WithBridgeMethods(Set.class)
public GHPersonSet<GHUser> getFollowers() throws IOException {
GHUser[] followers = root.retrieve().to("/users/" + login + "/followers", GHUser[].class);
return new GHPersonSet<GHUser>(Arrays.asList(wrap(followers,root)));
}
/**
* Returns true if this user belongs to the specified organization.
*/
public boolean isMemberOf(GHOrganization org) {
return org.hasMember(this);
}
/**
* Returns true if this user belongs to the specified organization as a public member.
*/
public boolean isPublicMemberOf(GHOrganization org) {
return org.hasPublicMember(this);
}
/*package*/ static GHUser[] wrap(GHUser[] users, GitHub root) {
for (GHUser f : users)
f.root = root;
return users;
}
/**
* Gets the organization that this user belongs to publicly.
*/
@WithBridgeMethods(Set.class)
public GHPersonSet<GHOrganization> getOrganizations() throws IOException {
GHPersonSet<GHOrganization> orgs = new GHPersonSet<GHOrganization>();
Set<String> names = new HashSet<String>();
for (GHOrganization o : root.retrieve().to("/users/" + login + "/orgs", GHOrganization[].class)) {
if (names.add(o.getLogin())) // I've seen some duplicates in the data
orgs.add(root.getOrganization(o.getLogin()));
}
return orgs;
}
@Override

View File

@@ -23,32 +23,29 @@
*/
package org.kohsuke.github;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlForm;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
import org.apache.commons.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 sun.misc.BASE64Encoder;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.InterruptedIOException;
import java.net.HttpURLConnection;
import java.io.Reader;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.TimeZone;
import static org.codehaus.jackson.annotate.JsonAutoDetect.Visibility.*;
@@ -59,38 +56,78 @@ import static org.codehaus.jackson.annotate.JsonAutoDetect.Visibility.*;
*/
public class GitHub {
/*package*/ final String login;
/**
* Value of the authorization header to be sent with the request.
*/
/*package*/ final String encodedAuthorization;
/*package*/ final String password;
/*package*/ final String apiToken;
private final Map<String,GHUser> users = new HashMap<String, GHUser>();
private final Map<String,GHOrganization> orgs = new HashMap<String, GHOrganization>();
private String oauthAccessToken;
private GitHub(String login, String apiToken, String password) {
private final String apiUrl;
/**
* 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 oauthAccessToken, String password) throws IOException {
if (apiUrl.endsWith("/")) apiUrl = apiUrl.substring(0, apiUrl.length()-1); // normalize
this.apiUrl = apiUrl;
if (oauthAccessToken!=null) {
encodedAuthorization = "token "+oauthAccessToken;
} else {
if (password!=null) {
String authorization = (login + ':' + password);
encodedAuthorization = "Basic "+new String(Base64.encodeBase64(authorization.getBytes()));
} else {// anonymous access
encodedAuthorization = null;
}
}
if (login==null)
login = getMyself().getLogin();
this.login = login;
this.apiToken = apiToken;
this.password = password;
BASE64Encoder enc = new sun.misc.BASE64Encoder();
if (apiToken!=null || password!=null) {
String userpassword = password==null ? (login + "/token" + ":" + apiToken) : (login + ':'+password);
encodedAuthorization = enc.encode(userpassword.getBytes());
} else
encodedAuthorization = null;
}
private GitHub (String oauthAccessToken) throws IOException {
this.password = null;
this.encodedAuthorization = null;
this.oauthAccessToken = oauthAccessToken;
this.apiToken = oauthAccessToken;
this.login = getMyself().getLogin();
}
/**
* Obtains the credential from "~/.github"
*/
@@ -103,118 +140,113 @@ public class GitHub {
} finally {
IOUtils.closeQuietly(in);
}
return new GitHub(props.getProperty("login"),props.getProperty("token"),props.getProperty("password"));
return new GitHub(GITHUB_URL,props.getProperty("login"), props.getProperty("oauth"),props.getProperty("password"));
}
public static GitHub connect(String login, String apiToken) throws IOException {
return new GitHub(login,apiToken,null);
/**
* Version that connects to GitHub Enterprise.
*
* @param apiUrl
* The URL of GitHub (or GitHub enterprise) API endpoint, such as "https://api.github.com" or
* "http://ghe.acme.com/api/v3". Note that GitHub Enterprise has <tt>/api/v3</tt> in the URL.
* For historical reasons, this parameter still accepts the bare domain name, but that's considered deprecated.
*/
public static GitHub connectToEnterprise(String apiUrl, String oauthAccessToken) throws IOException {
return connectUsingOAuth(apiUrl, oauthAccessToken);
}
public static GitHub connect(String login, String apiToken, String password) throws IOException {
return new GitHub(login,apiToken,password);
public static GitHub connectToEnterprise(String apiUrl, String login, String password) throws IOException {
return new GitHub(apiUrl, login, null, password);
}
public static GitHub connectUsingOAuth (String accessToken) throws IOException {
return new GitHub(accessToken);
public static GitHub connect(String login, String oauthAccessToken) throws IOException {
return new GitHub(login,oauthAccessToken,null);
}
/**
* @deprecated
* Either OAuth token or password is sufficient, so there's no point in passing both.
* Use {@link #connectUsingPassword(String, String)} or {@link #connectUsingOAuth(String)}.
*/
public static GitHub connect(String login, String oauthAccessToken, String password) throws IOException {
return new GitHub(login,oauthAccessToken,password);
}
public static GitHub connectUsingPassword(String login, String password) throws IOException {
return new GitHub(login,null,password);
}
public static GitHub connectUsingOAuth(String oauthAccessToken) throws IOException {
return new GitHub(null, oauthAccessToken, null);
}
public static GitHub connectUsingOAuth(String githubServer, String oauthAccessToken) throws IOException {
return new GitHub(githubServer,null, oauthAccessToken,null);
}
/**
* Connects to GitHub anonymously.
*
* All operations that requires authentication will fail.
*/
public static GitHub connectAnonymously() {
public static GitHub connectAnonymously() throws IOException {
return new GitHub(null,null,null);
}
/*package*/ void requireCredential() {
if ((login==null || encodedAuthorization==null) && oauthAccessToken == null)
if (login==null || encodedAuthorization==null)
throw new IllegalStateException("This operation requires a credential but none is given to the GitHub constructor");
}
/*package*/ URL getApiURL(ApiVersion v, String tailApiUrl) throws IOException {
if (oauthAccessToken != null) {
// append the access token
tailApiUrl = tailApiUrl + "?access_token=" + oauthAccessToken;
}
return new URL(v.url+tailApiUrl);
}
/*package*/ <T> T retrieve(String tailApiUrl, Class<T> type) throws IOException {
return _retrieve(tailApiUrl, type, "GET", false);
}
/*package*/ <T> T retrieveWithAuth(String tailApiUrl, Class<T> type) throws IOException {
return retrieveWithAuth(tailApiUrl,type,"GET");
}
/*package*/ <T> T retrieveWithAuth(String tailApiUrl, Class<T> type, String method) throws IOException {
return _retrieve(tailApiUrl, type, method, true);
}
private <T> T _retrieve(String tailApiUrl, Class<T> type, String method, boolean withAuth) throws IOException {
while (true) {// loop while API rate limit is hit
HttpURLConnection uc = (HttpURLConnection) getApiURL(ApiVersion.V2,tailApiUrl).openConnection();
if (withAuth && this.oauthAccessToken == null)
uc.setRequestProperty("Authorization", "Basic " + encodedAuthorization);
uc.setRequestMethod(method);
try {
InputStreamReader r = new InputStreamReader(uc.getInputStream(), "UTF-8");
if (type==null) {
String data = IOUtils.toString(r);
return null;
}
return MAPPER.readValue(r,type);
} catch (IOException e) {
handleApiError(e,uc);
/*package*/ URL getApiURL(String tailApiUrl) throws IOException {
if (tailApiUrl.startsWith("/")) {
if ("github.com".equals(apiUrl)) {// backward compatibility
return new URL(GITHUB_URL + tailApiUrl);
} else {
return new URL(apiUrl + tailApiUrl);
}
} else {
return new URL(tailApiUrl);
}
}
/*package*/ Requester retrieve() {
return new Requester(this).method("GET");
}
/**
* If the error is because of the API limit, wait 10 sec and return normally.
* Otherwise throw an exception reporting an error.
* Gets the current rate limit.
*/
/*package*/ void handleApiError(IOException e, HttpURLConnection uc) throws IOException {
if ("0".equals(uc.getHeaderField("X-RateLimit-Remaining"))) {
// API limit reached. wait 10 secs and return normally
try {
Thread.sleep(10000);
return;
} catch (InterruptedException _) {
throw (InterruptedIOException)new InterruptedIOException().initCause(e);
}
}
throw (IOException)new IOException(IOUtils.toString(uc.getErrorStream(),"UTF-8")).initCause(e);
public GHRateLimit getRateLimit() throws IOException {
return retrieve().to("/rate_limit", JsonRateLimit.class).rate;
}
/**
* Obtains the object that represents the named user.
*/
public GHUser getUser(String login) throws IOException {
GHUser u = users.get(login);
if (u==null) {
if (oauthAccessToken != null) {
u = retrieve("/user/show",JsonUser.class).user;
u.root = this;
users.put(u.getLogin(),u);
}
else {
u = retrieve("/user/show/"+login,JsonUser.class).user;
u.root = this;
users.put(login,u);
}
}
* Gets the {@link GHUser} that represents yourself.
*/
@WithBridgeMethods(GHUser.class)
public GHMyself getMyself() throws IOException {
requireCredential();
GHMyself u = retrieve().to("/user", GHMyself.class);
u.root = this;
users.put(u.getLogin(), u);
return u;
}
}
/**
* Obtains the object that represents the named user.
*/
public GHUser getUser(String login) throws IOException {
GHUser u = users.get(login);
if (u == null) {
u = retrieve().to("/users/" + login, GHUser.class);
u.root = this;
users.put(u.getLogin(), u);
}
return u;
}
/**
* Interns the given {@link GHUser}.
@@ -232,23 +264,60 @@ public class GitHub {
public GHOrganization getOrganization(String name) throws IOException {
GHOrganization o = orgs.get(name);
if (o==null) {
o = retrieve("/organizations/"+name,JsonOrganization.class).organization;
o.root = this;
o = retrieve().to("/orgs/" + name, GHOrganization.class).wrapUp(this);
orgs.put(name,o);
}
return o;
}
public Map<String, GHOrganization> getMyOrganizations() throws IOException {
return retrieveWithAuth("/organizations",JsonOrganizations.class).wrap(this);
}
/**
* Gets the {@link GHUser} that represents yourself.
* Gets the repository object from 'user/reponame' string that GitHub calls as "repository name"
*
* @see GHRepository#getName()
*/
public GHUser getMyself() throws IOException {
requireCredential();
return getUser(login);
public GHRepository getRepository(String name) throws IOException {
String[] tokens = name.split("/");
return retrieve().to("/repos/" + tokens[0] + '/' + tokens[1], GHRepository.class).wrap(this);
}
/**
* This method returns a shallowly populated organizations.
*
* To retrieve full organization details, you need to call {@link #getOrganization(String)}
* TODO: make this automatic.
*/
public Map<String, GHOrganization> getMyOrganizations() throws IOException {
GHOrganization[] orgs = retrieve().to("/user/orgs", GHOrganization[].class);
Map<String, GHOrganization> r = new HashMap<String, GHOrganization>();
for (GHOrganization o : orgs) {
// don't put 'o' into orgs because they are shallow
r.put(o.getLogin(),o.wrapUp(this));
}
return r;
}
/**
* 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);
}
/**
* Parses the GitHub event object.
*
* This is primarily intended for receiving a POST HTTP call from a hook.
* Unfortunately, hook script payloads aren't self-descriptive, so you need
* to know the type of the payload you are expecting.
*/
public <T extends GHEventPayload> T parseEventPayload(Reader r, Class<T> type) throws IOException {
T t = MAPPER.readValue(r, type);
t.wrapUp(this);
return t;
}
/**
@@ -258,35 +327,40 @@ public class GitHub {
* Newly created repository.
*/
public GHRepository createRepository(String name, String description, String homepage, boolean isPublic) throws IOException {
return new Poster(this).withCredential()
Requester requester = new Requester(this)
.with("name", name).with("description", description).with("homepage", homepage)
.with("public", isPublic ? 1 : 0).to("/repos/create", JsonRepository.class).wrap(this);
.with("public", isPublic ? 1 : 0);
return requester.method("POST").to("/user/repos", GHRepository.class).wrap(this);
}
/**
* Creates a new authorization.
*
* The token created can be then used for {@link GitHub#connectUsingOAuth(String)} in the future.
*
* @see <a href="http://developer.github.com/v3/oauth/#create-a-new-authorization">Documentation</a>
*/
public GHAuthorization createToken(Collection<String> scope, String note, String noteUrl) throws IOException{
Requester requester = new Requester(this)
.with("scopes", scope)
.with("note", note)
.with("note_url", noteUrl);
return requester.method("POST").to("/authorizations", GHAuthorization.class).wrap(this);
}
/**
* Ensures that the credential is valid.
*/
public boolean isCredentialValid() throws IOException {
try {
retrieveWithAuth("/user/show",JsonUser.class);
retrieve().to("/user", GHUser.class);
return true;
} catch (IOException e) {
return false;
}
}
WebClient createWebClient() throws IOException {
WebClient wc = new WebClient();
wc.setJavaScriptEnabled(false);
wc.setCssEnabled(false);
HtmlPage pg = (HtmlPage)wc.getPage("https://github.com/login");
HtmlForm f = pg.getForms().get(0);
f.getInputByName("login").setValueAttribute(login);
f.getInputByName("password").setValueAttribute(password);
f.submit();
return wc;
}
/*package*/ static URL parseURL(String s) {
try {
return s==null ? null : new URL(s);
@@ -296,9 +370,12 @@ public class GitHub {
}
/*package*/ static Date parseDate(String timestamp) {
if (timestamp==null) return null;
for (String f : TIME_FORMATS) {
try {
return new SimpleDateFormat(f).parse(timestamp);
SimpleDateFormat df = new SimpleDateFormat(f);
df.setTimeZone(TimeZone.getTimeZone("GMT"));
return df.parse(timestamp);
} catch (ParseException e) {
// try next
}
@@ -314,4 +391,6 @@ public class GitHub {
MAPPER.setVisibilityChecker(new Std(NONE, NONE, NONE, NONE, ANY));
MAPPER.getDeserializationConfig().set(Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
private static final String GITHUB_URL = "https://api.github.com";
}

View File

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

View File

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

View File

@@ -1,16 +0,0 @@
package org.kohsuke.github;
import java.util.List;
/**
* @author Kohsuke Kawaguchi
*/
class JsonIssueComments {
List<GHIssueComment> comments;
List<GHIssueComment> wrap(GHIssue owner) {
for (GHIssueComment c : comments)
c.owner = owner;
return comments;
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,6 +3,6 @@ package org.kohsuke.github;
/**
* @author Kohsuke Kawaguchi
*/
class JsonOrganization {
public GHOrganization organization;
class JsonRateLimit {
GHRateLimit rate;
}

View File

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

View File

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

View File

@@ -1,13 +0,0 @@
package org.kohsuke.github;
/**
* @author Kohsuke Kawaguchi
*/
class JsonTeam {
public GHTeam team;
GHTeam wrap(GHOrganization org) {
team.org = org;
return team;
}
}

View File

@@ -1,32 +0,0 @@
package org.kohsuke.github;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
/**
* @author Kohsuke Kawaguchi
*/
class JsonTeams {
public List<GHTeam> teams;
Map<String, GHTeam> toMap(GHOrganization org) {
Map<String, GHTeam> r = new TreeMap<String, GHTeam>();
for (GHTeam t : teams) {
t.org = org;
r.put(t.getName(),t);
}
return r;
}
Set<GHTeam> toSet(GHOrganization org) {
Set<GHTeam> r = new HashSet<GHTeam>();
for (GHTeam t : teams) {
t.org = org;
r.add(t);
}
return r;
}
}

View File

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

View File

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

View File

@@ -1,20 +0,0 @@
package org.kohsuke.github;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* @author Kohsuke Kawaguchi
*/
class JsonUsersWithDetails {
public List<GHUser> users;
public Set<GHUser> toSet(GitHub root) throws IOException {
Set<GHUser> r = new HashSet<GHUser>();
for (GHUser u : users)
r.add(root.getUser(u));
return r;
}
}

View File

@@ -0,0 +1,24 @@
package org.kohsuke.github;
import java.util.ArrayList;
import java.util.List;
/**
* {@link Iterable} that returns {@link PagedIterator}
*
* @author Kohsuke Kawaguchi
*/
public abstract class PagedIterable<T> implements Iterable<T> {
public abstract PagedIterator<T> iterator();
/**
* Eagerly walk {@link Iterable} and return the result in a list.
*/
public List<T> asList() {
List<T> r = new ArrayList<T>();
for(PagedIterator<T> i = iterator(); i.hasNext();) {
r.addAll(i.nextPage());
}
return r;
}
}

View File

@@ -0,0 +1,72 @@
package org.kohsuke.github;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
/**
* Iterator over a pagenated data source.
*
* Aside from the normal iterator operation, this method exposes {@link #nextPage()}
* that allows the caller to retrieve items per page.
*
* @author Kohsuke Kawaguchi
*/
public abstract class PagedIterator<T> implements Iterator<T> {
private final Iterator<T[]> base;
/**
* Current batch that we retrieved but haven't returned to the caller.
*/
private T[] current;
private int pos;
/*package*/ PagedIterator(Iterator<T[]> base) {
this.base = base;
}
protected abstract void wrapUp(T[] page);
public boolean hasNext() {
fetch();
return current!=null;
}
public T next() {
fetch();
if (current==null) throw new NoSuchElementException();
return current[pos++];
}
private void fetch() {
while (current==null || current.length<=pos) {
if (!base.hasNext()) {// no more to retrieve
current = null;
pos = 0;
return;
}
current = base.next();
wrapUp(current);
pos = 0;
}
// invariant at the end: there's some data to retrieve
}
public void remove() {
throw new UnsupportedOperationException();
}
/**
* Gets the next page worth of data.
*/
public List<T> nextPage() {
fetch();
List<T> r = Arrays.asList(current);
r = r.subList(pos,r.size());
current = null;
pos = 0;
return r;
}
}

View File

@@ -1,137 +0,0 @@
/*
* The MIT License
*
* Copyright (c) 2010, Kohsuke Kawaguchi
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.kohsuke.github;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import static org.kohsuke.github.GitHub.*;
/**
* Handles HTTP POST.
* @author Kohsuke Kawaguchi
*/
class Poster {
private final GitHub root;
private final List<String> args = new ArrayList<String>();
private boolean authenticate;
private final ApiVersion v;
Poster(GitHub root, ApiVersion v) {
this.root = root;
this.v = v;
}
Poster(GitHub root) {
this(root,ApiVersion.V2);
}
public Poster withCredential() {
root.requireCredential();
authenticate = true;
return this;
}
public Poster with(String key, int value) {
return with(key,String.valueOf(value));
}
public Poster with(String key, String value) {
if (value!=null) {
try {
args.add(URLEncoder.encode(key,"UTF-8")+'='+URLEncoder.encode(value,"UTF-8"));
} catch (UnsupportedEncodingException e) {
throw new Error(e); // impossible
}
}
return this;
}
public void to(String tailApiUrl) throws IOException {
to(tailApiUrl,null);
}
/**
* POSTs the form to the specified URL.
*
* @throws IOException
* if the server returns 4xx/5xx responses.
* @return
* {@link Reader} that reads the response.
*/
public <T> T to(String tailApiUrl, Class<T> type) throws IOException {
return to(tailApiUrl,type,"POST");
}
public <T> T to(String tailApiUrl, Class<T> type, String method) throws IOException {
while (true) {// loop while API rate limit is hit
HttpURLConnection uc = (HttpURLConnection) root.getApiURL(v,tailApiUrl).openConnection();
uc.setDoOutput(true);
uc.setRequestProperty("Content-type","application/x-www-form-urlencoded");
if (authenticate) {
if (v==ApiVersion.V3) {
if (root.password==null)
throw new IllegalArgumentException("V3 API doesn't support API token");
uc.setRequestProperty("Authorization", "Basic " + root.encodedAuthorization);
} else {
uc.setRequestProperty("Authorization", "Basic " + root.encodedAuthorization);
}
}
uc.setRequestMethod(method);
StringBuilder body = new StringBuilder();
for (String e : args) {
if (body.length()>0) body.append('&');
body.append(e);
}
OutputStreamWriter o = new OutputStreamWriter(uc.getOutputStream(), "UTF-8");
o.write(body.toString());
o.close();
try {
InputStreamReader r = new InputStreamReader(uc.getInputStream(), "UTF-8");
if (type==null) {
String data = IOUtils.toString(r);
return null;
}
return MAPPER.readValue(r,type);
} catch (IOException e) {
root.handleApiError(e,uc);
}
}
}
}

View File

@@ -0,0 +1,356 @@
/*
* The MIT License
*
* Copyright (c) 2010, Kohsuke Kawaguchi
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.kohsuke.github;
import org.apache.commons.io.IOUtils;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.InterruptedIOException;
import java.io.Reader;
import java.lang.reflect.Field;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.zip.GZIPInputStream;
import static org.kohsuke.github.GitHub.*;
/**
* A builder pattern for making HTTP call and parsing its output.
*
* @author Kohsuke Kawaguchi
*/
class Requester {
private final GitHub root;
private final List<Entry> args = new ArrayList<Entry>();
/**
* Request method.
*/
private String method = "POST";
private static class Entry {
String key;
Object value;
private Entry(String key, Object value) {
this.key = key;
this.value = value;
}
}
Requester(GitHub root) {
this.root = root;
}
/**
* Makes a request with authentication credential.
*/
@Deprecated
public Requester withCredential() {
// keeping it inline with retrieveWithAuth not to enforce the check
// root.requireCredential();
return this;
}
public Requester with(String key, int value) {
return _with(key, value);
}
public Requester with(String key, Integer value) {
if (value!=null)
_with(key, value.intValue());
return this;
}
public Requester with(String key, boolean value) {
return _with(key, value);
}
public Requester with(String key, String value) {
return _with(key, value);
}
public Requester with(String key, Collection<String> value) {
return _with(key, value);
}
public Requester with(String key, Map<String, String> value) {
return _with(key, value);
}
public Requester _with(String key, Object value) {
if (value!=null) {
args.add(new Entry(key,value));
}
return this;
}
public Requester method(String method) {
this.method = method;
return this;
}
public void to(String tailApiUrl) throws IOException {
to(tailApiUrl,null);
}
/**
* Sends a request to the specified URL, and parses the response into the given type via databinding.
*
* @throws IOException
* if the server returns 4xx/5xx responses.
* @return
* {@link Reader} that reads the response.
*/
public <T> T to(String tailApiUrl, Class<T> type) throws IOException {
return _to(tailApiUrl, type, null);
}
/**
* Like {@link #to(String, Class)} but updates an existing object instead of creating a new instance.
*/
public <T> T to(String tailApiUrl, T existingInstance) throws IOException {
return _to(tailApiUrl, null, existingInstance);
}
/**
* Short for {@code method(method).to(tailApiUrl,type)}
*/
@Deprecated
public <T> T to(String tailApiUrl, Class<T> type, String method) throws IOException {
return method(method).to(tailApiUrl,type);
}
private <T> T _to(String tailApiUrl, Class<T> type, T instance) throws IOException {
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");
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);
}
}
}
/**
* Loads pagenated resources.
*
* Every iterator call reports a new batch.
*/
/*package*/ <T> Iterator<T> asIterator(final String tailApiUrl, final Class<T> type) {
method("GET");
if (!args.isEmpty()) throw new IllegalStateException();
return new Iterator<T>() {
/**
* The next batch to be returned from {@link #next()}.
*/
T next;
/**
* URL of the next resource to be retrieved, or null if no more data is available.
*/
URL url;
{
try {
url = root.getApiURL(tailApiUrl);
} catch (IOException e) {
throw new Error(e);
}
}
public boolean hasNext() {
fetch();
return next!=null;
}
public T next() {
fetch();
T r = next;
if (r==null) throw new NoSuchElementException();
next = null;
return r;
}
public void remove() {
throw new UnsupportedOperationException();
}
private void fetch() {
if (next!=null) return; // already fetched
if (url==null) return; // no more data to fetch
try {
while (true) {// loop while API rate limit is hit
HttpURLConnection uc = setupConnection(url);
try {
next = parse(uc,type,null);
assert next!=null;
findNextURL(uc);
return;
} catch (IOException e) {
handleApiError(e,uc);
}
}
} catch (IOException e) {
throw new Error(e);
}
}
/**
* Locate the next page from the pagination "Link" tag.
*/
private void findNextURL(HttpURLConnection uc) throws MalformedURLException {
url = null; // start defensively
String link = uc.getHeaderField("Link");
if (link==null) return;
for (String token : link.split(", ")) {
if (token.endsWith("rel=\"next\"")) {
// found the next page. This should look something like
// <https://api.github.com/repos?page=3&per_page=100>; rel="next"
int idx = token.indexOf('>');
url = new URL(token.substring(1,idx));
return;
}
}
// no more "next" link. we are done.
}
};
}
private HttpURLConnection setupConnection(URL url) throws IOException {
HttpURLConnection uc = (HttpURLConnection) url.openConnection();
// if the authentication is needed but no credential is given, try it anyway (so that some calls
// that do work with anonymous access in the reduced form should still work.)
if (root.encodedAuthorization!=null)
uc.setRequestProperty("Authorization", root.encodedAuthorization);
try {
uc.setRequestMethod(method);
} catch (ProtocolException e) {
// JDK only allows one of the fixed set of verbs. Try to override that
try {
Field $method = HttpURLConnection.class.getDeclaredField("method");
$method.setAccessible(true);
$method.set(uc,method);
} catch (Exception x) {
throw (IOException)new IOException("Failed to set the custom verb").initCause(x);
}
}
uc.setRequestProperty("Accept-Encoding", "gzip");
return uc;
}
private <T> T parse(HttpURLConnection uc, Class<T> type, T instance) throws IOException {
InputStreamReader r = null;
try {
r = new InputStreamReader(wrapStream(uc, uc.getInputStream()), "UTF-8");
String data = IOUtils.toString(r);
if (type!=null)
return MAPPER.readValue(data,type);
if (instance!=null)
return MAPPER.readerForUpdating(instance).<T>readValue(data);
return null;
} finally {
IOUtils.closeQuietly(r);
}
}
/**
* Handles the "Content-Encoding" header.
*/
private InputStream wrapStream(HttpURLConnection uc, InputStream in) throws IOException {
String encoding = uc.getContentEncoding();
if (encoding==null || in==null) return in;
if (encoding.equals("gzip")) return new GZIPInputStream(in);
throw new UnsupportedOperationException("Unexpected Content-Encoding: "+encoding);
}
/**
* If the error is because of the API limit, wait 10 sec and return normally.
* Otherwise throw an exception reporting an error.
*/
/*package*/ void handleApiError(IOException e, HttpURLConnection uc) throws IOException {
if ("0".equals(uc.getHeaderField("X-RateLimit-Remaining"))) {
// API limit reached. wait 10 secs and return normally
try {
Thread.sleep(10000);
return;
} catch (InterruptedException _) {
throw (InterruptedIOException)new InterruptedIOException().initCause(e);
}
}
if (e instanceof FileNotFoundException)
throw e; // pass through 404 Not Found to allow the caller to handle it intelligently
InputStream es = wrapStream(uc, uc.getErrorStream());
try {
if (es!=null)
throw (IOException)new IOException(IOUtils.toString(es,"UTF-8")).initCause(e);
else
throw e;
} finally {
IOUtils.closeQuietly(es);
}
}
private Set<String> toSet(String s) {
Set<String> r = new HashSet<String>();
for (String t : s.split(","))
r.add(t.trim());
return r;
}
}

View File

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

View File

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

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

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

View File

@@ -1,6 +1,21 @@
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;
@@ -8,39 +23,204 @@ 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.connect().isCredentialValid());
assertFalse(GitHub.connect("totally","bogus").isCredentialValid());
assertTrue(gitHub.isCredentialValid());
assertFalse(GitHub.connect("totally", "bogus").isCredentialValid());
}
public void testIssueWithNoComment() throws IOException {
GHRepository repository = gitHub.getRepository("kohsuke/test");
List<GHIssueComment> v = repository.getIssue(4).getComments();
System.out.println(v);
assertTrue(v.isEmpty());
v = repository.getIssue(3).getComments();
System.out.println(v);
assertTrue(v.size() == 3);
}
public void testCreateIssue() throws IOException {
GHUser u = gitHub.getUser("kohsuke");
GHRepository r = u.getRepository("test");
GHMilestone someMilestone = r.listMilestones(GHIssueState.CLOSED).iterator().next();
GHIssue o = r.createIssue("testing").body("this is body").assignee(u).label("bug").label("question").milestone(someMilestone).create();
System.out.println(o.getUrl());
o.close();
}
public void testRateLimit() throws IOException {
System.out.println(gitHub.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 gh = GitHub.connect();
gh.getUser("kohsuke").getRepository("rubywm").forkTo(gh.getOrganization("jenkinsci"));
gitHub.getUser("kohsuke").getRepository("rubywm").forkTo(gitHub.getOrganization("jenkinsci"));
}
public void tryGetTeamsForRepo() throws Exception {
GitHub gh = GitHub.connect();
Set<GHTeam> o = gh.getOrganization("jenkinsci").getRepository("rubywm").getTeams();
Set<GHTeam> o = gitHub.getOrganization("jenkinsci").getRepository("rubywm").getTeams();
System.out.println(o);
}
public void testMembership() throws Exception {
GitHub gitHub = GitHub.connect();
Set<String> members = gitHub.getOrganization("jenkinsci").getRepository("violations-plugin").getCollaboratorNames();
System.out.println(members.contains("kohsuke"));
}
public void testMemberOrgs() throws Exception {
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 {
GitHub gitHub = GitHub.connect();
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);
@@ -66,7 +246,7 @@ public class AppTest extends TestCase {
// t.remove(gitHub.getMyself());
// System.out.println(t.getMembers());
// GHRepository r = GitHub.connect().getOrganization("HudsonLabs").createRepository("auto-test", "some description", "http://kohsuke.org/", "Plugin Developers", true);
// GHRepository r = gitHub.getOrganization("HudsonLabs").createRepository("auto-test", "some description", "http://kohsuke.org/", "Plugin Developers", true);
// r.
// GitHub hub = GitHub.connectAnonymously();
@@ -109,11 +289,6 @@ public class AppTest extends TestCase {
gitHub.getUser("kohsuke").getRepository("test").renameTo("test2");
}
private void tryOrgFork(GitHub gitHub) throws IOException {
GHOrganization o = gitHub.getOrganization("HudsonLabs");
System.out.println(gitHub.getUser("rtyler").getRepository("memcache-ada").forkTo(o).getUrl());
}
private void tryTeamCreation(GitHub gitHub) throws IOException {
GHOrganization o = gitHub.getOrganization("HudsonLabs");
GHTeam t = o.createTeam("auto team", Permission.PUSH);
@@ -129,10 +304,51 @@ public class AppTest extends TestCase {
System.out.println(hooks);
}
private void testOrganization(GitHub gitHub) throws IOException {
GHOrganization labs = gitHub.getOrganization("HudsonLabs");
GHTeam t = labs.getTeams().get("Core Developers");
public void 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");
t.add(labs.getRepository("xyz"));
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,32 @@
package org.kohsuke;
import org.kohsuke.github.GHEventPayload;
import org.kohsuke.github.GitHub;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.jetty.JettyRunner;
import java.io.IOException;
import java.io.StringReader;
/**
* App to test the hook script. You need some internet-facing server that can forward the request to you
* (typically via SSH reverse port forwarding.)
*
* @author Kohsuke Kawaguchi
*/
public class HookApp {
public static void main(String[] args) throws Exception {
// GitHub.connect().getMyself().getRepository("sandbox").createWebHook(
// new URL("http://173.203.118.45:18080/"), EnumSet.of(GHEvent.PULL_REQUEST));
JettyRunner jr = new JettyRunner(new HookApp());
jr.addHttpListener(8080);
jr.start();
}
public void doIndex(StaplerRequest req) throws IOException {
String str = req.getParameter("payload");
System.out.println(str);
GHEventPayload.PullRequest o = GitHub.connect().parseEventPayload(new StringReader(str),GHEventPayload.PullRequest.class);
System.out.println(o);
}
}

View File

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