Compare commits

...

213 Commits

Author SHA1 Message Date
Kohsuke Kawaguchi
c1bab63ebd [maven-release-plugin] prepare release github-api-1.95 2018-11-06 08:42:18 -08:00
Kohsuke Kawaguchi
40f012b03c rtyler no longer has 50 people he follows 2018-11-06 08:35:48 -08:00
Kohsuke Kawaguchi
a380059389 Merge branch 'master' of github.com:kohsuke/github-api 2018-11-06 08:16:06 -08:00
Kohsuke Kawaguchi
24b998ba2d Merge pull request #461 2018-11-06 08:15:11 -08:00
Kohsuke Kawaguchi
9a1bb09c9f Massaging the changes.
In particular, avoid the kind of addLabel() method that has lots of side
effect and do multiple things.
2018-11-06 08:14:12 -08:00
Kohsuke Kawaguchi
3ad66f8937 Merge pull request #468 from KostyaSha/fixMemLeak
Fix memory leak.
2018-11-06 07:56:15 -08:00
Kohsuke Kawaguchi
a6f3e7df55 Merge pull request #464 2018-11-06 07:49:17 -08:00
Kohsuke Kawaguchi
9345d3be31 Follow the convention in this library 2018-11-06 07:49:02 -08:00
Kohsuke Kawaguchi
8e85bf8839 Merge pull request #470 from recena/archived-attr
Added archived attribute in GHRepository
2018-10-29 08:28:00 -07:00
Manuel Recena
1012dcd194 Added archived attribute in GHRepository. Updated the parent POM 2018-10-25 11:24:46 +02:00
Kanstantsin Shautsou
70251ea11e Fix memory leak.
While repository object is active and code requests commits they are stored in Map.
GHCommit.files contains huge String[]/char[]  amount of data.
The same could be applied to Milestones.
2018-10-20 19:13:47 +03:00
I329802 (Xeric)
9381471fbd add request reviewers as attribute of GHPullRequest 2018-10-11 17:30:25 +08:00
Even Holthe
1c4b716f1a Add methods for adding/removing labels to GHIssue
Fixes #456
2018-10-01 23:29:15 +02:00
Kohsuke Kawaguchi
c8b0584127 [maven-release-plugin] prepare for next development iteration 2018-08-29 21:05:31 -07:00
Kohsuke Kawaguchi
5194a361f4 [maven-release-plugin] prepare release github-api-1.94 2018-08-29 21:05:21 -07:00
Kohsuke Kawaguchi
c44e5d2a87 findbugs fix 2018-08-29 21:00:24 -07:00
Kohsuke Kawaguchi
ee4d514b66 close an opened stream 2018-08-29 20:48:44 -07:00
Kohsuke Kawaguchi
863995cb50 findbugs warning fix 2018-08-29 20:48:36 -07:00
Kohsuke Kawaguchi
cbfe72a76e Fixed a broken test 2018-08-29 20:31:44 -07:00
Kohsuke Kawaguchi
4f17d3519c Merge pull request #411 from tadfisher/master
Add GHRepository.getRelease and GHRepository.getReleaseByTagName
2018-08-30 05:22:26 +02:00
Kohsuke Kawaguchi
3cfcad76ac Merge pull request #417 from twcurrie/tcurrie/revisions
Added release payload.
2018-08-30 05:21:24 +02:00
Kohsuke Kawaguchi
1ca6535811 Merge pull request #449 from martinvanzijl/issue_426_fix_nullptr_when_deleting_refs
Fix for issue #426. Fix null pointer when deleting refs.
2018-08-30 05:21:09 +02:00
Kohsuke Kawaguchi
53612ad2e4 Merge pull request #446 from daniel-beck/fix-page-size
Fix pagination for APIs that supported it ad hoc
2018-08-30 05:20:53 +02:00
Kohsuke Kawaguchi
4b799d264c Merge pull request #443 from Arrow768/GHEventPayload_Issue
Adds the GHEventPayload.Issue class
2018-08-30 05:20:26 +02:00
Kohsuke Kawaguchi
ca5594703a Merge pull request #439 from l3ender/master
Add support for repository searching by "topic"
2018-08-30 05:20:03 +02:00
Kohsuke Kawaguchi
7fc2d9dcca Merge remote-tracking branch 'origin/master' 2018-08-29 20:19:14 -07:00
Kohsuke Kawaguchi
c413fc1e30 Merge pull request #438 from jgangemi/jae/issue-434
- added overloaded 'uploadAsset' method
2018-08-30 05:18:51 +02:00
Kohsuke Kawaguchi
b5086c7759 Merge pull request #437 2018-08-29 20:18:21 -07:00
Kohsuke Kawaguchi
92a015ca4d Clear up import statements 2018-08-29 20:18:10 -07:00
Kohsuke Kawaguchi
3b2802e36d minor doc improvement 2018-08-29 20:18:00 -07:00
Kohsuke Kawaguchi
7bf23eaa15 Merge pull request #436 from jgangemi/jae/rm-exception
- remove unthrown IOException
2018-08-30 05:16:28 +02:00
Kohsuke Kawaguchi
9afd1c5ee8 Merge pull request #435 2018-08-29 20:15:08 -07:00
Kohsuke Kawaguchi
5e36377b36 It appears LOKI preview is done and all those methods are now final 2018-08-29 20:15:01 -07:00
Kohsuke Kawaguchi
c988df13a8 Use a short form 2018-08-29 20:15:01 -07:00
Kohsuke Kawaguchi
29a40d31b7 TAB -> space 2018-08-29 20:15:01 -07:00
Kohsuke Kawaguchi
ddc27e818b Let's not yet expose this new class because it's not final 2018-08-29 20:15:01 -07:00
Kohsuke Kawaguchi
3fa70ac841 doc improvement 2018-08-29 19:05:56 -07:00
Kohsuke Kawaguchi
b2b7dfaf37 Massaged the change to match the existing API design convention 2018-08-29 19:05:19 -07:00
Martin van Zijl
c309c2cf13 Fix for issue #426. Fix null pointer when deleting refs. 2018-08-13 12:36:15 +12:00
Daniel Beck
0ffcbdbd38 Fix pagination for APIs that supported it ad hoc 2018-07-17 11:27:54 +02:00
Werner
e368a17420 Adds the GHEventPayload.Issue class 2018-07-01 12:23:13 +02:00
Sharath
2fcfb2f67d address review comments in supporting updating content with sha 2018-06-12 18:26:20 -07:00
onoguera-ob
f68a85056e Update integration test. 2018-06-12 18:26:20 -07:00
Oliver Noguera
943f47d29d Add sha1 to updateFiles see
https://developer.github.com/v3/repos/contents/#update-a-file
2018-06-12 18:26:20 -07:00
l3ender
fd37a2c466 Add support for repository searching by "topic"
See https://help.github.com/articles/searching-repositories/#search-by-topic
2018-06-06 11:44:46 -05:00
Jae Gangemi
eacdd7afe8 - added overloaded 'uploadAsset' method 2018-05-30 08:18:25 -06:00
Rechi
fe5ea52cdf [feature] implement Repository Invitations API
fixes #374
2018-05-29 22:00:00 +02:00
Jae Gangemi
ca6d77cbb3 - remove unthrown IOException 2018-05-28 17:49:21 -06:00
Jae Gangemi
1145941d11 - add support for signed commits
- add support for required number of reviews
2018-05-27 15:33:58 -06:00
Kohsuke Kawaguchi
d61697a152 [maven-release-plugin] prepare for next development iteration 2018-05-01 07:56:38 -07:00
Kohsuke Kawaguchi
38b77a9c79 [maven-release-plugin] prepare release github-api-1.93 2018-05-01 07:56:27 -07:00
Kohsuke Kawaguchi
7d294ee8c2 Looks like release rollback didn't quite complete 2018-05-01 07:26:09 -07:00
Kohsuke Kawaguchi
33d9422d03 Merge pull request #422 from ggrell/fix-421-enum-case-issue
Fixes #421 - Enum case doesn't match for Pull Request Reviews
2018-05-01 07:19:42 -07:00
Kohsuke Kawaguchi
bb7302c23a Merge branch 'master' of github.com:kohsuke/github-api 2018-05-01 07:18:57 -07:00
Kohsuke Kawaguchi
d50ae63a5a Merge pull request #431 from Rechi/fixPRLabels
[fix] fetch labels with HTTP GET method
2018-05-01 07:18:43 -07:00
Kohsuke Kawaguchi
1961836e19 Merge pull request #427 from itepikin-smartling/master
Add support for previous_filename for file details in PR.
2018-05-01 07:17:32 -07:00
Kohsuke Kawaguchi
f2ed7c15ce Looks like the permission scheme changed on jenkinsci/violations-plugin 2018-05-01 07:12:30 -07:00
Kohsuke Kawaguchi
4dce173630 Extracted the List<GHUser>->List<String> out to Requester for reuse 2018-05-01 07:03:53 -07:00
Kohsuke Kawaguchi
86f868b2d4 Fixed compilation errors introduced by 8b38a20c18 2018-05-01 06:59:03 -07:00
Kohsuke Kawaguchi
363064f5c0 Merge branch 'master' of github.com:kohsuke/github-api 2018-04-30 19:58:33 -07:00
Kohsuke Kawaguchi
9d99ee9cfc Merge pull request #430 from twcurrie/tcurrie/requestReviewer
Added request reviewers function within GHPullRequest.
2018-04-30 19:58:19 -07:00
Rechi
db8969707d [fix] fetch labels with HTTP GET method 2018-04-06 10:00:00 +02:00
Trevor Currie
a24ac37dfd Added request reviewers function within GHPullRequest. 2018-03-28 23:33:08 -07:00
itepikin
1b04d471b3 Added support for previous_filename for file details in PR. 2018-03-22 11:16:09 +03:00
Gyuri Grell
9cc400a081 Fixes #421 - Enum case doesn't match for Pull Request Reviews
* Set Jackson to ignore case differences in enums.
2018-03-01 20:50:00 -05:00
Kohsuke Kawaguchi
e233aeec0c Merge pull request #410 from Limess/409/update-commons-lang
Update commons-lang to 3.7
2018-03-01 09:45:15 -08:00
Kohsuke Kawaguchi
5dfd621900 Merge pull request #420 from randomvariable/fix/tlsv12
OkHttpConnector: Enforce use of TLSv1.2 to match current Github and Github Enterprise TLS support.
2018-03-01 09:43:24 -08:00
Naadir Jeewa
f0f6a9988f OkHttpConnector: Enforce use of TLSv1.2 to match current Github
and Github Enterprise TLS support.
2018-02-28 09:36:15 +00:00
Trevor Currie
587438938c Added release payload. 2018-02-22 09:38:25 -08:00
Tad Fisher
75918c59cc Add GHRepository.getRelease and GHRepository.getReleaseByTagName
These implement the API endpoints for:

- GET /repos/:owner/:repo/releases/:id
- GET /repos/:owner/:repo/releases/tags/:tag
2018-02-05 14:30:24 -08:00
Charlie Briggs
8b38a20c18 Update commons-lang to 3.7
This fixes the vulnerabilities:
* https://issues.apache.org/jira/browse/LANG-1373
* https://issues.apache.org/jira/browse/LANG-805.
2018-01-23 11:00:58 +00:00
Kohsuke Kawaguchi
e7b76bfdc5 Doc improvements 2018-01-21 11:51:04 -08:00
Kohsuke Kawaguchi
3503ff6d36 Additional methods for issue comment 2018-01-21 11:50:22 -08:00
Kohsuke Kawaguchi
192e21a9fc [maven-release-plugin] prepare for next development iteration 2018-01-13 11:52:05 -08:00
Kohsuke Kawaguchi
24e288d584 [maven-release-plugin] prepare release github-api-1.92 2018-01-13 11:51:56 -08:00
Kohsuke Kawaguchi
0e5ffda5e5 Release failed due to javadoc errors 2018-01-13 11:45:57 -08:00
Kohsuke Kawaguchi
cdf6f18ec0 [maven-release-plugin] prepare for next development iteration 2018-01-13 10:36:53 -08:00
Kohsuke Kawaguchi
188245fa7f [maven-release-plugin] prepare release github-api-1.91 2018-01-13 10:36:45 -08:00
Kohsuke Kawaguchi
e10b747d6a Re-retried the object.
It looks like the format has changed a bit since this payload was
retrieved originally?
2018-01-13 10:30:48 -08:00
Kohsuke Kawaguchi
f2bb6a05a5 Merge pull request #384 2018-01-13 10:19:12 -08:00
Kohsuke Kawaguchi
f41da19db5 Further restoration of the compatibility 2018-01-13 09:54:14 -08:00
Kohsuke Kawaguchi
d0a56dbb21 Merge pull request #406
... with some further changes
2018-01-13 09:47:37 -08:00
Kohsuke Kawaguchi
acbf286e59 Massaged the changes
- Restored binary compatibility. The draft API has changed in some
  significant way when it became public, and the PR took the liberty to
  delete removed constants and method signatures. I brought them back so
  that older clients can keep functioning.

- Introduced a builder pattern to create PR review

- replying to a review comment should be an instance method, not a
  static method.

- GHPullRequestReview and GHPullRequestReviewDraft was not making sense
  to me, since GitHub API doesn't differentiate them. It creates a
  practical problem of not being able to submit a review comment unless
  you created the review comment in the same JVM session. That said, I
  don't understand the point of this two phase approach in the GitHub
  API to begin with!
