Compare commits

..

457 Commits

Author SHA1 Message Date
Liam Newman
eff48dfe67 [maven-release-plugin] prepare release github-api-1.99 2019-11-04 10:08:55 -08:00
Liam Newman
0c38bdc068 Merge pull request #590 from github-api/dependabot/maven/org.apache.maven.plugins-maven-source-plugin-3.2.0
Bump maven-source-plugin from 3.1.0 to 3.2.0
2019-11-04 09:35:00 -08:00
Liam Newman
b8e1736696 [maven-release-plugin] prepare for next development iteration 2019-11-04 09:16:45 -08:00
Liam Newman
c2f36444d0 [maven-release-plugin] prepare release github-api-1.98 2019-11-04 09:16:39 -08:00
Liam Newman
5dc74f5b4d [maven-release-plugin] prepare for next development iteration 2019-11-04 08:30:58 -08:00
Liam Newman
9694dfe4cc [maven-release-plugin] prepare release github-api-1.97 2019-11-04 08:30:51 -08:00
Liam Newman
734125ea14 [maven-release-plugin] prepare for next development iteration 2019-11-04 08:07:30 -08:00
Liam Newman
438f5d1b87 [maven-release-plugin] prepare release github-api-1.96 2019-11-04 08:07:19 -08:00
dependabot-preview[bot]
fadd6d7978 Bump maven-source-plugin from 3.1.0 to 3.2.0
Bumps [maven-source-plugin](https://github.com/apache/maven-source-plugin) from 3.1.0 to 3.2.0.
- [Release notes](https://github.com/apache/maven-source-plugin/releases)
- [Commits](https://github.com/apache/maven-source-plugin/compare/maven-source-plugin-3.1.0...maven-source-plugin-3.2.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-11-04 13:12:37 +00:00
Liam Newman
5026262273 Merge pull request #578 from martinvanzijl/issue_457_create_team_with_repo_access_fix
GHOrganization.createTeam now adds team to specified repositories
2019-11-01 10:50:53 -07:00
Liam Newman
5b53fba020 Merge pull request #581 from martinvanzijl/issue_546_list_repository_topics
Add method to list repository topics
2019-11-01 09:33:10 -07:00
Liam Newman
3892e6e16f Merge branch 'master' into issue_457_create_team_with_repo_access_fix 2019-11-01 09:30:50 -07:00
Liam Newman
33467de763 Formatting and snapshot cleanup 2019-11-01 09:30:33 -07:00
Martin van Zijl
f3528d97c2 Move team cleanup to @Before method as per GitHub review.
Removed constant for TEAM_NAME as it is now only used once in
the method.
2019-11-01 14:48:20 +13:00
Liam Newman
6fb3b01bf0 Fix gpg signing 2019-10-31 18:10:04 -07:00
Martin van Zijl
e3c9cec440 Fixed typo in comment: listTopics() not getTopics(). 2019-11-01 13:48:20 +13:00
Liam Newman
0e011425fb Merge pull request #586 from PauloMigAlmeida/master
[Documentation] :: Add GitHub App Developer Guide
2019-10-31 17:47:23 -07:00
Liam Newman
ba658f7fea Merge branch 'master' into master 2019-10-31 17:47:12 -07:00
Liam Newman
0e6087143b Merge pull request #587 from bitwiseman/site-build
Fix site errors
2019-10-31 17:46:45 -07:00
Liam Newman
5f967e6307 Fix javadoc on java 11 2019-10-31 17:41:05 -07:00
Martin van Zijl
2eb8ce632c Merge branch 'master' into issue_546_list_repository_topics 2019-11-01 13:35:57 +13:00
Liam Newman
f25dbd15d9 Stabilize time check 2019-10-31 16:10:34 -07:00
PauloMigAlmeida
cc5ea77170 Merge branch 'master' of https://github.com/kohsuke/github-api 2019-11-01 12:02:34 +13:00
PauloMigAlmeida
e323644af2 Correcting typos as pointed out during the code review
Signed-off-by: PauloMigAlmeida <paulo.miguel.almeida.rodenas@gmail.com>
2019-11-01 11:58:16 +13:00
Liam Newman
61f3a3219f Fix tt incompatible tags 2019-10-31 15:41:09 -07:00
Liam Newman
4dcf5dbc13 More pom fixes 2019-10-31 14:24:29 -07:00
Liam Newman
8faecba0a2 Fix site errors 2019-10-31 13:33:14 -07:00
Liam Newman
739551cbe3 Remove old index 2019-10-31 13:01:58 -07:00
PauloMigAlmeida
24a2e42881 [Documentation] :: Add GitHub Developer Guide
Signed-off-by: PauloMigAlmeida <paulo.miguel.almeida.rodenas@gmail.com>
2019-11-01 02:02:07 +13:00
Liam Newman
6921754c99 Merge pull request #585 from github-api/add-code-of-conduct-1
Create CODE_OF_CONDUCT.md
2019-10-30 17:22:09 -07:00
Liam Newman
d8827a0b44 Create CODE_OF_CONDUCT.md 2019-10-30 17:21:56 -07:00
Liam Newman
a48091988f Update release-drafter.yml 2019-10-30 17:04:40 -07:00
Liam Newman
9bd9c9cf1f Create release-drafter.yml 2019-10-30 16:29:26 -07:00
Liam Newman
695692285b Merge pull request #579 from martinvanzijl/issue_512_methods_to_update_milestones
Add methods to update and delete milestones.
2019-10-30 15:47:50 -07:00
Liam Newman
03edacf7b6 Enable test for Milestone due date update 2019-10-30 15:05:35 -07:00
Liam Newman
4802c97e89 Fix GitHub.printDate()
GitHub.printDate() was not setting GMT timezone resulting in completely bogus output.
2019-10-30 14:57:56 -07:00
Liam Newman
9fc24d1981 Merge branch 'master' into issue_512_methods_to_update_milestones 2019-10-30 12:57:18 -07:00
Liam Newman
d4ddf453b0 Merge pull request #580 from martinvanzijl/issue_529_fix_get_deployment_by_id
Fix for getting deployment by id
2019-10-30 12:55:58 -07:00
Liam Newman
c0117f07cf Merge pull request #583 from PauloMigAlmeida/master
Convenience method to auth with app installation token && documentation examples
2019-10-30 12:54:28 -07:00
Paulo Miguel Almeida
8a88c14b13 Add convenience method to authenticate with app installation tokens;
Convert existing markdown doc to APT due to formatting features;
Replace occurrences of kohsuke user to github-api org where applicable;
Fix repositoryIds method on GHAppCreateTokenBuilder;

Signed-off-by: PauloMigAlmeida <paulo.miguel.almeida.rodenas@gmail.com>
2019-10-30 22:18:12 +13:00
Martin van Zijl
a5b6302a69 Add method to list repository topics.
For #546.

The method to update repository topics does not work yet. It may
not have been implemented in the underlying API.
2019-10-30 15:05:09 +13:00
Martin van Zijl
9f29d4619d Fix for getting deployment by id.
Fixes #529.
2019-10-30 13:13:06 +13:00
Martin van Zijl
f4b9dd7d7b Add methods to update and delete milestones.
Fixes #512.
2019-10-30 12:20:06 +13:00
Martin van Zijl
c0a4152b00 GHOrganization.createTeam now adds team to specified repositories.
Fixes #457.
2019-10-29 17:49:41 +13:00
Liam Newman
efb87c5a9e Adjust Jacoco targets to further point to untouched classes. 2019-10-25 19:09:26 -07:00
Liam Newman
061e8bb662 Stop printing to System 2019-10-25 18:33:54 -07:00
Liam Newman
a4f42b2948 Add more tests from AppTest 2019-10-25 18:31:40 -07:00
Liam Newman
cb2fcd3a9e Enable Jacoco on at has-it-been-touched reporting 2019-10-25 18:19:13 -07:00
Liam Newman
1d004a35a0 Merge pull request #510 from awittha/get-user-orgs
Added getUserPublicOrganizations method
2019-10-24 18:32:09 -07:00
Austin Witt
c763c02018 [added] unit tests for getUserPublicOrganizations(...) 2019-10-24 11:03:37 -05:00
Austin Witt
eb36cb4436 Merge remote-tracking branch 'upstream/master' into get-user-orgs 2019-10-23 14:39:54 -05:00
Liam Newman
72d4b9bf62 Merge pull request #525 from vbehar/draft-pr
Add support for draft pull requests
2019-10-21 16:01:02 -07:00
Liam Newman
81ea138f41 Test and fixes for draft pull request 2019-10-21 15:38:04 -07:00
Liam Newman
1bbc66ad62 Merge remote-tracking branch 'github-api/master' into draft-pr 2019-10-21 14:34:40 -07:00
Liam Newman
e7348df372 Merge pull request #575 from romani/patch-1
bump jackson-databind to 2.10.0 to avoid security alert
2019-10-21 12:56:12 -07:00
Roman Ivanov
550e060647 bump jackson-databind to 2.10.0 to avoid security alert 2019-10-20 06:37:28 -07:00
Liam Newman
2970a58e95 Merge branch 'master' into draft-pr 2019-10-18 13:00:39 -07:00
Liam Newman
e0efa04fd4 Merge pull request #574 from github-api/dependabot/maven/com.github.tomakehurst-wiremock-jre8-standalone-2.25.1
Bump wiremock-jre8-standalone from 2.25.0 to 2.25.1
2019-10-18 13:00:14 -07:00
dependabot-preview[bot]
4d24088982 Bump wiremock-jre8-standalone from 2.25.0 to 2.25.1
Bumps [wiremock-jre8-standalone](https://github.com/tomakehurst/wiremock) from 2.25.0 to 2.25.1.
- [Release notes](https://github.com/tomakehurst/wiremock/releases)
- [Commits](https://github.com/tomakehurst/wiremock/compare/2.25.0...2.25.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-10-18 19:58:47 +00:00
Liam Newman
baba9f5de6 Merge pull request #573 from github-api/dependabot/maven/hamcrest.version-2.2
Bump hamcrest.version from 2.1 to 2.2
2019-10-18 12:57:35 -07:00
dependabot-preview[bot]
c35cd63d0e Bump hamcrest.version from 2.1 to 2.2
Bumps `hamcrest.version` from 2.1 to 2.2.

Updates `hamcrest` from 2.1 to 2.2
- [Release notes](https://github.com/hamcrest/JavaHamcrest/releases)
- [Changelog](https://github.com/hamcrest/JavaHamcrest/blob/master/CHANGES.md)
- [Commits](https://github.com/hamcrest/JavaHamcrest/compare/v2.1...v2.2)

Updates `hamcrest-core` from 2.1 to 2.2
- [Release notes](https://github.com/hamcrest/JavaHamcrest/releases)
- [Changelog](https://github.com/hamcrest/JavaHamcrest/blob/master/CHANGES.md)
- [Commits](https://github.com/hamcrest/JavaHamcrest/compare/v2.1...v2.2)

Updates `hamcrest-library` from 2.1 to 2.2
- [Release notes](https://github.com/hamcrest/JavaHamcrest/releases)
- [Changelog](https://github.com/hamcrest/JavaHamcrest/blob/master/CHANGES.md)
- [Commits](https://github.com/hamcrest/JavaHamcrest/compare/v2.1...v2.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-10-16 23:05:29 +00:00
Liam Newman
80fa389ce3 Fix GHContent.read()
Fixes #487
2019-10-11 15:21:57 -07:00
Liam Newman
8ec861c9fe Merge pull request #563 from bitwiseman/paged_iterable_closure
Simplify creation of PagedIterables from requests
2019-10-11 13:20:17 -07:00
Liam Newman
837400fb5e Merge pull request #572 from sullis/java-matrix-jdk13
GitHub workflow: add JDK 13 to build matrix
2019-10-11 13:18:00 -07:00
Liam Newman
ec4525b8ff Merge branch 'master' into java-matrix-jdk13 2019-10-11 13:13:09 -07:00
Liam Newman
281c927971 Update GHIssueEvent PagedIterable 2019-10-11 13:11:08 -07:00
Liam Newman
9dd44c13e4 Update for AuthorizationList 2019-10-11 13:05:13 -07:00
Liam Newman
f28edbcf8f Simplify creation of PagedIterables from requests 2019-10-11 13:05:13 -07:00
Liam Newman
29e147f992 wrupUp() package private 2019-10-11 13:02:42 -07:00
Sean C. Sullivan
830f4231e1 GitHub workflow: add JDK 13 to build matrix 2019-10-11 12:08:03 -07:00
Liam Newman
7d65e31055 Merge pull request #484 from martinvanzijl/issue_466_edit_gists
Add support for editing Gists
2019-10-11 11:46:56 -07:00
Liam Newman
dec7a91b5b Merge pull request #479 from martinvanzijl/issue_376_issue_events
Add issue events API
2019-10-11 11:44:03 -07:00
Liam Newman
1ba630c221 Minor Gist updater improvement 2019-10-11 11:21:11 -07:00
Martin van Zijl
7d3387fb96 Updated unit tests. 2019-10-11 16:37:08 +13:00
Liam Newman
a0c0de28d7 Move a few more tests to use temp repo 2019-10-09 13:41:00 -07:00
Liam Newman
dc31d52f83 Add facility to create and auto-cleanup temp repos 2019-10-09 13:00:21 -07:00
Martin van Zijl
620c822dac Updates per review on GitHub:
- Created class GHGistUpdater to do the updating.
- Added unit tests.
2019-10-10 07:08:05 +13:00
Liam Newman
5990f5ae22 Merge pull request #567 from jberglund-BSFT/master
Add merge options to GHRepository
2019-10-09 11:02:54 -07:00
Martin van Zijl
37329a50d4 Merge branch 'master' into issue_466_edit_gists 2019-10-10 05:43:10 +13:00
Martin van Zijl
550597f72e Make unit test output neater. 2019-10-09 15:11:02 +13:00
Martin van Zijl
2aa4022e2c Updated per review.
- Created separate class for GHIssueEvent instead of nested classes.
- Renamed test class accordingly.

Other fixes:

- Changed id type to long.
- Updated test cases.
- Added toString() method.
2019-10-09 14:59:08 +13:00
Martin van Zijl
b40741b14d Merge branch 'issue_376_issue_events' of https://github.com/martinvanzijl/github-api into issue_376_issue_events 2019-10-09 14:03:27 +13:00
Joey Berglund
3c739d889e Merge branch 'master' into master 2019-10-08 15:04:55 -05:00
Liam Newman
24a92a133e Merge branch 'master' into issue_376_issue_events 2019-10-08 11:04:22 -07:00
Liam Newman
ce97eb5b40 Merge pull request #482 from martinvanzijl/issue_467_add_user_to_org
Add method to invite user to organization
2019-10-08 11:04:01 -07:00
Liam Newman
7a4e3c480e Merge branch 'master' into issue_467_add_user_to_org 2019-10-08 10:59:27 -07:00
Liam Newman
eefa710839 Merge branch 'master' into issue_466_edit_gists 2019-10-08 10:59:09 -07:00
Liam Newman
4c3e1afcca Merge branch 'master' into get-user-orgs 2019-10-08 10:57:48 -07:00
Liam Newman
c1a77ada5d Merge branch 'master' into draft-pr 2019-10-08 10:55:51 -07:00
Liam Newman
5671e5ed67 Merge branch 'master' into master 2019-10-08 10:54:23 -07:00
Liam Newman
2fdc5a8541 Merge pull request #562 from sullis/github-workflow-java-matrix
GitHub workflow: enable Java matrix [ '1.8.0', '11.0.x' ]
2019-10-08 10:51:37 -07:00
Liam Newman
297dbae247 Merge branch 'master' into github-workflow-java-matrix 2019-10-08 10:47:19 -07:00
Liam Newman
0910cdf3a4 Add test for bridge method 2019-10-08 10:37:51 -07:00
jberglund
6877fb5c74 add unit test 2019-10-08 11:20:34 -05:00
Liam Newman
2d65daa8c9 Merge branch 'master' into master 2019-10-08 08:21:54 -07:00
Sean C. Sullivan
dab4cb6186 cleanup pom.xml 2019-10-07 22:46:40 -07:00
Sean C. Sullivan
40773a2b49 GitHub workflow: enable Java matrix [ '1.8.0', '11.0.x' ] 2019-10-07 22:37:58 -07:00
Liam Newman
7e0d61a6fc Merge pull request #481 from martinvanzijl/issue_459_list_authorizations
Added method to list authorizations
2019-10-07 19:04:00 -07:00
Liam Newman
7f9620d75f Delete Foo.java 2019-10-07 18:46:02 -07:00
Martin van Zijl
3559aef1a1 Fixes per review.
- Added missing brace at end of listMyAuthorizations().
- Added unit test for listMyAuthorizations().
2019-10-08 13:21:48 +13:00
Martin van Zijl
94ceb5eaaf Merge branch 'master' into issue_459_list_authorizations 2019-10-08 12:42:18 +13:00
Martin van Zijl
778ca76d22 Merge branch 'issue_376_issue_events' of https://github.com/martinvanzijl/github-api into issue_376_issue_events 2019-10-08 12:34:44 +13:00
Martin van Zijl
b1c0e41c5c Merge branch 'master' into issue_376_issue_events 2019-10-08 12:31:30 +13:00
Liam Newman
f826edd097 Merge pull request #564 from github-api/dependabot/maven/com.squareup.okio-okio-2.4.1
Bump okio from 2.4.0 to 2.4.1
2019-10-07 14:09:42 -07:00
Liam Newman
edc7c4a4b8 Merge pull request #565 from github-api/dependabot/maven/com.google.code.gson-gson-2.8.6
Bump gson from 2.8.5 to 2.8.6
2019-10-07 14:09:11 -07:00
Liam Newman
dafdc66d25 Merge pull request #568 from bitwiseman/more-tests
More tests
2019-10-07 14:08:45 -07:00
Liam Newman
0d135f14bb Add more CI tests 2019-10-07 14:01:32 -07:00
Liam Newman
568c08d0e9 Separate connection sensitive tests 2019-10-07 13:09:27 -07:00
Liam Newman
87410e295e Add more tests to ci 2019-10-07 13:04:13 -07:00
Joey Berglund
b163e68950 Merge branch 'master' into master 2019-10-07 08:41:56 -05:00
Joey Berglund
88b006e957 Update GHRepository.java 2019-10-07 08:39:38 -05:00
dependabot-preview[bot]
477f2f2814 Bump gson from 2.8.5 to 2.8.6
Bumps [gson](https://github.com/google/gson) from 2.8.5 to 2.8.6.
- [Release notes](https://github.com/google/gson/releases)
- [Changelog](https://github.com/google/gson/blob/master/CHANGELOG.md)
- [Commits](https://github.com/google/gson/compare/gson-parent-2.8.5...gson-parent-2.8.6)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-10-07 08:09:06 +00:00
dependabot-preview[bot]
933d99c23b Bump okio from 2.4.0 to 2.4.1
Bumps [okio](https://github.com/square/okio) from 2.4.0 to 2.4.1.
- [Release notes](https://github.com/square/okio/releases)
- [Changelog](https://github.com/square/okio/blob/master/CHANGELOG.md)
- [Commits](https://github.com/square/okio/compare/parent-2.4.0...2.4.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-10-07 08:08:48 +00:00
Martin van Zijl
d3a0c5ded1 Added unit test for adding user to organization. 2019-10-07 15:34:13 +13:00
Martin van Zijl
1e173ddc24 Merge branch 'master' of https://github.com/kohsuke/github-api into issue_467_add_user_to_org 2019-10-07 13:56:08 +13:00
Liam Newman
ca404cb33b Merge branch 'master' into issue_466_edit_gists 2019-10-05 19:40:56 -07:00
Liam Newman
f0dc7d5b02 Merge pull request #532 from farmdawgnation/team-pr-review-requests
Add support for team pr review requests
2019-10-05 19:39:55 -07:00
Liam Newman
933702376f Update Tests to check for lazy loading and refresh 2019-10-05 14:35:13 -07:00
Liam Newman
e08896775b Add Refreshable interfrace and use it in GHTeam and GHPullRequest 2019-10-05 14:17:17 -07:00
Matt Farmer
d610e46f8e Add a test for team review requests 2019-10-05 09:19:28 -04:00
Matt Farmer
7d1989f5a2 Materialize requested_teams from the teams API
We don't get the full object when we retrieve a PR, so when we see
requested teams we need to materialize them from the API
2019-10-05 09:13:13 -04:00
Matt Farmer
5c1e371427 Rebuild wiremock for test review requests 2019-10-05 08:35:12 -04:00
Matt Farmer
dbd9d51747 Merge remote-tracking branch 'upstream/master' into team-pr-review-requests 2019-10-05 08:29:27 -04:00
Liam Newman
fdf5d3f72d Merge pull request #475 from immanuelqrw/label-description
Adding Label description property
2019-10-04 22:28:11 -07:00
Liam Newman
2719867a86 Fix up Label Description and tests 2019-10-04 22:23:53 -07:00
Liam Newman
0240412ad2 Merge remote-tracking branch 'github-api/master' into label-description 2019-10-04 21:44:06 -07:00
Liam Newman
7c065c1205 Merge pull request #477 from martinvanzijl/issue_330_statistics
Add statistics API.
2019-10-04 21:38:08 -07:00
Liam Newman
f9aa1ad1f2 Fix JSON API FB warning 2019-10-04 21:34:55 -07:00
Liam Newman
5feffdf544 Minor fixups after merge from master
Rerecorded WireMock files with newer framework
2019-10-04 21:29:08 -07:00
Liam Newman
4e56b8bb78 Merge branch 'master' into issue_330_statistics 2019-10-04 21:12:35 -07:00
Liam Newman
845017313c Merge pull request #545 from gskjold/issue_425_projects_columns_cards
Support for projects
2019-10-04 21:10:51 -07:00
Liam Newman
dc2830d94f Enable Content test 2019-10-04 21:06:15 -07:00
Liam Newman
552edf8698 Switch to WireMockMultiServerRule as base 2019-10-04 21:06:00 -07:00
Liam Newman
41c51646fe Rename GitHubApiWireMockRule to GitHubWireMockRule 2019-10-04 21:02:44 -07:00
Joey Berglund
a42024bdbc Update GHRepository.java 2019-10-04 16:28:28 -05:00
Liam Newman
20cfb26a7f Disable Licence content test
Need to mock raw.githubusercontent.com to make this work
2019-10-04 11:09:05 -07:00
Liam Newman
3972d11827 WireMock License Test 2019-10-04 10:30:22 -07:00
Liam Newman
5cc88a0075 WireMock Project Tests 2019-10-04 10:27:47 -07:00
Gunnar Skjold
fc08711111 Expanded GHProject with columns and cards. Also added tests for projects, columns and cards 2019-10-04 10:11:18 -07:00
Martin van Zijl
b20c2babec Added support for projects.
Fixes issue #425
2019-10-04 10:11:18 -07:00
Martin van Zijl
cb0facc6ef Updates per GH review.
- Marked getContributorStats(bool) as deprecated, preview
- Moved "stats/" string to getApiTailUrl()
2019-10-05 05:43:23 +13:00
Liam Newman
78abb2edcb Merge pull request #530 from bozaro/request-log
Add GitHub API requests logging
2019-10-04 09:15:09 -07:00
Liam Newman
7d350d9def Merge pull request #561 from github-api/dependabot/maven/org.eclipse.jgit-org.eclipse.jgit-5.5.1.201910021850-r
Bump org.eclipse.jgit from 5.5.0.201909110433-r to 5.5.1.201910021850-r
2019-10-04 08:15:48 -07:00
dependabot-preview[bot]
48d7daf2c4 Bump org.eclipse.jgit from 5.5.0.201909110433-r to 5.5.1.201910021850-r
Bumps org.eclipse.jgit from 5.5.0.201909110433-r to 5.5.1.201910021850-r.

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-10-04 08:22:03 +00:00
Liam Newman
2782059d88 Merge branch 'master' into request-log 2019-10-04 00:44:21 -07:00
Liam Newman
0ca10bc2ac Merge branch 'master' into issue_459_list_authorizations 2019-10-04 00:43:14 -07:00
Liam Newman
880abe2305 Merge branch 'master' into issue_467_add_user_to_org 2019-10-04 00:41:50 -07:00
Liam Newman
22f61d6225 Merge branch 'master' into issue_376_issue_events 2019-10-04 00:41:15 -07:00
Liam Newman
4fbaa425fb Merge branch 'master' into issue_466_edit_gists 2019-10-04 00:36:18 -07:00
Liam Newman
e81e228f03 Merge branch 'master' into issue_330_statistics 2019-10-04 00:01:36 -07:00
Liam Newman
1076f89954 Merge branch 'master' into label-description 2019-10-03 23:56:27 -07:00
Liam Newman
9c6f977202 Merge branch 'master' into get-user-orgs 2019-10-03 23:55:56 -07:00
Martin van Zijl
bd23ba6077 Added Wiremock test files. 2019-10-04 16:27:41 +13:00
Martin van Zijl
c7af56d0ed Changes per review on GitHub.
- Moved statistics methods from GHRepository to new class GHRepositoryStatistics
- Updated StatisticsTest to suit.
- For ContributorStats, put the "wait-till-ready" loop in the accessor method.
  I'm planning to do something similar for the rest.
2019-10-04 16:23:26 +13:00
Liam Newman
8da6db9fcc Merge pull request #522 from PauloMigAlmeida/master
Implement GitHub App API methods
2019-10-03 19:29:37 -07:00
Liam Newman
2fe7342fd7 Merge branch 'master' into master 2019-10-03 19:07:40 -07:00
Liam Newman
f6221144a8 Merge pull request #478 from martinvanzijl/issue_460_team_description_field
Added description field to GHTeam class.
2019-10-03 17:37:52 -07:00
Liam Newman
872d713e50 Fix one more case of connecting outside the proxy 2019-10-03 17:34:52 -07:00
Liam Newman
d5b04f572b Fixed list traversal for paged queries 2019-10-03 17:23:31 -07:00
PauloMigAlmeida
0f7a5f1c08 Merge from upstream;
Remove servlet exclusion due to previous JDK version;

Signed-off-by: PauloMigAlmeida <paulo.miguel.almeida.rodenas@gmail.com>
2019-10-04 11:59:58 +13:00
PauloMigAlmeida
a163c565a8 Merge branch 'master' of https://github.com/kohsuke/github-api
 Conflicts:
	src/main/java/org/kohsuke/github/GitHubBuilder.java
	src/test/java/org/kohsuke/github/GitHubTest.java
2019-10-04 11:56:38 +13:00
Liam Newman
413a316b15 Record snapshot for test 2019-10-03 14:51:46 -07:00
Liam Newman
65a92a49b5 Merge remote-tracking branch 'github-api/master' into issue_460_team_description_field 2019-10-03 14:44:04 -07:00
Sharath Babu
563507ca21 add setPrivate functionality on repos 2019-10-03 14:38:26 -07:00
Liam Newman
21b2d02f0a Enable GHOrganizationTest in CI 2019-10-03 14:20:18 -07:00
Liam Newman
19e1a00fd7 Rename mapping files to include index 2019-10-03 14:05:15 -07:00
Liam Newman
a02a6a22d0 Move to Gson for formatting json 2019-10-03 13:49:53 -07:00
Liam Newman
3488421f67 More WireMock tests 2019-10-03 12:34:32 -07:00
Liam Newman
34a961088c Initial attempt at auto-format of JSON 2019-10-03 09:06:31 -07:00
Liam Newman
b35509150a Enable part of AppTest in CI 2019-10-02 15:27:22 -07:00
Liam Newman
d096aa2065 Merge pull request #558 from github-api/dependabot/maven/com.squareup.okio-okio-2.4.0
Bump okio from 2.2.2 to 2.4.0
2019-10-02 09:12:45 -07:00
dependabot-preview[bot]
efd5bef102 Bump okio from 2.2.2 to 2.4.0
Bumps [okio](https://github.com/square/okio) from 2.2.2 to 2.4.0.
- [Release notes](https://github.com/square/okio/releases)
- [Changelog](https://github.com/square/okio/blob/master/CHANGELOG.md)
- [Commits](https://github.com/square/okio/compare/2.2.2...parent-2.4.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-10-02 15:57:35 +00:00
Liam Newman
b8c6e44ab6 Update okhttp3 (with Java 8)
This version of okhttp3 is the last to suppurt UrlFactory, but we've replaced it anyway in preparation for future updates.
2019-10-01 14:05:50 -07:00
Liam Newman
9a91cc43e2 Merge pull request #542 from bitwiseman/cache-fix
Improved OkHttpConnector caching behavior
2019-10-01 13:11:26 -07:00
Liam Newman
f4cbab52a7 Merge branch 'master' into cache-fix 2019-10-01 13:07:35 -07:00
Liam Newman
57b58cf203 Merge pull request #557 from github-api/dependabot/maven/org.mockito-mockito-core-3.1.0
Bump mockito-core from 3.0.0 to 3.1.0
2019-10-01 08:25:59 -07:00
dependabot-preview[bot]
9ba8ae08a1 Bump mockito-core from 3.0.0 to 3.1.0
Bumps [mockito-core](https://github.com/mockito/mockito) from 3.0.0 to 3.1.0.
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v3.0.0...v3.1.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-10-01 12:54:01 +00:00
Liam Newman
dfe81f510e Add None and Zero Cache tests to CI 2019-09-30 16:23:08 -07:00
Liam Newman
f3a0b35442 Updated Tests to have non-cache tests run in CI 2019-09-30 11:05:11 -07:00
Liam Newman
64e3be3bc7 Merge branch 'master' into cache-fix 2019-09-30 09:13:52 -07:00
Liam Newman
4c82d1c56e Merge pull request #556 from github-api/dependabot/maven/com.github.tomakehurst-wiremock-jre8-standalone-2.25.0
Bump wiremock-jre8-standalone from 2.24.1 to 2.25.0
2019-09-30 09:13:06 -07:00
Liam Newman
d8451fc2f5 Implement new abstract methods from Admin 2019-09-30 09:00:55 -07:00
dependabot-preview[bot]
8078423252 Bump wiremock-jre8-standalone from 2.24.1 to 2.25.0
Bumps [wiremock-jre8-standalone](https://github.com/tomakehurst/wiremock) from 2.24.1 to 2.25.0.
- [Release notes](https://github.com/tomakehurst/wiremock/releases)
- [Commits](https://github.com/tomakehurst/wiremock/compare/2.24.1...2.25.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-09-30 08:40:16 +00:00
Liam Newman
68176b5695 Merge branch 'master' into draft-pr 2019-09-29 18:36:34 -07:00
Liam Newman
1bf7833959 Merge branch 'master' into request-log 2019-09-29 18:35:53 -07:00
Liam Newman
89770b922d Change listKeys to getKeys 2019-09-29 18:33:08 -07:00
Liam Newman
716947bbb1 Merge branch 'master' into draft-pr 2019-09-27 10:38:23 -07:00
Liam Newman
47e0d7786b Merge branch 'master' into cache-fix 2019-09-27 10:37:26 -07:00
Arngrimur Bjarnason
55f9c40bba Merge branch 'master' into getKeysForUser 2019-09-27 08:24:49 +02:00
Martin van Zijl
51afa30732 Added unit tests. 2019-09-26 14:32:30 +12:00
Liam Newman
23c07316ee Merge branch 'master' into label-description 2019-09-25 17:28:23 -07:00
Liam Newman
e740f52e5e Merge pull request #480 from martinvanzijl/issue_381_branch_url_escaping
Escape special characters in branch URLs
2019-09-25 17:15:50 -07:00
Liam Newman
a731109747 Add tests for URLEncoding 2019-09-25 17:11:46 -07:00
Martin van Zijl
a50a011eb3 Issue #381 - Escape branch URL.
This is using the "old" HTTP.UTF_8 Charset, since the new
StandardCharsets doesn't compile.
2019-09-25 16:59:58 -07:00
Liam Newman
e22c6d3a94 Merge branch 'master' into issue_330_statistics 2019-09-25 16:57:36 -07:00
Liam Newman
50fb9c1795 Merge pull request #473 from joaoe/issue-472-archive-repository
Implemented GitHub.doArchive
2019-09-25 16:55:12 -07:00
Liam Newman
22575c9a01 Add test for archive method 2019-09-25 16:48:54 -07:00
João Eiras
2934922cb5 Implemented GitHub.doArchive
doArchive() will mark a repository as archived.

Issue #472
2019-09-25 16:30:44 -07:00
Liam Newman
9042ff46c7 Clean up Previews 2019-09-25 15:47:07 -07:00
Liam Newman
a1a91539c2 Merge branch 'master' into getKeysForUser 2019-09-25 10:13:39 -07:00
Liam Newman
468f4f94a3 Merge pull request #554 from github-api/dependabot/maven/org.apache.maven.plugins-maven-surefire-plugin-2.22.2
Bump maven-surefire-plugin from 2.22.1 to 2.22.2
2019-09-25 10:10:02 -07:00
Liam Newman
3ebd35437e Merge pull request #552 from github-api/dependabot/maven/org.apache.commons-commons-lang3-3.9
Bump commons-lang3 from 3.7 to 3.9
2019-09-25 10:09:33 -07:00
dependabot-preview[bot]
a166bbadad Bump maven-surefire-plugin from 2.22.1 to 2.22.2
Bumps [maven-surefire-plugin](https://github.com/apache/maven-surefire) from 2.22.1 to 2.22.2.
- [Release notes](https://github.com/apache/maven-surefire/releases)
- [Commits](https://github.com/apache/maven-surefire/compare/surefire-2.22.1...surefire-2.22.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-09-25 17:08:56 +00:00
Liam Newman
7ff07a132a Merge branch 'master' into dependabot/maven/org.apache.commons-commons-lang3-3.9 2019-09-25 10:08:33 -07:00
Liam Newman
6f297bcbb8 Merge pull request #555 from github-api/dependabot/maven/commons-io-commons-io-2.6
Bump commons-io from 1.4 to 2.6
2019-09-25 10:07:45 -07:00
dependabot-preview[bot]
1c023e15af Bump commons-io from 1.4 to 2.6
Bumps commons-io from 1.4 to 2.6.

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-09-25 07:33:21 +00:00
dependabot-preview[bot]
37f96848a7 Bump commons-lang3 from 3.7 to 3.9
Bumps commons-lang3 from 3.7 to 3.9.

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-09-25 07:32:00 +00:00
Liam Newman
3d04765f51 Merge branch 'master' into issue_330_statistics 2019-09-24 18:19:35 -07:00
Liam Newman
e1152fa549 Merge branch 'master' into issue_466_edit_gists 2019-09-24 14:34:15 -07:00
Liam Newman
ab47896f17 Merge branch 'master' into cache-fix 2019-09-24 14:29:36 -07:00
Liam Newman
4f13565e51 Merge pull request #549 from github-api/dependabot/maven/com.infradna.tool-bridge-method-annotation-1.18
Bump bridge-method-annotation from 1.17 to 1.18
2019-09-24 14:29:03 -07:00
dependabot-preview[bot]
7ab93a59e2 Bump bridge-method-annotation from 1.17 to 1.18
Bumps [bridge-method-annotation](https://github.com/infradna/bridge-method-injector) from 1.17 to 1.18.
- [Release notes](https://github.com/infradna/bridge-method-injector/releases)
- [Commits](https://github.com/infradna/bridge-method-injector/compare/bridge-method-injector-parent-1.17...bridge-method-injector-parent-1.18)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-09-24 21:26:18 +00:00
Liam Newman
943c0d022b Merge pull request #551 from github-api/dependabot/maven/commons-codec-commons-codec-1.13
Bump commons-codec from 1.7 to 1.13
2019-09-24 14:24:58 -07:00
dependabot-preview[bot]
bea8922c39 Bump commons-codec from 1.7 to 1.13
Bumps [commons-codec](https://github.com/apache/commons-codec) from 1.7 to 1.13.
- [Release notes](https://github.com/apache/commons-codec/releases)
- [Changelog](https://github.com/apache/commons-codec/blob/master/RELEASE-NOTES.txt)
- [Commits](https://github.com/apache/commons-codec/compare/1.7...commons-codec-1.13)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-09-24 21:23:03 +00:00
Liam Newman
97c96a7ee1 Merge pull request #550 from github-api/dependabot/maven/com.github.spotbugs-spotbugs-maven-plugin-3.1.12.2
Bump spotbugs-maven-plugin from 3.1.11 to 3.1.12.2
2019-09-24 14:21:43 -07:00
dependabot-preview[bot]
3d831ba7e8 Bump spotbugs-maven-plugin from 3.1.11 to 3.1.12.2
Bumps [spotbugs-maven-plugin](https://github.com/spotbugs/spotbugs-maven-plugin) from 3.1.11 to 3.1.12.2.
- [Release notes](https://github.com/spotbugs/spotbugs-maven-plugin/releases)
- [Commits](https://github.com/spotbugs/spotbugs-maven-plugin/compare/spotbugs-maven-plugin-3.1.11...spotbugs-maven-plugin-3.1.12.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-09-24 21:19:00 +00:00
Liam Newman
df749a98cb Merge pull request #547 from github-api/dependabot/maven/org.eclipse.jgit-org.eclipse.jgit-5.5.0.201909110433-r
Bump org.eclipse.jgit from 4.9.0.201710071750-r to 5.5.0.201909110433-r
2019-09-24 14:17:31 -07:00
dependabot-preview[bot]
22bbc9c621 Bump org.eclipse.jgit from 4.9.0.201710071750-r to 5.5.0.201909110433-r
Bumps org.eclipse.jgit from 4.9.0.201710071750-r to 5.5.0.201909110433-r.

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-09-24 19:46:06 +00:00
Liam Newman
30def4e515 Update jackson-databind to 2.9.10 2019-09-24 12:43:43 -07:00
Matt Farmer
cf8be37310 Correct test run + add sample data 2019-09-21 10:29:48 -04:00
Matt Farmer
12409df446 Merge remote-tracking branch 'upstream/master' into team-pr-review-requests 2019-09-21 09:56:12 -04:00
Martin van Zijl
3452a08de3 Added more detail to unit tests.
Added another accessor method.
2019-09-20 16:27:32 +12:00
Liam Newman
425ea0579e Merge branch 'master' into label-description 2019-09-17 13:33:50 -07:00
Arngrimur Bjarnason
22afb769af Adding wiremocks 2019-09-17 14:56:43 +02:00
Arngrimur Bjarnason
884eae269e Adding possiblity to get ssh keys 2019-09-17 14:39:46 +02:00
PauloMigAlmeida
59a973970e Remove duplicated wiremock dependency
Signed-off-by: PauloMigAlmeida <paulo.miguel.almeida.rodenas@gmail.com>
2019-09-15 10:57:22 +12:00
PauloMigAlmeida
225383464a Merge remote-tracking branch 'origin/master' 2019-09-15 10:52:53 +12:00
PauloMigAlmeida
3d82731325 Update tests using the new wiremock structure as per requested
Signed-off-by: PauloMigAlmeida <paulo.miguel.almeida.rodenas@gmail.com>
2019-09-15 10:52:22 +12:00
PauloMigAlmeida
dacdf08309 Merge branch 'master' of https://github.com/kohsuke/github-api 2019-09-14 17:00:24 +12:00
Martin van Zijl
ad281adc97 Unit tests and fixes.
Fixed issue with getCodeFrequency() where it would occasionally throw
an exception when the statistics were still being generated.

Added comment about throwing an exception as a possibility when
202 is returned.
2019-09-14 10:39:59 +12:00
Liam Newman
80741c7773 Use CacheControl and only check for cache in constructor 2019-09-12 13:57:28 -07:00
Liam Newman
532f7a4b17 Merge branch 'master' into cache-fix 2019-09-11 22:48:26 -07:00
Liam Newman
5e87d582e4 Add tests showing cache performance 2019-09-11 22:05:02 -07:00
Liam Newman
b9bf47aaca Merge pull request #543 from jsoref/grammar
Grammar
2019-09-11 20:48:28 -07:00
Josh Soref
0dba882817 spelling: create a 2019-09-11 19:28:32 -04:00
Josh Soref
c419883814 spelling: single 2019-09-11 19:28:24 -04:00
Josh Soref
6f0ccbf2b5 grammar: agreement: returns...organizations 2019-09-11 19:28:14 -04:00
Josh Soref
54332c0bd1 this api is no longer a preview... 2019-09-11 19:27:40 -04:00
Josh Soref
5f80674e89 finish thought... 2019-09-11 19:27:04 -04:00
Josh Soref
c4d0d8ff06 grammar: operations-require 2019-09-11 19:26:47 -04:00
Josh Soref
8ac1b7082a brand: GitHub Enterprise 2019-09-11 19:26:14 -04:00
Josh Soref
f146ae8be4 spelling: user 2019-09-11 19:25:55 -04:00
Josh Soref
e7728fdc94 spelling: log-in 2019-09-11 19:25:44 -04:00
Josh Soref
052122e4d0 spelling: abuse 2019-09-11 19:25:29 -04:00
Josh Soref
38827d2b3d grammar: period at end of sentence (not inside parenthetical) 2019-09-11 19:25:19 -04:00
Liam Newman
9e1f16be27 Fix OkHttpConnector caching 2019-09-10 23:01:50 -07:00
Liam Newman
c845846317 Do not rerun tests
If they failed once, they will fail again.
2019-09-10 23:00:44 -07:00
Liam Newman
9276556f19 Mockito 3.0.0 2019-09-10 22:59:07 -07:00
Liam Newman
c796d18948 Merge pull request #541 from bitwiseman/wiremock-github-rule
Add GitHubApiWireMockRule
2019-09-10 22:54:27 -07:00
Liam Newman
8dee5520cf Add GitHubApiWireMockRule
Separates the WireMock and GitHub API info from project specific code.
2019-09-10 22:42:28 -07:00
Liam Newman
440e13714e Merge branch 'master' into issue_466_edit_gists 2019-09-09 22:04:11 -07:00
Liam Newman
44a70b372d Merge pull request #502 from CodeAndChoke/hashcode_equals
Added equals and hashcode
2019-09-09 22:02:22 -07:00
Liam Newman
504286e96e Merge pull request #540 from bitwiseman/test-run
Working CI Build
2019-09-09 22:01:33 -07:00
Liam Newman
ffdd54d2fa Merge branch 'master' into get-user-orgs 2019-09-09 19:40:00 -07:00
Liam Newman
32804b81c0 Merge pull request #527 from WouterG/patch-1
Remove unnessesairy "throws"
2019-09-09 19:38:09 -07:00
Liam Newman
403b22ade0 Working CI Build
This change automatically turns off tests where we haven't had a chance to implement wiremocking.
They can still be run locally by setting test.github.useProxy (even though most of them do actually use the proxy).
2019-09-09 19:34:09 -07:00
Liam Newman
a4e74abcca Merge branch 'master' into master 2019-09-09 17:19:58 -07:00
Liam Newman
8dcef5bac0 Merge branch 'master' into draft-pr 2019-09-09 16:57:14 -07:00
Liam Newman
49878bc4e3 Merge branch 'master' into patch-1 2019-09-09 16:56:34 -07:00
Liam Newman
3f1a71c8a4 Merge branch 'master' into request-log 2019-09-09 16:54:24 -07:00
Liam Newman
bf9b2c0da3 Merge branch 'master' into team-pr-review-requests 2019-09-09 16:40:00 -07:00
Liam Newman
909a274709 Merge pull request #538 from ewiegs4/pr-query-head
Namespace PR head queries with repo's owner by default
2019-09-09 16:31:21 -07:00
Liam Newman
fc529b8083 Merge remote-tracking branch 'kohsuke/master' into pr-query-head 2019-09-09 16:16:36 -07:00
Liam Newman
f1386f26fb maven-build.yml
Trying to get actions to trigger
2019-09-09 16:15:35 -07:00
Liam Newman
cccd09d329 Delete maven.yml 2019-09-09 16:13:51 -07:00
Liam Newman
0fccf7effe Update maven.yml 2019-09-09 16:06:21 -07:00
Liam Newman
4b91a47c8b Merge branch 'master' into issue_460_team_description_field 2019-09-09 16:01:27 -07:00
Liam Newman
bd40499bbe Improve behavior and documentation for WireMock testing 2019-09-09 15:55:03 -07:00
Liam Newman
c7c8cd76c6 Merge branch 'master' into label-description 2019-09-08 23:58:18 -07:00
Liam Newman
0bb9f0ce41 Merge branch 'master' into pr-query-head 2019-09-08 23:55:34 -07:00
Liam Newman
7136afce8e Update all WireMock data files 2019-09-08 23:33:44 -07:00
Liam Newman
ec17c1de6a Update PullRequestTest for WireMocking 2019-09-08 23:33:30 -07:00
Liam Newman
8d6daef354 Rewrite wiremock rule to snapshot on a per-method basis 2019-09-08 23:33:30 -07:00
Liam Newman
3a09d2de4a Rename README to README.md 2019-09-06 17:07:13 -07:00
Liam Newman
c78e10f92e Merge remote-tracking branch 'kohsuke/master' into pr-query-head 2019-09-06 16:18:49 -07:00
Liam Newman
b113ff35ac Merge pull request #537 from bitwiseman/wiremock
Add WireMock testing facility
2019-09-06 16:17:37 -07:00
Eddie Wiegers
6360024d20 Add test for qualified case. 2019-09-06 16:43:19 -05:00
Martin van Zijl
a7683f6bd7 Added setter and unit tests for GHTeam description field. 2019-09-05 16:23:38 +12:00
Liam Newman
e46a9f3f2a Java 8 required for testing, not for package 2019-09-03 13:16:28 -07:00
Eddie Wiegers
a8d711d4c8 Qualify PR head queries with repo's owner by default 2019-09-03 11:40:33 -05:00
Liam Newman
df74fc67bd Add WireMock tests to CI 2019-08-31 21:55:09 -07:00
Liam Newman
450261abbf Move scenario to json files 2019-08-31 19:52:50 -07:00
Liam Newman
12230edc54 Add UserTest to WireMock 2019-08-31 01:35:21 -07:00
Liam Newman
d1cfcf561b Converted GistTest to WireMock 2019-08-31 01:25:25 -07:00
Liam Newman
d143f5af8b Start of WireMock 2019-08-30 17:07:17 -07:00
Liam Newman
0f7ae3cda7 Merge pull request #534 from res0nance/https
Swap to HTTPs
2019-08-28 13:24:52 -07:00
Raihaan Shouhell
baf94e56c8 Swap to HTTPs 2019-08-27 10:45:02 +08:00
Paulo Miguel Almeida
905bd1a4c9 Fix typo
Signed-off-by: Paulo Almeida <paulo.miguel.almeida.rodenas@gmail.com>
2019-08-26 12:37:28 +12:00
Paulo Almeida
96bcf24047 Merge branch 'master' of https://github.com/kohsuke/github-api
# Conflicts:
#	pom.xml
2019-08-26 12:31:23 +12:00
Liam Newman
dfe47235cf Update maven.yml 2019-08-23 19:11:16 -07:00
Liam Newman
b82ea8eaa8 Merge pull request #533 from sullis/jackson-2.9.9.3
jackson-databind 2.9.9.3
2019-08-23 19:06:25 -07:00
Liam Newman
b1b830c268 Switch to spotbugs 2019-08-23 19:02:14 -07:00
Liam Newman
e654d4a6df Fix pom warnings 2019-08-23 18:48:49 -07:00
Liam Newman
ac30b17ecb Do no run GMaven on this project 2019-08-23 18:45:03 -07:00
Liam Newman
95efdb3a51 Use newer dependency plugin 2019-08-23 18:35:38 -07:00
Liam Newman
49c165e203 Update maven.yml 2019-08-23 18:24:52 -07:00
Liam Newman
a4be351d9f Update maven.yml 2019-08-23 18:20:36 -07:00
Liam Newman
3d0401aecd Update maven.yml 2019-08-23 18:17:34 -07:00
Liam Newman
7c27572a53 Split out dependency downloading to new step
For visualization, it helps to not have to search through all the dependency downloads
2019-08-23 18:17:17 -07:00
Liam Newman
1e7760012e Merge pull request #476 from turbanoff/patch-1
Provide more exception details
2019-08-23 18:12:34 -07:00
Liam Newman
24d464b6e5 Add github build action
This ensures that PR's at least build
Currently runs no tests, since they all depend on having user level access to github
2019-08-23 18:10:38 -07:00
Sean Sullivan
499d91f9f8 jackson-databind 2.9.9.3 2019-08-15 15:23:42 -04:00
Matt Farmer
8e0ceaa06b Add a test for requesting reviewers 2019-08-10 20:52:55 -04:00
Matt Farmer
4e62641f7e Add ability to get requested teams 2019-08-10 20:46:34 -04:00
Matt Farmer
688644f5a4 Add support for requesting teams review prs 2019-08-10 20:31:15 -04:00
Paulo Miguel Almeida
4703f2d1f5 Add tests for GithubApp integration;
Add wiremock-standalone library;

Signed-off-by: Paulo Almeida <paulo.miguel.almeida.rodenas@gmail.com>
2019-08-10 19:01:12 +12:00
Artem V. Navrotskiy
14149ae949 Add GitHub API requests logging 2019-07-19 12:00:48 +03:00
Wouter Gerarts
a0fb0d170c Remove unnessesairy "throws"
All IOExceptions are already caught within the method itself.
2019-07-12 17:46:34 +02:00
Vincent Behar
e133afec86 Add support for draft pull requests
See https://developer.github.com/v3/pulls/#input
and https://help.github.com/en/articles/about-pull-requests#draft-pull-requests

Note that it requires the use of a github api preview: https://developer.github.com/v3/previews/#draft-pull-requests
2019-07-11 17:12:20 +02:00
Liam Newman
3ae8ee8ddb Merge pull request #498 from rmetzger/fix-450
Add GHPullRequestReview.submitted_at field
2019-07-01 10:32:01 -07:00
Liam Newman
012643a231 Merge pull request #413 from ThomasCookOnline/fix_version
fix issues
2019-06-19 17:11:59 -07:00
Liam Newman
dccb43f4dc Merge pull request #517 from jamesatha/repo-by-id
Adds the ability to get a repository by ID
2019-06-19 17:11:03 -07:00
Paulo Almeida
15b3bc6a63 Remove JWT dependencies used for testing purposes 2019-06-18 13:36:00 +12:00
Paulo Almeida
2d15bef76d Add missing CHECK_SUITE github event 2019-06-18 11:24:59 +12:00
Paulo Almeida
756d298f04 Add missing INTEGRATION_INSTALLATION_REPOSITORIES github event 2019-06-18 10:50:08 +12:00
Paulo Almeida
9f35eb1a85 Add @Preview and @Deprecated annotations to methods in which the API is still in preview 2019-06-12 11:32:03 +12:00
Paulo Almeida
c905bb4b6c Implement GitHub App API methods 2019-06-11 17:27:07 +12:00
Austin Witt
c78af01c8f Added two getUserPublicOrganizations(...) methods to get public Org memberships of any user, not just the currently-authenticated one. 2019-05-23 09:08:18 -05:00
Austin Witt
084855645a Due-diligence & hygiene updates to GitHub.java 2019-05-23 09:03:47 -05:00
Liam Newman
afce9ece89 Merge pull request #507 from ingwarsw/add_get_team_by_id
Add getTeam by id function
2019-05-22 15:22:25 -07:00
Liam Newman
154e50e36d Merge pull request #499 from anatolyD/feature/let-create-pr-to-org-fork
Create PR from original repo to private org fork
2019-05-22 14:55:52 -07:00
Liam Newman
80a129102b Merge pull request #492 from arykov/fix_add_team_member_role_capital
fixed membership role problem with case
2019-05-17 14:03:43 -07:00
Liam Newman
93672a074e Merge pull request #508 from blacelle/mindthecode-2019-03-24
Update maven dependency
2019-05-16 19:32:04 -07:00
Liam Newman
c7f6f6233c Merge pull request #485 from scotty-g/remove-console-logging
remove system.out from listOrgs
2019-05-16 18:56:45 -07:00
Liam Newman
68915a9daf Added tests for createLabel() method 2019-05-16 18:51:44 -07:00
Liam Newman
13eb91f8dd Preserve api compatibility for createLabel() 2019-05-16 18:44:39 -07:00
Liam Newman
1a8355e6fb Merge branch 'master' into fix_version 2019-05-16 18:23:59 -07:00
James Athappilly
aa2b7ae60a Adds the ability to get a repository by ID
Fixes: https://github.com/kohsuke/github-api/issues/515
2019-04-26 00:29:26 -07:00
Benoit Lacelle
acd36b7ea5 Update maven dependency versions 2019-03-24 12:55:40 +00:00
Karol Lassak
87245ab79f Add getTeam by id function 2019-03-18 14:15:29 +01:00
Long Nguyen
13b6a17827 Added equals and hashcode 2019-03-07 01:08:56 +01:00
Anatoly Danilov
615e4cf24e Create PR is possible without the owner of the repo to modify PR.
Use case: when fork is done into private organization then the original repo maintainer can't have any access there hence this field has to be set to `false` explicitly
2019-02-20 09:25:35 +03:00
Robert Metzger
0e6d08f027 Add GHPullRequestReview.submitted_at field 2019-02-19 10:13:21 +01:00
Alex Rykov
d593e584dd fixed membership role problem with case 2019-02-01 12:19:28 -05:00
sg012265
e046b97160 remove system.out from listOrgs 2018-12-22 21:31:33 -06:00
Martin van Zijl
567d3dac55 Add support for editing Gists.
Fixes issue #466

NOTE: I could not get the deleteFile() method to work.
2018-12-20 08:16:25 +13:00
Martin van Zijl
a67a0aa924 Add method to invite user to organization.
Fixes issue #467
2018-12-14 07:18:29 +13:00
Турбанов Андрей
1b1e3e88fe Provide more exception details
For now if invalid credentials used to search github-api throws following exception

    Exception in thread "main" org.kohsuke.github.GHException: Failed to retrieve https://api.github.com/search/repositories?q=vk+language%3Ajava+created%3A2015-11-26..2016-11-26
	at org.kohsuke.github.Requester$PagingIterator.fetch(Requester.java:529)
	at org.kohsuke.github.Requester$PagingIterator.hasNext(Requester.java:494)
	at org.kohsuke.github.PagedSearchIterable$1.hasNext(PagedSearchIterable.java:55)
	at org.kohsuke.github.PagedIterator.fetch(PagedIterator.java:44)
	at org.kohsuke.github.PagedIterator.hasNext(PagedIterator.java:32)

There is no mentions if credentials at all. Better to propagate exception to user to be able to deal with it.
2018-11-26 01:21:28 +03:00
Immanuel Washington
ec99636ef0 Adding Label description property
Label in the GitHub API has an optional field description which had not been implemented.
2018-11-25 02:10:06 -05:00
Martin van Zijl
9d955d252a Add statistics API.
Fixes issue #330
2018-11-16 18:24:30 +13:00
Martin van Zijl
f136f6837a Added description field to GHTeam class.
Fixes issue #460.
2018-11-13 07:37:07 +13:00
Martin van Zijl
6fc1fb0966 Added a method to list authorizations.
Fixes issue #459
2018-11-12 17:50:49 +13:00
Kohsuke Kawaguchi
fad203a66d [maven-release-plugin] prepare for next development iteration 2018-11-06 08:42:28 -08:00
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
Martin van Zijl
71304b114a Fix for issue #376. Added issue events API. 2018-08-13 11:44:21 +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
Edem Osmanov
2497ac68d1 fix issues 2018-02-14 12:16:01 +02: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
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
2036 changed files with 959378 additions and 2005 deletions

19
.github/release-drafter.yml vendored Normal file
View File

@@ -0,0 +1,19 @@
name-template: 'v$NEXT_PATCH_VERSION 🌈'
tag-template: 'v$NEXT_PATCH_VERSION'
categories:
- title: '🚀 Features'
labels:
- 'feature'
- 'enhancement'
- title: '🐛 Bug Fixes'
labels:
- 'fix'
- 'bugfix'
- 'bug'
- title: '🧰 Maintenance'
label: 'chore'
change-template: '- $TITLE @$AUTHOR (#$NUMBER)'
template: |
## Changes
$CHANGES

22
.github/workflows/maven-build.yml vendored Normal file
View File

@@ -0,0 +1,22 @@
name: Java CI Build and Test
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
java: [ '1.8.0', '11.0.x', '13.0.x' ]
steps:
- uses: actions/checkout@v1
- name: Set up JDK
uses: actions/setup-java@v1
with:
java-version: ${{ matrix.java }}
- name: Maven Download all dependencies
run: mvn -B org.apache.maven.plugins:maven-dependency-plugin:3.1.1:go-offline
- name: Maven Build
run: mvn -B install site --file pom.xml

76
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,76 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at bitwiseman@gmail.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq

62
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,62 @@
# Contributing
## Using WireMock and Snapshots
This project has started converting to using WireMock to stub out http responses instead of use live data.
This change will allow tests to run in a CI environment without needing to touch github.com.
The tests will instead serve previously recorded responses from local data files.
### Running WireMock tests
Example:
`mvn install -Dtest=WireMockStatusReporterTest`
This the default behavior.
### Setting up credential
1. Create an OAuth token on github.com
2. Set the GITHUB_OAUTH environment variable to the value of that token
3. Set the system property `test.github.useProxy` (usually like "-Dtest.github.useProxy" as a Java VM option)
`mvn install -Dtest.github.useProxy -Dtest=WireMockStatusReporterTest`
4. The above should report no test failures and include the following console output:
`WireMockStatusReporterTest: GitHub proxying and user auth correctly configured for user login: <your login>`
Whenever you run tests with `-Dtest.github.useProxy`, they will try to get data from local files but will fallback to proxying to github if not found.
### Writing a new test
Once you have credentials setup, you add new test classes and test methods as you would normally.
Keep `useProxy` enabled and iterate on your tests as needed. Remember, while proxying your tests are interacting with GitHub - you will need to clean up your state between runs.
When you are ready to create a snapshot of your test data,
run your test with `test.github.takeSnapshot` ("-Dtest.github.takeSnapshot" as a Java VM option). For example:
`mvn install -Dtest.github.takeSnapshot -Dtest=YourTestClassName`
The above command would create snapshot WireMock data files under the path `src/test/resources/org/kohsuhke/github/YourTestClassName/wiremock`.
Each method would get a separate director that would hold the data files for that test method.
Add all files including the generated data to your commit and submit a PR.
### Modifying existing tests
When modifying existing tests, you can change the stubbed WireMock data files by hand or you can try generating a new snapshot.
#### Manual editing of data (minor changes only)
If you know what data will change, it is sometimes simplest to make any required changes to the data files manually.
This can be easier if the changes are minor or when you development environment is not setup to to take updated snapshots.
#### Generating a new snapshot
For more most changes, it is recommended to take a new snapshot when updating tests.
Delete the wiremock data files for the test method you will be modifying.
For more significant changes, you can even delete the WireMock files for an entire test class.
Then follow the same as when writing a new test: run with proxy enabled to debug, take a new snapshot when done, commit everything, and submit the PR.

3
README
View File

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

3
README.md Normal file
View File

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

404
pom.xml
View File

@@ -3,57 +3,148 @@
<parent>
<groupId>org.kohsuke</groupId>
<artifactId>pom</artifactId>
<version>17</version>
<version>21</version>
<relativePath />
</parent>
<artifactId>github-api</artifactId>
<version>1.90</version>
<version>1.99</version>
<name>GitHub API for Java</name>
<url>http://github-api.kohsuke.org/</url>
<url>https://github-api.kohsuke.org/</url>
<description>GitHub API for Java</description>
<scm>
<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.90</tag>
<connection>scm:git:git@github.com/github-api/${project.artifactId}.git</connection>
<developerConnection>scm:git:ssh://git@github.com/github-api/${project.artifactId}.git</developerConnection>
<url>https://${project.artifactId}.kohsuke.org/</url>
<tag>github-api-1.99</tag>
</scm>
<distributionManagement>
<site>
<id>github-pages</id>
<url>gitsite:git@github.com/kohsuke/${project.artifactId}.git</url>
<url>gitsite:git@github.com/github-api/${project.artifactId}.git</url>
</site>
</distributionManagement>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<findbugs-maven-plugin.version>3.0.2</findbugs-maven-plugin.version>
<findbugs-maven-plugin.failOnError>true</findbugs-maven-plugin.failOnError>
<spotbugs-maven-plugin.version>3.1.12.2</spotbugs-maven-plugin.version>
<spotbugs.version>3.1.12</spotbugs.version>
<spotbugs-maven-plugin.failOnError>true</spotbugs-maven-plugin.failOnError>
<hamcrest.version>2.2</hamcrest.version>
<okhttp3.version>3.12.3</okhttp3.version>
<okio.version>2.4.1</okio.version>
<jacoco.coverage.target.class>.80</jacoco.coverage.target.class>
<jacoco.coverage.target.method>0.20</jacoco.coverage.target.method>
<jacoco.coverage.target.line>0.50</jacoco.coverage.target.line>
<jacoco.coverage.target.other>0.50</jacoco.coverage.target.other>
<!-- For non-ci builds we'd like the build to still complete if jacoco metrics aren't met. -->
<jacoco.haltOnFailure>false</jacoco.haltOnFailure>
</properties>
<build>
<extensions>
<extension>
<groupId>org.apache.maven.scm</groupId>
<artifactId>maven-scm-provider-gitexe</artifactId>
<version>1.11.2</version>
</extension>
<extension>
<groupId>org.apache.maven.scm</groupId>
<artifactId>maven-scm-manager-plexus</artifactId>
<version>1.11.2</version>
</extension>
</extensions>
<testResources>
<testResource>
<directory>src/test/resources</directory>
<excludes>
<exclude>**/wiremock/**</exclude>
</excludes>
</testResource>
</testResources>
<pluginManagement>
<plugins>
<!-- adds jacoco coverage -->
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.5</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.1.1</version>
<configuration>
<source>8</source>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
<version>3.8.2</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<version>2.5.3</version>
<configuration>
<rerunFailingTestsCount>2</rerunFailingTestsCount>
<autoVersionSubmodules>true</autoVersionSubmodules>
<useReleaseProfile>false</useReleaseProfile>
<releaseProfiles>release</releaseProfiles>
<goals>deploy</goals>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.0.0</version>
<dependencies>
<dependency>
<groupId>org.apache.bcel</groupId>
<artifactId>bcel</artifactId>
<version>6.4.1</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<annotationProcessorPaths>
<annotationProcessorPath>
<groupId>org.jenkins-ci</groupId>
<artifactId>annotation-indexer</artifactId>
<version>1.12</version>
</annotationProcessorPath>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>animal-sniffer-maven-plugin</artifactId>
<version>1.15</version>
<version>1.18</version>
<configuration>
<signature>
<groupId>org.codehaus.mojo.signature</groupId>
<artifactId>java15</artifactId>
<artifactId>java18</artifactId>
<version>1.0</version>
</signature>
</configuration>
<executions>
<execution>
<id>ensure-java-1.5-class-library</id>
<id>ensure-java-1.8-class-library</id>
<phase>test</phase>
<goals>
<goal>check</goal>
@@ -61,32 +152,51 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.infradna.tool</groupId>
<artifactId>bridge-method-injector</artifactId>
<version>1.18</version>
<executions>
<execution>
<goals>
<goal>process</goal>
</goals>
</execution>
</executions>
<plugin>
<groupId>com.infradna.tool</groupId>
<artifactId>bridge-method-injector</artifactId>
<version>1.18</version>
<executions>
<execution>
<goals>
<goal>process</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>findbugs-maven-plugin</artifactId>
<version>${findbugs-maven-plugin.version}</version>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
<version>${spotbugs-maven-plugin.version}</version>
<configuration>
<xmlOutput>true</xmlOutput>
<failOnError>${findbugs-maven-plugin.failOnError}</failOnError>
<failOnError>${spotbugs-maven-plugin.failOnError}</failOnError>
</configuration>
<executions>
<execution>
<id>run-findbugs</id>
<phase>verify</phase>
<id>run-spotbugs</id>
<phase>verify</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
<dependencies>
<!-- overwrite dependency on spotbugs if you want to specify the version of spotbugs -->
<dependency>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs</artifactId>
<version>${spotbugs.version}</version>
</dependency>
</dependencies>
</plugin>
<!-- Do not use gmaven plugin -->
<plugin>
<groupId>org.codehaus.gmaven</groupId>
<artifactId>gmaven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
@@ -96,14 +206,33 @@
<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.9</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.7</version>
<version>1.13</version>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest</artifactId>
<version>${hamcrest.version}</version>
<scope>test</scope>
</dependency>
<!-- This is needed in order to force junit4 and JTH tests to use newer hamcrest version -->
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<version>${hamcrest.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
<version>${hamcrest.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
@@ -111,26 +240,21 @@
<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.9.2</version>
<version>2.10.0</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>1.4</version>
<version>2.6</version>
</dependency>
<dependency>
<groupId>com.infradna.tool</groupId>
<artifactId>bridge-method-annotation</artifactId>
<version>1.17</version>
<version>1.18</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.kohsuke.stapler</groupId>
@@ -141,19 +265,31 @@
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit</artifactId>
<version>4.9.0.201710071750-r</version>
<version>5.5.1.201910021850-r</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.squareup.okhttp</groupId>
<artifactId>okhttp-urlconnection</artifactId>
<version>2.7.5</version>
<groupId>com.squareup.okio</groupId>
<artifactId>okio</artifactId>
<version>${okio.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>${okhttp3.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp-urlconnection</artifactId>
<version>3.9.0</version>
<version>${okhttp3.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.squareup.okhttp</groupId>
<artifactId>okhttp-urlconnection</artifactId>
<version>2.7.5</version>
<optional>true</optional>
</dependency>
<dependency>
@@ -164,43 +300,189 @@
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.10.19</version>
<artifactId>mockito-core</artifactId>
<version>3.1.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>annotations</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-annotations</artifactId>
<version>${spotbugs.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock-jre8-standalone</artifactId>
<version>2.25.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.6</version>
<scope>test</scope>
</dependency>
</dependencies>
<repositories>
<repository>
<id>repo.jenkins-ci.org</id>
<url>http://repo.jenkins-ci.org/public/</url>
<url>https://repo.jenkins-ci.org/public/</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>repo.jenkins-ci.org</id>
<url>http://repo.jenkins-ci.org/public/</url>
<url>https://repo.jenkins-ci.org/public/</url>
</pluginRepository>
</pluginRepositories>
<profiles>
<profile>
<id>jacoco</id>
<activation>
<property>
<name>enable-jacoco</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.5</version>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<!-- attached to Maven test phase -->
<execution>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
<execution>
<id>check</id>
<phase>install</phase>
<goals>
<goal>check</goal>
</goals>
<configuration>
<rules>
<rule>
<!--We end up with chatty logs, but it shows us which particular classes -->
<!--are lacking in coverage. If this is too much, just remove the -->
<!--<element>CLASS</element> tag below. -->
<element>CLASS</element>
<limits>
<!-- These limits can be overridden, in the form of `0.50` for -->
<!-- 50%, as necessary. Using a property just puts it in one spot. -->
<!--
<limit>
<counter>LINE</counter>
<value>COVEREDRATIO</value>
<minimum>${jacoco.coverage.target.lines}</minimum>
</limit>
-->
<!--
<limit>
<counter>BRANCH</counter>
<value>COVEREDRATIO</value>
<minimum>${jacoco.coverage.target.other}</minimum>
</limit>
-->
<!--
<limit>
<counter>INSTRUCTION</counter>
<value>COVEREDRATIO</value>
<minimum>${jacoco.coverage.target.other}</minimum>
</limit>
-->
<!--
<limit>
<counter>COMPLEXITY</counter>
<value>COVEREDRATIO</value>
<minimum>${jacoco.coverage.target.other}</minimum>
</limit>
-->
<limit>
<counter>METHOD</counter>
<value>COVEREDRATIO</value>
<minimum>${jacoco.coverage.target.method}</minimum>
</limit>
<!-- <limit>-->
<!-- <counter>CLASS</counter>-->
<!-- <value>COVEREDRATIO</value>-->
<!-- <minimum>${jacoco.coverage.target.class}</minimum>-->
<!-- </limit>-->
</limits>
<excludes>
<exclude>org.kohsuke.github.extras.okhttp3.ObsoleteUrlFactory.**</exclude>
<exclude>org.kohsuke.github.extras.okhttp3.ObsoleteUrlFactory</exclude>
<exclude>org.kohsuke.github.extras.OkHttp3Connector</exclude>
<!--<exclude>io.jenkins.plugins.todeclarative.converter.api.*</exclude>-->
</excludes>
</rule>
</rules>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>release</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.6</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
<configuration>
<gpgArguments>
<arg>--pinentry-mode</arg>
<arg>loopback</arg>
</gpgArguments>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.2.0</version>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-project-info-reports-plugin</artifactId>
</plugin>
</plugins>
</reporting>
<licenses>
<license>
<name>The MIT license</name>
<url>http://www.opensource.org/licenses/mit-license.php</url>
<url>https://www.opensource.org/licenses/mit-license.php</url>
<distribution>repo</distribution>
</license>
</licenses>

View File

@@ -24,9 +24,10 @@ public abstract class AbuseLimitHandler {
* @see <a href="https://developer.github.com/v3/#abuse-rate-limits">API documentation from GitHub</a>
* @param e
* Exception from Java I/O layer. If you decide to fail the processing, you can throw
* this exception (or wrap this exception into another exception and throw it.)
* this exception (or wrap this exception into another exception and throw it).
* @param uc
* Connection that resulted in an error. Useful for accessing other response headers.
* @throws IOException
*/
public abstract void onError(IOException e, HttpURLConnection uc) throws IOException;
@@ -38,7 +39,7 @@ public abstract class AbuseLimitHandler {
public void onError(IOException e, HttpURLConnection uc) throws IOException {
try {
Thread.sleep(parseWaitTime(uc));
} catch (InterruptedException _) {
} catch (InterruptedException ex) {
throw (InterruptedIOException)new InterruptedIOException().initCause(e);
}
}
@@ -57,7 +58,7 @@ public abstract class AbuseLimitHandler {
public static final AbuseLimitHandler FAIL = new AbuseLimitHandler() {
@Override
public void onError(IOException e, HttpURLConnection uc) throws IOException {
throw (IOException)new IOException("Abust limit reached").initCause(e);
throw (IOException)new IOException("Abuse limit reached").initCause(e);
}
};
}

View File

@@ -0,0 +1,172 @@
package org.kohsuke.github;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.IOException;
import java.net.URL;
import java.util.List;
import java.util.Map;
import static org.kohsuke.github.Previews.MACHINE_MAN;
/**
* A Github App.
*
* @author Paulo Miguel Almeida
*
* @see GitHub#getApp()
*/
public class GHApp extends GHObject {
private GitHub root;
private GHUser owner;
private String name;
private String description;
@JsonProperty("external_url")
private String externalUrl;
private Map<String,String> permissions;
private List<GHEvent> events;
@JsonProperty("installations_count")
private long installationsCount;
@JsonProperty("html_url")
private String htmlUrl;
public GHUser getOwner() {
return owner;
}
public void setOwner(GHUser owner) {
this.owner = owner;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getExternalUrl() {
return externalUrl;
}
public void setExternalUrl(String externalUrl) {
this.externalUrl = externalUrl;
}
public List<GHEvent> getEvents() {
return events;
}
public void setEvents(List<GHEvent> events) {
this.events = events;
}
public long getInstallationsCount() {
return installationsCount;
}
public void setInstallationsCount(long installationsCount) {
this.installationsCount = installationsCount;
}
public URL getHtmlUrl() {
return GitHub.parseURL(htmlUrl);
}
public Map<String, String> getPermissions() {
return permissions;
}
public void setPermissions(Map<String, String> permissions) {
this.permissions = permissions;
}
/*package*/ GHApp wrapUp(GitHub root) {
this.root = root;
return this;
}
/**
* Obtains all the installations associated with this app.
*
* You must use a JWT to access this endpoint.
*
* @see <a href="https://developer.github.com/v3/apps/#list-installations">List installations</a>
* @return a list of App installations
*/
@Preview @Deprecated
public PagedIterable<GHAppInstallation> listInstallations() {
return root.retrieve().withPreview(MACHINE_MAN)
.asPagedIterable(
"/app/installations",
GHAppInstallation[].class,
item -> item.wrapUp(root) );
}
/**
* Obtain an installation associated with this app
* @param id - Installation Id
*
* You must use a JWT to access this endpoint.
*
* @see <a href="https://developer.github.com/v3/apps/#get-an-installation">Get an installation</a>
*/
@Preview @Deprecated
public GHAppInstallation getInstallationById(long id) throws IOException {
return root.retrieve().withPreview(MACHINE_MAN).to(String.format("/app/installations/%d", id), GHAppInstallation.class).wrapUp(root);
}
/**
* Obtain an organization installation associated with this app
* @param name - Organization name
*
* You must use a JWT to access this endpoint.
*
* @see <a href="https://developer.github.com/v3/apps/#get-an-organization-installation">Get an organization installation</a>
*/
@Preview @Deprecated
public GHAppInstallation getInstallationByOrganization(String name) throws IOException {
return root.retrieve().withPreview(MACHINE_MAN).to(String.format("/orgs/%s/installation", name), GHAppInstallation.class).wrapUp(root);
}
/**
* Obtain an repository installation associated with this app
* @param ownerName - Organization or user name
* @param repositoryName - Repository name
*
* You must use a JWT to access this endpoint.
*
* @see <a href="https://developer.github.com/v3/apps/#get-a-repository-installation">Get a repository installation</a>
*/
@Preview @Deprecated
public GHAppInstallation getInstallationByRepository(String ownerName, String repositoryName) throws IOException {
return root.retrieve().withPreview(MACHINE_MAN).to(String.format("/repos/%s/%s/installation", ownerName, repositoryName), GHAppInstallation.class).wrapUp(root);
}
/**
* Obtain a user installation associated with this app
* @param name - user name
*
* You must use a JWT to access this endpoint.
*
* @see <a href="https://developer.github.com/v3/apps/#get-a-user-installation">Get a user installation</a>
*/
@Preview @Deprecated
public GHAppInstallation getInstallationByUser(String name) throws IOException {
return root.retrieve().withPreview(MACHINE_MAN).to(String.format("/users/%s/installation", name), GHAppInstallation.class).wrapUp(root);
}
}

View File

@@ -0,0 +1,53 @@
package org.kohsuke.github;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import static org.kohsuke.github.Previews.MACHINE_MAN;
/**
* Creates a access token for a GitHub App Installation
*
* @author Paulo Miguel Almeida
*
* @see GHAppInstallation#createToken(Map)
*/
public class GHAppCreateTokenBuilder {
private final GitHub root;
protected final Requester builder;
private final String apiUrlTail;
@Preview @Deprecated
/*package*/ GHAppCreateTokenBuilder(GitHub root, String apiUrlTail, Map<String, GHPermissionType> permissions) {
this.root = root;
this.apiUrlTail = apiUrlTail;
this.builder = new Requester(root);
this.builder.withPermissions("permissions",permissions);
}
/**
* By default the installation token has access to all repositories that the installation can access. To restrict
* the access to specific repositories, you can provide the repository_ids when creating the token. When you omit
* repository_ids, the response does not contain neither the repositories nor the permissions key.
*
* @param repositoryIds - Array containing the repositories Ids
*
*/
@Preview @Deprecated
public GHAppCreateTokenBuilder repositoryIds(List<Long> repositoryIds) {
this.builder.with("repository_ids",repositoryIds);
return this;
}
/**
* Creates an app token with all the parameters.
*
* You must use a JWT to access this endpoint.
*/
@Preview @Deprecated
public GHAppInstallationToken create() throws IOException {
return builder.method("POST").withPreview(MACHINE_MAN).to(apiUrlTail, GHAppInstallationToken.class).wrapUp(root);
}
}

View File

@@ -0,0 +1,167 @@
package org.kohsuke.github;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.IOException;
import java.net.URL;
import java.util.List;
import java.util.Map;
import static org.kohsuke.github.Previews.GAMBIT;
/**
* A Github App Installation.
*
* @author Paulo Miguel Almeida
*
* @see GHApp#listInstallations()
* @see GHApp#getInstallationById(long)
* @see GHApp#getInstallationByOrganization(String)
* @see GHApp#getInstallationByRepository(String, String)
* @see GHApp#getInstallationByUser(String)
*/
public class GHAppInstallation extends GHObject {
private GitHub root;
private GHUser account;
@JsonProperty("access_tokens_url")
private String accessTokenUrl;
@JsonProperty("repositories_url")
private String repositoriesUrl;
@JsonProperty("app_id")
private long appId;
@JsonProperty("target_id")
private long targetId;
@JsonProperty("target_type")
private GHTargetType targetType;
private Map<String, GHPermissionType> permissions;
private List<GHEvent> events;
@JsonProperty("single_file_name")
private String singleFileName;
@JsonProperty("repository_selection")
private GHRepositorySelection repositorySelection;
private String htmlUrl;
public URL getHtmlUrl() {
return GitHub.parseURL(htmlUrl);
}
public GitHub getRoot() {
return root;
}
public void setRoot(GitHub root) {
this.root = root;
}
public GHUser getAccount() {
return account;
}
public void setAccount(GHUser account) {
this.account = account;
}
public String getAccessTokenUrl() {
return accessTokenUrl;
}
public void setAccessTokenUrl(String accessTokenUrl) {
this.accessTokenUrl = accessTokenUrl;
}
public String getRepositoriesUrl() {
return repositoriesUrl;
}
public void setRepositoriesUrl(String repositoriesUrl) {
this.repositoriesUrl = repositoriesUrl;
}
public long getAppId() {
return appId;
}
public void setAppId(long appId) {
this.appId = appId;
}
public long getTargetId() {
return targetId;
}
public void setTargetId(long targetId) {
this.targetId = targetId;
}
public GHTargetType getTargetType() {
return targetType;
}
public void setTargetType(GHTargetType targetType) {
this.targetType = targetType;
}
public Map<String, GHPermissionType> getPermissions() {
return permissions;
}
public void setPermissions(Map<String, GHPermissionType> permissions) {
this.permissions = permissions;
}
public List<GHEvent> getEvents() {
return events;
}
public void setEvents(List<GHEvent> events) {
this.events = events;
}
public String getSingleFileName() {
return singleFileName;
}
public void setSingleFileName(String singleFileName) {
this.singleFileName = singleFileName;
}
public GHRepositorySelection getRepositorySelection() {
return repositorySelection;
}
public void setRepositorySelection(GHRepositorySelection repositorySelection) {
this.repositorySelection = repositorySelection;
}
/*package*/ GHAppInstallation wrapUp(GitHub root) {
this.root = root;
return this;
}
/**
* Delete a Github App installation
*
* You must use a JWT to access this endpoint.
*
* @see <a href="https://developer.github.com/v3/apps/#delete-an-installation">Delete an installation</a>
*/
@Preview @Deprecated
public void deleteInstallation() throws IOException {
root.retrieve().method("DELETE").withPreview(GAMBIT).to(String.format("/app/installations/%d", id));
}
/**
* Starts a builder that creates a new App Installation Token.
*
* <p>
* You use the returned builder to set various properties, then call {@link GHAppCreateTokenBuilder#create()}
* to finally create an access token.
*/
@Preview @Deprecated
public GHAppCreateTokenBuilder createToken(Map<String,GHPermissionType> permissions){
return new GHAppCreateTokenBuilder(root,String.format("/app/installations/%d/access_tokens", id), permissions);
}
}

View File

@@ -0,0 +1,87 @@
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 java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* A Github App Installation Token.
*
* @author Paulo Miguel Almeida
*
* @see GHAppInstallation#createToken(Map)
*/
public class GHAppInstallationToken {
private GitHub root;
private String token;
protected String expires_at;
private Map<String, String> permissions;
private List<GHRepository> repositories;
@JsonProperty("repository_selection")
private GHRepositorySelection repositorySelection;
public GitHub getRoot() {
return root;
}
public void setRoot(GitHub root) {
this.root = root;
}
public Map<String, String> getPermissions() {
return permissions;
}
public void setPermissions(Map<String, String> permissions) {
this.permissions = permissions;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public List<GHRepository> getRepositories() {
return repositories;
}
public void setRepositories(List<GHRepository> repositories) {
this.repositories = repositories;
}
public GHRepositorySelection getRepositorySelection() {
return repositorySelection;
}
public void setRepositorySelection(GHRepositorySelection repositorySelection) {
this.repositorySelection = repositorySelection;
}
/**
* When was this tokens expires?
*/
@WithBridgeMethods(value=String.class, adapterMethod="expiresAtStr")
public Date getExpiresAt() throws IOException {
return GitHub.parseDate(expires_at);
}
@SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD", justification = "Bridge method of getExpiresAt")
private Object expiresAtStr(Date id, Class type) {
return expires_at;
}
/*package*/ GHAppInstallationToken wrapUp(GitHub root) {
this.root = root;
return this;
}
}

View File

@@ -65,9 +65,8 @@ public class GHBranch {
return GitHub.parseURL(protection_url);
}
@Preview @Deprecated
public GHBranchProtection getProtection() throws IOException {
return root.retrieve().withPreview(LOKI).to(protection_url, GHBranchProtection.class);
return root.retrieve().to(protection_url, GHBranchProtection.class).wrap(this);
}
/**
@@ -80,9 +79,8 @@ public class GHBranch {
/**
* Disables branch protection and allows anyone with push access to push changes.
*/
@Preview @Deprecated
public void disableProtection() throws IOException {
new Requester(root).method("DELETE").withPreview(LOKI).to(protection_url);
new Requester(root).method("DELETE").to(protection_url);
}
/**

View File

@@ -3,27 +3,46 @@ 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")
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD",
"URF_UNREAD_FIELD"}, justification = "JSON API")
public class GHBranchProtection {
@JsonProperty("enforce_admins")
private EnforceAdmins enforceAdmins;
private static final String REQUIRE_SIGNATURES_URI = "/required_signatures";
@JsonProperty("required_pull_request_reviews")
private RequiredReviews requiredReviews;
@JsonProperty("enforce_admins")
private EnforceAdmins enforceAdmins;
@JsonProperty("required_status_checks")
private RequiredStatusChecks requiredStatusChecks;
private GitHub root;
@JsonProperty
private Restrictions restrictions;
@JsonProperty("required_pull_request_reviews")
private RequiredReviews requiredReviews;
@JsonProperty
private String url;
public EnforceAdmins getEnforceAdmins() {
@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;
}
@@ -31,6 +50,12 @@ public class GHBranchProtection {
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;
}
@@ -43,12 +68,21 @@ public class GHBranchProtection {
return url;
}
public static class EnforceAdmins {
@JsonProperty
private boolean enabled;
GHBranchProtection wrap(GHBranch branch) {
this.root = branch.getRoot();
return this;
}
@JsonProperty
private String url;
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;
@@ -57,20 +91,23 @@ public class GHBranchProtection {
public boolean isEnabled() {
return enabled;
}
}
}
public static class RequiredReviews {
@JsonProperty("dismissal_restrictions")
private Restrictions dismissalRestriction;
public static class RequiredReviews {
@JsonProperty("dismissal_restrictions")
private Restrictions dismissalRestriction;
@JsonProperty("dismiss_stale_reviews")
private boolean dismissStaleReviews;
@JsonProperty("dismiss_stale_reviews")
private boolean dismissStaleReviews;
@JsonProperty("require_code_owner_reviews")
private boolean requireCodeOwnerReviews;
@JsonProperty("require_code_owner_reviews")
private boolean requireCodeOwnerReviews;
@JsonProperty
private String url;
@JsonProperty("required_approving_review_count")
private int requiredReviewers;
@JsonProperty
private String url;
public Restrictions getDismissalRestrictions() {
return dismissalRestriction;
@@ -87,17 +124,37 @@ public class GHBranchProtection {
public boolean isRequireCodeOwnerReviews() {
return requireCodeOwnerReviews;
}
}
public static class RequiredStatusChecks {
@JsonProperty
private Collection<String> contexts;
public int getRequiredReviewers() {
return requiredReviewers;
}
}
@JsonProperty
private boolean strict;
private static class RequiredSignatures {
@JsonProperty
private boolean enabled;
@JsonProperty
private String url;
@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;
@@ -110,23 +167,23 @@ public class GHBranchProtection {
public boolean isRequiresBranchUpToDate() {
return strict;
}
}
}
public static class Restrictions {
@JsonProperty
private Collection<GHTeam> teams;
public static class Restrictions {
@JsonProperty
private Collection<GHTeam> teams;
@JsonProperty("teams_url")
private String teamsUrl;
@JsonProperty("teams_url")
private String teamsUrl;
@JsonProperty
private String url;
@JsonProperty
private String url;
@JsonProperty
private Collection<GHUser> users;
@JsonProperty
private Collection<GHUser> users;
@JsonProperty("users_url")
private String usersUrl;
@JsonProperty("users_url")
private String usersUrl;
public Collection<GHTeam> getTeams() {
return teams;
@@ -147,5 +204,5 @@ public class GHBranchProtection {
public String getUsersUrl() {
return usersUrl;
}
}
}
}

View File

@@ -44,7 +44,11 @@ public class GHBranchProtectionBuilder {
}
public GHBranchProtectionBuilder dismissStaleReviews() {
getPrReviews().put("dismiss_stale_reviews", true);
return dismissStaleReviews(true);
}
public GHBranchProtectionBuilder dismissStaleReviews(boolean v) {
getPrReviews().put("dismiss_stale_reviews", v);
return this;
}
@@ -54,7 +58,8 @@ public class GHBranchProtectionBuilder {
.withNullable("required_pull_request_reviews", prReviews)
.withNullable("restrictions", restrictions)
.withNullable("enforce_admins", enforceAdmins)
.to(branch.getProtectionUrl().toString(), GHBranchProtection.class);
.to(branch.getProtectionUrl().toString(), GHBranchProtection.class)
.wrap(branch);
}
public GHBranchProtectionBuilder includeAdmins() {
@@ -66,6 +71,11 @@ public class GHBranchProtectionBuilder {
return this;
}
public GHBranchProtectionBuilder requiredReviewers(int v) {
getPrReviews().put("required_approving_review_count", v);
return this;
}
public GHBranchProtectionBuilder requireBranchIsUpToDate() {
return requireBranchIsUpToDate(true);
}
@@ -89,6 +99,16 @@ public class GHBranchProtectionBuilder {
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;
@@ -151,12 +171,7 @@ public class GHBranchProtectionBuilder {
}
private void addReviewRestriction(String restriction, boolean isTeam) {
getPrReviews();
if (!prReviews.containsKey("dismissal_restrictions")) {
prReviews.put("dismissal_restrictions", new Restrictions());
}
restrictReviewDismissals();
Restrictions restrictions = (Restrictions) prReviews.get("dismissal_restrictions");
if (isTeam) {
@@ -188,7 +203,7 @@ public class GHBranchProtectionBuilder {
}
private Requester requester() {
return new Requester(branch.getRoot()).withPreview(LOKI);
return new Requester(branch.getRoot()).withPreview(LUKE_CAGE);
}
private static class Restrictions {

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

@@ -327,17 +327,11 @@ public class GHCommit {
* Lists up all the commit comments in this repository.
*/
public PagedIterable<GHCommitComment> listComments() {
return new PagedIterable<GHCommitComment>() {
public PagedIterator<GHCommitComment> _iterator(int pageSize) {
return new PagedIterator<GHCommitComment>(owner.root.retrieve().asIterator(String.format("/repos/%s/%s/commits/%s/comments", owner.getOwnerName(), owner.getName(), sha), GHCommitComment[].class, pageSize)) {
@Override
protected void wrapUp(GHCommitComment[] page) {
for (GHCommitComment c : page)
c.wrap(owner);
}
};
}
};
return owner.root.retrieve()
.asPagedIterable(
String.format("/repos/%s/%s/commits/%s/comments", owner.getOwnerName(), owner.getName(), sha),
GHCommitComment[].class,
item -> item.wrap(owner) );
}
/**

View File

@@ -98,17 +98,11 @@ public class GHCommitComment extends GHObject implements Reactable {
@Preview @Deprecated
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(getApiTail()+"/reactions", GHReaction[].class, pageSize)) {
@Override
protected void wrapUp(GHReaction[] page) {
for (GHReaction c : page)
c.wrap(owner.root);
}
};
}
};
return owner.root.retrieve().withPreview(SQUIRREL_GIRL)
.asPagedIterable(
getApiTail()+"/reactions",
GHReaction[].class,
item -> item.wrap(owner.root) );
}
/**

View File

@@ -91,15 +91,10 @@ public class GHCommitQueryBuilder {
* Lists up the commits with the criteria built so far.
*/
public PagedIterable<GHCommit> list() {
return new PagedIterable<GHCommit>() {
public PagedIterator<GHCommit> _iterator(int pageSize) {
return new PagedIterator<GHCommit>(req.asIterator(repo.getApiTailUrl("commits"), GHCommit[].class, pageSize)) {
protected void wrapUp(GHCommit[] page) {
for (GHCommit c : page)
c.wrapUp(repo);
}
};
}
};
return req
.asPagedIterable(
repo.getApiTailUrl("commits"),
GHCommit[].class,
item -> item.wrapUp(repo) );
}
}

View File

@@ -1,6 +1,6 @@
package org.kohsuke.github;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import java.io.IOException;

View File

@@ -2,10 +2,12 @@ package org.kohsuke.github;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import org.apache.commons.codec.binary.Base64InputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
/**
* A Content of a repository.
@@ -14,7 +16,7 @@ import java.io.InputStream;
* @see GHRepository#getFileContent(String)
*/
@SuppressWarnings({"UnusedDeclaration"})
public class GHContent {
public class GHContent implements Refreshable {
/*
In normal use of this class, repository field is set via wrap(),
but in the code search API, there's a nested 'repository' field that gets populated from JSON.
@@ -91,10 +93,8 @@ public class GHContent {
* Use {@link #read()}
*/
public String getEncodedContent() throws IOException {
if (content!=null)
return content;
else
return Base64.encodeBase64String(IOUtils.toByteArray(read()));
refresh(content);
return content;
}
public String getUrl() {
@@ -112,16 +112,27 @@ public class GHContent {
/**
* Retrieves the actual content stored here.
*/
/**
* Retrieves the actual bytes of the blob.
*/
public InputStream read() throws IOException {
// if the download link is encoded with a token on the query string, the default behavior of POST will fail
return new Requester(root).method("GET").asStream(getDownloadUrl());
refresh(content);
if (encoding.equals("base64")) {
try {
return new Base64InputStream(new ByteArrayInputStream(content.getBytes("US-ASCII")), false);
} catch (UnsupportedEncodingException e) {
throw new AssertionError(e); // US-ASCII is mandatory
}
}
throw new UnsupportedOperationException("Unrecognized encoding: "+encoding);
}
/**
* URL to retrieve the raw content of the file. Null if this is a directory.
*/
public String getDownloadUrl() throws IOException {
populate();
refresh(download_url);
return download_url;
}
@@ -139,7 +150,6 @@ public class GHContent {
* Depending on the original API call where this object is created, it may not contain everything.
*/
protected synchronized void populate() throws IOException {
if (download_url!=null) return; // already populated
root.retrieve().to(url, this);
}
@@ -150,16 +160,11 @@ public class GHContent {
if (!isDirectory())
throw new IllegalStateException(path+" is not a directory");
return new PagedIterable<GHContent>() {
public PagedIterator<GHContent> _iterator(int pageSize) {
return new PagedIterator<GHContent>(root.retrieve().asIterator(url, GHContent[].class, pageSize)) {
@Override
protected void wrapUp(GHContent[] page) {
GHContent.wrap(page, repository);
}
};
}
};
return root.retrieve()
.asPagedIterable(
url,
GHContent[].class,
item -> item.wrap(repository) );
}
@SuppressFBWarnings("DM_DEFAULT_ENCODING")
@@ -243,4 +248,14 @@ public class GHContent {
}
return contents;
}
/**
* Fully populate the data by retrieving missing data.
*
* Depending on the original API call where this object is created, it may not contain everything.
*/
@Override
public synchronized void refresh() throws IOException {
root.retrieve().to(url, this);
}
}

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

@@ -7,7 +7,6 @@ package org.kohsuke.github;
* @see <a href="https://developer.github.com/v3/licenses/#get-a-repositorys-license">documentation</a>
* @see GHRepository#getLicense()
*/
@Preview @Deprecated
class GHContentWithLicense extends GHContent {
GHLicense license;

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,6 +1,6 @@
package org.kohsuke.github;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
import java.io.IOException;
@@ -8,10 +8,10 @@ public class GHDeployKey {
protected String url, key, title;
protected boolean verified;
protected int id;
protected long id;
private GHRepository owner;
public int getId() {
public long getId() {
return id;
}

View File

@@ -71,17 +71,11 @@ public class GHDeployment extends GHObject {
}
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);
}
};
}
};
return root.retrieve()
.asPagedIterable(
statuses_url,
GHDeploymentStatus[].class,
item -> item.wrap(owner) );
}
}

View File

@@ -23,6 +23,8 @@ public enum GHEvent {
GOLLUM,
INSTALLATION,
INSTALLATION_REPOSITORIES,
INTEGRATION_INSTALLATION_REPOSITORIES,
CHECK_SUITE,
ISSUE_COMMENT,
ISSUES,
LABEL,

View File

@@ -34,7 +34,7 @@ public class GHEventInfo {
"UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR" }, justification = "JSON API")
public static class GHEventRepository {
@SuppressFBWarnings(value = "UUF_UNUSED_FIELD", justification = "We don't provide it in API now")
private int id;
private long id;
@SuppressFBWarnings(value = "UUF_UNUSED_FIELD", justification = "We don't provide it in API now")
private String url; // repository API URL
private String name; // owner/repo

View File

@@ -85,6 +85,140 @@ public abstract class GHEventPayload {
}
}
/**
* 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
*
@@ -627,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

@@ -1,6 +1,7 @@
package org.kohsuke.github;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.apache.commons.lang3.StringUtils;
import java.io.IOException;
import java.net.URL;
@@ -103,7 +104,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();
@@ -115,8 +116,13 @@ public class GHGist extends GHObject {
e.getValue().fileName = e.getKey();
}
}
String getApiTailUrl(String tail) {
return "/gists/" + id + '/' + tail;
String result = "/gists/" + id;
if (!StringUtils.isBlank(tail)) {
result += StringUtils.prependIfMissing(tail, "/");
}
return result;
}
public void star() throws IOException {
@@ -139,21 +145,11 @@ public class GHGist extends GHObject {
}
public PagedIterable<GHGist> listForks() {
return new PagedIterable<GHGist>() {
public PagedIterator<GHGist> _iterator(int pageSize) {
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);
}
}
};
}
};
return root.retrieve()
.asPagedIterable(
getApiTailUrl("forks"),
GHGist[].class,
item -> item.wrapUp(root) );
}
/**
@@ -163,6 +159,13 @@ public class GHGist extends GHObject {
new Requester(root).method("DELETE").to("/gists/" + id);
}
/**
* Updates this gist via a builder.
*/
public GHGistUpdater update() throws IOException {
return new GHGistUpdater(this);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
@@ -176,4 +179,10 @@ public class GHGist extends GHObject {
public int hashCode() {
return id.hashCode();
}
GHGist wrap(GHUser owner) {
this.owner = owner;
this.root = owner.root;
return this;
}
}

View File

@@ -0,0 +1,60 @@
package org.kohsuke.github;
import java.io.IOException;
import java.util.Collections;
import java.util.LinkedHashMap;
/**
* Builder pattern for updating a Gist.
*
* @author Martin van Zijl
*/
public class GHGistUpdater {
private final GHGist base;
private final Requester builder;
LinkedHashMap<String,Object> files;
GHGistUpdater(GHGist base) {
this.base = base;
this.builder = new Requester(base.root);
files = new LinkedHashMap<>();
}
public GHGistUpdater addFile(String fileName, String content) throws IOException {
updateFile(fileName, content);
return this;
}
// // This method does not work.
// public GHGistUpdater deleteFile(String fileName) throws IOException {
// files.put(fileName, Collections.singletonMap("filename", null));
// return this;
// }
public GHGistUpdater renameFile(String fileName, String newFileName) throws IOException
{
files.put(fileName, Collections.singletonMap("filename", newFileName));
return this;
}
public GHGistUpdater updateFile(String fileName, String content) throws IOException {
files.put(fileName, Collections.singletonMap("content", content));
return this;
}
public GHGistUpdater description(String desc) {
builder.with("description",desc);
return this;
}
/**
* Updates the Gist based on the parameters specified thus far.
*/
public GHGist update() throws IOException {
builder._with("files", files);
return builder
.method("PATCH")
.to(base.getApiTailUrl(""), GHGist.class).wrap(base.owner);
}
}

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;
@@ -203,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);
}
@@ -211,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.
*
@@ -224,16 +292,11 @@ public class GHIssue extends GHObject implements Reactable{
* Obtains all the comments associated with this issue.
*/
public PagedIterable<GHIssueComment> listComments() throws IOException {
return new PagedIterable<GHIssueComment>() {
public PagedIterator<GHIssueComment> _iterator(int pageSize) {
return new PagedIterator<GHIssueComment>(root.retrieve().asIterator(getIssuesApiRoute() + "/comments", GHIssueComment[].class, pageSize)) {
protected void wrapUp(GHIssueComment[] page) {
for (GHIssueComment c : page)
c.wrapUp(GHIssue.this);
}
};
}
};
return root.retrieve()
.asPagedIterable(
getIssuesApiRoute() + "/comments",
GHIssueComment[].class,
item -> item.wrapUp(GHIssue.this) );
}
@Preview @Deprecated
@@ -246,17 +309,11 @@ public class GHIssue extends GHObject implements Reactable{
@Preview @Deprecated
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)) {
@Override
protected void wrapUp(GHReaction[] page) {
for (GHReaction c : page)
c.wrap(owner.root);
}
};
}
};
return owner.root.retrieve().withPreview(SQUIRREL_GIRL)
.asPagedIterable(
getApiRoute()+"/reactions",
GHReaction[].class,
item -> item.wrap(owner.root) );
}
public void addAssignees(GHUser... assignees) throws IOException {
@@ -264,8 +321,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 {
@@ -273,7 +329,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 {
@@ -281,16 +337,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() {
@@ -372,4 +419,15 @@ public class GHIssue extends GHObject implements Reactable{
return GitHub.parseURL(html_url);
}
}
/**
* Lists events for this issue.
* See https://developer.github.com/v3/issues/events/
*/
public PagedIterable<GHIssueEvent> listEvents() throws IOException {
return root.retrieve().asPagedIterable(
owner.getApiTailUrl(String.format("/issues/%s/events", number)),
GHIssueEvent[].class,
item -> item.wrapUp(GHIssue.this) );
}
}

View File

@@ -32,11 +32,13 @@ 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);
}
/**
@@ -106,17 +109,12 @@ public class GHIssueComment extends GHObject implements Reactable {
@Preview @Deprecated
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)) {
@Override
protected void wrapUp(GHReaction[] page) {
for (GHReaction c : page)
c.wrap(owner.root);
}
};
}
};
return owner.root.retrieve()
.withPreview(SQUIRREL_GIRL)
.asPagedIterable(
getApiRoute()+"/reactions",
GHReaction[].class,
item -> item.wrap(owner.root) );
}
private String getApiRoute() {

View File

@@ -0,0 +1,81 @@
package org.kohsuke.github;
import java.util.Date;
/**
* @author Martin van Zijl
*/
public class GHIssueEvent {
private GitHub root;
private long id;
private String node_id;
private String url;
private GHUser actor;
private String event;
private String commit_id;
private String commit_url;
private String created_at;
private GHIssue issue;
public long getId() {
return id;
}
public String getNodeId() {
return node_id;
}
public String getUrl() {
return url;
}
public GHUser getActor() {
return actor;
}
public String getEvent() {
return event;
}
public String getCommitId() {
return commit_id;
}
public String getCommitUrl() {
return commit_url;
}
public Date getCreatedAt() {
return GitHub.parseDate(created_at);
}
public GitHub getRoot() {
return root;
}
public GHIssue getIssue() {
return issue;
}
GHIssueEvent wrapUp(GitHub root) {
this.root = root;
return this;
}
GHIssueEvent wrapUp(GHIssue parent) {
this.issue = parent;
this.root = parent.root;
return this;
}
@Override
public String toString() {
return String.format("Issue %d was %s by %s on %s",
getIssue().getNumber(),
getEvent(),
getActor().getLogin(),
getCreatedAt().toString());
}
}

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,14 +1,19 @@
package org.kohsuke.github;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import static org.kohsuke.github.Previews.SYMMETRA;
/**
* @author Kohsuke Kawaguchi
* @see GHIssue#getLabels()
* @see GHRepository#listLabels()
*/
public class GHLabel {
private String url, name, color;
private String url, name, color, description;
private GHRepository repo;
public String getUrl() {
@@ -26,6 +31,14 @@ public class GHLabel {
return color;
}
/**
* Purpose of Label
*/
@Preview @Deprecated
public String getDescription() {
return description;
}
/*package*/ GHLabel wrapUp(GHRepository repo) {
this.repo = repo;
return this;
@@ -40,6 +53,49 @@ public class GHLabel {
* 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);
repo.root.retrieve().method("PATCH")
.withPreview(SYMMETRA)
.with("name", name)
.with("color", newColor)
.with("description", description)
.to(url);
}
/**
* @param newDescription
* Description of label
*/
@Preview @Deprecated
public void setDescription(String newDescription) throws IOException {
repo.root.retrieve().method("PATCH")
.withPreview(SYMMETRA)
.with("name", name)
.with("color", color)
.with("description", newDescription)
.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;
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final GHLabel ghLabel = (GHLabel) o;
return Objects.equals(url, ghLabel.url) &&
Objects.equals(name, ghLabel.name) &&
Objects.equals(color, ghLabel.color) &&
Objects.equals(repo, ghLabel.repo);
}
@Override
public int hashCode() {
return Objects.hash(url, name, color, repo);
}
}

View File

@@ -32,19 +32,15 @@ import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import static org.kohsuke.github.Previews.*;
/**
* The GitHub Preview API's license information
* <p>
* WARNING: This uses a PREVIEW API - subject to change.
*
* @author Duncan Dickinson
* @see GitHub#getLicense(String)
* @see GHRepository#getLicense()
* @see <a href="https://developer.github.com/v3/licenses/">https://developer.github.com/v3/licenses/</a>
*/
@Preview @Deprecated
@SuppressWarnings({"UnusedDeclaration"})
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
"NP_UNWRITTEN_FIELD"}, justification = "JSON API")
@@ -144,7 +140,7 @@ public class GHLicense extends GHObject {
protected synchronized void populate() throws IOException {
if (description!=null) return; // already populated
root.retrieve().withPreview(DRAX).to(url, this);
root.retrieve().to(url, this);
}
@Override

View File

@@ -85,10 +85,29 @@ public class GHMilestone extends GHObject {
edit("state", "open");
}
/**
* Deletes this milestone.
*/
public void delete() throws IOException {
root.retrieve().method("DELETE").to(getApiRoute());
}
private void edit(String key, Object value) throws IOException {
new Requester(root)._with(key, value).method("PATCH").to(getApiRoute());
}
public void setTitle(String title) throws IOException {
edit("title", title);
}
public void setDescription(String description) throws IOException {
edit("description", description);
}
public void setDueOn(Date dueOn) throws IOException {
edit("due_on", GitHub.printDate(dueOn));
}
protected String getApiRoute() {
return "/repos/"+owner.getOwnerName()+"/"+owner.getName()+"/milestones/"+number;
}

View File

@@ -156,17 +156,13 @@ public class GHMyself extends GHUser {
* @param repoType type of repository returned in the listing
*/
public PagedIterable<GHRepository> listRepositories(final int pageSize, final RepositoryListFilter repoType) {
return new PagedIterable<GHRepository>() {
public PagedIterator<GHRepository> _iterator(int pageSize) {
return new PagedIterator<GHRepository>(root.retrieve().with("type",repoType).asIterator("/user/repos", GHRepository[].class, pageSize)) {
@Override
protected void wrapUp(GHRepository[] page) {
for (GHRepository c : page)
c.wrap(root);
}
};
}
}.withPageSize(pageSize);
return root.retrieve()
.with("type",repoType)
.asPagedIterable(
"/user/repos",
GHRepository[].class,
item -> item.wrap(root)
).withPageSize(pageSize);
}
/**
@@ -191,16 +187,12 @@ public class GHMyself extends GHUser {
* Filter by a specific state
*/
public PagedIterable<GHMembership> listOrgMemberships(final GHMembership.State state) {
return new PagedIterable<GHMembership>() {
public PagedIterator<GHMembership> _iterator(int pageSize) {
return new PagedIterator<GHMembership>(root.retrieve().with("state",state).asIterator("/user/memberships/orgs", GHMembership[].class, pageSize)) {
@Override
protected void wrapUp(GHMembership[] page) {
GHMembership.wrap(page,root);
}
};
}
};
return root.retrieve()
.with("state",state)
.asPagedIterable(
"/user/memberships/orgs",
GHMembership[].class,
item -> item.wrap(root) );
}
/**

View File

@@ -2,8 +2,8 @@ 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.ToStringStyle;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import javax.annotation.CheckForNull;
import java.io.IOException;

View File

@@ -9,6 +9,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import static org.kohsuke.github.Previews.INERTIA;
/**
* @author Kohsuke Kawaguchi
@@ -50,7 +51,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);
}
@@ -69,17 +70,11 @@ public class GHOrganization extends GHPerson {
* List up all the teams.
*/
public PagedIterable<GHTeam> listTeams() throws IOException {
return new PagedIterable<GHTeam>() {
public PagedIterator<GHTeam> _iterator(int pageSize) {
return new PagedIterator<GHTeam>(root.retrieve().asIterator(String.format("/orgs/%s/teams", login), GHTeam[].class, pageSize)) {
@Override
protected void wrapUp(GHTeam[] page) {
for (GHTeam c : page)
c.wrapUp(GHOrganization.this);
}
};
}
};
return root.retrieve()
.asPagedIterable(
String.format("/orgs/%s/teams", login),
GHTeam[].class,
item -> item.wrapUp(GHOrganization.this) );
}
/**
@@ -104,6 +99,22 @@ public class GHOrganization extends GHPerson {
return null;
}
/** Member's role in an organization */
public enum Role {
ADMIN, /** The user is an owner of the organization. */
MEMBER /** The user is a non-owner member of the organization. */
}
/**
* Adds (invites) a user to the organization.
* @see <a href="https://developer.github.com/v3/orgs/members/#add-or-update-organization-membership">documentation</a>
*/
public void add(GHUser user, Role role) throws IOException {
root.retrieve().method("PUT")
.with("role", role.name().toLowerCase())
.to("/orgs/" + login + "/memberships/" + user.getLogin());
}
/**
* Checks if this organization has the specified user as a member.
*/
@@ -173,17 +184,12 @@ public class GHOrganization extends GHPerson {
}
private PagedIterable<GHUser> listMembers(final String suffix, final String filter) throws IOException {
return new PagedIterable<GHUser>() {
public PagedIterator<GHUser> _iterator(int pageSize) {
String filterParams = (filter == null) ? "" : ("?filter=" + filter);
return new PagedIterator<GHUser>(root.retrieve().asIterator(String.format("/orgs/%s/%s%s", login, suffix, filterParams), GHUser[].class, pageSize)) {
@Override
protected void wrapUp(GHUser[] users) {
GHUser.wrap(users, root);
}
};
}
};
String filterParams = (filter == null) ? "" : ("?filter=" + filter);
return root.retrieve()
.asPagedIterable(
String.format("/orgs/%s/%s%s", login, suffix, filterParams),
GHUser[].class,
item -> item.wrapUp(root) );
}
/**
@@ -193,6 +199,37 @@ public class GHOrganization extends GHPerson {
root.retrieve().method("DELETE").to("/orgs/" + login + "/public_members/" + u.getLogin(), null);
}
/**
* Returns the projects for this organization.
* @param status The status filter (all, open or closed).
*/
public PagedIterable<GHProject> listProjects(final GHProject.ProjectStateFilter status) throws IOException {
return root.retrieve().withPreview(INERTIA)
.with("state", status)
.asPagedIterable(
String.format("/orgs/%s/projects", login),
GHProject[].class,
item -> item.wrap(root) );
}
/**
* Returns all open projects for the organization.
*/
public PagedIterable<GHProject> listProjects() throws IOException {
return listProjects(GHProject.ProjectStateFilter.OPEN);
}
/**
* Creates a project for the organization.
*/
public GHProject createProject(String name, String body) throws IOException {
return root.retrieve().method("POST")
.withPreview(INERTIA)
.with("name", name)
.with("body", body)
.to(String.format("/orgs/%s/projects", login), GHProject.class).wrap(root);
}
public enum Permission { ADMIN, PUSH, PULL }
/**
@@ -202,7 +239,7 @@ public class GHOrganization extends GHPerson {
Requester post = new Requester(root).with("name", name).with("permission", p);
List<String> repo_names = new ArrayList<String>();
for (GHRepository r : repositories) {
repo_names.add(r.getName());
repo_names.add(login + "/" + r.getName());
}
post.with("repo_names",repo_names);
return post.method("POST").to("/orgs/" + login + "/teams", GHTeam.class).wrapUp(this);
@@ -245,17 +282,11 @@ public class GHOrganization extends GHPerson {
* Lists events performed by a user (this includes private events if the caller is authenticated.
*/
public PagedIterable<GHEventInfo> listEvents() throws IOException {
return new PagedIterable<GHEventInfo>() {
public PagedIterator<GHEventInfo> _iterator(int pageSize) {
return new PagedIterator<GHEventInfo>(root.retrieve().asIterator(String.format("/orgs/%s/events", login), GHEventInfo[].class, pageSize)) {
@Override
protected void wrapUp(GHEventInfo[] page) {
for (GHEventInfo c : page)
c.wrapUp(root);
}
};
}
};
return root.retrieve()
.asPagedIterable(
String.format("/orgs/%s/events", login),
GHEventInfo[].class,
item -> item.wrapUp(root) );
}
/**
@@ -267,17 +298,12 @@ public class GHOrganization extends GHPerson {
*/
@Override
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)) {
@Override
protected void wrapUp(GHRepository[] page) {
for (GHRepository c : page)
c.wrap(root);
}
};
}
};
return root.retrieve()
.asPagedIterable(
"/orgs/" + login + "/repos",
GHRepository[].class,
item -> item.wrap(root)
).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);
@@ -79,36 +79,32 @@ public abstract class GHPerson extends GHObject {
* Unlike {@link #getRepositories()}, this does not wait until all the repositories are returned.
*/
public PagedIterable<GHRepository> listRepositories(final int pageSize) {
return new PagedIterable<GHRepository>() {
public PagedIterator<GHRepository> _iterator(int pageSize) {
return new PagedIterator<GHRepository>(root.retrieve().asIterator("/users/" + login + "/repos?per_page=" + pageSize, GHRepository[].class, pageSize)) {
@Override
protected void wrapUp(GHRepository[] page) {
for (GHRepository c : page)
c.wrap(root);
}
};
}
};
return root.retrieve()
.asPagedIterable(
"/users/" + login + "/repos",
GHRepository[].class,
item -> item.wrap(root)
).withPageSize(pageSize);
}
/**
* Loads repository list in a paginated fashion.
*
*
* <p>
* For a person with a lot of repositories, GitHub returns the list of repositories in a paginated fashion.
* Unlike {@link #getRepositories()}, this method allows the caller to start processing data as it arrives.
*
*
* Every {@link Iterator#next()} call results in I/O. Exceptions that occur during the processing is wrapped
* into {@link Error}.
*
* @deprecated
* Use {@link #listRepositories()}
*/
@Deprecated
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

@@ -0,0 +1,183 @@
/*
* The MIT License
*
* Copyright 2018 Martin van Zijl.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.kohsuke.github;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;
import java.util.Locale;
import static org.kohsuke.github.Previews.INERTIA;
/**
* A GitHub project.
* @see <a href="https://developer.github.com/v3/projects/">Projects</a>
* @author Martin van Zijl
*/
public class GHProject extends GHObject {
protected GitHub root;
protected GHObject owner;
private String owner_url;
private String html_url;
private String node_id;
private String name;
private String body;
private int number;
private String state;
private GHUser creator;
@Override
public URL getHtmlUrl() throws IOException {
return GitHub.parseURL(html_url);
}
public GitHub getRoot() {
return root;
}
public GHObject getOwner() throws IOException {
if(owner == null) {
try {
if(owner_url.contains("/orgs/")) {
owner = root.retrieve().to(getOwnerUrl().getPath(), GHOrganization.class).wrapUp(root);
} else if(owner_url.contains("/users/")) {
owner = root.retrieve().to(getOwnerUrl().getPath(), GHUser.class).wrapUp(root);
} else if(owner_url.contains("/repos/")) {
owner = root.retrieve().to(getOwnerUrl().getPath(), GHRepository.class).wrap(root);
}
} catch (FileNotFoundException e) {
return null;
}
}
return owner;
}
public URL getOwnerUrl() {
return GitHub.parseURL(owner_url);
}
public String getNode_id() {
return node_id;
}
public String getName() {
return name;
}
public String getBody() {
return body;
}
public int getNumber() {
return number;
}
public ProjectState getState() {
return Enum.valueOf(ProjectState.class, state.toUpperCase(Locale.ENGLISH));
}
public GHUser getCreator() {
return creator;
}
public GHProject wrap(GHRepository repo) {
this.owner = repo;
this.root = repo.root;
return this;
}
public GHProject wrap(GitHub root) {
this.root = root;
return this;
}
private void edit(String key, Object value) throws IOException {
new Requester(root).withPreview(INERTIA)._with(key, value).method("PATCH").to(getApiRoute());
}
protected String getApiRoute() {
return "/projects/" + id;
}
public void setName(String name) throws IOException {
edit("name", name);
}
public void setBody(String body) throws IOException {
edit("body", body);
}
public enum ProjectState {
OPEN,
CLOSED
}
public void setState(ProjectState state) throws IOException {
edit("state", state.toString().toLowerCase());
}
public static enum ProjectStateFilter {
ALL,
OPEN,
CLOSED
}
/**
* Set the permission level that all members of the project's organization will have on this project.
* Only applicable for organization-owned projects.
*/
public void setOrganizationPermission(GHPermissionType permission) throws IOException {
edit("organization_permission", permission.toString().toLowerCase());
}
/**
* Sets visibility of the project within the organization.
* Only applicable for organization-owned projects.
*/
public void setPublic(boolean isPublic) throws IOException {
edit("public", isPublic);
}
public void delete() throws IOException {
new Requester(root).withPreview(INERTIA).method("DELETE").to(getApiRoute());
}
public PagedIterable<GHProjectColumn> listColumns() throws IOException {
final GHProject project = this;
return root.retrieve()
.withPreview(INERTIA)
.asPagedIterable(
String.format("/projects/%d/columns", id),
GHProjectColumn[].class,
item -> item.wrap(project) );
}
public GHProjectColumn createColumn(String name) throws IOException {
return root.retrieve().method("POST")
.withPreview(INERTIA)
.with("name", name)
.to(String.format("/projects/%d/columns", id), GHProjectColumn.class).wrap(this);
}
}

View File

@@ -0,0 +1,123 @@
package org.kohsuke.github;
import org.apache.commons.lang3.StringUtils;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;
import static org.kohsuke.github.Previews.INERTIA;
/**
* @author Gunnar Skjold
*/
public class GHProjectCard extends GHObject {
private GitHub root;
private GHProject project;
private GHProjectColumn column;
private String note;
private GHUser creator;
private String content_url, project_url, column_url;
private boolean archived;
public URL getHtmlUrl() throws IOException {
return null;
}
public GHProjectCard wrap(GitHub root) {
this.root = root;
return this;
}
public GHProjectCard wrap(GHProjectColumn column) {
this.column = column;
this.project = column.project;
this.root = column.root;
return this;
}
public GitHub getRoot() {
return root;
}
public GHProject getProject() throws IOException {
if(project == null) {
try {
project = root.retrieve().to(getProjectUrl().getPath(), GHProject.class).wrap(root);
} catch (FileNotFoundException e) {
return null;
}
}
return project;
}
public GHProjectColumn getColumn() throws IOException {
if(column == null) {
try {
column = root.retrieve().to(getColumnUrl().getPath(), GHProjectColumn.class).wrap(root);
} catch (FileNotFoundException e) {
return null;
}
}
return column;
}
public GHIssue getContent() throws IOException {
if(StringUtils.isEmpty(content_url))
return null;
try {
if(content_url.contains("/pulls")) {
return root.retrieve().to(getContentUrl().getPath(), GHPullRequest.class).wrap(root);
} else {
return root.retrieve().to(getContentUrl().getPath(), GHIssue.class).wrap(root);
}
} catch (FileNotFoundException e) {
return null;
}
}
public String getNote() {
return note;
}
public GHUser getCreator() {
return creator;
}
public URL getContentUrl() {
return GitHub.parseURL(content_url);
}
public URL getProjectUrl() {
return GitHub.parseURL(project_url);
}
public URL getColumnUrl() {
return GitHub.parseURL(column_url);
}
public boolean isArchived() {
return archived;
}
public void setNote(String note) throws IOException {
edit("note", note);
}
public void setArchived(boolean archived) throws IOException {
edit("archived", archived);
}
private void edit(String key, Object value) throws IOException {
new Requester(root).withPreview(INERTIA)._with(key, value).method("PATCH").to(getApiRoute());
}
protected String getApiRoute() {
return String.format("/projects/columns/cards/%d", id);
}
public void delete() throws IOException {
new Requester(root).withPreview(INERTIA).method("DELETE").to(getApiRoute());
}
}

View File

@@ -0,0 +1,98 @@
package org.kohsuke.github;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;
import static org.kohsuke.github.Previews.INERTIA;
/**
* @author Gunnar Skjold
*/
public class GHProjectColumn extends GHObject {
protected GitHub root;
protected GHProject project;
private String name;
private String project_url;
@Override
public URL getHtmlUrl() throws IOException {
return null;
}
public GHProjectColumn wrap(GitHub root) {
this.root = root;
return this;
}
public GHProjectColumn wrap(GHProject project) {
this.project = project;
this.root = project.root;
return this;
}
public GitHub getRoot() {
return root;
}
public GHProject getProject() throws IOException {
if(project == null) {
try {
project = root.retrieve().to(getProjectUrl().getPath(), GHProject.class).wrap(root);
} catch (FileNotFoundException e) {
return null;
}
}
return project;
}
public String getName() {
return name;
}
public URL getProjectUrl() {
return GitHub.parseURL(project_url);
}
public void setName(String name) throws IOException {
edit("name", name);
}
private void edit(String key, Object value) throws IOException {
new Requester(root).withPreview(INERTIA)._with(key, value).method("PATCH").to(getApiRoute());
}
protected String getApiRoute() {
return String.format("/projects/columns/%d", id);
}
public void delete() throws IOException {
new Requester(root).withPreview(INERTIA).method("DELETE").to(getApiRoute());
}
public PagedIterable<GHProjectCard> listCards() throws IOException {
final GHProjectColumn column = this;
return root.retrieve()
.withPreview(INERTIA)
.asPagedIterable(
String.format("/projects/columns/%d/cards", id),
GHProjectCard[].class,
item -> item.wrap(column) );
}
public GHProjectCard createCard(String note) throws IOException {
return root.retrieve().method("POST")
.withPreview(INERTIA)
.with("note", note)
.to(String.format("/projects/columns/%d/cards", id), GHProjectCard.class).wrap(this);
}
public GHProjectCard createCard(GHIssue issue) throws IOException {
return root.retrieve().method("POST")
.withPreview(INERTIA)
.with("content_type", issue instanceof GHPullRequest ? "PullRequest" : "Issue")
.with("content_id", issue.getId())
.to(String.format("/projects/columns/%d/cards", id), GHProjectCard.class).wrap(this);
}
}

View File

@@ -29,19 +29,23 @@ import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import static org.kohsuke.github.Previews.*;
import static org.kohsuke.github.Previews.SHADOW_CAT;
/**
* A pull request.
*
*
* @author Kohsuke Kawaguchi
* @see GHRepository#getPullRequest(int)
*/
@SuppressWarnings({"UnusedDeclaration"})
public class GHPullRequest extends GHIssue {
public class GHPullRequest extends GHIssue implements Refreshable {
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;
@@ -52,12 +56,18 @@ public class GHPullRequest extends GHIssue {
private GHUser merged_by;
private int review_comments, additions, commits;
private boolean merged, maintainer_can_modify;
// making these package private to all for testing
boolean draft;
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;
private GHTeam[] requested_teams;
/**
* 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
@@ -76,6 +86,8 @@ 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);
if (requested_teams != null) GHTeam.wrapUp(requested_teams, this);
return this;
}
@@ -91,7 +103,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
@@ -114,7 +126,7 @@ public class GHPullRequest extends GHIssue {
public GHCommitPointer getHead() {
return head;
}
@Deprecated
public Date getIssueUpdatedAt() throws IOException {
return super.getUpdatedAt();
@@ -182,11 +194,33 @@ public class GHPullRequest extends GHIssue {
return maintainer_can_modify;
}
public Boolean getMergeable() throws IOException {
public boolean isDraft() throws IOException {
populate();
return draft;
}
/**
* 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 {
refresh(mergeable);
return mergeable;
}
/**
* for test purposes only
*/
@Deprecated
Boolean getMergeableNoRefresh() throws IOException {
return mergeable;
}
public int getDeletions() throws IOException {
populate();
return deletions;
@@ -210,6 +244,16 @@ public class GHPullRequest extends GHIssue {
return merge_commit_sha;
}
public List<GHUser> getRequestedReviewers() throws IOException {
refresh(requested_reviewers);
return Collections.unmodifiableList(Arrays.asList(requested_reviewers));
}
public List<GHTeam> getRequestedTeams() throws IOException {
refresh(requested_teams);
return Collections.unmodifiableList(Arrays.asList(requested_teams));
}
/**
* Fully populate the data by retrieving missing data.
*
@@ -217,111 +261,89 @@ public class GHPullRequest extends GHIssue {
*/
private void populate() throws IOException {
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
}
root.retrieve().to(url, this).wrapUp(owner);
root.retrieve()
.withPreview(SHADOW_CAT)
.to(url, this).wrapUp(owner);
}
/**
* Retrieves all the files associated to this pull request.
*/
public PagedIterable<GHPullRequestFileDetail> listFiles() {
return new PagedIterable<GHPullRequestFileDetail>() {
public PagedIterator<GHPullRequestFileDetail> _iterator(int pageSize) {
return new PagedIterator<GHPullRequestFileDetail>(root.retrieve().asIterator(String.format("%s/files", getApiRoute()),
GHPullRequestFileDetail[].class, pageSize)) {
@Override
protected void wrapUp(GHPullRequestFileDetail[] page) {
}
};
}
};
return root.retrieve()
.asPagedIterable(
String.format("%s/files", getApiRoute()),
GHPullRequestFileDetail[].class,
null);
}
/**
* 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()
.withPreview(BLACK_CAT)
.asIterator(String.format("%s/reviews", getApiRoute()),
GHPullRequestReview[].class, pageSize)) {
@Override
protected void wrapUp(GHPullRequestReview[] page) {
for (GHPullRequestReview r: page) {
r.wrapUp(GHPullRequest.this);
}
}
};
}
};
return root.retrieve()
.asPagedIterable(
String.format("%s/reviews", getApiRoute()),
GHPullRequestReview[].class,
item -> item.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",
GHPullRequestReviewComment[].class, pageSize)) {
protected void wrapUp(GHPullRequestReviewComment[] page) {
for (GHPullRequestReviewComment c : page)
c.wrapUp(GHPullRequest.this);
}
};
}
};
return root.retrieve()
.asPagedIterable(
getApiRoute() + COMMENTS_ACTION,
GHPullRequestReviewComment[].class,
item -> item.wrapUp(GHPullRequest.this) );
}
/**
* Retrieves all the commits associated to this pull request.
*/
public PagedIterable<GHPullRequestCommitDetail> listCommits() {
return new PagedIterable<GHPullRequestCommitDetail>() {
public PagedIterator<GHPullRequestCommitDetail> _iterator(int pageSize) {
return new PagedIterator<GHPullRequestCommitDetail>(root.retrieve().asIterator(
return root.retrieve()
.asPagedIterable(
String.format("%s/commits", getApiRoute()),
GHPullRequestCommitDetail[].class, pageSize)) {
@Override
protected void wrapUp(GHPullRequestCommitDetail[] page) {
for (GHPullRequestCommitDetail c : page)
c.wrapUp(GHPullRequest.this);
}
};
}
};
GHPullRequestCommitDetail[].class,
item -> item.wrapUp(GHPullRequest.this) );
}
@Preview
@Deprecated
/**
* @deprecated
* Use {@link #createReview()}
*/
public GHPullRequestReview createReview(String body, @CheckForNull GHPullRequestReviewState event,
GHPullRequestReviewComment... comments)
throws IOException {
GHPullRequestReviewComment... comments) throws IOException {
return createReview(body, event, Arrays.asList(comments));
}
@Preview
@Deprecated
/**
* @deprecated
* Use {@link #createReview()}
*/
public GHPullRequestReview createReview(String body, @CheckForNull GHPullRequestReviewState event,
List<GHPullRequestReviewComment> comments)
throws IOException {
// if (event == null) {
// event = GHPullRequestReviewState.PENDING;
// }
List<DraftReviewComment> draftComments = new ArrayList<DraftReviewComment>(comments.size());
List<GHPullRequestReviewComment> comments) throws IOException {
GHPullRequestReviewBuilder b = createReview().body(body);
for (GHPullRequestReviewComment c : comments) {
draftComments.add(new DraftReviewComment(c.getBody(), c.getPath(), c.getPosition()));
b.comment(c.getBody(), c.getPath(), c.getPosition());
}
return new Requester(root).method("POST")
.with("body", body)
//.with("event", event.name())
._with("comments", draftComments)
.withPreview(BLACK_CAT)
.to(getApiRoute() + "/reviews", GHPullRequestReview.class).wrapUp(this);
return b.create();
}
public GHPullRequestReviewBuilder createReview() {
return new GHPullRequestReviewBuilder(this);
}
public GHPullRequestReviewComment createReviewComment(String body, String sha, String path, int position) throws IOException {
@@ -330,7 +352,23 @@ public class GHPullRequest extends GHIssue {
.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);
}
public void requestTeamReviewers(List<GHTeam> teams) throws IOException {
List<String> teamReviewers = new ArrayList<String>(teams.size());
for (GHTeam team : teams) {
teamReviewers.add(team.getSlug());
}
new Requester(root).method("POST")
.with("team_reviewers", teamReviewers)
.to(getApiRoute() + REQUEST_REVIEWERS);
}
/**
@@ -371,42 +409,18 @@ public class GHPullRequest extends GHIssue {
*/
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");
.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;
}
}
private static class DraftReviewComment {
private String body;
private String path;
private int position;
public 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

@@ -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

@@ -1,5 +1,7 @@
package org.kohsuke.github;
import static org.kohsuke.github.Previews.SHADOW_CAT;
/**
* Lists up pull requests with some filtering and sorting.
*
@@ -20,6 +22,9 @@ public class GHPullRequestQueryBuilder extends GHQueryBuilder<GHPullRequest> {
}
public GHPullRequestQueryBuilder head(String head) {
if (head != null && !head.contains(":")) {
head = repo.getOwnerName() + ":" + head;
}
req.with("head",head);
return this;
}
@@ -43,16 +48,11 @@ public class GHPullRequestQueryBuilder extends GHQueryBuilder<GHPullRequest> {
@Override
public PagedIterable<GHPullRequest> list() {
return new PagedIterable<GHPullRequest>() {
public PagedIterator<GHPullRequest> _iterator(int pageSize) {
return new PagedIterator<GHPullRequest>(req.asIterator(repo.getApiTailUrl("pulls"), GHPullRequest[].class, pageSize)) {
@Override
protected void wrapUp(GHPullRequest[] page) {
for (GHPullRequest pr : page)
pr.wrapUp(repo);
}
};
}
};
return req
.withPreview(SHADOW_CAT)
.asPagedIterable(
repo.getApiTailUrl("pulls"),
GHPullRequest[].class,
item -> item.wrapUp(repo) );
}
}

View File

@@ -23,17 +23,20 @@
*/
package org.kohsuke.github;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.Date;
import javax.annotation.CheckForNull;
import java.io.IOException;
import java.net.URL;
import static org.kohsuke.github.Previews.*;
/**
* Review to the pull request
* Review to a pull request.
*
* @see GHPullRequest#listReviews()
* @see GHPullRequest#createReview(String, GHPullRequestReviewState, GHPullRequestReviewComment...)
* @see GHPullRequestReviewBuilder
*/
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_FIELD"}, justification = "JSON API")
public class GHPullRequestReview extends GHObject {
GHPullRequest owner;
@@ -41,6 +44,7 @@ public class GHPullRequestReview extends GHObject {
private GHUser user;
private String commit_id;
private GHPullRequestReviewState state;
private String submitted_at;
/*package*/ GHPullRequestReview wrapUp(GHPullRequest owner) {
this.owner = owner;
@@ -72,6 +76,7 @@ public class GHPullRequestReview extends GHObject {
return commit_id;
}
@CheckForNull
public GHPullRequestReviewState getState() {
return state;
}
@@ -85,41 +90,56 @@ public class GHPullRequestReview extends GHObject {
return owner.getApiRoute()+"/reviews/"+id;
}
/**
* When was this resource created?
*/
public Date getSubmittedAt() throws IOException {
return GitHub.parseDate(submitted_at);
}
/**
* Since this method does not exist, we forward this value.
*/
@Override
public Date getCreatedAt() throws IOException {
return getSubmittedAt();
}
/**
* @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.
*/
@Preview
@Deprecated
public void submit(String body, GHPullRequestReviewState event) throws IOException {
public void submit(String body, GHPullRequestReviewEvent event) throws IOException {
new Requester(owner.root).method("POST")
.with("body", body)
.with("event", event.action())
.withPreview("application/vnd.github.black-cat-preview+json")
.to(getApiRoute()+"/events",this);
this.body = body;
this.state = event;
this.state = event.toState();
}
/**
* Deletes this review.
*/
@Preview
@Deprecated
public void delete() throws IOException {
new Requester(owner.root).method("DELETE")
.withPreview(BLACK_CAT)
.to(getApiRoute());
}
/**
* Dismisses this review.
*/
@Preview
@Deprecated
public void dismiss(String message) throws IOException {
new Requester(owner.root).method("PUT")
.with("message", message)
.withPreview(BLACK_CAT)
.to(getApiRoute()+"/dismissals");
state = GHPullRequestReviewState.DISMISSED;
}
@@ -127,23 +147,11 @@ public class GHPullRequestReview extends GHObject {
/**
* Obtains all the review comments associated with this pull request review.
*/
@Preview
@Deprecated
public PagedIterable<GHPullRequestReviewComment> listReviewComments() throws IOException {
return new PagedIterable<GHPullRequestReviewComment>() {
public PagedIterator<GHPullRequestReviewComment> _iterator(int pageSize) {
return new PagedIterator<GHPullRequestReviewComment>(
owner.root.retrieve()
.withPreview(BLACK_CAT)
.asIterator(getApiRoute() + "/comments",
GHPullRequestReviewComment[].class, pageSize)) {
protected void wrapUp(GHPullRequestReviewComment[] page) {
for (GHPullRequestReviewComment c : page)
c.wrapUp(owner);
}
};
}
};
return owner.root.retrieve()
.asPagedIterable(
getApiRoute() + "/comments",
GHPullRequestReviewComment[].class,
item -> item.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,9 +42,15 @@ 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;
@@ -82,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
@@ -114,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)
@@ -124,16 +148,11 @@ public class GHPullRequestReviewComment extends GHObject implements Reactable {
@Preview @Deprecated
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)) {
@Override
protected void wrapUp(GHReaction[] page) {
for (GHReaction c : page)
c.wrap(owner.root);
}
};
}
};
return owner.root.retrieve()
.withPreview(SQUIRREL_GIRL)
.asPagedIterable(
getApiRoute() + "/reactions",
GHReaction[].class,
item -> item.wrap(owner.root) );
}
}

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

@@ -1,19 +1,39 @@
package org.kohsuke.github;
/**
* Current state of {@link GHPullRequestReview}
*/
public enum GHPullRequestReviewState {
PENDING(null),
APPROVED("APPROVE"),
REQUEST_CHANGES("REQUEST_CHANGES"),
COMMENTED("COMMENT"),
DISMISSED(null);
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;
private final String _action;
GHPullRequestReviewState(String action) {
_action = action;
/**
* @deprecated
* This was an internal method accidentally exposed.
* Left here for compatibility.
*/
public String action() {
GHPullRequestReviewEvent e = toEvent();
return e==null ? null : e.action();
}
public String action() {
return _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

@@ -3,6 +3,7 @@ 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;
@@ -125,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);
}

View File

@@ -24,10 +24,12 @@
package org.kohsuke.github;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.exc.MismatchedInputException;
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;
@@ -37,6 +39,7 @@ import java.io.InterruptedIOException;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Arrays;
@@ -50,6 +53,10 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.WeakHashMap;
import java.util.NoSuchElementException;
import java.util.logging.Level;
import java.util.logging.Logger;
import static java.util.Arrays.*;
import static org.kohsuke.github.Previews.*;
@@ -76,15 +83,20 @@ 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;
private boolean allow_squash_merge;
private boolean allow_merge_commit;
private boolean allow_rebase_merge;
@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;
@@ -106,24 +118,18 @@ public class GHRepository extends GHObject {
public PagedIterable<GHDeployment> listDeployments(String sha,String ref,String task,String environment){
List<String> params = Arrays.asList(getParam("sha", sha), getParam("ref", ref), getParam("task", task), getParam("environment", environment));
final String deploymentsUrl = getApiTailUrl("deployments") + "?"+ join(params,"&");
return new PagedIterable<GHDeployment>() {
public PagedIterator<GHDeployment> _iterator(int pageSize) {
return new PagedIterator<GHDeployment>(root.retrieve().asIterator(deploymentsUrl, GHDeployment[].class, pageSize)) {
@Override
protected void wrapUp(GHDeployment[] page) {
for (GHDeployment c : page)
c.wrap(GHRepository.this);
}
};
}
};
return root.retrieve()
.asPagedIterable(
deploymentsUrl,
GHDeployment[].class,
item -> item.wrap(GHRepository.this) );
}
/**
* 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);
return root.retrieve().to(getApiTailUrl("deployments/" + id), GHDeployment.class).wrap(this);
}
private String join(List<String> params, String joinStr) {
@@ -173,6 +179,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;
}
@@ -264,17 +278,11 @@ public class GHRepository extends GHObject {
* Lists up all the issues in this repository.
*/
public PagedIterable<GHIssue> listIssues(final GHIssueState state) {
return new PagedIterable<GHIssue>() {
public PagedIterator<GHIssue> _iterator(int pageSize) {
return new PagedIterator<GHIssue>(root.retrieve().with("state",state).asIterator(getApiTailUrl("issues"), GHIssue[].class, pageSize)) {
@Override
protected void wrapUp(GHIssue[] page) {
for (GHIssue c : page)
c.wrap(GHRepository.this);
}
};
}
};
return root.retrieve().with("state",state)
.asPagedIterable(
getApiTailUrl("issues"),
GHIssue[].class,
item -> item.wrap(GHRepository.this) );
}
public GHReleaseBuilder createRelease(String tag) {
@@ -302,6 +310,22 @@ public class GHRepository extends GHObject {
public List<GHRelease> getReleases() throws IOException {
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 {
@@ -312,31 +336,19 @@ public class GHRepository extends GHObject {
}
public PagedIterable<GHRelease> listReleases() throws IOException {
return new PagedIterable<GHRelease>() {
public PagedIterator<GHRelease> _iterator(int pageSize) {
return new PagedIterator<GHRelease>(root.retrieve().asIterator(getApiTailUrl("releases"), GHRelease[].class, pageSize)) {
@Override
protected void wrapUp(GHRelease[] page) {
for (GHRelease c : page)
c.wrap(GHRepository.this);
}
};
}
};
return root.retrieve()
.asPagedIterable(
getApiTailUrl("releases"),
GHRelease[].class,
item -> item.wrap(GHRepository.this) );
}
public PagedIterable<GHTag> listTags() throws IOException {
return new PagedIterable<GHTag>() {
public PagedIterator<GHTag> _iterator(int pageSize) {
return new PagedIterator<GHTag>(root.retrieve().asIterator(getApiTailUrl("tags"), GHTag[].class, pageSize)) {
@Override
protected void wrapUp(GHTag[] page) {
for (GHTag c : page)
c.wrap(GHRepository.this);
}
};
}
};
return root.retrieve()
.asPagedIterable(
getApiTailUrl("tags"),
GHTag[].class,
item -> item.wrap(GHRepository.this) );
}
/**
@@ -371,6 +383,22 @@ public class GHRepository extends GHObject {
return fork;
}
public boolean isArchived() {
return archived;
}
public boolean isAllowSquashMerge() {
return allow_squash_merge;
}
public boolean isAllowMergeCommit() {
return allow_merge_commit;
}
public boolean isAllowRebaseMerge() {
return allow_rebase_merge;
}
/**
* Returns the number of all forks of this repository.
* This not only counts direct forks, but also forks of forks, and so on.
@@ -593,6 +621,22 @@ public class GHRepository extends GHObject {
edit("default_branch", value);
}
public void setPrivate(boolean value) throws IOException {
edit("private", Boolean.toString(value));
}
public void allowSquashMerge(boolean value) throws IOException {
edit("allow_squash_merge", Boolean.toString(value));
}
public void allowMergeCommit(boolean value) throws IOException {
edit("allow_merge_commit", Boolean.toString(value));
}
public void allowRebaseMerge(boolean value) throws IOException {
edit("allow_rebase_merge", Boolean.toString(value));
}
/**
* Deletes this repository.
*/
@@ -604,6 +648,29 @@ public class GHRepository extends GHObject {
}
}
/**
* Will archive and this repository as read-only. When a repository is archived, any operation
* that can change its state is forbidden. This applies symmetrically if trying to unarchive it.
*
* <p>When you try to do any operation that modifies a read-only repository, it returns the
* response:
*
* <pre>
* org.kohsuke.github.HttpException: {
* "message":"Repository was archived so is read-only.",
* "documentation_url":"https://developer.github.com/v3/repos/#edit"
* }
* </pre>
*
* @throws IOException In case of any networking error or error from the server.
*/
public void archive() throws IOException {
edit("archived", "true");
// Generall would not update this record,
// but do so here since this will result in any other update actions failing
archived = true;
}
/**
* Sort orders for listing forks
*/
@@ -623,18 +690,11 @@ public class GHRepository extends GHObject {
* currently {@link ForkSort#NEWEST ForkSort.NEWEST}.
*/
public PagedIterable<GHRepository> listForks(final ForkSort sort) {
return new PagedIterable<GHRepository>() {
public PagedIterator<GHRepository> _iterator(int pageSize) {
return new PagedIterator<GHRepository>(root.retrieve().with("sort",sort).asIterator(getApiTailUrl("forks"), GHRepository[].class, pageSize)) {
@Override
protected void wrapUp(GHRepository[] page) {
for (GHRepository c : page) {
c.wrap(root);
}
}
};
}
};
return root.retrieve().with("sort",sort)
.asPagedIterable(
getApiTailUrl("forks"),
GHRepository[].class,
item -> item.wrap(root) );
}
/**
@@ -685,7 +745,9 @@ public class GHRepository extends GHObject {
* Retrieves a specified pull request.
*/
public GHPullRequest getPullRequest(int i) throws IOException {
return root.retrieve().to(getApiTailUrl("pulls/" + i), GHPullRequest.class).wrapUp(this);
return root.retrieve()
.withPreview(SHADOW_CAT)
.to(getApiTailUrl("pulls/" + i), GHPullRequest.class).wrapUp(this);
}
/**
@@ -731,10 +793,64 @@ public class GHRepository extends GHObject {
* of a pull request.
*/
public GHPullRequest createPullRequest(String title, String head, String base, String body) throws IOException {
return new Requester(root).with("title",title)
return createPullRequest(title, head, base, body, true);
}
/**
* Creates a new pull request. Maintainer's permissions aware.
*
* @param title
* Required. The title of the pull request.
* @param head
* Required. The name of the branch where your changes are implemented.
* For cross-repository pull requests in the same network,
* namespace head with a user like this: username:branch.
* @param base
* Required. The name of the branch you want your changes pulled into.
* This should be an existing branch on the current repository.
* @param body
* The contents of the pull request. This is the markdown description
* of a pull request.
* @param maintainerCanModify
* Indicates whether maintainers can modify the pull request.
*/
public GHPullRequest createPullRequest(String title, String head, String base, String body,
boolean maintainerCanModify) throws IOException {
return createPullRequest(title, head, base, body, maintainerCanModify, false);
}
/**
* Creates a new pull request. Maintainer's permissions and draft aware.
*
* @param title
* Required. The title of the pull request.
* @param head
* Required. The name of the branch where your changes are implemented.
* For cross-repository pull requests in the same network,
* namespace head with a user like this: username:branch.
* @param base
* Required. The name of the branch you want your changes pulled into.
* This should be an existing branch on the current repository.
* @param body
* The contents of the pull request. This is the markdown description
* of a pull request.
* @param maintainerCanModify
* Indicates whether maintainers can modify the pull request.
* @param draft
* Indicates whether to create a draft pull request or not.
*/
public GHPullRequest createPullRequest(String title, String head, String base, String body,
boolean maintainerCanModify, boolean draft) throws IOException {
return new Requester(root)
.withPreview(SHADOW_CAT)
.with("title",title)
.with("head",head)
.with("base",base)
.with("body",body).to(getApiTailUrl("pulls"),GHPullRequest.class).wrapUp(this);
.with("body",body)
.with("maintainer_can_modify", maintainerCanModify)
.with("draft", draft)
.to(getApiTailUrl("pulls"),GHPullRequest.class)
.wrapUp(this);
}
/**
@@ -750,7 +866,7 @@ public class GHRepository extends GHObject {
/**
* Gets a comparison between 2 points in the repository. This would be similar
* to calling <tt>git log id1...id2</tt> against a local repository.
* to calling <code>git log id1...id2</code> against a local repository.
* @param id1 an identifier for the first point to compare from, this can be a sha1 ID (for a commit, tag etc) or a direct tag name
* @param id2 an identifier for the second point to compare to. Can be the same as the first point.
* @return the comparison output
@@ -803,20 +919,16 @@ public class GHRepository extends GHObject {
*/
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) {
// no-op
}
};
}
};
return root.retrieve()
.asPagedIterable(
url,
GHRef[].class,
item -> item.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>
* @param refType the type of reg to search for e.g. <code>tags</code> or <code>commits</code>
* @return an array of all refs matching the request type
* @throws IOException on failure communicating with GitHub, potentially due to an invalid ref type being requested
*/
@@ -827,21 +939,17 @@ public class GHRepository extends GHObject {
/**
* 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>
* @param refType the type of reg to search for e.g. <code>tags</code> or <code>commits</code>
* @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
}
};
}
};
return root.retrieve()
.asPagedIterable(
url,
GHRef[].class,
item -> item.wrap(root));
}
/**
@@ -872,7 +980,7 @@ public class GHRepository extends GHObject {
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.
*
@@ -955,16 +1063,11 @@ public class GHRepository extends GHObject {
* Lists all the commits.
*/
public PagedIterable<GHCommit> listCommits() {
return new PagedIterable<GHCommit>() {
public PagedIterator<GHCommit> _iterator(int pageSize) {
return new PagedIterator<GHCommit>(root.retrieve().asIterator(String.format("/repos/%s/%s/commits", getOwnerName(), name), GHCommit[].class, pageSize)) {
protected void wrapUp(GHCommit[] page) {
for (GHCommit c : page)
c.wrapUp(GHRepository.this);
}
};
}
};
return root.retrieve()
.asPagedIterable(
String.format("/repos/%s/%s/commits", getOwnerName(), name),
GHCommit[].class,
item -> item.wrapUp(GHRepository.this) );
}
/**
@@ -978,28 +1081,20 @@ public class GHRepository extends GHObject {
* Lists up all the commit comments in this repository.
*/
public PagedIterable<GHCommitComment> listCommitComments() {
return new PagedIterable<GHCommitComment>() {
public PagedIterator<GHCommitComment> _iterator(int pageSize) {
return new PagedIterator<GHCommitComment>(root.retrieve().asIterator(String.format("/repos/%s/%s/comments", getOwnerName(), name), GHCommitComment[].class, pageSize)) {
@Override
protected void wrapUp(GHCommitComment[] page) {
for (GHCommitComment c : page)
c.wrap(GHRepository.this);
}
};
}
};
return root.retrieve()
.asPagedIterable(
String.format("/repos/%s/%s/comments", getOwnerName(), name),
GHCommitComment[].class,
item -> item.wrap(GHRepository.this) );
}
/**
* Gets the basic license details for the repository.
* <p>
* This is a preview item and subject to change.
*
* @throws IOException as usual but also if you don't use the preview connector
* @return null if there's no license.
*/
@Preview @Deprecated
public GHLicense getLicense() throws IOException{
GHContentWithLicense lic = getLicenseContent_();
return lic!=null ? lic.license : null;
@@ -1008,21 +1103,17 @@ public class GHRepository extends GHObject {
/**
* Retrieves the contents of the repository's license file - makes an additional API call
* <p>
* This is a preview item and subject to change.
*
* @return details regarding the license contents, or null if there's no license.
* @throws IOException as usual but also if you don't use the preview connector
*/
@Preview @Deprecated
public GHContent getLicenseContent() throws IOException {
return getLicenseContent_();
}
@Preview @Deprecated
private GHContentWithLicense getLicenseContent_() throws IOException {
try {
return root.retrieve()
.withPreview(DRAX)
.to(getApiTailUrl("license"), GHContentWithLicense.class).wrap(this);
} catch (FileNotFoundException e) {
return null;
@@ -1035,17 +1126,11 @@ public class GHRepository extends GHObject {
* Lists all the commit statues attached to the given commit, newer ones first.
*/
public PagedIterable<GHCommitStatus> listCommitStatuses(final String sha1) throws IOException {
return new PagedIterable<GHCommitStatus>() {
public PagedIterator<GHCommitStatus> _iterator(int pageSize) {
return new PagedIterator<GHCommitStatus>(root.retrieve().asIterator(String.format("/repos/%s/%s/statuses/%s", getOwnerName(), name, sha1), GHCommitStatus[].class, pageSize)) {
@Override
protected void wrapUp(GHCommitStatus[] page) {
for (GHCommitStatus c : page)
c.wrapUp(root);
}
};
}
};
return root.retrieve()
.asPagedIterable(
String.format("/repos/%s/%s/statuses/%s", getOwnerName(), name, sha1),
GHCommitStatus[].class,
item -> item.wrapUp(root) );
}
/**
@@ -1086,17 +1171,11 @@ public class GHRepository extends GHObject {
* Lists repository events.
*/
public PagedIterable<GHEventInfo> listEvents() throws IOException {
return new PagedIterable<GHEventInfo>() {
public PagedIterator<GHEventInfo> _iterator(int pageSize) {
return new PagedIterator<GHEventInfo>(root.retrieve().asIterator(String.format("/repos/%s/%s/events", getOwnerName(), name), GHEventInfo[].class, pageSize)) {
@Override
protected void wrapUp(GHEventInfo[] page) {
for (GHEventInfo c : page)
c.wrapUp(root);
}
};
}
};
return root.retrieve()
.asPagedIterable(
String.format("/repos/%s/%s/events", getOwnerName(), name),
GHEventInfo[].class,
item -> item.wrapUp(root) );
}
/**
@@ -1105,30 +1184,54 @@ public class GHRepository extends GHObject {
* https://developer.github.com/v3/issues/labels/#list-all-labels-for-this-repository
*/
public PagedIterable<GHLabel> listLabels() throws IOException {
return new PagedIterable<GHLabel>() {
public PagedIterator<GHLabel> _iterator(int pageSize) {
return new PagedIterator<GHLabel>(root.retrieve().asIterator(getApiTailUrl("labels"), GHLabel[].class, pageSize)) {
@Override
protected void wrapUp(GHLabel[] page) {
for (GHLabel c : page)
c.wrapUp(GHRepository.this);
}
};
}
};
return root.retrieve()
.withPreview(SYMMETRA)
.asPagedIterable(
getApiTailUrl("labels"),
GHLabel[].class,
item -> item.wrapUp(GHRepository.this) );
}
public GHLabel getLabel(String name) throws IOException {
return root.retrieve().to(getApiTailUrl("labels/"+name), GHLabel.class).wrapUp(this);
return root.retrieve()
.withPreview(SYMMETRA)
.to(getApiTailUrl("labels/"+name), GHLabel.class)
.wrapUp(this);
}
public GHLabel createLabel(String name, String color) throws IOException {
return createLabel(name, color, "");
}
/**
* Description is still in preview.
* @param name
* @param color
* @param description
* @return
* @throws IOException
*/
@Preview @Deprecated
public GHLabel createLabel(String name, String color, String description) throws IOException {
return root.retrieve().method("POST")
.withPreview(SYMMETRA)
.with("name",name)
.with("color", color)
.with("description", description)
.to(getApiTailUrl("labels"), GHLabel.class).wrapUp(this);
}
/**
* Lists all the invitations.
*/
public PagedIterable<GHInvitation> listInvitations() {
return root.retrieve()
.asPagedIterable(
String.format("/repos/%s/%s/invitations", getOwnerName(), name),
GHInvitation[].class,
item -> item.wrapUp(root) );
}
/**
* Lists all the subscribers (aka watchers.)
*
@@ -1152,34 +1255,20 @@ public class GHRepository extends GHObject {
* see {@link #listStargazers()}
*/
public PagedIterable<GHStargazer> listStargazers2() {
return new PagedIterable<GHStargazer>() {
@Override
public PagedIterator<GHStargazer> _iterator(int pageSize) {
Requester requester = root.retrieve();
requester.setHeader("Accept", "application/vnd.github.v3.star+json");
return new PagedIterator<GHStargazer>(requester.asIterator(getApiTailUrl("stargazers"), GHStargazer[].class, pageSize)) {
@Override
protected void wrapUp(GHStargazer[] page) {
for (GHStargazer c : page) {
c.wrapUp(GHRepository.this);
}
}
};
}
};
return root.retrieve()
.withPreview("application/vnd.github.v3.star+json")
.asPagedIterable(
getApiTailUrl("stargazers"),
GHStargazer[].class,
item -> item.wrapUp(GHRepository.this) );
}
private PagedIterable<GHUser> listUsers(final String suffix) {
return new PagedIterable<GHUser>() {
public PagedIterator<GHUser> _iterator(int pageSize) {
return new PagedIterator<GHUser>(root.retrieve().asIterator(getApiTailUrl(suffix), GHUser[].class, pageSize)) {
protected void wrapUp(GHUser[] page) {
for (GHUser c : page)
c.wrapUp(root);
}
};
}
};
return root.retrieve()
.asPagedIterable(
getApiTailUrl(suffix),
GHUser[].class,
item -> item.wrapUp(root) );
}
/**
@@ -1298,15 +1387,32 @@ public class GHRepository extends GHObject {
*/
public Map<String,GHBranch> getBranches() throws IOException {
Map<String,GHBranch> r = new TreeMap<String,GHBranch>();
for (GHBranch p : root.retrieve().withPreview(LOKI).to(getApiTailUrl("branches"), GHBranch[].class)) {
for (GHBranch p : root.retrieve().to(getApiTailUrl("branches"), GHBranch[].class)) {
p.wrap(this);
r.put(p.getName(),p);
}
return r;
}
/**
* Replace special characters (e.g. #) with standard values (e.g. %23) so
* GitHub understands what is being requested.
* @param value string to be encoded.
* @return The encoded string.
*/
private String UrlEncode(String value) {
try {
return URLEncoder.encode(value, org.apache.commons.codec.CharEncoding.UTF_8);
} catch (UnsupportedEncodingException ex) {
Logger.getLogger(GHRepository.class.getName()).log(Level.SEVERE, null, ex);
}
// Something went wrong - just return original value as is.
return value;
}
public GHBranch getBranch(String name) throws IOException {
return root.retrieve().withPreview(LOKI).to(getApiTailUrl("branches/"+name),GHBranch.class).wrap(this);
return root.retrieve().to(getApiTailUrl("branches/"+UrlEncode(name)),GHBranch.class).wrap(this);
}
/**
@@ -1325,17 +1431,11 @@ public class GHRepository extends GHObject {
* Lists up all the milestones in this repository.
*/
public PagedIterable<GHMilestone> listMilestones(final GHIssueState state) {
return new PagedIterable<GHMilestone>() {
public PagedIterator<GHMilestone> _iterator(int pageSize) {
return new PagedIterator<GHMilestone>(root.retrieve().with("state",state).asIterator(getApiTailUrl("milestones"), GHMilestone[].class, pageSize)) {
@Override
protected void wrapUp(GHMilestone[] page) {
for (GHMilestone c : page)
c.wrap(GHRepository.this);
}
};
}
};
return root.retrieve().with("state",state)
.asPagedIterable(
getApiTailUrl("milestones"),
GHMilestone[].class,
item -> item.wrap(GHRepository.this) );
}
public GHMilestone getMilestone(int number) throws IOException {
@@ -1386,41 +1486,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 {
@@ -1498,17 +1600,11 @@ public class GHRepository extends GHObject {
}
public PagedIterable<Contributor> listContributors() throws IOException {
return new PagedIterable<Contributor>() {
public PagedIterator<Contributor> _iterator(int pageSize) {
return new PagedIterator<Contributor>(root.retrieve().asIterator(getApiTailUrl("contributors"), Contributor[].class, pageSize)) {
@Override
protected void wrapUp(Contributor[] page) {
for (Contributor c : page)
c.wrapUp(root);
}
};
}
};
return root.retrieve()
.asPagedIterable(
getApiTailUrl("contributors"),
Contributor[].class,
item -> item.wrapUp(root) );
}
public static class Contributor extends GHUser {
@@ -1531,6 +1627,46 @@ public class GHRepository extends GHObject {
}
}
/**
* Returns the statistics for this repository.
*/
public GHRepositoryStatistics getStatistics() {
// TODO: Use static object and introduce refresh() method,
// instead of returning new object each time.
return new GHRepositoryStatistics(this);
}
/**
* Create a project for this repository.
*/
public GHProject createProject(String name, String body) throws IOException {
return root.retrieve().method("POST")
.withPreview(INERTIA)
.with("name", name)
.with("body", body)
.to(getApiTailUrl("projects"), GHProject.class).wrap(this);
}
/**
* Returns the projects for this repository.
* @param status The status filter (all, open or closed).
*/
public PagedIterable<GHProject> listProjects(final GHProject.ProjectStateFilter status) throws IOException {
return root.retrieve().withPreview(INERTIA)
.with("state", status)
.asPagedIterable(
getApiTailUrl("projects"),
GHProject[].class,
item -> item.wrap(GHRepository.this) );
}
/**
* Returns open projects for this repository.
*/
public PagedIterable<GHProject> listProjects() throws IOException {
return listProjects(GHProject.ProjectStateFilter.OPEN);
}
/**
* Render a Markdown document.
*
@@ -1589,4 +1725,48 @@ public class GHRepository extends GHObject {
if (tail.length()>0 && !tail.startsWith("/")) tail='/'+tail;
return "/repos/" + getOwnerName() + "/" + name +tail;
}
/**
* Get all issue events for this repository.
* See https://developer.github.com/v3/issues/events/#list-events-for-a-repository
*/
public PagedIterable<GHIssueEvent> listIssueEvents() throws IOException {
return root.retrieve().asPagedIterable(
getApiTailUrl("issues/events"),
GHIssueEvent[].class,
item -> item.wrapUp(root) );
}
/**
* Get a single issue event.
* See https://developer.github.com/v3/issues/events/#get-a-single-event
*/
public GHIssueEvent getIssueEvent(long id) throws IOException {
return root.retrieve().to(getApiTailUrl("issues/events/" + id), GHIssueEvent.class).wrapUp(root);
}
// Only used within listTopics().
private static class Topics {
public List<String> names;
}
/**
* Return the topics for this repository.
* See https://developer.github.com/v3/repos/#list-all-topics-for-a-repository
*/
public List<String> listTopics() throws IOException {
Topics topics = root.retrieve().withPreview(MERCY).to(getApiTailUrl("topics"), Topics.class);
return topics.names;
}
/**
* Set the topics for this repository.
* See https://developer.github.com/v3/repos/#replace-all-topics-for-a-repository
*/
// This currently returns a "404" error (as of 30 Oct 2019).
// public void setTopics(List<String> topics) throws IOException {
// Requester requester = new Requester(root);
// requester.with("names", topics);
// requester.method("PUT").withPreview(MERCY).to(getApiTailUrl("topics"));
// }
}

View File

@@ -55,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,22 @@
package org.kohsuke.github;
import java.util.Locale;
/**
* App installation repository selection.
*
* @author Paulo Miguel Almeida
*
* @see GHAppInstallation
*/
public enum GHRepositorySelection {
SELECTED,
ALL;
/**
* Returns GitHub's internal representation of this event.
*/
String symbol() {
return name().toLowerCase(Locale.ENGLISH);
}
}

View File

@@ -0,0 +1,433 @@
package org.kohsuke.github;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.exc.MismatchedInputException;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
/**
* Statistics for a GitHub repository.
*
* @author Martin van Zijl
*/
public class GHRepositoryStatistics {
private final GHRepository repo;
private final GitHub root;
private static final int MAX_WAIT_ITERATIONS = 3;
private static final int WAIT_SLEEP_INTERVAL = 5000;
public GHRepositoryStatistics(GHRepository repo) {
this.repo = repo;
this.root = repo.root;
}
/**
* Get contributors list with additions, deletions, and commit count. See
* https://developer.github.com/v3/repos/statistics/#get-contributors-list-with-additions-deletions-and-commit-counts
*/
public PagedIterable<ContributorStats> getContributorStats() throws IOException, InterruptedException {
return getContributorStats(true);
}
/**
* @param waitTillReady Whether to sleep the thread if necessary until the
* statistics are ready. This is true by default.
*/
@Preview
@Deprecated
@SuppressWarnings("SleepWhileInLoop")
@SuppressFBWarnings(value = {"RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE"}, justification = "JSON API")
public PagedIterable<ContributorStats> getContributorStats(boolean waitTillReady) throws IOException, InterruptedException {
PagedIterable<GHRepositoryStatistics.ContributorStats> stats =
getContributorStatsImpl();
if (stats == null && waitTillReady) {
for (int i = 0; i < MAX_WAIT_ITERATIONS; i += 1) {
// Wait a few seconds and try again.
Thread.sleep(WAIT_SLEEP_INTERVAL);
stats = getContributorStatsImpl();
if (stats != null) {
break;
}
}
}
return stats;
}
/**
* This gets the actual statistics from the server. Returns null if they
* are still being cached.
*/
private PagedIterable<ContributorStats> getContributorStatsImpl() throws IOException {
return root.retrieve()
.asPagedIterable(
getApiTailUrl("contributors"),
ContributorStats[].class,
item -> item.wrapUp(root) );
}
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
"NP_UNWRITTEN_FIELD", "URF_UNREAD_FIELD"}, justification = "JSON API")
public static class ContributorStats extends GHObject {
/*package almost final*/ private GitHub root;
private GHUser author;
private int total;
private List<Week> weeks;
@Override
public URL getHtmlUrl() throws IOException {
throw new UnsupportedOperationException("Not supported yet.");
}
public GitHub getRoot() {
return root;
}
/**
* @return The author described by these statistics.
*/
public GHUser getAuthor() {
return author;
}
/**
* @return The total number of commits authored by the contributor.
*/
public int getTotal() {
return total;
}
/**
* Convenience method to look up week with particular timestamp.
*
* @param timestamp The timestamp to look for.
* @return The week starting with the given timestamp. Throws an
* exception if it is not found.
* @throws NoSuchElementException
*/
public Week getWeek(long timestamp) throws NoSuchElementException {
// maybe store the weeks in a map to make this more efficient?
for (Week week : weeks) {
if (week.getWeekTimestamp() == timestamp) {
return week;
}
}
// this is safer than returning null
throw new NoSuchElementException();
}
/**
* @return The total number of commits authored by the contributor.
*/
public List<Week> getWeeks() {
return weeks;
}
@Override
public String toString() {
return author.getLogin() + " made " + String.valueOf(total)
+ " contributions over " + String.valueOf(weeks.size())
+ " weeks";
}
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
"NP_UNWRITTEN_FIELD", "URF_UNREAD_FIELD"}, justification = "JSON API")
public static class Week {
private long w;
private int a;
private int d;
private int c;
/**
* @return Start of the week, as a UNIX timestamp.
*/
public long getWeekTimestamp() {
return w;
}
/**
* @return The number of additions for the week.
*/
public int getNumberOfAdditions() {
return a;
}
/**
* @return The number of deletions for the week.
*/
public int getNumberOfDeletions() {
return d;
}
/**
* @return The number of commits for the week.
*/
public int getNumberOfCommits() {
return c;
}
@Override
public String toString() {
return String.format("Week starting %d - Additions: %d, Deletions: %d, Commits: %d", w, a, d, c);
}
}
/*package*/ ContributorStats wrapUp(GitHub root) {
this.root = root;
return this;
}
}
/**
* Get the last year of commit activity data. See
* https://developer.github.com/v3/repos/statistics/#get-the-last-year-of-commit-activity-data
*/
public PagedIterable<CommitActivity> getCommitActivity() throws IOException {
return root.retrieve()
.asPagedIterable(
getApiTailUrl("commit_activity"),
CommitActivity[].class,
item -> item.wrapUp(root) );
}
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
"NP_UNWRITTEN_FIELD"}, justification = "JSON API")
public static class CommitActivity extends GHObject {
/*package almost final*/ private GitHub root;
private List<Integer> days;
private int total;
private long week;
/**
* @return The number of commits for each day of the week. 0 = Sunday, 1
* = Monday, etc.
*/
public List<Integer> getDays() {
return days;
}
/**
* @return The total number of commits for the week.
*/
public int getTotal() {
return total;
}
/**
* @return The start of the week as a UNIX timestamp.
*/
public long getWeek() {
return week;
}
/*package*/ CommitActivity wrapUp(GitHub root) {
this.root = root;
return this;
}
public GitHub getRoot() {
return root;
}
@Override
public URL getHtmlUrl() throws IOException {
throw new UnsupportedOperationException("Not supported yet.");
}
}
/**
* Get the number of additions and deletions per week.
* See https://developer.github.com/v3/repos/statistics/#get-the-number-of-additions-and-deletions-per-week
*/
public List<CodeFrequency> getCodeFrequency() throws IOException {
// Map to ArrayLists first, since there are no field names in the
// returned JSON.
try {
InputStream stream = root.retrieve().asStream(getApiTailUrl("code_frequency"));
ObjectMapper mapper = new ObjectMapper();
TypeReference<ArrayList<ArrayList<Integer> > > typeRef =
new TypeReference<ArrayList< ArrayList<Integer> > >() {};
ArrayList<ArrayList <Integer> > list = mapper.readValue(stream, typeRef);
// Convert to proper objects.
ArrayList<CodeFrequency> returnList = new ArrayList<CodeFrequency>();
for(ArrayList<Integer> item: list)
{
CodeFrequency cf = new CodeFrequency(item);
returnList.add(cf);
}
return returnList;
} catch (MismatchedInputException e) {
// This sometimes happens when retrieving code frequency statistics
// for a repository for the first time. It is probably still being
// generated, so return null.
return null;
}
}
public static class CodeFrequency {
private int week;
private int additions;
private int deletions;
private CodeFrequency(ArrayList<Integer> item) {
week = item.get(0);
additions = item.get(1);
deletions = item.get(2);
}
/**
* @return The start of the week as a UNIX timestamp.
*/
public int getWeekTimestamp() {
return week;
}
/**
* @return The number of additions for the week.
*/
public long getAdditions() {
return additions;
}
/**
* @return The number of deletions for the week.
* NOTE: This will be a NEGATIVE number.
*/
public long getDeletions() {
// TODO: Perhaps return Math.abs(deletions),
// since most developers may not expect a negative number.
return deletions;
}
@Override
public String toString() {
return "Week starting " + getWeekTimestamp() + " has " + getAdditions() +
" additions and " + Math.abs(getDeletions()) + " deletions";
}
}
/**
* Get the weekly commit count for the repository owner and everyone else.
* See https://developer.github.com/v3/repos/statistics/#get-the-weekly-commit-count-for-the-repository-owner-and-everyone-else
*/
public Participation getParticipation() throws IOException {
return root.retrieve().to(getApiTailUrl("participation"), Participation.class);
}
public static class Participation extends GHObject {
/*package almost final*/ private GitHub root;
private List<Integer> all;
private List<Integer> owner;
@Override
public URL getHtmlUrl() throws IOException {
throw new UnsupportedOperationException("Not supported yet.");
}
public GitHub getRoot() {
return root;
}
/**
* @return The list of commit counts for everyone combined, for the
* last 52 weeks.
*/
public List<Integer> getAllCommits() {
return all;
}
/**
* @return The list of commit counts for the owner, for the
* last 52 weeks.
*/
public List<Integer> getOwnerCommits() {
return owner;
}
/*package*/ Participation wrapUp(GitHub root) {
this.root = root;
return this;
}
}
/**
* Get the number of commits per hour in each day.
* See https://developer.github.com/v3/repos/statistics/#get-the-number-of-commits-per-hour-in-each-day
*/
public List<PunchCardItem> getPunchCard() throws IOException {
// Map to ArrayLists first, since there are no field names in the
// returned JSON.
InputStream stream = root.retrieve().asStream(getApiTailUrl("punch_card"));
ObjectMapper mapper = new ObjectMapper();
TypeReference<ArrayList<ArrayList<Integer> > > typeRef =
new TypeReference<ArrayList< ArrayList<Integer> > >() {};
ArrayList<ArrayList <Integer> > list = mapper.readValue(stream, typeRef);
// Convert to proper objects.
ArrayList<PunchCardItem> returnList = new ArrayList<PunchCardItem>();
for(ArrayList<Integer> item: list) {
PunchCardItem pci = new PunchCardItem(item);
returnList.add(pci);
}
return returnList;
}
public static class PunchCardItem {
private int dayOfWeek;
private int hourOfDay;
private int numberOfCommits;
private PunchCardItem(ArrayList<Integer> item) {
dayOfWeek = item.get(0);
hourOfDay = item.get(1);
numberOfCommits = item.get(2);
}
/**
* @return The day of the week.
* 0 = Sunday, 1 = Monday, etc.
*/
public int getDayOfWeek() {
return dayOfWeek;
}
/**
* @return The hour of the day from 0 to 23.
*/
public long getHourOfDay() {
return hourOfDay;
}
/**
* @return The number of commits for the day and hour.
*/
public long getNumberOfCommits() {
return numberOfCommits;
}
public String toString() {
return "Day " + getDayOfWeek() + " Hour " + getHourOfDay() + ": " +
getNumberOfCommits() + " commits";
}
}
String getApiTailUrl(String tail) {
return repo.getApiTailUrl("stats/" + tail);
}
}

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

@@ -49,7 +49,7 @@ public class GHSubscription {
* Removes this subscription.
*/
public void delete() throws IOException {
new Requester(root).method("DELETE").to(url);
new Requester(root).method("DELETE").to(repo.getApiTailUrl("subscription"));
}
GHSubscription wrapUp(GHRepository repo) {

View File

@@ -0,0 +1,24 @@
package org.kohsuke.github;
import org.apache.commons.lang3.StringUtils;
import java.util.Locale;
/**
* App installation target type.
*
* @author Paulo Miguel Almeida
*
* @see GHAppInstallation
*/
public enum GHTargetType {
ORGANIZATION,
USER;
/**
* Returns GitHub's internal representation of this event.
*/
String symbol() {
return StringUtils.capitalize(name().toLowerCase(Locale.ENGLISH));
}
}

View File

@@ -11,15 +11,28 @@ import java.util.TreeMap;
*
* @author Kohsuke Kawaguchi
*/
public class GHTeam {
private String name,permission,slug;
public class GHTeam implements Refreshable {
private String name,permission,slug,description;
private int id;
private GHOrganization organization; // populated by GET /user/teams where Teams+Orgs are returned together
protected /*final*/ GHOrganization org;
protected /*final*/ GitHub root;
/** 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;
this.organization = owner;
this.root = owner.root;
return this;
}
@@ -35,6 +48,13 @@ public class GHTeam {
return teams;
}
/*package*/ static GHTeam[] wrapUp(GHTeam[] teams, GHPullRequest owner) {
for (GHTeam t : teams) {
t.root = owner.root;
}
return teams;
}
public String getName() {
return name;
}
@@ -47,6 +67,16 @@ public class GHTeam {
return slug;
}
public String getDescription() {
return description;
}
public void setDescription(String description) throws IOException {
root.retrieve().method("PATCH")
.with("description", description)
.to(api(""));
}
public int getId() {
return id;
}
@@ -55,16 +85,11 @@ public class GHTeam {
* Retrieves the current members.
*/
public PagedIterable<GHUser> listMembers() throws IOException {
return new PagedIterable<GHUser>() {
public PagedIterator<GHUser> _iterator(int pageSize) {
return new PagedIterator<GHUser>(org.root.retrieve().asIterator(api("/members"), GHUser[].class, pageSize)) {
@Override
protected void wrapUp(GHUser[] page) {
GHUser.wrap(page, org.root);
}
};
}
};
return root.retrieve()
.asPagedIterable(
api("/members"),
GHUser[].class,
item -> item.wrapUp(root) );
}
public Set<GHUser> getMembers() throws IOException {
@@ -76,7 +101,7 @@ public class GHTeam {
*/
public boolean hasMember(GHUser user) {
try {
org.root.retrieve().to("/teams/" + id + "/members/" + user.getLogin());
root.retrieve().to("/teams/" + id + "/members/" + user.getLogin());
return true;
} catch (IOException ignore) {
return false;
@@ -92,17 +117,11 @@ public class GHTeam {
}
public PagedIterable<GHRepository> listRepositories() {
return new PagedIterable<GHRepository>() {
public PagedIterator<GHRepository> _iterator(int pageSize) {
return new PagedIterator<GHRepository>(org.root.retrieve().asIterator(api("/repos"), GHRepository[].class, pageSize)) {
@Override
protected void wrapUp(GHRepository[] page) {
for (GHRepository r : page)
r.wrap(org.root);
}
};
}
};
return root.retrieve()
.asPagedIterable(
api("/repos"),
GHRepository[].class,
item -> item.wrap(root) );
}
/**
@@ -113,14 +132,30 @@ public class GHTeam {
* @since 1.59
*/
public void add(GHUser u) throws IOException {
org.root.retrieve().method("PUT").to(api("/memberships/" + u.getLogin()), null);
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 {
root.retrieve().method("PUT")
.with("role", role)
.to(api("/memberships/" + user.getLogin()), null);
}
/**
* Removes a member to the team.
*/
public void remove(GHUser u) throws IOException {
org.root.retrieve().method("DELETE").to(api("/members/" + u.getLogin()), null);
root.retrieve().method("DELETE").to(api("/members/" + u.getLogin()), null);
}
public void add(GHRepository r) throws IOException {
@@ -128,27 +163,33 @@ public class GHTeam {
}
public void add(GHRepository r, GHOrganization.Permission permission) throws IOException {
org.root.retrieve().method("PUT")
.with("permission",permission)
root.retrieve().method("PUT")
.with("permission", permission)
.to(api("/repos/" + r.getOwnerName() + '/' + r.getName()), null);
}
public void remove(GHRepository r) throws IOException {
org.root.retrieve().method("DELETE").to(api("/repos/" + r.getOwnerName() + '/' + r.getName()), null);
root.retrieve().method("DELETE").to(api("/repos/" + r.getOwnerName() + '/' + r.getName()), null);
}
/**
* Deletes this team.
*/
public void delete() throws IOException {
org.root.retrieve().method("DELETE").to(api(""));
root.retrieve().method("DELETE").to(api(""));
}
private String api(String tail) {
return "/teams/"+id+tail;
return "/teams/" + id + tail;
}
public GHOrganization getOrganization() {
return org;
public GHOrganization getOrganization() throws IOException {
refresh(organization);
return organization;
}
@Override
public void refresh() throws IOException {
root.retrieve().to(api(""), this).wrapUp(root);
}
}

View File

@@ -26,8 +26,7 @@ package org.kohsuke.github;
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import java.util.*;
/**
* Represents an user of GitHub.
@@ -36,6 +35,10 @@ import java.util.Set;
*/
public class GHUser extends GHPerson {
public List<GHKey> getKeys() throws IOException {
return Collections.unmodifiableList(Arrays.asList(root.retrieve().to(getApiTailUrl("keys"), GHKey[].class)));
}
/**
* Follow this user.
*/
@@ -81,15 +84,11 @@ public class GHUser extends GHPerson {
}
private PagedIterable<GHUser> listUser(final String suffix) {
return new PagedIterable<GHUser>() {
public PagedIterator<GHUser> _iterator(int pageSize) {
return new PagedIterator<GHUser>(root.retrieve().asIterator(getApiTailUrl(suffix), GHUser[].class, pageSize)) {
protected void wrapUp(GHUser[] page) {
GHUser.wrap(page,root);
}
};
}
};
return root.retrieve()
.asPagedIterable(
getApiTailUrl(suffix),
GHUser[].class,
item -> item.wrapUp(root) );
}
/**
@@ -109,16 +108,11 @@ public class GHUser extends GHPerson {
}
private PagedIterable<GHRepository> listRepositories(final String suffix) {
return new PagedIterable<GHRepository>() {
public PagedIterator<GHRepository> _iterator(int pageSize) {
return new PagedIterator<GHRepository>(root.retrieve().asIterator(getApiTailUrl(suffix), GHRepository[].class, pageSize)) {
protected void wrapUp(GHRepository[] page) {
for (GHRepository c : page)
c.wrap(root);
}
};
}
};
return root.retrieve()
.asPagedIterable(
getApiTailUrl(suffix),
GHRepository[].class,
item -> item.wrap(root) );
}
/**
@@ -166,34 +160,22 @@ public class GHUser extends GHPerson {
* Lists events performed by a user (this includes private events if the caller is authenticated.
*/
public PagedIterable<GHEventInfo> listEvents() throws IOException {
return new PagedIterable<GHEventInfo>() {
public PagedIterator<GHEventInfo> _iterator(int pageSize) {
return new PagedIterator<GHEventInfo>(root.retrieve().asIterator(String.format("/users/%s/events", login), GHEventInfo[].class, pageSize)) {
@Override
protected void wrapUp(GHEventInfo[] page) {
for (GHEventInfo c : page)
c.wrapUp(root);
}
};
}
};
return root.retrieve()
.asPagedIterable(
String.format("/users/%s/events", login),
GHEventInfo[].class,
item -> item.wrapUp(root) );
}
/**
* Lists Gists created by this user.
*/
public PagedIterable<GHGist> listGists() throws IOException {
return new PagedIterable<GHGist>() {
public PagedIterator<GHGist> _iterator(int pageSize) {
return new PagedIterator<GHGist>(root.retrieve().asIterator(String.format("/users/%s/gists", login), GHGist[].class, pageSize)) {
@Override
protected void wrapUp(GHGist[] page) {
for (GHGist c : page)
c.wrapUp(GHUser.this);
}
};
}
};
return root.retrieve()
.asPagedIterable(
String.format("/users/%s/gists", login),
GHGist[].class,
item -> item.wrapUp(GHUser.this) );
}
@Override

View File

@@ -24,6 +24,7 @@
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;
@@ -33,34 +34,23 @@ 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;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.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.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.logging.Logger;
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.*;
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.INERTIA;
import static org.kohsuke.github.Previews.MACHINE_MAN;
/**
* Root of the GitHub API.
@@ -104,7 +94,7 @@ public class GitHub {
* to represent different ways of authentication.
*
* <dl>
* <dt>Loging anonymously
* <dt>Log in anonymously
* <dd>Leave all three parameters null and you will be making HTTP requests without any authentication.
*
* <dt>Log in with password
@@ -115,15 +105,21 @@ public class GitHub {
* <dd>Specify oauthAccessToken, and optionally specify the login. Leave password null.
* This will send OAuth token to the GitHub API. If the login parameter is null,
* The constructor makes an API call to figure out the user name that owns the token.
*
* <dt>Log in with JWT token
* <dd>Specify jwtToken. Leave password null.
* This will send JWT token to the GitHub API via the Authorization HTTP header.
* Please note that only operations in which permissions have been previously configured and accepted during
* the GitHub App will be executed successfully.
* </dl>
*
* @param apiUrl
* The URL of GitHub (or GitHub enterprise) API endpoint, such as "https://api.github.com" or
* "http://ghe.acme.com/api/v3". Note that GitHub Enterprise has <tt>/api/v3</tt> in the URL.
* "http://ghe.acme.com/api/v3". Note that GitHub Enterprise has <code>/api/v3</code> in the URL.
* For historical reasons, this parameter still accepts the bare domain name, but that's considered deprecated.
* Password is also considered deprecated as it is no longer required for api usage.
* @param login
* The use ID on GitHub that you are logging in as. Can be omitted if the OAuth token is
* The user ID on GitHub that you are logging in as. Can be omitted if the OAuth token is
* provided or if logging in anonymously. Specifying this would save one API call.
* @param oauthAccessToken
* Secret OAuth token.
@@ -132,7 +128,7 @@ public class GitHub {
* @param connector
* HttpConnector to use. Pass null to use default connector.
*/
/* package */ GitHub(String apiUrl, String login, String oauthAccessToken, String password, HttpConnector connector, RateLimitHandler rateLimitHandler, AbuseLimitHandler abuseLimitHandler) throws IOException {
/* package */ GitHub(String apiUrl, String login, String oauthAccessToken, String jwtToken, String password, HttpConnector connector, RateLimitHandler rateLimitHandler, AbuseLimitHandler abuseLimitHandler) throws IOException {
if (apiUrl.endsWith("/")) apiUrl = apiUrl.substring(0, apiUrl.length()-1); // normalize
this.apiUrl = apiUrl;
if (null != connector) this.connector = connector;
@@ -140,7 +136,9 @@ public class GitHub {
if (oauthAccessToken!=null) {
encodedAuthorization = "token "+oauthAccessToken;
} else {
if (password!=null) {
if(jwtToken!=null){
encodedAuthorization = "Bearer "+jwtToken;
}else if (password!=null) {
String authorization = (login + ':' + password);
String charsetName = Charsets.UTF_8.name();
encodedAuthorization = "Basic "+new String(Base64.encodeBase64(authorization.getBytes(charsetName)), charsetName);
@@ -154,7 +152,7 @@ public class GitHub {
this.rateLimitHandler = rateLimitHandler;
this.abuseLimitHandler = abuseLimitHandler;
if (login==null && encodedAuthorization!=null)
if (login==null && encodedAuthorization!=null && jwtToken == null)
login = getMyself().getLogin();
this.login = login;
}
@@ -172,6 +170,7 @@ public class GitHub {
* @deprecated
* Use {@link #connectToEnterpriseWithOAuth(String, String, String)}
*/
@Deprecated
public static GitHub connectToEnterprise(String apiUrl, String oauthAccessToken) throws IOException {
return connectToEnterpriseWithOAuth(apiUrl,null,oauthAccessToken);
}
@@ -180,8 +179,8 @@ public class GitHub {
* Version that connects to GitHub Enterprise.
*
* @param apiUrl
* The URL of GitHub (or GitHub enterprise) API endpoint, such as "https://api.github.com" or
* "http://ghe.acme.com/api/v3". Note that GitHub Enterprise has <tt>/api/v3</tt> in the URL.
* 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 <code>/api/v3</code> in the URL.
* For historical reasons, this parameter still accepts the bare domain name, but that's considered deprecated.
*/
public static GitHub connectToEnterpriseWithOAuth(String apiUrl, String login, String oauthAccessToken) throws IOException {
@@ -194,6 +193,7 @@ public class GitHub {
* @deprecated
* Use with caution. Login with password is not a preferred method.
*/
@Deprecated
public static GitHub connectToEnterprise(String apiUrl, String login, String password) throws IOException {
return new GitHubBuilder().withEndpoint(apiUrl).withPassword(login, password).build();
}
@@ -207,6 +207,7 @@ public class GitHub {
* Either OAuth token or password is sufficient, so there's no point in passing both.
* Use {@link #connectUsingPassword(String, String)} or {@link #connectUsingOAuth(String)}.
*/
@Deprecated
public static GitHub connect(String login, String oauthAccessToken, String password) throws IOException {
return new GitHubBuilder().withOAuthToken(oauthAccessToken, login).withPassword(login, password).build();
}
@@ -225,7 +226,7 @@ public class GitHub {
/**
* Connects to GitHub anonymously.
*
* All operations that requires authentication will fail.
* All operations that require authentication will fail.
*/
public static GitHub connectAnonymously() throws IOException {
return new GitHubBuilder().build();
@@ -234,7 +235,7 @@ public class GitHub {
/**
* Connects to GitHub Enterprise anonymously.
*
* All operations that requires authentication will fail.
* All operations that require authentication will fail.
*/
public static GitHub connectToEnterpriseAnonymously(String apiUrl) throws IOException {
return new GitHubBuilder().withEndpoint(apiUrl).build();
@@ -380,7 +381,7 @@ public class GitHub {
requireCredential();
synchronized (this) {
if (this.myself != null) return myself;
GHMyself u = retrieve().to("/user", GHMyself.class);
u.root = this;
@@ -402,9 +403,9 @@ public class GitHub {
return u;
}
/**
* clears all cached data in order for external changes (modifications and del
* clears all cached data in order for external changes (modifications and del) to be reflected
*/
public void refreshCache() {
users.clear();
@@ -424,6 +425,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) {
@@ -433,6 +437,27 @@ 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 retrieve()
.with("since",since)
.asPagedIterable(
"/organizations",
GHOrganization[].class,
item -> item.wrapUp(GitHub.this) );
}
/**
* Gets the repository object from 'user/reponame' string that GitHub calls as "repository name"
*
@@ -442,65 +467,64 @@ public class GitHub {
String[] tokens = name.split("/");
return retrieve().to("/repos/" + tokens[0] + '/' + tokens[1], GHRepository.class).wrap(this);
}
/**
* Gets the repository object from its ID
*/
public GHRepository getRepositoryById(String id) throws IOException {
return retrieve().to("/repositories/" + id, GHRepository.class).wrap(this);
}
/**
* Returns a list of popular open source licenses
*
* WARNING: This uses a PREVIEW API.
*
* @see <a href="https://developer.github.com/v3/licenses/">GitHub API - Licenses</a>
*
* @return a list of popular open source licenses
*/
@Preview @Deprecated
public PagedIterable<GHLicense> listLicenses() throws IOException {
return new PagedIterable<GHLicense>() {
public PagedIterator<GHLicense> _iterator(int pageSize) {
return new PagedIterator<GHLicense>(retrieve().withPreview(DRAX).asIterator("/licenses", GHLicense[].class, pageSize)) {
@Override
protected void wrapUp(GHLicense[] page) {
for (GHLicense c : page)
c.wrap(GitHub.this);
}
};
}
};
return retrieve()
.asPagedIterable(
"/licenses",
GHLicense[].class,
item -> item.wrap(GitHub.this) );
}
/**
* Returns a list of all users.
*/
public PagedIterable<GHUser> listUsers() throws IOException {
return new PagedIterable<GHUser>() {
public PagedIterator<GHUser> _iterator(int pageSize) {
return new PagedIterator<GHUser>(retrieve().asIterator("/users", GHUser[].class, pageSize)) {
@Override
protected void wrapUp(GHUser[] page) {
for (GHUser u : page)
u.wrapUp(GitHub.this);
}
};
}
};
return retrieve()
.asPagedIterable(
"/users",
GHUser[].class,
item -> item.wrapUp(GitHub.this) );
}
/**
* Returns the full details for a license
*
* WARNING: This uses a PREVIEW API.
*
* @param key The license key provided from the API
* @return The license details
* @see GHLicense#getKey()
*/
@Preview @Deprecated
public GHLicense getLicense(String key) throws IOException {
return retrieve().withPreview(DRAX).to("/licenses/" + key, GHLicense.class);
return retrieve().to("/licenses/" + key, GHLicense.class);
}
/**
* 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.
* This method returns shallowly populated organizations.
*
* To retrieve full organization details, you need to call {@link #getOrganization(String)}
* TODO: make this automatic.
@@ -514,6 +538,32 @@ public class GitHub {
}
return r;
}
/**
* Alias for {@link #getUserPublicOrganizations(String)}.
*/
public Map<String, GHOrganization> getUserPublicOrganizations(GHUser user) throws IOException {
return getUserPublicOrganizations( user.getLogin() );
}
/**
* This method returns a shallowly populated organizations.
*
* To retrieve full organization details, you need to call {@link #getOrganization(String)}
*
* @param login the user to retrieve public Organization membership information for
*
* @return the public Organization memberships for the user
*/
public Map<String, GHOrganization> getUserPublicOrganizations(String login) throws IOException {
GHOrganization[] orgs = retrieve().to("/users/" + login + "/orgs", GHOrganization[].class);
Map<String, GHOrganization> r = new HashMap<String, GHOrganization>();
for (GHOrganization o : orgs) {
// don't put 'o' into orgs because they are shallow
r.put(o.getLogin(),o.wrapUp(this));
}
return r;
}
/**
* Gets complete map of organizations/teams that current user belongs to.
@@ -537,6 +587,13 @@ public class GitHub {
return allMyTeams;
}
/**
* Gets a sigle team by ID.
*/
public GHTeam getTeam(int id) throws IOException {
return retrieve().to("/teams/" + id, GHTeam.class).wrapUp(this);
}
/**
* Public events visible to you. Equivalent of what's displayed on https://github.com/
*/
@@ -548,7 +605,7 @@ public class GitHub {
}
/**
* Gets a sigle gist by ID.
* Gets a single gist by ID.
*/
public GHGist getGist(String id) throws IOException {
return retrieve().to("/gists/"+id,GHGist.class).wrapUp(this);
@@ -579,6 +636,7 @@ public class GitHub {
* @deprecated
* Use {@link #createRepository(String)} that uses a builder pattern to let you control every aspect.
*/
@Deprecated
public GHRepository createRepository(String name, String description, String homepage, boolean isPublic) throws IOException {
return createRepository(name).description(description).homepage(homepage).private_(!isPublic).create();
}
@@ -588,7 +646,7 @@ public class GitHub {
*
* <p>
* You use the returned builder to set various properties, then call {@link GHCreateRepositoryBuilder#create()}
* to finally createa repository.
* to finally create a repository.
*
* <p>
* To create a repository in an organization, see
@@ -650,10 +708,34 @@ public class GitHub {
return retrieve().method("POST").to("/applications/" + clientId + "/tokens/" + accessToken, GHAuthorization.class);
}
/**
* Returns a list of all authorizations.
* @see <a href="https://developer.github.com/v3/oauth_authorizations/#list-your-authorizations">List your authorizations</a>
*/
public PagedIterable<GHAuthorization> listMyAuthorizations() throws IOException {
return retrieve()
.asPagedIterable(
"/authorizations",
GHAuthorization[].class,
item -> item.wrap(GitHub.this) );
}
/**
* Returns the GitHub App associated with the authentication credentials used.
*
* You must use a JWT to access this endpoint.
*
* @see <a href="https://developer.github.com/v3/apps/#get-the-authenticated-github-app">Get the authenticated GitHub App</a>
*/
@Preview @Deprecated
public GHApp getApp() throws IOException {
return retrieve().withPreview(MACHINE_MAN).to("/app", GHApp.class).wrapUp(this);
}
/**
* Ensures that the credential is valid.
*/
public boolean isCredentialValid() throws IOException {
public boolean isCredentialValid() {
try {
retrieve().to("/user", GHUser.class);
return true;
@@ -676,6 +758,18 @@ public class GitHub {
return user;
}
public GHProject getProject(long id) throws IOException {
return retrieve().withPreview(INERTIA).to("/projects/"+id, GHProject.class).wrap(this);
}
public GHProjectColumn getProjectColumn(long id) throws IOException {
return retrieve().withPreview(INERTIA).to("/projects/columns/"+id, GHProjectColumn.class).wrap(this);
}
public GHProjectCard getProjectCard(long id) throws IOException {
return retrieve().withPreview(INERTIA).to("/projects/columns/cards/"+id, GHProjectCard.class).wrap(this);
}
private static class GHApiInfo {
private String rate_limit_url;
@@ -813,17 +907,11 @@ public class GitHub {
* @see <a href="https://developer.github.com/v3/repos/#list-all-public-repositories">documentation</a>
*/
public PagedIterable<GHRepository> listAllPublicRepositories(final String since) {
return new PagedIterable<GHRepository>() {
public PagedIterator<GHRepository> _iterator(int pageSize) {
return new PagedIterator<GHRepository>(retrieve().with("since",since).asIterator("/repositories", GHRepository[].class, pageSize)) {
@Override
protected void wrapUp(GHRepository[] page) {
for (GHRepository c : page)
c.wrap(GitHub.this);
}
};
}
};
return retrieve().with("since",since)
.asPagedIterable(
"/repositories",
GHRepository[].class,
item -> item.wrap(GitHub.this) );
}
/**
@@ -868,16 +956,23 @@ public class GitHub {
}
/*package*/ static String printDate(Date dt) {
return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").format(dt);
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
df.setTimeZone(TimeZone.getTimeZone("GMT"));
return df.format(dt);
}
/*package*/ static final ObjectMapper MAPPER = new ObjectMapper();
private static final String[] TIME_FORMATS = {"yyyy/MM/dd HH:mm:ss ZZZZ","yyyy-MM-dd'T'HH:mm:ss'Z'"};
private static final String[] TIME_FORMATS = {
"yyyy/MM/dd HH:mm:ss ZZZZ",
"yyyy-MM-dd'T'HH:mm:ss'Z'",
"yyyy-MM-dd'T'HH:mm:ss.S'Z'" // GitHub App endpoints return a different date format
};
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

@@ -19,14 +19,15 @@ import java.util.Properties;
*
* @since 1.59
*/
public class GitHubBuilder {
public class GitHubBuilder implements Cloneable {
// default scoped so unit tests can read them.
/* private */ String endpoint = GitHub.GITHUB_URL;
/* private */ String user;
/* private */ String password;
/* private */ String oauthToken;
/* private */ String jwtToken;
private HttpConnector connector;
private RateLimitHandler rateLimitHandler = RateLimitHandler.WAIT;
@@ -36,36 +37,36 @@ public class GitHubBuilder {
}
/**
* First check if the credentials are configured using the ~/.github properties file.
*
* If no user is specified it means there is no configuration present so check the environment instead.
* First check if the credentials are configured in the environment.
* We use environment first because users are not likely to give required (full) permissions to their default key.
*
* If no user is specified it means there is no configuration present, so try using the ~/.github properties file.
**
* If there is still no user it means there are no credentials defined and throw an IOException.
*
* @return the configured Builder from credentials defined on the system or in the environment.
* @return the configured Builder from credentials defined on the system or in the environment. Otherwise returns null.
*
* @throws IOException If there are no credentials defined in the ~/.github properties file or the process environment.
*/
public static GitHubBuilder fromCredentials() throws IOException {
static GitHubBuilder fromCredentials() throws IOException {
Exception cause = null;
GitHubBuilder builder;
GitHubBuilder builder = null;
builder = fromEnvironment();
if (builder.oauthToken != null || builder.user != null || builder.jwtToken != null)
return builder;
try {
builder = fromPropertyFile();
if (builder.oauthToken != null || builder.user != null)
if (builder.oauthToken != null || builder.user != null || builder.jwtToken != null)
return builder;
} catch (FileNotFoundException e) {
// fall through
cause = e;
}
builder = fromEnvironment();
if (builder.oauthToken != null || builder.user != null)
return builder;
else
throw (IOException)new IOException("Failed to resolve credentials from ~/.github or the environment.").initCause(cause);
throw (IOException)new IOException("Failed to resolve credentials from ~/.github or the environment.").initCause(cause);
}
/**
@@ -108,6 +109,7 @@ public class GitHubBuilder {
* <li>GITHUB_PASSWORD: raw password
* <li>GITHUB_OAUTH: OAuth token to login
* <li>GITHUB_ENDPOINT: URL of the API endpoint
* <li>GITHUB_JWT: JWT token to login
* </ul>
*
* <p>
@@ -126,7 +128,7 @@ public class GitHubBuilder {
}
return fromProperties(props);
}
public static GitHubBuilder fromPropertyFile() throws IOException {
File homeDir = new File(System.getProperty("user.home"));
File propertyFile = new File(homeDir, ".github");
@@ -149,6 +151,7 @@ public class GitHubBuilder {
public static GitHubBuilder fromProperties(Properties props) {
GitHubBuilder self = new GitHubBuilder();
self.withOAuthToken(props.getProperty("oauth"), props.getProperty("login"));
self.withJwtToken(props.getProperty("jwt"));
self.withPassword(props.getProperty("login"), props.getProperty("password"));
self.withEndpoint(props.getProperty("endpoint", GitHub.GITHUB_URL));
return self;
@@ -157,7 +160,7 @@ public class GitHubBuilder {
/**
* @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.
* "http://ghe.acme.com/api/v3". Note that GitHub Enterprise has <code>/api/v3</code> in the URL.
* For historical reasons, this parameter still accepts the bare domain name, but that's considered deprecated.
*/
public GitHubBuilder withEndpoint(String endpoint) {
@@ -177,6 +180,22 @@ public class GitHubBuilder {
this.user = user;
return this;
}
/**
* Configures {@link GitHubBuilder} with Installation Token generated by the GitHub Application
*
* @param appInstallationToken A string containing the GitHub App installation token
* @return the configured Builder from given GitHub App installation token.
* @see GHAppInstallation#createToken(java.util.Map)
*/
public GitHubBuilder withAppInstallationToken(String appInstallationToken){
return withOAuthToken(appInstallationToken, "");
}
public GitHubBuilder withJwtToken(String jwtToken){
this.jwtToken = jwtToken;
return this;
}
public GitHubBuilder withConnector(HttpConnector connector) {
this.connector = connector;
return this;
@@ -204,6 +223,15 @@ public class GitHubBuilder {
}
public GitHub build() throws IOException {
return new GitHub(endpoint, user, oauthToken, password, connector, rateLimitHandler, abuseLimitHandler);
return new GitHub(endpoint, user, oauthToken, jwtToken, password, connector, rateLimitHandler, abuseLimitHandler);
}
@Override
public GitHubBuilder clone() {
try {
return (GitHubBuilder) super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException("Clone should be supported", e);
}
}
}

View File

@@ -1,12 +1,81 @@
package org.kohsuke.github;
/**
* Provides the media type strings for GitHub API previews
*
* https://developer.github.com/v3/previews/
*
* @author Kohsuke Kawaguchi
*/
/*package*/ class Previews {
static final String LOKI = "application/vnd.github.loki-preview+json";
static final String DRAX = "application/vnd.github.drax-preview+json";
/**
* Commit Search
*
* @see <a href="https://developer.github.com/v3/previews/#commit-search">GitHub API Previews</a>
*/
static final String CLOAK = "application/vnd.github.cloak-preview+json";
/**
* Owners of GitHub Apps can now uninstall an app using the Apps API
*
* @see <a href="https://developer.github.com/v3/previews/#uninstall-a-github-app">GitHub API Previews</a>
*/
static final String GAMBIT = "application/vnd.github.gambit-preview+json";
/**
* Manage projects
*
* @see <a href="https://developer.github.com/v3/previews/#projects">GitHub API Previews</a>
*/
static final String INERTIA = "application/vnd.github.inertia-preview+json";
/**
* Require multiple approving reviews
*
* @see <a href="https://developer.github.com/v3/previews/#require-multiple-approving-reviews">GitHub API Previews</a>
*/
static final String LUKE_CAGE = "application/vnd.github.luke-cage-preview+json";
/**
* Manage integrations through the API
*
* @see <a href="https://developer.github.com/v3/previews/#integrations">GitHub API Previews</a>
*/
static final String MACHINE_MAN = "application/vnd.github.machine-man-preview+json";
/**
* View a list of repository topics in calls that return repository results
*
* @see <a href="https://developer.github.com/v3/previews/#repository-topics">GitHub API Previews</a>
*/
static final String MERCY = "application/vnd.github.mercy-preview+json";
/**
* Draft pull requests
*
* @see <a href="https://developer.github.com/v3/previews/#draft-pull-requests">GitHub API Previews</a>
*/
static final String SHADOW_CAT = "application/vnd.github.shadow-cat-preview+json";
/**
* Reactions
*
* @see <a href="https://developer.github.com/v3/previews/#reactions">GitHub API Previews</a>
*/
static final String SQUIRREL_GIRL = "application/vnd.github.squirrel-girl-preview";
static final String CLOAK = "application/vnd.github.cloak-preview";
static final String BLACK_CAT = "application/vnd.github.black-cat-preview+json";
/**
* Label emoji, search, and descriptions
*
* @see <a href="https://developer.github.com/v3/previews/#label-emoji-search-and-descriptions">GitHub API Previews</a>
*/
static final String SYMMETRA = "application/vnd.github.symmetra-preview+json";
/**
* Require signed commits
*
* @see <a href="https://developer.github.com/v3/previews/#require-signed-commits">GitHub API Previews</a>
*/
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

@@ -0,0 +1,30 @@
package org.kohsuke.github;
import org.kohsuke.github.extras.ImpatientHttpConnector;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
/**
*
*
*
* @author Liam Newman
*/
public interface Refreshable {
/**
* Opens a connection to the given URL.
*/
void refresh() throws IOException;
/**
* Calls refresh if the provided value is null
*/
default void refresh(Object value) throws IOException {
if (value == null) {
this.refresh();
}
}
}

View File

@@ -26,7 +26,7 @@ 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.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import javax.annotation.CheckForNull;
import javax.annotation.WillClose;
@@ -54,16 +54,18 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.logging.Level;
import java.util.function.Consumer;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.GZIPInputStream;
import static java.util.Arrays.*;
import static java.util.logging.Level.*;
import static org.apache.commons.lang.StringUtils.*;
import static org.kohsuke.github.GitHub.*;
import static java.util.Arrays.asList;
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;
/**
* A builder pattern for making HTTP call and parsing its output.
@@ -134,6 +136,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);
@@ -149,25 +155,37 @@ class Requester {
public Requester with(String key, Enum e) {
if (e==null) return _with(key, null);
// by convention Java constant names are upper cases, but github uses
// lower-case constants. GitHub also uses '-', which in Java we always
// replace by '_'
return with(key, e.toString().toLowerCase(Locale.ENGLISH).replace('_', '-'));
return with(key, transformEnum(e));
}
public Requester with(String key, String value) {
return _with(key, value);
}
public Requester with(String key, Collection<String> value) {
public Requester with(String key, Collection<?> value) {
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);
}
public Requester withPermissions(String key, Map<String, GHPermissionType> value) {
Map<String,String> retMap = new HashMap<String, String>();
for (Map.Entry<String, GHPermissionType> entry : value.entrySet()) {
retMap.put(entry.getKey(), transformEnum(entry.getValue()));
}
return _with(key, retMap);
}
public Requester with(@WillClose/*later*/ InputStream body) {
this.body = body;
return this;
@@ -422,6 +440,40 @@ class Requester {
return forceBody || !METHODS_WITHOUT_BODY.contains(method);
}
/*package*/ <T> PagedIterable<T> asPagedIterable(String tailApiUrl, Class<T[]> type, Consumer<T> consumer) {
return new PagedIterableWithConsumer(type, this, tailApiUrl, consumer);
}
private static class PagedIterableWithConsumer<S> extends PagedIterable<S> {
private final Class<S[]> clazz;
private final Requester requester;
private final String tailApiUrl;
private final Consumer<S> consumer;
public PagedIterableWithConsumer(Class<S[]> clazz, Requester requester, String tailApiUrl, Consumer<S> consumer) {
this.clazz = clazz;
this.tailApiUrl = tailApiUrl;
this.requester = requester;
this.consumer = consumer;
}
@Override
public PagedIterator<S> _iterator(int pageSize) {
final Iterator<S[]> iterator = requester.asIterator(tailApiUrl, clazz, pageSize);
return new PagedIterator<S>(iterator) {
@Override
protected void wrapUp(S[] page) {
if (consumer != null) {
for (S item : page) {
consumer.accept(item);
}
}
}
};
}
}
/**
* Loads paginated resources.
*
@@ -452,7 +504,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);
}
}
@@ -513,7 +565,7 @@ class Requester {
}
}
} catch (IOException e) {
throw new Error(e);
throw new GHException("Failed to retrieve " + url, e);
}
}
@@ -541,6 +593,9 @@ class Requester {
private void setupConnection(URL url) throws IOException {
if (LOGGER.isLoggable(FINE)) {
LOGGER.log(FINE, "GitHub API request [" + (root.login == null ? "anonymous" : root.login) + "]: " + method + " " + url.toString());
}
uc = root.getConnector().connect(url);
// if the authentication is needed but no credential is given, try it anyway (so that some calls
@@ -609,6 +664,14 @@ class Requester {
return type.cast(Array.newInstance(type.getComponentType(),0));
}
// Response code 202 means the statistics are still being cached.
// See https://developer.github.com/v3/repos/statistics/#a-word-about-caching
if (responseCode == 202) {
LOGGER.log(INFO, "The statistics are still being generated. Please try again in 5 seconds.");
// Maybe throw an exception instead?
return null;
}
r = new InputStreamReader(wrapStream(uc.getInputStream()), "UTF-8");
String data = IOUtils.toString(r);
if (type!=null)
@@ -713,6 +776,18 @@ class Requester {
throw e;
}
/**
* Transform Java Enum into Github constants given its conventions
* @param en - Enum to be transformed
* @return a String containing the value of a Github constant
*/
private String transformEnum(Enum en){
// by convention Java constant names are upper cases, but github uses
// lower-case constants. GitHub also uses '-', which in Java we always
// replace by '_'
return en.toString().toLowerCase(Locale.ENGLISH).replace('_', '-');
}
private static final List<String> METHODS_WITHOUT_BODY = asList("GET", "DELETE");
private static final Logger LOGGER = Logger.getLogger(Requester.class.getName());
}

View File

@@ -16,12 +16,18 @@ import java.net.URL;
* response does not count against the rate limit.
* See http://developer.github.com/v3/#conditional-requests
*
* @see org.kohsuke.github.extras.okhttp3.OkHttpConnector
* @author Roberto Tyley
* @author Kohsuke Kawaguchi
*/
@Deprecated
public class OkHttp3Connector implements HttpConnector {
private final OkUrlFactory urlFactory;
/*
* @see org.kohsuke.github.extras.okhttp3.OkHttpConnector
*/
@Deprecated
public OkHttp3Connector(OkUrlFactory urlFactory) {
this.urlFactory = urlFactory;
}

View File

@@ -1,13 +1,27 @@
package org.kohsuke.github.extras;
import com.squareup.okhttp.CacheControl;
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 java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
/**
* {@link HttpConnector} for {@link OkHttpClient}.
*
@@ -20,13 +34,69 @@ import java.net.URL;
* @author Kohsuke Kawaguchi
*/
public class OkHttpConnector implements HttpConnector {
private static final String HEADER_NAME = "Cache-Control";
private final OkUrlFactory urlFactory;
private final String maxAgeHeaderValue;
public OkHttpConnector(OkUrlFactory urlFactory) {
this.urlFactory = urlFactory;
this(urlFactory, 0);
}
/**
* package private for tests to be able to change max-age for cache.
* @param urlFactory
* @param cacheMaxAge
*/
OkHttpConnector(OkUrlFactory urlFactory, int cacheMaxAge) {
urlFactory.client().setSslSocketFactory(TlsSocketFactory());
urlFactory.client().setConnectionSpecs(TlsConnectionSpecs());
this.urlFactory = urlFactory;
if (cacheMaxAge >= 0 && urlFactory.client() != null && urlFactory.client().getCache() != null) {
maxAgeHeaderValue = new CacheControl.Builder()
.maxAge(cacheMaxAge, TimeUnit.SECONDS)
.build()
.toString();
} else {
maxAgeHeaderValue = null;
}
}
public HttpURLConnection connect(URL url) throws IOException {
return urlFactory.open(url);
HttpURLConnection urlConnection = urlFactory.open(url);
if (maxAgeHeaderValue != null) {
// By default OkHttp honors max-age, meaning it will use local cache
// without checking the network within that time frame.
// However, that can result in stale data being returned during that time so
// we force network-based checking no matter how often the query is made.
// OkHttp still automatically does ETag checking and returns cached data when
// GitHub reports 304, but those do not count against rate limit.
urlConnection.setRequestProperty(HEADER_NAME, maxAgeHeaderValue);
}
return urlConnection;
}
/** 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);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,77 @@
package org.kohsuke.github.extras.okhttp3;
import com.squareup.okhttp.CacheControl;
import okhttp3.ConnectionSpec;
import okhttp3.OkHttpClient;
import org.kohsuke.github.HttpConnector;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* {@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 Liam Newman
* @author Kohsuke Kawaguchi
*/
public class OkHttpConnector implements HttpConnector {
private static final String HEADER_NAME = "Cache-Control";
private final String maxAgeHeaderValue;
private final OkHttpClient client;
private final ObsoleteUrlFactory urlFactory;
public OkHttpConnector(OkHttpClient client) {
this(client, 0);
}
public OkHttpConnector(OkHttpClient client, int cacheMaxAge) {
OkHttpClient.Builder builder = client.newBuilder();
builder.connectionSpecs(TlsConnectionSpecs());
this.client = builder.build();
if (cacheMaxAge >= 0 && this.client != null && this.client.cache() != null) {
maxAgeHeaderValue = new CacheControl.Builder()
.maxAge(cacheMaxAge, TimeUnit.SECONDS)
.build()
.toString();
} else {
maxAgeHeaderValue = null;
}
this.urlFactory = new ObsoleteUrlFactory(this.client);
}
public HttpURLConnection connect(URL url) throws IOException {
HttpURLConnection urlConnection = urlFactory.open(url);
if (maxAgeHeaderValue != null) {
// By default OkHttp honors max-age, meaning it will use local cache
// without checking the network within that timeframe.
// However, that can result in stale data being returned during that time so
// we force network-based checking no matter how often the query is made.
// OkHttp still automatically does ETag checking and returns cached data when
// GitHub reports 304, but those do not count against rate limit.
urlConnection.setRequestProperty(HEADER_NAME, maxAgeHeaderValue);
}
return urlConnection;
}
/** Returns connection spec with TLS v1.2 in it */
private List<ConnectionSpec> TlsConnectionSpecs() {
return Arrays.asList(ConnectionSpec.MODERN_TLS, ConnectionSpec.CLEARTEXT);
}
}

View File

@@ -0,0 +1,28 @@
Authenticating as an installation
In order to authenticate to GitHub as an installation of your GitHub App, you must use the App Installation Token
authentication mechanism. This can be achieved with by creating a <<<GitHub>>> instance like this:
+-----+
GitHub githubAuthAsInst = new GitHubBuilder()
.withAppInstallationToken(appInstallationToken.getToken())
.build();
+-----+
How do I create an App Installation Token?
Assuming that you followed the {{{/githubappjwtauth.html} GitHub App Authentication via JWT token guide}} then you
can create the App Installation Token like this:
+-----+
String jwtToken = createJWT("44435", 600000); //sdk-github-api-app-test
GitHub gitHubApp = new GitHubBuilder().withJwtToken(jwtToken).build();
GHAppInstallation appInstallation = gitHubApp.getApp().getInstallationById(111111); // Installation Id
Map<String, GHPermissionType> permissions = new HashMap<>();
permissions.put("pull_requests", GHPermissionType.WRITE);
GHAppInstallationToken appInstallationToken = appInstallation
.createToken(permissions)
.create();
+-----+

View File

@@ -0,0 +1,41 @@
GitHub App Auth Flow
GitHub Apps are commonly mistaken for OAuth Apps due to their similarities but understanding the differences between
them will help you decide which kind of app you want to create.
In a nutshell, an OAuth App acts as a GitHub user, whereas a GitHub App uses its own identity when installed on an
organization or on repositories within an organization. For a comprehensive comparision please refer to the official
GitHub {{{https://developer.github.com/apps/differences-between-apps/}documentation}}.
For this guide, we are going assume that you are using a GitHub App.
Overview
Assuming that your GitHub app has already been installed on either a user or an organization, the programmatic flow
the developer must follow in order to be able to authenticate on their behalf is:
[images/GitHub_App_Auth_Flow.jpg] GitHub_App_Auth_Flow
Multiple <<<GitHub>>> instances will have to be created and each of them will be using a different authentication
mechanism. Some actions are only accessible if you are authenticated as a GitHub App while others will only be
possible if you are authenticated on behalf of a user or org.
Prerequisites
In order to follow this guide, you must have:
* A GitHub App created as described {{{https://developer.github.com/apps/building-github-apps/creating-a-github-app/}here}}
* A Private Key must be configured in your GitHub App as described {{{https://developer.github.com/apps/building-github-apps/authenticating-with-github-apps/#generating-a-private-key}here}}
* A User or an organisation that has already installed your GitHub App as described {{{https://developer.github.com/apps/installing-github-apps/}here}}
[]
What next?
* Authenticating as a GitHub App via the {{{/githubappjwtauth.html}JWT Authentication}}
* Authenticating as an installation via the {{{/githubappappinsttokenauth.html}App Installation Token}}
[]

View File

@@ -0,0 +1,135 @@
GitHub App Authentication via JWT token
In order to authenticate to GitHub as a GitHub App, you must use the JWT token authentication mechanism. This can be
easily achieved with this library by obtaining a <<<GitHub>>> instance like this:
+-----+
GitHub github = new GitHubBuilder().withJwtToken("my_jwt_token").build();
+-----+
Authenticating as a GitHub App lets you do a couple of things:
* You can retrieve high-level management information about your GitHub App.
* You can request access tokens for an installation of the app.
[]
Where do I get the JWT token from?
To generate the JWT token required to authenticate as a GitHub app you have to:
* Sign the JWT token using the private key you configured on your GitHub app as described {{{https://developer.github.com/apps/building-github-apps/authenticating-with-github-apps/#generating-a-private-key}here}}
* Encode it using the <<<RS256>>> algorithm.
[]
GitHub checks that the request is authenticated by verifying the token with the app's stored public key.
Converting the private key into a Java friendly format
<<Note:>> GitHub let's you download the GitHub App private key in the <<<PEM>>> format which isn't natively supported
by the JVM unless you leverage a third-party library such as {{{https://www.bouncycastle.org/}BouncyCastle}}. In this
guide we will convert it to <<<DER>>> using the <<<openssl>>> utility.
+-----+
openssl pkcs8 -topk8 -inform PEM -outform DER -in ~/github-api-app.private-key.pem -out ~/github-api-app.private-key.der -nocrypt
+-----+
How can I generate the JWT token?
Once you have the private key converted to the <<<DER>>> format, you will need 2 more things before you are able to
generate JWT tokens:
<<GitHub App Id:>>
You can obtain the GitHub App Id from your app settings webpage as shown below:
[images/Github_App_Id.png] Github_App_Id
<<JWT library:>>
In order to generate the JWT, you will have to likely use a JWT library.
In this guide we will use {{{https://github.com/jwtk/jjwt}jjwt}} to that matter.
Having said that, add on your <<<pom.xml>>> the following dependencies:
+-----+
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.10.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.10.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.10.5</version>
<scope>runtime</scope>
</dependency>
+-----+
Now we have everything we need so let's generate the JWT token:
+-----+
static PrivateKey get(String filename) throws Exception {
byte[] keyBytes = Files.toByteArray(new File(filename));
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(spec);
}
static String createJWT(String githubAppId, long ttlMillis) throws Exception {
//The JWT signature algorithm we will be using to sign the token
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.RS256;
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
//We will sign our JWT with our private key
Key signingKey = get("github-api-app.private-key.der");
//Let's set the JWT Claims
JwtBuilder builder = Jwts.builder()
.setIssuedAt(now)
.setIssuer(githubAppId)
.signWith(signingKey, signatureAlgorithm);
//if it has been specified, let's add the expiration
if (ttlMillis > 0) {
long expMillis = nowMillis + ttlMillis;
Date exp = new Date(expMillis);
builder.setExpiration(exp);
}
//Builds the JWT and serializes it to a compact, URL-safe string
return builder.compact();
}
public static void main(String[] args) throws Exception {
String jwtToken = createJWT("44435", 600000); //sdk-github-api-app-test
GitHub gitHubApp = new GitHubBuilder().withJwtToken(jwtToken).build();
}
+-----+
How do I get a specific app installation?
+-----+
String jwtToken = createJWT("44435", 600000); //sdk-github-api-app-test
GitHub gitHubApp = new GitHubBuilder().withJwtToken(jwtToken).build();
GHAppInstallation appInstallation = gitHubApp.getApp().getInstallationById(111111); // Installation Id
+-----+
What next?
* Authenticating as an installation via the {{{/githubappappinsttokenauth.html}App Installation Token}}
[]

154
src/site/apt/index.apt Normal file
View File

@@ -0,0 +1,154 @@
What is this?
This library defines an object oriented representation of the GitHub API. By "object oriented" we mean
there are classes that correspond to the domain model of GitHub (such as <<<GHUser>>> and <<<GHRepository>>>),
operations that act on them as defined as methods (such as <<<GHUser.follow()>>>), and those object references
are used in favor of using string handle (such as <<<GHUser.isMemberOf(GHOrganization)>>> instead of
<<<GHUser.isMemberOf(String)>>>)
The library supports both github.com and GitHub Enterprise.
Most of the GitHub APIs are covered, although there are some corners that are still not yet implemented.
Sample Usage
+-----+
GitHub github = GitHub.connect();
GHRepository repo = github.createRepository(
"new-repository","this is my new repository",
"http://www.kohsuke.org/",true/*public*/);
repo.addCollaborators(github.getUser("abayer"),github.getUser("rtyler"));
repo.delete();
+-----+
Authentication
The library allows connecting to GitHub via several different authentication mechanisms.
* Programmatically
To connect via Username and Password (not recommended):
+-----+
GitHub github = new GitHubBuilder().withPassword("my_user", "my_passwd").build();
+-----+
To connect via Personal access token:
+-----+
// If you don't specify the GitHub user id then the sdk will retrieve it via /user endpoint
GitHub github = new GitHubBuilder().withOAuthToken("my_personal_token").build();
// If the token has access to an organization, you can specify it here.
GitHub github = new GitHubBuilder().withOAuthToken("my_personal_token","user_id_OR_org_name").build();
+-----+
To connect via JWT token as a GitHub App:
+-----+
GitHub github = new GitHubBuilder().withJwtToken("my_jwt_token").build();
+-----+
To connect via GitHub App installation token on behalf of a user or organization:
+-----+
GitHub github = new GitHubBuilder().withAppInstallationToken("my_installation_token").build();
+-----+
* Property file
This library defines a common convention so that applications using this library will look at a consistent location.
In this convention, the library looks at <<<~/.github>>> property file. The content of the files depends on the way
you want this library to authenticate as shown below:
To connect via Username and Password (not recommended):
+-----+
login=kohsuke
password=012345678
+-----+
To connect via Personal access token:
+-----+
oauth=4d98173f7c075527cb64878561d1fe70
+-----+
To connect via Personal access token as a user or organization:
+-----+
login=my_org
oauth=4d98173f7c075527cb64878561d1fe70
+-----+
To connect via JWT token as a GitHub App:
+-----+
jwt=my_jwt_token
+-----+
Once your <<<~/.github>>> property file is properly configured, you can obtain a <<<GitHub>>> instance using:
+-----+
// if you are using the default configuration file
GitHub github = GitHubBuilder.fromPropertyFile().build();
// if you need to use a separate configuration file
GitHub github = GitHubBuilder.fromPropertyFile("location/my_custom_github.properties").build();
+-----+
* Environmental variables
This library also allows developers to authenticate GitHub with environmental variables.
To connect via Username and Password (not recommended):
+-----+
export GITHUB_LOGIN=kohsuke
export GITHUB_PASSWORD=012345678
+-----+
To connect via Personal access token:
+-----+
export GITHUB_OAUTH=4d98173f7c075527cb64878561d1fe70
+-----+
To connect via Personal access token as a user or organization:
+-----+
export GITHUB_LOGIN=my_org
export GITHUB_OAUTH=4d98173f7c075527cb64878561d1fe70
+-----+
To connect via JWT token as a GitHub App:
+-----+
export GITHUB_JWT=my_jwt_token
+-----+
Once exported, you can obtain a <<<GitHub>>> instance using:
+-----+
GitHub github = GitHubBuilder.fromEnvironment().build();
+-----+
Pluggable HTTP client
This library comes with a pluggable connector to use different HTTP client implementations
through <<<HttpConnector>>>. In particular, this means you can use {{{http://square.github.io/okhttp/}OkHttp}},
so we can make use of it's HTTP response cache.
Making a conditional request against the GitHub API and receiving a 304 response
{{{http://developer.github.com/v3/#conditional-requests}does not count against the rate limit}}.
The following code shows an example of how to set up persistent cache on the disk:
+-----+
Cache cache = new Cache(cacheDirectory, 10 * 1024 * 1024); // 10MB cache
GitHub gitHub = GitHubBuilder.fromEnvironment()
.withConnector(new OkHttpConnector(new OkUrlFactory(new OkHttpClient().setCache(cache))))
.build();
+-----+

View File

@@ -1,51 +0,0 @@
What is this?
=====
This library defines an object oriented representation of the GitHub API. By "object oriented" we mean
there are classes that correspond to the domain model of GitHub (such as `GHUser` and `GHRepository`),
operations that act on them as defined as methods (such as `GHUser.follow()`), and those object references
are used in favor of using string handle (such as `GHUser.isMemberOf(GHOrganization)` instead of
`GHUser.isMemberOf(String)`)
The library supports both github.com and GitHub Enterprise.
Most of the GitHub APIs are covered, although there are some corners that are still not yet implemented.
Sample Usage
-----
GitHub github = GitHub.connect();
GHRepository repo = github.createRepository(
"new-repository","this is my new repository",
"http://www.kohsuke.org/",true/*public*/);
repo.addCollaborators(github.getUser("abayer"),github.getUser("rtyler"));
repo.delete();
Credential
----
This library allows the caller to supply the credential as parameters, but it also defines a common convention
so that applications using this library will look at the consistent location. In this convention, the library
looks at `~/.github` property file, which should have the following two values:
login=kohsuke
password=012345678
Alternatively, you can have just the OAuth token in this file:
oauth=4d98173f7c075527cb64878561d1fe70
OkHttp
----
This library comes with a pluggable connector to use different HTTP client implementations
through `HttpConnector`. In particular, this means you can use [OkHttp](http://square.github.io/okhttp/),
so we can make use of it's HTTP response cache.
Making a conditional request against the GitHub API and receiving a 304 response
[does not count against the rate limit](http://developer.github.com/v3/#conditional-requests).
The following code shows an example of how to set up persistent cache on the disk:
Cache cache = new Cache(cacheDirectory, 10 * 1024 * 1024); // 10MB cache
GitHub gitHub = GitHubBuilder.fromCredentials()
.withConnector(new OkHttpConnector(new OkUrlFactory(new OkHttpClient().setCache(cache))))
.build();

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

View File

@@ -9,15 +9,22 @@
<artifactId>maven-skin</artifactId>
<version>1.2</version>
</skin>
<body>
<menu name="Git Hub API for Java">
<item name="Introduction" href="/index.html"/>
<item name="Download" href="http://mvnrepository.com/artifact/${project.groupId}/${project.artifactId}"/>
<item name="Source code" href="https://github.com/kohsuke/${project.artifactId}"/>
<item name="Introduction" href="/index.html"/>
<item name="Download" href="http://mvnrepository.com/artifact/${project.groupId}/${project.artifactId}"/>
<item name="Source code" href="https://github.com/github-api/${project.artifactId}"/>
<item name="Mailing List" href="https://groups.google.com/forum/#!forum/github-api"/>
</menu>
<menu name="Guides">
<item name="GitHub App Auth Flow" href="/githubappflow.html">
<item name="JWT Authentication" href="/githubappjwtauth.html"/>
<item name="App Installation Token " href="/githubappappinsttokenauth.html"/>
</item>
</menu>
<menu name="References">
<item name="Javadoc" href="apidocs/index.html"/>
</menu>

View File

@@ -1,22 +0,0 @@
import org.kohsuke.github.GHRepository.Contributor;
import org.kohsuke.github.GHUser;
import org.kohsuke.github.GitHub;
/**
* @author Kohsuke Kawaguchi
*/
public class Foo {
public static void main(String[] args) throws Exception {
GitHub gh = GitHub.connect();
for (Contributor c : gh.getRepository("kohsuke/yo").listContributors()) {
System.out.println(c);
}
}
private static void testRateLimit() throws Exception {
GitHub g = GitHub.connectAnonymously();
for (GHUser u : g.getOrganization("jenkinsci").listMembers()) {
u.getFollowersCount();
}
}
}

View File

@@ -25,8 +25,8 @@ public class HookApp {
public void doIndex(StaplerRequest req) throws IOException {
String str = req.getParameter("payload");
System.out.println(str);
GHEventPayload.PullRequest o = GitHub.connect().parseEventPayload(new StringReader(str),GHEventPayload.PullRequest.class);
System.out.println(o);
// System.out.println(str);
GHEventPayload.PullRequest o = GitHub.connect().parseEventPayload(new StringReader(str), GHEventPayload.PullRequest.class);
// System.out.println(o);
}
}

View File

@@ -2,6 +2,7 @@ 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;
@@ -11,44 +12,21 @@ import org.kohsuke.randname.RandomNameGenerator;
import java.io.File;
import java.io.IOException;
import static org.junit.Assume.assumeTrue;
/**
* @author Kohsuke Kawaguchi
*/
public abstract class AbstractGitHubApiTestBase extends Assert {
protected GitHub gitHub;
public abstract class AbstractGitHubApiTestBase extends AbstractGitHubWireMockTest {
@Before
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.fromProperties(props).withRateLimitHandler(RateLimitHandler.FAIL).build();
} else {
gitHub = GitHubBuilder.fromCredentials().withRateLimitHandler(RateLimitHandler.FAIL).build();
}
}
protected GHUser getUser() {
try {
return gitHub.getMyself();
} catch (IOException e) {
throw new RuntimeException(e.getMessage(), e);
}
assumeTrue("All tests inheriting from this class are not guaranteed to work without proxy", mockGitHub.isUseProxy());
}
protected void kohsuke() {
String login = getUser().getLogin();
Assume.assumeTrue(login.equals("kohsuke") || login.equals("kohsuke2"));
assumeTrue(login.equals("kohsuke") || login.equals("kohsuke2"));
}
protected static final RandomNameGenerator rnd = new RandomNameGenerator();

View File

@@ -0,0 +1,222 @@
package org.kohsuke.github;
import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
import org.apache.commons.io.IOUtils;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.kohsuke.github.junit.GitHubWireMockRule;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.*;
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
/**
* @author Liam Newman
*/
public abstract class AbstractGitHubWireMockTest extends Assert {
private final GitHubBuilder githubBuilder = createGitHubBuilder();
final static String GITHUB_API_TEST_ORG = "github-api-test-org";
final static String STUBBED_USER_LOGIN = "placeholder-user";
final static String STUBBED_USER_PASSWORD = "placeholder-password";
protected boolean useDefaultGitHub = true;
protected final Set<String> tempGitHubRepositories = new HashSet<>();
/**
* {@link GitHub} instance for use during test.
* Traffic will be part of snapshot when taken.
*/
protected GitHub gitHub;
/**
* {@link GitHub} instance for use before/after test.
* Traffic will not be part of snapshot when taken.
* Should only be used when isUseProxy() or isTakeSnapShot().
*/
protected GitHub gitHubBeforeAfter;
protected final String baseFilesClassPath = this.getClass().getName().replace('.', '/');
protected final String baseRecordPath = "src/test/resources/" + baseFilesClassPath + "/wiremock";
@Rule
public final GitHubWireMockRule mockGitHub;
public AbstractGitHubWireMockTest() {
mockGitHub = new GitHubWireMockRule(
this.getWireMockOptions()
);
}
protected WireMockConfiguration getWireMockOptions() {
return WireMockConfiguration.options()
.dynamicPort()
.usingFilesUnderDirectory(baseRecordPath);
}
private static GitHubBuilder createGitHubBuilder() {
GitHubBuilder builder = new GitHubBuilder();
try {
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.
builder = GitHubBuilder.fromProperties(props);
} else {
builder = GitHubBuilder.fromEnvironment();
builder = GitHubBuilder.fromCredentials();
}
} catch (IOException e) {
}
return builder.withRateLimitHandler(RateLimitHandler.FAIL);
}
protected GitHubBuilder getGitHubBuilder() {
GitHubBuilder builder = githubBuilder.clone();
if (!mockGitHub.isUseProxy()) {
// This sets the user and password to a placeholder for wiremock testing
// This makes the tests believe they are running with permissions
// The recorded stubs will behave like they running with permissions
builder.oauthToken = null;
builder.withPassword(STUBBED_USER_LOGIN, STUBBED_USER_PASSWORD);
}
return builder;
}
@Before
public void wireMockSetup() throws Exception {
GitHubBuilder builder = getGitHubBuilder()
.withEndpoint(mockGitHub.apiServer().baseUrl());
if (useDefaultGitHub) {
gitHub = builder
.build();
}
if (mockGitHub.isUseProxy()) {
gitHubBeforeAfter = getGitHubBuilder()
.withEndpoint("https://api.github.com/")
.build();
} else {
gitHubBeforeAfter = null;
}
}
protected void snapshotNotAllowed() {
assumeFalse("Test contains hand written mappings. Only valid when not taking a snapshot.", mockGitHub.isTakeSnapshot());
}
protected void requireProxy(String reason) {
assumeTrue("Test only valid when proxying (-Dtest.github.useProxy to enable): " + reason, mockGitHub.isUseProxy());
}
protected GHUser getUser() {
return getUser(gitHub);
}
protected static GHUser getUser(GitHub gitHub) {
try {
return gitHub.getMyself();
} catch (IOException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
/**
*
*/
protected GHRepository getTempRepository() throws IOException{
return getTempRepository("temp-" + this.mockGitHub.getMethodName());
}
/**
* Creates
*/
protected GHRepository getTempRepository(String name) throws IOException {
String fullName = GITHUB_API_TEST_ORG +'/' + name;
if (mockGitHub.isUseProxy()) {
cleanupRepository(fullName);
GHRepository repository = gitHubBeforeAfter.getOrganization(GITHUB_API_TEST_ORG)
.createRepository(name)
.description("A test repository for testing the github-api project: " + name)
.homepage("http://github-api.kohsuke.org/")
.autoInit(true)
.create();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e.getMessage(), e);
}
configureTempRepository(repository);
}
return gitHub.getRepository(fullName);
}
protected void configureTempRepository(GHRepository repository) throws IOException {
repository.enableIssueTracker(true);
repository.enableDownloads(true);
repository.enableWiki(true);
}
@Before
@After
public void cleanupTempRepositories() throws IOException {
if (mockGitHub.isUseProxy()) {
for(String fullName : tempGitHubRepositories) {
cleanupRepository(fullName);
}
}
}
protected void cleanupRepository(String fullName) throws IOException {
if (mockGitHub.isUseProxy()) {
tempGitHubRepositories.add(fullName);
try {
GHRepository repository = gitHubBeforeAfter.getRepository(fullName);
if (repository != null) {
repository.delete();
}
} catch (GHFileNotFoundException e) {
// Repo already deleted
}
}
}
protected void kohsuke() {
// No-op for now
// Generally this means the test is doing something that requires additional access rights
// Not always clear which ones.
// TODO: Add helpers that assert the expected rights using gitHubBeforeAfter and only when proxy is enabled
// String login = getUserTest().getLogin();
// assumeTrue(login.equals("kohsuke") || login.equals("kohsuke2"));
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,43 @@
package org.kohsuke.github;
import org.junit.Assert;
import org.junit.Test;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.equalTo;
/**
* @author Kohsuke Kawaguchi
*/
public class BridgeMethodTest extends Assert {
@Test
public void lastStatus() throws IOException {
GHObject obj = new GHIssue();
List<Method> createdAtMethods = new ArrayList<>();
for (Method method : obj.getClass().getMethods()) {
if (method.getName().equalsIgnoreCase("getCreatedAt")) {
if(method.getReturnType() == Date.class) {
createdAtMethods.add(0, method);
} else {
createdAtMethods.add(method);
}
}
}
assertThat(createdAtMethods.size(), equalTo(2));
assertThat(createdAtMethods.get(0).getParameterCount(), equalTo(0));
assertThat(createdAtMethods.get(1).getParameterCount(), equalTo(0));
assertThat(createdAtMethods.get(0).getReturnType(), is(Date.class));
assertThat(createdAtMethods.get(1).getReturnType(), is(String.class));
}
}

View File

@@ -8,11 +8,11 @@ import java.io.IOException;
/**
* @author Kohsuke Kawaguchi
*/
public class CommitTest extends AbstractGitHubApiTestBase {
public class CommitTest extends AbstractGitHubWireMockTest {
@Test // issue 152
public void lastStatus() throws IOException {
GHTag t = gitHub.getRepository("stapler/stapler").listTags().iterator().next();
t.getCommit().getLastStatus();
assertNotNull(t.getCommit().getLastStatus());
}
@Test // issue 230
@@ -20,7 +20,7 @@ public class CommitTest extends AbstractGitHubApiTestBase {
GHRepository repo = gitHub.getRepository("stapler/stapler");
PagedIterable<GHCommit> commits = repo.queryCommits().path("pom.xml").list();
for (GHCommit commit : Iterables.limit(commits, 10)) {
GHCommit expected = repo.getCommit( commit.getSHA1() );
GHCommit expected = repo.getCommit(commit.getSHA1());
assertEquals(expected.getFiles().size(), commit.getFiles().size());
}
}

View File

@@ -0,0 +1,148 @@
package org.kohsuke.github;
import org.junit.Test;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.hamcrest.Matchers.*;
/**
* Tests for the GitHub App API methods
*
* @author Paulo Miguel Almeida
*/
public class GHAppTest extends AbstractGitHubWireMockTest {
protected GitHubBuilder getGitHubBuilder() {
return super.getGitHubBuilder()
// ensure that only JWT will be used against the tests below
.withPassword(null, null)
.withJwtToken("bogus");
}
@Test
public void getGitHubApp() throws IOException {
GHApp app = gitHub.getApp();
assertThat(app.id, is((long) 11111));
assertThat(app.getOwner().id, is((long) 111111111));
assertThat(app.getOwner().login, is("bogus"));
assertThat(app.getName(), is("Bogus-Development"));
assertThat(app.getDescription(), is(""));
assertThat(app.getExternalUrl(), is("https://bogus.domain.com"));
assertThat(app.getHtmlUrl().toString(), is("https://github.com/apps/bogus-development"));
assertThat(app.getCreatedAt(), is(GitHub.parseDate("2019-06-10T04:21:41Z")));
assertThat(app.getUpdatedAt(), is(GitHub.parseDate("2019-06-10T04:21:41Z")));
assertThat(app.getPermissions().size(), is(4));
assertThat(app.getEvents().size(), is(2));
assertThat(app.getInstallationsCount(), is((long) 1));
}
@Test
public void listInstallations() throws IOException {
GHApp app = gitHub.getApp();
List<GHAppInstallation> installations = app.listInstallations().asList();
assertThat(installations.size(), is(1));
GHAppInstallation appInstallation = installations.get(0);
testAppInstallation(appInstallation);
}
@Test
public void getInstallationById() throws IOException {
GHApp app = gitHub.getApp();
GHAppInstallation installation = app.getInstallationById(1111111);
testAppInstallation(installation);
}
@Test
public void getInstallationByOrganization() throws IOException {
GHApp app = gitHub.getApp();
GHAppInstallation installation = app.getInstallationByOrganization("bogus");
testAppInstallation(installation);
}
@Test
public void getInstallationByRepository() throws IOException {
GHApp app = gitHub.getApp();
GHAppInstallation installation = app.getInstallationByRepository("bogus", "bogus");
testAppInstallation(installation);
}
@Test
public void getInstallationByUser() throws IOException {
GHApp app = gitHub.getApp();
GHAppInstallation installation = app.getInstallationByUser("bogus");
testAppInstallation(installation);
}
@Test
public void deleteInstallation() throws IOException {
GHApp app = gitHub.getApp();
GHAppInstallation installation = app.getInstallationByUser("bogus");
try {
installation.deleteInstallation();
} catch (IOException e) {
fail("deleteInstallation wasn't suppose to fail in this test");
}
}
@Test
public void createToken() throws IOException {
GHApp app = gitHub.getApp();
GHAppInstallation installation = app.getInstallationByUser("bogus");
Map<String, GHPermissionType> permissions = new HashMap<String, GHPermissionType>();
permissions.put("checks", GHPermissionType.WRITE);
permissions.put("pull_requests", GHPermissionType.WRITE);
permissions.put("contents", GHPermissionType.READ);
permissions.put("metadata", GHPermissionType.READ);
GHAppInstallationToken installationToken = installation.createToken(permissions)
.repositoryIds(Arrays.asList((long)111111111))
.create();
assertThat(installationToken.getToken(), is("bogus"));
assertThat(installation.getPermissions(), is(permissions));
assertThat(installationToken.getRepositorySelection(),is(GHRepositorySelection.SELECTED));
assertThat(installationToken.getExpiresAt(), is(GitHub.parseDate("2019-08-10T05:54:58Z")));
GHRepository repository = installationToken.getRepositories().get(0);
assertThat(installationToken.getRepositories().size(), is(1));
assertThat(repository.getId(), is((long) 111111111));
assertThat(repository.getName(), is("bogus"));
}
private void testAppInstallation(GHAppInstallation appInstallation) throws IOException {
Map<String, GHPermissionType> appPermissions = appInstallation.getPermissions();
GHUser appAccount = appInstallation.getAccount();
assertThat(appInstallation.id, is((long) 11111111));
assertThat(appAccount.id, is((long) 111111111));
assertThat(appAccount.login, is("bogus"));
assertThat(appInstallation.getRepositorySelection(), is(GHRepositorySelection.SELECTED));
assertThat(appInstallation.getAccessTokenUrl(), endsWith("/app/installations/11111111/access_tokens"));
assertThat(appInstallation.getRepositoriesUrl(), endsWith("/installation/repositories"));
assertThat(appInstallation.getAppId(), is((long) 11111));
assertThat(appInstallation.getTargetId(), is((long) 111111111));
assertThat(appInstallation.getTargetType(), is(GHTargetType.ORGANIZATION));
Map<String, GHPermissionType> permissionsMap = new HashMap<String, GHPermissionType>();
permissionsMap.put("checks", GHPermissionType.WRITE);
permissionsMap.put("pull_requests", GHPermissionType.WRITE);
permissionsMap.put("contents", GHPermissionType.READ);
permissionsMap.put("metadata", GHPermissionType.READ);
assertThat(appPermissions, is(permissionsMap));
List<GHEvent> events = Arrays.asList(GHEvent.PULL_REQUEST, GHEvent.PUSH);
assertThat(appInstallation.getEvents(), containsInAnyOrder(events.toArray(new GHEvent[0])));
assertThat(appInstallation.getCreatedAt(), is(GitHub.parseDate("2019-07-04T01:19:36.000Z")));
assertThat(appInstallation.getUpdatedAt(), is(GitHub.parseDate("2019-07-30T22:48:09.000Z")));
assertNull(appInstallation.getSingleFileName());
}
}

View File

@@ -32,6 +32,12 @@ public class GHBranchProtectionTest extends AbstractGitHubApiTestBase {
branch = repo.getBranch(BRANCH);
if (branch.isProtected()) {
GHBranchProtection protection = branch.getProtection();
if (protection.getRequiredSignatures()) {
protection.disableSignedCommits();
}
assertFalse(protection.getRequiredSignatures());
branch.disableProtection();
}
@@ -43,23 +49,25 @@ public class GHBranchProtectionTest extends AbstractGitHubApiTestBase {
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()
.includeAdmins()
.enable();
.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());
@@ -74,9 +82,22 @@ public class GHBranchProtectionTest extends AbstractGitHubApiTestBase {
@Test
public void testEnableRequireReviewsOnly() throws Exception {
GHBranchProtection protection = branch.enableProtection()
.requireReviews()
.enable();
assertNotNull(protection.getRequiredReviews());
.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

@@ -1,27 +1,46 @@
package org.kohsuke.github;
import org.apache.commons.io.IOUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
/**
* Integration test for {@link GHContent}.
*/
public class GHContentIntegrationTest extends AbstractGitHubApiTestBase {
public class GHContentIntegrationTest extends AbstractGitHubWireMockTest {
private GHRepository repo;
private String createdFilename = rnd.next();
private String createdFilename = "test-file-to-create.txt";
@Before
@Override
public void setUp() throws Exception {
super.setUp();
repo = gitHub.getRepository("github-api-test-org/GHContentIntegrationTest").fork();
@After
public void cleanup() throws Exception {
if(mockGitHub.isUseProxy()) {
repo = gitHubBeforeAfter.getRepository("github-api-test-org/GHContentIntegrationTest");
try {
GHContent content = repo.getFileContent(createdFilename);
if (content != null) {
content.delete("Cleanup");
}
} catch (IOException e) {}
}
}
@Before
public void setUp() throws Exception {
repo = gitHub.getRepository("github-api-test-org/GHContentIntegrationTest");
}
@Test
public void testGetFileContent() throws Exception {
repo = gitHub.getRepository("github-api-test-org/GHContentIntegrationTest");
GHContent content = repo.getFileContent("ghcontent-ro/a-file-with-content");
assertTrue(content.isFile());
@@ -53,7 +72,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());
@@ -67,7 +87,8 @@ public class GHContentIntegrationTest extends AbstractGitHubApiTestBase {
assertNotNull(updatedContentResponse.getCommit());
assertNotNull(updatedContentResponse.getContent());
// due to what appears to be a cache propagation delay, this test is too flaky
// assertEquals("this is some new content\n", updatedContent.getContent());
assertEquals("this is some new content", new BufferedReader(new InputStreamReader(updatedContent.read())).readLine());
assertEquals("this is some new content\n", updatedContent.getContent());
GHContentUpdateResponse deleteResponse = updatedContent.delete("Enough of this foolishness!");

View File

@@ -0,0 +1,27 @@
package org.kohsuke.github;
import org.junit.Test;
import java.io.IOException;
import static org.junit.Assert.assertNotNull;
/**
* @author Martin van Zijl
*/
public class GHDeploymentTest extends AbstractGitHubWireMockTest {
@Test
public void testGetDeploymentById() throws IOException {
GHRepository repo = getRepository();
GHDeployment deployment = repo.getDeployment(178653229);
assertNotNull(deployment);
}
protected GHRepository getRepository() throws IOException {
return getRepository(gitHub);
}
private GHRepository getRepository(GitHub gitHub) throws IOException {
return gitHub.getOrganization("github-api-test-org").getRepository("github-api");
}
}

View File

@@ -15,7 +15,7 @@ public class GHEventPayloadTest {
@Test
public void commit_comment() throws Exception {
GHEventPayload.CommitComment event =
GitHub.offline().parseEventPayload(payload.asReader(), GHEventPayload.CommitComment.class);
GitHub.offline().parseEventPayload(payload.asReader(), GHEventPayload.CommitComment.class);
assertThat(event.getAction(), is("created"));
assertThat(event.getComment().getSHA1(), is("9049f1265b7d61be4a8904a9a27120d2064dab3b"));
assertThat(event.getComment().getUser().getLogin(), is("baxterthehacker"));
@@ -27,7 +27,7 @@ public class GHEventPayloadTest {
@Test
public void create() throws Exception {
GHEventPayload.Create event =
GitHub.offline().parseEventPayload(payload.asReader(), GHEventPayload.Create.class);
GitHub.offline().parseEventPayload(payload.asReader(), GHEventPayload.Create.class);
assertThat(event.getRef(), is("0.0.1"));
assertThat(event.getRefType(), is("tag"));
assertThat(event.getMasterBranch(), is("master"));
@@ -40,7 +40,7 @@ public class GHEventPayloadTest {
@Test
public void delete() throws Exception {
GHEventPayload.Delete event =
GitHub.offline().parseEventPayload(payload.asReader(), GHEventPayload.Delete.class);
GitHub.offline().parseEventPayload(payload.asReader(), GHEventPayload.Delete.class);
assertThat(event.getRef(), is("simple-tag"));
assertThat(event.getRefType(), is("tag"));
assertThat(event.getRepository().getName(), is("public-repo"));
@@ -51,7 +51,7 @@ public class GHEventPayloadTest {
@Test
public void deployment() throws Exception {
GHEventPayload.Deployment event =
GitHub.offline().parseEventPayload(payload.asReader(), GHEventPayload.Deployment.class);
GitHub.offline().parseEventPayload(payload.asReader(), GHEventPayload.Deployment.class);
assertThat(event.getDeployment().getSha(), is("9049f1265b7d61be4a8904a9a27120d2064dab3b"));
assertThat(event.getDeployment().getEnvironment(), is("production"));
assertThat(event.getDeployment().getCreator().getLogin(), is("baxterthehacker"));
@@ -63,7 +63,7 @@ public class GHEventPayloadTest {
@Test
public void deployment_status() throws Exception {
GHEventPayload.DeploymentStatus event =
GitHub.offline().parseEventPayload(payload.asReader(), GHEventPayload.DeploymentStatus.class);
GitHub.offline().parseEventPayload(payload.asReader(), GHEventPayload.DeploymentStatus.class);
assertThat(event.getDeploymentStatus().getState(), is(GHDeploymentState.SUCCESS));
assertThat(event.getDeploymentStatus().getTargetUrl(), nullValue());
assertThat(event.getDeployment().getSha(), is("9049f1265b7d61be4a8904a9a27120d2064dab3b"));
@@ -77,7 +77,7 @@ public class GHEventPayloadTest {
@Test
public void fork() throws Exception {
GHEventPayload.Fork event =
GitHub.offline().parseEventPayload(payload.asReader(), GHEventPayload.Fork.class);
GitHub.offline().parseEventPayload(payload.asReader(), GHEventPayload.Fork.class);
assertThat(event.getForkee().getName(), is("public-repo"));
assertThat(event.getForkee().getOwner().getLogin(), is("baxterandthehackers"));
assertThat(event.getRepository().getName(), is("public-repo"));
@@ -106,7 +106,7 @@ public class GHEventPayloadTest {
@Test
public void issue_comment() throws Exception {
GHEventPayload.IssueComment event =
GitHub.offline().parseEventPayload(payload.asReader(), GHEventPayload.IssueComment.class);
GitHub.offline().parseEventPayload(payload.asReader(), GHEventPayload.IssueComment.class);
assertThat(event.getAction(), is("created"));
assertThat(event.getIssue().getNumber(), is(2));
assertThat(event.getIssue().getTitle(), is("Spelling error in the README 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
@@ -148,7 +158,7 @@ public class GHEventPayloadTest {
@Payload("public")
public void public_() throws Exception {
GHEventPayload.Public event =
GitHub.offline().parseEventPayload(payload.asReader(), GHEventPayload.Public.class);
GitHub.offline().parseEventPayload(payload.asReader(), GHEventPayload.Public.class);
assertThat(event.getRepository().getName(), is("public-repo"));
assertThat(event.getRepository().getOwner().getLogin(), is("baxterthehacker"));
assertThat(event.getSender().getLogin(), is("baxterthehacker"));
@@ -157,13 +167,13 @@ public class GHEventPayloadTest {
@Test
public void pull_request() throws Exception {
GHEventPayload.PullRequest event =
GitHub.offline().parseEventPayload(payload.asReader(), GHEventPayload.PullRequest.class);
GitHub.offline().parseEventPayload(payload.asReader(), GHEventPayload.PullRequest.class);
assertThat(event.getAction(), is("opened"));
assertThat(event.getNumber(), is(1));
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."));
+ "master."));
assertThat(event.getPullRequest().getUser().getLogin(), is("baxterthehacker"));
assertThat(event.getPullRequest().getHead().getUser().getLogin(), is("baxterthehacker"));
assertThat(event.getPullRequest().getHead().getRef(), is("changes"));
@@ -187,18 +197,66 @@ 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"));
// TODO implement support classes and write test
// @Test
// public void pull_request_review_comment() throws Exception {}
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"));
}
@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 {
GHEventPayload.Push event =
GitHub.offline().parseEventPayload(payload.asReader(), GHEventPayload.Push.class);
GitHub.offline().parseEventPayload(payload.asReader(), GHEventPayload.Push.class);
assertThat(event.getRef(), is("refs/heads/changes"));
assertThat(event.getBefore(), is("9049f1265b7d61be4a8904a9a27120d2064dab3b"));
assertThat(event.getHead(), is("0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c"));
@@ -228,7 +286,7 @@ public class GHEventPayloadTest {
@Test
public void repository() throws Exception {
GHEventPayload.Repository event =
GitHub.offline().parseEventPayload(payload.asReader(), GHEventPayload.Repository.class);
GitHub.offline().parseEventPayload(payload.asReader(), GHEventPayload.Repository.class);
assertThat(event.getAction(), is("created"));
assertThat(event.getRepository().getName(), is("new-repository"));
assertThat(event.getRepository().getOwner().getLogin(), is("baxterandthehackers"));

View File

@@ -0,0 +1,73 @@
package org.kohsuke.github;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* @author Martin van Zijl
*/
public class GHGistUpdaterTest extends AbstractGitHubWireMockTest {
private GHGist gist;
@Before
public void setUp() throws IOException {
GHGistBuilder builder = new GHGistBuilder(gitHub);
gist = builder.description("Test for the API")
.file("unmodified.txt", "Should be unmodified")
//.file("delete-me.txt", "To be deleted")
.file("rename-me.py", "print 'hello'")
.file("update-me.txt", "To be updated")
.public_(true)
.create();
}
@After
public void cleanUp() throws Exception {
// Cleanup is only needed when proxying
if (!mockGitHub.isUseProxy()) {
return;
}
gist.delete();
}
@Test
public void testGitUpdater() throws Exception {
GHGistUpdater updater = gist.update();
GHGist updatedGist = updater.description("Description updated by API")
.addFile("new-file.txt", "Added by updater")
//.deleteFile("delete-me.txt")
.renameFile("rename-me.py", "renamed.py")
.updateFile("update-me.txt", "Content updated by API")
.update();
assertEquals("Description updated by API", updatedGist.getDescription());
Map<String,GHGistFile> files = updatedGist.getFiles();
// Check that the unmodified file stays intact.
assertTrue(files.containsKey("unmodified.txt"));
assertEquals("Should be unmodified", files.get("unmodified.txt").getContent());
// Check that the files are updated as expected.
//assertFalse("File was not deleted.", files.containsKey("delete-me.txt"));
assertTrue(files.containsKey("new-file.txt"));
assertEquals("Added by updater", files.get("new-file.txt").getContent());
assertFalse(files.containsKey("rename-me.py"));
assertTrue(files.containsKey("renamed.py"));
assertEquals("print 'hello'", files.get("renamed.py").getContent());
assertEquals("Content updated by API", files.get("update-me.txt").getContent());
}
}

View File

@@ -0,0 +1,59 @@
package org.kohsuke.github;
import java.io.IOException;
import java.util.List;
import org.junit.Test;
/**
* @author Martin van Zijl
*/
public class GHIssueEventTest extends AbstractGitHubApiTestBase {
@Test
public void testEventsForSingleIssue() throws Exception {
// Create the issue.
GHRepository repo = getRepository();
GHIssueBuilder builder = repo.createIssue("Test from the API");
GHIssue issue = builder.create();
// Generate some events.
issue.addLabels("test-label");
// Test that the events are present.
List<GHIssueEvent> list = issue.listEvents().asList();
assertEquals(1, list.size());
GHIssueEvent event = list.get(0);
assertEquals(issue.getNumber(), event.getIssue().getNumber());
assertEquals("labeled", event.getEvent());
// Test that we can get a single event directly.
GHIssueEvent eventFromRepo = repo.getIssueEvent(event.getId());
assertEquals(event.getId(), eventFromRepo.getId());
assertEquals(event.getCreatedAt(), eventFromRepo.getCreatedAt());
// Close the issue.
issue.close();
}
@Test
public void testRepositoryEvents() throws Exception {
GHRepository repo = getRepository();
List<GHIssueEvent> list = repo.listIssueEvents().asList();
assertTrue(list.size() > 0);
int i = 0;
for (GHIssueEvent event : list) {
assertNotNull(event.getIssue());
if (i++ > 10) break;
}
}
protected GHRepository getRepository() throws IOException {
return getRepository(gitHub);
}
private GHRepository getRepository(GitHub gitHub) throws IOException {
return gitHub.getOrganization("github-api-test-org").getRepository("github-api");
}
}

View File

@@ -25,8 +25,6 @@
package org.kohsuke.github;
import org.apache.commons.io.IOUtils;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
@@ -35,15 +33,7 @@ import java.net.URL;
/**
* @author Duncan Dickinson
*/
public class GHLicenseTest extends Assert {
private GitHub gitHub;
@Before
public void setUp() throws Exception {
gitHub = new GitHubBuilder()
.fromCredentials()
.build();
}
public class GHLicenseTest extends AbstractGitHubWireMockTest {
/**
* Basic test to ensure that the list of licenses from {@link GitHub#listLicenses()} is returned
@@ -67,7 +57,7 @@ public class GHLicenseTest extends Assert {
PagedIterable<GHLicense> licenses = gitHub.listLicenses();
for (GHLicense lic : licenses) {
if (lic.getKey().equals("mit")) {
assertTrue(lic.getUrl().equals(new URL("https://api.github.com/licenses/mit")));
assertTrue(lic.getUrl().equals(new URL(mockGitHub.apiServer().baseUrl() + "/licenses/mit")));
return;
}
}
@@ -97,12 +87,12 @@ public class GHLicenseTest extends Assert {
*/
@Test
public void checkRepositoryLicense() throws IOException {
GHRepository repo = gitHub.getRepository("kohsuke/github-api");
GHRepository repo = gitHub.getRepository("github-api/github-api");
GHLicense license = repo.getLicense();
assertNotNull("The license is populated", license);
assertTrue("The key is correct", license.getKey().equals("mit"));
assertTrue("The name is correct", license.getName().equals("MIT License"));
assertTrue("The URL is correct", license.getUrl().equals(new URL("https://api.github.com/licenses/mit")));
assertTrue("The URL is correct", license.getUrl().equals(new URL(mockGitHub.apiServer().baseUrl() + "/licenses/mit")));
}
/**
@@ -118,7 +108,7 @@ public class GHLicenseTest extends Assert {
assertNotNull("The license is populated", license);
assertTrue("The key is correct", license.getKey().equals("mit"));
assertTrue("The name is correct", license.getName().equals("MIT License"));
assertTrue("The URL is correct", license.getUrl().equals(new URL("https://api.github.com/licenses/mit")));
assertTrue("The URL is correct", license.getUrl().equals(new URL(mockGitHub.apiServer().baseUrl() + "/licenses/mit")));
}
/**
@@ -134,7 +124,7 @@ public class GHLicenseTest extends Assert {
assertNotNull("The license is populated", license);
assertTrue("The key is correct", license.getKey().equals("apache-2.0"));
assertTrue("The name is correct", license.getName().equals("Apache License 2.0"));
assertTrue("The URL is correct", license.getUrl().equals(new URL("https://api.github.com/licenses/apache-2.0")));
assertTrue("The URL is correct", license.getUrl().equals(new URL(mockGitHub.apiServer().baseUrl() + "/licenses/apache-2.0")));
}
/**
@@ -145,7 +135,7 @@ public class GHLicenseTest extends Assert {
*/
@Test
public void checkRepositoryWithoutLicense() throws IOException {
GHRepository repo = gitHub.getRepository("dedickinson/test-repo");
GHRepository repo = gitHub.getRepository(GITHUB_API_TEST_ORG + "/empty");
GHLicense license = repo.getLicense();
assertNull("There is no license", license);
}
@@ -159,12 +149,12 @@ public class GHLicenseTest extends Assert {
*/
@Test
public void checkRepositoryFullLicense() throws IOException {
GHRepository repo = gitHub.getRepository("kohsuke/github-api");
GHRepository repo = gitHub.getRepository("github-api/github-api");
GHLicense license = repo.getLicense();
assertNotNull("The license is populated", license);
assertTrue("The key is correct", license.getKey().equals("mit"));
assertTrue("The name is correct", license.getName().equals("MIT License"));
assertTrue("The URL is correct", license.getUrl().equals(new URL("https://api.github.com/licenses/mit")));
assertTrue("The URL is correct", license.getUrl().equals(new URL(mockGitHub.apiServer().baseUrl() + "/licenses/mit")));
assertTrue("The HTML URL is correct", license.getHtmlUrl().equals(new URL("http://choosealicense.com/licenses/mit/")));
}

View File

@@ -0,0 +1,64 @@
package org.kohsuke.github;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.util.Date;
/**
* @author Martin van Zijl
*/
public class GHMilestoneTest extends AbstractGitHubWireMockTest {
@Before
@After
public void cleanUp() throws Exception {
// Cleanup is only needed when proxying
if (!mockGitHub.isUseProxy()) {
return;
}
for (GHMilestone milestone : getRepository(gitHubBeforeAfter).listMilestones(GHIssueState.ALL)) {
if ("Original Title".equals(milestone.getTitle()) ||
"Updated Title".equals(milestone.getTitle())) {
milestone.delete();
}
}
}
@Test
public void testUpdateMilestone() throws Exception {
GHRepository repo = getRepository();
GHMilestone milestone = repo.createMilestone("Original Title",
"To test the update methods");
String NEW_TITLE = "Updated Title";
String NEW_DESCRIPTION = "Updated Description";
Date NEW_DUE_DATE = GitHub.parseDate("2020-10-05T13:00:00Z");
Date OUTPUT_DUE_DATE = GitHub.parseDate("2020-10-05T07:00:00Z");
milestone.setTitle(NEW_TITLE);
milestone.setDescription(NEW_DESCRIPTION);
milestone.setDueOn(NEW_DUE_DATE);
// Force reload.
milestone = repo.getMilestone(milestone.getNumber());
assertEquals(NEW_TITLE, milestone.getTitle());
assertEquals(NEW_DESCRIPTION, milestone.getDescription());
// The time is truncated when sent to the server, but still part of the returned value
// 07:00 midnight PDT
assertEquals(OUTPUT_DUE_DATE, milestone.getDueOn());
}
protected GHRepository getRepository() throws IOException {
return getRepository(gitHub);
}
private GHRepository getRepository(GitHub gitHub) throws IOException {
return gitHub.getOrganization("github-api-test-org").getRepository("github-api");
}
}

View File

@@ -1,24 +1,39 @@
package org.kohsuke.github;
import com.jcraft.jsch.IO;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
public class GHOrganizationTest extends AbstractGitHubApiTestBase {
public class GHOrganizationTest extends AbstractGitHubWireMockTest {
public static final String GITHUB_API_TEST = "github-api-test";
private GHOrganization org;
public static final String TEAM_NAME_CREATE = "create-team-test";
@Override
public void setUp() throws Exception {
super.setUp();
org = gitHub.getOrganization("github-api-test-org");
@Before
@After
public void cleanUpTeam() throws IOException {
// Cleanup is only needed when proxying
if (!mockGitHub.isUseProxy()) {
return;
}
GHTeam team = gitHubBeforeAfter.getOrganization(GITHUB_API_TEST_ORG).
getTeamByName(TEAM_NAME_CREATE);
if (team != null) {
team.delete();
}
}
@Test
public void testCreateRepository() throws IOException {
cleanupRepository(GITHUB_API_TEST_ORG + '/' + GITHUB_API_TEST);
GHOrganization org = gitHub.getOrganization(GITHUB_API_TEST_ORG);
GHRepository repository = org.createRepository(GITHUB_API_TEST,
"a test repository used to test kohsuke's github-api", "http://github-api.kohsuke.org/", "Core Developers", true);
Assert.assertNotNull(repository);
@@ -26,18 +41,49 @@ public class GHOrganizationTest extends AbstractGitHubApiTestBase {
@Test
public void testCreateRepositoryWithAutoInitialization() throws IOException {
cleanupRepository(GITHUB_API_TEST_ORG + '/' + GITHUB_API_TEST);
GHOrganization org = gitHub.getOrganization(GITHUB_API_TEST_ORG);
GHRepository repository = org.createRepository(GITHUB_API_TEST)
.description("a test repository used to test kohsuke's github-api")
.homepage("http://github-api.kohsuke.org/")
.team(org.getTeamByName("Core Developers"))
.autoInit(true).create();
.description("a test repository used to test kohsuke's github-api")
.homepage("http://github-api.kohsuke.org/")
.team(org.getTeamByName("Core Developers"))
.autoInit(true).create();
Assert.assertNotNull(repository);
Assert.assertNotNull(repository.getReadme());
}
@After
public void cleanUp() throws Exception {
GHRepository repository = org.getRepository(GITHUB_API_TEST);
repository.delete();
@Test
public void testInviteUser() throws IOException {
GHOrganization org = gitHub.getOrganization(GITHUB_API_TEST_ORG);
GHUser user = gitHub.getUser("martinvanzijl2");
// First remove the user
if (org.hasMember(user)) {
org.remove(user);
}
// Then invite the user again
org.add(user, GHOrganization.Role.MEMBER);
// Now the user has to accept the invitation
// Can this be automated?
// user.acceptInvitationTo(org); // ?
// Check the invitation has worked.
// assertTrue(org.hasMember(user));
}
@Test
public void testCreateTeamWithRepoAccess() throws IOException {
String REPO_NAME = "github-api";
GHOrganization org = gitHub.getOrganization(GITHUB_API_TEST_ORG);
GHRepository repo = org.getRepository(REPO_NAME);
// Create team with access to repository. Check access was granted.
GHTeam team = org.createTeam(TEAM_NAME_CREATE, GHOrganization.Permission.PUSH, repo);
Assert.assertTrue(team.getRepositories().containsKey(REPO_NAME));
}
}

View File

@@ -0,0 +1,107 @@
package org.kohsuke.github;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
* @author Gunnar Skjold
*/
public class GHProjectCardTest extends AbstractGitHubWireMockTest {
private GHOrganization org;
private GHProject project;
private GHProjectColumn column;
private GHProjectCard card;
@Before
public void setUp() throws Exception {
org = gitHub.getOrganization(GITHUB_API_TEST_ORG);
project = org.createProject("test-project", "This is a test project");
column = project.createColumn("column-one");
card = column.createCard("This is a card");
}
@Test
public void testCreatedCard() {
Assert.assertEquals("This is a card", card.getNote());
Assert.assertFalse(card.isArchived());
}
@Test
public void testEditCardNote() throws IOException {
card.setNote("New note");
card = gitHub.getProjectCard(card.getId());
Assert.assertEquals("New note", card.getNote());
Assert.assertFalse(card.isArchived());
}
@Test
public void testArchiveCard() throws IOException {
card.setArchived(true);
card = gitHub.getProjectCard(card.getId());
Assert.assertEquals("This is a card", card.getNote());
Assert.assertTrue(card.isArchived());
}
@Test
public void testCreateCardFromIssue() throws IOException {
GHRepository repo = org.createRepository("repo-for-project-card").create();
try {
GHIssue issue = repo.createIssue("new-issue").body("With body").create();
GHProjectCard card = column.createCard(issue);
Assert.assertEquals(issue.getUrl(), card.getContentUrl());
} finally {
repo.delete();
}
}
@Test
public void testDeleteCard() throws IOException {
card.delete();
try {
card = gitHub.getProjectCard(card.getId());
Assert.assertNull(card);
} catch (FileNotFoundException e) {
card = null;
}
}
@After
public void after() throws IOException {
if(mockGitHub.isUseProxy()) {
if (card != null) {
card = gitHubBeforeAfter.getProjectCard(card.getId());
try {
card.delete();
card = null;
} catch (FileNotFoundException e) {
card = null;
}
}
if (column != null) {
column = gitHubBeforeAfter
.getProjectColumn(column.getId());
try {
column.delete();
column = null;
} catch (FileNotFoundException e) {
column = null;
}
}
if (project != null) {
project = gitHubBeforeAfter
.getProject(project.getId());
try {
project.delete();
project = null;
} catch (FileNotFoundException e) {
project = null;
}
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More