2018-01-13 09:43:44 -08:00
Kohsuke Kawaguchi
0f7c160409 Based on issue #399, adjusting the behaviour.
This ends up changing the behaviour cloes to that of #394.
2018-01-12 21:33:03 -08:00
Kohsuke Kawaguchi
5113aacb89 Issue #403: Typo in method name 2018-01-12 21:28:37 -08:00
Kohsuke Kawaguchi
4e31636181 Allow the caller to wait for the mergeable state to change.
This approaches #394 differently. The problem with the original #394 is
that the supposed behaviour is only useful for people waiting for
`getMergeable()` to return non-null in a busy loop, yet it impacts all
the other methods of this object.
2018-01-12 21:25:30 -08:00
Kohsuke Kawaguchi
1e497d2c44 Merge branch 'master' into bridge-method-annotation 2018-01-12 21:13:49 -08:00
Kohsuke Kawaguchi
70bd4fa161 Merge pull request #379 2018-01-12 21:12:48 -08:00
Kohsuke Kawaguchi
65996050d5 Massaged changes to follow the convention 2018-01-12 21:12:40 -08:00
Kohsuke Kawaguchi
7f52031199 Merge branch 'master' of github.com:kohsuke/github-api 2018-01-12 21:07:11 -08:00
Kohsuke Kawaguchi
5430f3d33c Merge pull request #391 2018-01-12 21:06:55 -08:00
Kohsuke Kawaguchi
43075faaf8 By convention we use "listXyz" method names 2018-01-12 21:06:47 -08:00
Kohsuke Kawaguchi
f3a1272e31 Merge pull request #397 from mizoguche/set-milestone
Add GHIssue#setMilestone
2018-01-12 21:04:18 -08:00
Kohsuke Kawaguchi
41c028d4d9 Merge pull request #401 2018-01-12 21:00:56 -08:00
Kohsuke Kawaguchi
a17ce04552 Adjustment to compensate 2018-01-12 21:00:25 -08:00
Björn Häuser
d0b4652dcd Replace "new Error" with GHException
(rolled back some of the hunks from the original PR)
2018-01-12 19:44:25 -08:00
Kohsuke Kawaguchi
e66f71c76e Merge pull request #407 from notsudo/GHCreateRepositoryBuilder_Add_Merge_Settings
Adding merge settings to GHCreateRepositoryBuilder
2018-01-12 19:39:03 -08:00
Kohsuke Kawaguchi
6effd4b846 Merge pull request #396 from Rechi/fixGHPersonNullPointer
[fix] GHPerson: check if root is null
2018-01-12 08:45:02 -08:00
Timothy McNally
df861f5403 Adding methods to GHCreateRepositoryBuilder to allow setting the allowed merge methods for pull requests. 2018-01-09 17:32:54 -08:00
Sébastien Lesaint
e74346fed6 support create PR review in single API call & remove @Preview 2018-01-09 14:13:25 +01:00
Sébastien Lesaint
6961c467a6 create review comment reply from GHPullRequest 2018-01-09 14:13:25 +01:00
Sébastien Lesaint
892d305165 add Requester#with override for long value 2018-01-09 14:13:25 +01:00
Sébastien Lesaint
fa16261d7a position of pr comment can be null and original_position is not parsed
position of pr comment is null when comment is outdated (ie. not located in diff patch anymore)
2018-01-09 14:13:25 +01:00
Sébastien Lesaint
15991fd2f7 PullRequest review state and event do not have same values
this fixes parsing of response of WS call submitting a review to fail
when event of new review is REQUEST_CHANGES
also, specifying event when creating a pending PR review is useless and
providing it when submitting the review is enough
2018-01-09 14:13:25 +01:00
Michiaki Mizoguchi
c80b8f60f8 Add GHIssue#setMilestone 2017-11-16 12:42:52 +09:00
Rechi
ab6253cbd0 [fix] GHPerson: check if root is null 2017-11-09 22:41:49 +01:00
sg012265
35ba267115 Revert wildcard usage for imports 2017-10-30 15:38:19 -05:00
sg012265
ab24e6e1c1 Add get for all orgs 2017-10-30 15:32:25 -05:00
Kohsuke Kawaguchi
e25ae27a15 [maven-release-plugin] prepare for next development iteration 2017-10-28 15:54:44 -07:00
Kohsuke Kawaguchi
2b7c524908 [maven-release-plugin] prepare release github-api-1.90 2017-10-28 15:54:33 -07:00
Kohsuke Kawaguchi
0b069df9ce Pick up the version that fixes int->long adaption 2017-10-28 15:48:26 -07:00
Kohsuke Kawaguchi
60c9ba88ae Updated tests 2017-10-28 15:27:38 -07:00
Kohsuke Kawaguchi
cc2e60c84a Merge pull request #388 2017-10-28 09:01:01 -07:00
Kohsuke Kawaguchi
83c2c4e92e Fixed the broken design of how GHDeploymentStatus get exposed. 2017-10-28 08:59:41 -07:00
Kohsuke Kawaguchi
ab3d9e82ef A little better version of the bridge method 2017-10-28 08:43:48 -07:00
Kohsuke Kawaguchi
b6063dd534 Restored binary compatibility 2017-10-28 08:42:27 -07:00
Kohsuke Kawaguchi
e6754354e4 Merge pull request #389 2017-10-28 07:54:47 -07:00
Kohsuke Kawaguchi
4849619d67 None of the connectToEnterprise methods are preferrable so document accordingly 2017-10-28 07:54:41 -07:00
Kohsuke Kawaguchi
9b0ace242a Merge pull request #390 2017-10-28 07:45:14 -07:00
Kohsuke Kawaguchi
e94ba74058 Convention is to call these methods setXyz. Plus doc 2017-10-28 07:44:23 -07:00
Baptiste Mathus
569fa06d2d Labels: add method to update color 2017-10-25 14:06:28 +02:00
iraleigh
46dce17abc Fixed Typo 2017-10-24 14:53:23 -07:00
iraleigh
40d8f4a352 Fixed OAuth connection to enterprise API 2017-10-24 14:24:25 -07:00
Arne Burmeister
6415785220 boyscout: updated dependencies 2017-10-23 17:05:31 +02:00
Arne Burmeister
7735edeae8 extend id from int to long 2017-10-23 17:05:02 +02:00
Matt Nelson
0f81d1dbb3 Add support for pr review/review comment events 2017-10-05 18:14:15 -05:00
Anton Zagorskii
ae1ec8b558 Roles for team members 2017-09-20 13:22:30 +01:00
Jesse Glick
09c2b39530 bridge-method-annotation should be an optional dep. 2017-09-13 15:14:42 -04:00
Kohsuke Kawaguchi
b443e866f9 Added lock/unlock op and additional properties
Issue #355
2017-09-09 20:38:40 -07:00
Kohsuke Kawaguchi
d4404713a8 [maven-release-plugin] prepare for next development iteration 2017-09-09 13:28:40 -07:00
Kohsuke Kawaguchi
47409a9a99 [maven-release-plugin] prepare release github-api-1.89 2017-09-09 13:28:31 -07:00
Kohsuke Kawaguchi
60bfea2d3b Bug fix 2017-09-09 13:22:16 -07:00
Kohsuke Kawaguchi
d3ed8eaed5 Merge pull request #339 2017-09-09 13:07:18 -07:00
Kohsuke Kawaguchi
692dccf110 Massaging the change a bit 2017-09-09 13:03:18 -07:00
Kohsuke Kawaguchi
92caf98683 Reverting java1.6 change which I assume is accidental.
Not that I really care about Java5 but I think that change should
be done separatel & intentionally
2017-09-09 12:57:52 -07:00
Kohsuke Kawaguchi
6178d38895 connector usage is unsynchronized 2017-09-09 12:47:18 -07:00
Kohsuke Kawaguchi
40fb38a9ba Window focus problem 2017-09-09 12:45:58 -07:00
Kohsuke Kawaguchi
20e68d53fd Merge pull request #283 2017-09-09 12:45:02 -07:00
Kohsuke Kawaguchi
2d3557e049 Improved the intern logic
if the user record does not exist yet, there's no need to fetch that
eagerly, as they are fetched on demand via the populate() method.
2017-09-09 12:44:12 -07:00
Kohsuke Kawaguchi
d8f4bc7395 Added updater
This solves #331 differently
2017-09-09 12:31:10 -07:00
Kohsuke Kawaguchi
353f9bb809 pointless null check since the with method already does it 2017-09-09 12:23:40 -07:00
Kohsuke Kawaguchi
ccfe3ad4f7 Merge pull request #333 2017-09-09 12:17:39 -07:00
Kohsuke Kawaguchi
9012820c03 Massage the signature a bit.
AFAICT sha and merge_method are not mutually exclusive.
2017-09-09 12:17:21 -07:00
Kohsuke Kawaguchi
fe2af19e42 Unused constant 2017-09-09 12:15:21 -07:00
Kohsuke Kawaguchi
f721e053f1 Added convenience connector for OkHttp3
Note that the existing one needs to be kept for compatibility with OkHttp2
2017-09-09 12:11:36 -07:00
Kohsuke Kawaguchi
e6ad9feb84 Keeping Findbugs happy 2017-09-09 12:05:39 -07:00
Kohsuke Kawaguchi
635350c40e Additional naming consistency change 2017-09-09 12:02:44 -07:00
Kohsuke Kawaguchi
17edd33703 Reorganized imports following #337 2017-09-09 12:00:23 -07:00
Kohsuke Kawaguchi
b0f2a871c6 Merge pull request #337 2017-09-09 11:58:13 -07:00
Kohsuke Kawaguchi
8928a8a1dc Content type should be JSON by default when sending JSON.
This solves #350 a little differently.
2017-09-09 11:51:55 -07:00
Kohsuke Kawaguchi
2b6f37a6cc Merge pull request #361 2017-09-09 11:48:33 -07:00
Kohsuke Kawaguchi
f3a3b87861 Defined entry points 2017-09-09 11:48:25 -07:00
Kohsuke Kawaguchi
9cf6ee78d4 Pointless string conversion 2017-09-09 11:44:09 -07:00
Kohsuke Kawaguchi
bbc2f3962f Proper access control modifier 2017-09-09 11:44:02 -07:00
Kohsuke Kawaguchi
be49eb22d2 Merge pull request #368 2017-09-09 11:40:41 -07:00
Kohsuke Kawaguchi
fb47067215 Naming changes to emphasize that these are just traffic info 2017-09-09 11:40:28 -07:00
Kohsuke Kawaguchi
2c80ef178d Capture commonality between total and daily 2017-09-09 11:39:12 -07:00
Kohsuke Kawaguchi
9af8112148 Tightening up access control and use primitive type 2017-09-09 11:37:18 -07:00
Kohsuke Kawaguchi
57c36f437a [maven-release-plugin] prepare for next development iteration 2017-09-09 08:19:29 -07:00
Kohsuke Kawaguchi
5ed8a34566 [maven-release-plugin] prepare release github-api-1.88 2017-09-09 08:19:20 -07:00
Kohsuke Kawaguchi
ea8df9bd61 javadoc fix 2017-09-09 08:13:51 -07:00
Kohsuke Kawaguchi
b0c51e03b7 [maven-release-plugin] prepare for next development iteration 2017-09-09 08:06:27 -07:00
Kohsuke Kawaguchi
336924ef23 [maven-release-plugin] prepare release github-api-1.87 2017-09-09 08:06:18 -07:00
Kohsuke Kawaguchi
ad28ca4a90 Use string constant like other previews 2017-09-09 07:59:31 -07:00
Kohsuke Kawaguchi
aebbe86cfc Keeping findbugs happy 2017-09-09 07:57:49 -07:00
Kohsuke Kawaguchi
df9faf4943 Auto-retry flaky tests 2017-09-08 16:09:41 -07:00
Kohsuke Kawaguchi
3e295b6be4 Merge branch 'master' of github.com:kohsuke/github-api 2017-09-08 15:52:31 -07:00
Kohsuke Kawaguchi
8dd6dbf995 getRef never returns null 2017-09-08 15:52:11 -07:00
Kohsuke Kawaguchi
612139f2ff Merge pull request #375 from stephenc/tag-object-support
Add basic support for tag objects
2017-09-08 15:49:33 -07:00
Kohsuke Kawaguchi
240bcabb76 Merge pull request #351 2017-09-08 14:16:45 -07:00
Kohsuke Kawaguchi
cda27d5963 This field should be still final 2017-09-08 14:16:09 -07:00
Kohsuke Kawaguchi
2f8c3997f7 Restored signature of the previous enableProtection() method 2017-09-08 14:13:21 -07:00
Kohsuke Kawaguchi
46b89a48db Merge Pull request #369 2017-09-08 14:06:22 -07:00
Kohsuke Kawaguchi
5c9cbee2f9 Merge pull request #332 from sebkur/fix_javadoc
Fix a bug in the Javadocs (due to copy and paste)
2017-09-08 10:39:58 -07:00
Kohsuke Kawaguchi
ee8973c239 Merge pull request #338 from sebkur/ignore-eclipse-files
Ignore eclipse files
2017-09-08 10:39:39 -07:00
Kohsuke Kawaguchi
2e2813f363 Merge pull request #343 from kamontat/feature/latest-release
add latest release
2017-09-08 10:39:13 -07:00
Kohsuke Kawaguchi
7ceca0769f Merge pull request #363 from PauloMigAlmeida/master
Add missing event types used by repository webhooks
2017-09-08 10:36:33 -07:00
Kohsuke Kawaguchi
4d277cc61f Merge pull request #352 from stephenc/pr-reviews
Add support for PR reviews preview
2017-09-08 10:35:58 -07:00
Kohsuke Kawaguchi
e67fbb4621 Merge pull request #362 from KostyaSha/pingHook
Add ping hook method
2017-09-08 10:34:45 -07:00
Kohsuke Kawaguchi
29b5357ceb Merge pull request #358 from jglick/no-preview-JENKINS-36240
[JENKINS-36240] /repos/:owner/:repo/collaborators/:username/permission no longer requires korra preview
2017-09-08 10:33:31 -07:00
Stephen Connolly
9dabec107b Add basic support for tag objects 2017-09-01 12:52:15 +01:00
Matt Mitchell
f2a2ad90b7 Switch to a concurrent hash map 2017-08-29 11:30:25 -07:00
Matt Mitchell
cfe4c0c510 Merge remote-tracking branch 'upstream/master' into synchro-api 2017-08-29 10:38:54 -07:00
Jae Gangemi
23cd51a6da - improved branch protection support 2017-08-08 16:17:58 -06:00
Jae Gangemi
7396395f90 - updated ignore entries for eclipse/moc os 2017-08-08 14:31:11 -06:00
adw1n
a1819bf232 Added getClones method to GHRepository (https://developer.github.com/v3/repos/traffic/#clones). 2017-08-01 06:38:28 +02:00
adw1n
8accf07d46 Changed timestamp (GHRepositoryViews.DayViews.timestamp) field type from String to Date. 2017-08-01 04:02:10 +02:00
adw1n
6dcbace572 Added getViews method to GHRepository.
getViews implements https://developer.github.com/v3/repos/traffic/#views
2017-08-01 03:38:46 +02:00
Greg Gianforcaro
e90c86ec2f Remove Preview status, merge_method is now out of preview 2017-07-18 11:36:38 -04:00
Greg Gianforcaro
971ae1fa4d Merge branch 'master' into pr-merge-method 2017-07-18 11:33:20 -04:00
Paulo Miguel Almeida
4abe87036c Add missing event types used by repository webhooks 2017-07-14 04:23:08 +00:00
Kanstantsin Shautsou
8d1b44db97 Add ping hook method 2017-07-10 02:39:03 +03:00
Serban Iordache
b537f9925b issue #360: Add support for committing multiple files 2017-07-07 16:21:11 +02:00
Kohsuke Kawaguchi
f2fe8eaf86 [maven-release-plugin] prepare for next development iteration 2017-07-02 17:08:35 -07:00
Kohsuke Kawaguchi
46715cac08 [maven-release-plugin] prepare release github-api-1.86 2017-07-02 17:08:24 -07:00
Kohsuke Kawaguchi
0f21eba57f Merge pull request #359 from jglick/SocketTimeoutException-JENKINS-45142
[JENKINS-45142] Retry connections after getting SocketTimeoutException
2017-06-29 17:13:31 -04:00
Jesse Glick
cb7620395a [JENKINS-45142] Retry connections after getting SocketTimeoutException. 2017-06-28 17:09:54 -04:00
Jesse Glick
3a40af8871 [JENKINS-36240] /repos/:owner/:repo/collaborators/:username/permission no longer requires korra preview. 2017-06-12 10:28:25 -04:00
mdeverdelhan
c9b5074bc4 Fix the wrapping of retrieved commits (owner/repository) 2017-05-11 12:32:34 +02:00
Stephen Connolly
44d4d0d767 Add support for PR reviews preview 2017-03-30 12:17:06 +01:00
mdeverdelhan
64af13f40d Add the Commit search API (still in preview) 2017-03-30 12:55:55 +02:00
Kohsuke Kawaguchi
e9b59c6bef [maven-release-plugin] prepare for next development iteration 2017-02-28 21:06:14 -08:00
kamontat
5554332b5b add return null if latest release not found 2017-02-20 10:20:53 +07:00
kamontat
1cffea892b add test 2017-02-20 10:20:08 +07:00
kamontat
fd859815b0 fixed indent, rename to getLastestRelease 2017-02-20 09:36:28 +07:00
kamontat
166e26d101 add latest release 2017-02-19 23:57:44 +07:00
Kanstantsin Shautsou
be081eec3f Inject responce headers in GHObject and Exceptions.
GH has specific to GET/POST headers required for analysing in case of error.

Signed-off-by: Kanstantsin Shautsou <kanstantsin.sha@gmail.com>
2017-02-10 04:11:20 +03:00
Kanstantsin Shautsou
55b00a87f6 Set 1.6 level. I'm not so old.
Signed-off-by: Kanstantsin Shautsou <kanstantsin.sha@gmail.com>
2017-02-10 03:28:06 +03:00
Sebastian Kürten
429b26cee8 Ignore eclipse files 2017-02-09 18:18:08 +01:00
Sebastian Kürten
fafe6b0ff7 Remove unused imports
Especially also remove the unsued import of
javax.xml.bind.DatatypeConverter from GHContent which is non-public API
as of Java 8
2017-02-09 18:15:20 +01:00
Greg Gianforcaro
5b156006fb Add 'Preview' support for MergeMethod on GHPullRequest
- Add 'polaris' preview
- Add MergeMethod Enum
- Add merge method to GHPullRequest which takes a MergeMethod
2017-01-27 23:36:54 -05:00
Sebastian Kürten
75f0c08ca4 Fix a bug in the Javadocs (due to copy and paste) 2017-01-23 12:21:13 +01:00
Matt Mitchell
9f3f644b83 Add some level of synchronization to the root of the API
This adds some synchronization to the maps at the root of the API to avoid duplicated calls to the actual GH REST API.  Specifically this is targeted around the two maps, orgs and users.  This fix makes the GHPRB jenkins plugin behave much better when there are lots of projects that could build for a specific repo (even if only a few are actually triggered)

There are also a few fixes around GHUser and GHPullRequest
* GHPullRequest was checking a field that may be null (merged_by) when determining whether to fetch details.  An unmerged PR would make a bunch of Github API calls for each property accessed.
* Where GHUser was returned in various objects, we weren't going through the caching mechanism at the root, so calls to APIs on GHUSer often resulted in new REST calls.  Instead, return from the cache wherever possible.
2016-06-08 10:43:40 -07:00
93 changed files with 3436 additions and 336 deletions

4
.gitignore vendored
View File

@@ -3,3 +3,7 @@ target
*.iml
*.ipr
*.iws
.classpath
.project
.settings/
.DS_Store

48
pom.xml
View File

@@ -3,11 +3,11 @@
<parent>
<groupId>org.kohsuke</groupId>
<artifactId>pom</artifactId>
<version>17</version>
<version>20</version>
</parent>
<artifactId>github-api</artifactId>
<version>1.85</version>
<version>1.95</version>
<name>GitHub API for Java</name>
<url>http://github-api.kohsuke.org/</url>
<description>GitHub API for Java</description>
@@ -16,7 +16,7 @@
<connection>scm:git:git@github.com/kohsuke/${project.artifactId}.git</connection>
<developerConnection>scm:git:ssh://git@github.com/kohsuke/${project.artifactId}.git</developerConnection>
<url>http://${project.artifactId}.kohsuke.org/</url>
<tag>github-api-1.85</tag>
<tag>github-api-1.95</tag>
</scm>
<distributionManagement>
@@ -34,6 +34,13 @@
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
<configuration>
<rerunFailingTestsCount>2</rerunFailingTestsCount>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>animal-sniffer-maven-plugin</artifactId>
@@ -58,7 +65,7 @@
<plugin>
<groupId>com.infradna.tool</groupId>
<artifactId>bridge-method-injector</artifactId>
<version>1.14</version>
<version>1.18</version>
<executions>
<execution>
<goals>
@@ -90,9 +97,9 @@
<dependencies>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.7</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
@@ -102,13 +109,19 @@
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.2.3</version>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
@@ -118,7 +131,8 @@
<dependency>
<groupId>com.infradna.tool</groupId>
<artifactId>bridge-method-annotation</artifactId>
<version>1.14</version>
<version>1.17</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.kohsuke.stapler</groupId>
@@ -129,7 +143,7 @@
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit</artifactId>
<version>3.1.0.201310021548-r</version>
<version>4.9.0.201710071750-r</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -138,22 +152,28 @@
<version>2.7.5</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp-urlconnection</artifactId>
<version>3.9.0</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.kohsuke</groupId>
<artifactId>wordnet-random-name</artifactId>
<version>1.2</version>
<version>1.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.5</version>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>annotations</artifactId>
<version>3.0.0</version>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
</dependencies>

View File

@@ -1,21 +0,0 @@
package org.kohsuke.github;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.ArrayList;
import java.util.List;
/**
* @author Kohsuke Kawaguchi
* @see GHBranch#disableProtection()
*/
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD", "URF_UNREAD_FIELD"}, justification = "JSON API")
class BranchProtection {
boolean enabled;
RequiredStatusChecks requiredStatusChecks;
static class RequiredStatusChecks {
EnforcementLevel enforcement_level;
List<String> contexts = new ArrayList<String>();
}
}

View File

@@ -3,8 +3,11 @@ package org.kohsuke.github;
import java.util.Locale;
/**
* This was added during preview API period but it has changed since then.
*
* @author Kohsuke Kawaguchi
*/
@Deprecated
public enum EnforcementLevel {
OFF, NON_ADMINS, EVERYONE;

View File

@@ -1,9 +1,9 @@
package org.kohsuke.github;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.net.URL;
import java.util.Collection;
import java.util.Date;
import java.util.List;
/**

View File

@@ -0,0 +1,49 @@
package org.kohsuke.github;
import org.apache.commons.codec.binary.Base64;
import java.io.IOException;
/**
* Builder pattern for creating a new blob.
* Based on https://developer.github.com/v3/git/blobs/#create-a-blob
*/
public class GHBlobBuilder {
private final GHRepository repo;
private final Requester req;
GHBlobBuilder(GHRepository repo) {
this.repo = repo;
req = new Requester(repo.root);
}
/**
* Configures a blob with the specified text {@code content}.
*/
public GHBlobBuilder textContent(String content) {
req.with("content", content);
req.with("encoding", "utf-8");
return this;
}
/**
* Configures a blob with the specified binary {@code content}.
*/
public GHBlobBuilder binaryContent(byte[] content) {
String base64Content = Base64.encodeBase64String(content);
req.with("content", base64Content);
req.with("encoding", "base64");
return this;
}
private String getApiTail() {
return String.format("/repos/%s/%s/git/blobs", repo.getOwnerName(), repo.getName());
}
/**
* Creates a blob based on the parameters specified thus far.
*/
public GHBlob create() throws IOException {
return req.method("POST").to(getApiTail(), GHBlob.class);
}
}

View File

@@ -1,24 +1,20 @@
package org.kohsuke.github;
import static org.kohsuke.github.Previews.LOKI;
import com.fasterxml.jackson.annotation.JsonProperty;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.IOException;
import java.net.URL;
import java.util.Arrays;
import java.util.Collection;
import org.kohsuke.github.BranchProtection.RequiredStatusChecks;
import com.fasterxml.jackson.annotation.JsonProperty;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import static org.kohsuke.github.Previews.*;
/**
* A branch in a repository.
*
*
* @author Yusuke Kokubo
*/
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
"NP_UNWRITTEN_FIELD", "URF_UNREAD_FIELD"}, justification = "JSON API")
public class GHBranch {
private GitHub root;
@@ -33,7 +29,7 @@ public class GHBranch {
public static class Commit {
String sha;
@SuppressFBWarnings(value = "UUF_UNUSED_FIELD", justification = "We don't provide it in API now")
String url;
}
@@ -69,6 +65,9 @@ public class GHBranch {
return GitHub.parseURL(protection_url);
}
public GHBranchProtection getProtection() throws IOException {
return root.retrieve().to(protection_url, GHBranchProtection.class).wrap(this);
}
/**
* The commit that this branch currently points to.
@@ -80,11 +79,8 @@ public class GHBranch {
/**
* Disables branch protection and allows anyone with push access to push changes.
*/
@Preview @Deprecated
public void disableProtection() throws IOException {
BranchProtection bp = new BranchProtection();
bp.enabled = false;
setProtection(bp);
new Requester(root).method("DELETE").to(protection_url);
}
/**
@@ -93,28 +89,31 @@ public class GHBranch {
* @see GHCommitStatus#getContext()
*/
@Preview @Deprecated
public GHBranchProtectionBuilder enableProtection() {
return new GHBranchProtectionBuilder(this);
}
// backward compatibility with previous signature
@Deprecated
public void enableProtection(EnforcementLevel level, Collection<String> contexts) throws IOException {
BranchProtection bp = new BranchProtection();
bp.enabled = true;
bp.requiredStatusChecks = new RequiredStatusChecks();
bp.requiredStatusChecks.enforcement_level = level;
bp.requiredStatusChecks.contexts.addAll(contexts);
setProtection(bp);
}
@Preview @Deprecated
public void enableProtection(EnforcementLevel level, String... contexts) throws IOException {
enableProtection(level, Arrays.asList(contexts));
}
private void setProtection(BranchProtection bp) throws IOException {
new Requester(root).method("PATCH").withPreview(LOKI)._with("protection",bp).to(getApiRoute());
switch (level) {
case OFF:
disableProtection();
break;
case NON_ADMINS:
case EVERYONE:
enableProtection()
.addRequiredChecks(contexts)
.includeAdmins(level==EnforcementLevel.EVERYONE)
.enable();
break;
}
}
String getApiRoute() {
return owner.getApiTailUrl("/branches/"+name);
}
@Override
public String toString() {
final String url = owner != null ? owner.getUrl().toString() : "unknown";

View File

@@ -0,0 +1,208 @@
package org.kohsuke.github;
import com.fasterxml.jackson.annotation.JsonProperty;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import static org.kohsuke.github.Previews.ZZZAX;
import java.io.IOException;
import java.util.Collection;
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD",
"URF_UNREAD_FIELD"}, justification = "JSON API")
public class GHBranchProtection {
private static final String REQUIRE_SIGNATURES_URI = "/required_signatures";
@JsonProperty("enforce_admins")
private EnforceAdmins enforceAdmins;
private GitHub root;
@JsonProperty("required_pull_request_reviews")
private RequiredReviews requiredReviews;
@JsonProperty("required_status_checks")
private RequiredStatusChecks requiredStatusChecks;
@JsonProperty
private Restrictions restrictions;
@JsonProperty
private String url;
@Preview @Deprecated
public void enabledSignedCommits() throws IOException {
requester().method("POST")
.to(url + REQUIRE_SIGNATURES_URI, RequiredSignatures.class);
}
@Preview @Deprecated
public void disableSignedCommits() throws IOException {
requester().method("DELETE")
.to(url + REQUIRE_SIGNATURES_URI);
}
public EnforceAdmins getEnforceAdmins() {
return enforceAdmins;
}
public RequiredReviews getRequiredReviews() {
return requiredReviews;
}
@Preview @Deprecated
public boolean getRequiredSignatures() throws IOException {
return requester().method("GET")
.to(url + REQUIRE_SIGNATURES_URI, RequiredSignatures.class).enabled;
}
public RequiredStatusChecks getRequiredStatusChecks() {
return requiredStatusChecks;
}
public Restrictions getRestrictions() {
return restrictions;
}
public String getUrl() {
return url;
}
GHBranchProtection wrap(GHBranch branch) {
this.root = branch.getRoot();
return this;
}
private Requester requester() {
return new Requester(root).withPreview(ZZZAX);
}
public static class EnforceAdmins {
@JsonProperty
private boolean enabled;
@JsonProperty
private String url;
public String getUrl() {
return url;
}
public boolean isEnabled() {
return enabled;
}
}
public static class RequiredReviews {
@JsonProperty("dismissal_restrictions")
private Restrictions dismissalRestriction;
@JsonProperty("dismiss_stale_reviews")
private boolean dismissStaleReviews;
@JsonProperty("require_code_owner_reviews")
private boolean requireCodeOwnerReviews;
@JsonProperty("required_approving_review_count")
private int requiredReviewers;
@JsonProperty
private String url;
public Restrictions getDismissalRestrictions() {
return dismissalRestriction;
}
public String getUrl() {
return url;
}
public boolean isDismissStaleReviews() {
return dismissStaleReviews;
}
public boolean isRequireCodeOwnerReviews() {
return requireCodeOwnerReviews;
}
public int getRequiredReviewers() {
return requiredReviewers;
}
}
private static class RequiredSignatures {
@JsonProperty
private boolean enabled;
@JsonProperty
private String url;
public String getUrl() {
return url;
}
public boolean isEnabled() {
return enabled;
}
}
public static class RequiredStatusChecks {
@JsonProperty
private Collection<String> contexts;
@JsonProperty
private boolean strict;
@JsonProperty
private String url;
public Collection<String> getContexts() {
return contexts;
}
public String getUrl() {
return url;
}
public boolean isRequiresBranchUpToDate() {
return strict;
}
}
public static class Restrictions {
@JsonProperty
private Collection<GHTeam> teams;
@JsonProperty("teams_url")
private String teamsUrl;
@JsonProperty
private String url;
@JsonProperty
private Collection<GHUser> users;
@JsonProperty("users_url")
private String usersUrl;
public Collection<GHTeam> getTeams() {
return teams;
}
public String getTeamsUrl() {
return teamsUrl;
}
public String getUrl() {
return url;
}
public Collection<GHUser> getUsers() {
return users;
}
public String getUsersUrl() {
return usersUrl;
}
}
}

View File

@@ -0,0 +1,218 @@
package org.kohsuke.github;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.kohsuke.github.Previews.*;
/**
* Builder to configure the branch protection settings.
*
* @see GHBranch#enableProtection()
*/
@SuppressFBWarnings(value = { "UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD",
"URF_UNREAD_FIELD" }, justification = "JSON API")
public class GHBranchProtectionBuilder {
private final GHBranch branch;
private boolean enforceAdmins;
private Map<String, Object> prReviews;
private Restrictions restrictions;
private StatusChecks statusChecks;
GHBranchProtectionBuilder(GHBranch branch) {
this.branch = branch;
}
public GHBranchProtectionBuilder addRequiredChecks(Collection<String> checks) {
getStatusChecks().contexts.addAll(checks);
return this;
}
public GHBranchProtectionBuilder addRequiredChecks(String... checks) {
addRequiredChecks(Arrays.asList(checks));
return this;
}
public GHBranchProtectionBuilder dismissStaleReviews() {
return dismissStaleReviews(true);
}
public GHBranchProtectionBuilder dismissStaleReviews(boolean v) {
getPrReviews().put("dismiss_stale_reviews", v);
return this;
}
public GHBranchProtection enable() throws IOException {
return requester().method("PUT")
.withNullable("required_status_checks", statusChecks)
.withNullable("required_pull_request_reviews", prReviews)
.withNullable("restrictions", restrictions)
.withNullable("enforce_admins", enforceAdmins)
.to(branch.getProtectionUrl().toString(), GHBranchProtection.class)
.wrap(branch);
}
public GHBranchProtectionBuilder includeAdmins() {
return includeAdmins(true);
}
public GHBranchProtectionBuilder includeAdmins(boolean v) {
enforceAdmins = v;
return this;
}
public GHBranchProtectionBuilder requiredReviewers(int v) {
getPrReviews().put("required_approving_review_count", v);
return this;
}
public GHBranchProtectionBuilder requireBranchIsUpToDate() {
return requireBranchIsUpToDate(true);
}
public GHBranchProtectionBuilder requireBranchIsUpToDate(boolean v) {
getStatusChecks().strict = v;
return this;
}
public GHBranchProtectionBuilder requireCodeOwnReviews() {
return requireCodeOwnReviews(true);
}
public GHBranchProtectionBuilder requireCodeOwnReviews(boolean v) {
getPrReviews().put("require_code_owner_reviews", v);
return this;
}
public GHBranchProtectionBuilder requireReviews() {
getPrReviews();
return this;
}
public GHBranchProtectionBuilder restrictReviewDismissals() {
getPrReviews();
if (!prReviews.containsKey("dismissal_restrictions")) {
prReviews.put("dismissal_restrictions", new Restrictions());
}
return this;
}
public GHBranchProtectionBuilder restrictPushAccess() {
getRestrictions();
return this;
}
public GHBranchProtectionBuilder teamPushAccess(Collection<GHTeam> teams) {
for (GHTeam team : teams) {
teamPushAccess(team);
}
return this;
}
public GHBranchProtectionBuilder teamPushAccess(GHTeam... teams) {
for (GHTeam team : teams) {
getRestrictions().teams.add(team.getSlug());
}
return this;
}
public GHBranchProtectionBuilder teamReviewDismissals(Collection<GHTeam> teams) {
for (GHTeam team : teams) {
teamReviewDismissals(team);
}
return this;
}
public GHBranchProtectionBuilder teamReviewDismissals(GHTeam... teams) {
for (GHTeam team : teams) {
addReviewRestriction(team.getSlug(), true);
}
return this;
}
public GHBranchProtectionBuilder userPushAccess(Collection<GHUser> users) {
for (GHUser user : users) {
userPushAccess(user);
}
return this;
}
public GHBranchProtectionBuilder userPushAccess(GHUser... users) {
for (GHUser user : users) {
getRestrictions().users.add(user.getLogin());
}
return this;
}
public GHBranchProtectionBuilder userReviewDismissals(Collection<GHUser> users) {
for (GHUser team : users) {
userReviewDismissals(team);
}
return this;
}
public GHBranchProtectionBuilder userReviewDismissals(GHUser... users) {
for (GHUser user : users) {
addReviewRestriction(user.getLogin(), false);
}
return this;
}
private void addReviewRestriction(String restriction, boolean isTeam) {
restrictReviewDismissals();
Restrictions restrictions = (Restrictions) prReviews.get("dismissal_restrictions");
if (isTeam) {
restrictions.teams.add(restriction);
} else {
restrictions.users.add(restriction);
}
}
private Map<String, Object> getPrReviews() {
if (prReviews == null) {
prReviews = new HashMap<String, Object>();
}
return prReviews;
}
private Restrictions getRestrictions() {
if (restrictions == null) {
restrictions = new Restrictions();
}
return restrictions;
}
private StatusChecks getStatusChecks() {
if (statusChecks == null) {
statusChecks = new StatusChecks();
}
return statusChecks;
}
private Requester requester() {
return new Requester(branch.getRoot()).withPreview(LUKE_CAGE);
}
private static class Restrictions {
private Set<String> teams = new HashSet<String>();
private Set<String> users = new HashSet<String>();
}
private static class StatusChecks {
final List<String> contexts = new ArrayList<String>();
boolean strict;
}
}

View File

@@ -0,0 +1,37 @@
package org.kohsuke.github;
/**
* How is an user associated with a repository?
*
* @author Kohsuke Kawaguchi
*/
public enum GHCommentAuthorAssociation {
/**
* Author has been invited to collaborate on the repository.
*/
COLLABORATOR,
/**
* Author has previously committed to the repository.
*/
CONTRIBUTOR,
/**
* Author has not previously committed to GitHub.
*/
FIRST_TIMER,
/**
* Author has not previously committed to the repository.
*/
FIRST_TIME_CONTRIBUTOR,
/**
* Author is a member of the organization that owns the repository.
*/
MEMBER,
/**
* Author has no association with the repository.
*/
NONE,
/**
* Author is the owner of the repository.
*/
OWNER
}

View File

@@ -2,6 +2,7 @@ package org.kohsuke.github;
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.IOException;
import java.net.URL;
import java.util.AbstractList;

View File

@@ -0,0 +1,92 @@
package org.kohsuke.github;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
/**
* Builder pattern for creating a new commit.
* Based on https://developer.github.com/v3/git/commits/#create-a-commit
*/
public class GHCommitBuilder {
private final GHRepository repo;
private final Requester req;
private final List<String> parents = new ArrayList<String>();
private static final class UserInfo {
private final String name;
private final String email;
private final String date;
private UserInfo(String name, String email, Date date) {
this.name = name;
this.email = email;
TimeZone tz = TimeZone.getTimeZone("UTC");
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
df.setTimeZone(tz);
this.date = df.format((date != null) ? date : new Date());
}
}
GHCommitBuilder(GHRepository repo) {
this.repo = repo;
req = new Requester(repo.root);
}
/**
* @param message the commit message
*/
public GHCommitBuilder message(String message) {
req.with("message", message);
return this;
}
/**
* @param tree the SHA of the tree object this commit points to
*/
public GHCommitBuilder tree(String tree) {
req.with("tree", tree);
return this;
}
/**
* @param parent the SHA of a parent commit.
*/
public GHCommitBuilder parent(String parent) {
parents.add(parent);
return this;
}
/**
* Configures the author of this commit.
*/
public GHCommitBuilder author(String name, String email, Date date) {
req._with("author", new UserInfo(name, email, date));
return this;
}
/**
* Configures the committer of this commit.
*/
public GHCommitBuilder committer(String name, String email, Date date) {
req._with("committer", new UserInfo(name, email, date));
return this;
}
private String getApiTail() {
return String.format("/repos/%s/%s/git/commits", repo.getOwnerName(), repo.getName());
}
/**
* Creates a blob based on the parameters specified thus far.
*/
public GHCommit create() throws IOException {
req._with("parents", parents);
return req.method("POST").to(getApiTail(), GHCommit.class).wrapUp(repo);
}
}

View File

@@ -1,11 +1,11 @@
package org.kohsuke.github;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.IOException;
import java.net.URL;
import java.util.Date;
import static org.kohsuke.github.Previews.SQUIRREL_GIRL;
import static org.kohsuke.github.Previews.*;
/**
* A comment attached to a commit (or a specific line in a specific file of a commit.)

View File

@@ -39,7 +39,8 @@ public class GHCommitPointer {
* This points to the user who owns
* the {@link #getRepository()}.
*/
public GHUser getUser() {
public GHUser getUser() throws IOException {
if (user != null) return user.root.intern(user);
return user;
}

View File

@@ -0,0 +1,137 @@
package org.kohsuke.github;
import org.apache.commons.lang3.StringUtils;
import java.io.IOException;
/**
* Search commits.
*
* @author Marc de Verdelhan
* @see GitHub#searchCommits()
*/
@Preview @Deprecated
public class GHCommitSearchBuilder extends GHSearchBuilder<GHCommit> {
/*package*/ GHCommitSearchBuilder(GitHub root) {
super(root,CommitSearchResult.class);
req.withPreview(Previews.CLOAK);
}
/**
* Search terms.
*/
public GHCommitSearchBuilder q(String term) {
super.q(term);
return this;
}
public GHCommitSearchBuilder author(String v) {
return q("author:"+v);
}
public GHCommitSearchBuilder committer(String v) {
return q("committer:"+v);
}
public GHCommitSearchBuilder authorName(String v) {
return q("author-name:"+v);
}
public GHCommitSearchBuilder committerName(String v) {
return q("committer-name:"+v);
}
public GHCommitSearchBuilder authorEmail(String v) {
return q("author-email:"+v);
}
public GHCommitSearchBuilder committerEmail(String v) {
return q("committer-email:"+v);
}
public GHCommitSearchBuilder authorDate(String v) {
return q("author-date:"+v);
}
public GHCommitSearchBuilder committerDate(String v) {
return q("committer-date:"+v);
}
public GHCommitSearchBuilder merge(boolean merge) {
return q("merge:"+Boolean.valueOf(merge).toString().toLowerCase());
}
public GHCommitSearchBuilder hash(String v) {
return q("hash:"+v);
}
public GHCommitSearchBuilder parent(String v) {
return q("parent:"+v);
}
public GHCommitSearchBuilder tree(String v) {
return q("tree:"+v);
}
public GHCommitSearchBuilder is(String v) {
return q("is:"+v);
}
public GHCommitSearchBuilder user(String v) {
return q("user:"+v);
}
public GHCommitSearchBuilder org(String v) {
return q("org:"+v);
}
public GHCommitSearchBuilder repo(String v) {
return q("repo:"+v);
}
public GHCommitSearchBuilder order(GHDirection v) {
req.with("order",v);
return this;
}
public GHCommitSearchBuilder sort(Sort sort) {
req.with("sort",sort);
return this;
}
public enum Sort { AUTHOR_DATE, COMMITTER_DATE }
private static class CommitSearchResult extends SearchResult<GHCommit> {
private GHCommit[] items;
@Override
/*package*/ GHCommit[] getItems(GitHub root) {
for (GHCommit commit : items) {
String repoName = getRepoName(commit.url);
try {
GHRepository repo = root.getRepository(repoName);
commit.wrapUp(repo);
} catch (IOException ioe) {}
}
return items;
}
}
/**
* @param commitUrl a commit URL
* @return the repo name ("username/reponame")
*/
private static String getRepoName(String commitUrl) {
if (StringUtils.isBlank(commitUrl)) {
return null;
}
int indexOfUsername = (GitHub.GITHUB_URL + "/repos/").length();
String[] tokens = commitUrl.substring(indexOfUsername).split("/", 3);
return tokens[0] + '/' + tokens[1];
}
@Override
protected String getApiUrl() {
return "/search/commits";
}
}

View File

@@ -1,5 +1,6 @@
package org.kohsuke.github;
import java.io.IOException;
import java.net.URL;
/**
@@ -45,8 +46,8 @@ public class GHCommitStatus extends GHObject {
return description;
}
public GHUser getCreator() {
return creator;
public GHUser getCreator() throws IOException {
return root.intern(creator);
}
public String getContext() {

View File

@@ -7,8 +7,6 @@ import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.io.InputStream;
import javax.xml.bind.DatatypeConverter;
/**
* A Content of a repository.
*

View File

@@ -0,0 +1,76 @@
package org.kohsuke.github;
import org.apache.commons.codec.binary.Base64;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
/**
* Used to create/update content.
*
* <p>
* Call various methods to build up parameters, then call {@link #commit()} to make the change effective.
*
* @author Kohsuke Kawaguchi
* @see GHRepository#createContent()
*/
public final class GHContentBuilder {
private final GHRepository repo;
private final Requester req;
private String path;
GHContentBuilder(GHRepository repo) {
this.repo = repo;
this.req = new Requester(repo.root).method("PUT");
}
public GHContentBuilder path(String path) {
this.path = path;
req.with("path",path);
return this;
}
public GHContentBuilder branch(String branch) {
req.with("branch", branch);
return this;
}
/**
* Used when updating (but not creating a new content) to specify
* Thetblob SHA of the file being replaced.
*/
public GHContentBuilder sha(String sha) {
req.with("sha", sha);
return this;
}
public GHContentBuilder content(byte[] content) {
req.with("content", Base64.encodeBase64String(content));
return this;
}
public GHContentBuilder content(String content) {
try {
return content(content.getBytes("UTF-8"));
} catch (UnsupportedEncodingException x) {
throw new AssertionError();
}
}
public GHContentBuilder message(String commitMessage) {
req.with("message", commitMessage);
return this;
}
/**
* Commits a new content.
*/
public GHContentUpdateResponse commit() throws IOException {
GHContentUpdateResponse response = req.to(repo.getApiTailUrl("contents/" + path), GHContentUpdateResponse.class);
response.getContent().wrap(repo);
response.getCommit().wrapUp(repo);
return response;
}
}

View File

@@ -21,8 +21,8 @@ public class GHCreateRepositoryBuilder {
}
public GHCreateRepositoryBuilder description(String description) {
this.builder.with("description",description);
return this;
this.builder.with("description",description);
return this;
}
public GHCreateRepositoryBuilder homepage(URL homepage) {
@@ -74,6 +74,30 @@ public class GHCreateRepositoryBuilder {
return this;
}
/**
* Allow or disallow squash-merging pull requests.
*/
public GHCreateRepositoryBuilder allowSquashMerge(boolean b) {
this.builder.with("allow_squash_merge",b);
return this;
}
/**
* Allow or disallow merging pull requests with a merge commit.
*/
public GHCreateRepositoryBuilder allowMergeCommit(boolean b) {
this.builder.with("allow_merge_commit",b);
return this;
}
/**
* Allow or disallow rebase-merging pull requests.
*/
public GHCreateRepositoryBuilder allowRebaseMerge(boolean b) {
this.builder.with("allow_rebase_merge",b);
return this;
}
/**
* Creates a default .gitignore
*

View File

@@ -1,8 +1,8 @@
package org.kohsuke.github;
import java.io.IOException;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;
import java.io.IOException;
public class GHDeployKey {

View File

@@ -1,8 +1,15 @@
package org.kohsuke.github;
import java.io.IOException;
import java.net.URL;
/**
* Represents a deployment
*
* @see <a href="https://developer.github.com/v3/repos/deployments/">documentation</a>
* @see GHRepository#listDeployments(String, String, String, String)
* @see GHRepository#getDeployment(long)
*/
public class GHDeployment extends GHObject {
private GHRepository owner;
private GitHub root;
@@ -41,8 +48,8 @@ public class GHDeployment extends GHObject {
public String getEnvironment() {
return environment;
}
public GHUser getCreator() {
return creator;
public GHUser getCreator() throws IOException {
return root.intern(creator);
}
public String getRef() {
return ref;
@@ -58,4 +65,23 @@ public class GHDeployment extends GHObject {
public URL getHtmlUrl() {
return null;
}
public GHDeploymentStatusBuilder createStatus(GHDeploymentState state) {
return new GHDeploymentStatusBuilder(owner,id,state);
}
public PagedIterable<GHDeploymentStatus> listStatuses() {
return new PagedIterable<GHDeploymentStatus>() {
public PagedIterator<GHDeploymentStatus> _iterator(int pageSize) {
return new PagedIterator<GHDeploymentStatus>(root.retrieve().asIterator(statuses_url, GHDeploymentStatus[].class, pageSize)) {
@Override
protected void wrapUp(GHDeploymentStatus[] page) {
for (GHDeploymentStatus c : page)
c.wrap(owner);
}
};
}
};
}
}

View File

@@ -1,14 +1,27 @@
package org.kohsuke.github;
import java.io.IOException;
import java.util.Locale;
/**
* Creates a new deployment status.
*
* @see
* GHDeployment#createStatus(GHDeploymentState)
*/
public class GHDeploymentStatusBuilder {
private final Requester builder;
private GHRepository repo;
private int deploymentId;
private long deploymentId;
/**
* @deprecated
* Use {@link GHDeployment#createStatus(GHDeploymentState)}
*/
public GHDeploymentStatusBuilder(GHRepository repo, int deploymentId, GHDeploymentState state) {
this(repo,(long)deploymentId,state);
}
/*package*/ GHDeploymentStatusBuilder(GHRepository repo, long deploymentId, GHDeploymentState state) {
this.repo = repo;
this.deploymentId = deploymentId;
this.builder = new Requester(repo.root);
@@ -26,6 +39,6 @@ public class GHDeploymentStatusBuilder {
}
public GHDeploymentStatus create() throws IOException {
return builder.to(repo.getApiTailUrl("deployments")+"/"+deploymentId+"/statuses",GHDeploymentStatus.class).wrap(repo);
return builder.to(repo.getApiTailUrl("deployments/"+deploymentId+"/statuses"),GHDeploymentStatus.class).wrap(repo);
}
}

View File

@@ -21,17 +21,30 @@ public enum GHEvent {
FORK_APPLY,
GIST,
GOLLUM,
INSTALLATION,
INSTALLATION_REPOSITORIES,
ISSUE_COMMENT,
ISSUES,
LABEL,
MARKETPLACE_PURCHASE,
MEMBER,
MEMBERSHIP,
MILESTONE,
ORGANIZATION,
ORG_BLOCK,
PAGE_BUILD,
PROJECT_CARD,
PROJECT_COLUMN,
PROJECT,
PUBLIC,
PULL_REQUEST,
PULL_REQUEST_REVIEW,
PULL_REQUEST_REVIEW_COMMENT,
PUSH,
RELEASE,
REPOSITORY, // only valid for org hooks
STATUS,
TEAM,
TEAM_ADD,
WATCH,
PING,

View File

@@ -1,11 +1,11 @@
package org.kohsuke.github;
import java.io.IOException;
import java.util.Date;
import com.fasterxml.jackson.databind.node.ObjectNode;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.IOException;
import java.util.Date;
/**
* Represents an event.
*

View File

@@ -3,6 +3,7 @@ package org.kohsuke.github;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSetter;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.Reader;
import java.util.List;
@@ -77,13 +78,147 @@ public abstract class GHEventPayload {
throw new IllegalStateException("Expected pull_request payload, but got something else. Maybe we've got another type of event?");
if (repository!=null) {
repository.wrap(root);
pull_request.wrap(repository);
pull_request.wrapUp(repository);
} else {
pull_request.wrapUp(root);
}
}
}
/**
* A review was added to a pull request
*
* @see <a href="https://developer.github.com/v3/activity/events/types/#pullrequestreviewevent">authoritative source</a>
*/
public static class PullRequestReview extends GHEventPayload {
private String action;
private GHPullRequestReview review;
private GHPullRequest pull_request;
private GHRepository repository;
public String getAction() {
return action;
}
public GHPullRequestReview getReview() {
return review;
}
public GHPullRequest getPullRequest() {
return pull_request;
}
public GHRepository getRepository() {
return repository;
}
@Override
void wrapUp(GitHub root) {
super.wrapUp(root);
if (review==null)
throw new IllegalStateException("Expected pull_request_review payload, but got something else. Maybe we've got another type of event?");
review.wrapUp(pull_request);
if (repository!=null) {
repository.wrap(root);
pull_request.wrapUp(repository);
} else {
pull_request.wrapUp(root);
}
}
}
/**
* A review comment was added to a pull request
*
* @see <a href="https://developer.github.com/v3/activity/events/types/#pullrequestreviewcommentevent">authoritative source</a>
*/
public static class PullRequestReviewComment extends GHEventPayload {
private String action;
private GHPullRequestReviewComment comment;
private GHPullRequest pull_request;
private GHRepository repository;
public String getAction() {
return action;
}
public GHPullRequestReviewComment getComment() {
return comment;
}
public GHPullRequest getPullRequest() {
return pull_request;
}
public GHRepository getRepository() {
return repository;
}
@Override
void wrapUp(GitHub root) {
super.wrapUp(root);
if (comment==null)
throw new IllegalStateException("Expected pull_request_review_comment payload, but got something else. Maybe we've got another type of event?");
comment.wrapUp(pull_request);
if (repository!=null) {
repository.wrap(root);
pull_request.wrapUp(repository);
} else {
pull_request.wrapUp(root);
}
}
}
/**
* A Issue has been assigned, unassigned, labeled, unlabeled, opened, edited, milestoned, demilestoned, closed, or reopened.
*
* @see <a href="http://developer.github.com/v3/activity/events/types/#issueevent">authoritative source</a>
*/
@SuppressFBWarnings(value = {"UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR", "NP_UNWRITTEN_FIELD" },
justification = "Constructed by JSON deserialization")
public static class Issue extends GHEventPayload {
private String action;
private GHIssue issue;
private GHRepository repository;
@SuppressFBWarnings(value = "UWF_UNWRITTEN_FIELD", justification = "Comes from JSON deserialization")
public String getAction() {
return action;
}
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);
if (repository != null) {
repository.wrap(root);
issue.wrap(repository);
} else {
issue.wrap(root);
}
}
}
/**
* A comment was added to an issue
*
@@ -626,6 +761,48 @@ public abstract class GHEventPayload {
}
}
/**
* A release was added to the repo
*
* @see <a href="http://developer.github.com/v3/activity/events/types/#releaseevent">authoritative source</a>
*/
@SuppressFBWarnings(value = {"UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR", "NP_UNWRITTEN_FIELD" },
justification = "Constructed by JSON deserialization")
public static class Release extends GHEventPayload {
private String action;
private GHRelease release;
private GHRepository repository;
@SuppressFBWarnings(value = "UWF_UNWRITTEN_FIELD", justification = "Comes from JSON deserialization")
public String getAction() {
return action;
}
public GHRelease getRelease() {
return release;
}
public void setRelease(GHRelease release) {
this.release = release;
}
public GHRepository getRepository() {
return repository;
}
public void setRepository(GHRepository repository) {
this.repository = repository;
}
@Override
void wrapUp(GitHub root) {
super.wrapUp(root);
if (repository != null) {
repository.wrap(root);
}
}
}
/**
* A repository was created, deleted, made public, or made private.
*

View File

@@ -0,0 +1,34 @@
package org.kohsuke.github;
import javax.annotation.CheckForNull;
import java.io.FileNotFoundException;
import java.net.HttpURLConnection;
import java.util.List;
import java.util.Map;
/**
* Request/responce contains useful metadata.
* Custom exception allows store info for next diagnostics.
*
* @author Kanstantsin Shautsou
*/
public class GHFileNotFoundException extends FileNotFoundException {
protected Map<String, List<String>> responseHeaderFields;
public GHFileNotFoundException() {
}
public GHFileNotFoundException(String s) {
super(s);
}
@CheckForNull
public Map<String, List<String>> getResponseHeaderFields() {
return responseHeaderFields;
}
GHFileNotFoundException withResponseHeaderFields(HttpURLConnection urlConnection) {
this.responseHeaderFields = urlConnection.getHeaderFields();
return this;
}
}

View File

@@ -38,8 +38,8 @@ public class GHGist extends GHObject {
/**
* User that owns this Gist.
*/
public GHUser getOwner() {
return owner;
public GHUser getOwner() throws IOException {
return root.intern(owner);
}
public String getForksUrl() {
@@ -103,7 +103,7 @@ public class GHGist extends GHObject {
* Used when caller obtains {@link GHGist} without knowing its owner.
* A partially constructed owner object is interned.
*/
/*package*/ GHGist wrapUp(GitHub root) throws IOException {
/*package*/ GHGist wrapUp(GitHub root) {
this.owner = root.getUser(owner);
this.root = root;
wrapUp();
@@ -144,12 +144,8 @@ public class GHGist extends GHObject {
return new PagedIterator<GHGist>(root.retrieve().asIterator(getApiTailUrl("forks"), GHGist[].class, pageSize)) {
@Override
protected void wrapUp(GHGist[] page) {
try {
for (GHGist c : page)
c.wrapUp(root);
} catch (IOException e) {
throw new Error(e);
}
for (GHGist c : page)
c.wrapUp(root);
}
};
}

View File

@@ -1,6 +1,7 @@
package org.kohsuke.github;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.IOException;
import java.net.URL;
import java.util.Collections;
@@ -41,6 +42,13 @@ public abstract class GHHook extends GHObject {
return Collections.unmodifiableMap(config);
}
/**
* @see <a href="https://developer.github.com/v3/repos/hooks/#ping-a-hook">Ping hook</a>
*/
public void ping() throws IOException {
new Requester(getRoot()).method("POST").to(getApiRoute() + "/pings");
}
/**
* Deletes this hook.
*/

View File

@@ -5,7 +5,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.Map;
/**

View File

@@ -0,0 +1,34 @@
package org.kohsuke.github;
import javax.annotation.CheckForNull;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.util.List;
import java.util.Map;
/**
* Request/responce contains useful metadata.
* Custom exception allows store info for next diagnostics.
*
* @author Kanstantsin Shautsou
*/
public class GHIOException extends IOException {
protected Map<String, List<String>> responseHeaderFields;
public GHIOException() {
}
public GHIOException(String message) {
super(message);
}
@CheckForNull
public Map<String, List<String>> getResponseHeaderFields() {
return responseHeaderFields;
}
GHIOException withResponseHeaderFields(HttpURLConnection urlConnection) {
this.responseHeaderFields = urlConnection.getHeaderFields();
return this;
}
}

View File

@@ -0,0 +1,46 @@
package org.kohsuke.github;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.IOException;
import java.net.URL;
/**
* @see GitHub#getMyInvitations()
* @see GHRepository#listInvitations()
*/
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
"NP_UNWRITTEN_FIELD", "UUF_UNUSED_FIELD"}, justification = "JSON API")
public class GHInvitation extends GHObject {
/*package almost final*/ GitHub root;
private int id;
private GHRepository repository;
private GHUser invitee, inviter;
private String permissions;
private String html_url;
/*package*/ GHInvitation wrapUp(GitHub root) {
this.root = root;
return this;
}
/**
* Accept a repository invitation.
*/
public void accept() throws IOException {
root.retrieve().method("PATCH").to("/user/repository_invitations/" + id);
}
/**
* Decline a repository invitation.
*/
public void decline() throws IOException {
root.retrieve().method("DELETE").to("/user/repository_invitations/" + id);
}
@Override
public URL getHtmlUrl() {
return GitHub.parseURL(html_url);
}
}

View File

@@ -24,9 +24,10 @@
package org.kohsuke.github;
import static org.kohsuke.github.Previews.SQUIRREL_GIRL;
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
@@ -34,10 +35,10 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import static org.kohsuke.github.Previews.*;
import java.util.Set;
/**
* Represents an issue on GitHub.
@@ -49,6 +50,8 @@ import static org.kohsuke.github.Previews.*;
* @see GHIssueSearchBuilder
*/
public class GHIssue extends GHObject implements Reactable{
private static final String ASSIGNEES = "assignees";
GitHub root;
GHRepository owner;
@@ -68,6 +71,7 @@ public class GHIssue extends GHObject implements Reactable{
protected GHIssue.PullRequest pull_request;
protected GHMilestone milestone;
protected GHUser closed_by;
protected boolean locked;
/**
* @deprecated use {@link GHLabel}
@@ -129,6 +133,10 @@ public class GHIssue extends GHObject implements Reactable{
return title;
}
public boolean isLocked() {
return locked;
}
public GHIssueState getState() {
return Enum.valueOf(GHIssueState.class, state.toUpperCase(Locale.ENGLISH));
}
@@ -148,6 +156,14 @@ public class GHIssue extends GHObject implements Reactable{
return GitHub.parseURL(url);
}
public void lock() throws IOException {
new Requester(root).method("PUT").to(getApiRoute()+"/lock");
}
public void unlock() throws IOException {
new Requester(root).method("PUT").to(getApiRoute()+"/lock");
}
/**
* Updates the issue by adding a comment.
*
@@ -190,6 +206,10 @@ public class GHIssue extends GHObject implements Reactable{
edit("body",body);
}
public void setMilestone(GHMilestone milestone) throws IOException {
edit("milestone",milestone.getNumber());
}
public void assignTo(GHUser user) throws IOException {
setAssignees(user);
}
@@ -198,6 +218,67 @@ public class GHIssue extends GHObject implements Reactable{
editIssue("labels",labels);
}
/**
* Adds labels to the issue.
*
* @param names Names of the label
*/
public void addLabels(String... names) throws IOException {
_addLabels(Arrays.asList(names));
}
public void addLabels(GHLabel... labels) throws IOException {
addLabels(Arrays.asList(labels));
}
public void addLabels(Collection<GHLabel> labels) throws IOException {
_addLabels(GHLabel.toNames(labels));
}
private void _addLabels(Collection<String> names) throws IOException {
List<String> newLabels = new ArrayList<String>();
for (GHLabel label : getLabels()) {
newLabels.add(label.getName());
}
for (String name : names) {
if (!newLabels.contains(name)) {
newLabels.add(name);
}
}
setLabels(newLabels.toArray(new String[0]));
}
/**
* Remove a given label by name from this issue.
*/
public void removeLabels(String... names) throws IOException {
_removeLabels(Arrays.asList(names));
}
/**
* @see #removeLabels(String...)
*/
public void removeLabels(GHLabel... labels) throws IOException {
removeLabels(Arrays.asList(labels));
}
public void removeLabels(Collection<GHLabel> labels) throws IOException {
_removeLabels(GHLabel.toNames(labels));
}
private void _removeLabels(Collection<String> names) throws IOException {
List<String> newLabels = new ArrayList<String>();
for (GHLabel l : getLabels()) {
if (!names.contains(l.getName())) {
newLabels.add(l.getName());
}
}
setLabels(newLabels.toArray(new String[0]));
}
/**
* Obtains all the comments associated with this issue.
*
@@ -251,8 +332,7 @@ public class GHIssue extends GHObject implements Reactable{
}
public void addAssignees(Collection<GHUser> assignees) throws IOException {
List<String> names = toLogins(assignees);
root.retrieve().method("POST").with("assignees",names).to(getIssuesApiRoute()+"/assignees",this);
root.retrieve().method("POST").withLogins(ASSIGNEES,assignees).to(getIssuesApiRoute()+"/assignees",this);
}
public void setAssignees(GHUser... assignees) throws IOException {
@@ -260,7 +340,7 @@ public class GHIssue extends GHObject implements Reactable{
}
public void setAssignees(Collection<GHUser> assignees) throws IOException {
editIssue("assignees",toLogins(assignees));
new Requester(root).withLogins(ASSIGNEES, assignees).method("PATCH").to(getIssuesApiRoute());
}
public void removeAssignees(GHUser... assignees) throws IOException {
@@ -268,16 +348,7 @@ public class GHIssue extends GHObject implements Reactable{
}
public void removeAssignees(Collection<GHUser> assignees) throws IOException {
List<String> names = toLogins(assignees);
root.retrieve().method("DELETE").with("assignees",names).inBody().to(getIssuesApiRoute()+"/assignees",this);
}
private List<String> toLogins(Collection<GHUser> assignees) {
List<String> names = new ArrayList<String>(assignees.size());
for (GHUser a : assignees) {
names.add(a.getLogin());
}
return names;
root.retrieve().method("DELETE").withLogins(ASSIGNEES,assignees).inBody().to(getIssuesApiRoute()+"/assignees",this);
}
protected String getApiRoute() {
@@ -288,8 +359,8 @@ public class GHIssue extends GHObject implements Reactable{
return "/repos/"+owner.getOwnerName()+"/"+owner.getName()+"/issues/"+number;
}
public GHUser getAssignee() {
return assignee;
public GHUser getAssignee() throws IOException {
return root.intern(assignee);
}
public List<GHUser> getAssignees() {
@@ -299,8 +370,8 @@ public class GHIssue extends GHObject implements Reactable{
/**
* User who submitted the issue.
*/
public GHUser getUser() {
return user;
public GHUser getUser() throws IOException {
return root.intern(user);
}
/**
@@ -311,12 +382,16 @@ public class GHIssue extends GHObject implements Reactable{
* even for an issue that's already closed. See
* https://github.com/kohsuke/github-api/issues/60.
*/
public GHUser getClosedBy() {
public GHUser getClosedBy() throws IOException {
if(!"closed".equals(state)) return null;
if(closed_by != null) return closed_by;
//TODO closed_by = owner.getIssue(number).getClosed_by();
return closed_by;
//TODO
/*
if (closed_by==null) {
closed_by = owner.getIssue(number).getClosed_by();
}
*/
return root.intern(closed_by);
}
public int getCommentsCount(){

View File

@@ -26,17 +26,19 @@ package org.kohsuke.github;
import java.io.IOException;
import java.net.URL;
import static org.kohsuke.github.Previews.SQUIRREL_GIRL;
import static org.kohsuke.github.Previews.*;
/**
* Comment to the issue
*
* @author Kohsuke Kawaguchi
* @see GHIssue#comment(String)
* @see GHIssue#listComments()
*/
public class GHIssueComment extends GHObject implements Reactable {
GHIssue owner;
private String body, gravatar_id;
private String body, gravatar_id, html_url, author_association;
private GHUser user; // not fully populated. beware.
/*package*/ GHIssueComment wrapUp(GHIssue owner) {
@@ -73,12 +75,13 @@ public class GHIssueComment extends GHObject implements Reactable {
return owner == null || owner.root.isOffline() ? user : owner.root.getUser(user.getLogin());
}
/**
* @deprecated This object has no HTML URL.
*/
@Override
public URL getHtmlUrl() {
return null;
return GitHub.parseURL(html_url);
}
public GHCommentAuthorAssociation getAuthorAssociation() {
return GHCommentAuthorAssociation.valueOf(author_association);
}
/**

View File

@@ -1,7 +1,5 @@
package org.kohsuke.github;
import java.util.Locale;
/**
* Search issues.
*

View File

@@ -1,7 +1,7 @@
package org.kohsuke.github;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
/**
* SSH public key.

View File

@@ -1,6 +1,9 @@
package org.kohsuke.github;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* @author Kohsuke Kawaguchi
@@ -34,4 +37,20 @@ public class GHLabel {
public void delete() throws IOException {
repo.root.retrieve().method("DELETE").to(url);
}
/**
* @param newColor
* 6-letter hex color code, like "f29513"
*/
public void setColor(String newColor) throws IOException {
repo.root.retrieve().method("PATCH").with("name", name).with("color", newColor).to(url);
}
/*package*/ static Collection<String> toNames(Collection<GHLabel> labels) {
List<String> r = new ArrayList<String>();
for (GHLabel l : labels) {
r.add(l.getName());
}
return r;
}
}

View File

@@ -32,7 +32,7 @@ import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import static org.kohsuke.github.Previews.DRAX;
import static org.kohsuke.github.Previews.*;
/**
* The GitHub Preview API's license information

View File

@@ -27,8 +27,8 @@ public class GHMilestone extends GHObject {
return owner;
}
public GHUser getCreator() {
return creator;
public GHUser getCreator() throws IOException {
return root.intern(creator);
}
public Date getDueOn() {

View File

@@ -6,7 +6,6 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

View File

@@ -2,15 +2,16 @@ package org.kohsuke.github;
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.apache.commons.lang.builder.ReflectionToStringBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.apache.commons.lang.reflect.FieldUtils;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import javax.annotation.CheckForNull;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* Most (all?) domain objects in GitHub seems to have these 4 properties.
@@ -18,14 +19,34 @@ import java.util.Date;
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
"NP_UNWRITTEN_FIELD"}, justification = "JSON API")
public abstract class GHObject {
/**
* Capture response HTTP headers on the state object.
*/
protected Map<String, List<String>> responseHeaderFields;
protected String url;
protected int id;
protected long id;
protected String created_at;
protected String updated_at;
/*package*/ GHObject() {
}
/**
* Returns the HTTP response headers given along with the state of this object.
*
* <p>
* Some of the HTTP headers have nothing to do with the object, for example "Cache-Control"
* and others are different depending on how this object was retrieved.
*
* This method was added as a kind of hack to allow the caller to retrieve OAuth scopes and such.
* Use with caution. The method might be removed in the future.
*/
@CheckForNull @Deprecated
public Map<String, List<String>> getResponseHeaderFields() {
return responseHeaderFields;
}
/**
* When was this resource created?
*/
@@ -63,14 +84,18 @@ public abstract class GHObject {
/**
* Unique ID number of this resource.
*/
@WithBridgeMethods(value=String.class, adapterMethod="intToString")
public int getId() {
@WithBridgeMethods(value={String.class,int.class}, adapterMethod="longToStringOrInt")
public long getId() {
return id;
}
@SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD", justification = "Bridge method of getId")
private Object intToString(int id, Class type) {
return String.valueOf(id);
private Object longToStringOrInt(long id, Class type) {
if (type==String.class)
return String.valueOf(id);
if (type==int.class)
return (int)id;
throw new AssertionError("Unexpected type: "+type);
}
@SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD", justification = "Bridge method of getHtmlUrl")

View File

@@ -50,7 +50,7 @@ public class GHOrganization extends GHPerson {
* You use the returned builder to set various properties, then call {@link GHCreateRepositoryBuilder#create()}
* to finally createa repository.
*/
public GHCreateRepositoryBuilder createRepository(String name) throws IOException {
public GHCreateRepositoryBuilder createRepository(String name) {
return new GHCreateRepositoryBuilder(root,"/orgs/"+login+"/repos",name);
}
@@ -269,7 +269,7 @@ public class GHOrganization extends GHPerson {
public PagedIterable<GHRepository> listRepositories(final int pageSize) {
return new PagedIterable<GHRepository>() {
public PagedIterator<GHRepository> _iterator(int pageSize) {
return new PagedIterator<GHRepository>(root.retrieve().asIterator("/orgs/" + login + "/repos?per_page=" + pageSize, GHRepository[].class, pageSize)) {
return new PagedIterator<GHRepository>(root.retrieve().asIterator("/orgs/" + login + "/repos", GHRepository[].class, pageSize)) {
@Override
protected void wrapUp(GHRepository[] page) {
for (GHRepository c : page)
@@ -277,7 +277,7 @@ public class GHOrganization extends GHPerson {
}
};
}
};
}.withPageSize(pageSize);
}
/**

View File

@@ -41,7 +41,7 @@ public abstract class GHPerson extends GHObject {
if (created_at!=null) {
return; // already populated
}
if (root.isOffline()) {
if (root == null || root.isOffline()) {
return; // cannot populate, will have to live with what we have
}
root.retrieve().to(url, this);
@@ -81,7 +81,7 @@ public abstract class GHPerson extends GHObject {
public PagedIterable<GHRepository> listRepositories(final int pageSize) {
return new PagedIterable<GHRepository>() {
public PagedIterator<GHRepository> _iterator(int pageSize) {
return new PagedIterator<GHRepository>(root.retrieve().asIterator("/users/" + login + "/repos?per_page=" + pageSize, GHRepository[].class, pageSize)) {
return new PagedIterator<GHRepository>(root.retrieve().asIterator("/users/" + login + "/repos", GHRepository[].class, pageSize)) {
@Override
protected void wrapUp(GHRepository[] page) {
for (GHRepository c : page)
@@ -89,7 +89,7 @@ public abstract class GHPerson extends GHObject {
}
};
}
};
}.withPageSize(pageSize);
}
/**
@@ -108,7 +108,7 @@ public abstract class GHPerson extends GHObject {
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, pageSize);
final Iterator<GHRepository[]> pager = root.retrieve().asIterator("/users/" + login + "/repos",GHRepository[].class, pageSize);
return new Iterator<List<GHRepository>>() {
public boolean hasNext() {

View File

@@ -23,20 +23,27 @@
*/
package org.kohsuke.github;
import javax.annotation.CheckForNull;
import java.io.IOException;
import java.net.URL;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
/**
* A pull request.
*
*
* @author Kohsuke Kawaguchi
* @see GHRepository#getPullRequest(int)
*/
@SuppressWarnings({"UnusedDeclaration"})
public class GHPullRequest extends GHIssue {
private static final String COMMENTS_ACTION = "/comments";
private static final String REQUEST_REVIEWERS = "/requested_reviewers";
private String patch_url, diff_url, issue_url;
private GHCommitPointer base;
private String merged_at;
@@ -44,14 +51,17 @@ public class GHPullRequest extends GHIssue {
// details that are only available when obtained from ID
private GHUser merged_by;
private int review_comments, additions;
private boolean merged;
private int review_comments, additions, commits;
private boolean merged, maintainer_can_modify;
private Boolean mergeable;
private int deletions;
private String mergeable_state;
private int changed_files;
private String merge_commit_sha;
// pull request reviewers
private GHUser[] requested_reviewers;
/**
* GitHub doesn't return some properties of {@link GHIssue} when requesting the GET on the 'pulls' API
* route as opposed to 'issues' API route. This flag remembers whether we made the GET call on the 'issues' route
@@ -70,6 +80,7 @@ public class GHPullRequest extends GHIssue {
if (base != null) base.wrapUp(root);
if (head != null) head.wrapUp(root);
if (merged_by != null) merged_by.wrapUp(root);
if (requested_reviewers != null) GHUser.wrap(requested_reviewers, root);
return this;
}
@@ -85,7 +96,7 @@ public class GHPullRequest extends GHIssue {
public URL getPatchUrl() {
return GitHub.parseURL(patch_url);
}
/**
* The URL of the patch file.
* like https://github.com/jenkinsci/jenkins/pull/100.patch
@@ -108,7 +119,7 @@ public class GHPullRequest extends GHIssue {
public GHCommitPointer getHead() {
return head;
}
@Deprecated
public Date getIssueUpdatedAt() throws IOException {
return super.getUpdatedAt();
@@ -161,13 +172,32 @@ public class GHPullRequest extends GHIssue {
return additions;
}
public int getCommits() throws IOException {
populate();
return commits;
}
public boolean isMerged() throws IOException {
populate();
return merged;
}
public Boolean getMergeable() throws IOException {
public boolean canMaintainerModify() throws IOException {
populate();
return maintainer_can_modify;
}
/**
* Is this PR mergeable?
*
* @return
* null if the state has not been determined yet, for example when a PR is newly created.
* If this method is called on an instance whose mergeable state is not yet known,
* API call is made to retrieve the latest state.
*/
public Boolean getMergeable() throws IOException {
if (mergeable==null)
refresh();
return mergeable;
}
@@ -194,13 +224,25 @@ public class GHPullRequest extends GHIssue {
return merge_commit_sha;
}
public List<GHUser> getRequestedReviewers() throws IOException {
populate();
return Collections.unmodifiableList(Arrays.asList(requested_reviewers));
}
/**
* 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
if (mergeable_state!=null) return; // already populated
refresh();
}
/**
* Repopulates this object.
*/
public void refresh() throws IOException {
if (root.isOffline()) {
return; // cannot populate, will have to live with what we have
}
@@ -208,7 +250,7 @@ public class GHPullRequest extends GHIssue {
}
/**
* Retrieves all the commits associated to this pull request.
* Retrieves all the files associated to this pull request.
*/
public PagedIterable<GHPullRequestFileDetail> listFiles() {
return new PagedIterable<GHPullRequestFileDetail>() {
@@ -223,13 +265,33 @@ public class GHPullRequest extends GHIssue {
};
}
/**
* Retrieves all the reviews associated to this pull request.
*/
public PagedIterable<GHPullRequestReview> listReviews() {
return new PagedIterable<GHPullRequestReview>() {
public PagedIterator<GHPullRequestReview> _iterator(int pageSize) {
return new PagedIterator<GHPullRequestReview>(root.retrieve()
.asIterator(String.format("%s/reviews", getApiRoute()),
GHPullRequestReview[].class, pageSize)) {
@Override
protected void wrapUp(GHPullRequestReview[] page) {
for (GHPullRequestReview r: page) {
r.wrapUp(GHPullRequest.this);
}
}
};
}
};
}
/**
* Obtains all the review comments associated with this pull request.
*/
public PagedIterable<GHPullRequestReviewComment> listReviewComments() throws IOException {
return new PagedIterable<GHPullRequestReviewComment>() {
public PagedIterator<GHPullRequestReviewComment> _iterator(int pageSize) {
return new PagedIterator<GHPullRequestReviewComment>(root.retrieve().asIterator(getApiRoute() + "/comments",
return new PagedIterator<GHPullRequestReviewComment>(root.retrieve().asIterator(getApiRoute() + COMMENTS_ACTION,
GHPullRequestReviewComment[].class, pageSize)) {
protected void wrapUp(GHPullRequestReviewComment[] page) {
for (GHPullRequestReviewComment c : page)
@@ -259,15 +321,47 @@ public class GHPullRequest extends GHIssue {
};
}
/**
* @deprecated
* Use {@link #createReview()}
*/
public GHPullRequestReview createReview(String body, @CheckForNull GHPullRequestReviewState event,
GHPullRequestReviewComment... comments) throws IOException {
return createReview(body, event, Arrays.asList(comments));
}
/**
* @deprecated
* Use {@link #createReview()}
*/
public GHPullRequestReview createReview(String body, @CheckForNull GHPullRequestReviewState event,
List<GHPullRequestReviewComment> comments) throws IOException {
GHPullRequestReviewBuilder b = createReview().body(body);
for (GHPullRequestReviewComment c : comments) {
b.comment(c.getBody(), c.getPath(), c.getPosition());
}
return b.create();
}
public GHPullRequestReviewBuilder createReview() {
return new GHPullRequestReviewBuilder(this);
}
public GHPullRequestReviewComment createReviewComment(String body, String sha, String path, int position) throws IOException {
return new Requester(root).method("POST")
.with("body", body)
.with("commit_id", sha)
.with("path", path)
.with("position", position)
.to(getApiRoute() + "/comments", GHPullRequestReviewComment.class).wrapUp(this);
.to(getApiRoute() + COMMENTS_ACTION, GHPullRequestReviewComment.class).wrapUp(this);
}
public void requestReviewers(List<GHUser> reviewers) throws IOException {
new Requester(root).method("POST")
.withLogins("reviewers", reviewers)
.to(getApiRoute() + REQUEST_REVIEWERS);
}
/**
* Merge this pull request.
*
@@ -291,12 +385,32 @@ public class GHPullRequest extends GHIssue {
* SHA that pull request head must match to allow merge.
*/
public void merge(String msg, String sha) throws IOException {
new Requester(root).method("PUT").with("commit_message",msg).with("sha",sha).to(getApiRoute()+"/merge");
merge(msg, sha, null);
}
/**
* Merge this pull request, using the specified merge method.
*
* The equivalent of the big green "Merge pull request" button.
*
* @param msg
* Commit message. If null, the default one will be used.
* @param method
* SHA that pull request head must match to allow merge.
*/
public void merge(String msg, String sha, MergeMethod method) throws IOException {
new Requester(root).method("PUT")
.with("commit_message", msg)
.with("sha", sha)
.with("merge_method", method)
.to(getApiRoute() + "/merge");
}
public enum MergeMethod{ MERGE, SQUASH, REBASE }
private void fetchIssue() throws IOException {
if (!fetchedIssueDetails) {
new Requester(root).to(getIssuesApiRoute(), this);
new Requester(root).method("GET").to(getIssuesApiRoute(), this);
fetchedIssueDetails = true;
}
}

View File

@@ -43,6 +43,7 @@ public class GHPullRequestFileDetail {
String raw_url;
String contents_url;
String patch;
String previous_filename;
public String getSha() {
return sha;
@@ -83,4 +84,9 @@ public class GHPullRequestFileDetail {
public String getPatch() {
return patch;
}
public String getPreviousFilename()
{
return previous_filename;
}
}

View File

@@ -0,0 +1,148 @@
/*
* The MIT License
*
* Copyright (c) 2017, CloudBees, Inc.
*
* 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 edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import javax.annotation.CheckForNull;
import java.io.IOException;
import java.net.URL;
/**
* Review to a pull request.
*
* @see GHPullRequest#listReviews()
* @see GHPullRequestReviewBuilder
*/
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_FIELD"}, justification = "JSON API")
public class GHPullRequestReview extends GHObject {
GHPullRequest owner;
private String body;
private GHUser user;
private String commit_id;
private GHPullRequestReviewState state;
/*package*/ GHPullRequestReview wrapUp(GHPullRequest owner) {
this.owner = owner;
return this;
}
/**
* Gets the pull request to which this review is associated.
*/
public GHPullRequest getParent() {
return owner;
}
/**
* The comment itself.
*/
public String getBody() {
return body;
}
/**
* Gets the user who posted this review.
*/
public GHUser getUser() throws IOException {
return owner.root.getUser(user.getLogin());
}
public String getCommitId() {
return commit_id;
}
@CheckForNull
public GHPullRequestReviewState getState() {
return state;
}
@Override
public URL getHtmlUrl() {
return null;
}
protected String getApiRoute() {
return owner.getApiRoute()+"/reviews/"+id;
}
/**
* @deprecated
* Former preview method that changed when it got public. Left here for backward compatibility.
* Use {@link #submit(String, GHPullRequestReviewEvent)}
*/
public void submit(String body, GHPullRequestReviewState state) throws IOException {
submit(body,state.toEvent());
}
/**
* Updates the comment.
*/
public void submit(String body, GHPullRequestReviewEvent event) throws IOException {
new Requester(owner.root).method("POST")
.with("body", body)
.with("event", event.action())
.to(getApiRoute()+"/events",this);
this.body = body;
this.state = event.toState();
}
/**
* Deletes this review.
*/
public void delete() throws IOException {
new Requester(owner.root).method("DELETE")
.to(getApiRoute());
}
/**
* Dismisses this review.
*/
public void dismiss(String message) throws IOException {
new Requester(owner.root).method("PUT")
.with("message", message)
.to(getApiRoute()+"/dismissals");
state = GHPullRequestReviewState.DISMISSED;
}
/**
* Obtains all the review comments associated with this pull request review.
*/
public PagedIterable<GHPullRequestReviewComment> listReviewComments() throws IOException {
return new PagedIterable<GHPullRequestReviewComment>() {
public PagedIterator<GHPullRequestReviewComment> _iterator(int pageSize) {
return new PagedIterator<GHPullRequestReviewComment>(
owner.root.retrieve()
.asIterator(getApiRoute() + "/comments",
GHPullRequestReviewComment[].class, pageSize)) {
protected void wrapUp(GHPullRequestReviewComment[] page) {
for (GHPullRequestReviewComment c : page)
c.wrapUp(owner);
}
};
}
};
}
}

View File

@@ -0,0 +1,91 @@
package org.kohsuke.github;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* Builds up a creation of new {@link GHPullRequestReview}.
*
* @author Kohsuke Kawaguchi
* @see GHPullRequest#createReview()
*/
public class GHPullRequestReviewBuilder {
private final GHPullRequest pr;
private final Requester builder;
private final List<DraftReviewComment> comments = new ArrayList<DraftReviewComment>();
/*package*/ GHPullRequestReviewBuilder(GHPullRequest pr) {
this.pr = pr;
this.builder = new Requester(pr.root);
}
// public GHPullRequestReview createReview(@Nullable String commitId, String body, GHPullRequestReviewEvent event,
// List<GHPullRequestReviewComment> comments) throws IOException
/**
* The SHA of the commit that needs a review. Not using the latest commit SHA may render your review comment outdated if a subsequent commit modifies the line you specify as the position. Defaults to the most recent commit in the pull request when you do not specify a value.
*/
public GHPullRequestReviewBuilder commitId(String commitId) {
builder.with("commit_id",commitId);
return this;
}
/**
* Required when using REQUEST_CHANGES or COMMENT for the event parameter. The body text of the pull request review.
*/
public GHPullRequestReviewBuilder body(String body) {
builder.with("body",body);
return this;
}
/**
* The review action you want to perform. The review actions include: APPROVE, REQUEST_CHANGES, or COMMENT.
* By leaving this blank, you set the review action state to PENDING,
* which means you will need to {@linkplain GHPullRequestReview#submit(String, GHPullRequestReviewEvent) submit the pull request review} when you are ready.
*/
public GHPullRequestReviewBuilder event(GHPullRequestReviewEvent event) {
builder.with("event",event.action());
return this;
}
/**
* @param body The relative path to the file that necessitates a review comment.
* @param path The position in the diff where you want to add a review comment. Note this value is not the same as the line number in the file. For help finding the position value, read the note below.
* @param position Text of the review comment.
*/
public GHPullRequestReviewBuilder comment(String body, String path, int position) {
comments.add(new DraftReviewComment(body,path,position));
return this;
}
public GHPullRequestReview create() throws IOException {
return builder.method("POST")._with("comments",comments)
.to(pr.getApiRoute() + "/reviews", GHPullRequestReview.class)
.wrapUp(pr);
}
private static class DraftReviewComment {
private String body;
private String path;
private int position;
DraftReviewComment(String body, String path, int position) {
this.body = body;
this.path = path;
this.position = position;
}
public String getBody() {
return body;
}
public String getPath() {
return path;
}
public int getPosition() {
return position;
}
}
}

View File

@@ -25,6 +25,7 @@ package org.kohsuke.github;
import java.io.IOException;
import java.net.URL;
import javax.annotation.CheckForNull;
import static org.kohsuke.github.Previews.*;
@@ -41,8 +42,22 @@ public class GHPullRequestReviewComment extends GHObject implements Reactable {
private String body;
private GHUser user;
private String path;
private int position;
private int originalPosition;
private int position = -1;
private int original_position = -1;
private long in_reply_to_id = -1L;
/**
* @deprecated
* You should be using {@link GHPullRequestReviewBuilder#comment(String, String, int)}
*/
public static GHPullRequestReviewComment draft(String body, String path, int position) {
GHPullRequestReviewComment result = new GHPullRequestReviewComment();
result.body = body;
result.path = path;
result.position = position;
return result;
}
/*package*/ GHPullRequestReviewComment wrapUp(GHPullRequest owner) {
this.owner = owner;
@@ -74,12 +89,18 @@ public class GHPullRequestReviewComment extends GHObject implements Reactable {
return path;
}
@CheckForNull
public int getPosition() {
return position;
}
public int getOriginalPosition() {
return originalPosition;
return original_position;
}
@CheckForNull
public long getInReplyToId() {
return in_reply_to_id;
}
@Override
@@ -106,6 +127,17 @@ public class GHPullRequestReviewComment extends GHObject implements Reactable {
new Requester(owner.root).method("DELETE").to(getApiRoute());
}
/**
* Create a new comment that replies to this comment.
*/
public GHPullRequestReviewComment reply(String body) throws IOException {
return new Requester(owner.root).method("POST")
.with("body", body)
.with("in_reply_to", getId())
.to(getApiRoute() + "/comments", GHPullRequestReviewComment.class)
.wrapUp(owner);
}
@Preview @Deprecated
public GHReaction createReaction(ReactionContent content) throws IOException {
return new Requester(owner.root)
@@ -118,7 +150,7 @@ public class GHPullRequestReviewComment extends GHObject implements Reactable {
public PagedIterable<GHReaction> listReactions() {
return new PagedIterable<GHReaction>() {
public PagedIterator<GHReaction> _iterator(int pageSize) {
return new PagedIterator<GHReaction>(owner.root.retrieve().withPreview(SQUIRREL_GIRL).asIterator(getApiRoute()+"/reactions", GHReaction[].class, pageSize)) {
return new PagedIterator<GHReaction>(owner.root.retrieve().withPreview(SQUIRREL_GIRL).asIterator(getApiRoute() + "/reactions", GHReaction[].class, pageSize)) {
@Override
protected void wrapUp(GHReaction[] page) {
for (GHReaction c : page)

View File

@@ -0,0 +1,51 @@
/*
* 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;
/**
* Action to perform on {@link GHPullRequestReview}.
*/
public enum GHPullRequestReviewEvent {
PENDING,
APPROVE,
REQUEST_CHANGES,
COMMENT;
/*package*/ String action() {
return this==PENDING ? null : name();
}
/**
* When a {@link GHPullRequestReview} is submitted with this event, it should transition to this state.
*/
/*package*/ GHPullRequestReviewState toState() {
switch (this) {
case PENDING: return GHPullRequestReviewState.PENDING;
case APPROVE: return GHPullRequestReviewState.APPROVED;
case REQUEST_CHANGES: return GHPullRequestReviewState.CHANGES_REQUESTED;
case COMMENT: return GHPullRequestReviewState.COMMENTED;
}
throw new IllegalStateException();
}
}

View File

@@ -0,0 +1,39 @@
package org.kohsuke.github;
/**
* Current state of {@link GHPullRequestReview}
*/
public enum GHPullRequestReviewState {
PENDING,
APPROVED,
CHANGES_REQUESTED,
/**
* @deprecated
* This was the thing when this API was in preview, but it changed when it became public.
* Use {@link #CHANGES_REQUESTED}. Left here for compatibility.
*/
REQUEST_CHANGES,
COMMENTED,
DISMISSED;
/**
* @deprecated
* This was an internal method accidentally exposed.
* Left here for compatibility.
*/
public String action() {
GHPullRequestReviewEvent e = toEvent();
return e==null ? null : e.action();
}
/*package*/ GHPullRequestReviewEvent toEvent() {
switch (this) {
case PENDING: return GHPullRequestReviewEvent.PENDING;
case APPROVED: return GHPullRequestReviewEvent.APPROVE;
case CHANGES_REQUESTED: return GHPullRequestReviewEvent.REQUEST_CHANGES;
case REQUEST_CHANGES: return GHPullRequestReviewEvent.REQUEST_CHANGES;
case COMMENTED: return GHPullRequestReviewEvent.COMMENT;
}
return null;
}
}

View File

@@ -1,6 +1,7 @@
package org.kohsuke.github;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.Date;
/**

View File

@@ -3,7 +3,7 @@ package org.kohsuke.github;
import java.io.IOException;
import java.net.URL;
import static org.kohsuke.github.Previews.SQUIRREL_GIRL;
import static org.kohsuke.github.Previews.*;
/**
* Reaction to issue, comment, PR, and so on.

View File

@@ -1,6 +1,7 @@
package org.kohsuke.github;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.IOException;
import java.net.URL;

View File

@@ -3,12 +3,13 @@ package org.kohsuke.github;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import static java.lang.String.format;
import static java.lang.String.*;
/**
* Release in a github repository.
@@ -45,10 +46,12 @@ public class GHRelease extends GHObject {
return draft;
}
/**
* @deprecated
* Use {@link #update()}
*/
public GHRelease setDraft(boolean draft) throws IOException {
edit("draft", draft);
this.draft = draft;
return this;
return update().draft(draft).update();
}
public URL getHtmlUrl() {
@@ -123,12 +126,21 @@ public class GHRelease extends GHObject {
* handling of the HTTP requests to github's API.
*/
public GHAsset uploadAsset(File file, String contentType) throws IOException {
FileInputStream s = new FileInputStream(file);
try {
return uploadAsset(file.getName(), s, contentType);
} finally {
s.close();
}
}
public GHAsset uploadAsset(String filename, InputStream stream, String contentType) throws IOException {
Requester builder = new Requester(owner.root);
String url = format("https://uploads.github.com%s/releases/%d/assets?name=%s",
owner.getApiTailUrl(""), getId(), file.getName());
owner.getApiTailUrl(""), getId(), filename);
return builder.contentType(contentType)
.with(new FileInputStream(file))
.with(stream)
.to(url, GHAsset.class).wrap(this);
}
@@ -149,10 +161,10 @@ public class GHRelease extends GHObject {
}
/**
* Edit this release.
* Updates this release via a builder.
*/
private void edit(String key, Object value) throws IOException {
new Requester(root)._with(key, value).method("PATCH").to(owner.getApiTailUrl("releases/"+id));
public GHReleaseUpdater update() {
return new GHReleaseUpdater(this);
}
private String getApiTailUrl(String end) {

View File

@@ -21,9 +21,7 @@ public class GHReleaseBuilder {
* @param body The release notes body.
*/
public GHReleaseBuilder body(String body) {
if (body != null) {
builder.with("body", body);
}
builder.with("body", body);
return this;
}
@@ -35,9 +33,7 @@ public class GHReleaseBuilder {
* already exists.
*/
public GHReleaseBuilder commitish(String commitish) {
if (commitish != null) {
builder.with("target_commitish", commitish);
}
builder.with("target_commitish", commitish);
return this;
}
@@ -56,9 +52,7 @@ public class GHReleaseBuilder {
* @param name the name of the release
*/
public GHReleaseBuilder name(String name) {
if (name != null) {
builder.with("name", name);
}
builder.with("name", name);
return this;
}

View File

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

View File

@@ -26,8 +26,7 @@ package org.kohsuke.github;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -35,7 +34,6 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.InterruptedIOException;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.util.AbstractSet;
import java.util.ArrayList;
@@ -50,6 +48,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.WeakHashMap;
import static java.util.Arrays.*;
import static org.kohsuke.github.Previews.*;
@@ -60,7 +59,7 @@ import static org.kohsuke.github.Previews.*;
* @author Kohsuke Kawaguchi
*/
@SuppressWarnings({"UnusedDeclaration"})
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
"NP_UNWRITTEN_FIELD"}, justification = "JSON API")
public class GHRepository extends GHObject {
/*package almost final*/ GitHub root;
@@ -76,15 +75,15 @@ public class GHRepository extends GHObject {
private String git_url, ssh_url, clone_url, svn_url, mirror_url;
private GHUser owner; // not fully populated. beware.
private boolean has_issues, has_wiki, fork, has_downloads, has_pages;
private boolean has_issues, has_wiki, fork, has_downloads, has_pages, archived;
@JsonProperty("private")
private boolean _private;
private int forks_count, stargazers_count, watchers_count, size, open_issues_count, subscribers_count;
private String pushed_at;
private Map<Integer,GHMilestone> milestones = new HashMap<Integer, GHMilestone>();
private Map<Integer,GHMilestone> milestones = new WeakHashMap<Integer, GHMilestone>();
private String default_branch,language;
private Map<String,GHCommit> commits = new HashMap<String, GHCommit>();
private Map<String,GHCommit> commits = new WeakHashMap<String, GHCommit>();
@SkipFromToString
private GHRepoPermission permissions;
@@ -95,18 +94,12 @@ public class GHRepository extends GHObject {
return new GHDeploymentBuilder(this,ref);
}
public PagedIterable<GHDeploymentStatus> getDeploymentStatuses(final int id) {
return new PagedIterable<GHDeploymentStatus>() {
public PagedIterator<GHDeploymentStatus> _iterator(int pageSize) {
return new PagedIterator<GHDeploymentStatus>(root.retrieve().asIterator(getApiTailUrl("deployments")+"/"+id+"/statuses", GHDeploymentStatus[].class, pageSize)) {
@Override
protected void wrapUp(GHDeploymentStatus[] page) {
for (GHDeploymentStatus c : page)
c.wrap(GHRepository.this);
}
};
}
};
/**
* @deprecated
* Use {@code getDeployment(id).listStatuses()}
*/
public PagedIterable<GHDeploymentStatus> getDeploymentStatuses(final int id) throws IOException {
return getDeployment(id).listStatuses();
}
public PagedIterable<GHDeployment> listDeployments(String sha,String ref,String task,String environment){
@@ -123,7 +116,13 @@ public class GHRepository extends GHObject {
};
}
};
}
/**
* Obtains a single {@link GHDeployment} by its ID.
*/
public GHDeployment getDeployment(long id) throws IOException {
return root.retrieve().to("deployments/" + id, GHDeployment.class).wrap(this);
}
private String join(List<String> params, String joinStr) {
@@ -140,8 +139,12 @@ public class GHRepository extends GHObject {
return StringUtils.trimToNull(value)== null? null: name+"="+value;
}
public GHDeploymentStatusBuilder createDeployStatus(int deploymentId, GHDeploymentState ghDeploymentState) {
return new GHDeploymentStatusBuilder(this,deploymentId,ghDeploymentState);
/**
* @deprecated
* Use {@code getDeployment(deploymentId).createStatus(ghDeploymentState)}
*/
public GHDeploymentStatusBuilder createDeployStatus(int deploymentId, GHDeploymentState ghDeploymentState) throws IOException {
return getDeployment(deploymentId).createStatus(ghDeploymentState);
}
private static class GHRepoPermission {
@@ -169,6 +172,14 @@ public class GHRepository extends GHObject {
* Gets the HTTPS URL to this repository, such as "https://github.com/kohsuke/jenkins.git"
* This URL is read-only.
*/
public String getHttpTransportUrl() {
return clone_url;
}
/**
* @deprecated
* Typo of {@link #getHttpTransportUrl()}
*/
public String gitHttpTransportUrl() {
return clone_url;
}
@@ -299,6 +310,30 @@ public class GHRepository extends GHObject {
return listReleases().asList();
}
public GHRelease getRelease(long id) throws IOException {
try {
return root.retrieve().to(getApiTailUrl("releases/" + id), GHRelease.class).wrap(this);
} catch (FileNotFoundException e) {
return null; // no release for this id
}
}
public GHRelease getReleaseByTagName(String tag) throws IOException {
try {
return root.retrieve().to(getApiTailUrl("releases/tags/" + tag), GHRelease.class).wrap(this);
} catch (FileNotFoundException e) {
return null; // no release for this tag
}
}
public GHRelease getLatestRelease() throws IOException {
try {
return root.retrieve().to(getApiTailUrl("releases/latest"), GHRelease.class).wrap(this);
} catch (FileNotFoundException e) {
return null; // no latest release
}
}
public PagedIterable<GHRelease> listReleases() throws IOException {
return new PagedIterable<GHRelease>() {
public PagedIterator<GHRelease> _iterator(int pageSize) {
@@ -359,6 +394,10 @@ public class GHRepository extends GHObject {
return fork;
}
public boolean isArchived() {
return archived;
}
/**
* Returns the number of all forks of this repository.
* This not only counts direct forks, but also forks of forks, and so on.
@@ -434,8 +473,8 @@ public class GHRepository extends GHObject {
public int getSize() {
return size;
}
/**
* Gets the collaborators on this repository.
* This set always appear to include the owner.
@@ -486,9 +525,8 @@ public class GHRepository extends GHObject {
* @throws FileNotFoundException under some conditions (e.g., private repo you can see but are not an admin of); treat as unknown
* @throws HttpException with a 403 under other conditions (e.g., public repo you have no special rights to); treat as unknown
*/
@Deprecated @Preview
public GHPermissionType getPermission(String user) throws IOException {
GHPermission perm = root.retrieve().withPreview(KORRA).to(getApiTailUrl("collaborators/" + user + "/permission"), GHPermission.class);
GHPermission perm = root.retrieve().to(getApiTailUrl("collaborators/" + user + "/permission"), GHPermission.class);
perm.wrapUp(root);
return perm.getPermissionType();
}
@@ -498,7 +536,6 @@ public class GHRepository extends GHObject {
* @throws FileNotFoundException under some conditions (e.g., private repo you can see but are not an admin of); treat as unknown
* @throws HttpException with a 403 under other conditions (e.g., public repo you have no special rights to); treat as unknown
*/
@Deprecated @Preview
public GHPermissionType getPermission(GHUser u) throws IOException {
return getPermission(u.getLogin());
}
@@ -784,6 +821,28 @@ public class GHRepository extends GHObject {
return GHRef.wrap(root.retrieve().to(String.format("/repos/%s/%s/git/refs", getOwnerName(), name), GHRef[].class), root);
}
/**
* Retrieves all refs for the github repository.
*
* @return paged iterable of all refs
* @throws IOException on failure communicating with GitHub, potentially due to an invalid ref type being requested
*/
public PagedIterable<GHRef> listRefs() throws IOException {
final String url = String.format("/repos/%s/%s/git/refs", getOwnerName(), name);
return new PagedIterable<GHRef>() {
public PagedIterator<GHRef> _iterator(int pageSize) {
return new PagedIterator<GHRef>(root.retrieve().asIterator(url, GHRef[].class, pageSize)) {
protected void wrapUp(GHRef[] page) {
for(GHRef p: page) {
p.wrap(root);
}
}
};
}
};
}
/**
* Retrieves all refs of the given type for the current GitHub repository.
* @param refType the type of reg to search for e.g. <tt>tags</tt> or <tt>commits</tt>
@@ -793,6 +852,27 @@ public class GHRepository extends GHObject {
public GHRef[] getRefs(String refType) throws IOException {
return GHRef.wrap(root.retrieve().to(String.format("/repos/%s/%s/git/refs/%s", getOwnerName(), name, refType), GHRef[].class),root);
}
/**
* Retrieves all refs of the given type for the current GitHub repository.
*
* @param refType the type of reg to search for e.g. <tt>tags</tt> or <tt>commits</tt>
* @return paged iterable of all refs of the specified type
* @throws IOException on failure communicating with GitHub, potentially due to an invalid ref type being requested
*/
public PagedIterable<GHRef> listRefs(String refType) throws IOException {
final String url = String.format("/repos/%s/%s/git/refs/%s", getOwnerName(), name, refType);
return new PagedIterable<GHRef>() {
public PagedIterator<GHRef> _iterator(int pageSize) {
return new PagedIterator<GHRef>(root.retrieve().asIterator(url, GHRef[].class, pageSize)) {
protected void wrapUp(GHRef[] page) {
// no-op
}
};
}
};
}
/**
* Retrive a ref of the given type for the current GitHub repository.
*
@@ -810,6 +890,18 @@ public class GHRepository extends GHObject {
refName = refName.replaceAll("#", "%23");
return root.retrieve().to(String.format("/repos/%s/%s/git/refs/%s", getOwnerName(), name, refName), GHRef.class).wrap(root);
}
/**
* Returns the <strong>annotated</strong> tag object. Only valid if the {@link GHRef#getObject()} has a
* {@link GHRef.GHObject#getType()} of {@code tag}.
*
* @param sha the sha of the tag object
* @return the annotated tag object
*/
public GHTagObject getTagObject(String sha) throws IOException {
return root.retrieve().to(getApiTailUrl("git/tags/" + sha), GHTagObject.class).wrap(this);
}
/**
* Retrive a tree of the given type for the current GitHub repository.
*
@@ -824,6 +916,10 @@ public class GHRepository extends GHObject {
return root.retrieve().to(url, GHTree.class).wrap(this);
}
public GHTreeBuilder createTree() {
return new GHTreeBuilder(this);
}
/**
* Retrieves the tree for the current GitHub repository, recursively as described in here:
* https://developer.github.com/v3/git/trees/#get-a-tree-recursively
@@ -853,6 +949,10 @@ public class GHRepository extends GHObject {
return root.retrieve().to(target, GHBlob.class);
}
public GHBlobBuilder createBlob() {
return new GHBlobBuilder(this);
}
/**
* Reads the content of a blob as a stream for better efficiency.
*
@@ -876,6 +976,10 @@ public class GHRepository extends GHObject {
return c;
}
public GHCommitBuilder createCommit() {
return new GHCommitBuilder(this);
}
/**
* Lists all the commits.
*/
@@ -1054,6 +1158,22 @@ public class GHRepository extends GHObject {
.to(getApiTailUrl("labels"), GHLabel.class).wrapUp(this);
}
/**
* Lists all the invitations.
*/
public PagedIterable<GHInvitation> listInvitations() {
return new PagedIterable<GHInvitation>() {
public PagedIterator<GHInvitation> _iterator(int pageSize) {
return new PagedIterator<GHInvitation>(root.retrieve().asIterator(String.format("/repos/%s/%s/invitations", getOwnerName(), name), GHInvitation[].class, pageSize)) {
protected void wrapUp(GHInvitation[] page) {
for (GHInvitation c : page)
c.wrapUp(root);
}
};
}
};
}
/**
* Lists all the subscribers (aka watchers.)
*
@@ -1146,7 +1266,7 @@ public class GHRepository extends GHObject {
* @deprecated
* Use {@link #getHooks()} and {@link #createHook(String, Map, Collection, boolean)}
*/
@SuppressFBWarnings(value = "DMI_COLLECTION_OF_URLS",
@SuppressFBWarnings(value = "DMI_COLLECTION_OF_URLS",
justification = "It causes a performance degradation, but we have already exposed it to the API")
public Set<URL> getPostCommitHooks() {
return postCommitHooks;
@@ -1155,7 +1275,7 @@ public class GHRepository extends GHObject {
/**
* Live set view of the post-commit hook.
*/
@SuppressFBWarnings(value = "DMI_COLLECTION_OF_URLS",
@SuppressFBWarnings(value = "DMI_COLLECTION_OF_URLS",
justification = "It causes a performance degradation, but we have already exposed it to the API")
@SkipFromToString
private final Set<URL> postCommitHooks = new AbstractSet<URL>() {
@@ -1311,41 +1431,43 @@ public class GHRepository extends GHObject {
return requester.to(getApiTailUrl("readme"), GHContent.class).wrap(this);
}
/**
* Creates a new content, or update an existing content.
*/
public GHContentBuilder createContent() {
return new GHContentBuilder(this);
}
/**
* Use {@link #createContent()}.
*/
@Deprecated
public GHContentUpdateResponse createContent(String content, String commitMessage, String path) throws IOException {
return createContent(content, commitMessage, path, null);
return createContent().content(content).message(commitMessage).path(path).commit();
}
/**
* Use {@link #createContent()}.
*/
@Deprecated
public GHContentUpdateResponse createContent(String content, String commitMessage, String path, String branch) throws IOException {
final byte[] payload;
try {
payload = content.getBytes("UTF-8");
} catch (UnsupportedEncodingException ex) {
throw (IOException) new IOException("UTF-8 encoding is not supported").initCause(ex);
}
return createContent(payload, commitMessage, path, branch);
return createContent().content(content).message(commitMessage).path(path).branch(branch).commit();
}
/**
* Use {@link #createContent()}.
*/
@Deprecated
public GHContentUpdateResponse createContent(byte[] contentBytes, String commitMessage, String path) throws IOException {
return createContent(contentBytes, commitMessage, path, null);
return createContent().content(contentBytes).message(commitMessage).path(path).commit();
}
/**
* Use {@link #createContent()}.
*/
@Deprecated
public GHContentUpdateResponse createContent(byte[] contentBytes, String commitMessage, String path, String branch) throws IOException {
Requester requester = new Requester(root)
.with("path", path)
.with("message", commitMessage)
.with("content", Base64.encodeBase64String(contentBytes))
.method("PUT");
if (branch != null) {
requester.with("branch", branch);
}
GHContentUpdateResponse response = requester.to(getApiTailUrl("contents/" + path), GHContentUpdateResponse.class);
response.getContent().wrap(this);
response.getCommit().wrapUp(this);
return response;
return createContent().content(contentBytes).message(commitMessage).path(path).branch(branch).commit();
}
public GHMilestone createMilestone(String title, String description) throws IOException {
@@ -1453,7 +1575,7 @@ public class GHRepository extends GHObject {
public boolean equals(Object obj) {
// We ignore contributions in the calculation
return super.equals(obj);
}
}
}
/**
@@ -1481,6 +1603,19 @@ public class GHRepository extends GHObject {
return new GHNotificationStream(root,getApiTailUrl("/notifications"));
}
/**
* <a href="https://developer.github.com/v3/repos/traffic/#views">https://developer.github.com/v3/repos/traffic/#views</a>
*/
public GHRepositoryViewTraffic getViewTraffic() throws IOException{
return root.retrieve().to(getApiTailUrl("/traffic/views"), GHRepositoryViewTraffic.class);
}
/**
* <a href="https://developer.github.com/v3/repos/traffic/#clones">https://developer.github.com/v3/repos/traffic/#clones</a>
*/
public GHRepositoryCloneTraffic getCloneTraffic() throws IOException{
return root.retrieve().to(getApiTailUrl("/traffic/clones"), GHRepositoryCloneTraffic.class);
}
@Override
public int hashCode() {

View File

@@ -0,0 +1,37 @@
package org.kohsuke.github;
import java.util.List;
/**
* Repository clone statistics.
*
* @see GHRepository#getCloneTraffic()
*/
public class GHRepositoryCloneTraffic extends GHRepositoryTraffic {
private List<DailyInfo> clones;
/*package*/ GHRepositoryCloneTraffic() {
}
/*package*/ GHRepositoryCloneTraffic(Integer count, Integer uniques, List<DailyInfo> clones) {
super(count, uniques);
this.clones = clones;
}
public List<DailyInfo> getClones() {
return clones;
}
public List<DailyInfo> getDailyInfo() {
return getClones();
}
public static class DailyInfo extends GHRepositoryTraffic.DailyInfo {
/*package*/ DailyInfo() {
}
/*package*/ DailyInfo(String timestamp, int count, int uniques) {
super(timestamp, count, uniques);
}
}
}

View File

@@ -1,7 +1,5 @@
package org.kohsuke.github;
import java.util.Locale;
/**
* Search repositories.
*
@@ -57,6 +55,10 @@ public class GHRepositorySearchBuilder extends GHSearchBuilder<GHRepository> {
return q("stars:"+v);
}
public GHRepositorySearchBuilder topic(String v) {
return q("topic:"+v);
}
public GHRepositorySearchBuilder order(GHDirection v) {
req.with("order",v);
return this;

View File

@@ -0,0 +1,54 @@
package org.kohsuke.github;
import java.util.Date;
import java.util.List;
public abstract class GHRepositoryTraffic implements TrafficInfo {
private int count;
private int uniques;
/*package*/ GHRepositoryTraffic() {
}
/*package*/ GHRepositoryTraffic(int count, int uniques) {
this.count = count;
this.uniques = uniques;
}
public int getCount() {
return count;
}
public int getUniques() {
return uniques;
}
public abstract List<? extends DailyInfo> getDailyInfo();
public static abstract class DailyInfo implements TrafficInfo {
private String timestamp;
private int count;
private int uniques;
public Date getTimestamp() {
return GitHub.parseDate(timestamp);
}
public int getCount() {
return count;
}
public int getUniques() {
return uniques;
}
/*package*/ DailyInfo() {
}
/*package*/ DailyInfo(String timestamp, Integer count, Integer uniques) {
this.timestamp = timestamp;
this.count = count;
this.uniques = uniques;
}
}
}

View File

@@ -0,0 +1,37 @@
package org.kohsuke.github;
import java.util.List;
/**
* Repository view statistics.
*
* @see GHRepository#getViewTraffic()
*/
public class GHRepositoryViewTraffic extends GHRepositoryTraffic {
private List<DailyInfo> views;
/*package*/ GHRepositoryViewTraffic() {
}
/*package*/ GHRepositoryViewTraffic(int count, int uniques, List<DailyInfo> views) {
super(count, uniques);
this.views = views;
}
public List<DailyInfo> getViews() {
return views;
}
public List<DailyInfo> getDailyInfo() {
return getViews();
}
public static class DailyInfo extends GHRepositoryTraffic.DailyInfo {
/*package*/ DailyInfo() {
}
/*package*/ DailyInfo(String timestamp, int count, int uniques) {
super(timestamp, count, uniques);
}
}
}

View File

@@ -1,6 +1,6 @@
package org.kohsuke.github;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.List;

View File

@@ -0,0 +1,60 @@
package org.kohsuke.github;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
/**
* Represents an annotated tag in a {@link GHRepository}
*
* @see GHRepository#getTagObject(String)
*/
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
"NP_UNWRITTEN_FIELD"}, justification = "JSON API")
public class GHTagObject {
private GHRepository owner;
private GitHub root;
private String tag;
private String sha;
private String url;
private String message;
private GitUser tagger;
private GHRef.GHObject object;
/*package*/ GHTagObject wrap(GHRepository owner) {
this.owner = owner;
this.root = owner.root;
return this;
}
public GHRepository getOwner() {
return owner;
}
public GitHub getRoot() {
return root;
}
public String getTag() {
return tag;
}
public String getSha() {
return sha;
}
public String getUrl() {
return url;
}
public String getMessage() {
return message;
}
public GitUser getTagger() {
return tagger;
}
public GHRef.GHObject getObject() {
return object;
}
}

View File

@@ -18,6 +18,18 @@ public class GHTeam {
protected /*final*/ GHOrganization org;
/** Member's role in a team */
public enum Role {
/**
* A normal member of the team
*/
MEMBER,
/**
* Able to add/remove other team members, promote other team members to team maintainer, and edit the team's name and description.
*/
MAINTAINER
}
/*package*/ GHTeam wrapUp(GHOrganization owner) {
this.org = owner;
return this;
@@ -116,6 +128,22 @@ public class GHTeam {
org.root.retrieve().method("PUT").to(api("/memberships/" + u.getLogin()), null);
}
/**
* Adds a member to the team
*
* The user will be invited to the organization if required.
*
* @param user github user
* @param role role for the new member
*
* @throws IOException
*/
public void add(GHUser user, Role role) throws IOException {
org.root.retrieve().method("PUT")
.with("role", role.name())
.to(api("/memberships/" + user.getLogin()), null);
}
/**
* Removes a member to the team.
*/
@@ -129,7 +157,7 @@ public class GHTeam {
public void add(GHRepository r, GHOrganization.Permission permission) throws IOException {
org.root.retrieve().method("PUT")
.with("permission",permission)
.with("permission", permission)
.to(api("/repos/" + r.getOwnerName() + '/' + r.getName()), null);
}
@@ -145,7 +173,7 @@ public class GHTeam {
}
private String api(String tail) {
return "/teams/"+id+tail;
return "/teams/" + id + tail;
}
public GHOrganization getOrganization() {

View File

@@ -1,6 +1,7 @@
package org.kohsuke.github;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;

View File

@@ -0,0 +1,90 @@
package org.kohsuke.github;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* Builder pattern for creating a new tree.
* Based on https://developer.github.com/v3/git/trees/#create-a-tree
*/
public class GHTreeBuilder {
private final GHRepository repo;
private final Requester req;
private final List<TreeEntry> treeEntries = new ArrayList<TreeEntry>();
@SuppressFBWarnings("URF_UNREAD_FIELD")
private static final class TreeEntry {
private final String path;
private final String mode;
private final String type;
private String sha;
private String content;
private TreeEntry(String path, String mode, String type) {
this.path = path;
this.mode = mode;
this.type = type;
}
}
GHTreeBuilder(GHRepository repo) {
this.repo = repo;
req = new Requester(repo.root);
}
/**
* @param baseTree the SHA of tree you want to update with new data
*/
public GHTreeBuilder baseTree(String baseTree) {
req.with("base_tree", baseTree);
return this;
}
/**
* Adds a new entry to the tree.
* Exactly one of the parameters {@code sha} and {@code content} must be non-null.
*/
public GHTreeBuilder entry(String path, String mode, String type, String sha, String content) {
TreeEntry entry = new TreeEntry(path, mode, type);
entry.sha = sha;
entry.content = content;
treeEntries.add(entry);
return this;
}
/**
* Specialized version of {@link #entry(String, String, String, String, String)} for adding an existing blob referred by its SHA.
*/
public GHTreeBuilder shaEntry(String path, String sha, boolean executable) {
TreeEntry entry = new TreeEntry(path, executable ? "100755" : "100644", "blob");
entry.sha = sha;
treeEntries.add(entry);
return this;
}
/**
* Specialized version of {@link #entry(String, String, String, String, String)} for adding a text file with the specified {@code content}.
*/
public GHTreeBuilder textEntry(String path, String content, boolean executable) {
TreeEntry entry = new TreeEntry(path, executable ? "100755" : "100644", "blob");
entry.content = content;
treeEntries.add(entry);
return this;
}
private String getApiTail() {
return String.format("/repos/%s/%s/git/trees", repo.getOwnerName(), repo.getName());
}
/**
* Creates a tree based on the parameters specified thus far.
*/
public GHTree create() throws IOException {
req._with("tree", treeEntries);
return req.method("POST").to(getApiTail(), GHTree.class).wrap(repo);
}
}

View File

@@ -1,7 +1,5 @@
package org.kohsuke.github;
import java.util.Locale;
/**
* Search users.
*

View File

@@ -24,9 +24,16 @@
package org.kohsuke.github;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.introspect.VisibilityChecker.Std;
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
import org.apache.commons.codec.Charsets;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -42,23 +49,18 @@ import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.logging.Logger;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import org.apache.commons.codec.Charsets;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.ANY;
import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.NONE;
import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
import static java.util.logging.Level.FINE;
import static org.kohsuke.github.Previews.DRAX;
import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.*;
import static java.net.HttpURLConnection.*;
import static java.util.logging.Level.*;
import static org.kohsuke.github.Previews.*;
/**
* Root of the GitHub API.
@@ -79,9 +81,10 @@ public class GitHub {
*/
/*package*/ final String encodedAuthorization;
private final Map<String,GHUser> users = new Hashtable<String, GHUser>();
private final Map<String,GHOrganization> orgs = new Hashtable<String, GHOrganization>();
private final ConcurrentMap<String,GHUser> users;
private final ConcurrentMap<String,GHOrganization> orgs;
// Cache of myself object.
private GHMyself myself;
private final String apiUrl;
/*package*/ final RateLimitHandler rateLimitHandler;
@@ -146,6 +149,8 @@ public class GitHub {
}
}
users = new ConcurrentHashMap<String, GHUser>();
orgs = new ConcurrentHashMap<String, GHOrganization>();
this.rateLimitHandler = rateLimitHandler;
this.abuseLimitHandler = abuseLimitHandler;
@@ -161,6 +166,16 @@ public class GitHub {
return GitHubBuilder.fromCredentials().build();
}
/**
* Version that connects to GitHub Enterprise.
*
* @deprecated
* Use {@link #connectToEnterpriseWithOAuth(String, String, String)}
*/
public static GitHub connectToEnterprise(String apiUrl, String oauthAccessToken) throws IOException {
return connectToEnterpriseWithOAuth(apiUrl,null,oauthAccessToken);
}
/**
* Version that connects to GitHub Enterprise.
*
@@ -169,10 +184,16 @@ public class GitHub {
* "http://ghe.acme.com/api/v3". Note that GitHub Enterprise has <tt>/api/v3</tt> in the URL.
* For historical reasons, this parameter still accepts the bare domain name, but that's considered deprecated.
*/
public static GitHub connectToEnterprise(String apiUrl, String oauthAccessToken) throws IOException {
return new GitHubBuilder().withEndpoint(apiUrl).withOAuthToken(oauthAccessToken).build();
public static GitHub connectToEnterpriseWithOAuth(String apiUrl, String login, String oauthAccessToken) throws IOException {
return new GitHubBuilder().withEndpoint(apiUrl).withOAuthToken(oauthAccessToken, login).build();
}
/**
* Version that connects to GitHub Enterprise.
*
* @deprecated
* Use with caution. Login with password is not a preferred method.
*/
public static GitHub connectToEnterprise(String apiUrl, String login, String password) throws IOException {
return new GitHubBuilder().withEndpoint(apiUrl).withPassword(login, password).build();
}
@@ -357,13 +378,15 @@ public class GitHub {
@WithBridgeMethods(GHUser.class)
public GHMyself getMyself() throws IOException {
requireCredential();
synchronized (this) {
if (this.myself != null) return myself;
GHMyself u = retrieve().to("/user", GHMyself.class);
GHMyself u = retrieve().to("/user", GHMyself.class);
u.root = this;
users.put(u.getLogin(), u);
return u;
u.root = this;
this.myself = u;
return u;
}
}
/**
@@ -379,7 +402,7 @@ public class GitHub {
return u;
}
/**
* clears all cached data in order for external changes (modifications and del
*/
@@ -401,6 +424,9 @@ public class GitHub {
return u;
}
/**
* Gets {@link GHOrganization} specified by name.
*/
public GHOrganization getOrganization(String name) throws IOException {
GHOrganization o = orgs.get(name);
if (o==null) {
@@ -410,6 +436,35 @@ public class GitHub {
return o;
}
/**
* Gets a list of all organizations.
*/
public PagedIterable<GHOrganization> listOrganizations() {
return listOrganizations(null);
}
/**
* Gets a list of all organizations starting after the organization identifier specified by 'since'.
*
* @see <a href="https://developer.github.com/v3/orgs/#parameters">List All Orgs - Parameters</a>
*/
public PagedIterable<GHOrganization> listOrganizations(final String since) {
return new PagedIterable<GHOrganization>() {
@Override
public PagedIterator<GHOrganization> _iterator(int pageSize) {
System.out.println("page size: " + pageSize);
return new PagedIterator<GHOrganization>(retrieve().with("since",since)
.asIterator("/organizations", GHOrganization[].class, pageSize)) {
@Override
protected void wrapUp(GHOrganization[] page) {
for (GHOrganization c : page)
c.wrapUp(GitHub.this);
}
};
}
};
}
/**
* Gets the repository object from 'user/reponame' string that GitHub calls as "repository name"
*
@@ -475,6 +530,15 @@ public class GitHub {
}
/**
* Gets complete list of open invitations for current user.
*/
public List<GHInvitation> getMyInvitations() throws IOException {
GHInvitation[] invitations = retrieve().to("/user/repository_invitations", GHInvitation[].class);
for (GHInvitation i : invitations) {
i.wrapUp(this);
}
return Arrays.asList(invitations);
}
/**
* This method returns a shallowly populated organizations.
@@ -641,6 +705,18 @@ public class GitHub {
}
}
/*package*/ GHUser intern(GHUser user) throws IOException {
if (user==null) return user;
// if we already have this user in our map, use it
GHUser u = users.get(user.getLogin());
if (u!=null) return u;
// if not, remember this new user
users.putIfAbsent(user.getLogin(),user);
return user;
}
private static class GHApiInfo {
private String rate_limit_url;
@@ -719,6 +795,14 @@ public class GitHub {
}
}
/**
* Search commits.
*/
@Preview @Deprecated
public GHCommitSearchBuilder searchCommits() {
return new GHCommitSearchBuilder(this);
}
/**
* Search issues.
*/
@@ -766,7 +850,7 @@ public class GitHub {
* This provides a dump of every public repository, in the order that they were created.
*
* @param since
* The integer ID of the last Repository that youve seen. See {@link GHRepository#getId()}
* The numeric ID of the last Repository that youve seen. See {@link GHRepository#getId()}
* @see <a href="https://developer.github.com/v3/repos/#list-all-public-repositories">documentation</a>
*/
public PagedIterable<GHRepository> listAllPublicRepositories(final String since) {
@@ -835,6 +919,7 @@ public class GitHub {
static {
MAPPER.setVisibilityChecker(new Std(NONE, NONE, NONE, NONE, ANY));
MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
MAPPER.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS, true);
}
/* package */ static final String GITHUB_URL = "https://api.github.com";

View File

@@ -154,6 +154,12 @@ public class GitHubBuilder {
return self;
}
/**
* @param endpoint
* 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 GitHubBuilder withEndpoint(String endpoint) {
this.endpoint = endpoint;
return this;

View File

@@ -1,6 +1,7 @@
package org.kohsuke.github;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.Date;
/**

View File

@@ -5,7 +5,6 @@ import org.kohsuke.github.extras.ImpatientHttpConnector;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.TimeUnit;
/**
* Pluggability for customizing HTTP request behaviors or using altogether different library.

View File

@@ -1,11 +1,10 @@
package org.kohsuke.github;
import javax.annotation.CheckForNull;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import javax.annotation.CheckForNull;
/**
* {@link IOException} for http exceptions because {@link HttpURLConnection} throws un-discerned
* {@link IOException} and it can help to know the http response code to decide how to handle an

View File

@@ -1,6 +1,5 @@
package org.kohsuke.github;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;

View File

@@ -2,7 +2,6 @@ package org.kohsuke.github;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.IOException;
import java.util.Iterator;
/**

View File

@@ -4,8 +4,9 @@ package org.kohsuke.github;
* @author Kohsuke Kawaguchi
*/
/*package*/ class Previews {
static final String LOKI = "application/vnd.github.loki-preview+json";
static final String LUKE_CAGE = "application/vnd.github.luke-cage-preview+json";
static final String DRAX = "application/vnd.github.drax-preview+json";
static final String SQUIRREL_GIRL = "application/vnd.github.squirrel-girl-preview";
static final String KORRA = "application/vnd.github.korra-preview";
static final String CLOAK = "application/vnd.github.cloak-preview";
static final String ZZZAX = "application/vnd.github.zzzax-preview+json";
}

View File

@@ -37,7 +37,7 @@ public abstract class RateLimitHandler {
public void onError(IOException e, HttpURLConnection uc) throws IOException {
try {
Thread.sleep(parseWaitTime(uc));
} catch (InterruptedException _) {
} catch (InterruptedException x) {
throw (InterruptedIOException)new InterruptedIOException().initCause(e);
}
}

View File

@@ -25,6 +25,11 @@ package org.kohsuke.github;
import com.fasterxml.jackson.databind.JsonMappingException;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import javax.annotation.CheckForNull;
import javax.annotation.WillClose;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
@@ -36,6 +41,7 @@ import java.lang.reflect.Field;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
@@ -52,12 +58,12 @@ import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.GZIPInputStream;
import javax.annotation.WillClose;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import static java.util.Arrays.asList;
import static java.util.logging.Level.*;
import static java.util.logging.Level.FINE;
import static java.util.logging.Level.FINEST;
import static java.util.logging.Level.INFO;
import static org.apache.commons.lang3.StringUtils.defaultString;
import static org.kohsuke.github.GitHub.MAPPER;
/**
@@ -74,7 +80,7 @@ class Requester {
* Request method.
*/
private String method = "POST";
private String contentType = "application/x-www-form-urlencoded";
private String contentType = null;
private InputStream body;
/**
@@ -129,6 +135,10 @@ class Requester {
return _with(key, value);
}
public Requester with(String key, long value) {
return _with(key, value);
}
public Requester with(String key, Integer value) {
if (value!=null)
_with(key, value);
@@ -159,6 +169,14 @@ class Requester {
return _with(key, value);
}
public Requester withLogins(String key, Collection<GHUser> users) {
List<String> names = new ArrayList<String>(users.size());
for (GHUser a : users) {
names.add(a.getLogin());
}
return with(key,names);
}
public Requester with(String key, Map<String, String> value) {
return _with(key, value);
}
@@ -168,6 +186,11 @@ class Requester {
return this;
}
public Requester withNullable(String key, Object value) {
args.add(new Entry(key, value));
return this;
}
public Requester _with(String key, Object value) {
if (value!=null) {
args.add(new Entry(key,value));
@@ -268,7 +291,7 @@ class Requester {
if (nextLinkMatcher.find()) {
final String link = nextLinkMatcher.group(1);
T nextResult = _to(link, type, instance);
setResponseHeaders(nextResult);
final int resultLength = Array.getLength(result);
final int nextResultLength = Array.getLength(nextResult);
T concatResult = (T) Array.newInstance(type.getComponentType(), resultLength + nextResultLength);
@@ -278,7 +301,7 @@ class Requester {
}
}
}
return result;
return setResponseHeaders(result);
} catch (IOException e) {
handleApiError(e);
} finally {
@@ -312,7 +335,7 @@ class Requester {
setupConnection(root.getApiURL(tailApiUrl));
buildRequest();
try {
return wrapStream(uc.getInputStream());
} catch (IOException e) {
@@ -385,18 +408,19 @@ class Requester {
private void buildRequest() throws IOException {
if (isMethodWithBody()) {
uc.setDoOutput(true);
uc.setRequestProperty("Content-type", contentType);
if (body == null) {
uc.setRequestProperty("Content-type", defaultString(contentType,"application/json"));
Map json = new HashMap();
for (Entry e : args) {
json.put(e.key, e.value);
}
MAPPER.writeValue(uc.getOutputStream(), json);
} else {
uc.setRequestProperty("Content-type", defaultString(contentType,"application/x-www-form-urlencoded"));
try {
byte[] bytes = new byte[32768];
int read = 0;
int read;
while ((read = body.read(bytes)) != -1) {
uc.getOutputStream().write(bytes, 0, read);
}
@@ -441,7 +465,7 @@ class Requester {
try {
return new PagingIterator<T>(type, tailApiUrl, root.getApiURL(s.toString()));
} catch (IOException e) {
throw new Error(e);
throw new GHException("Unable to build github Api URL",e);
}
}
@@ -502,7 +526,7 @@ class Requester {
}
}
} catch (IOException e) {
throw new Error(e);
throw new GHException("Failed to retrieve "+url);
}
}
@@ -578,7 +602,12 @@ class Requester {
throw new IllegalStateException("Failed to set the request method to "+method);
}
@CheckForNull
private <T> T parse(Class<T> type, T instance) throws IOException {
return parse(type, instance, 2);
}
private <T> T parse(Class<T> type, T instance, int timeouts) throws IOException {
InputStreamReader r = null;
int responseCode = -1;
String responseMessage = null;
@@ -597,24 +626,44 @@ class Requester {
String data = IOUtils.toString(r);
if (type!=null)
try {
return MAPPER.readValue(data,type);
return setResponseHeaders(MAPPER.readValue(data, type));
} catch (JsonMappingException e) {
throw (IOException)new IOException("Failed to deserialize " +data).initCause(e);
}
if (instance!=null)
return MAPPER.readerForUpdating(instance).<T>readValue(data);
if (instance!=null) {
return setResponseHeaders(MAPPER.readerForUpdating(instance).<T>readValue(data));
}
return null;
} catch (FileNotFoundException e) {
// java.net.URLConnection handles 404 exception has FileNotFoundException, don't wrap exception in HttpException
// to preserve backward compatibility
throw e;
} catch (IOException e) {
if (e instanceof SocketTimeoutException && timeouts > 0) {
LOGGER.log(INFO, "timed out accessing " + uc.getURL() + "; will try " + timeouts + " more time(s)", e);
return parse(type, instance, timeouts - 1);
}
throw new HttpException(responseCode, responseMessage, uc.getURL(), e);
} finally {
IOUtils.closeQuietly(r);
}
}
private <T> T setResponseHeaders(T readValue) {
if (readValue instanceof GHObject[]) {
for (GHObject ghObject : (GHObject[]) readValue) {
setResponseHeaders(ghObject);
}
} else if (readValue instanceof GHObject) {
setResponseHeaders((GHObject) readValue);
}
return readValue;
}
private void setResponseHeaders(GHObject readValue) {
readValue.responseHeaderFields = uc.getHeaderFields();
}
/**
* Handles the "Content-Encoding" header.
*/
@@ -647,13 +696,13 @@ class Requester {
String error = IOUtils.toString(es, "UTF-8");
if (e instanceof FileNotFoundException) {
// pass through 404 Not Found to allow the caller to handle it intelligently
e = (IOException) new FileNotFoundException(error).initCause(e);
e = (IOException) new GHFileNotFoundException(error).withResponseHeaderFields(uc).initCause(e);
} else if (e instanceof HttpException) {
HttpException http = (HttpException) e;
e = new HttpException(error, http.getResponseCode(), http.getResponseMessage(),
http.getUrl(), e);
} else {
e = (IOException) new IOException(error).initCause(e);
e = (IOException) new GHIOException(error).withResponseHeaderFields(uc).initCause(e);
}
} finally {
IOUtils.closeQuietly(es);

View File

@@ -0,0 +1,16 @@
package org.kohsuke.github;
/**
* @author Kohsuke Kawaguchi
*/
public interface TrafficInfo {
/**
* Total count of hits.
*/
int getCount();
/**
* Unique visitors.
*/
int getUniques();
}

View File

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

View File

@@ -1,13 +1,25 @@
package org.kohsuke.github.extras;
import com.squareup.okhttp.ConnectionSpec;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.OkUrlFactory;
import org.kohsuke.github.HttpConnector;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.List;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
/**
* {@link HttpConnector} for {@link OkHttpClient}.
*
@@ -23,10 +35,33 @@ public class OkHttpConnector implements HttpConnector {
private final OkUrlFactory urlFactory;
public OkHttpConnector(OkUrlFactory urlFactory) {
urlFactory.client().setSslSocketFactory(TlsSocketFactory());
urlFactory.client().setConnectionSpecs(TlsConnectionSpecs());
this.urlFactory = urlFactory;
}
public HttpURLConnection connect(URL url) throws IOException {
return urlFactory.open(url);
}
/** Returns TLSv1.2 only SSL Socket Factory. */
private SSLSocketFactory TlsSocketFactory() {
SSLContext sc;
try {
sc = SSLContext.getInstance("TLSv1.2");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e.getMessage(), e);
}
try {
sc.init(null, null, null);
return sc.getSocketFactory();
} catch (KeyManagementException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
/** Returns connection spec with TLS v1.2 in it */
private List<ConnectionSpec> TlsConnectionSpecs() {
return Arrays.asList(ConnectionSpec.MODERN_TLS, ConnectionSpec.CLEARTEXT);
}
}

View File

@@ -1,10 +1,7 @@
import org.kohsuke.github.GHRepository;
import org.kohsuke.github.GHRepository.Contributor;
import org.kohsuke.github.GHUser;
import org.kohsuke.github.GitHub;
import java.util.Collection;
/**
* @author Kohsuke Kawaguchi
*/

View File

@@ -1,5 +1,8 @@
package org.kohsuke.github;
import java.io.FileInputStream;
import java.util.Properties;
import org.apache.commons.io.IOUtils;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Before;
@@ -19,9 +22,17 @@ public abstract class AbstractGitHubApiTestBase extends Assert {
public void setUp() throws Exception {
File f = new File(System.getProperty("user.home"), ".github.kohsuke2");
if (f.exists()) {
Properties props = new Properties();
FileInputStream in = null;
try {
in = new FileInputStream(f);
props.load(in);
} finally {
IOUtils.closeQuietly(in);
}
// use the non-standard credential preferentially, so that developers of this library do not have
// to clutter their event stream.
gitHub = GitHubBuilder.fromPropertyFile(f.getPath()).withRateLimitHandler(RateLimitHandler.FAIL).build();
gitHub = GitHubBuilder.fromProperties(props).withRateLimitHandler(RateLimitHandler.FAIL).build();
} else {
gitHub = GitHubBuilder.fromCredentials().withRateLimitHandler(RateLimitHandler.FAIL).build();
}

View File

@@ -5,8 +5,6 @@ import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import org.apache.commons.io.IOUtils;
import org.hamcrest.CoreMatchers;
import org.junit.Assume;
import org.junit.Test;
import org.kohsuke.github.GHCommit.File;
import org.kohsuke.github.GHOrganization.Permission;
@@ -16,7 +14,6 @@ import java.io.InputStream;
import java.net.URL;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.ExecutionException;
import java.util.regex.Pattern;
import static org.hamcrest.CoreMatchers.*;
@@ -137,10 +134,10 @@ public class AppTest extends AbstractGitHubApiTestBase {
.description("question")
.payload("{\"user\":\"atmos\",\"room_id\":123456}")
.create();
GHDeploymentStatus ghDeploymentStatus = repository.createDeployStatus(deployment.getId(), GHDeploymentState.SUCCESS)
GHDeploymentStatus ghDeploymentStatus = deployment.createStatus(GHDeploymentState.SUCCESS)
.description("success")
.targetUrl("http://www.github.com").create();
Iterable<GHDeploymentStatus> deploymentStatuses = repository.getDeploymentStatuses(deployment.getId());
Iterable<GHDeploymentStatus> deploymentStatuses = deployment.listStatuses();
assertNotNull(deploymentStatuses);
assertEquals(1,Iterables.size(deploymentStatuses));
assertEquals(ghDeploymentStatus.getId(), Iterables.get(deploymentStatuses, 0).getId());
@@ -307,7 +304,7 @@ public class AppTest extends AbstractGitHubApiTestBase {
@Test
public void testMembership() throws Exception {
Set<String> members = gitHub.getOrganization("jenkinsci").getRepository("violations-plugin").getCollaboratorNames();
Set<String> members = gitHub.getOrganization("github-api-test-org").getRepository("jenkins").getCollaboratorNames();
System.out.println(members.contains("kohsuke"));
}
@@ -685,6 +682,15 @@ public class AppTest extends AbstractGitHubApiTestBase {
assertFalse(all.isEmpty());
}
@Test
public void testCommitSearch() throws IOException {
PagedSearchIterable<GHCommit> r = gitHub.searchCommits().author("kohsuke").list();
assertTrue(r.getTotalCount() > 0);
GHCommit firstCommit = r.iterator().next();
assertTrue(firstCommit.getFiles().size() > 0);
}
@Test
public void testIssueSearch() throws IOException {
PagedSearchIterable<GHIssue> r = gitHub.searchIssues().mentions("kohsuke").isOpen().list();
@@ -747,6 +753,10 @@ public class AppTest extends AbstractGitHubApiTestBase {
assertEquals(t.getColor(), "123456");
assertEquals(t.getColor(), t2.getColor());
assertEquals(t.getUrl(), t2.getUrl());
t.setColor("000000");
GHLabel t3 = r.getLabel("test");
assertEquals(t3.getColor(), "000000");
t.delete();
}
}
@@ -778,7 +788,7 @@ public class AppTest extends AbstractGitHubApiTestBase {
GHRepository r = itr.next();
System.out.println(r.getFullName());
assertNotNull(r.getUrl());
assertNotEquals(0,r.getId());
assertNotEquals(0L,r.getId());
}
}

View File

@@ -1,7 +1,6 @@
package org.kohsuke.github;
import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
import org.junit.Test;
import java.io.IOException;

View File

@@ -0,0 +1,103 @@
package org.kohsuke.github;
import org.junit.Before;
import org.junit.Test;
import org.kohsuke.github.GHBranchProtection.EnforceAdmins;
import org.kohsuke.github.GHBranchProtection.RequiredReviews;
import org.kohsuke.github.GHBranchProtection.RequiredStatusChecks;
import java.io.FileNotFoundException;
public class GHBranchProtectionTest extends AbstractGitHubApiTestBase {
private static final String BRANCH = "bp-test";
private static final String BRANCH_REF = "heads/" + BRANCH;
private GHBranch branch;
private GHRepository repo;
@Before
@Override
public void setUp() throws Exception {
super.setUp();
repo = gitHub.getRepository("github-api-test-org/GHContentIntegrationTest").fork();
try {
repo.getRef(BRANCH_REF);
} catch (FileNotFoundException e) {
repo.createRef("refs/" + BRANCH_REF, repo.getBranch("master").getSHA1());
}
branch = repo.getBranch(BRANCH);
if (branch.isProtected()) {
GHBranchProtection protection = branch.getProtection();
if (protection.getRequiredSignatures()) {
protection.disableSignedCommits();
}
assertFalse(protection.getRequiredSignatures());
branch.disableProtection();
}
branch = repo.getBranch(BRANCH);
assertFalse(branch.isProtected());
}
@Test
public void testEnableBranchProtections() throws Exception {
// team/user restrictions require an organization repo to test against
GHBranchProtection protection = branch.enableProtection()
.addRequiredChecks("test-status-check")
.requireBranchIsUpToDate()
.requireCodeOwnReviews()
.dismissStaleReviews()
.requiredReviewers(2)
.includeAdmins()
.enable();
RequiredStatusChecks statusChecks = protection.getRequiredStatusChecks();
assertNotNull(statusChecks);
assertTrue(statusChecks.isRequiresBranchUpToDate());
assertTrue(statusChecks.getContexts().contains("test-status-check"));
RequiredReviews requiredReviews = protection.getRequiredReviews();
assertNotNull(requiredReviews);
assertTrue(requiredReviews.isDismissStaleReviews());
assertTrue(requiredReviews.isRequireCodeOwnerReviews());
assertEquals(2, requiredReviews.getRequiredReviewers());
EnforceAdmins enforceAdmins = protection.getEnforceAdmins();
assertNotNull(enforceAdmins);
assertTrue(enforceAdmins.isEnabled());
}
@Test
public void testEnableProtectionOnly() throws Exception {
branch.enableProtection().enable();
assertTrue(repo.getBranch(BRANCH).isProtected());
}
@Test
public void testEnableRequireReviewsOnly() throws Exception {
GHBranchProtection protection = branch.enableProtection()
.requireReviews()
.enable();
assertNotNull(protection.getRequiredReviews());
}
@Test
public void testSignedCommits() throws Exception {
GHBranchProtection protection = branch.enableProtection().enable();
assertFalse(protection.getRequiredSignatures());
protection.enabledSignedCommits();
assertTrue(protection.getRequiredSignatures());
protection.disableSignedCommits();
assertFalse(protection.getRequiredSignatures());
}
}

View File

@@ -20,13 +20,6 @@ public class GHContentIntegrationTest extends AbstractGitHubApiTestBase {
repo = gitHub.getRepository("github-api-test-org/GHContentIntegrationTest").fork();
}
@Test
public void testBranchProtection() throws Exception {
GHBranch b = repo.getBranch("master");
b.enableProtection(EnforcementLevel.NON_ADMINS, "foo/bar");
b.disableProtection();
}
@Test
public void testGetFileContent() throws Exception {
GHContent content = repo.getFileContent("ghcontent-ro/a-file-with-content");
@@ -60,7 +53,8 @@ public class GHContentIntegrationTest extends AbstractGitHubApiTestBase {
@Test
public void testCRUDContent() throws Exception {
GHContentUpdateResponse created = repo.createContent("this is an awesome file I created\n", "Creating a file for integration tests.", createdFilename);
GHContentUpdateResponse created =
repo.createContent("this is an awesome file I created\n", "Creating a file for integration tests.", createdFilename);
GHContent createdContent = created.getContent();
assertNotNull(created.getCommit());

View File

@@ -120,9 +120,19 @@ public class GHEventPayloadTest {
assertThat(event.getSender().getLogin(), is("baxterthehacker"));
}
// TODO implement support classes and write test
// @Test
// public void issues() throws Exception {}
@Test
public void issues() throws Exception {
GHEventPayload.Issue event = GitHub.offline().parseEventPayload(payload.asReader(),GHEventPayload.Issue.class);
assertThat(event.getAction(),is("opened"));
assertThat(event.getIssue().getNumber(), is(2));
assertThat(event.getIssue().getTitle(), is("Spelling error in the README file"));
assertThat(event.getIssue().getState(), is(GHIssueState.OPEN));
assertThat(event.getIssue().getLabels().size(), is(1));
assertThat(event.getIssue().getLabels().iterator().next().getName(), is("bug"));
assertThat(event.getRepository().getName(), is("public-repo"));
assertThat(event.getRepository().getOwner().getLogin(), is("baxterthehacker"));
assertThat(event.getSender().getLogin(), is("baxterthehacker"));
}
// TODO implement support classes and write test
// @Test
@@ -187,13 +197,61 @@ public class GHEventPayloadTest {
assertThat(event.getSender().getLogin(), is("baxterthehacker"));
}
// TODO implement support classes and write test
// @Test
// public void pull_request_review() throws Exception {}
@Test
public void pull_request_review() throws Exception {
GHEventPayload.PullRequestReview event =
GitHub.offline().parseEventPayload(payload.asReader(), GHEventPayload.PullRequestReview.class);
assertThat(event.getAction(), is("submitted"));
assertThat(event.getReview().getId(), is(2626884L));
assertThat(event.getReview().getBody(), is("Looks great!\n"));
assertThat(event.getReview().getState(), is(GHPullRequestReviewState.APPROVED));
assertThat(event.getPullRequest().getNumber(), is(8));
assertThat(event.getPullRequest().getTitle(), is("Add a README description"));
assertThat(event.getPullRequest().getBody(), is("Just a few more details"));
assertThat(event.getPullRequest().getUser().getLogin(), is("skalnik"));
assertThat(event.getPullRequest().getHead().getUser().getLogin(), is("skalnik"));
assertThat(event.getPullRequest().getHead().getRef(), is("patch-2"));
assertThat(event.getPullRequest().getHead().getLabel(), is("skalnik:patch-2"));
assertThat(event.getPullRequest().getHead().getSha(), is("b7a1f9c27caa4e03c14a88feb56e2d4f7500aa63"));
assertThat(event.getPullRequest().getBase().getUser().getLogin(), is("baxterthehacker"));
assertThat(event.getPullRequest().getBase().getRef(), is("master"));
assertThat(event.getPullRequest().getBase().getLabel(), is("baxterthehacker:master"));
assertThat(event.getPullRequest().getBase().getSha(), is("9049f1265b7d61be4a8904a9a27120d2064dab3b"));
assertThat(event.getRepository().getName(), is("public-repo"));
assertThat(event.getRepository().getOwner().getLogin(), is("baxterthehacker"));
assertThat(event.getSender().getLogin(), is("baxterthehacker"));
}
// TODO implement support classes and write test
// @Test
// public void pull_request_review_comment() throws Exception {}
@Test
public void pull_request_review_comment() throws Exception {
GHEventPayload.PullRequestReviewComment event =
GitHub.offline().parseEventPayload(payload.asReader(), GHEventPayload.PullRequestReviewComment.class);
assertThat(event.getAction(), is("created"));
assertThat(event.getComment().getBody(), is("Maybe you should use more emojji on this line."));
assertThat(event.getPullRequest().getNumber(), is(1));
assertThat(event.getPullRequest().getTitle(), is("Update the README with new information"));
assertThat(event.getPullRequest().getBody(), is("This is a pretty simple change that we need to pull into master."));
assertThat(event.getPullRequest().getUser().getLogin(), is("baxterthehacker"));
assertThat(event.getPullRequest().getHead().getUser().getLogin(), is("baxterthehacker"));
assertThat(event.getPullRequest().getHead().getRef(), is("changes"));
assertThat(event.getPullRequest().getHead().getLabel(), is("baxterthehacker:changes"));
assertThat(event.getPullRequest().getHead().getSha(), is("0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c"));
assertThat(event.getPullRequest().getBase().getUser().getLogin(), is("baxterthehacker"));
assertThat(event.getPullRequest().getBase().getRef(), is("master"));
assertThat(event.getPullRequest().getBase().getLabel(), is("baxterthehacker:master"));
assertThat(event.getPullRequest().getBase().getSha(), is("9049f1265b7d61be4a8904a9a27120d2064dab3b"));
assertThat(event.getRepository().getName(), is("public-repo"));
assertThat(event.getRepository().getOwner().getLogin(), is("baxterthehacker"));
assertThat(event.getSender().getLogin(), is("baxterthehacker"));
}
@Test
public void push() throws Exception {

View File

@@ -0,0 +1,78 @@
package org.kohsuke.github;
import org.apache.commons.lang.StringUtils;
import org.junit.Ignore;
import org.junit.Test;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasKey;
import static org.hamcrest.Matchers.hasValue;
import static org.hamcrest.core.IsInstanceOf.instanceOf;
import static org.junit.Assert.assertThat;
/**
* @author Kanstantsin Shautsou
*/
public class GHHookTest {
@Ignore
@Test
public void exposeResponceHeaders() throws Exception {
String user1Login = "KostyaSha-auto";
String user1Pass = "secret";
String clientId = "90140219451";
String clientSecret = "1451245425";
String orgRepo = "KostyaSha-org/test";
// some login based user that has access to application
final GitHub gitHub = GitHub.connectUsingPassword(user1Login, user1Pass);
gitHub.getMyself();
// we request read
final List<String> scopes = Arrays.asList("repo", "read:org", "user:email", "read:repo_hook");
// application creates token with scopes
final GHAuthorization auth = gitHub.createOrGetAuth(clientId, clientSecret, scopes, "", "");
String token = auth.getToken();
if (StringUtils.isEmpty(token)) {
gitHub.deleteAuth(auth.getId());
token = gitHub.createOrGetAuth(clientId, clientSecret, scopes, "", "").getToken();
}
/// now create connection using token
final GitHub gitHub2 = GitHub.connectUsingOAuth(token);
// some repo in organisation
final GHRepository repository = gitHub2.getRepository(orgRepo);
// doesn't fail because we have read access
final List<GHHook> hooks = repository.getHooks();
try {
// fails because application isn't approved in organisation and you can find it only after doing real call
final GHHook hook = repository.createHook(
"my-hook",
singletonMap("url", "http://localhost"),
singletonList(GHEvent.PUSH),
true
);
} catch (IOException ex) {
assertThat(ex, instanceOf(GHFileNotFoundException.class));
final GHFileNotFoundException ghFileNotFoundException = (GHFileNotFoundException) ex;
final Map<String, List<String>> responseHeaderFields = ghFileNotFoundException.getResponseHeaderFields();
assertThat(responseHeaderFields, hasKey("X-Accepted-OAuth-Scopes"));
assertThat(responseHeaderFields.get("X-Accepted-OAuth-Scopes"),
hasItem("admin:repo_hook, public_repo, repo, write:repo_hook")
);
}
}
}

View File

@@ -5,12 +5,14 @@ import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
@@ -156,4 +158,16 @@ public class GitHubTest {
System.out.println(u.getName());
}
}
@Test
public void getOrgs() throws IOException {
GitHub hub = GitHub.connect();
int iterations = 10;
Set<Long> orgIds = new HashSet<Long>();
for (GHOrganization org : Iterables.limit(hub.listOrganizations().withPageSize(2), iterations)) {
orgIds.add(org.getId());
System.out.println(org.getName());
}
assertThat(orgIds.size(), equalTo(iterations));
}
}

View File

@@ -7,6 +7,8 @@ import java.io.IOException;
import java.util.Collection;
import java.util.List;
import static org.hamcrest.CoreMatchers.*;
/**
* @author Kohsuke Kawaguchi
*/
@@ -26,6 +28,35 @@ public class PullRequestTest extends AbstractGitHubApiTestBase {
p.comment("Some comment");
}
@Test
public void testPullRequestReviews() throws Exception {
String name = rnd.next();
GHPullRequest p = getRepository().createPullRequest(name, "stable", "master", "## test");
GHPullRequestReview draftReview = p.createReview()
.body("Some draft review")
.comment("Some niggle", "changelog.html", 1)
.create();
assertThat(draftReview.getState(), is(GHPullRequestReviewState.PENDING));
assertThat(draftReview.getBody(), is("Some draft review"));
assertThat(draftReview.getCommitId(), notNullValue());
List<GHPullRequestReview> reviews = p.listReviews().asList();
assertThat(reviews.size(), is(1));
GHPullRequestReview review = reviews.get(0);
assertThat(review.getState(), is(GHPullRequestReviewState.PENDING));
assertThat(review.getBody(), is("Some draft review"));
assertThat(review.getCommitId(), notNullValue());
draftReview.submit("Some review comment", GHPullRequestReviewEvent.COMMENT);
List<GHPullRequestReviewComment> comments = review.listReviewComments().asList();
assertEquals(1, comments.size());
GHPullRequestReviewComment comment = comments.get(0);
assertEquals("Some niggle", comment.getBody());
draftReview = p.createReview()
.body("Some new review")
.comment("Some niggle", "changelog.html", 1)
.create();
draftReview.delete();
}
@Test
public void testPullRequestReviewComments() throws Exception {
String name = rnd.next();
@@ -69,6 +100,41 @@ public class PullRequestTest extends AbstractGitHubApiTestBase {
fail();
}
@Test
public void testSquashMerge() throws Exception {
String name = rnd.next();
GHRef masterRef = getRepository().getRef("heads/master");
GHRef branchRef = getRepository().createRef("refs/heads/" + name, masterRef.getObject().getSha());
getRepository().createContent(name, name, name, name);
Thread.sleep(1000);
GHPullRequest p = getRepository().createPullRequest(name, name, "master", "## test squash");
Thread.sleep(1000);
p.merge("squash merge", null, GHPullRequest.MergeMethod.SQUASH);
branchRef.delete();
}
@Test
public void testUpdateContentSquashMerge() throws Exception {
String name = rnd.next();
GHRef masterRef = getRepository().getRef("heads/master");
GHRef branchRef = getRepository().createRef("refs/heads/" + name, masterRef.getObject().getSha());
GHContentUpdateResponse response = getRepository().createContent(name, name, name, name);
Thread.sleep(1000);
getRepository().createContent()
.content(name + name)
.path(name)
.branch(name)
.message(name)
.sha(response.getContent().getSha())
.commit();
GHPullRequest p = getRepository().createPullRequest(name, name, "master", "## test squash");
Thread.sleep(1000);
p.merge("squash merge", null, GHPullRequest.MergeMethod.SQUASH);
branchRef.delete();
}
@Test
// Requires push access to the test repo to pass
public void setLabels() throws Exception {

View File

@@ -67,6 +67,62 @@ public class RepositoryTest extends AbstractGitHubApiTestBase {
}
}
}
@Test
public void LatestRepositoryExist() {
try {
// add the repository that have latest release
GHRelease release = gitHub.getRepository("kamontat/CheckIDNumber").getLatestRelease();
assertEquals("v3.0", release.getTagName());
} catch (IOException e) {
e.printStackTrace();
fail();
}
}
@Test
public void LatestRepositoryNotExist() {
try {
// add the repository that `NOT` have latest release
GHRelease release = gitHub.getRepository("kamontat/Java8Example").getLatestRelease();
assertNull(release);
} catch (IOException e) {
e.printStackTrace();
fail();
}
}
@Test public void listReleases() throws IOException {
PagedIterable<GHRelease> releases = gitHub.getOrganization("github").getRepository("hub").listReleases();
assertTrue(releases.iterator().hasNext());
}
@Test
public void getReleaseExists() throws IOException {
GHRelease release = gitHub.getOrganization("github").getRepository("hub").getRelease(6839710);
assertEquals("v2.3.0-pre10", release.getTagName());
}
@Test
public void getReleaseDoesNotExist() throws IOException {
GHRelease release = gitHub.getOrganization("github").getRepository("hub").getRelease(Long.MAX_VALUE);
assertNull(release);
}
@Test
public void getReleaseByTagNameExists() throws IOException {
GHRelease release = gitHub.getOrganization("github").getRepository("hub").getReleaseByTagName("v2.3.0-pre10");
assertNotNull(release);
assertEquals("v2.3.0-pre10", release.getTagName());
}
@Test
public void getReleaseByTagNameDoesNotExist() throws IOException {
GHRelease release = getRepository().getReleaseByTagName("foo-bar-baz");
assertNull(release);
}
private GHRepository getRepository() throws IOException {
return gitHub.getOrganization("github-api-test-org").getRepository("jenkins");

View File

@@ -0,0 +1,167 @@
package org.kohsuke.github;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.io.IOUtils;
import org.junit.Assert;
import org.junit.Test;
import org.kohsuke.github.GHRepositoryTraffic.DailyInfo;
import org.mockito.Mockito;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.TimeZone;
public class RepositoryTrafficTest {
final private String login = "kohsuke", repositoryName = "github-api";
@SuppressWarnings("unchecked")
private <T extends GHRepositoryTraffic> void checkResponse(T expected, T actual){
Assert.assertEquals(expected.getCount(), actual.getCount());
Assert.assertEquals(expected.getUniques(), actual.getUniques());
List<? extends DailyInfo> expectedList = expected.getDailyInfo();
List<? extends DailyInfo> actualList = actual.getDailyInfo();
Iterator<? extends DailyInfo> expectedIt;
Iterator<? extends DailyInfo> actualIt;
Assert.assertEquals(expectedList.size(), actualList.size());
expectedIt = expectedList.iterator();
actualIt = actualList.iterator();
while(expectedIt.hasNext() && actualIt.hasNext()) {
DailyInfo expectedDailyInfo = expectedIt.next();
DailyInfo actualDailyInfo = actualIt.next();
Assert.assertEquals(expectedDailyInfo.getCount(), actualDailyInfo.getCount());
Assert.assertEquals(expectedDailyInfo.getUniques(), actualDailyInfo.getUniques());
Assert.assertEquals(expectedDailyInfo.getTimestamp(), actualDailyInfo.getTimestamp());
}
}
private <T extends GHRepositoryTraffic> void testTraffic(T expectedResult) throws IOException{
SimpleDateFormat dateFormat=new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
ObjectMapper mapper = new ObjectMapper().setDateFormat(dateFormat);
String mockedResponse = mapper.writeValueAsString(expectedResult);
GitHub gitHub = GitHub.connect(login, null);
GitHub gitHubSpy = Mockito.spy(gitHub);
GHRepository repo = gitHubSpy.getUser(login).getRepository(repositoryName);
// accessing traffic info requires push access to the repo
// since we don't have that, let the mocking begin...
HttpConnector connectorSpy = Mockito.spy(gitHubSpy.getConnector());
Mockito.doReturn(connectorSpy).when(gitHubSpy).getConnector();
// also known as the "uc" in the Requester class
HttpURLConnection mockHttpURLConnection = Mockito.mock(HttpURLConnection.class);
// needed for Requester.setRequestMethod
Mockito.doReturn("GET").when(mockHttpURLConnection).getRequestMethod();
// this covers calls on "uc" in Requester.setupConnection and Requester.buildRequest
URL trafficURL = new URL(
"https://api.github.com/repos/"+login+"/"+repositoryName+"/traffic/" +
((expectedResult instanceof GHRepositoryViewTraffic) ? "views" : "clones")
);
Mockito.doReturn(mockHttpURLConnection).when(connectorSpy).connect(Mockito.eq(trafficURL));
// make Requester.parse work
Mockito.doReturn(200).when(mockHttpURLConnection).getResponseCode();
Mockito.doReturn("OK").when(mockHttpURLConnection).getResponseMessage();
InputStream stubInputStream = IOUtils.toInputStream(mockedResponse, "UTF-8");
Mockito.doReturn(stubInputStream).when(mockHttpURLConnection).getInputStream();
if(expectedResult instanceof GHRepositoryViewTraffic){
GHRepositoryViewTraffic views = repo.getViewTraffic();
checkResponse(expectedResult, views);
}
else if(expectedResult instanceof GHRepositoryCloneTraffic) {
GHRepositoryCloneTraffic clones = repo.getCloneTraffic();
checkResponse(expectedResult, clones);
}
}
@Test
public void testGetViews() throws IOException{
GHRepositoryViewTraffic expectedResult = new GHRepositoryViewTraffic(
21523359,
65534,
Arrays.asList(
new GHRepositoryViewTraffic.DailyInfo("2016-10-10T00:00:00Z", 3, 2),
new GHRepositoryViewTraffic.DailyInfo("2016-10-11T00:00:00Z", 9, 4),
new GHRepositoryViewTraffic.DailyInfo("2016-10-12T00:00:00Z", 27, 8),
new GHRepositoryViewTraffic.DailyInfo("2016-10-13T00:00:00Z", 81, 16),
new GHRepositoryViewTraffic.DailyInfo("2016-10-14T00:00:00Z", 243, 32),
new GHRepositoryViewTraffic.DailyInfo("2016-10-15T00:00:00Z", 729, 64),
new GHRepositoryViewTraffic.DailyInfo("2016-10-16T00:00:00Z", 2187, 128),
new GHRepositoryViewTraffic.DailyInfo("2016-10-17T00:00:00Z", 6561, 256),
new GHRepositoryViewTraffic.DailyInfo("2016-10-18T00:00:00Z", 19683, 512),
new GHRepositoryViewTraffic.DailyInfo("2016-10-19T00:00:00Z", 59049, 1024),
new GHRepositoryViewTraffic.DailyInfo("2016-10-20T00:00:00Z", 177147, 2048),
new GHRepositoryViewTraffic.DailyInfo("2016-10-21T00:00:00Z", 531441, 4096),
new GHRepositoryViewTraffic.DailyInfo("2016-10-22T00:00:00Z", 1594323, 8192),
new GHRepositoryViewTraffic.DailyInfo("2016-10-23T00:00:00Z", 4782969, 16384),
new GHRepositoryViewTraffic.DailyInfo("2016-10-24T00:00:00Z", 14348907, 32768)
)
);
testTraffic(expectedResult);
}
@Test
public void testGetClones() throws IOException{
GHRepositoryCloneTraffic expectedResult = new GHRepositoryCloneTraffic(
1500,
455,
Arrays.asList(
new GHRepositoryCloneTraffic.DailyInfo("2016-10-10T00:00:00Z", 10,3),
new GHRepositoryCloneTraffic.DailyInfo("2016-10-11T00:00:00Z", 20,6),
new GHRepositoryCloneTraffic.DailyInfo("2016-10-12T00:00:00Z", 30,5),
new GHRepositoryCloneTraffic.DailyInfo("2016-10-13T00:00:00Z", 40,7),
new GHRepositoryCloneTraffic.DailyInfo("2016-10-14T00:00:00Z", 50,11),
new GHRepositoryCloneTraffic.DailyInfo("2016-10-15T00:00:00Z", 60,12),
new GHRepositoryCloneTraffic.DailyInfo("2016-10-16T00:00:00Z", 70,19),
new GHRepositoryCloneTraffic.DailyInfo("2016-10-17T00:00:00Z", 170,111),
new GHRepositoryCloneTraffic.DailyInfo("2016-10-18T00:00:00Z", 180,70),
new GHRepositoryCloneTraffic.DailyInfo("2016-10-19T00:00:00Z", 190,10),
new GHRepositoryCloneTraffic.DailyInfo("2016-10-20T00:00:00Z", 200,18),
new GHRepositoryCloneTraffic.DailyInfo("2016-10-21T00:00:00Z", 210,8),
new GHRepositoryCloneTraffic.DailyInfo("2016-10-22T00:00:00Z", 220,168),
new GHRepositoryCloneTraffic.DailyInfo("2016-10-23T00:00:00Z", 5,2),
new GHRepositoryCloneTraffic.DailyInfo("2016-10-24T00:00:00Z", 45,5)
)
);
testTraffic(expectedResult);
}
@Test
public void testGetTrafficStatsAccessFailureDueToInsufficientPermissions() throws IOException {
String errorMsg = "Exception should be thrown, since we don't have permission to access repo traffic info.";
GitHub gitHub = GitHub.connect(login, null);
GHRepository repo = gitHub.getUser(login).getRepository(repositoryName);
try {
repo.getViewTraffic();
Assert.fail(errorMsg);
}
catch (HttpException ex){
}
try {
repo.getCloneTraffic();
Assert.fail(errorMsg);
}
catch (HttpException ex){
}
}
}

View File

@@ -14,17 +14,17 @@ public class UserTest extends AbstractGitHubApiTestBase {
public void listFollowsAndFollowers() throws IOException {
GHUser u = gitHub.getUser("rtyler");
assertNotEquals(
count50(u.listFollowers()),
count50(u.listFollows()));
count30(u.listFollowers()),
count30(u.listFollows()));
}
private Set<GHUser> count50(PagedIterable<GHUser> l) {
private Set<GHUser> count30(PagedIterable<GHUser> l) {
Set<GHUser> users = new HashSet<GHUser>();
PagedIterator<GHUser> itr = l.iterator();
for (int i=0; i<50 && itr.hasNext(); i++) {
for (int i=0; i<30 && itr.hasNext(); i++) {
users.add(itr.next());
}
assertEquals(50, users.size());
assertEquals(30, users.size());
return users;
}
}

View File

@@ -5,7 +5,7 @@
"user": {
"login": "baxterthehacker",
"id": 6752317,
"avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3",
"avatar_url": "https://avatars3.githubusercontent.com/u/6752317?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/baxterthehacker",
"html_url": "https://github.com/baxterthehacker",
@@ -21,11 +21,11 @@
"type": "User",
"site_admin": false
},
"body": "Looks great!",
"submitted_at": "2016-10-03T23:39:09Z",
"state": "approved",
"body": "Looks great!\n",
"state": "APPROVED",
"html_url": "https://github.com/baxterthehacker/public-repo/pull/8#pullrequestreview-2626884",
"pull_request_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/8",
"author_association": "OWNER",
"_links": {
"html": {
"href": "https://github.com/baxterthehacker/public-repo/pull/8#pullrequestreview-2626884"
@@ -33,7 +33,9 @@
"pull_request": {
"href": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/8"
}
}
},
"submitted_at": "2016-10-03T23:39:09Z",
"commit_id": "b7a1f9c27caa4e03c14a88feb56e2d4f7500aa63"
},
"pull_request": {
"url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/8",