Compare commits

...

416 Commits

Author SHA1 Message Date
Liam Newman
cb381dfa06 [maven-release-plugin] prepare release github-api-1.122 2021-01-14 20:13:57 -08:00
Liam Newman
80124e3b85 Merge pull request #1021 from bitwiseman/jwt-string
Allow JWT from string
2021-01-14 20:09:21 -08:00
Liam Newman
7aae27e36f Allow JWT from string 2021-01-14 14:25:51 -08:00
Liam Newman
b212956fbb [maven-release-plugin] prepare for next development iteration 2021-01-14 13:19:41 -08:00
Liam Newman
d033355e84 [maven-release-plugin] prepare release github-api-1.121 2021-01-14 13:19:31 -08:00
Liam Newman
59d7a117d0 [maven-release-plugin] prepare for next development iteration 2021-01-14 10:51:52 -08:00
Liam Newman
dfbb38c5f1 [maven-release-plugin] prepare release github-api-1.120 2021-01-14 10:51:41 -08:00
Liam Newman
3f9954144a Merge pull request #945 from MarcosCela/feat/credential-provider-refresh
Feat/credential provider refresh
2021-01-14 10:37:30 -08:00
Liam Newman
1b84efdbfa Add GitHub.DependentAuthorizationProvider
Rather than exposing an unsafe wrapper for GitHub instances, I added a base class
that can be extended by anyone wanting to implement an authorization provider
that needs a GitHub instance to generate it's authorization string.
2021-01-14 10:32:25 -08:00
Liam Newman
c33e78a7dc Create authorization package 2021-01-14 09:23:17 -08:00
Marcos.Cela
747c759bbb fix code violations again 2021-01-08 10:10:44 +01:00
Marcos.Cela
e0a709676e fix format violations 2021-01-08 09:56:05 +01:00
Marcos.Cela
a96275c286 tests for JWTTokenProvider, verifying the "Authentication" header
This test basically ensures that the requests made with a
JWTTokenProvider follow a valid Authentication pattern,
verifying that the header "conforms" to a valid JWT token
More information on JWT tokens can be found at:

- https://jwt.io/introduction/
2021-01-08 09:52:50 +01:00
Marcos.Cela
ca7c809feb remove unused field MINUTES_10 from JWTTokenProvider 2021-01-08 08:21:35 +01:00
Marcos Cela López
a8a0bcb7db Merge branch 'master' into feat/credential-provider-refresh 2021-01-07 12:03:27 +01:00
Marcos.Cela
0e2bf23830 add CODE_SCANNING_ALERT to GHEvent enum 2021-01-07 11:32:33 +01:00
Marcos.Cela
44a8b797fb fix: JWTTokenProvider has an incorrect value for the returned authorization header
more info:
https://docs.github.com/en/free-pro-team@latest/developers/apps/authenticating-with-github-apps#authenticating-as-a-github-app
2021-01-07 11:23:22 +01:00
Marcos.Cela
cdede298a9 rename OrgInstallationAuthorizationProvider to OrgAppInstallationAuthorizationProvider 2021-01-07 09:53:19 +01:00
Marcos.Cela
f6ac4d3559 rename: credential provider -> authorization provider
This includes renames in comments, related methods,
javadocs and fields/variables.
2021-01-07 09:46:30 +01:00
Liam Newman
7e1531dbca [maven-release-plugin] prepare for next development iteration 2021-01-05 17:27:23 -08:00
Liam Newman
9aeb422157 [maven-release-plugin] prepare release github-api-1.119 2021-01-05 17:27:08 -08:00
Liam Newman
fba0f8cf8e Merge pull request #1015 from seregamorph/feature/mock-previews
Fix mocking Previews
2021-01-05 17:23:55 -08:00
seregamorph
0f4a5227e1 internal package 2021-01-05 23:03:49 +03:00
seregamorph
d16a752b43 Fix mocking Previews 2021-01-05 18:50:24 +03:00
Liam Newman
4bec27fd49 [maven-release-plugin] prepare for next development iteration 2021-01-04 01:48:27 -08:00
Liam Newman
be3bd74bb7 [maven-release-plugin] prepare release github-api-1.118 2021-01-04 01:48:16 -08:00
Liam Newman
0b14cee817 Merge pull request #1011 from hub4j/dependabot/maven/org.kohsuke.stapler-stapler-1.262
Chore(deps-dev): Bump stapler from 1.260 to 1.262
2021-01-03 23:43:02 -08:00
dependabot[bot]
252ca04084 Chore(deps-dev): Bump stapler from 1.260 to 1.262
Bumps [stapler](https://github.com/stapler/stapler) from 1.260 to 1.262.
- [Release notes](https://github.com/stapler/stapler/releases)
- [Changelog](https://github.com/stapler/stapler/blob/master/CHANGELOG.md)
- [Commits](https://github.com/stapler/stapler/compare/stapler-parent-1.260...stapler-parent-1.262)

Signed-off-by: dependabot[bot] <support@github.com>
2021-01-01 02:00:35 +00:00
Liam Newman
6670446037 Reenable GitHubBuilder tests 2020-12-31 09:49:51 -08:00
Liam Newman
bd39b07bb5 Fix javadoc issues 2020-12-30 14:07:37 -08:00
Liam Newman
a9438b6121 Move tests to use JWTTokenProvider 2020-12-30 10:46:45 -08:00
Liam Newman
f546cf4521 Use only credential providers internally to track credentials
Removes extra fields from GitHubClient.
2020-12-30 09:52:30 -08:00
Liam Newman
43efa78750 Post-merge fixes 2020-12-29 09:29:30 -08:00
Liam Newman
9e3de43802 Merge remote-tracking branch 'upstream/master' into feat/credential-provider-refresh 2020-12-29 09:19:09 -08:00
Liam Newman
dc615e432e Merge pull request #985 from lower-case/bugfix-883
Fixes null commit date
2020-12-28 22:06:47 -08:00
Liam Newman
cf9caa6af5 Update test to check values 2020-12-28 22:00:44 -08:00
Liam Newman
15f748358d Merge remote-tracking branch 'upstream/master' into bugfix-883 2020-12-28 20:02:24 -08:00
Liam Newman
b30d648623 Merge pull request #939 from jgangemi/jae/bulk-update
- bulk update of repository options
2020-12-28 19:54:44 -08:00
Liam Newman
33d70560b8 Deprecate templateRepository() for isTemplate() 2020-12-28 18:44:30 -08:00
Liam Newman
865a49d2e8 Update GHRepository method to use Setter
It appears that the correct way to pass these booleans is as booleans not as strings.

Fixes #765
2020-12-28 18:07:32 -08:00
Liam Newman
4fca68c25c Add GHRepository.Setter 2020-12-28 17:03:36 -08:00
Liam Newman
f131a0c1c2 Formatting fixes 2020-12-28 16:26:23 -08:00
Liam Newman
cd4368fa79 Merge remote-tracking branch 'upstream/master' into jae/bulk-update 2020-12-28 16:25:31 -08:00
Liam Newman
4ec4b160b0 Update contributing.md for more clarity 2020-12-28 16:18:04 -08:00
Liam Newman
a585b4957f Merge pull request #1004 from bitwiseman/object-base
Make root field transient in all classes
2020-12-28 16:05:48 -08:00
Liam Newman
e6b02b3bed Merge branch 'master' into object-base 2020-12-28 15:48:41 -08:00
Liam Newman
1ef0ec0432 Update src/main/java/org/kohsuke/github/GitHub.java
Co-authored-by: Tim Jacomb <21194782+timja@users.noreply.github.com>
2020-12-28 15:47:28 -08:00
Liam Newman
2e87bd86a1 Merge pull request #1006 from hub4j/dependabot/maven/org.eclipse.jgit-org.eclipse.jgit-5.10.0.202012080955-r
Chore(deps-dev): Bump org.eclipse.jgit from 5.9.0.202009080501-r to 5.10.0.202012080955-r
2020-12-28 15:46:53 -08:00
Liam Newman
0228a0d023 Merge pull request #1010 from hub4j/dependabot/maven/org.slf4j-slf4j-simple-1.7.30
Chore(deps-dev): Bump slf4j-simple from 1.7.2 to 1.7.30
2020-12-28 15:46:33 -08:00
dependabot[bot]
6365f3749d Chore(deps-dev): Bump slf4j-simple from 1.7.2 to 1.7.30
Bumps [slf4j-simple](https://github.com/qos-ch/slf4j) from 1.7.2 to 1.7.30.
- [Release notes](https://github.com/qos-ch/slf4j/releases)
- [Commits](https://github.com/qos-ch/slf4j/compare/v_1.7.2...v_1.7.30)

Signed-off-by: dependabot[bot] <support@github.com>
2020-12-28 23:45:31 +00:00
dependabot[bot]
25c18130f9 Chore(deps-dev): Bump org.eclipse.jgit
Bumps org.eclipse.jgit from 5.9.0.202009080501-r to 5.10.0.202012080955-r.

Signed-off-by: dependabot[bot] <support@github.com>
2020-12-28 23:45:30 +00:00
Liam Newman
436c19634d Merge pull request #1009 from hub4j/dependabot/maven/com.github.spotbugs-spotbugs-maven-plugin-4.1.4
Chore(deps): Bump spotbugs-maven-plugin from 4.0.4 to 4.1.4
2020-12-28 15:45:13 -08:00
Liam Newman
1a6facc685 Merge pull request #1008 from hub4j/dependabot/maven/com.tngtech.archunit-archunit-0.15.0
Chore(deps-dev): Bump archunit from 0.14.1 to 0.15.0
2020-12-28 15:44:45 -08:00
Liam Newman
bd0093c8ea Change reader and writer to no longer deprecated
While still no recommended, these methods are more recommended than
users creating their own.  These will continue to work even when
internals change, whereas user configured readers or writers may not.
2020-12-28 12:39:21 -08:00
Liam Newman
e150280010 Merge remote-tracking branch 'upstream/master' into object-base 2020-12-28 10:52:16 -08:00
dependabot[bot]
827fd5e472 Chore(deps): Bump spotbugs-maven-plugin from 4.0.4 to 4.1.4
Bumps [spotbugs-maven-plugin](https://github.com/spotbugs/spotbugs-maven-plugin) from 4.0.4 to 4.1.4.
- [Release notes](https://github.com/spotbugs/spotbugs-maven-plugin/releases)
- [Commits](https://github.com/spotbugs/spotbugs-maven-plugin/compare/spotbugs-maven-plugin-4.0.4...spotbugs-maven-plugin-4.1.4)

Signed-off-by: dependabot[bot] <support@github.com>
2020-12-28 18:51:35 +00:00
dependabot[bot]
f89fbc67b9 Chore(deps-dev): Bump archunit from 0.14.1 to 0.15.0
Bumps [archunit](https://github.com/TNG/ArchUnit) from 0.14.1 to 0.15.0.
- [Release notes](https://github.com/TNG/ArchUnit/releases)
- [Commits](https://github.com/TNG/ArchUnit/compare/v0.14.1...v0.15.0)

Signed-off-by: dependabot[bot] <support@github.com>
2020-12-28 18:51:35 +00:00
Liam Newman
c567a88892 Merge pull request #1005 from bitwiseman/task/spotless
Switch to using spotless plugin for formatting
2020-12-28 10:51:06 -08:00
Liam Newman
6a39d7fca5 Trim whitespace and end with newline 2020-12-23 17:13:27 -08:00
Liam Newman
a15e67f065 Switch formatting to spotless
This is change includes minimal changes required to make the switch
2020-12-23 17:13:27 -08:00
Liam Newman
7a1bce9578 Merge branch 'master' into object-base 2020-12-22 09:17:52 -08:00
Liam Newman
f2b4de7943 Merge branch 'master' into jae/bulk-update 2020-12-22 08:32:12 -08:00
Liam Newman
b3ff4ac6d9 Merge pull request #984 from gsmet/okhttp-close-responsebody
Close Okhttp ResponseBody instances when closing the InputStream
2020-12-21 10:20:50 -08:00
Liam Newman
1c56e7fab5 Add missing argument 2020-12-21 09:28:44 -08:00
Liam Newman
70ba4df385 Make root field transient in all classes 2020-12-18 14:58:12 -08:00
Liam Newman
8062c705e8 Merge branch 'master' into jae/bulk-update 2020-12-17 15:12:19 -08:00
Liam Newman
fafb23c1a6 Merge pull request #1002 from marcoferrer/add-sdk-beta-annotation
Implement BetaApi annotation for hub4j sdk
2020-12-17 12:57:01 -08:00
Marco Ferrer
4e7ac7030c Add beta api annotation to project 2020-12-16 16:39:48 -05:00
Liam Newman
4803daca5a Merge branch 'master' into okhttp-close-responsebody 2020-12-15 16:21:08 -08:00
Liam Newman
facfc61316 Merge branch 'master' into jae/bulk-update 2020-12-15 16:08:41 -08:00
Liam Newman
e3e495bfb1 Merge pull request #1001 from marcoferrer/preview-enum-and-cleanup
Implement static typing for previews and clean up usage declarations
2020-12-15 15:39:06 -08:00
Liam Newman
e007284d2f Merge branch 'master' into preview-enum-and-cleanup 2020-12-15 15:20:30 -08:00
Liam Newman
1da8416ebd Merge pull request #983 from marcoferrer/add-preview-arch-rules
Add arch test for preview API usage
2020-12-15 15:20:05 -08:00
Marco Ferrer
79b49a469c Clean up preview declarations 2020-12-15 14:39:00 -05:00
Marco Ferrer
5888efcaef Merge branch 'master' into add-preview-arch-rules 2020-12-15 12:41:51 -05:00
Marco Ferrer
459d1b4f56 update arch tests to add enum checks 2020-12-15 12:41:02 -05:00
Liam Newman
9151102bda Merge pull request #998 from bitwiseman/obsolete-okhttp
Deprecate OkHttp 2.x connector
2020-12-12 16:49:13 -08:00
Liam Newman
3819984add Merge pull request #999 from tginiotis-at-work/targeturl-for-status
"target_url" for the payload of the status event
2020-12-12 16:48:16 -08:00
Tadas Giniotis
3b58fbc186 test "target_url" 2020-12-12 23:53:40 +02:00
Tadas Giniotis
55e589b3d9 add the "target_url" field for "status" events 2020-12-12 23:46:03 +02:00
Liam Newman
e64d64d8d8 Deprecate OkHttp 2.x connector
OkHttp 2.x is unsupported.  OkHttpUrlFactory contains bugs and limiations which will not
be fixed and also cannot be mitigated by this library.  Users should move to OkHttp3.

Closes #997
2020-12-11 17:17:08 -08:00
Jae Gangemi
37c2d9135b - bulk update of repository options 2020-12-11 16:56:26 -07:00
Liam Newman
30c96221bd Make wiremock less noisy with slf4j simple logger during tests 2020-12-11 13:54:47 -08:00
Liam Newman
bf7305e3f8 Merge pull request #990 from hub4j/dependabot/maven/org.apache.maven.plugins-maven-project-info-reports-plugin-3.1.1
Chore(deps): Bump maven-project-info-reports-plugin from 3.1.0 to 3.1.1
2020-12-09 08:54:55 -08:00
Liam Newman
3b12a229c3 Merge branch 'master' into dependabot/maven/org.apache.maven.plugins-maven-project-info-reports-plugin-3.1.1 2020-12-04 14:41:12 -08:00
Liam Newman
5726ceb8dc Merge branch 'master' into okhttp-close-responsebody 2020-12-03 16:55:48 -08:00
Liam Newman
c06c06624d Merge pull request #981 from eSentire/AffiliationFilter
Add affiliation filter for collaborators
2020-12-03 16:54:02 -08:00
Rob Rodrigues
ad40d7071e Streamline per feedback 2020-12-02 11:41:13 -08:00
Rob Rodrigues
f55a39eb90 Merge branch 'master' into AffiliationFilter 2020-12-01 20:08:54 -08:00
Rob Rodrigues
c3869bee31 Reverted changes which added filter unnecessarily, cleanup, add test cache, enable test 2020-12-01 19:50:24 -08:00
dependabot[bot]
6eac15df0f Chore(deps): Bump maven-project-info-reports-plugin from 3.1.0 to 3.1.1
Bumps [maven-project-info-reports-plugin](https://github.com/apache/maven-project-info-reports-plugin) from 3.1.0 to 3.1.1.
- [Release notes](https://github.com/apache/maven-project-info-reports-plugin/releases)
- [Commits](https://github.com/apache/maven-project-info-reports-plugin/compare/maven-project-info-reports-plugin-3.1.0...maven-project-info-reports-plugin-3.1.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-12-01 18:21:10 +00:00
Liam Newman
6f5d3c32c3 Spotbugs 4.1.3 2020-12-01 10:07:46 -08:00
Liam Newman
68ef40e4d0 Merge branch 'master' into bugfix-883 2020-12-01 09:11:23 -08:00
Liam Newman
4046bc4f72 Merge pull request #991 from hub4j/dependabot/maven/org.mockito-mockito-core-3.6.28
Chore(deps-dev): Bump mockito-core from 3.6.0 to 3.6.28
2020-12-01 08:49:54 -08:00
dependabot[bot]
1b8d131915 Chore(deps-dev): Bump mockito-core from 3.6.0 to 3.6.28
Bumps [mockito-core](https://github.com/mockito/mockito) from 3.6.0 to 3.6.28.
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v3.6.0...v3.6.28)

Signed-off-by: dependabot[bot] <support@github.com>
2020-12-01 02:00:23 +00:00
Guillaume Smet
f5ad332d28 Properly close ResponseBody when closing the InputStream
ResponseBody objects from Okhttp needs to be closed and, right now, we
just close the InputStream which is not sufficient.

I noticed that because I saw some warnings about leaked ResponseBody
instances in my logs.
2020-11-29 21:56:11 +01:00
Lovekesh Garg
938603ff60 Fixes null commit date 2020-11-28 17:20:28 +05:30
Rob Rodrigues
17af78f2bb Merge branch 'master' into AffiliationFilter 2020-11-25 13:10:09 -08:00
Marco Ferrer
7588267743 Add arch test for preview API usage 2020-11-25 13:16:04 -05:00
Liam Newman
ed4f9c8176 Merge pull request #960 from marcoferrer/update-deployments-api
Implement deployment API support for ant-man and flash previews
2020-11-25 06:37:36 -08:00
Liam Newman
bbb46e88b0 Merge branch 'master' into update-deployments-api 2020-11-25 06:18:07 -08:00
Rob Rodrigues
3db7aac0d8 Merge branch 'master' into AffiliationFilter 2020-11-24 12:52:48 -08:00
Liam Newman
fdbbd2e563 [maven-release-plugin] prepare for next development iteration 2020-11-24 11:13:29 -08:00
Liam Newman
e92f1321d4 [maven-release-plugin] prepare release github-api-1.117 2020-11-24 11:13:17 -08:00
Marco Ferrer
da2aaff9e5 Merge branch 'master' into update-deployments-api 2020-11-24 11:37:36 -05:00
Liam Newman
208904b634 Merge pull request #980 from mrginglymus/check-patcher
Add ability to update a check run
2020-11-23 18:28:48 -08:00
Liam Newman
a433bcda2e Merge pull request #979 from seregamorph/feature/pull_request-edited
pull_request action "edited": changes
2020-11-23 18:26:08 -08:00
Marco Ferrer
4bba692170 Merge branch 'master' into update-deployments-api 2020-11-23 17:20:40 -05:00
Marco Ferrer
59b61cd8be add deprecated to logUrl field 2020-11-23 17:19:57 -05:00
Marco Ferrer
247b013e16 add deprecated annotations to preview usage 2020-11-23 15:51:57 -05:00
Bill Collins
77baafa643 Add method for updating check run by ID 2020-11-20 09:16:34 +00:00
Rob Rodrigues
3c56f1f076 Merge branch 'master' into AffiliationFilter 2020-11-19 15:57:36 -08:00
Bill Collins
224d8c7cb4 Add wiremocks for tests, move existing tests to test org 2020-11-19 15:05:25 +00:00
seregamorph
0feb520549 pull_request action "edited": changes - test 2020-11-19 13:50:33 +03:00
seregamorph
ca365b12f6 Merge remote-tracking branch 'origin/master' into feature/pull_request-edited 2020-11-19 13:44:56 +03:00
Bill Collins
bde6ad9a06 Add a test for check updates 2020-11-19 09:20:58 +00:00
Bill Collins
4953f4500d Create check run updater 2020-11-19 09:20:58 +00:00
Rob Rodrigues
7fee1fcc74 Add affiliation filter for collaborators 2020-11-18 17:58:51 -08:00
Liam Newman
4415ac8fd2 Update src/test/java/org/kohsuke/github/GHEventPayloadTest.java 2020-11-18 12:50:47 -08:00
Liam Newman
8c81e48a31 Update src/test/java/org/kohsuke/github/GHEventPayloadTest.java 2020-11-18 12:48:51 -08:00
Liam Newman
9ad0329c56 Apply suggestions from code review 2020-11-18 12:47:18 -08:00
Liam Newman
78f533bbfc Merge pull request #977 from skaldarnar/feat/528-ghrelease-assets
Include assets directly in GHRelease
2020-11-18 12:46:11 -08:00
Tobias Nett
79c7dd9ecf fix formatting 2020-11-18 21:31:42 +01:00
Tobias Nett
5d796d1f79 revert unwanted assertion change 2020-11-18 21:24:58 +01:00
Tobias Nett
68a82be6c4 rename 'getCachedAssets' to 'assets' (bare property name as getter)) 2020-11-18 19:28:27 +01:00
Tobias Nett
2676ef2b73 rename 'fetchAssets' to 'listAssets' and return PagedIterable 2020-11-18 19:27:31 +01:00
seregamorph
04b283c539 pull_request action "edited": changes 2020-11-18 20:41:58 +03:00
Tobias Nett
98b067937a Make changes backwards compatible
Add '@Deprecation' and '@Preview' annotations to make the changes
backwards compatible and prepare for a transition to the cached behavior
as default.
This explicitly leaves the variant for re-fetching assets under a
different name.
2020-11-17 17:16:44 +01:00
Liam Newman
8ababb60bf Merge pull request #976 from seregamorph/feature/base-event-payload
#947 base event payload
2020-11-16 17:29:38 -08:00
seregamorph
b51d655f77 #947 address review comments 2020-11-13 11:02:54 +03:00
seregamorph
74496d32da Merge remote-tracking branch 'origin/master' into feature/base-event-payload 2020-11-13 10:27:57 +03:00
Liam Newman
316e278be1 Merge pull request #952 from tginiotis-at-work/getRepositories
List repositories for a GHAppInstallation
2020-11-12 10:02:53 -08:00
Tobias Nett
d881bf6504 Bring back previous semantic of getAssets() as fetchAssets()
I don't know a better name for this, and I also don't know whether this
should be an option or not.
The life cycle tests use the feature that retrieving the assets from a
release actually does a roundtrip to Github and sends another request.
This indicates that re-purposing `getAssets()` to be the cached access
might cause problems on consumers that potentially rely on this
assumption, too.
2020-11-11 19:49:58 +01:00
Tobias Nett
c74fbbe1fd Include assets directly in GHRelease
Resolves #528

Instead of doing a separate request to fetch the assets associated with
a release this keeps a local list of the assets that are part of the
list releases endpoint.

See https://docs.github.com/en/free-pro-team@latest/rest/reference/repos#list-releases
2020-11-11 19:16:34 +01:00
seregamorph
929d9fb7bd #947 base event payload 2020-11-09 22:48:04 +03:00
Tadas Giniotis
5d069d0531 add wiremock snapshots 2020-11-05 22:43:49 +02:00
Tadas Giniotis
dd9e6dc5d3 add tests 2020-11-05 22:39:43 +02:00
Tadas Giniotis
d22c77c41d method for listing repositories for a GHAppInstallation 2020-11-05 22:39:15 +02:00
Liam Newman
3a11b7ccbf Merge pull request #950 from tginiotis-at-work/listTeamMembers
List team members by role
2020-11-05 09:12:10 -08:00
Tadas Giniotis
9d161b28bb add wiremock snapshots 2020-11-04 15:38:00 +02:00
Tadas Giniotis
9b16a1caa0 add tests 2020-11-04 15:37:58 +02:00
Tadas Giniotis
9a918e3bac implement ability to list members on a team by role 2020-11-04 15:35:17 +02:00
Liam Newman
d4c5c6a1e0 Merge pull request #944 from seregamorph/feature/push-pulls-extensions
user, push, pull event extensions
2020-11-03 12:53:07 -08:00
Liam Newman
63fda3555c Merge pull request #953 from tginiotis-at-work/commentsOnCommit
Get comments on a specific commit
2020-11-03 12:33:00 -08:00
Liam Newman
6a2381c06b Merge pull request #951 from tginiotis-at-work/sign_commits
Allow adding signature to commits
2020-11-03 12:30:56 -08:00
Liam Newman
e9c0a16c26 Merge pull request #974 from hub4j/dependabot/maven/org.mockito-mockito-core-3.6.0
Chore(deps-dev): Bump mockito-core from 3.5.7 to 3.6.0
2020-11-03 08:43:55 -08:00
Liam Newman
2101a67ac1 Merge pull request #973 from hub4j/dependabot/maven/org.eclipse.jgit-org.eclipse.jgit-5.9.0.202009080501-r
Chore(deps-dev): Bump org.eclipse.jgit from 5.7.0.202003110725-r to 5.9.0.202009080501-r
2020-11-03 08:43:44 -08:00
dependabot[bot]
ddac568aaa Chore(deps-dev): Bump mockito-core from 3.5.7 to 3.6.0
Bumps [mockito-core](https://github.com/mockito/mockito) from 3.5.7 to 3.6.0.
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v3.5.7...v3.6.0)

Signed-off-by: dependabot[bot] <support@github.com>
2020-11-01 02:00:38 +00:00
dependabot[bot]
262ae9f635 Chore(deps-dev): Bump org.eclipse.jgit
Bumps org.eclipse.jgit from 5.7.0.202003110725-r to 5.9.0.202009080501-r.

Signed-off-by: dependabot[bot] <support@github.com>
2020-11-01 02:00:23 +00:00
dependabot[bot]
381502fb80 Merge pull request #963 from hub4j/dependabot/maven/junit-junit-4.13.1 2020-10-22 21:27:26 +00:00
dependabot[bot]
92fb441eb2 Chore(deps-dev): Bump junit from 4.13 to 4.13.1
Bumps [junit](https://github.com/junit-team/junit4) from 4.13 to 4.13.1.
- [Release notes](https://github.com/junit-team/junit4/releases)
- [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.13.1.md)
- [Commits](https://github.com/junit-team/junit4/compare/r4.13...r4.13.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-10-22 21:21:02 +00:00
dependabot[bot]
29e08037a8 Merge pull request #956 from hub4j/dependabot/maven/com.github.tomakehurst-wiremock-jre8-standalone-2.27.2 2020-10-22 21:20:13 +00:00
dependabot[bot]
84cc6d9315 Chore(deps-dev): Bump wiremock-jre8-standalone from 2.27.1 to 2.27.2
Bumps [wiremock-jre8-standalone](https://github.com/tomakehurst/wiremock) from 2.27.1 to 2.27.2.
- [Release notes](https://github.com/tomakehurst/wiremock/releases)
- [Commits](https://github.com/tomakehurst/wiremock/compare/2.27.1...2.27.2)

Signed-off-by: dependabot[bot] <support@github.com>
2020-10-22 21:14:54 +00:00
dependabot[bot]
b8d5a1c732 Merge pull request #958 from hub4j/dependabot/maven/org.jacoco-jacoco-maven-plugin-0.8.6 2020-10-22 21:14:09 +00:00
dependabot[bot]
0197ab9661 Chore(deps): Bump jacoco-maven-plugin from 0.8.5 to 0.8.6
Bumps [jacoco-maven-plugin](https://github.com/jacoco/jacoco) from 0.8.5 to 0.8.6.
- [Release notes](https://github.com/jacoco/jacoco/releases)
- [Commits](https://github.com/jacoco/jacoco/compare/v0.8.5...v0.8.6)

Signed-off-by: dependabot[bot] <support@github.com>
2020-10-22 21:08:37 +00:00
Tim Jacomb
b7915e61a6 Merge pull request #965 from chids/fix-wiremock-status-test
GH docs have moved, adjust assertion accordingly.
2020-10-22 22:03:05 +01:00
Tim Jacomb
586db99450 Merge branch 'master' into fix-wiremock-status-test 2020-10-22 21:57:46 +01:00
Tim Jacomb
5377d0dd18 Merge pull request #964 from jglick/GHPullRequest.getLabels
GHPullRequest.getLabels should not go to the GHIssue API endpoint
2020-10-22 21:35:37 +01:00
Mårten Gustafson
c5d3a7d573 GH docs have moved, adjust assertion accordingly. 2020-10-20 18:41:17 +02:00
Jesse Glick
8267050f06 GHPullRequest.getLabels should not go to the GHIssue API endpoint 2020-10-15 13:25:20 -04:00
Marcos.Cela
610b02968e exlude org.kohsuke.github.extras.auth.* from code coverage
This is a package for examples/extra implementations
2020-10-05 13:57:52 +02:00
Marcos.Cela
a7112c42df linting: JWTTokenProvider.java 2020-10-05 13:48:37 +02:00
Marcos.Cela
8a474a3b00 add: example for Org Installation token on extras package 2020-10-05 13:39:30 +02:00
Marcos.Cela
59e18d155e add dependencies for jwt token generation
These dependencies are marked as "provided" because they are only
used in the extras package
2020-10-05 13:39:01 +02:00
Marco Ferrer
12ca5d8063 update deployment wiremocks 2020-10-02 17:40:47 -04:00
Marco Ferrer
c959e0a928 update accept header in wiremocks 2020-10-02 17:37:21 -04:00
Marco Ferrer
89a08b021d formatting 2020-10-02 17:14:28 -04:00
Marco Ferrer
04b553cdec update deployment status checks 2020-10-02 17:12:10 -04:00
Marco Ferrer
15e9ee30ee formatting 2020-10-02 16:52:23 -04:00
Marco Ferrer
a0d650a86c update deployment tests to read new fields 2020-10-02 16:47:01 -04:00
Marco Ferrer
1a6ad48e08 formatting 2020-10-02 16:35:46 -04:00
Marco Ferrer
7c82eeb018 Support deployment api previews ant-man and flash 2020-10-02 15:57:52 -04:00
Marco Ferrer
b188e74ee0 Add new const for flash preview 2020-10-02 14:16:50 -04:00
Tadas Giniotis
c21bd5765a add wiremock snapshots 2020-10-01 01:25:17 +03:00
Tadas Giniotis
b78c37a695 add tests 2020-10-01 01:23:34 +03:00
Tadas Giniotis
2f151d45c3 add wiremock snapshots 2020-10-01 01:00:20 +03:00
Tadas Giniotis
3ebe3afdbd add tests 2020-10-01 00:58:54 +03:00
Tadas Giniotis
f4845df6c0 implement ability to list comments of a specific commit 2020-10-01 00:58:42 +03:00
Tadas Giniotis
272b87f04d add ability to attach a signature when creating a commit 2020-09-30 23:21:05 +03:00
Marcos.Cela
ff790eeefb formatting of OrgInstallationCredentialProvider.java 2020-09-30 16:48:54 +02:00
Marcos.Cela
97e918da03 remove unused JWTTokenProvider (we are now using a github client) 2020-09-30 16:39:41 +02:00
Marcos.Cela
4f30998873 OrgInstallationCredentialProvider now receives a pre-configured client
This is required to pass integration tests. In terms of functionality,
the user should be able to provide a client with the given token provider.
It additionally increases control (e.g: usage of proxies)

Add tests
2020-09-29 16:13:23 +02:00
Marcos.Cela
a0fc478a28 remove final modifier from credentialProvider (required for tests) 2020-09-29 16:12:06 +02:00
Marcos.Cela
bb03fd1968 use Date#after instead of compareTo 2020-09-28 16:11:16 +02:00
Marcos.Cela
0c65f74662 formatting 2020-09-28 14:45:00 +02:00
Marcos.Cela
29ac2bd4f5 return a correctly formatted token 2020-09-28 14:24:01 +02:00
Marcos.Cela
0d8b4f32e8 document oauthAccessToken 2020-09-28 13:48:56 +02:00
Marcos.Cela
83db7f24eb document CredentialProvider @throws 2020-09-28 13:48:26 +02:00
Marcos.Cela
5f9976a193 formatting for OrgInstallationCredentialProvider 2020-09-28 13:37:59 +02:00
Marcos.Cela
9480ef485b withCredentialProvider is now public 2020-09-28 13:34:14 +02:00
Marcos.Cela
a9b7432584 formatting 2020-09-28 13:28:13 +02:00
Marcos.Cela
6d7081910f add OrgInstallationCredentialProvider and JWTTokenProvider
The JWTTokenProvider implementation is left to the end user,
otherwise we would need to include specific libraries, at least
as far as I am aware.
The OrgInstallationCredentialProvider will give a token,
refreshing when necessary and using the JWTTokenProvider
that the user needs to provide to request new tokens
2020-09-28 13:17:46 +02:00
Marcos.Cela
aa96089ab4 remove @see to external docs 2020-09-28 13:01:55 +02:00
Marcos.Cela
58ae681417 reduce visibilitof GitHubBuilder#withCredentialProvider 2020-09-28 12:59:21 +02:00
Marcos.Cela
c038e0af5e typo it'ts -> it's 2020-09-28 12:51:54 +02:00
Marcos.Cela
4f9976c0cb add GitHubBuilder#withCredentialProvider
With this we also need to check for exceptions when calling
"/user", because now we don't know what kind of credentials
are coming from the provider, and we could be requesting
a "/user" when the type of credentials is not supported
2020-09-28 12:40:44 +02:00
Marcos.Cela
e308e5ed57 use static utility methods instead of building logic in the constructor 2020-09-28 12:26:56 +02:00
Marcos.Cela
7b1b1ca994 ensure that isAnonymous() correctly handles IOException 2020-09-28 12:18:22 +02:00
Marcos.Cela
551be49a1a utility methods on ImmutableCredentialProvider
These methods let us build the most-used cases
for static credentials that will never change:
- JWT credentials
- Token-based credentials
- Basic Auth credentials
2020-09-28 10:43:43 +02:00
Marcos.Cela
a3888e6902 add CredentialProvider#ANONYMOUS class and field
This is basically an implementation of a CredentialProvider
that will always authenticate anonymously
2020-09-28 10:09:54 +02:00
Marcos Cela López
43bb6a0dd8 Merge branch 'master' into feat/credential-provider-refresh 2020-09-28 10:02:16 +02:00
seregamorph
6e3f754366 javadoc 2020-09-26 12:09:15 +03:00
seregamorph
6360112432 Merge remote-tracking branch 'origin/master' into feature/push-pulls-extensions 2020-09-26 12:04:55 +03:00
Liam Newman
f1ca0b5417 Merge pull request #946 from seregamorph/feature/pull-request-review-html-url
pullRequestReview.review.htmlUrl
2020-09-25 12:54:13 -07:00
seregamorph
0894c8007c pullRequestReview.review.htmlUrl 2020-09-25 13:24:35 +03:00
Marcos Cela López
05863acbcd Merge branch 'master' into feat/credential-provider-refresh 2020-09-25 12:05:31 +02:00
Marcos.Cela
0e4cd06137 GitHubClient uses CredentialProvider, instead of encodedAuthorization (string) 2020-09-25 12:02:14 +02:00
Marcos.Cela
85d2d974e7 add ImmutableCredentialProvider
This is basically a class that will hold an authorization
string, returning the same value all the time
2020-09-25 12:01:33 +02:00
Marcos.Cela
3f021f9552 lint CredentialProvider 2020-09-25 10:56:07 +02:00
seregamorph
0456f10709 address objections 2020-09-24 22:55:04 +03:00
seregamorph
b7d03f7463 user, push, pull event extensions 2020-09-24 22:41:56 +03:00
Liam Newman
07a392c2a7 Merge pull request #942 from seregamorph/feature/javadoc-references
Fix events javadoc references
2020-09-24 08:50:53 -07:00
seregamorph
5b69de770f Fix events javadoc references 2020-09-24 14:37:49 +03:00
Marcos.Cela
4688870984 add CredentialProvider interface 2020-09-24 09:01:52 +02:00
Liam Newman
bf67069768 Merge pull request #922 from tginiotis-at-work/commit_updates
methods for listing PRs where the commit is head & listing branches which contain the commit
2020-09-22 15:48:03 -07:00
Liam Newman
91764c1c74 Merge pull request #921 from tginiotis-at-work/pr_updates
update branch & change base on PRs
2020-09-22 15:46:06 -07:00
Tadas Giniotis
8b2a3e1221 add GHCommit tests 2020-09-03 23:17:39 +03:00
Tadas Giniotis
def2f0b37d methods for listing PRs where the commit is head & listing branches which contain the commit 2020-09-03 23:16:31 +03:00
Tadas Giniotis
5d7479a3dd add GHPullRequest tests 2020-09-03 22:13:28 +03:00
Tadas Giniotis
ceb2d35f9f update branch & change base on PRs 2020-09-03 22:10:33 +03:00
Liam Newman
fc38dba59a Update release drafter to $MAJOR.$MINOR
This project uses "x.y" for most releases.  At some point we might change this, but it is convenient for now.
2020-08-27 09:32:19 -07:00
Liam Newman
75b383d398 Update release-drafter.yml 2020-08-27 09:16:33 -07:00
Liam Newman
ee2d9491fb Update CHANGELOG.md 2020-08-27 09:13:03 -07:00
Liam Newman
bf86a7c75a Update dependabot.yml 2020-08-27 09:09:22 -07:00
Liam Newman
70f6d129e2 Merge pull request #936 from hub4j/dependabot/maven/org.mockito-mockito-core-3.5.7
Chore(deps-dev): Bump mockito-core from 3.5.2 to 3.5.7
2020-08-27 09:06:55 -07:00
dependabot[bot]
a4ac2aa99a Chore(deps-dev): Bump mockito-core from 3.5.2 to 3.5.7
Bumps [mockito-core](https://github.com/mockito/mockito) from 3.5.2 to 3.5.7.
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v3.5.2...v3.5.7)

Signed-off-by: dependabot[bot] <support@github.com>
2020-08-26 02:00:58 +00:00
Liam Newman
ae3b6fbe6b Merge pull request #931 from hub4j/dependabot/maven/spotbugs.version-4.1.2
Chore(deps): Bump spotbugs.version from 4.1.1 to 4.1.2
2020-08-19 19:52:24 -07:00
Liam Newman
e357fca963 Merge pull request #930 from hub4j/dependabot/maven/org.mockito-mockito-core-3.5.2
Chore(deps-dev): Bump mockito-core from 3.5.0 to 3.5.2
2020-08-19 19:52:08 -07:00
dependabot[bot]
c84cc89805 Chore(deps): Bump spotbugs.version from 4.1.1 to 4.1.2
Bumps `spotbugs.version` from 4.1.1 to 4.1.2.

Updates `spotbugs` from 4.1.1 to 4.1.2
- [Release notes](https://github.com/spotbugs/spotbugs/releases)
- [Changelog](https://github.com/spotbugs/spotbugs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/spotbugs/spotbugs/compare/4.1.1...4.1.2)

Updates `spotbugs-annotations` from 4.1.1 to 4.1.2
- [Release notes](https://github.com/spotbugs/spotbugs/releases)
- [Changelog](https://github.com/spotbugs/spotbugs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/spotbugs/spotbugs/compare/4.1.1...4.1.2)

Signed-off-by: dependabot[bot] <support@github.com>
2020-08-20 02:01:10 +00:00
dependabot[bot]
181238cd50 Chore(deps-dev): Bump mockito-core from 3.5.0 to 3.5.2
Bumps [mockito-core](https://github.com/mockito/mockito) from 3.5.0 to 3.5.2.
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v3.5.0...v3.5.2)

Signed-off-by: dependabot[bot] <support@github.com>
2020-08-19 02:00:47 +00:00
Liam Newman
214c24c736 Merge pull request #927 from hub4j/dependabot/maven/org.mockito-mockito-core-3.5.0
Chore(deps-dev): Bump mockito-core from 3.4.6 to 3.5.0
2020-08-17 08:40:40 -07:00
dependabot[bot]
cf51ce8f26 Chore(deps-dev): Bump mockito-core from 3.4.6 to 3.5.0
Bumps [mockito-core](https://github.com/mockito/mockito) from 3.4.6 to 3.5.0.
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v3.4.6...v3.5.0)

Signed-off-by: dependabot[bot] <support@github.com>
2020-08-17 02:00:42 +00:00
Liam Newman
2b7ed40d01 [maven-release-plugin] prepare for next development iteration 2020-08-12 18:47:49 -07:00
Liam Newman
349ef7a54c [maven-release-plugin] prepare release github-api-1.116 2020-08-12 18:47:42 -07:00
Liam Newman
94df5fc389 Merge pull request #923 from bitwiseman/feature/is-template
Added GHRepository.isTemplate() method
2020-08-12 18:43:07 -07:00
Liam Newman
906238a297 Add GHRepository.isTemplate() 2020-08-12 18:28:18 -07:00
Liam Newman
7963fa82b5 Ensure withPreview can be called multiple times 2020-08-12 18:27:48 -07:00
Liam Newman
1aba6012fb Centralize GHRepository read 2020-08-12 18:21:44 -07:00
Liam Newman
ff4324ac67 Merge pull request #897 from bonnie-young/add-create-repo-with-template-support
add create repo with template support
2020-08-12 08:43:17 -07:00
Yang Ting
11bc669e1d update code for create repository from template
Signed-off-by: Yang Ting <bonnie.young@maxwit.com>
2020-08-12 22:08:31 +08:00
Liam Newman
dcf26d58e4 Merge pull request #919 from bitwiseman/task/one-more-date
Fixed and streamlined date parsing
2020-08-07 16:42:49 -07:00
Liam Newman
4d46872c35 Fixed and streamlined date parsing
Fixes #917
2020-08-07 16:36:23 -07:00
Liam Newman
4f0d62f421 Merge pull request #920 from ewiegs4/deployment-payload
Support for object deployment payloads
2020-08-07 16:35:54 -07:00
Eddie Wiegers
f7ad1f517b formatting 2020-08-07 17:46:33 -05:00
Eddie Wiegers
345d6197f3 Support for object deployment payloads. 2020-08-07 17:36:28 -05:00
Liam Newman
bb4d44138a Merge branch 'master' into add-create-repo-with-template-support 2020-08-04 16:05:02 -07:00
Liam Newman
a8ef0cde53 Merge pull request #913 from hub4j/dependabot/maven/spotbugs.version-4.1.1
Chore(deps): Bump spotbugs.version from 4.0.6 to 4.1.1
2020-08-03 10:58:51 -07:00
dependabot[bot]
77dc009c95 Chore(deps): Bump spotbugs.version from 4.0.6 to 4.1.1
Bumps `spotbugs.version` from 4.0.6 to 4.1.1.

Updates `spotbugs` from 4.0.6 to 4.1.1
- [Release notes](https://github.com/spotbugs/spotbugs/releases)
- [Changelog](https://github.com/spotbugs/spotbugs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/spotbugs/spotbugs/compare/4.0.6...4.1.1)

Updates `spotbugs-annotations` from 4.0.6 to 4.1.1
- [Release notes](https://github.com/spotbugs/spotbugs/releases)
- [Changelog](https://github.com/spotbugs/spotbugs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/spotbugs/spotbugs/compare/4.0.6...4.1.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-08-03 02:00:46 +00:00
Liam Newman
aa298c93cc Merge pull request #911 from bitwiseman/task/workflow-run
Add GHEvent.WORKFLOW_RUN
2020-08-02 08:27:02 -07:00
Liam Newman
dfb0a5240e Merge pull request #909 from JKalash/jkalash/master
GHCheckRun.getPullRequests public
2020-07-31 16:06:17 -07:00
Liam Newman
9cfc3c22b5 Add GHEvent.WORKFLOW_RUN 2020-07-31 16:03:34 -07:00
Liam Newman
b177d98e29 Populate Check pull requests
Turns out there were quite a few bugs in this area (it was exposed publicly before. Some
tweaking was needed and updates to the tests to show this working as expected.
2020-07-31 15:57:17 -07:00
Joe
5405fb0370 return unmodifiable list 2020-07-30 21:48:42 +01:00
Joe
72a1c24b3b format 2020-07-30 21:43:37 +01:00
Joe
f146ae94ec return iterable 2020-07-30 21:41:16 +01:00
Joe
a0bbba748a check suite exposes pull requests 2020-07-30 20:10:44 +01:00
Joe
81bf818573 pullRequests returns iterable 2020-07-30 20:01:44 +01:00
Joe
d5913dc292 fix array exposure 2020-07-30 15:25:35 +01:00
Joe Kalash
e1e901b794 Merge branch 'master' into jkalash/master 2020-07-30 14:01:01 +01:00
Joe
2f2f26767e make getPullRequests public 2020-07-30 13:59:36 +01:00
Liam Newman
bffa78c1b8 Merge pull request #907 from hub4j/dependabot/maven/org.mockito-mockito-core-3.4.6
Chore(deps-dev): Bump mockito-core from 3.4.4 to 3.4.6
2020-07-29 20:11:55 -07:00
dependabot[bot]
c55719c67a Chore(deps-dev): Bump mockito-core from 3.4.4 to 3.4.6
Bumps [mockito-core](https://github.com/mockito/mockito) from 3.4.4 to 3.4.6.
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v3.4.4...v3.4.6)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-30 02:00:49 +00:00
Liam Newman
cb3b4a6642 Merge pull request #906 from JKalash/jkalash/master
Merge arbitrary branches
2020-07-28 16:37:40 -07:00
Liam Newman
92c141cee6 Add support for string head
Also support all changes already merged.
2020-07-28 12:57:31 -07:00
Liam Newman
fd1a1a1c23 Simplified tests 2020-07-28 11:22:07 -07:00
Joe
b835884b2e fix test 2020-07-28 14:37:50 +01:00
Joe
660763908d test refactor 2020-07-28 14:24:35 +01:00
Joe
fe8bdb755a Merge branches method 2020-07-28 12:41:31 +01:00
Liam Newman
67dc6d2d23 Merge pull request #904 from Javaru/add-missing-org-permissions
Added MAINTAIN and TRIAGE to GHOrganization.Permission enum
2020-07-27 21:27:27 -07:00
Mark Vedder
9c8d73cbe2 Merge branch 'master' into add-missing-org-permissions 2020-07-27 18:54:32 -04:00
Liam Newman
5db97d92dd Merge pull request #899 from gastaldi/set_milestone
PullRequest.setMilestone should use the Issue API
2020-07-27 14:18:05 -07:00
Liam Newman
ac470dddb5 Merge branch 'master' into add-create-repo-with-template-support 2020-07-27 14:14:16 -07:00
Liam Newman
43063fe8ce Minor tweaks 2020-07-27 14:12:15 -07:00
George Gastaldi
59e0046c1e Validate argument in GitHub.getRepository
This avoids the ArrayIndexOutOfBoundsException when the argument format is invalid
2020-07-27 17:47:56 -03:00
George Gastaldi
36ab05c265 PullRequest.setMilestone should use the Issue API 2020-07-27 17:47:56 -03:00
Liam Newman
2b2be05dae Merge pull request #900 from gastaldi/repo_fix
Fixes modifyCollaborators for multiple users
2020-07-27 13:23:06 -07:00
Liam Newman
fb1adbd1ef Make withUrlPath() overwrite instead of append
I had some ideas about having multiple calls to  apend to build up paths,
but it turns out that idea is pretty bad.  `with*()` methods should
overwrite when called for the same field.

If we want to create and , we can do that later.
2020-07-27 13:12:46 -07:00
Javaru
ab68a59b25 Added MAINTAIN and TRIAGE to GHOrganization.Permission enum 2020-07-27 15:50:32 -04:00
George Gastaldi
9c7de767e9 Fixes modifyCollaborators for multiple users
Fixes #868
2020-07-27 12:37:34 -07:00
George Gastaldi
8ba5cf7c2e Formatting tweaks and fixes 2020-07-27 12:31:09 -07:00
Bonnie Young
b194a19b98 Merge branch 'master' into add-create-repo-with-template-support 2020-07-21 00:32:52 +08:00
Liam Newman
1d344b016f Merge pull request #898 from hub4j/dependabot/maven/org.mockito-mockito-core-3.4.4
Chore(deps-dev): Bump mockito-core from 3.4.2 to 3.4.4
2020-07-20 00:35:58 -07:00
dependabot[bot]
474f3ef4ca Chore(deps-dev): Bump mockito-core from 3.4.2 to 3.4.4
Bumps [mockito-core](https://github.com/mockito/mockito) from 3.4.2 to 3.4.4.
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v3.4.2...v3.4.4)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-20 02:00:54 +00:00
Yang Ting
9830927020 add create repo with template support
Signed-off-by: Yang Ting <bonnie.young@maxwit.com>
2020-07-19 23:07:19 +08:00
Liam Newman
727932a442 Merge pull request #895 from jeetchoudhary/protection
GHBranch getProtection method to return correct number of reviewers #890
2020-07-17 12:27:05 -07:00
jeetchoudhary
cd92b51845 Merge branch 'master' into protection 2020-07-17 23:59:51 +05:30
Liam Newman
fe26d16411 Update test and data to verify getProtection() code path
The create and get code paths are different.  Both need testing.
2020-07-17 10:50:54 -07:00
Liam Newman
d68c66ce2b Merge pull request #893 from hub4j/dependabot/maven/org.mockito-mockito-core-3.4.2
Chore(deps-dev): Bump mockito-core from 3.4.0 to 3.4.2
2020-07-17 09:59:31 -07:00
Jitender kumar
e7bfbfb48f reformat class 2020-07-17 17:28:15 +05:30
jeetchoudhary
f2a88ae61c fixing getRequiredReviewers to return count #890 2020-07-17 16:55:51 +05:30
jeetchoudhary
e2113f6ee5 Update rest request for test case 2020-07-17 16:52:17 +05:30
dependabot[bot]
3867224024 Chore(deps-dev): Bump mockito-core from 3.4.0 to 3.4.2
Bumps [mockito-core](https://github.com/mockito/mockito) from 3.4.0 to 3.4.2.
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v3.4.0...v3.4.2)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-17 02:01:30 +00:00
Liam Newman
9ee0bf43bc [maven-release-plugin] prepare for next development iteration 2020-07-16 15:16:56 -07:00
Liam Newman
2844542efa [maven-release-plugin] prepare release github-api-1.115 2020-07-16 15:16:47 -07:00
Liam Newman
e3fcae9392 Revert "Remove Java 15-ea"
This reverts commit c6ccfa91f3.
2020-07-15 15:32:46 -07:00
Liam Newman
c6ccfa91f3 Remove Java 15-ea
It is unclear exactly why this started failing but we're not interested figuring this out.
2020-07-15 15:19:06 -07:00
Liam Newman
b6fcee1cb9 Rollback to surefire 2.22.2 2020-07-15 15:14:16 -07:00
Liam Newman
9071befb04 Run slow or flaky tests in test phase 2020-07-15 15:05:26 -07:00
Liam Newman
bdd5fe98f3 Use file to specify slow-or-flaky-tests list 2020-07-15 15:00:03 -07:00
Liam Newman
a3d3e83a49 Move slow or flaky tests to separate surefire execution
This lets us have most tests run immediately and with no retries and then have slower tests that may be flaky run later with retries.
2020-07-15 14:45:32 -07:00
Liam Newman
08bde72028 Merge pull request #849 from alexanderkjall/add-support-for-child-teams
Added support for fetching what teams are part of this team.
2020-07-15 14:05:47 -07:00
Liam Newman
108a136368 Remove getChildTeams and add test for no children 2020-07-15 13:46:11 -07:00
Liam Newman
57d87ad6b1 Merge branch 'master' into add-support-for-child-teams 2020-07-15 13:28:32 -07:00
Liam Newman
0c22815ff7 Merge pull request #872 from MarcosCela/doc/org-level-resources
Add documentation for organization-level resources
2020-07-15 13:27:05 -07:00
Liam Newman
0ca792ecfd Merge pull request #830 from bitwiseman/task/rate-limit/full
Handle header and endpoint rate limit responses consistently
2020-07-15 12:42:11 -07:00
Liam Newman
987c34c69e Merge branch 'master' into task/rate-limit/full 2020-07-15 12:13:16 -07:00
Liam Newman
c1c02bc8ab Merge pull request #887 from hub4j/dependabot/maven/org.mockito-mockito-core-3.4.0
Chore(deps-dev): Bump mockito-core from 3.3.3 to 3.4.0
2020-07-14 12:02:54 -07:00
dependabot[bot]
4ee369f27c Chore(deps-dev): Bump mockito-core from 3.3.3 to 3.4.0
Bumps [mockito-core](https://github.com/mockito/mockito) from 3.3.3 to 3.4.0.
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v3.3.3...v3.4.0)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-13 15:52:33 +00:00
Liam Newman
c9012efdcb Merge pull request #886 from hub4j/dependabot/maven/net.revelc.code.formatter-formatter-maven-plugin-2.12.1
Chore(deps): Bump formatter-maven-plugin from 2.12.0 to 2.12.1
2020-07-13 08:51:51 -07:00
dependabot[bot]
41524fc67d Chore(deps): Bump formatter-maven-plugin from 2.12.0 to 2.12.1
Bumps [formatter-maven-plugin](https://github.com/revelc/formatter-maven-plugin) from 2.12.0 to 2.12.1.
- [Release notes](https://github.com/revelc/formatter-maven-plugin/releases)
- [Changelog](https://github.com/revelc/formatter-maven-plugin/blob/formatter-maven-plugin-2.12.1/CHANGELOG.md)
- [Commits](https://github.com/revelc/formatter-maven-plugin/compare/formatter-maven-plugin-2.12.0...formatter-maven-plugin-2.12.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-13 02:00:47 +00:00
Liam Newman
04ff61e981 Merge pull request #885 from XiongKezhi/add-missing-conclusion
Add the missing SKIPPED check run conclusion
2020-07-12 14:11:56 -07:00
Liam Newman
532468dc67 Update src/main/java/org/kohsuke/github/GHCheckRun.java 2020-07-12 13:41:57 -07:00
Kezhi Xiong
9c9a2dae47 Add JavaDoc for check run conclusion 2020-07-12 15:58:34 +08:00
Kezhi Xiong
c8a868b57f Add the missing SKIPPED check run conclusion 2020-07-12 15:54:09 +08:00
Liam Newman
4b3f81ee34 Clean up comments and javadoc 2020-07-10 10:51:20 -07:00
Liam Newman
afa170ba7c Add more tests for rate limit record selection 2020-07-10 09:01:29 -07:00
Liam Newman
46e3b2272e Clean up and reorganize changes
Changed GitHubRateLimitSpecifier to RateLimitTarget
Made RateLimitTarget public so it can be passed to GitHubBuilder
2020-07-10 09:01:29 -07:00
Liam Newman
52472e90ec Simplified rate limit record selection
Here we have another example of trying to do something clever when simplicity is the better choice.
Rather than trying to guess the rate limit record for a request based on the url path,
I added an enumeration which can be set on the request to say which rate limit record to applies.

This is simpler, safer, and faster than trying to guess the rate limit from the url path.
2020-07-10 09:01:29 -07:00
Liam Newman
4ef0d00846 Integrate full rate limit checking 2020-07-10 09:01:29 -07:00
Liam Newman
580f2537f2 Move GHRef readers to GHRef class 2020-07-09 15:00:40 -07:00
Liam Newman
3d9fd96026 Merge pull request #884 from bitwiseman/task/gitbucket
Workaround for GitBucket refs issue
2020-07-09 13:38:55 -07:00
Liam Newman
f449b92721 Workaround for GitBucket refs issue
The bug that caused this has been fixed by GitBucket but may not be released for some time.
It was a change to this library that broke them, so to be nice we're fully working around it here.

That said, the GET /refs endpoint is deprecated and will be going away at some point.
We will be making a breaking change in this area again in the next few months. We'll need to raise
it to GitBucket to make sure they handle it proproperly.
2020-07-09 09:50:29 -07:00
Liam Newman
3b0216b023 Merge pull request #877 from hub4j/dependabot/maven/com.github.tomakehurst-wiremock-jre8-standalone-2.27.1
Chore(deps-dev): Bump wiremock-jre8-standalone from 2.26.3 to 2.27.1
2020-07-07 12:40:23 -07:00
Liam Newman
98cf839737 Add new methods 2020-07-07 12:29:59 -07:00
dependabot[bot]
0bb0846505 Chore(deps-dev): Bump wiremock-jre8-standalone from 2.26.3 to 2.27.1
Bumps [wiremock-jre8-standalone](https://github.com/tomakehurst/wiremock) from 2.26.3 to 2.27.1.
- [Release notes](https://github.com/tomakehurst/wiremock/releases)
- [Commits](https://github.com/tomakehurst/wiremock/compare/2.26.3...2.27.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-07 19:23:07 +00:00
Liam Newman
70969400a3 Merge pull request #876 from jtnord/symlink-support
Add minimal symlink support via GHContent.getTarget()
2020-07-07 12:21:39 -07:00
Liam Newman
147e8d5d12 Merge branch 'master' into symlink-support 2020-07-07 11:38:14 -07:00
Liam Newman
cacc3e6edd Update formatter cache directory 2020-07-07 10:15:00 -07:00
Liam Newman
a284eca147 Shorten file paths for windows 2020-07-07 09:48:31 -07:00
James Nord
0d3ba9d7f0 formatting fixes 2020-07-07 13:29:33 +01:00
Liam Newman
be8064d642 Merge pull request #879 from hub4j/dependabot/maven/org.codehaus.mojo-animal-sniffer-maven-plugin-1.19
Chore(deps): Bump animal-sniffer-maven-plugin from 1.18 to 1.19
2020-07-06 10:15:33 -07:00
dependabot[bot]
e30dba742d Chore(deps): Bump animal-sniffer-maven-plugin from 1.18 to 1.19
Bumps [animal-sniffer-maven-plugin](https://github.com/mojohaus/animal-sniffer) from 1.18 to 1.19.
- [Release notes](https://github.com/mojohaus/animal-sniffer/releases)
- [Commits](https://github.com/mojohaus/animal-sniffer/compare/animal-sniffer-parent-1.18...animal-sniffer-parent-1.19)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-06 02:01:00 +00:00
James Nord
44b72ed647 Add initial symlink support.
this adds rudimentary symlink support (just enough to get a symlink).

still todo, howto handle GHRepository.getFileContent where the path
contains a symlink within the repository
2020-07-02 17:18:20 +01:00
Liam Newman
666bd77dac Merge pull request #874 from hub4j/bitwiseman-patch-1
Add "workflow_dispatch" to GHEvent
2020-07-02 08:52:31 -07:00
Liam Newman
0a6613e60d Add "workflow_dispatch" to GHEvent
This event is undocumented at this time, but this change will stop the deserialization errors.

Fixes #854
2020-07-01 15:57:33 -07:00
Liam Newman
62e186c123 Merge pull request #871 from hub4j/dependabot/maven/net.revelc.code.formatter-formatter-maven-plugin-2.12.0
Chore(deps): Bump formatter-maven-plugin from 2.11.0 to 2.12.0
2020-06-30 11:02:37 -07:00
Marcos.Cela
50dd8f5bcc add a new doc section "Working with organizations" with a simple example
Closes #812
2020-06-30 09:44:57 +02:00
Marcos Cela
d5fcac9c45 Merge pull request #1 from hub4j/master
Update base
2020-06-30 08:44:42 +02:00
dependabot[bot]
c2bed85190 Chore(deps): Bump formatter-maven-plugin from 2.11.0 to 2.12.0
Bumps [formatter-maven-plugin](https://github.com/revelc/formatter-maven-plugin) from 2.11.0 to 2.12.0.
- [Release notes](https://github.com/revelc/formatter-maven-plugin/releases)
- [Changelog](https://github.com/revelc/formatter-maven-plugin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/revelc/formatter-maven-plugin/compare/formatter-maven-plugin-2.11.0...formatter-maven-plugin-2.12.0)

Signed-off-by: dependabot[bot] <support@github.com>
2020-06-30 02:00:52 +00:00
Liam Newman
183b463ef2 Merge branch 'master' into add-support-for-child-teams 2020-06-29 09:58:14 -07:00
Liam Newman
92fdac44a0 Merge pull request #869 from hub4j/dependabot/maven/org.apache.maven.plugins-maven-site-plugin-3.9.1
Chore(deps): Bump maven-site-plugin from 3.9.0 to 3.9.1
2020-06-29 09:57:00 -07:00
dependabot[bot]
12829ecc73 Chore(deps): Bump maven-site-plugin from 3.9.0 to 3.9.1
Bumps [maven-site-plugin](https://github.com/apache/maven-site-plugin) from 3.9.0 to 3.9.1.
- [Release notes](https://github.com/apache/maven-site-plugin/releases)
- [Commits](https://github.com/apache/maven-site-plugin/compare/maven-site-plugin-3.9.0...maven-site-plugin-3.9.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-06-25 02:00:46 +00:00
Alexander Kjäll
51319c3b26 use this.organization instead of providing the organization as a parameter 2020-06-24 07:30:59 +02:00
Liam Newman
8fd827040b Merge pull request #860 from hub4j/dependabot/github_actions/actions/cache-v2
Chore(deps): Bump actions/cache from v1 to v2
2020-06-23 09:24:06 -07:00
dependabot[bot]
5ec46eae0d Chore(deps): Bump actions/cache from v1 to v2
Bumps [actions/cache](https://github.com/actions/cache) from v1 to v2.
- [Release notes](https://github.com/actions/cache/releases)
- [Commits](https://github.com/actions/cache/compare/v1...b8204782bbb5f872091ecc5eb9cb7d004e35b1fa)

Signed-off-by: dependabot[bot] <support@github.com>
2020-06-23 14:59:54 +00:00
Liam Newman
32c03301be Merge pull request #853 from sullis/add-dependabot
add Dependabot
2020-06-23 07:59:26 -07:00
Liam Newman
df7f29b2ab Merge pull request #858 from hub4j/dependabot/maven/spotbugs.version-4.0.6
Chore(deps): Bump spotbugs.version from 4.0.4 to 4.0.6
2020-06-23 07:55:48 -07:00
dependabot-preview[bot]
e863113c36 Chore(deps): Bump spotbugs.version from 4.0.4 to 4.0.6
Bumps `spotbugs.version` from 4.0.4 to 4.0.6.

Updates `spotbugs` from 4.0.4 to 4.0.6
- [Release notes](https://github.com/spotbugs/spotbugs/releases)
- [Changelog](https://github.com/spotbugs/spotbugs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/spotbugs/spotbugs/compare/4.0.4...4.0.6)

Updates `spotbugs-annotations` from 4.0.4 to 4.0.6
- [Release notes](https://github.com/spotbugs/spotbugs/releases)
- [Changelog](https://github.com/spotbugs/spotbugs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/spotbugs/spotbugs/compare/4.0.4...4.0.6)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-23 06:40:11 +00:00
Liam Newman
8e2c1d7382 Merge pull request #851 from MarcosCela/fix/ghteambuilder-accept-long
GHTeamBuilder#parentTeamId now accepts a long instead of an int
2020-06-22 06:28:20 -07:00
Liam Newman
ab7b9cccba Merge branch 'master' into fix/ghteambuilder-accept-long 2020-06-22 06:18:54 -07:00
Liam Newman
81bf61a161 Merge pull request #857 from hub4j/dependabot/maven/com.github.spotbugs-spotbugs-maven-plugin-4.0.4
Chore(deps): Bump spotbugs-maven-plugin from 4.0.0 to 4.0.4
2020-06-22 06:14:39 -07:00
dependabot-preview[bot]
b40f008647 Chore(deps): Bump spotbugs-maven-plugin from 4.0.0 to 4.0.4
Bumps [spotbugs-maven-plugin](https://github.com/spotbugs/spotbugs-maven-plugin) from 4.0.0 to 4.0.4.
- [Release notes](https://github.com/spotbugs/spotbugs-maven-plugin/releases)
- [Commits](https://github.com/spotbugs/spotbugs-maven-plugin/compare/spotbugs-maven-plugin-4.0.0...spotbugs-maven-plugin-4.0.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-22 06:57:13 +00:00
Sean C. Sullivan
734e41702b add Dependabot
https://github.blog/2020-06-01-keep-all-your-packages-up-to-date-with-dependabot/
2020-06-20 07:10:28 -07:00
Alexander Kjäll
038dd20a91 added a unit test for fetching a child team 2020-06-19 08:27:25 +02:00
Marcos.Cela
1dd62b8550 add a simple test for GHTeamBuilder: create a team with a parent/child relation
Additionally, ensure that when creating the team and setting the parentTeamId
on the GHTeamBuilder, we receive it directly from a previously retrieved
GHTeam. This ensures that the return type of GHTeam#getId() is compatible
with GHTeamBuilder#parentTeamId()
2020-06-18 10:33:04 +02:00
Marcos.Cela
715deebe05 GHTeamBuilder#parentTeamId now accepts a long instead of an int
Closes #850
2020-06-17 12:15:29 +02:00
Alexander Kjäll
b3fe3d8590 Added support for fetching what teams are part of this team.
The call is to this endpoint https://developer.github.com/v3/teams/#list-child-teams-legacy
2020-06-17 09:38:17 +02:00
Liam Newman
f74c3ed3ea Merge pull request #848 from hub4j/dependabot/maven/org.kohsuke.stapler-stapler-1.260
Chore(deps-dev): Bump stapler from 1.259 to 1.260
2020-06-16 08:39:53 -07:00
Liam Newman
2c9aebeeed Merge pull request #847 from hub4j/timja-patch-1
Fix tag template in release drafter
2020-06-16 08:34:54 -07:00
dependabot-preview[bot]
7474f1e11f Chore(deps-dev): Bump stapler from 1.259 to 1.260
Bumps [stapler](https://github.com/stapler/stapler) from 1.259 to 1.260.
- [Release notes](https://github.com/stapler/stapler/releases)
- [Changelog](https://github.com/stapler/stapler/blob/master/CHANGELOG.md)
- [Commits](https://github.com/stapler/stapler/compare/stapler-parent-1.259...stapler-parent-1.260)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-16 06:28:21 +00:00
Tim Jacomb
dba9c55b64 Fix tag template in release drafter 2020-06-16 07:16:52 +01:00
Liam Newman
b432364397 [maven-release-plugin] prepare for next development iteration 2020-06-10 20:02:26 -07:00
Liam Newman
696967bdd1 [maven-release-plugin] prepare release github-api-1.114 2020-06-10 20:02:18 -07:00
Liam Newman
b76889efc3 Merge pull request #845 from bitwiseman/task/redo-822
Modify getRef() changes to be compatible with older GHE versions
2020-06-10 20:00:44 -07:00
Liam Newman
e6a7b64ebe Merge branch 'master' into task/redo-822 2020-06-10 19:53:42 -07:00
Liam Newman
9daa0df311 Modify getRef() changes to be compatible with older GHE versions
Fixes #844
    Fixes #794
2020-06-10 19:47:23 -07:00
Liam Newman
612800bda5 Merge pull request #843 from bitwiseman/task/body-close
[JENKINS-62655] Ensure connection response stream is always closed
2020-06-10 17:30:12 -07:00
Liam Newman
a6bbb1dec9 Ensure connection response stream is always closed 2020-06-10 17:22:25 -07:00
Liam Newman
873c93ab64 Merge pull request #841 from hub4j/dependabot/maven/org.apache.bcel-bcel-6.5.0
Chore(deps): Bump bcel from 6.4.1 to 6.5.0
2020-06-10 08:59:00 -07:00
dependabot-preview[bot]
d15242e2d2 Chore(deps): Bump bcel from 6.4.1 to 6.5.0
Bumps bcel from 6.4.1 to 6.5.0.

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-10 03:50:37 +00:00
Liam Newman
992d2b937c Merge pull request #840 from hub4j/dependabot/maven/spotbugs.version-4.0.4
Chore(deps): Bump spotbugs.version from 4.0.3 to 4.0.4
2020-06-09 20:49:24 -07:00
dependabot-preview[bot]
1e05ddad4b Chore(deps): Bump spotbugs.version from 4.0.3 to 4.0.4
Bumps `spotbugs.version` from 4.0.3 to 4.0.4.

Updates `spotbugs` from 4.0.3 to 4.0.4
- [Release notes](https://github.com/spotbugs/spotbugs/releases)
- [Changelog](https://github.com/spotbugs/spotbugs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/spotbugs/spotbugs/compare/4.0.3...4.0.4)

Updates `spotbugs-annotations` from 4.0.3 to 4.0.4
- [Release notes](https://github.com/spotbugs/spotbugs/releases)
- [Changelog](https://github.com/spotbugs/spotbugs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/spotbugs/spotbugs/compare/4.0.3...4.0.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-09 06:33:50 +00:00
Liam Newman
4f8a64610b [maven-release-plugin] prepare for next development iteration 2020-06-08 14:37:20 -07:00
Liam Newman
b82366218c [maven-release-plugin] prepare release github-api-1.113 2020-06-08 14:37:11 -07:00
Liam Newman
acbe1f4cb3 Update PULL_REQUEST_TEMPLATE.md 2020-06-08 13:25:56 -07:00
Liam Newman
4c5e018583 Merge pull request #838 from Chew/feature/new-profile-data
Add bio, hireable, and twitter_username fields to Person
2020-06-08 12:21:16 -07:00
Liam Newman
6c0380e85c Merge pull request #839 from bitwiseman/issue-828
Add GitHub Team Discussions as GHDiscussion
2020-06-08 12:18:46 -07:00
Liam Newman
fde48e604f Test coverage and javadoc fixes 2020-06-08 12:06:39 -07:00
Liam Newman
e83a4de5fb Merge branch 'master' into feature/new-profile-data 2020-06-08 10:33:29 -07:00
Liam Newman
927d2799dc Move url construction to single method 2020-06-08 10:12:15 -07:00
Liam Newman
1ad701fe5d Add convenience override of getId() 2020-06-08 09:59:43 -07:00
Liam Newman
086425d2da Tweaks for batch update 2020-06-08 09:59:43 -07:00
Charles Moulliard
beca54416a Merge branch 'master' into issue-828 2020-06-08 18:43:15 +02:00
Chew
c92f5c5713 Update test value and add new test for Twitter Username 2020-06-07 15:05:02 -05:00
Chew
dee4e6caff Add twitter_username to Person and bio and hireable to User 2020-06-07 00:55:44 -05:00
Liam Newman
dd5a39e72e Improve wiremock stub accuracy 2020-06-06 16:29:26 -07:00
Charles Moulliard
e5ed52165c Fix: Add missing @param for the delete() method 2020-06-04 18:26:14 +02:00
Charles Moulliard
9484f8e0f5 Chore: Add more methods to test CRUD operations on discusions 2020-06-04 18:19:41 +02:00
Charles Moulliard
947caffe0a Chore: Add method to get a discussion using a number/id 2020-06-04 17:27:15 +02:00
Charles Moulliard
870090e8df Chore: Remove javadoc Throwing the exception for the GHDiscussionbuilder - update method 2020-06-04 13:51:04 +02:00
Charles Moulliard
73f07f13c5 Chore: Remove javadoc Throwing the exception 2020-06-04 13:47:41 +02:00
Charles Moulliard
d1952bf591 Chore: Reformat method 2020-06-04 13:37:08 +02:00
Charles Moulliard
5a612e1332 Chore: Add try/catch block if we cannot find the discussion to be updated 2020-06-04 13:25:57 +02:00
Charles Moulliard
b00a9faea6 Fix: Add missing parameter 2020-06-04 12:58:07 +02:00
Charles Moulliard
74db42a703 Chore: Add method to update a discussion 2020-06-04 12:52:58 +02:00
Charles Moulliard
ddf625ca04 Chore: Add method to delete a discussion using its number. Add field number 2020-06-04 12:20:36 +02:00
Charles Moulliard
eca2f017d8 Fix: Add missing import statement for the Jackson Annotation. Use the correct htmlUrl field 2020-06-04 11:32:33 +02:00
Charles Moulliard
3190bde343 Fix: Add mising try/catch block to report the exeption when no discussions are found 2020-06-04 11:31:50 +02:00
Charles Moulliard
c6ebf42a47 Update src/main/java/org/kohsuke/github/GHTeam.java
Dont wrapUp using the team object

Co-authored-by: Liam Newman <bitwiseman@gmail.com>
2020-06-04 11:14:42 +02:00
Charles Moulliard
c116b60d12 Update src/main/java/org/kohsuke/github/GHDiscussion.java
Add doc link to github team discussion API

Co-authored-by: Liam Newman <bitwiseman@gmail.com>
2020-06-04 11:13:19 +02:00
Charles Moulliard
5d09e6d9ab Update src/main/java/org/kohsuke/github/GHDiscussion.java
Remove `this.root` as it is already set with the org

Co-authored-by: Liam Newman <bitwiseman@gmail.com>
2020-06-04 11:12:38 +02:00
Charles Moulliard
2613ce0ac9 Update src/main/java/org/kohsuke/github/GHDiscussion.java
Remove to set the field `root`

Co-authored-by: Liam Newman <bitwiseman@gmail.com>
2020-06-04 11:10:21 +02:00
Charles Moulliard
a88e9b28ea Update src/main/java/org/kohsuke/github/GHDiscussion.java
Change the visibility of the fields from protected to private. Add @JacksonInject annotation. Rename html_url to htmlUrl as needed by Jackson

Co-authored-by: Liam Newman <bitwiseman@gmail.com>
2020-06-04 11:09:24 +02:00
Charles Moulliard
f0a3c26ee6 Fix: Add the missing correct file to check the discussion created using wiremock 2020-06-04 10:44:26 +02:00
Charles Moulliard
84c87ecb32 Chore: Fixed the null org within the generated json file but we still get an error 404 2020-06-04 08:49:16 +02:00
Charles Moulliard
6573f44d41 Fix: As the name of the organization could be empty/null, then use getLogin to get the org name 2020-06-04 07:46:51 +02:00
Charles Moulliard
3cacbc552c Fix: Set the organisation name to avoid to populate a url request having /orgs/null 2020-06-04 07:30:44 +02:00
Charles Moulliard
343d623e02 chore: Push new resource files generated 2020-06-04 07:22:53 +02:00
Charles Moulliard
6b80bb2b11 chore: Remove deleted resources files 2020-06-04 07:00:14 +02:00
Charles Moulliard
56fe7452eb chore. Review test case. Add new wrapUp methods 2020-06-04 06:59:41 +02:00
Charles Moulliard
d3a66f6605 chore: Regenerate new testing files 2020-05-29 17:37:49 +02:00
Charles Moulliard
dd7b4712f1 fix: Add missing @throws IOException 2020-05-29 17:24:49 +02:00
Charles Moulliard
9df5871f6b chore: wrapUp Github instance 2020-05-29 17:20:54 +02:00
Charles Moulliard
29aab9e9f4 chore: Add missing classes and test case 2020-05-29 16:34:07 +02:00
Charles Moulliard
af67eb7f0b feat: add new APi for Discussion 2020-05-29 16:28:47 +02:00
Liam Newman
10482c0141 [maven-release-plugin] prepare for next development iteration 2020-05-28 07:48:53 -07:00
709 changed files with 45984 additions and 3546 deletions

View File

@@ -10,3 +10,7 @@ We love getting PRs, but we hate asking people for the same basic changes every
- [ ] Run `mvn clean compile` locally. This may reformat your code, commit those changes.
- [ ] Run `mvn -D enable-ci clean install site` locally. If this command doesn't succeed, your change will not pass CI.
# When creating a PR:
- [ ] Fill in the "Description" above.
- [ ] Enable "Allow edits from maintainers".

12
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,12 @@
version: 2
updates:
- package-ecosystem: "maven"
directory: "/"
schedule:
interval: "monthly"
time: "02:00"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"
time: "02:00"

View File

@@ -1,5 +1,6 @@
name-template: 'v$NEXT_PATCH_VERSION 🌈'
tag-template: 'v$NEXT_PATCH_VERSION'
name-template: 'v$NEXT_MINOR_VERSION 🌈'
tag-template: 'github-api-$NEXT_MINOR_VERSION'
version-template: '$MAJOR.$MINOR'
categories:
- title: '🚀 Features'
labels:

View File

@@ -17,7 +17,7 @@ jobs:
with:
java-version: ${{ matrix.java }}
- name: Cached .m2
uses: actions/cache@v1
uses: actions/cache@v2
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
@@ -37,7 +37,7 @@ jobs:
uses: actions/setup-java@v1
with:
java-version: ${{ matrix.java }}
- uses: actions/cache@v1
- uses: actions/cache@v2
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
@@ -58,7 +58,7 @@ jobs:
uses: actions/setup-java@v1
with:
java-version: ${{ matrix.java }}
- uses: actions/cache@v1
- uses: actions/cache@v2
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}

View File

@@ -1,5 +1,7 @@
# Changelog
For changes after v1.101 see the [GitHub Releases page](https://github.com/hub4j/github-api/releases) for the project.
## [github-api-1.101](https://github.com/hub4j/github-api/tree/github-api-1.101) (2019-11-27)
[Full Changelog](https://github.com/hub4j/github-api/compare/github-api-1.100...github-api-1.101)

View File

@@ -14,6 +14,10 @@ Example:
This the default behavior.
Example for a single test case:
`mvn install -Dtest=WireMockStatusReporterTest#user_whenProxying_AuthCorrectlyConfigured`
### Setting up credential
@@ -27,21 +31,37 @@ This the default behavior.
`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.
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:
#### Running tests using GitHub test proxy
`mvn install -Dtest.github.takeSnapshot -Dtest=YourTestClassName`
Keep `useProxy` enabled and iterate on your tests as needed. With `useProxy` enabled your tests will interact with
GitHub - you will need to clean up your server-state between runs. This can be done manually to start with.
Once your test code is somewhat stable, use `getGitHubBeforeAfter()` to get a `GitHub` instance for test setup and cleanup.
Interactions with that `GitHub` instance will not be recorded as part of the test, keeping the test data files to a minimum.
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.
#### Running tests against your personal GitHub user account
By default, test helper methods such as `getTempRepository()` target the `hub4j-test-org` GitHub organization.
Please request access to this org to record your tests before submitting a PR. This helps keep the project stable and nimble.
Until you have access (or if you don't want access), you can set the following additional system property to target
your personal github account.
`mvn install -Dtest.github.org=false -Dtest=YourTestClassName`
#### Taking a snapshot
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.github.org=false -Dtest=YourTestClassName`
The above command will create snapshot WireMock data files under the path `src/test/resources/org/kohsuhke/github/YourTestClassName/wiremock`.
Each method will get a separate directory that will hold the data files for that test method.
Add all files including the generated data to your commit and submit a PR.

145
pom.xml
View File

@@ -2,7 +2,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.kohsuke</groupId>
<artifactId>github-api</artifactId>
<version>1.112</version>
<version>1.122</version>
<name>GitHub API for Java</name>
<url>https://github-api.kohsuke.org/</url>
<description>GitHub API for Java</description>
@@ -11,7 +11,7 @@
<connection>scm:git:git@github.com/hub4j/${project.artifactId}.git</connection>
<developerConnection>scm:git:ssh://git@github.com/hub4j/${project.artifactId}.git</developerConnection>
<url>https://github.com/hub4j/github-api/</url>
<tag>github-api-1.112</tag>
<tag>github-api-1.122</tag>
</scm>
<distributionManagement>
@@ -33,19 +33,19 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spotbugs-maven-plugin.version>4.0.0</spotbugs-maven-plugin.version>
<spotbugs.version>4.0.3</spotbugs.version>
<spotbugs-maven-plugin.version>4.1.4</spotbugs-maven-plugin.version>
<spotbugs.version>4.1.3</spotbugs.version>
<spotbugs-maven-plugin.failOnError>true</spotbugs-maven-plugin.failOnError>
<hamcrest.version>2.2</hamcrest.version>
<okhttp3.version>4.4.1</okhttp3.version>
<okio.version>2.5.0</okio.version>
<formatter-maven-plugin.goal>format</formatter-maven-plugin.goal>
<impsort-maven-plugin.goal>sort</impsort-maven-plugin.goal>
<spotless-maven-plugin.goal>apply</spotless-maven-plugin.goal>
<!-- Using this as the minimum bar for code coverage. Adding methods without covering them will fail this. -->
<jacoco.coverage.target.bundle.method>0.60</jacoco.coverage.target.bundle.method>
<jacoco.coverage.target.class.method>0.25</jacoco.coverage.target.class.method>
<!-- For non-ci builds we'd like the build to still complete if jacoco metrics aren't met. -->
<jacoco.haltOnFailure>false</jacoco.haltOnFailure>
<jjwt.suite.version>0.11.2</jjwt.suite.version>
</properties>
<build>
@@ -79,6 +79,14 @@
</testResources>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<configuration>
<!-- SUREFIRE-1226 workaround -->
<trimStackTrace>false</trimStackTrace>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
@@ -92,7 +100,7 @@
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.5</version>
<version>0.8.6</version>
<executions>
<execution>
<goals>
@@ -146,7 +154,7 @@
<exclude>org.kohsuke.github.example.*</exclude>
<!-- No methods -->
<exclude>org.kohsuke.github.Previews</exclude>
<exclude>org.kohsuke.github.internal.Previews</exclude>
<!-- Deprecated -->
<exclude>org.kohsuke.github.extras.OkHttp3Connector</exclude>
@@ -233,7 +241,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
<version>3.9.0</version>
<version>3.9.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
@@ -253,12 +261,12 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.1.0</version>
<version>3.1.1</version>
<dependencies>
<dependency>
<groupId>org.apache.bcel</groupId>
<artifactId>bcel</artifactId>
<version>6.4.1</version>
<version>6.5.0</version>
</dependency>
</dependencies>
</plugin>
@@ -280,16 +288,31 @@
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<configuration>
<!-- SUREFIRE-1226 workaround -->
<trimStackTrace>false</trimStackTrace>
</configuration>
<executions>
<execution>
<id>default-test</id>
<configuration>
<excludesFile>src/test/resources/slow-or-flaky-tests.txt</excludesFile>
</configuration>
</execution>
<execution>
<id>slow-or-flaky-test</id>
<phase>test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<rerunFailingTestsCount>2</rerunFailingTestsCount>
<!-- There are some tests that take longer or are a little flaky. Run them here. -->
<includesFile>src/test/resources/slow-or-flaky-tests.txt</includesFile>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>animal-sniffer-maven-plugin</artifactId>
<version>1.18</version>
<version>1.19</version>
<configuration>
<signature>
<groupId>org.codehaus.mojo.signature</groupId>
@@ -320,36 +343,34 @@
</executions>
</plugin>
<plugin>
<groupId>net.revelc.code.formatter</groupId>
<artifactId>formatter-maven-plugin</artifactId>
<version>2.11.0</version>
<groupId>com.diffplug.spotless</groupId>
<artifactId>spotless-maven-plugin</artifactId>
<version>2.6.1</version>
<executions>
<execution>
<id>spotless-check</id>
<phase>process-sources</phase>
<goals>
<goal>${formatter-maven-plugin.goal}</goal>
<goal>${spotless-maven-plugin.goal}</goal>
</goals>
<configuration>
<configFile>src/main/resources/eclipse/formatter.xml</configFile>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>net.revelc.code</groupId>
<artifactId>impsort-maven-plugin</artifactId>
<version>1.4.1</version>
<configuration>
<groups>*,java.,javax.</groups>
<removeUnused>true</removeUnused>
<staticAfter>true</staticAfter>
<java>
<eclipse>
<file>${basedir}/src/build/eclipse/formatter.xml</file>
</eclipse>
<importOrder>
<file>${basedir}/src/build/eclipse/eclipse.importorder</file>
</importOrder>
<removeUnusedImports />
<trimTrailingWhitespace />
<endWithNewline />
</java>
</configuration>
<executions>
<execution>
<goals>
<goal>${impsort-maven-plugin.goal}</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.github.spotbugs</groupId>
@@ -386,6 +407,12 @@
<artifactId>commons-lang3</artifactId>
<version>3.9</version>
</dependency>
<dependency>
<groupId>com.tngtech.archunit</groupId>
<artifactId>archunit</artifactId>
<version>0.15.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest</artifactId>
@@ -408,7 +435,7 @@
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -445,7 +472,7 @@
<dependency>
<groupId>org.kohsuke.stapler</groupId>
<artifactId>stapler</artifactId>
<version>1.259</version>
<version>1.262</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -457,9 +484,27 @@
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit</artifactId>
<version>5.7.0.202003110725-r</version>
<version>5.10.0.202012080955-r</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>${jjwt.suite.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>${jjwt.suite.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>${jjwt.suite.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.squareup.okio</groupId>
<artifactId>okio</artifactId>
@@ -495,7 +540,7 @@
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.3.3</version>
<version>3.6.28</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -507,7 +552,7 @@
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock-jre8-standalone</artifactId>
<version>2.26.3</version>
<version>2.27.2</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -516,6 +561,12 @@
<version>2.8.6</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.30</version>
<scope>test</scope>
</dependency>
</dependencies>
<repositories>
<repository>
@@ -541,8 +592,7 @@
</os>
</activation>
<properties>
<formatter-maven-plugin.goal>validate</formatter-maven-plugin.goal>
<impsort-maven-plugin.goal>check</impsort-maven-plugin.goal>
<spotless-maven-plugin.goal>check</spotless-maven-plugin.goal>
</properties>
</profile>
<profile>
@@ -567,8 +617,7 @@
<profile>
<id>release</id>
<properties>
<formatter-maven-plugin.goal>validate</formatter-maven-plugin.goal>
<impsort-maven-plugin.goal>check</impsort-maven-plugin.goal>
<spotless-maven-plugin.goal>check</spotless-maven-plugin.goal>
</properties>
<build>
<plugins>

View File

@@ -0,0 +1,6 @@
#Organize Import Order
# Import this file in Window -> Preferences -> Java -> Code Style -> Organize Imports -> Import...
0=
1=java
2=javax
3=\#

View File

@@ -12,14 +12,14 @@ import javax.annotation.Nonnull;
* <p>
* Batching looks like this:
* </p>
*
*
* <pre>
* update().someName(value).otherName(value).done()
* </pre>
* <p>
* Single changes look like this:
* </p>
*
*
* <pre>
* set().someName(value);
* set().otherName(value);
@@ -38,7 +38,7 @@ import javax.annotation.Nonnull;
* Intermediate return type for this builder returned by calls to {@link #with(String, Object)}. If {@link S}
* the same as {@link R}, this builder will commit changes after each call to {@link #with(String, Object)}.
*/
abstract class AbstractBuilder<R, S> {
abstract class AbstractBuilder<R, S> extends GitHubInteractiveObject {
@Nonnull
private final Class<R> returnType;
@@ -75,6 +75,7 @@ abstract class AbstractBuilder<R, S> {
@Nonnull Class<S> intermediateReturnType,
@Nonnull GitHub root,
@CheckForNull R baseInstance) {
super(root);
this.requester = root.createRequest();
this.returnType = finalReturnType;
this.commitChangesImmediately = returnType.equals(intermediateReturnType);
@@ -97,7 +98,7 @@ abstract class AbstractBuilder<R, S> {
* if there is an I/O Exception
*/
@Nonnull
@Preview
@BetaApi
@Deprecated
public R done() throws IOException {
R result;
@@ -127,7 +128,7 @@ abstract class AbstractBuilder<R, S> {
* if an I/O error occurs
*/
@Nonnull
@Preview
@BetaApi
@Deprecated
protected S with(@Nonnull String name, Object value) throws IOException {
requester.with(name, value);
@@ -148,7 +149,7 @@ abstract class AbstractBuilder<R, S> {
* if an I/O error occurs
*/
@Nonnull
@Preview
@BetaApi
@Deprecated
protected S continueOrDone() throws IOException {
// This little bit of roughness in this base class means all inheriting builders get to create Updater and

View File

@@ -0,0 +1,18 @@
package org.kohsuke.github;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Indicates that the method/class/etc marked is a beta implementation of an sdk feature.
* <p>
* These APIs are subject to change and not a part of the backward compatibility commitment. Always used in conjunction
* with 'deprecated' to raise awareness to clients.
* </p>
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface BetaApi {
}

View File

@@ -5,7 +5,7 @@ import java.net.URL;
import java.util.List;
import java.util.Map;
import static org.kohsuke.github.Previews.MACHINE_MAN;
import static org.kohsuke.github.internal.Previews.MACHINE_MAN;
/**
* A Github App.
@@ -15,7 +15,6 @@ import static org.kohsuke.github.Previews.MACHINE_MAN;
*/
public class GHApp extends GHObject {
private GitHub root;
private GHUser owner;
private String name;
private String description;
@@ -189,7 +188,7 @@ public class GHApp extends GHObject {
* @return a list of App installations
* @see <a href="https://developer.github.com/v3/apps/#list-installations">List installations</a>
*/
@Preview
@Preview(MACHINE_MAN)
@Deprecated
public PagedIterable<GHAppInstallation> listInstallations() {
return root.createRequest()
@@ -210,7 +209,7 @@ public class GHApp extends GHObject {
* on error
* @see <a href="https://developer.github.com/v3/apps/#get-an-installation">Get an installation</a>
*/
@Preview
@Preview(MACHINE_MAN)
@Deprecated
public GHAppInstallation getInstallationById(long id) throws IOException {
return root.createRequest()
@@ -233,7 +232,7 @@ public class GHApp extends GHObject {
* @see <a href="https://developer.github.com/v3/apps/#get-an-organization-installation">Get an organization
* installation</a>
*/
@Preview
@Preview(MACHINE_MAN)
@Deprecated
public GHAppInstallation getInstallationByOrganization(String name) throws IOException {
return root.createRequest()
@@ -258,7 +257,7 @@ public class GHApp extends GHObject {
* @see <a href="https://developer.github.com/v3/apps/#get-a-repository-installation">Get a repository
* installation</a>
*/
@Preview
@Preview(MACHINE_MAN)
@Deprecated
public GHAppInstallation getInstallationByRepository(String ownerName, String repositoryName) throws IOException {
return root.createRequest()
@@ -280,7 +279,7 @@ public class GHApp extends GHObject {
* on error
* @see <a href="https://developer.github.com/v3/apps/#get-a-user-installation">Get a user installation</a>
*/
@Preview
@Preview(MACHINE_MAN)
@Deprecated
public GHAppInstallation getInstallationByUser(String name) throws IOException {
return root.createRequest()

View File

@@ -5,7 +5,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.kohsuke.github.Previews.MACHINE_MAN;
import static org.kohsuke.github.internal.Previews.MACHINE_MAN;
/**
* Creates a access token for a GitHub App Installation
@@ -14,12 +14,11 @@ import static org.kohsuke.github.Previews.MACHINE_MAN;
* @see GHAppInstallation#createToken(Map) GHAppInstallation#createToken(Map)
* @see GHAppInstallation#createToken() GHAppInstallation#createToken()
*/
public class GHAppCreateTokenBuilder {
private final GitHub root;
public class GHAppCreateTokenBuilder extends GitHubInteractiveObject {
protected final Requester builder;
private final String apiUrlTail;
@Preview
@BetaApi
@Deprecated
GHAppCreateTokenBuilder(GitHub root, String apiUrlTail) {
this.root = root;
@@ -27,7 +26,7 @@ public class GHAppCreateTokenBuilder {
this.builder = root.createRequest();
}
@Preview
@BetaApi
@Deprecated
GHAppCreateTokenBuilder(GitHub root, String apiUrlTail, Map<String, GHPermissionType> permissions) {
this(root, apiUrlTail);
@@ -43,7 +42,7 @@ public class GHAppCreateTokenBuilder {
* Array containing the repositories Ids
* @return a GHAppCreateTokenBuilder
*/
@Preview
@BetaApi
@Deprecated
public GHAppCreateTokenBuilder repositoryIds(List<Long> repositoryIds) {
this.builder.with("repository_ids", repositoryIds);
@@ -58,7 +57,7 @@ public class GHAppCreateTokenBuilder {
* Map containing the permission names and types.
* @return a GHAppCreateTokenBuilder
*/
@Preview
@BetaApi
@Deprecated
public GHAppCreateTokenBuilder permissions(Map<String, GHPermissionType> permissions) {
Map<String, String> retMap = new HashMap<>();
@@ -78,7 +77,7 @@ public class GHAppCreateTokenBuilder {
* @throws IOException
* on error
*/
@Preview
@Preview(MACHINE_MAN)
@Deprecated
public GHAppInstallationToken create() throws IOException {
return builder.method("POST")

View File

@@ -3,11 +3,13 @@ package org.kohsuke.github;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import java.util.Map;
import static org.kohsuke.github.Previews.GAMBIT;
import static org.kohsuke.github.internal.Previews.GAMBIT;
import static org.kohsuke.github.internal.Previews.MACHINE_MAN;
/**
* A Github App Installation.
@@ -20,7 +22,6 @@ import static org.kohsuke.github.Previews.GAMBIT;
* @see GHApp#getInstallationByUser(String) GHApp#getInstallationByUser(String)
*/
public class GHAppInstallation extends GHObject {
private GitHub root;
private GHUser account;
@JsonProperty("access_tokens_url")
@@ -117,6 +118,36 @@ public class GHAppInstallation extends GHObject {
return repositoriesUrl;
}
/**
* List repositories that this app installation can access.
*
* @return the paged iterable
*/
@Preview(MACHINE_MAN)
@Deprecated
public PagedSearchIterable<GHRepository> listRepositories() {
GitHubRequest request;
try {
request = root.createRequest().withPreview(MACHINE_MAN).withUrlPath("/installation/repositories").build();
} catch (MalformedURLException e) {
throw new GHException("", e);
}
return new PagedSearchIterable<>(root, request, GHAppInstallationRepositoryResult.class);
}
private static class GHAppInstallationRepositoryResult extends SearchResult<GHRepository> {
private GHRepository[] repositories;
@Override
GHRepository[] getItems(GitHub root) {
for (GHRepository item : repositories)
item.wrap(root);
return repositories;
}
}
/**
* Sets repositories url.
*
@@ -290,7 +321,7 @@ public class GHAppInstallation extends GHObject {
* on error
* @see <a href="https://developer.github.com/v3/apps/#delete-an-installation">Delete an installation</a>
*/
@Preview
@Preview(GAMBIT)
@Deprecated
public void deleteInstallation() throws IOException {
root.createRequest()
@@ -312,7 +343,7 @@ public class GHAppInstallation extends GHObject {
* @return a GHAppCreateTokenBuilder instance
* @deprecated Use {@link GHAppInstallation#createToken()} instead.
*/
@Preview
@BetaApi
@Deprecated
public GHAppCreateTokenBuilder createToken(Map<String, GHPermissionType> permissions) {
return new GHAppCreateTokenBuilder(root,
@@ -329,7 +360,7 @@ public class GHAppInstallation extends GHObject {
*
* @return a GHAppCreateTokenBuilder instance
*/
@Preview
@BetaApi
@Deprecated
public GHAppCreateTokenBuilder createToken() {
return new GHAppCreateTokenBuilder(root, String.format("/app/installations/%d/access_tokens", getId()));

View File

@@ -14,9 +14,7 @@ import java.util.Map;
* @author Paulo Miguel Almeida
* @see GHAppInstallation#createToken(Map) GHAppInstallation#createToken(Map)
*/
public class GHAppInstallationToken {
private GitHub root;
public class GHAppInstallationToken extends GitHubInteractiveObject {
private String token;
protected String expires_at;
private Map<String, String> permissions;

View File

@@ -9,7 +9,6 @@ import java.net.URL;
* @see GHRelease#getAssets() GHRelease#getAssets()
*/
public class GHAsset extends GHObject {
GitHub root;
GHRepository owner;
private String name;
private String label;

View File

@@ -33,7 +33,6 @@ public class GHAuthorization extends GHObject {
public static final String WRITE_KEY = "write:public_key";
public static final String ADMIN_KEY = "admin:public_key";
private GitHub root;
private List<String> scopes;
private String token;
private String token_last_eight;

View File

@@ -3,12 +3,15 @@ package org.kohsuke.github;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.kohsuke.github.internal.Previews;
import java.io.IOException;
import java.net.URL;
import java.util.Collection;
import java.util.Objects;
import javax.annotation.CheckForNull;
/**
* A branch in a repository.
*
@@ -18,8 +21,7 @@ import java.util.Objects;
value = { "UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD",
"URF_UNREAD_FIELD" },
justification = "JSON API")
public class GHBranch {
private GitHub root;
public class GHBranch extends GitHubInteractiveObject {
private GHRepository owner;
private String name;
@@ -76,7 +78,7 @@ public class GHBranch {
*
* @return true if the push to this branch is restricted via branch protection.
*/
@Preview
@Preview(Previews.LUKE_CAGE)
@Deprecated
public boolean isProtected() {
return protection;
@@ -87,7 +89,7 @@ public class GHBranch {
*
* @return API URL that deals with the protection of this branch.
*/
@Preview
@Preview(Previews.LUKE_CAGE)
@Deprecated
public URL getProtectionUrl() {
return GitHubClient.parseURL(protection_url);
@@ -100,8 +102,14 @@ public class GHBranch {
* @throws IOException
* the io exception
*/
@Preview(Previews.LUKE_CAGE)
@Deprecated
public GHBranchProtection getProtection() throws IOException {
return root.createRequest().setRawUrlPath(protection_url).fetch(GHBranchProtection.class).wrap(this);
return root.createRequest()
.withPreview(Previews.LUKE_CAGE)
.setRawUrlPath(protection_url)
.fetch(GHBranchProtection.class)
.wrap(this);
}
/**
@@ -129,7 +137,7 @@ public class GHBranch {
* @return GHBranchProtectionBuilder for enabling protection
* @see GHCommitStatus#getContext() GHCommitStatus#getContext()
*/
@Preview
@Preview(Previews.LUKE_CAGE)
@Deprecated
public GHBranchProtectionBuilder enableProtection() {
return new GHBranchProtectionBuilder(this);
@@ -161,6 +169,59 @@ public class GHBranch {
}
}
/**
* Merge a branch into this branch.
*
* @param headBranch
* the branch whose head will be merged
*
* @param commitMessage
* the commit message
*
* @return the merge {@link GHCommit} created, or {@code null} if the base already contains the head (nothing to
* merge).
*
* @throws IOException
* if merging fails
*/
@CheckForNull
public GHCommit merge(GHBranch headBranch, String commitMessage) throws IOException {
return merge(headBranch.getName(), commitMessage);
}
/**
* Merge a ref into this branch.
*
* @param head
* the ref name that will be merged into this branch. Follows the usual ref naming rules, could be a
* branch name, tag, or commit sha.
*
* @param commitMessage
* the commit message
*
* @return the merge {@link GHCommit} created, or {@code null} if the base already contains the head (nothing to
* merge).
*
* @throws IOException
* if merging fails
*/
@CheckForNull
public GHCommit merge(String head, String commitMessage) throws IOException {
GHCommit result = root.createRequest()
.withUrlPath(owner.getApiTailUrl("merges"))
.method("POST")
.with("commit_message", commitMessage)
.with("base", this.name)
.with("head", head)
.fetch(GHCommit.class);
if (result != null) {
result.wrapUp(owner);
}
return result;
}
String getApiRoute() {
return owner.getApiTailUrl("/branches/" + name);
}

View File

@@ -6,23 +6,23 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.IOException;
import java.util.Collection;
import static org.kohsuke.github.Previews.ZZZAX;
import static org.kohsuke.github.internal.Previews.ZZZAX;
/**
* The type GHBranchProtection.
*
* @see <a href="https://docs.github.com/en/rest/reference/repos#get-branch-protection">GitHub Branch Protection</a>
*/
@SuppressFBWarnings(
value = { "UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD",
"URF_UNREAD_FIELD" },
justification = "JSON API")
public class GHBranchProtection {
public class GHBranchProtection extends GitHubInteractiveObject {
private static final String REQUIRE_SIGNATURES_URI = "/required_signatures";
@JsonProperty
private EnforceAdmins enforceAdmins;
private GitHub root;
@JsonProperty("required_pull_request_reviews")
private RequiredReviews requiredReviews;
@@ -41,7 +41,7 @@ public class GHBranchProtection {
* @throws IOException
* the io exception
*/
@Preview
@Preview(ZZZAX)
@Deprecated
public void enabledSignedCommits() throws IOException {
requester().method("POST").withUrlPath(url + REQUIRE_SIGNATURES_URI).fetch(RequiredSignatures.class);
@@ -53,7 +53,7 @@ public class GHBranchProtection {
* @throws IOException
* the io exception
*/
@Preview
@Preview(ZZZAX)
@Deprecated
public void disableSignedCommits() throws IOException {
requester().method("DELETE").withUrlPath(url + REQUIRE_SIGNATURES_URI).send();
@@ -84,7 +84,7 @@ public class GHBranchProtection {
* @throws IOException
* the io exception
*/
@Preview
@Preview(ZZZAX)
@Deprecated
public boolean getRequiredSignatures() throws IOException {
return requester().withUrlPath(url + REQUIRE_SIGNATURES_URI).fetch(RequiredSignatures.class).enabled;

View File

@@ -12,7 +12,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.kohsuke.github.Previews.*;
import static org.kohsuke.github.internal.Previews.LUKE_CAGE;
/**
* Builder to configure the branch protection settings.

View File

@@ -1,10 +1,16 @@
package org.kohsuke.github;
import com.fasterxml.jackson.annotation.JsonProperty;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.kohsuke.github.internal.Previews;
import java.io.IOException;
import java.net.URL;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
/**
* Represents a check run.
@@ -14,8 +20,9 @@ import java.util.Date;
@SuppressFBWarnings(value = { "UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD", "URF_UNREAD_FIELD" },
justification = "JSON API")
public class GHCheckRun extends GHObject {
@JsonProperty("repository")
GHRepository owner;
GitHub root;
private String status;
private String conclusion;
@@ -34,7 +41,7 @@ public class GHCheckRun extends GHObject {
GHCheckRun wrap(GHRepository owner) {
this.owner = owner;
this.root = owner.root;
wrap(owner.root);
return this;
}
@@ -42,7 +49,24 @@ public class GHCheckRun extends GHObject {
this.root = root;
if (owner != null) {
owner.wrap(root);
if (pullRequests != null && pullRequests.length != 0) {
for (GHPullRequest singlePull : pullRequests) {
singlePull.wrap(owner);
}
}
}
if (checkSuite != null) {
if (owner != null) {
checkSuite.wrap(owner);
} else {
checkSuite.wrap(root);
}
}
if (app != null) {
app.wrapUp(root);
}
return this;
}
@@ -74,8 +98,14 @@ public class GHCheckRun extends GHObject {
return conclusion;
}
/**
* Final conclusion of the check.
*
* From <a href="https://docs.github.com/en/rest/reference/checks#create-a-check-run--parameters">Check Run
* Parameters - <code>conclusion</code></a>.
*/
public static enum Conclusion {
SUCCESS, FAILURE, NEUTRAL, CANCELLED, TIMED_OUT, ACTION_REQUIRED
SUCCESS, FAILURE, NEUTRAL, CANCELLED, TIMED_OUT, ACTION_REQUIRED, SKIPPED
}
/**
@@ -99,15 +129,22 @@ public class GHCheckRun extends GHObject {
/**
* Gets the pull requests participated in this check run.
*
* @return Pull requests of this check run
* Note this field is only populated for events. When getting a {@link GHCheckRun} outside of an event, this is
* always empty.
*
* @return the list of {@link GHPullRequest}s for this check run. Only populated for events.
* @throws IOException
* the io exception
*/
GHPullRequest[] getPullRequests() throws IOException {
public List<GHPullRequest> getPullRequests() throws IOException {
if (pullRequests != null && pullRequests.length != 0) {
for (GHPullRequest singlePull : pullRequests) {
singlePull.refresh();
// Only refresh if we haven't do so before
singlePull.refresh(singlePull.getTitle());
}
return Collections.unmodifiableList(Arrays.asList(pullRequests));
}
return pullRequests;
return Collections.emptyList();
}
/**
@@ -256,4 +293,15 @@ public class GHCheckRun extends GHObject {
NOTICE, WARNING, FAILURE
}
/**
* Updates this check run.
*
* @return a builder which you should customize, then call {@link GHCheckRunBuilder#create}
*/
@Preview(Previews.ANTIOPE)
@Deprecated
public @NonNull GHCheckRunBuilder update() {
return new GHCheckRunBuilder(owner, getId());
}
}

View File

@@ -28,6 +28,7 @@ import com.fasterxml.jackson.annotation.JsonInclude;
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.kohsuke.github.internal.Previews;
import java.io.IOException;
import java.util.Collections;
@@ -37,30 +38,45 @@ import java.util.List;
import java.util.Locale;
/**
* Drafts a check run.
* Drafts or updates a check run.
*
* @see GHCheckRun
* @see GHRepository#createCheckRun
* @see <a href="https://developer.github.com/v3/checks/runs/#create-a-check-run">documentation</a>
* @see GHCheckRun#update()
* @see <a href="https://developer.github.com/v3/checks/runs/#update-a-check-run">documentation</a>
*/
@SuppressFBWarnings(value = "URF_UNREAD_FIELD", justification = "Jackson serializes these even without a getter")
@Preview
@Preview(Previews.ANTIOPE)
@Deprecated
public final class GHCheckRunBuilder {
private final GHRepository repo;
private final Requester requester;
protected final GHRepository repo;
protected final Requester requester;
private Output output;
private List<Action> actions;
GHCheckRunBuilder(GHRepository repo, String name, String headSHA) {
private GHCheckRunBuilder(GHRepository repo, Requester requester) {
this.repo = repo;
requester = repo.root.createRequest()
.withPreview(Previews.ANTIOPE)
.method("POST")
.with("name", name)
.with("head_sha", headSHA)
.withUrlPath(repo.getApiTailUrl("check-runs"));
this.requester = requester;
}
GHCheckRunBuilder(GHRepository repo, String name, String headSHA) {
this(repo,
repo.root.createRequest()
.withPreview(Previews.ANTIOPE)
.method("POST")
.with("name", name)
.with("head_sha", headSHA)
.withUrlPath(repo.getApiTailUrl("check-runs")));
}
GHCheckRunBuilder(GHRepository repo, long checkId) {
this(repo,
repo.root.createRequest()
.withPreview(Previews.ANTIOPE)
.method("PATCH")
.withUrlPath(repo.getApiTailUrl("check-runs/" + checkId)));
}
public @NonNull GHCheckRunBuilder withDetailsURL(@CheckForNull String detailsURL) {

View File

@@ -8,7 +8,7 @@ import javax.annotation.Nonnull;
* Iterable for check-runs listing.
*/
class GHCheckRunsIterable extends PagedIterable<GHCheckRun> {
private GitHub root;
private final transient GitHub root;
private final GitHubRequest request;
private GHCheckRunsPage result;

View File

@@ -1,10 +1,14 @@
package org.kohsuke.github;
import com.fasterxml.jackson.annotation.JsonProperty;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.IOException;
import java.net.URL;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
/**
* Represents a check suite.
@@ -14,8 +18,9 @@ import java.util.Date;
@SuppressFBWarnings(value = { "UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD", "URF_UNREAD_FIELD" },
justification = "JSON API")
public class GHCheckSuite extends GHObject {
@JsonProperty("repository")
GHRepository owner;
GitHub root;
private String nodeId;
private String headBranch;
@@ -32,7 +37,7 @@ public class GHCheckSuite extends GHObject {
GHCheckSuite wrap(GHRepository owner) {
this.owner = owner;
this.root = owner.root;
this.wrap(owner.root);
return this;
}
@@ -40,6 +45,14 @@ public class GHCheckSuite extends GHObject {
this.root = root;
if (owner != null) {
owner.wrap(root);
if (pullRequests != null && pullRequests.length != 0) {
for (GHPullRequest singlePull : pullRequests) {
singlePull.wrap(owner);
}
}
}
if (app != null) {
app.wrapUp(root);
}
return this;
}
@@ -153,15 +166,22 @@ public class GHCheckSuite extends GHObject {
/**
* Gets the pull requests participated in this check suite.
*
* @return Pull requests
* Note this field is only populated for events. When getting a {@link GHCheckSuite} outside of an event, this is
* always empty.
*
* @return the list of {@link GHPullRequest}s for this check suite. Only populated for events.
* @throws IOException
* the io exception
*/
GHPullRequest[] getPullRequests() throws IOException {
public List<GHPullRequest> getPullRequests() throws IOException {
if (pullRequests != null && pullRequests.length != 0) {
for (GHPullRequest singlePull : pullRequests) {
singlePull.refresh();
// Only refresh if we haven't do so before
singlePull.refresh(singlePull.getTitle());
}
return Collections.unmodifiableList(Arrays.asList(pullRequests));
}
return pullRequests;
return Collections.emptyList();
}
/**

View File

@@ -11,6 +11,9 @@ import java.util.Collections;
import java.util.Date;
import java.util.List;
import static org.kohsuke.github.internal.Previews.ANTIOPE;
import static org.kohsuke.github.internal.Previews.GROOT;
/**
* A commit in a repository.
*
@@ -63,7 +66,7 @@ public class GHCommit {
* @return the authored date
*/
public Date getAuthoredDate() {
return GitHubClient.parseDate(author.date);
return author.getDate();
}
/**
@@ -82,7 +85,7 @@ public class GHCommit {
* @return the commit date
*/
public Date getCommitDate() {
return GitHubClient.parseDate(committer.date);
return committer.getDate();
}
/**
@@ -119,7 +122,6 @@ public class GHCommit {
* @deprecated Use {@link GitUser} instead.
*/
public static class GHAuthor extends GitUser {
private String date;
}
/**
@@ -446,6 +448,39 @@ public class GHCommit {
return owner.root.getUser(author.login);
}
/**
* Retrieves a list of pull requests which contain this commit.
*
* @return {@link PagedIterable} with the pull requests which contain this commit
*/
@Preview(GROOT)
@Deprecated
public PagedIterable<GHPullRequest> listPullRequests() {
return owner.root.createRequest()
.withPreview(GROOT)
.withUrlPath(String.format("/repos/%s/%s/commits/%s/pulls", owner.getOwnerName(), owner.getName(), sha))
.toIterable(GHPullRequest[].class, item -> item.wrapUp(owner));
}
/**
* Retrieves a list of branches where this commit is the head commit.
*
* @return {@link PagedIterable} with the branches where the commit is the head commit
* @throws IOException
* the io exception
*/
@Preview(GROOT)
@Deprecated
public PagedIterable<GHBranch> listBranchesWhereHead() throws IOException {
return owner.root.createRequest()
.withPreview(GROOT)
.withUrlPath(String.format("/repos/%s/%s/commits/%s/branches-where-head",
owner.getOwnerName(),
owner.getName(),
sha))
.toIterable(GHBranch[].class, item -> item.wrap(owner));
}
/**
* List comments paged iterable.
*
@@ -530,7 +565,7 @@ public class GHCommit {
* @throws IOException
* on error
*/
@Preview
@Preview(ANTIOPE)
@Deprecated
public PagedIterable<GHCheckRun> getCheckRuns() throws IOException {
return owner.getCheckRuns(sha);
@@ -538,7 +573,7 @@ public class GHCommit {
/**
* Some of the fields are not always filled in when this object is retrieved as a part of another API call.
*
*
* @throws IOException
* on error
*/

View File

@@ -89,6 +89,19 @@ public class GHCommitBuilder {
return this;
}
/**
* Configures the PGP signature of this commit.
*
* @param signature
* the signature calculated from the commit
*
* @return the gh commit builder
*/
public GHCommitBuilder withSignature(String signature) {
req.with("signature", signature);
return this;
}
/**
* Configures the committer of this commit.
*

View File

@@ -5,7 +5,7 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.IOException;
import java.net.URL;
import static org.kohsuke.github.Previews.*;
import static org.kohsuke.github.internal.Previews.SQUIRREL_GIRL;
/**
* A comment attached to a commit (or a specific line in a specific file of a commit.)
@@ -121,7 +121,7 @@ public class GHCommitComment extends GHObject implements Reactable {
this.body = body;
}
@Preview
@Preview(SQUIRREL_GIRL)
@Deprecated
public GHReaction createReaction(ReactionContent content) throws IOException {
return owner.root.createRequest()
@@ -133,7 +133,7 @@ public class GHCommitComment extends GHObject implements Reactable {
.wrap(owner.root);
}
@Preview
@Preview(SQUIRREL_GIRL)
@Deprecated
public PagedIterable<GHReaction> listReactions() {
return owner.root.createRequest()

View File

@@ -1,6 +1,7 @@
package org.kohsuke.github;
import org.apache.commons.lang3.StringUtils;
import org.kohsuke.github.internal.Previews;
import java.io.IOException;
@@ -10,7 +11,7 @@ import java.io.IOException;
* @author Marc de Verdelhan
* @see GitHub#searchCommits() GitHub#searchCommits()
*/
@Preview
@Preview(Previews.CLOAK)
@Deprecated
public class GHCommitSearchBuilder extends GHSearchBuilder<GHCommit> {
GHCommitSearchBuilder(GitHub root) {

View File

@@ -18,8 +18,6 @@ public class GHCommitStatus extends GHObject {
String context;
GHUser creator;
private GitHub root;
GHCommitStatus wrapUp(GitHub root) {
if (creator != null)
creator.wrapUp(root);

View File

@@ -15,21 +15,20 @@ import java.util.Base64;
* @see GHRepository#getFileContent(String) GHRepository#getFileContent(String)
*/
@SuppressWarnings({ "UnusedDeclaration" })
public class GHContent implements Refreshable {
public class GHContent extends GitHubInteractiveObject 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.
*/
private GHRepository repository;
private GitHub root;
private String type;
private String encoding;
private long size;
private String sha;
private String name;
private String path;
private String target;
private String content;
private String url; // this is the API url
private String git_url; // this is the Blob url
@@ -99,6 +98,15 @@ public class GHContent implements Refreshable {
return path;
}
/**
* Gets target of a symlink. This will only be set if {@code "symlink".equals(getType())}
*
* @return the target
*/
public String getTarget() {
return target;
}
/**
* Retrieve the decoded content that is stored at this location.
*

View File

@@ -1,166 +1,25 @@
package org.kohsuke.github;
import java.io.IOException;
import java.net.URL;
import static org.kohsuke.github.internal.Previews.BAPTISTE;
/**
* Creates a repository
*
* @author Kohsuke Kawaguchi
*/
public class GHCreateRepositoryBuilder {
private final GitHub root;
protected final Requester builder;
private final String apiUrlTail;
public class GHCreateRepositoryBuilder extends GHRepositoryBuilder<GHCreateRepositoryBuilder> {
GHCreateRepositoryBuilder(GitHub root, String apiUrlTail, String name) {
this.root = root;
this.apiUrlTail = apiUrlTail;
this.builder = root.createRequest();
this.builder.with("name", name);
}
public GHCreateRepositoryBuilder(String name, GitHub root, String apiTail) {
super(GHCreateRepositoryBuilder.class, root, null);
requester.method("POST").withUrlPath(apiTail);
/**
* Description for repository
*
* @param description
* description of repository
* @return a builder to continue with building
*/
public GHCreateRepositoryBuilder description(String description) {
this.builder.with("description", description);
return this;
}
/**
* Homepage for repository
*
* @param homepage
* homepage of repository
* @return a builder to continue with building
*/
public GHCreateRepositoryBuilder homepage(URL homepage) {
return homepage(homepage.toExternalForm());
}
/**
* Homepage for repository
*
* @param homepage
* homepage of repository
* @return a builder to continue with building
*/
public GHCreateRepositoryBuilder homepage(String homepage) {
this.builder.with("homepage", homepage);
return this;
}
/**
* Creates a private repository
*
* @param enabled
* private if true
* @return a builder to continue with building
*/
public GHCreateRepositoryBuilder private_(boolean enabled) {
this.builder.with("private", enabled);
return this;
}
/**
* Enables issue tracker
*
* @param enabled
* true if enabled
* @return a builder to continue with building
*/
public GHCreateRepositoryBuilder issues(boolean enabled) {
this.builder.with("has_issues", enabled);
return this;
}
/**
* Enables projects
*
* @param enabled
* true if enabled
* @return a builder to continue with building
*/
public GHCreateRepositoryBuilder projects(boolean enabled) {
this.builder.with("has_projects", enabled);
return this;
}
/**
* Enables wiki
*
* @param enabled
* true if enabled
* @return a builder to continue with building
*/
public GHCreateRepositoryBuilder wiki(boolean enabled) {
this.builder.with("has_wiki", enabled);
return this;
}
/**
* Enables downloads
*
* @param enabled
* true if enabled
* @return a builder to continue with building
*/
public GHCreateRepositoryBuilder downloads(boolean enabled) {
this.builder.with("has_downloads", enabled);
return this;
}
/**
* If true, create an initial commit with empty README.
*
* @param enabled
* true if enabled
* @return a builder to continue with building
*/
public GHCreateRepositoryBuilder autoInit(boolean enabled) {
this.builder.with("auto_init", enabled);
return this;
}
/**
* Allow or disallow squash-merging pull requests.
*
* @param enabled
* true if enabled
* @return a builder to continue with building
*/
public GHCreateRepositoryBuilder allowSquashMerge(boolean enabled) {
this.builder.with("allow_squash_merge", enabled);
return this;
}
/**
* Allow or disallow merging pull requests with a merge commit.
*
* @param enabled
* true if enabled
* @return a builder to continue with building
*/
public GHCreateRepositoryBuilder allowMergeCommit(boolean enabled) {
this.builder.with("allow_merge_commit", enabled);
return this;
}
/**
* Allow or disallow rebase-merging pull requests.
*
* @param enabled
* true if enabled
* @return a builder to continue with building
*/
public GHCreateRepositoryBuilder allowRebaseMerge(boolean enabled) {
this.builder.with("allow_rebase_merge", enabled);
return this;
try {
name(name);
} catch (IOException e) {
// not going to happen here
}
}
/**
@@ -169,10 +28,11 @@ public class GHCreateRepositoryBuilder {
* @param language
* template to base the ignore file on
* @return a builder to continue with building See https://developer.github.com/v3/repos/#create
* @throws IOException
* In case of any networking error or error from the server.
*/
public GHCreateRepositoryBuilder gitignoreTemplate(String language) {
this.builder.with("gitignore_template", language);
return this;
public GHCreateRepositoryBuilder gitignoreTemplate(String language) throws IOException {
return with("gitignore_template", language);
}
/**
@@ -181,10 +41,24 @@ public class GHCreateRepositoryBuilder {
* @param license
* template to base the license file on
* @return a builder to continue with building See https://developer.github.com/v3/repos/#create
* @throws IOException
* In case of any networking error or error from the server.
*/
public GHCreateRepositoryBuilder licenseTemplate(String license) {
this.builder.with("license_template", license);
return this;
public GHCreateRepositoryBuilder licenseTemplate(String license) throws IOException {
return with("license_template", license);
}
/**
* If true, create an initial commit with empty README.
*
* @param enabled
* true if enabled
* @return a builder to continue with building
* @throws IOException
* In case of any networking error or error from the server.
*/
public GHCreateRepositoryBuilder autoInit(boolean enabled) throws IOException {
return with("auto_init", enabled);
}
/**
@@ -193,10 +67,57 @@ public class GHCreateRepositoryBuilder {
* @param team
* team to grant access to
* @return a builder to continue with building
* @throws IOException
* In case of any networking error or error from the server.
*/
public GHCreateRepositoryBuilder team(GHTeam team) {
public GHCreateRepositoryBuilder team(GHTeam team) throws IOException {
if (team != null)
this.builder.with("team_id", team.getId());
return with("team_id", team.getId());
return this;
}
/**
* Specifies whether the repository is a template.
*
* @param enabled
* true if enabled
* @return a builder to continue with building
* @throws IOException
* In case of any networking error or error from the server.
* @deprecated Use {@link #isTemplate(boolean)} method instead
*/
@Deprecated
public GHCreateRepositoryBuilder templateRepository(boolean enabled) throws IOException {
return isTemplate(enabled);
}
/**
* Specifies the ownership of the repository.
*
* @param owner
* organization or personage
* @return a builder to continue with building
* @throws IOException
* In case of any networking error or error from the server.
*/
public GHCreateRepositoryBuilder owner(String owner) throws IOException {
return with("owner", owner);
}
/**
* Create repository from template repository
*
* @param templateOwner
* template repository owner
* @param templateRepo
* template repository
* @return a builder to continue with building
* @see <a href="https://developer.github.com/v3/previews/">GitHub API Previews</a>
*/
@Preview(BAPTISTE)
@Deprecated
public GHCreateRepositoryBuilder fromTemplateRepository(String templateOwner, String templateRepo) {
requester.withPreview(BAPTISTE).withUrlPath("/repos/" + templateOwner + "/" + templateRepo + "/generate");
return this;
}
@@ -205,10 +126,9 @@ public class GHCreateRepositoryBuilder {
*
* @return the gh repository
* @throws IOException
* if repsitory cannot be created
* if repository cannot be created
*/
public GHRepository create() throws IOException {
return builder.method("POST").withUrlPath(apiUrlTail).fetch(GHRepository.class).wrap(root);
return done();
}
}

View File

@@ -1,7 +1,10 @@
package org.kohsuke.github;
import org.kohsuke.github.internal.Previews;
import java.io.IOException;
import java.net.URL;
import java.util.Map;
/**
* Represents a deployment
@@ -13,7 +16,6 @@ import java.net.URL;
*/
public class GHDeployment extends GHObject {
private GHRepository owner;
private GitHub root;
protected String sha;
protected String ref;
protected String task;
@@ -23,6 +25,9 @@ public class GHDeployment extends GHObject {
protected String statuses_url;
protected String repository_url;
protected GHUser creator;
protected String original_environment;
protected boolean transient_environment;
protected boolean production_environment;
GHDeployment wrap(GHRepository owner) {
this.owner = owner;
@@ -60,7 +65,8 @@ public class GHDeployment extends GHObject {
}
/**
* Gets payload.
* Gets payload. <b>NOTE:</b> only use this method if you can guarantee the payload will be a simple string,
* otherwise use {@link #getPayloadObject()}.
*
* @return the payload
*/
@@ -68,6 +74,38 @@ public class GHDeployment extends GHObject {
return (String) payload;
}
/**
* Gets payload. <b>NOTE:</b> only use this method if you can guarantee the payload will be a JSON object (Map),
* otherwise use {@link #getPayloadObject()}.
*
* @return the payload
*/
public Map<String, Object> getPayloadMap() {
return (Map<String, Object>) payload;
}
/**
* Gets payload without assuming its type. It could be a String or a Map.
*
* @return the payload
*/
public Object getPayloadObject() {
return payload;
}
/**
* The environment defined when the deployment was first created.
*
* @deprecated until preview feature has graduated to stable
*
* @return the original deployment environment
*/
@Deprecated
@Preview(Previews.FLASH)
public String getOriginalEnvironment() {
return original_environment;
}
/**
* Gets environment.
*
@@ -77,6 +115,33 @@ public class GHDeployment extends GHObject {
return environment;
}
/**
* Specifies if the given environment is specific to the deployment and will no longer exist at some point in the
* future.
*
* @deprecated until preview feature has graduated to stable
*
* @return the environment is transient
*/
@Deprecated
@Preview(Previews.ANT_MAN)
public boolean isTransientEnvironment() {
return transient_environment;
}
/**
* Specifies if the given environment is one that end-users directly interact with.
*
* @deprecated until preview feature has graduated to stable
*
* @return the environment is used by end-users directly
*/
@Deprecated
@Preview(Previews.ANT_MAN)
public boolean isProductionEnvironment() {
return production_environment;
}
/**
* Gets creator.
*
@@ -133,6 +198,8 @@ public class GHDeployment extends GHObject {
public PagedIterable<GHDeploymentStatus> listStatuses() {
return root.createRequest()
.withUrlPath(statuses_url)
.withPreview(Previews.ANT_MAN)
.withPreview(Previews.FLASH)
.toIterable(GHDeploymentStatus[].class, item -> item.wrap(owner));
}

View File

@@ -1,5 +1,7 @@
package org.kohsuke.github;
import org.kohsuke.github.internal.Previews;
import java.io.IOException;
import java.util.List;
@@ -19,7 +21,10 @@ public class GHDeploymentBuilder {
*/
public GHDeploymentBuilder(GHRepository repo) {
this.repo = repo;
this.builder = repo.root.createRequest().method("POST");
this.builder = repo.root.createRequest()
.withPreview(Previews.ANT_MAN)
.withPreview(Previews.FLASH)
.method("POST");
}
/**
@@ -40,6 +45,7 @@ public class GHDeploymentBuilder {
*
* @param branch
* the branch
*
* @return the gh deployment builder
*/
public GHDeploymentBuilder ref(String branch) {
@@ -52,6 +58,7 @@ public class GHDeploymentBuilder {
*
* @param task
* the task
*
* @return the gh deployment builder
*/
public GHDeploymentBuilder task(String task) {
@@ -64,6 +71,7 @@ public class GHDeploymentBuilder {
*
* @param autoMerge
* the auto merge
*
* @return the gh deployment builder
*/
public GHDeploymentBuilder autoMerge(boolean autoMerge) {
@@ -76,6 +84,7 @@ public class GHDeploymentBuilder {
*
* @param requiredContexts
* the required contexts
*
* @return the gh deployment builder
*/
public GHDeploymentBuilder requiredContexts(List<String> requiredContexts) {
@@ -88,6 +97,7 @@ public class GHDeploymentBuilder {
*
* @param payload
* the payload
*
* @return the gh deployment builder
*/
public GHDeploymentBuilder payload(String payload) {
@@ -100,6 +110,7 @@ public class GHDeploymentBuilder {
*
* @param environment
* the environment
*
* @return the gh deployment builder
*/
public GHDeploymentBuilder environment(String environment) {
@@ -107,11 +118,47 @@ public class GHDeploymentBuilder {
return this;
}
/**
* Specifies if the given environment is specific to the deployment and will no longer exist at some point in the
* future.
*
* @deprecated until preview feature has graduated to stable
*
* @param transientEnvironment
* the environment is transient
*
* @return the gh deployment builder
*/
@Deprecated
@Preview(Previews.ANT_MAN)
public GHDeploymentBuilder transientEnvironment(boolean transientEnvironment) {
builder.with("transient_environment", transientEnvironment);
return this;
}
/**
* Specifies if the given environment is one that end-users directly interact with.
*
* @deprecated until preview feature has graduated to stable
*
* @param productionEnvironment
* the environment is used by end-users directly
*
* @return the gh deployment builder
*/
@Deprecated
@Preview(Previews.ANT_MAN)
public GHDeploymentBuilder productionEnvironment(boolean productionEnvironment) {
builder.with("production_environment", productionEnvironment);
return this;
}
/**
* Description gh deployment builder.
*
* @param description
* the description
*
* @return the gh deployment builder
*/
public GHDeploymentBuilder description(String description) {
@@ -123,6 +170,7 @@ public class GHDeploymentBuilder {
* Create gh deployment.
*
* @return the gh deployment
*
* @throws IOException
* the io exception
*/

View File

@@ -1,8 +1,40 @@
package org.kohsuke.github;
import org.kohsuke.github.internal.Previews;
/**
* Represents the state of deployment
*/
public enum GHDeploymentState {
PENDING, SUCCESS, ERROR, FAILURE
PENDING,
SUCCESS,
ERROR,
FAILURE,
/**
* The state of the deployment currently reflects it's in progress.
*
* @deprecated until preview feature has graduated to stable
*/
@Deprecated
@Preview(Previews.FLASH)
IN_PROGRESS,
/**
* The state of the deployment currently reflects it's queued up for processing.
*
* @deprecated until preview feature has graduated to stable
*/
@Deprecated
@Preview(Previews.FLASH)
QUEUED,
/**
* The state of the deployment currently reflects it's no longer active.
*
* @deprecated until preview feature has graduated to stable
*/
@Deprecated
@Preview(Previews.ANT_MAN)
INACTIVE
}

View File

@@ -1,5 +1,7 @@
package org.kohsuke.github;
import org.kohsuke.github.internal.Previews;
import java.net.URL;
import java.util.Locale;
@@ -8,19 +10,21 @@ import java.util.Locale;
*/
public class GHDeploymentStatus extends GHObject {
private GHRepository owner;
private GitHub root;
protected GHUser creator;
protected String state;
protected String description;
protected String target_url;
protected String log_url;
protected String deployment_url;
protected String repository_url;
protected String environment_url;
/**
* Wrap gh deployment status.
*
* @param owner
* the owner
*
* @return the gh deployment status
*/
public GHDeploymentStatus wrap(GHRepository owner) {
@@ -34,12 +38,30 @@ public class GHDeploymentStatus extends GHObject {
/**
* Gets target url.
*
* @deprecated Target url is deprecated in favor of {@link #getLogUrl() getLogUrl}
*
* @return the target url
*/
@Deprecated
public URL getTargetUrl() {
return GitHubClient.parseURL(target_url);
}
/**
* Gets target url.
* <p>
* This method replaces {@link #getTargetUrl() getTargetUrl}}.
*
* @deprecated until preview feature has graduated to stable
*
* @return the target url
*/
@Deprecated
@Preview(Previews.ANT_MAN)
public URL getLogUrl() {
return GitHubClient.parseURL(log_url);
}
/**
* Gets deployment url.
*
@@ -49,6 +71,19 @@ public class GHDeploymentStatus extends GHObject {
return GitHubClient.parseURL(deployment_url);
}
/**
* Gets deployment environment url.
*
* @deprecated until preview feature has graduated to stable
*
* @return the deployment environment url
*/
@Deprecated
@Preview(Previews.ANT_MAN)
public URL getEnvironmentUrl() {
return GitHubClient.parseURL(environment_url);
}
/**
* Gets repository url.
*

View File

@@ -1,5 +1,7 @@
package org.kohsuke.github;
import org.kohsuke.github.internal.Previews;
import java.io.IOException;
/**
@@ -21,6 +23,7 @@ public class GHDeploymentStatusBuilder {
* the deployment id
* @param state
* the state
*
* @deprecated Use {@link GHDeployment#createStatus(GHDeploymentState)}
*/
@Deprecated
@@ -31,15 +34,38 @@ public class GHDeploymentStatusBuilder {
GHDeploymentStatusBuilder(GHRepository repo, long deploymentId, GHDeploymentState state) {
this.repo = repo;
this.deploymentId = deploymentId;
this.builder = repo.root.createRequest().method("POST");
this.builder = repo.root.createRequest()
.withPreview(Previews.ANT_MAN)
.withPreview(Previews.FLASH)
.method("POST");
this.builder.with("state", state);
}
/**
* Add an inactive status to all prior non-transient, non-production environment deployments with the same
* repository and environment name as the created status's deployment.
*
* @deprecated until preview feature has graduated to stable
*
* @param autoInactive
* Add inactive status flag
*
* @return the gh deployment status builder
*/
@Deprecated
@Preview({ Previews.ANT_MAN, Previews.FLASH })
public GHDeploymentStatusBuilder autoInactive(boolean autoInactive) {
this.builder.with("auto_inactive", autoInactive);
return this;
}
/**
* Description gh deployment status builder.
*
* @param description
* the description
*
* @return the gh deployment status builder
*/
public GHDeploymentStatusBuilder description(String description) {
@@ -47,13 +73,70 @@ public class GHDeploymentStatusBuilder {
return this;
}
/**
* Name for the target deployment environment, which can be changed when setting a deploy status.
*
* @deprecated until preview feature has graduated to stable
*
* @param environment
* the environment name
*
* @return the gh deployment status builder
*/
@Deprecated
@Preview(Previews.FLASH)
public GHDeploymentStatusBuilder environment(String environment) {
this.builder.with("environment", environment);
return this;
}
/**
* The URL for accessing the environment
*
* @deprecated until preview feature has graduated to stable
*
* @param environmentUrl
* the environment url
*
* @return the gh deployment status builder
*/
@Deprecated
@Preview(Previews.ANT_MAN)
public GHDeploymentStatusBuilder environmentUrl(String environmentUrl) {
this.builder.with("environment_url", environmentUrl);
return this;
}
/**
* The full URL of the deployment's output.
* <p>
* This method replaces {@link #targetUrl(String) targetUrl}.
*
* @deprecated until preview feature has graduated to stable
*
* @param logUrl
* the deployment output url
*
* @return the gh deployment status builder
*/
@Deprecated
@Preview(Previews.ANT_MAN)
public GHDeploymentStatusBuilder logUrl(String logUrl) {
this.builder.with("log_url", logUrl);
return this;
}
/**
* Target url gh deployment status builder.
*
* @deprecated Target url is deprecated in favor of {@link #logUrl(String) logUrl}
*
* @param targetUrl
* the target url
*
* @return the gh deployment status builder
*/
@Deprecated
public GHDeploymentStatusBuilder targetUrl(String targetUrl) {
this.builder.with("target_url", targetUrl);
return this;
@@ -63,6 +146,7 @@ public class GHDeploymentStatusBuilder {
* Create gh deployment status.
*
* @return the gh deployment status
*
* @throws IOException
* the io exception
*/

View File

@@ -0,0 +1,230 @@
package org.kohsuke.github;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.kohsuke.github.internal.Previews;
import java.io.IOException;
import java.net.URL;
import java.util.Objects;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
/**
* A discussion in GitHub Team.
*
* @author Charles Moulliard
* @see <a href="https://developer.github.com/v3/teams/discussions">GitHub Team Discussions</a>
*/
public class GHDiscussion extends GHObject {
private GHTeam team;
private long number;
private String body, title, htmlUrl;
@JsonProperty(value = "private")
private boolean isPrivate;
@Override
public URL getHtmlUrl() throws IOException {
return GitHubClient.parseURL(htmlUrl);
}
GHDiscussion wrapUp(GHTeam team) {
this.team = team;
return this;
}
/**
* Get the team to which this discussion belongs.
*
* @return the team for this discussion
*/
@Nonnull
public GHTeam getTeam() {
return team;
}
/**
* Get the title of the discussion.
*
* @return the title
*/
public String getTitle() {
return title;
}
/**
* The description of this discussion.
*
* @return the body
*/
public String getBody() {
return body;
}
/**
* The number of this discussion.
*
* @return the number
*/
public long getNumber() {
return number;
}
/**
* The id number of this discussion. GitHub discussions have "number" instead of "id". This is provided for
* convenience.
*
* @return the id number for this discussion
* @see #getNumber()
*/
@Override
public long getId() {
return getNumber();
}
/**
* Whether the discussion is private to the team.
*
* @return {@code true} if discussion is private.
*/
public boolean isPrivate() {
return isPrivate;
}
/**
* Begins the creation of a new instance.
*
* Consumer must call {@link GHDiscussion.Creator#done()} to commit changes.
*
* @param team
* the team in which the discussion will be created.
* @return a {@link GHLabel.Creator}
* @throws IOException
* the io exception
*/
static GHDiscussion.Creator create(GHTeam team) throws IOException {
return new GHDiscussion.Creator(team);
}
static GHDiscussion read(GHTeam team, long discussionNumber) throws IOException {
return team.root.createRequest()
.setRawUrlPath(getRawUrlPath(team, discussionNumber))
.fetch(GHDiscussion.class)
.wrapUp(team);
}
static PagedIterable<GHDiscussion> readAll(GHTeam team) throws IOException {
return team.root.createRequest()
.setRawUrlPath(getRawUrlPath(team, null))
.toIterable(GHDiscussion[].class, item -> item.wrapUp(team));
}
/**
* Begins a batch update
*
* Consumer must call {@link GHDiscussion.Updater#done()} to commit changes.
*
* @return a {@link GHDiscussion.Updater}
*/
@Preview(Previews.SQUIRREL_GIRL)
@Deprecated
public GHDiscussion.Updater update() {
return new GHDiscussion.Updater(this);
}
/**
* Begins a single property update.
*
* @return a {@link GHDiscussion.Setter}
*/
@Preview(Previews.SQUIRREL_GIRL)
@Deprecated
public GHDiscussion.Setter set() {
return new GHDiscussion.Setter(this);
}
/**
* Delete the discussion
*
* @throws IOException
* the io exception
*/
public void delete() throws IOException {
team.root.createRequest().method("DELETE").setRawUrlPath(getRawUrlPath(team, number)).send();
}
private static String getRawUrlPath(@Nonnull GHTeam team, @CheckForNull Long discussionNumber) {
return team.getUrl().toString() + "/discussions" + (discussionNumber == null ? "" : "/" + discussionNumber);
}
/**
* A {@link GHLabelBuilder} that updates a single property per request
*
* {@link #done()} is called automatically after the property is set.
*/
public static class Setter extends GHDiscussionBuilder<GHDiscussion> {
private Setter(@Nonnull GHDiscussion base) {
super(GHDiscussion.class, base.team, base);
requester.method("PATCH").setRawUrlPath(base.getUrl().toString());
}
}
/**
* A {@link GHLabelBuilder} that allows multiple properties to be updated per request.
*
* Consumer must call {@link #done()} to commit changes.
*/
public static class Updater extends GHDiscussionBuilder<Updater> {
private Updater(@Nonnull GHDiscussion base) {
super(GHDiscussion.Updater.class, base.team, base);
requester.method("PATCH").setRawUrlPath(base.getUrl().toString());
}
}
/**
* A {@link GHLabelBuilder} that creates a new {@link GHLabel}
*
* Consumer must call {@link #done()} to create the new instance.
*/
public static class Creator extends GHDiscussionBuilder<Creator> {
private Creator(@Nonnull GHTeam team) {
super(GHDiscussion.Creator.class, team, null);
requester.method("POST").setRawUrlPath(getRawUrlPath(team, null));
}
/**
* Sets whether this discussion is private to this team.
*
* @param value
* privacy of this discussion
* @return either a continuing builder or an updated {@link GHDiscussion}
* @throws IOException
* if there is an I/O Exception
*/
@Nonnull
public Creator private_(boolean value) throws IOException {
return with("private", value);
}
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
GHDiscussion that = (GHDiscussion) o;
return number == that.number && Objects.equals(getUrl(), that.getUrl()) && Objects.equals(team, that.team)
&& Objects.equals(body, that.body) && Objects.equals(title, that.title);
}
@Override
public int hashCode() {
return Objects.hash(team, number, body, title);
}
}

View File

@@ -0,0 +1,80 @@
package org.kohsuke.github;
import java.io.IOException;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
/**
* Base class for creating or updating a discussion.
*
* @param <S>
* Intermediate return type for this builder returned by calls to {@link #with(String, Object)}. If {@link S}
* the same as {@link GHLabel}, this builder will commit changes after each call to
* {@link #with(String, Object)}.
*/
class GHDiscussionBuilder<S> extends AbstractBuilder<GHDiscussion, S> {
private final GHTeam team;
/**
*
* @param intermediateReturnType
* Intermediate return type for this builder returned by calls to {@link #with(String, Object)}. If
* {@link S} the same as {@link GHDiscussion}, this builder will commit changes after each call to
* {@link #with(String, Object)}.
* @param team
* the GitHub team. Updates will be sent to the root of this team.
* @param baseInstance
* instance on which to base this builder. If {@code null} a new instance will be created.
*/
protected GHDiscussionBuilder(@Nonnull Class<S> intermediateReturnType,
@Nonnull GHTeam team,
@CheckForNull GHDiscussion baseInstance) {
super(GHDiscussion.class, intermediateReturnType, team.root, baseInstance);
this.team = team;
if (baseInstance != null) {
requester.with("title", baseInstance.getTitle());
requester.with("body", baseInstance.getBody());
}
}
/**
* Title for this discussion.
*
* @param value
* title of discussion
* @return either a continuing builder or an updated {@link GHDiscussion}
* @throws IOException
* if there is an I/O Exception
*/
@Nonnull
public S title(String value) throws IOException {
return with("title", value);
}
/**
* Body content for this discussion.
*
* @param value
* body of discussion*
* @return either a continuing builder or an updated {@link GHDiscussion}
* @throws IOException
* if there is an I/O Exception
*/
@Nonnull
public S body(String value) throws IOException {
return with("body", value);
}
/**
* {@inheritDoc}
*/
@Nonnull
@Override
public GHDiscussion done() throws IOException {
return super.done().wrapUp(team);
}
}

View File

@@ -12,6 +12,7 @@ import java.util.Locale;
public enum GHEvent {
CHECK_RUN,
CHECK_SUITE,
CODE_SCANNING_ALERT,
COMMIT_COMMENT,
CONTENT_REFERENCE,
CREATE,
@@ -62,6 +63,8 @@ public enum GHEvent {
TEAM,
TEAM_ADD,
WATCH,
WORKFLOW_DISPATCH,
WORKFLOW_RUN,
/**
* Special event type that means "every possible event"

View File

@@ -12,9 +12,7 @@ import java.util.Date;
* @author Kohsuke Kawaguchi
*/
@SuppressFBWarnings(value = "UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", justification = "JSON API")
public class GHEventInfo {
private GitHub root;
public class GHEventInfo extends GitHubInteractiveObject {
// we don't want to expose Jackson dependency to the user. This needs databinding
private ObjectNode payload;

File diff suppressed because it is too large Load Diff

View File

@@ -23,7 +23,6 @@ import java.util.Map.Entry;
public class GHGist extends GHObject {
final GHUser owner;
final GitHub root;
private String forks_url, commits_url, id, git_pull_url, git_push_url, html_url;
@@ -123,7 +122,7 @@ public class GHGist extends GHObject {
/**
* Get the html url.
*
*
* @return the github html url
*/
public URL getHtmlUrl() {

View File

@@ -13,7 +13,6 @@ import javax.annotation.Nonnull;
* @see GitHub#createGist() GitHub#createGist()
*/
public class GHGistBuilder {
private final GitHub root;
private final Requester req;
private final LinkedHashMap<String, Object> files = new LinkedHashMap<String, Object>();
@@ -24,7 +23,6 @@ public class GHGistBuilder {
* the root
*/
public GHGistBuilder(GitHub root) {
this.root = root;
req = root.createRequest().method("POST");
}

View File

@@ -12,8 +12,7 @@ import java.util.Map;
* functionality
*/
class GHHooks {
static abstract class Context {
private final GitHub root;
static abstract class Context extends GitHubInteractiveObject {
private Context(GitHub root) {
this.root = root;

View File

@@ -16,7 +16,6 @@ import java.net.URL;
"UUF_UNUSED_FIELD" },
justification = "JSON API")
public class GHInvitation extends GHObject {
/* package almost final */ GitHub root;
private int id;
private GHRepository repository;

View File

@@ -39,7 +39,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Objects;
import static org.kohsuke.github.Previews.SQUIRREL_GIRL;
import static org.kohsuke.github.internal.Previews.SQUIRREL_GIRL;
/**
* Represents an issue on GitHub.
@@ -53,7 +53,6 @@ import static org.kohsuke.github.Previews.SQUIRREL_GIRL;
public class GHIssue extends GHObject implements Reactable {
private static final String ASSIGNEES = "assignees";
GitHub root;
GHRepository owner;
// API v3
@@ -158,10 +157,8 @@ public class GHIssue extends GHObject implements Reactable {
* Gets labels.
*
* @return the labels
* @throws IOException
* the io exception
*/
public Collection<GHLabel> getLabels() throws IOException {
public Collection<GHLabel> getLabels() {
if (labels == null) {
return Collections.emptyList();
}
@@ -239,7 +236,7 @@ public class GHIssue extends GHObject implements Reactable {
}
private void editIssue(String key, Object value) throws IOException {
root.createRequest().with(key, value).method("PATCH").withUrlPath(getIssuesApiRoute()).send();
root.createRequest().withNullable(key, value).method("PATCH").withUrlPath(getIssuesApiRoute()).send();
}
/**
@@ -296,9 +293,9 @@ public class GHIssue extends GHObject implements Reactable {
*/
public void setMilestone(GHMilestone milestone) throws IOException {
if (milestone == null) {
editNullable("milestone", null);
editIssue("milestone", null);
} else {
edit("milestone", milestone.getNumber());
editIssue("milestone", milestone.getNumber());
}
}
@@ -450,7 +447,7 @@ public class GHIssue extends GHObject implements Reactable {
.toIterable(GHIssueComment[].class, item -> item.wrapUp(this));
}
@Preview
@Preview(SQUIRREL_GIRL)
@Deprecated
public GHReaction createReaction(ReactionContent content) throws IOException {
return root.createRequest()
@@ -462,7 +459,7 @@ public class GHIssue extends GHObject implements Reactable {
.wrap(root);
}
@Preview
@Preview(SQUIRREL_GIRL)
@Deprecated
public PagedIterable<GHReaction> listReactions() {
return root.createRequest()

View File

@@ -26,7 +26,7 @@ package org.kohsuke.github;
import java.io.IOException;
import java.net.URL;
import static org.kohsuke.github.Previews.*;
import static org.kohsuke.github.internal.Previews.SQUIRREL_GIRL;
/**
* Comment to the issue
@@ -126,7 +126,7 @@ public class GHIssueComment extends GHObject implements Reactable {
owner.root.createRequest().method("DELETE").withUrlPath(getApiRoute()).send();
}
@Preview
@Preview(SQUIRREL_GIRL)
@Deprecated
public GHReaction createReaction(ReactionContent content) throws IOException {
return owner.root.createRequest()
@@ -138,7 +138,7 @@ public class GHIssueComment extends GHObject implements Reactable {
.wrap(owner.root);
}
@Preview
@Preview(SQUIRREL_GIRL)
@Deprecated
public PagedIterable<GHReaction> listReactions() {
return owner.root.createRequest()

View File

@@ -9,9 +9,7 @@ import java.util.Date;
*
* @author Martin van Zijl
*/
public class GHIssueEvent {
private GitHub root;
public class GHIssueEvent extends GitHubInteractiveObject {
private long id;
private String node_id;
private String url;

View File

@@ -31,4 +31,4 @@ package org.kohsuke.github;
*/
public enum GHIssueState {
OPEN, CLOSED, ALL
}
}

View File

@@ -9,9 +9,7 @@ import org.apache.commons.lang3.builder.ToStringBuilder;
* @author Kohsuke Kawaguchi
*/
@SuppressFBWarnings(value = "UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", justification = "JSON API")
public class GHKey {
/* package almost final */ GitHub root;
public class GHKey extends GitHubInteractiveObject {
protected String url, key, title;
protected boolean verified;
protected int id;

View File

@@ -20,7 +20,7 @@ import javax.annotation.Nonnull;
* @see GHIssue#getLabels() GHIssue#getLabels()
* @see GHRepository#listLabels() GHRepository#listLabels()
*/
public class GHLabel {
public class GHLabel extends GitHubInteractiveObject {
@Nonnull
private String url, name, color;
@@ -28,9 +28,6 @@ public class GHLabel {
@CheckForNull
private String description;
@Nonnull
private final GitHub root;
@JsonCreator
private GHLabel(@JacksonInject @Nonnull GitHub root) {
this.root = root;
@@ -132,7 +129,7 @@ public class GHLabel {
* @throws IOException
* the io exception
*/
@Preview
@BetaApi
@Deprecated
static Creator create(GHRepository repository) throws IOException {
return new Creator(repository);
@@ -179,7 +176,7 @@ public class GHLabel {
*
* @return a {@link Updater}
*/
@Preview
@BetaApi
@Deprecated
public Updater update() {
return new Updater(this);
@@ -187,10 +184,10 @@ public class GHLabel {
/**
* Begins a single property update.
*
*
* @return a {@link Setter}
*/
@Preview
@BetaApi
@Deprecated
public Setter set() {
return new Setter(this);
@@ -227,7 +224,7 @@ public class GHLabel {
*
* {@link #done()} is called automatically after the property is set.
*/
@Preview
@BetaApi
@Deprecated
public static class Setter extends GHLabelBuilder<GHLabel> {
private Setter(@Nonnull GHLabel base) {
@@ -241,7 +238,7 @@ public class GHLabel {
*
* Consumer must call {@link #done()} to commit changes.
*/
@Preview
@BetaApi
@Deprecated
public static class Updater extends GHLabelBuilder<Updater> {
private Updater(@Nonnull GHLabel base) {
@@ -255,7 +252,7 @@ public class GHLabel {
*
* Consumer must call {@link #done()} to create the new instance.
*/
@Preview
@BetaApi
@Deprecated
public static class Creator extends GHLabelBuilder<Creator> {
private Creator(@Nonnull GHRepository repository) {

View File

@@ -38,21 +38,21 @@ class GHLabelBuilder<S> extends AbstractBuilder<GHLabel, S> {
}
@Nonnull
@Preview
@BetaApi
@Deprecated
public S name(String value) throws IOException {
return with("name", value);
}
@Nonnull
@Preview
@BetaApi
@Deprecated
public S color(String value) throws IOException {
return with("color", value);
}
@Nonnull
@Preview
@BetaApi
@Deprecated
public S description(String value) throws IOException {
return with("description", value);

View File

@@ -44,9 +44,6 @@ import java.util.Objects;
@SuppressFBWarnings(value = { "UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD" },
justification = "JSON API")
public class GHLicense extends GHObject {
@SuppressFBWarnings("IS2_INCONSISTENT_SYNC")
// root is set before the object is returned to the app
/* package almost final */ GitHub root;
// these fields are always present, even in the short form
protected String key, name;

View File

@@ -9,9 +9,7 @@ import java.net.URL;
* @see GitHub#getMyMarketplacePurchases()
* @see GHMarketplaceListAccountBuilder#createRequest()
*/
public class GHMarketplaceAccount {
protected GitHub root;
public class GHMarketplaceAccount extends GitHubInteractiveObject {
private String url;
private long id;
private String login;

View File

@@ -8,8 +8,7 @@ import java.io.IOException;
* @author Paulo Miguel Almeida
* @see GHMarketplacePlan#listAccounts()
*/
public class GHMarketplaceListAccountBuilder {
private final GitHub root;
public class GHMarketplaceListAccountBuilder extends GitHubInteractiveObject {
private final Requester builder;
private final long planId;

View File

@@ -10,8 +10,7 @@ import java.util.Date;
* @author Paulo Miguel Almeida
* @see GHMarketplaceListAccountBuilder#createRequest()
*/
public class GHMarketplacePendingChange {
private GitHub root;
public class GHMarketplacePendingChange extends GitHubInteractiveObject {
private long id;
@SuppressFBWarnings(value = "UWF_UNWRITTEN_FIELD", justification = "Field comes from JSON deserialization")
private Long unitCount;

View File

@@ -11,9 +11,7 @@ import java.util.List;
* @author Paulo Miguel Almeida
* @see GitHub#listMarketplacePlans()
*/
public class GHMarketplacePlan {
private GitHub root;
public class GHMarketplacePlan extends GitHubInteractiveObject {
private String url;
private String accountsUrl;
private long id;

View File

@@ -10,9 +10,8 @@ import java.util.Date;
* @author Paulo Miguel Almeida
* @see GHMarketplaceListAccountBuilder#createRequest() GHMarketplaceListAccountBuilder#createRequest()
*/
public class GHMarketplacePurchase {
public class GHMarketplacePurchase extends GitHubInteractiveObject {
private GitHub root;
private String billingCycle;
private String nextBillingDate;
private boolean onFreeTrial;

View File

@@ -10,8 +10,7 @@ import java.util.Date;
* @author Paulo Miguel Almeida
* @see GitHub#getMyMarketplacePurchases()
*/
public class GHMarketplaceUserPurchase {
protected GitHub root;
public class GHMarketplaceUserPurchase extends GitHubInteractiveObject {
private String billingCycle;
private String nextBillingDate;
private boolean onFreeTrial;

View File

@@ -10,9 +10,7 @@ import java.util.Locale;
* @author Kohsuke Kawaguchi
* @see GHMyself#listOrgMemberships() GHMyself#listOrgMemberships()
*/
public class GHMembership /* extends GHObject --- but it doesn't have id, created_at, etc. */ {
GitHub root;
public class GHMembership extends GitHubInteractiveObject {
String url;
String state;
String role;

View File

@@ -11,7 +11,6 @@ import java.util.Locale;
* @author Yusuke Kokubo
*/
public class GHMilestone extends GHObject {
GitHub root;
GHRepository owner;
GHUser creator;

View File

@@ -7,4 +7,4 @@ package org.kohsuke.github;
*/
public enum GHMilestoneState {
OPEN, CLOSED
}
}

View File

@@ -23,9 +23,7 @@ import java.util.NoSuchElementException;
* @see GitHub#listNotifications() GitHub#listNotifications()
* @see GHRepository#listNotifications() GHRepository#listNotifications()
*/
public class GHNotificationStream implements Iterable<GHThread> {
private final GitHub root;
public class GHNotificationStream extends GitHubInteractiveObject implements Iterable<GHThread> {
private Boolean all, participating;
private String since;
private String apiUrl;

View File

@@ -20,7 +20,7 @@ import javax.annotation.CheckForNull;
*/
@SuppressFBWarnings(value = { "UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD" },
justification = "JSON API")
public abstract class GHObject {
public abstract class GHObject extends GitHubInteractiveObject {
/**
* Capture response HTTP headers on the state object.
*/
@@ -38,7 +38,7 @@ public abstract class GHObject {
/**
* Called by Jackson
*
*
* @param responseInfo
* the {@link GitHubResponse.ResponseInfo} to get headers from.
*/

View File

@@ -10,7 +10,7 @@ import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import static org.kohsuke.github.Previews.INERTIA;
import static org.kohsuke.github.internal.Previews.INERTIA;
/**
* The type GHOrganization.
@@ -97,7 +97,7 @@ public class GHOrganization extends GHPerson {
* @return the gh create repository builder
*/
public GHCreateRepositoryBuilder createRepository(String name) {
return new GHCreateRepositoryBuilder(root, "/orgs/" + login + "/repos", name);
return new GHCreateRepositoryBuilder(name, root, "/orgs/" + login + "/repos");
}
/**
@@ -181,7 +181,7 @@ public class GHOrganization extends GHPerson {
/**
* Finds a team that has the given slug in its {@link GHTeam#getSlug()}
*
*
* @param slug
* the slug
* @return the team by slug
@@ -421,7 +421,7 @@ public class GHOrganization extends GHPerson {
* The enum Permission.
*/
public enum Permission {
ADMIN, PUSH, PULL
ADMIN, MAINTAIN, PUSH, TRIAGE, PULL
}
/**

View File

@@ -28,7 +28,7 @@ import java.util.Locale;
/**
* Permission for a user in a repository.
*
*
* @see <a href="https://developer.github.com/v3/repos/collaborators/#review-a-users-permission-level">API</a>
*/
class GHPermission {

View File

@@ -18,16 +18,15 @@ import java.util.TreeMap;
* @author Kohsuke Kawaguchi
*/
public abstract class GHPerson extends GHObject {
/* package almost final */ GitHub root;
// core data fields that exist even for "small" user data (such as the user info in pull request)
protected String login, avatar_url;
// other fields (that only show up in full data)
protected String location, blog, email, name, company, type;
protected String location, blog, email, bio, name, company, type, twitter_username;
protected String html_url;
protected int followers, following, public_repos, public_gists;
protected boolean site_admin;
protected boolean site_admin, hireable;
// other fields (that only show up in full data) that require privileged scope
protected Integer total_private_repos;
@@ -154,10 +153,7 @@ public abstract class GHPerson extends GHObject {
*/
public GHRepository getRepository(String name) throws IOException {
try {
return root.createRequest()
.withUrlPath("/repos/" + login + '/' + name)
.fetch(GHRepository.class)
.wrap(root);
return GHRepository.read(root, login, name);
} catch (FileNotFoundException e) {
return null;
}
@@ -237,6 +233,18 @@ public abstract class GHPerson extends GHObject {
return location;
}
/**
* Gets the Twitter Username of this user, like "GitHub"
*
* @return the Twitter username
* @throws IOException
* the io exception
*/
public String getTwitterUsername() throws IOException {
populate();
return twitter_username;
}
public Date getCreatedAt() throws IOException {
populate();
return super.getCreatedAt();

View File

@@ -28,7 +28,7 @@ import java.io.IOException;
import java.net.URL;
import java.util.Locale;
import static org.kohsuke.github.Previews.INERTIA;
import static org.kohsuke.github.internal.Previews.INERTIA;
/**
* A GitHub project.
@@ -37,7 +37,6 @@ import static org.kohsuke.github.Previews.INERTIA;
* @see <a href="https://developer.github.com/v3/projects/">Projects</a>
*/
public class GHProject extends GHObject {
protected GitHub root;
protected GHObject owner;
private String owner_url;
@@ -80,10 +79,8 @@ public class GHProject extends GHObject {
} else if (owner_url.contains("/users/")) {
owner = root.createRequest().withUrlPath(getOwnerUrl().getPath()).fetch(GHUser.class).wrapUp(root);
} else if (owner_url.contains("/repos/")) {
owner = root.createRequest()
.withUrlPath(getOwnerUrl().getPath())
.fetch(GHRepository.class)
.wrap(root);
String[] pathElements = getOwnerUrl().getPath().split("/");
owner = GHRepository.read(root, pathElements[1], pathElements[2]);
}
} catch (FileNotFoundException e) {
return null;
@@ -313,4 +310,4 @@ public class GHProject extends GHObject {
.fetch(GHProjectColumn.class)
.wrap(this);
}
}
}

View File

@@ -6,7 +6,7 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;
import static org.kohsuke.github.Previews.INERTIA;
import static org.kohsuke.github.internal.Previews.INERTIA;
/**
* The type GHProjectCard.
@@ -14,7 +14,6 @@ import static org.kohsuke.github.Previews.INERTIA;
* @author Gunnar Skjold
*/
public class GHProjectCard extends GHObject {
private GitHub root;
private GHProject project;
private GHProjectColumn column;

View File

@@ -4,7 +4,7 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;
import static org.kohsuke.github.Previews.INERTIA;
import static org.kohsuke.github.internal.Previews.INERTIA;
/**
* The type GHProjectColumn.
@@ -12,7 +12,6 @@ import static org.kohsuke.github.Previews.INERTIA;
* @author Gunnar Skjold
*/
public class GHProjectColumn extends GHObject {
protected GitHub root;
protected GHProject project;
private String name;

View File

@@ -29,7 +29,6 @@ import java.io.IOException;
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;
@@ -37,7 +36,8 @@ import java.util.Objects;
import javax.annotation.CheckForNull;
import static org.kohsuke.github.Previews.SHADOW_CAT;
import static org.kohsuke.github.internal.Previews.LYDIAN;
import static org.kohsuke.github.internal.Previews.SHADOW_CAT;
/**
* A pull request.
@@ -72,13 +72,6 @@ public class GHPullRequest extends GHIssue implements Refreshable {
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 on this
* object to fill in those missing details
*/
private transient boolean fetchedIssueDetails;
GHPullRequest wrapUp(GHRepository owner) {
this.wrap(owner);
return wrapUp(owner.root);
@@ -177,12 +170,6 @@ public class GHPullRequest extends GHIssue implements Refreshable {
return GitHubClient.parseDate(merged_at);
}
@Override
public Collection<GHLabel> getLabels() throws IOException {
fetchIssue();
return super.getLabels();
}
@Override
public GHUser getClosedBy() {
return null;
@@ -565,6 +552,41 @@ public class GHPullRequest extends GHIssue implements Refreshable {
.send();
}
/**
* Set the base branch on the pull request
*
* @param newBaseBranch
* the name of the new base branch
* @throws IOException
* the io exception
* @return the updated pull request
*/
public GHPullRequest setBaseBranch(String newBaseBranch) throws IOException {
return root.createRequest()
.method("PATCH")
.with("base", newBaseBranch)
.withUrlPath(getApiRoute())
.fetch(GHPullRequest.class)
.wrapUp(root);
}
/**
* Updates the branch. The same as pressing the button in the web GUI.
*
* @throws IOException
* the io exception
*/
@Preview(LYDIAN)
@Deprecated
public void updateBranch() throws IOException {
root.createRequest()
.withPreview(LYDIAN)
.method("PUT")
.with("expected_head_sha", head.getSha())
.withUrlPath(getApiRoute() + "/update-branch")
.send();
}
/**
* Merge this pull request.
* <p>
@@ -626,10 +648,4 @@ public class GHPullRequest extends GHIssue implements Refreshable {
MERGE, SQUASH, REBASE
}
private void fetchIssue() throws IOException {
if (!fetchedIssueDetails) {
root.createRequest().withUrlPath(getIssuesApiRoute()).fetchInto(this);
fetchedIssueDetails = true;
}
}
}

View File

@@ -0,0 +1,86 @@
package org.kohsuke.github;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
/**
* Wrapper to define changed fields on pull_request action="edited"
*
* @see GHEventPayload.PullRequest
*/
@SuppressFBWarnings("UWF_UNWRITTEN_FIELD")
public class GHPullRequestChanges {
private GHCommitPointer base;
private GHFrom title;
private GHFrom body;
/**
* Old target branch for pull request.
*
* @return old target branch info (or null if not changed)
*/
public GHCommitPointer getBase() {
return base;
}
/**
* Old pull request title.
*
* @return old pull request title (or null if not changed)
*/
public GHFrom getTitle() {
return title;
}
/**
* Old pull request body.
*
* @return old pull request body (or null if not changed)
*/
public GHFrom getBody() {
return body;
}
/**
* @see org.kohsuke.github.GHCommitPointer
*/
public static class GHCommitPointer {
private GHFrom ref;
private GHFrom sha;
/**
* Named ref to the commit. This (from value) appears to be a "short ref" that doesn't include "refs/heads/"
* portion.
*
* @return the ref
*/
public GHFrom getRef() {
return ref;
}
/**
* SHA1 of the commit.
*
* @return sha
*/
public GHFrom getSha() {
return sha;
}
}
/**
* Wrapper for changed values.
*/
public static class GHFrom {
private String from;
/**
* Previous value that was changed.
*
* @return previous value
*/
public String getFrom() {
return from;
}
}
}

View File

@@ -1,6 +1,6 @@
package org.kohsuke.github;
import static org.kohsuke.github.Previews.SHADOW_CAT;
import static org.kohsuke.github.internal.Previews.SHADOW_CAT;
/**
* Lists up pull requests with some filtering and sorting.

View File

@@ -46,6 +46,7 @@ public class GHPullRequestReview extends GHObject {
private String commit_id;
private GHPullRequestReviewState state;
private String submitted_at;
private String html_url;
GHPullRequestReview wrapUp(GHPullRequest owner) {
this.owner = owner;
@@ -102,7 +103,7 @@ public class GHPullRequestReview extends GHObject {
@Override
public URL getHtmlUrl() {
return null;
return GitHubClient.parseURL(html_url);
}
/**

View File

@@ -28,7 +28,7 @@ import java.net.URL;
import javax.annotation.CheckForNull;
import static org.kohsuke.github.Previews.*;
import static org.kohsuke.github.internal.Previews.SQUIRREL_GIRL;
/**
* Review comment to the pull request
@@ -198,7 +198,7 @@ public class GHPullRequestReviewComment extends GHObject implements Reactable {
.wrapUp(owner);
}
@Preview
@Preview(SQUIRREL_GIRL)
@Deprecated
public GHReaction createReaction(ReactionContent content) throws IOException {
return owner.root.createRequest()
@@ -210,7 +210,7 @@ public class GHPullRequestReviewComment extends GHObject implements Reactable {
.wrap(owner.root);
}
@Preview
@Preview(SQUIRREL_GIRL)
@Deprecated
public PagedIterable<GHReaction> listReactions() {
return owner.root.createRequest()

View File

@@ -7,8 +7,7 @@ package org.kohsuke.github;
* the type parameter
* @author Kohsuke Kawaguchi
*/
public abstract class GHQueryBuilder<T> {
protected final GitHub root;
public abstract class GHQueryBuilder<T> extends GitHubInteractiveObject {
protected final Requester req;
GHQueryBuilder(GitHub root) {

View File

@@ -6,6 +6,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.apache.commons.lang3.StringUtils;
import java.time.Duration;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
@@ -29,7 +30,7 @@ public class GHRateLimit {
/**
* Remaining calls that can be made.
*
* @deprecated This value should never have been made public. Use {@link #getRemaining()}
* @deprecated This field should never have been made public. Use {@link #getRemaining()}
*/
@Deprecated
public int remaining;
@@ -37,7 +38,7 @@ public class GHRateLimit {
/**
* Allotted API call per hour.
*
* @deprecated This value should never have been made public. Use {@link #getLimit()}
* @deprecated This field should never have been made public. Use {@link #getLimit()}
*/
@Deprecated
public int limit;
@@ -48,7 +49,7 @@ public class GHRateLimit {
* date. To use this field in any meaningful way, it must be converted to a long using {@link Date#getTime()}
* multiplied by 1000.
*
* @deprecated This value should never have been made public. Use {@link #getResetDate()}
* @deprecated This field should never have been made public. Use {@link #getResetDate()}
*/
@Deprecated
public Date reset;
@@ -65,17 +66,58 @@ public class GHRateLimit {
@Nonnull
private final Record integrationManifest;
/**
* The default GHRateLimit provided to new {@link GitHubClient}s.
*
* Contains all expired records that will cause {@link GitHubClient#rateLimit(RateLimitTarget)} to refresh with new
* data when called.
*
* Private, but made internal for testing.
*/
@Nonnull
static GHRateLimit Unknown() {
return new GHRateLimit(new UnknownLimitRecord(),
new UnknownLimitRecord(),
new UnknownLimitRecord(),
new UnknownLimitRecord());
}
static final GHRateLimit DEFAULT = new GHRateLimit(UnknownLimitRecord.DEFAULT,
UnknownLimitRecord.DEFAULT,
UnknownLimitRecord.DEFAULT,
UnknownLimitRecord.DEFAULT);
/**
* Creates a new {@link GHRateLimit} from a single record for the specified endpoint with place holders for other
* records.
*
* This is used to create {@link GHRateLimit} instances that can merged with other instances.
*
* @param record
* the rate limit record. Can be a regular {@link Record} constructed from header information or an
* {@link UnknownLimitRecord} placeholder.
* @param rateLimitTarget
* which rate limit record to fill
* @return a new {@link GHRateLimit} instance containing the supplied record
*/
@Nonnull
static GHRateLimit fromHeaderRecord(Record header) {
return new GHRateLimit(header, new UnknownLimitRecord(), new UnknownLimitRecord(), new UnknownLimitRecord());
static GHRateLimit fromRecord(@Nonnull Record record, @Nonnull RateLimitTarget rateLimitTarget) {
if (rateLimitTarget == RateLimitTarget.CORE || rateLimitTarget == RateLimitTarget.NONE) {
return new GHRateLimit(record,
UnknownLimitRecord.DEFAULT,
UnknownLimitRecord.DEFAULT,
UnknownLimitRecord.DEFAULT);
} else if (rateLimitTarget == RateLimitTarget.SEARCH) {
return new GHRateLimit(UnknownLimitRecord.DEFAULT,
record,
UnknownLimitRecord.DEFAULT,
UnknownLimitRecord.DEFAULT);
} else if (rateLimitTarget == RateLimitTarget.GRAPHQL) {
return new GHRateLimit(UnknownLimitRecord.DEFAULT,
UnknownLimitRecord.DEFAULT,
record,
UnknownLimitRecord.DEFAULT);
} else if (rateLimitTarget == RateLimitTarget.INTEGRATION_MANIFEST) {
return new GHRateLimit(UnknownLimitRecord.DEFAULT,
UnknownLimitRecord.DEFAULT,
UnknownLimitRecord.DEFAULT,
record);
} else {
throw new IllegalArgumentException("Unknown rate limit target: " + rateLimitTarget.toString());
}
}
@JsonCreator
@@ -142,7 +184,7 @@ public class GHRateLimit {
}
/**
* Whether the rate limit reset date for this instance has passed.
* Whether the reset date for the Core API rate limit has passed.
*
* @return true if the rate limit reset date has passed. Otherwise false.
* @since 1.100
@@ -152,7 +194,7 @@ public class GHRateLimit {
}
/**
* The core object provides your rate limit status for all non-search-related resources in the REST API.
* The core object provides the rate limit status for all non-search-related resources in the REST API.
*
* @return a rate limit record
* @since 1.100
@@ -163,42 +205,43 @@ public class GHRateLimit {
}
/**
* The search object provides your rate limit status for the Search API. TODO: integrate with header limit updating.
* Issue #605.
* The search record provides the rate limit status for the Search API.
*
* @return a rate limit record
* @since 1.115
*/
@Nonnull
Record getSearch() {
public Record getSearch() {
return search;
}
/**
* The graphql object provides your rate limit status for the GraphQL API. TODO: integrate with header limit
* updating. Issue #605.
* The graphql record provides the rate limit status for the GraphQL API.
*
* @return a rate limit record
* @since 1.115
*/
@Nonnull
Record getGraphQL() {
public Record getGraphQL() {
return graphql;
}
/**
* The integration_manifest object provides your rate limit status for the GitHub App Manifest code conversion
* endpoint. TODO: integrate with header limit updating. Issue #605.
* The integration manifest record provides the rate limit status for the GitHub App Manifest code conversion
* endpoint.
*
* @return a rate limit record
* @since 1.115
*/
@Nonnull
Record getIntegrationManifest() {
public Record getIntegrationManifest() {
return integrationManifest;
}
@Override
public String toString() {
return "GHRateLimit {" + "core " + getCore().toString() + "search " + getSearch().toString() + "graphql "
+ getGraphQL().toString() + "integrationManifest " + getIntegrationManifest().toString() + '}';
return "GHRateLimit {" + "core " + getCore().toString() + ", search " + getSearch().toString() + ", graphql "
+ getGraphQL().toString() + ", integrationManifest " + getIntegrationManifest().toString() + "}";
}
@Override
@@ -221,44 +264,111 @@ public class GHRateLimit {
}
/**
* Gets the appropriate {@link Record} for a particular url path.
* Merge a {@link GHRateLimit} with another one to create a new {@link GHRateLimit} keeping the latest
* {@link Record}s from each.
*
* @param urlPath
* the url path of the request
* @return the {@link Record} for a url path.
* @param newLimit
* {@link GHRateLimit} with potentially updated {@link Record}s.
* @return a merged {@link GHRateLimit} with the latest {@link Record}s from these two instances. If the merged
* instance is equal to the current instance, the current instance is returned.
*/
@Nonnull
Record getRecordForUrlPath(@Nonnull String urlPath) {
if (urlPath.equals("/rate_limit")) {
return new UnknownLimitRecord();
} else if (urlPath.startsWith("/search")) {
return getSearch();
} else if (urlPath.startsWith("/graphql")) {
return getGraphQL();
} else if (urlPath.startsWith("/app-manifests")) {
return getIntegrationManifest();
} else {
GHRateLimit getMergedRateLimit(@Nonnull GHRateLimit newLimit) {
GHRateLimit merged = new GHRateLimit(getCore().currentOrUpdated(newLimit.getCore()),
getSearch().currentOrUpdated(newLimit.getSearch()),
getGraphQL().currentOrUpdated(newLimit.getGraphQL()),
getIntegrationManifest().currentOrUpdated(newLimit.getIntegrationManifest()));
if (merged.equals(this)) {
merged = this;
}
return merged;
}
/**
* Gets the specified {@link Record}.
*
* {@link RateLimitTarget#NONE} will return {@link UnknownLimitRecord#DEFAULT} to prevent any clients from
* accidentally waiting on that record to reset before continuing.
*
* @param rateLimitTarget
* the target rate limit record
* @return the target {@link Record} from this instance.
*/
@Nonnull
Record getRecord(@Nonnull RateLimitTarget rateLimitTarget) {
if (rateLimitTarget == RateLimitTarget.CORE) {
return getCore();
} else if (rateLimitTarget == RateLimitTarget.SEARCH) {
return getSearch();
} else if (rateLimitTarget == RateLimitTarget.GRAPHQL) {
return getGraphQL();
} else if (rateLimitTarget == RateLimitTarget.INTEGRATION_MANIFEST) {
return getIntegrationManifest();
} else if (rateLimitTarget == RateLimitTarget.NONE) {
return UnknownLimitRecord.DEFAULT;
} else {
throw new IllegalArgumentException("Unknown rate limit target: " + rateLimitTarget.toString());
}
}
/**
* A limit record used as a placeholder when the the actual limit is not known.
* <p>
* Has a large limit and long duration so that it will doesn't expire too often.
*
* @since 1.100
*/
public static class UnknownLimitRecord extends Record {
// One hour
private static final long unknownLimitResetSeconds = 60L * 60L;
private static final long defaultUnknownLimitResetSeconds = Duration.ofSeconds(30).getSeconds();
/**
* The number of seconds until a {@link UnknownLimitRecord} will expire.
*
* This is set to a somewhat short duration, rather than a long one. This avoids
* {@link {@link GitHubClient#rateLimit(RateLimitTarget)}} requesting rate limit updates continuously, but also
* avoids holding on to stale unknown records indefinitely.
*
* When merging {@link GHRateLimit} instances, {@link UnknownLimitRecord}s will be superseded by incoming
* regular {@link Record}s.
*
* @see GHRateLimit#getMergedRateLimit(GHRateLimit)
*/
static long unknownLimitResetSeconds = defaultUnknownLimitResetSeconds;
static final int unknownLimit = 1000000;
static final int unknownRemaining = 999999;
private UnknownLimitRecord() {
super(unknownLimit, unknownRemaining, System.currentTimeMillis() / 1000L + unknownLimitResetSeconds);
// The default UnknownLimitRecord is an expired record.
private static final UnknownLimitRecord DEFAULT = new UnknownLimitRecord(Long.MIN_VALUE);
// The starting current UnknownLimitRecord is an expired record.
private static UnknownLimitRecord current = DEFAULT;
/**
* Create a new unknown record that resets at the specified time.
*
* @param resetEpochSeconds
* the epoch second time when this record will expire.
*/
private UnknownLimitRecord(long resetEpochSeconds) {
super(unknownLimit, unknownRemaining, resetEpochSeconds);
}
static synchronized Record current() {
if (current.isExpired()) {
current = new UnknownLimitRecord(System.currentTimeMillis() / 1000L + unknownLimitResetSeconds);
}
return current;
}
/**
* Reset the current UnknownLimitRecord. For use during testing only.
*/
static synchronized void reset() {
current = DEFAULT;
unknownLimitResetSeconds = defaultUnknownLimitResetSeconds;
}
}
@@ -274,14 +384,12 @@ public class GHRateLimit {
private final int remaining;
/**
* Allotted API call per hour.
* Allotted API call per time period.
*/
private final int limit;
/**
* The time at which the current rate limit window resets in UTC epoch seconds.
*
* This is the raw value returned by the server.
*/
private final long resetEpochSeconds;
@@ -291,9 +399,11 @@ public class GHRateLimit {
private final long createdAtEpochSeconds = System.currentTimeMillis() / 1000;
/**
* The time at which the rate limit will reset. This value is calculated based on
* {@link #getResetEpochSeconds()} by calling {@link #calculateResetDate}. If the clock on the local machine not
* synchronized with the server clock, this time value will be adjusted to match the local machine's clock.
* The date at which the rate limit will reset, adjusted to local machine time if the local machine's clock not
* synchronized with to the same clock as the GitHub server.
*
* @see #calculateResetDate(String)
* @see #getResetDate()
*/
@Nonnull
private final Date resetDate;
@@ -341,12 +451,58 @@ public class GHRateLimit {
this.resetDate = calculateResetDate(updatedAt);
}
/**
* Determine if the current {@link Record} is outdated compared to another. Rate Limit dates are only accurate
* to the second, so we look at other information in the record as well.
*
* {@link Record}s with earlier {@link #getResetEpochSeconds()} are replaced by those with later.
* {@link Record}s with the same {@link #getResetEpochSeconds()} are replaced by those with less remaining
* count.
*
* {@link UnknownLimitRecord}s compare with each other like regular {@link Record}s.
*
* {@link Record}s are replaced by {@link UnknownLimitRecord}s only when the current {@link Record} is expired
* and the {@link UnknownLimitRecord} is not. Otherwise Regular {@link Record}s are not replaced by
* {@link UnknownLimitRecord}s.
*
* Expiration is only considered after other checks, meaning expired records may sometimes be replaced by other
* expired records.
*
* @param other
* the other {@link Record}
* @return the {@link Record} that is most current
*/
Record currentOrUpdated(@Nonnull Record other) {
// This set of checks avoids most calls to isExpired()
// Depends on UnknownLimitRecord.current() to prevent continuous updating of GHRateLimit rateLimit()
if (getResetEpochSeconds() > other.getResetEpochSeconds()
|| (getResetEpochSeconds() == other.getResetEpochSeconds()
&& getRemaining() <= other.getRemaining())) {
// If the current record has a later reset
// or the current record has the same reset and fewer or same requests remaining
// Then it is most recent
return this;
} else if (!(other instanceof UnknownLimitRecord)) {
// If the above is not the case that means other has a later reset
// or the same resent and fewer requests remaining.
// If the other record is not an unknown record, the the other is more recent
return other;
} else if (this.isExpired() && !other.isExpired()) {
// The other is an unknown record.
// If the current record has expired and the other hasn't, return the other.
return other;
}
// If none of the above, the current record is most valid.
return this;
}
/**
* Recalculates the {@link #resetDate} relative to the local machine clock.
* <p>
* {@link RateLimitChecker}s and {@link RateLimitHandler}s use {@link #getResetDate()} to make decisions about
* how long to wait for until for the rate limit to reset. That means that {@link #getResetDate()} needs to be
* accurate to the local machine.
* calculated based on the local machine clock.
* </p>
* <p>
* When we say that the clock on two machines is "synchronized", we mean that the UTC time returned from
@@ -415,7 +571,7 @@ public class GHRateLimit {
* {@link #getResetDate()} or implement a {@link RateLimitChecker} instead.
*
* @return a long representing the time in epoch seconds when the rate limit will reset
* @see #getResetDate() #getResetDate()
* @see #getResetDate()
*/
public long getResetEpochSeconds() {
return resetEpochSeconds;
@@ -424,6 +580,8 @@ public class GHRateLimit {
/**
* Whether the rate limit reset date indicated by this instance is expired
*
* If attempting to wait for the rate limit to reset, consider implementing a {@link RateLimitChecker} instead.
*
* @return true if the rate limit reset date has passed. Otherwise false.
*/
public boolean isExpired() {
@@ -431,8 +589,8 @@ public class GHRateLimit {
}
/**
* Returns the date at which the rate limit will reset, adjusted to local machine time if the local machine's
* clock not synchronized with to the same clock as the GitHub server.
* The date at which the rate limit will reset, adjusted to local machine time if the local machine's clock not
* synchronized with to the same clock as the GitHub server.
*
* If attempting to wait for the rate limit to reset, consider implementing a {@link RateLimitChecker} instead.
*

View File

@@ -3,7 +3,7 @@ package org.kohsuke.github;
import java.io.IOException;
import java.net.URL;
import static org.kohsuke.github.Previews.*;
import static org.kohsuke.github.internal.Previews.SQUIRREL_GIRL;
/**
* Reaction to issue, comment, PR, and so on.
@@ -11,10 +11,9 @@ import static org.kohsuke.github.Previews.*;
* @author Kohsuke Kawaguchi
* @see Reactable
*/
@Preview
@Preview(SQUIRREL_GIRL)
@Deprecated
public class GHReaction extends GHObject {
private GitHub root;
private GHUser user;
private ReactionContent content;

View File

@@ -1,5 +1,6 @@
package org.kohsuke.github;
import com.fasterxml.jackson.databind.JsonMappingException;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.IOException;
@@ -10,9 +11,7 @@ import java.net.URL;
*
* @author Michael Clarke
*/
public class GHRef {
/* package almost final */ GitHub root;
public class GHRef extends GitHubInteractiveObject {
private String ref, url;
private GHObject object;
@@ -90,6 +89,78 @@ public class GHRef {
return this;
}
/**
* Retrive a ref of the given type for the current GitHub repository.
*
* @param repository
* the repository to read from
* @param refName
* eg: heads/branch
* @return refs matching the request type
* @throws IOException
* on failure communicating with GitHub, potentially due to an invalid ref type being requested
*/
static GHRef read(GHRepository repository, String refName) throws IOException {
// Also accept e.g. "refs/heads/branch" for consistency with createRef().
if (refName.startsWith("refs/")) {
refName = refName.replaceFirst("refs/", "");
}
// We would expect this to use `git/ref/%s` but some versions of GHE seem to not support it
// Instead use `git/refs/%s` and check the result actually matches the ref
GHRef result = null;
try {
result = repository.root.createRequest()
.withUrlPath(repository.getApiTailUrl(String.format("git/refs/%s", refName)))
.fetch(GHRef.class)
.wrap(repository.root);
} catch (IOException e) {
// If the parse exception is due to the above returning an array instead of a single ref
// that means the individual ref did not exist. Handled by result check below.
// Otherwise, rethrow.
if (!(e.getCause() instanceof JsonMappingException)) {
throw e;
}
}
// Verify that the ref returned is the one requested
// Used .endsWith(refName) instead of .equals("refs/" + refName) to workaround a GitBucket
// issue where the "ref" field omits the "refs/" prefix. "endsWith()" is functionally
// the same for this scenario - the server refs matching is prefix-based, so
// a ref that ends with the correct string will always be the correct one.
if (result == null || !result.getRef().endsWith(refName)) {
throw new GHFileNotFoundException(String.format("git/refs/%s", refName)
+ " {\"message\":\"Not Found\",\"documentation_url\":\"https://developer.github.com/v3/git/refs/#get-a-reference\"}");
}
return result;
}
/**
* Retrieves all refs of the given type for the current GitHub repository.
*
* @param repository
* the repository to read from
* @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
*/
static PagedIterable<GHRef> readMatching(GHRepository repository, String refType) throws IOException {
if (refType.startsWith("refs/")) {
refType = refType.replaceFirst("refs/", "");
}
String url = repository.getApiTailUrl(String.format("git/refs/%s", refType));
// if no types, do not end with slash just to be safe.
if (refType.equals("")) {
url = url.substring(0, url.length() - 1);
}
return repository.root.createRequest()
.withUrlPath(url)
.toIterable(GHRef[].class, item -> item.wrap(repository.root));
}
/**
* The type GHObject.
*/

View File

@@ -15,14 +15,15 @@ import static java.lang.String.*;
* Release in a github repository.
*
* @see GHRepository#getReleases() GHRepository#getReleases()
* @see GHRepository#listReleases() () GHRepository#listReleases()
* @see GHRepository#createRelease(String) GHRepository#createRelease(String)
*/
public class GHRelease extends GHObject {
GitHub root;
GHRepository owner;
private String html_url;
private String assets_url;
private List<GHAsset> assets;
private String upload_url;
private String tag_name;
private String target_commitish;
@@ -249,18 +250,44 @@ public class GHRelease extends GHObject {
}
/**
* Gets assets.
* Get the cached assets.
*
* @return the assets
*
* @deprecated This should be the default behavior of {@link #getAssets()} in a future release. This method is
* introduced in addition to enable a transition to using cached asset information while keeping the
* existing logic in place for backwards compatibility.
*/
@Deprecated
public List<GHAsset> assets() {
return assets;
}
/**
* Re-fetch the assets of this release.
*
* @return the assets
* @throws IOException
* the io exception
* @deprecated The behavior of this method will change in a future release. It will then provide cached assets as
* provided by {@link #assets()}. Use {@link #listAssets()} instead to fetch up-to-date information of
* assets.
*/
@Deprecated
public List<GHAsset> getAssets() throws IOException {
return listAssets().toList();
}
/**
* Re-fetch the assets of this release.
*
* @return the assets
* @throws IOException
* the io exception
*/
public List<GHAsset> getAssets() throws IOException {
public PagedIterable<GHAsset> listAssets() throws IOException {
Requester builder = owner.root.createRequest();
return builder.withUrlPath(getApiTailUrl("assets"))
.toIterable(GHAsset[].class, item -> item.wrap(this))
.toList();
return builder.withUrlPath(getApiTailUrl("assets")).toIterable(GHAsset[].class, item -> item.wrap(this));
}
/**

View File

@@ -46,6 +46,7 @@ import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -53,8 +54,16 @@ import java.util.Set;
import java.util.TreeMap;
import java.util.WeakHashMap;
import javax.annotation.Nonnull;
import static java.util.Arrays.*;
import static org.kohsuke.github.Previews.*;
import static org.kohsuke.github.internal.Previews.ANTIOPE;
import static org.kohsuke.github.internal.Previews.ANT_MAN;
import static org.kohsuke.github.internal.Previews.BAPTISTE;
import static org.kohsuke.github.internal.Previews.FLASH;
import static org.kohsuke.github.internal.Previews.INERTIA;
import static org.kohsuke.github.internal.Previews.MERCY;
import static org.kohsuke.github.internal.Previews.SHADOW_CAT;
/**
* A repository on GitHub.
@@ -65,10 +74,11 @@ import static org.kohsuke.github.Previews.*;
@SuppressFBWarnings(value = { "UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD" },
justification = "JSON API")
public class GHRepository extends GHObject {
/* package almost final */ transient GitHub root;
private String nodeId, description, homepage, name, full_name;
private String html_url; // this is the UI
/*
* The license information makes use of the preview API.
*
@@ -77,22 +87,30 @@ public class GHRepository extends GHObject {
private GHLicense license;
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, archived, has_projects;
private boolean allow_squash_merge;
private boolean allow_merge_commit;
private boolean allow_rebase_merge;
private boolean delete_branch_on_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 WeakHashMap<Integer, GHMilestone>();
private String default_branch, language;
private Map<String, GHCommit> commits = new WeakHashMap<String, GHCommit>();
@SkipFromToString
@@ -100,6 +118,12 @@ public class GHRepository extends GHObject {
private GHRepository source, parent;
private Boolean isTemplate;
static GHRepository read(GitHub root, String owner, String name) throws IOException {
return root.createRequest().withUrlPath("/repos/" + owner + '/' + name).fetch(GHRepository.class).wrap(root);
}
/**
* Create deployment gh deployment builder.
*
@@ -146,6 +170,8 @@ public class GHRepository extends GHObject {
.with("task", task)
.with("environment", environment)
.withUrlPath(getApiTailUrl("deployments"))
.withPreview(ANT_MAN)
.withPreview(FLASH)
.toIterable(GHDeployment[].class, item -> item.wrap(this));
}
@@ -161,6 +187,8 @@ public class GHRepository extends GHObject {
public GHDeployment getDeployment(long id) throws IOException {
return root.createRequest()
.withUrlPath(getApiTailUrl("deployments/" + id))
.withPreview(ANT_MAN)
.withPreview(FLASH)
.fetch(GHDeployment.class)
.wrap(this);
}
@@ -681,6 +709,28 @@ public class GHRepository extends GHObject {
return _private;
}
/**
* Is template boolean.
*
* @return the boolean
*/
@Deprecated
@Preview(BAPTISTE)
public boolean isTemplate() {
// isTemplate is still in preview, we do not want to retrieve it unless needed.
if (isTemplate == null) {
try {
populate();
} catch (IOException e) {
// Convert this to a runtime exception to avoid messy method signature
throw new GHException("Could not populate the template setting of the repository", e);
}
// if this somehow is not populated, set it to false;
isTemplate = Boolean.TRUE.equals(isTemplate);
}
return isTemplate;
}
/**
* Has downloads boolean.
*
@@ -775,6 +825,13 @@ public class GHRepository extends GHObject {
return size;
}
/**
* Affiliation of a repository collaborator
*/
public enum CollaboratorAffiliation {
ALL, DIRECT, OUTSIDE
}
/**
* Gets the collaborators on this repository. This set always appear to include the owner.
*
@@ -798,6 +855,19 @@ public class GHRepository extends GHObject {
return listUsers("collaborators");
}
/**
* Lists up the collaborators on this repository.
*
* @param affiliation
* Filter users by affiliation
* @return Users paged iterable
* @throws IOException
* the io exception
*/
public PagedIterable<GHUser> listCollaborators(CollaboratorAffiliation affiliation) throws IOException {
return listUsers(root.createRequest().with("affiliation", affiliation), "collaborators");
}
/**
* Lists all
* <a href="https://help.github.com/articles/assigning-issues-and-pull-requests-to-other-github-users/">the
@@ -845,6 +915,29 @@ public class GHRepository extends GHObject {
return r;
}
/**
* Gets the names of the collaborators on this repository. This method deviates from the principle of this library
* but it works a lot faster than {@link #getCollaborators()}.
*
* @param affiliation
* Filter users by affiliation
* @return the collaborator names
* @throws IOException
* the io exception
*/
public Set<String> getCollaboratorNames(CollaboratorAffiliation affiliation) throws IOException {
Set<String> r = new HashSet<>();
// no initializer - we just want to the logins
PagedIterable<GHUser> users = root.createRequest()
.withUrlPath(getApiTailUrl("collaborators"))
.with("affiliation", affiliation)
.toIterable(GHUser[].class, null);
for (GHUser u : users.toArray()) {
r.add(u.login);
}
return r;
}
/**
* Obtain permission for a given user in this repository.
*
@@ -970,12 +1063,12 @@ public class GHRepository extends GHObject {
@NonNull String method,
@CheckForNull GHOrganization.Permission permission) throws IOException {
Requester requester = root.createRequest().method(method);
if (permission != null) {
requester = requester.with("permission", permission).inBody();
}
for (GHUser user : users) {
// Make sure that the users collection doesn't have any duplicates
for (GHUser user : new LinkedHashSet<GHUser>(users)) {
requester.withUrlPath(getApiTailUrl("collaborators/" + user.getLogin())).send();
}
}
@@ -1000,13 +1093,6 @@ public class GHRepository extends GHObject {
.send();
}
private void edit(String key, String value) throws IOException {
Requester requester = root.createRequest();
if (!key.equals("name"))
requester.with("name", name); // even when we don't change the name, we need to send it in
requester.with(key, value).method("PATCH").withUrlPath(getApiTailUrl("")).send();
}
/**
* Enables or disables the issue tracker for this repository.
*
@@ -1016,7 +1102,7 @@ public class GHRepository extends GHObject {
* the io exception
*/
public void enableIssueTracker(boolean v) throws IOException {
edit("has_issues", String.valueOf(v));
set().issues(v);
}
/**
@@ -1028,7 +1114,7 @@ public class GHRepository extends GHObject {
* the io exception
*/
public void enableProjects(boolean v) throws IOException {
edit("has_projects", String.valueOf(v));
set().projects(v);
}
/**
@@ -1040,7 +1126,7 @@ public class GHRepository extends GHObject {
* the io exception
*/
public void enableWiki(boolean v) throws IOException {
edit("has_wiki", String.valueOf(v));
set().wiki(v);
}
/**
@@ -1052,7 +1138,7 @@ public class GHRepository extends GHObject {
* the io exception
*/
public void enableDownloads(boolean v) throws IOException {
edit("has_downloads", String.valueOf(v));
set().downloads(v);
}
/**
@@ -1064,7 +1150,7 @@ public class GHRepository extends GHObject {
* the io exception
*/
public void renameTo(String name) throws IOException {
edit("name", name);
set().name(name);
}
/**
@@ -1076,7 +1162,7 @@ public class GHRepository extends GHObject {
* the io exception
*/
public void setDescription(String value) throws IOException {
edit("description", value);
set().description(value);
}
/**
@@ -1088,7 +1174,7 @@ public class GHRepository extends GHObject {
* the io exception
*/
public void setHomepage(String value) throws IOException {
edit("homepage", value);
set().homepage(value);
}
/**
@@ -1100,7 +1186,7 @@ public class GHRepository extends GHObject {
* the io exception
*/
public void setDefaultBranch(String value) throws IOException {
edit("default_branch", value);
set().defaultBranch(value);
}
/**
@@ -1112,7 +1198,7 @@ public class GHRepository extends GHObject {
* the io exception
*/
public void setPrivate(boolean value) throws IOException {
edit("private", Boolean.toString(value));
set().private_(value);
}
/**
@@ -1124,7 +1210,7 @@ public class GHRepository extends GHObject {
* the io exception
*/
public void allowSquashMerge(boolean value) throws IOException {
edit("allow_squash_merge", Boolean.toString(value));
set().allowSquashMerge(value);
}
/**
@@ -1136,7 +1222,7 @@ public class GHRepository extends GHObject {
* the io exception
*/
public void allowMergeCommit(boolean value) throws IOException {
edit("allow_merge_commit", Boolean.toString(value));
set().allowMergeCommit(value);
}
/**
@@ -1148,7 +1234,7 @@ public class GHRepository extends GHObject {
* the io exception
*/
public void allowRebaseMerge(boolean value) throws IOException {
edit("allow_rebase_merge", Boolean.toString(value));
set().allowRebaseMerge(value);
}
/**
@@ -1160,7 +1246,7 @@ public class GHRepository extends GHObject {
* the io exception
*/
public void deleteBranchOnMerge(boolean value) throws IOException {
edit("delete_branch_on_merge", Boolean.toString(value));
set().deleteBranchOnMerge(value);
}
/**
@@ -1197,12 +1283,30 @@ public class GHRepository extends GHObject {
* 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
set().archive();
// Generally would not update this record,
// but doing so here since this will result in any other update actions failing
archived = true;
}
/**
* Creates a builder that can be used to bulk update repository settings.
*
* @return the repository updater
*/
public Updater update() {
return new Updater(this);
}
/**
* Creates a builder that can be used to bulk update repository settings.
*
* @return the repository updater
*/
public Setter set() {
return new Setter(this);
}
/**
* Sort orders for listing forks
*/
@@ -1248,8 +1352,9 @@ public class GHRepository extends GHObject {
// this API is asynchronous. we need to wait for a bit
for (int i = 0; i < 10; i++) {
GHRepository r = root.getMyself().getRepository(name);
if (r != null)
if (r != null) {
return r;
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
@@ -1278,8 +1383,9 @@ public class GHRepository extends GHObject {
// this API is asynchronous. we need to wait for a bit
for (int i = 0; i < 10; i++) {
GHRepository r = org.getRepository(name);
if (r != null)
if (r != null) {
return r;
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
@@ -1540,8 +1646,7 @@ public class GHRepository extends GHObject {
* on failure communicating with GitHub, potentially due to an invalid ref type being requested
*/
public PagedIterable<GHRef> listRefs() throws IOException {
final String url = String.format("/repos/%s/%s/git/refs", getOwnerName(), name);
return root.createRequest().withUrlPath(url).toIterable(GHRef[].class, item -> item.wrap(root));
return listRefs("");
}
/**
@@ -1567,11 +1672,7 @@ public class GHRepository extends GHObject {
* on failure communicating with GitHub, potentially due to an invalid ref type being requested
*/
public PagedIterable<GHRef> listRefs(String refType) throws IOException {
if (refType.startsWith("refs/")) {
refType = refType.replaceFirst("refs/", "");
}
final String url = String.format("/repos/%s/%s/git/refs/%s", getOwnerName(), name, refType);
return root.createRequest().withUrlPath(url).toIterable(GHRef[].class, item -> item.wrap(root));
return GHRef.readMatching(this, refType);
}
/**
@@ -1584,15 +1685,7 @@ public class GHRepository extends GHObject {
* on failure communicating with GitHub, potentially due to an invalid ref type being requested
*/
public GHRef getRef(String refName) throws IOException {
// Also accept e.g. "refs/heads/branch" for consistency with createRef().
if (refName.startsWith("refs/")) {
refName = refName.replaceFirst("refs/", "");
}
return root.createRequest()
.withUrlPath(getApiTailUrl(String.format("git/ref/%s", refName)))
.fetch(GHRef.class)
.wrap(root);
return GHRef.read(this, refName);
}
/**
@@ -1759,6 +1852,20 @@ public class GHRepository extends GHObject {
.toIterable(GHCommitComment[].class, item -> item.wrap(this));
}
/**
* Lists all comments on a specific commit.
*
* @param commitSha
* the hash of the commit
*
* @return the paged iterable
*/
public PagedIterable<GHCommitComment> listCommitComments(String commitSha) {
return root.createRequest()
.withUrlPath(String.format("/repos/%s/%s/commits/%s/comments", getOwnerName(), name, commitSha))
.toIterable(GHCommitComment[].class, item -> item.wrap(this));
}
/**
* Gets the basic license details for the repository.
* <p>
@@ -1835,7 +1942,7 @@ public class GHRepository extends GHObject {
* @see <a href="https://developer.github.com/v3/checks/runs/#list-check-runs-for-a-specific-ref">List check runs
* for a specific ref</a>
*/
@Preview
@Preview(ANTIOPE)
@Deprecated
public PagedIterable<GHCheckRun> getCheckRuns(String ref) throws IOException {
GitHubRequest request = root.createRequest()
@@ -1909,12 +2016,25 @@ public class GHRepository extends GHObject {
* the commit hash
* @return a builder which you should customize, then call {@link GHCheckRunBuilder#create}
*/
@Preview
@Preview(ANTIOPE)
@Deprecated
public @NonNull GHCheckRunBuilder createCheckRun(@NonNull String name, @NonNull String headSHA) {
return new GHCheckRunBuilder(this, name, headSHA);
}
/**
* Updates an existing check run.
*
* @param checkId
* the existing checkId
* @return a builder which you should customize, then call {@link GHCheckRunBuilder#create}
*/
@Preview(BAPTISTE)
@Deprecated
public @NonNull GHCheckRunBuilder updateCheckRun(long checkId) {
return new GHCheckRunBuilder(this, checkId);
}
/**
* Lists repository events.
*
@@ -2032,9 +2152,11 @@ public class GHRepository extends GHObject {
}
private PagedIterable<GHUser> listUsers(final String suffix) {
return root.createRequest()
.withUrlPath(getApiTailUrl(suffix))
.toIterable(GHUser[].class, item -> item.wrapUp(root));
return listUsers(root.createRequest(), suffix);
}
private PagedIterable<GHUser> listUsers(Requester requester, final String suffix) {
return requester.withUrlPath(getApiTailUrl(suffix)).toIterable(GHUser[].class, item -> item.wrapUp(root));
}
/**
@@ -2097,7 +2219,12 @@ public class GHRepository extends GHObject {
justification = "It causes a performance degradation, but we have already exposed it to the API")
@Deprecated
public Set<URL> getPostCommitHooks() {
return postCommitHooks;
synchronized (this) {
if (postCommitHooks == null) {
postCommitHooks = setupPostCommitHooks();
}
return postCommitHooks;
}
}
/**
@@ -2106,57 +2233,63 @@ public class GHRepository extends GHObject {
@SuppressFBWarnings(value = "DMI_COLLECTION_OF_URLS",
justification = "It causes a performance degradation, but we have already exposed it to the API")
@SkipFromToString
private final Set<URL> postCommitHooks = new AbstractSet<URL>() {
private List<URL> getPostCommitHooks() {
try {
List<URL> r = new ArrayList<>();
for (GHHook h : getHooks()) {
if (h.getName().equals("web")) {
r.add(new URL(h.getConfig().get("url")));
private /* final */ transient Set<URL> postCommitHooks;
@SuppressFBWarnings(value = "DMI_COLLECTION_OF_URLS",
justification = "It causes a performance degradation, but we have already exposed it to the API")
private Set<URL> setupPostCommitHooks() {
return new AbstractSet<URL>() {
private List<URL> getPostCommitHooks() {
try {
List<URL> r = new ArrayList<>();
for (GHHook h : getHooks()) {
if (h.getName().equals("web")) {
r.add(new URL(h.getConfig().get("url")));
}
}
return r;
} catch (IOException e) {
throw new GHException("Failed to retrieve post-commit hooks", e);
}
return r;
} catch (IOException e) {
throw new GHException("Failed to retrieve post-commit hooks", e);
}
}
@Override
public Iterator<URL> iterator() {
return getPostCommitHooks().iterator();
}
@Override
public int size() {
return getPostCommitHooks().size();
}
@Override
public boolean add(URL url) {
try {
createWebHook(url);
return true;
} catch (IOException e) {
throw new GHException("Failed to update post-commit hooks", e);
@Override
public Iterator<URL> iterator() {
return getPostCommitHooks().iterator();
}
}
@Override
public boolean remove(Object url) {
try {
String _url = ((URL) url).toExternalForm();
for (GHHook h : getHooks()) {
if (h.getName().equals("web") && h.getConfig().get("url").equals(_url)) {
h.delete();
return true;
@Override
public int size() {
return getPostCommitHooks().size();
}
@Override
public boolean add(URL url) {
try {
createWebHook(url);
return true;
} catch (IOException e) {
throw new GHException("Failed to update post-commit hooks", e);
}
}
@Override
public boolean remove(Object url) {
try {
String _url = ((URL) url).toExternalForm();
for (GHHook h : getHooks()) {
if (h.getName().equals("web") && h.getConfig().get("url").equals(_url)) {
h.delete();
return true;
}
}
return false;
} catch (IOException e) {
throw new GHException("Failed to update post-commit hooks", e);
}
return false;
} catch (IOException e) {
throw new GHException("Failed to update post-commit hooks", e);
}
}
};
};
}
GHRepository wrap(GitHub root) {
this.root = root;
@@ -2734,8 +2867,9 @@ public class GHRepository extends GHObject {
}
String getApiTailUrl(String tail) {
if (tail.length() > 0 && !tail.startsWith("/"))
if (tail.length() > 0 && !tail.startsWith("/")) {
tail = '/' + tail;
}
return "/repos/" + getOwnerName() + "/" + name + tail;
}
@@ -2842,23 +2976,64 @@ public class GHRepository extends GHObject {
* The IO exception
*/
void populate() throws IOException {
if (root.isOffline())
if (root.isOffline()) {
return; // can't populate if the root is offline
}
final URL url = Objects.requireNonNull(getUrl(), "Missing instance URL!");
try {
// IMPORTANT: the url for repository records is does not reliably point to the API url.
// IMPORTANT: the url for repository records does not reliably point to the API url.
// There is bug in Push event payloads that returns the wrong url.
// All other occurrences of "url" take the form "https://api.github.com/...".
// For Push event repository records, they take the form "https://github.com/{fullName}".
root.createRequest().setRawUrlPath(url.toString()).fetchInto(this).wrap(root);
root.createRequest().withPreview(BAPTISTE).setRawUrlPath(url.toString()).fetchInto(this).wrap(root);
} catch (HttpException e) {
if (e.getCause() instanceof JsonParseException) {
root.createRequest().withUrlPath("/repos/" + full_name).fetchInto(this).wrap(root);
root.createRequest()
.withPreview(BAPTISTE)
.withUrlPath("/repos/" + full_name)
.fetchInto(this)
.wrap(root);
} else {
throw e;
}
}
}
/**
* A {@link GHRepositoryBuilder} that allows multiple properties to be updated per request.
*
* Consumer must call {@link #done()} to commit changes.
*/
@BetaApi
@Deprecated
public static class Updater extends GHRepositoryBuilder<Updater> {
protected Updater(@Nonnull GHRepository repository) {
super(Updater.class, repository.root, null);
// even when we don't change the name, we need to send it in
// this requirement may be out-of-date, but we do not want to break it
requester.with("name", repository.name);
requester.method("PATCH").withUrlPath(repository.getApiTailUrl(""));
}
}
/**
* A {@link GHRepositoryBuilder} that allows multiple properties to be updated per request.
*
* Consumer must call {@link #done()} to commit changes.
*/
@BetaApi
@Deprecated
public static class Setter extends GHRepositoryBuilder<GHRepository> {
protected Setter(@Nonnull GHRepository repository) {
super(GHRepository.class, repository.root, null);
// even when we don't change the name, we need to send it in
// this requirement may be out-of-date, but we do not want to break it
requester.with("name", repository.name);
requester.method("PATCH").withUrlPath(repository.getApiTailUrl(""));
}
}
}

View File

@@ -0,0 +1,235 @@
package org.kohsuke.github;
import java.io.IOException;
import java.net.URL;
import static org.kohsuke.github.internal.Previews.BAPTISTE;
abstract class GHRepositoryBuilder<S> extends AbstractBuilder<GHRepository, S> {
protected GHRepositoryBuilder(Class<S> intermediateReturnType, GitHub root, GHRepository baseInstance) {
super(GHRepository.class, intermediateReturnType, root, baseInstance);
}
/**
* Allow or disallow squash-merging pull requests.
*
* @param enabled
* true if enabled
*
* @return a builder to continue with building
*
* @throws IOException
* In case of any networking error or error from the server.
*/
public S allowSquashMerge(boolean enabled) throws IOException {
return with("allow_squash_merge", enabled);
}
/**
* Allow or disallow merging pull requests with a merge commit.
*
* @param enabled
* true if enabled
*
* @return a builder to continue with building
*
* @throws IOException
* In case of any networking error or error from the server.
*/
public S allowMergeCommit(boolean enabled) throws IOException {
return with("allow_merge_commit", enabled);
}
/**
* Allow or disallow rebase-merging pull requests.
*
* @param enabled
* true if enabled
*
* @return a builder to continue with building
*
* @throws IOException
* In case of any networking error or error from the server.
*/
public S allowRebaseMerge(boolean enabled) throws IOException {
return with("allow_rebase_merge", enabled);
}
/**
* After pull requests are merged, you can have head branches deleted automatically.
*
* @param enabled
* true if enabled
*
* @return a builder to continue with building
*
* @throws IOException
* In case of any networking error or error from the server.
*/
public S deleteBranchOnMerge(boolean enabled) throws IOException {
return with("delete_branch_on_merge", enabled);
}
/**
* Default repository branch
*
* @param branch
* branch name
*
* @return a builder to continue with building
*
* @throws IOException
* In case of any networking error or error from the server.
*/
public S defaultBranch(String branch) throws IOException {
return with("default_branch", branch);
}
/**
* Description for repository
*
* @param description
* description of repository
*
* @return a builder to continue with building
*
* @throws IOException
* In case of any networking error or error from the server.
*/
public S description(String description) throws IOException {
return with("description", description);
}
/**
* Homepage for repository
*
* @param homepage
* homepage of repository
*
* @return a builder to continue with building
*
* @throws IOException
* In case of any networking error or error from the server.
*/
public S homepage(URL homepage) throws IOException {
return homepage(homepage.toExternalForm());
}
/**
* Homepage for repository
*
* @param homepage
* homepage of repository
*
* @return a builder to continue with building
*
* @throws IOException
* In case of any networking error or error from the server.
*/
public S homepage(String homepage) throws IOException {
return with("homepage", homepage);
}
/**
* Sets the repository to private
*
* @param enabled
* private if true
*
* @return a builder to continue with building
*
* @throws IOException
* In case of any networking error or error from the server.
*/
public S private_(boolean enabled) throws IOException {
return with("private", enabled);
}
/**
* Enables issue tracker
*
* @param enabled
* true if enabled
*
* @return a builder to continue with building
*
* @throws IOException
* In case of any networking error or error from the server.
*/
public S issues(boolean enabled) throws IOException {
return with("has_issues", enabled);
}
/**
* Enables projects
*
* @param enabled
* true if enabled
*
* @return a builder to continue with building
*
* @throws IOException
* In case of any networking error or error from the server.
*/
public S projects(boolean enabled) throws IOException {
return with("has_projects", enabled);
}
/**
* Enables wiki
*
* @param enabled
* true if enabled
* @return a builder to continue with building
* @throws IOException
* In case of any networking error or error from the server.
*/
public S wiki(boolean enabled) throws IOException {
return with("has_wiki", enabled);
}
/**
* Enables downloads
*
* @param enabled
* true if enabled
*
* @return a builder to continue with building
*
* @throws IOException
* In case of any networking error or error from the server.
*/
public S downloads(boolean enabled) throws IOException {
return with("has_downloads", enabled);
}
/**
* Specifies whether the repository is a template.
*
* @param enabled
* true if enabled
* @return a builder to continue with building
* @throws IOException
* In case of any networking error or error from the server.
*/
@Preview(BAPTISTE)
@Deprecated
public S isTemplate(boolean enabled) throws IOException {
requester.withPreview(BAPTISTE);
return with("is_template", enabled);
}
@Override
public GHRepository done() throws IOException {
return super.done().wrap(this.root);
}
S archive() throws IOException {
return with("archived", true);
}
S name(String name) throws IOException {
return with("name", name);
}
}

View File

@@ -16,10 +16,9 @@ import java.util.NoSuchElementException;
*
* @author Martin van Zijl
*/
public class GHRepositoryStatistics {
public class GHRepositoryStatistics extends GitHubInteractiveObject {
private final GHRepository repo;
private final GitHub root;
private static final int MAX_WAIT_ITERATIONS = 3;
private static final int WAIT_SLEEP_INTERVAL = 5000;
@@ -60,7 +59,7 @@ public class GHRepositoryStatistics {
* @throws InterruptedException
* the interrupted exception
*/
@Preview
@BetaApi
@Deprecated
@SuppressWarnings("SleepWhileInLoop")
@SuppressFBWarnings(value = { "RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE" }, justification = "JSON API")
@@ -99,7 +98,6 @@ public class GHRepositoryStatistics {
"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;
@@ -255,7 +253,6 @@ public class GHRepositoryStatistics {
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;
@@ -398,7 +395,6 @@ public class GHRepositoryStatistics {
* The type Participation.
*/
public static class Participation extends GHObject {
/* package almost final */ private GitHub root;
private List<Integer> all;
private List<Integer> owner;

View File

@@ -8,7 +8,6 @@ import java.net.URL;
justification = "JSON API")
public class GHRequestedAction extends GHObject {
private GHRepository owner;
private GitHub root;
private String identifier;
private String label;
private String description;
@@ -46,4 +45,4 @@ public class GHRequestedAction extends GHObject {
return null;
}
}
}

View File

@@ -25,6 +25,7 @@ public abstract class GHSearchBuilder<T> extends GHQueryBuilder<T> {
super(root);
this.receiverType = receiverType;
req.withUrlPath(getApiUrl());
req.rateLimit(RateLimitTarget.SEARCH);
}
/**

View File

@@ -10,11 +10,10 @@ import java.util.Date;
* @see GHRepository#getSubscription() GHRepository#getSubscription()
* @see GHThread#getSubscription() GHThread#getSubscription()
*/
public class GHSubscription {
public class GHSubscription extends GitHubInteractiveObject {
private String created_at, url, repository_url, reason;
private boolean subscribed, ignored;
private GitHub root;
private GHRepository repo;
/**

View File

@@ -9,9 +9,8 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
*/
@SuppressFBWarnings(value = { "UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD" },
justification = "JSON API")
public class GHTag {
public class GHTag extends GitHubInteractiveObject {
private GHRepository owner;
private GitHub root;
private String name;
private GHCommit commit;

View File

@@ -9,9 +9,8 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
*/
@SuppressFBWarnings(value = { "UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD" },
justification = "JSON API")
public class GHTagObject {
public class GHTagObject extends GitHubInteractiveObject {
private GHRepository owner;
private GitHub root;
private String tag;
private String sha;

View File

@@ -3,9 +3,12 @@ package org.kohsuke.github;
import java.io.IOException;
import java.net.URL;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;
import javax.annotation.Nonnull;
/**
* A team in GitHub organization.
*
@@ -21,8 +24,6 @@ public class GHTeam extends GHObject implements Refreshable {
private GHOrganization organization; // populated by GET /user/teams where Teams+Orgs are returned together
protected /* final */ GitHub root;
public enum Privacy {
SECRET, // only visible to organization owners and members of this team.
CLOSED // visible to all members of this organization.
@@ -130,6 +131,52 @@ public class GHTeam extends GHObject implements Refreshable {
root.createRequest().method("PATCH").with("privacy", privacy).withUrlPath(api("")).send();
}
/**
* Retrieves the discussions.
*
* @return the paged iterable
* @throws IOException
* the io exception
*/
@Nonnull
public PagedIterable<GHDiscussion> listDiscussions() throws IOException {
return GHDiscussion.readAll(this);
}
/**
* List members with specified role paged iterable.
*
* @param role
* the role
* @return the paged iterable
* @throws IOException
* the io exception
*/
public PagedIterable<GHUser> listMembers(String role) throws IOException {
return root.createRequest()
.withUrlPath(api("/members"))
.with("role", role)
.toIterable(GHUser[].class, item -> item.wrapUp(root));
}
/**
* Gets a single discussion by ID.
*
* @param discussionNumber
* id of the discussion that we want to query for
* @return the discussion
* @throws java.io.FileNotFoundException
* if the discussion does not exist
* @throws IOException
* the io exception
*
* @see <a href= "https://developer.github.com/v3/teams/discussions/#get-a-discussion">documentation</a>
*/
@Nonnull
public GHDiscussion getDiscussion(long discussionNumber) throws IOException {
return GHDiscussion.read(this, discussionNumber);
}
/**
* Retrieves the current members.
*
@@ -138,7 +185,20 @@ public class GHTeam extends GHObject implements Refreshable {
* the io exception
*/
public PagedIterable<GHUser> listMembers() throws IOException {
return root.createRequest().withUrlPath(api("/members")).toIterable(GHUser[].class, item -> item.wrapUp(root));
return listMembers("all");
}
/**
* Retrieves the teams that are children of this team.
*
* @return the paged iterable
* @throws IOException
* the io exception
*/
public PagedIterable<GHTeam> listChildTeams() throws IOException {
return root.createRequest()
.withUrlPath(api("/teams"))
.toIterable(GHTeam[].class, item -> item.wrapUp(this.organization));
}
/**
@@ -297,6 +357,21 @@ public class GHTeam extends GHObject implements Refreshable {
return "/teams/" + getId() + tail;
}
/**
* Begins the creation of a new instance.
*
* Consumer must call {@link GHDiscussion.Creator#done()} to commit changes.
*
* @param title
* title of the discussion to be created
* @return a {@link GHDiscussion.Creator}
* @throws IOException
* the io exception
*/
public GHDiscussion.Creator createDiscussion(String title) throws IOException {
return GHDiscussion.create(this).title(title);
}
/**
* Gets organization.
*
@@ -318,4 +393,23 @@ public class GHTeam extends GHObject implements Refreshable {
public URL getHtmlUrl() {
return GitHubClient.parseURL(html_url);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
GHTeam ghTeam = (GHTeam) o;
return Objects.equals(name, ghTeam.name) && Objects.equals(getUrl(), ghTeam.getUrl())
&& Objects.equals(permission, ghTeam.permission) && Objects.equals(slug, ghTeam.slug)
&& Objects.equals(description, ghTeam.description) && privacy == ghTeam.privacy;
}
@Override
public int hashCode() {
return Objects.hash(name, getUrl(), permission, slug, description, privacy);
}
}

View File

@@ -7,9 +7,7 @@ import java.io.IOException;
*
* https://developer.github.com/v3/teams/#create-team
*/
public class GHTeamBuilder {
private final GitHub root;
public class GHTeamBuilder extends GitHubInteractiveObject {
protected final Requester builder;
private final String orgName;
@@ -75,7 +73,7 @@ public class GHTeamBuilder {
* parentTeamId of team
* @return a builder to continue with building
*/
public GHTeamBuilder parentTeamId(int parentTeamId) {
public GHTeamBuilder parentTeamId(long parentTeamId) {
this.builder.with("parent_team_id", parentTeamId);
return this;
}

View File

@@ -17,7 +17,6 @@ import java.util.Date;
@SuppressFBWarnings(value = { "UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD" },
justification = "JSON API")
public class GHThread extends GHObject {
private GitHub root;
private GHRepository repository;
private Subject subject;
private String reason;

View File

@@ -173,6 +173,19 @@ public class GHUser extends GHPerson {
return org.hasPublicMember(this);
}
/**
* Returns true if this user is marked as hireable, false otherwise
*
* @return if the user is marked as hireable
*/
public boolean isHireable() {
return hireable;
}
public String getBio() {
return bio;
}
static GHUser[] wrap(GHUser[] users, GitHub root) {
for (GHUser f : users)
f.root = root;

View File

@@ -26,6 +26,8 @@ package org.kohsuke.github;
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
import org.kohsuke.github.authorization.AuthorizationProvider;
import org.kohsuke.github.internal.Previews;
import java.io.*;
import java.util.*;
@@ -37,8 +39,8 @@ import java.util.logging.Logger;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import static org.kohsuke.github.Previews.INERTIA;
import static org.kohsuke.github.Previews.MACHINE_MAN;
import static org.kohsuke.github.internal.Previews.INERTIA;
import static org.kohsuke.github.internal.Previews.MACHINE_MAN;
/**
* Root of the GitHub API.
@@ -93,39 +95,112 @@ public class GitHub {
* "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 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.
* @param password
* User's password. Always used in conjunction with the {@code login} parameter
* @param connector
* HttpConnector to use. Pass null to use default connector.
* a connector
* @param rateLimitHandler
* rateLimitHandler
* @param abuseLimitHandler
* abuseLimitHandler
* @param rateLimitChecker
* rateLimitChecker
* @param authorizationProvider
* a authorization provider
*/
GitHub(String apiUrl,
String login,
String oauthAccessToken,
String jwtToken,
String password,
HttpConnector connector,
RateLimitHandler rateLimitHandler,
AbuseLimitHandler abuseLimitHandler,
GitHubRateLimitChecker rateLimitChecker) throws IOException {
GitHubRateLimitChecker rateLimitChecker,
AuthorizationProvider authorizationProvider) throws IOException {
if (authorizationProvider instanceof DependentAuthorizationProvider) {
((DependentAuthorizationProvider) authorizationProvider).bind(this);
}
this.client = new GitHubHttpUrlConnectionClient(apiUrl,
login,
oauthAccessToken,
jwtToken,
password,
connector,
rateLimitHandler,
abuseLimitHandler,
rateLimitChecker,
(myself) -> setMyself(myself));
(myself) -> setMyself(myself),
authorizationProvider);
users = new ConcurrentHashMap<>();
orgs = new ConcurrentHashMap<>();
}
private GitHub(GitHubClient client) {
this.client = client;
users = new ConcurrentHashMap<>();
orgs = new ConcurrentHashMap<>();
}
public static abstract class DependentAuthorizationProvider implements AuthorizationProvider {
private GitHub baseGitHub;
private GitHub gitHub;
private final AuthorizationProvider authorizationProvider;
/**
* An AuthorizationProvider that requires an authenticated GitHub instance to provide its authorization.
*
* @param authorizationProvider
* A authorization provider to be used when refreshing this authorization provider.
*/
@BetaApi
@Deprecated
protected DependentAuthorizationProvider(AuthorizationProvider authorizationProvider) {
this.authorizationProvider = authorizationProvider;
}
/**
* Binds this authorization provider to a github instance.
*
* Only needs to be implemented by dynamic credentials providers that use a github instance in order to refresh.
*
* @param github
* The github instance to be used for refreshing dynamic credentials
*/
synchronized void bind(GitHub github) {
if (baseGitHub != null) {
throw new IllegalStateException("Already bound to another GitHub instance.");
}
this.baseGitHub = github;
}
protected synchronized final GitHub gitHub() {
if (gitHub == null) {
gitHub = new GitHub.AuthorizationRefreshGitHubWrapper(this.baseGitHub, authorizationProvider);
}
return gitHub;
}
}
private static class AuthorizationRefreshGitHubWrapper extends GitHub {
private final AuthorizationProvider authorizationProvider;
AuthorizationRefreshGitHubWrapper(GitHub github, AuthorizationProvider authorizationProvider) {
super(github.client);
this.authorizationProvider = authorizationProvider;
// no dependent authorization providers nest like this currently, but they might in future
if (authorizationProvider instanceof DependentAuthorizationProvider) {
((DependentAuthorizationProvider) authorizationProvider).bind(this);
}
}
@Nonnull
@Override
Requester createRequest() {
try {
// Override
return super.createRequest().setHeader("Authorization", authorizationProvider.getEncodedAuthorization())
.rateLimit(RateLimitTarget.NONE);
} catch (IOException e) {
throw new GHException("Failed to create requester to refresh credentials", e);
}
}
}
/**
* Obtains the credential from "~/.github" or from the System Environment Properties.
*
@@ -373,12 +448,20 @@ public class GitHub {
}
/**
* Gets the current rate limit.
* Gets the current full rate limit information from the server.
*
* For some versions of GitHub Enterprise, the {@code /rate_limit} endpoint returns a {@code 404 Not Found}. In that
* case, the most recent {@link GHRateLimit} information will be returned, including rate limit information returned
* in the response header for this request in if was present.
*
* For most use cases it would be better to implement a {@link RateLimitChecker} and add it via
* {@link GitHubBuilder#withRateLimitChecker(RateLimitChecker)}.
*
* @return the rate limit
* @throws IOException
* the io exception
*/
@Nonnull
public GHRateLimit getRateLimit() throws IOException {
return client.getRateLimit();
}
@@ -388,8 +471,11 @@ public class GitHub {
* GitHub Enterprise) or if no requests have been made.
*
* @return the most recently observed rate limit data or {@code null}.
* @deprecated implement a {@link RateLimitChecker} and add it via
* {@link GitHubBuilder#withRateLimitChecker(RateLimitChecker)}.
*/
@CheckForNull
@Nonnull
@Deprecated
public GHRateLimit lastRateLimit() {
return client.lastRateLimit();
}
@@ -400,10 +486,13 @@ public class GitHub {
* @return the current rate limit data.
* @throws IOException
* if we couldn't get the current rate limit data.
* @deprecated implement a {@link RateLimitChecker} and add it via
* {@link GitHubBuilder#withRateLimitChecker(RateLimitChecker)}.
*/
@Nonnull
@Deprecated
public GHRateLimit rateLimit() throws IOException {
return client.rateLimit();
return client.rateLimit(RateLimitTarget.CORE);
}
/**
@@ -518,7 +607,7 @@ public class GitHub {
}
/**
* Gets the repository object from 'user/reponame' string that GitHub calls as "repository name"
* Gets the repository object from 'owner/repo' string that GitHub calls as "repository name"
*
* @param name
* the name
@@ -529,9 +618,10 @@ public class GitHub {
*/
public GHRepository getRepository(String name) throws IOException {
String[] tokens = name.split("/");
return createRequest().withUrlPath("/repos/" + tokens[0] + '/' + tokens[1])
.fetch(GHRepository.class)
.wrap(this);
if (tokens.length < 2) {
throw new IllegalArgumentException("Repository name must be in format owner/repo");
}
return GHRepository.read(this, tokens[0], tokens[1]);
}
/**
@@ -726,7 +816,7 @@ public class GitHub {
* @return the team
* @throws IOException
* the io exception
*
*
* @deprecated Use {@link GHOrganization#getTeam(long)}
* @see <a href= "https://developer.github.com/v3/teams/#get-team-legacy">deprecation notice</a>
*/
@@ -830,7 +920,7 @@ public class GitHub {
* @return the gh create repository builder
*/
public GHCreateRepositoryBuilder createRepository(String name) {
return new GHCreateRepositoryBuilder(this, "/user/repos", name);
return new GHCreateRepositoryBuilder(name, this, "/user/repos");
}
/**
@@ -998,7 +1088,7 @@ public class GitHub {
* @see <a href="https://developer.github.com/v3/apps/#get-the-authenticated-github-app">Get the authenticated
* GitHub App</a>
*/
@Preview
@Preview(MACHINE_MAN)
@Deprecated
public GHApp getApp() throws IOException {
return createRequest().withPreview(MACHINE_MAN).withUrlPath("/app").fetch(GHApp.class).wrapUp(this);
@@ -1093,7 +1183,7 @@ public class GitHub {
*
* @return the gh commit search builder
*/
@Preview
@Preview(Previews.CLOAK)
@Deprecated
public GHCommitSearchBuilder searchCommits() {
return new GHCommitSearchBuilder(this);
@@ -1193,26 +1283,34 @@ public class GitHub {
}
/**
* Do not use this method. This method will be removed and should never have been needed in the first place.
* Gets an {@link ObjectWriter} that can be used to convert data objects in this library to JSON.
*
* If you must convert data object in this library to JSON, the {@link ObjectWriter} returned by this method is the
* only supported way of doing so. This {@link ObjectWriter} can be used to convert any library data object to JSON
* without throwing an exception.
*
* WARNING: While the JSON generated is generally expected to be stable, it is not part of the API of this library
* and may change without warning. Use with extreme caution.
*
* @return an {@link ObjectWriter} instance that can be further configured.
* @deprecated DO NOT USE THIS METHOD. Provided for backward compatibility with projects that did their own jackson
* mapping of this project's data objects, such as Jenkins Blue Ocean.
*/
@Deprecated
@Nonnull
public static ObjectWriter getMappingObjectWriter() {
return GitHubClient.getMappingObjectWriter();
}
/**
* Do not use this method. This method will be removed and should never have been needed in the first place.
* Gets an {@link ObjectReader} that can be used to convert JSON into library data objects.
*
* If you must manually create library data objects from JSON, the {@link ObjectReader} returned by this method is
* the only supported way of doing so.
*
* WARNING: Objects generated from this method have limited functionality. They will not throw when being crated
* from valid JSON matching the expected object, but they are not guaranteed to be usable beyond that. Use with
* extreme caution.
*
* @return an {@link ObjectReader} instance that can be further configured.
* @deprecated DO NOT USE THIS METHOD. Provided for backward compatibility with projects that did their own jackson
* mapping of this project's data objects, such as Jenkins Blue Ocean.
*/
@Deprecated
@Nonnull
public static ObjectReader getMappingObjectReader() {
return GitHubClient.getMappingObjectReader(GitHub.offline());

View File

@@ -1,6 +1,8 @@
package org.kohsuke.github;
import org.apache.commons.io.IOUtils;
import org.kohsuke.github.authorization.AuthorizationProvider;
import org.kohsuke.github.authorization.ImmutableAuthorizationProvider;
import org.kohsuke.github.extras.ImpatientHttpConnector;
import java.io.File;
@@ -24,16 +26,13 @@ public class GitHubBuilder implements Cloneable {
// default scoped so unit tests can read them.
/* private */ String endpoint = GitHubClient.GITHUB_URL;
/* private */ String user;
/* private */ String password;
/* private */ String oauthToken;
/* private */ String jwtToken;
private HttpConnector connector;
private RateLimitHandler rateLimitHandler = RateLimitHandler.WAIT;
private AbuseLimitHandler abuseLimitHandler = AbuseLimitHandler.WAIT;
private GitHubRateLimitChecker rateLimitChecker = new GitHubRateLimitChecker();
/* private */ AuthorizationProvider authorizationProvider = AuthorizationProvider.ANONYMOUS;
/**
* Instantiates a new Git hub builder.
@@ -61,13 +60,13 @@ public class GitHubBuilder implements Cloneable {
builder = fromEnvironment();
if (builder.oauthToken != null || builder.user != null || builder.jwtToken != null)
if (builder.authorizationProvider != null)
return builder;
try {
builder = fromPropertyFile();
if (builder.oauthToken != null || builder.user != null || builder.jwtToken != null)
if (builder.authorizationProvider != null)
return builder;
} catch (FileNotFoundException e) {
// fall through
@@ -215,9 +214,20 @@ public class GitHubBuilder implements Cloneable {
*/
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"));
String oauth = props.getProperty("oauth");
String jwt = props.getProperty("jwt");
String login = props.getProperty("login");
String password = props.getProperty("password");
if (oauth != null) {
self.withOAuthToken(oauth, login);
}
if (jwt != null) {
self.withJwtToken(jwt);
}
if (password != null) {
self.withPassword(login, password);
}
self.withEndpoint(props.getProperty("endpoint", GitHubClient.GITHUB_URL));
return self;
}
@@ -247,9 +257,7 @@ public class GitHubBuilder implements Cloneable {
* @return the git hub builder
*/
public GitHubBuilder withPassword(String user, String password) {
this.user = user;
this.password = password;
return this;
return withAuthorizationProvider(ImmutableAuthorizationProvider.fromLoginAndPassword(user, password));
}
/**
@@ -260,7 +268,7 @@ public class GitHubBuilder implements Cloneable {
* @return the git hub builder
*/
public GitHubBuilder withOAuthToken(String oauthToken) {
return withOAuthToken(oauthToken, null);
return withAuthorizationProvider(ImmutableAuthorizationProvider.fromOauthToken(oauthToken));
}
/**
@@ -273,8 +281,21 @@ public class GitHubBuilder implements Cloneable {
* @return the git hub builder
*/
public GitHubBuilder withOAuthToken(String oauthToken, String user) {
this.oauthToken = oauthToken;
this.user = user;
return withAuthorizationProvider(ImmutableAuthorizationProvider.fromOauthToken(oauthToken, user));
}
/**
* Configures a {@link AuthorizationProvider} for this builder
*
* There can be only one authorization provider per client instance.
*
* @param authorizationProvider
* the authorization provider
* @return the git hub builder
*
*/
public GitHubBuilder withAuthorizationProvider(final AuthorizationProvider authorizationProvider) {
this.authorizationProvider = authorizationProvider;
return this;
}
@@ -287,7 +308,7 @@ public class GitHubBuilder implements Cloneable {
* @see GHAppInstallation#createToken(java.util.Map) GHAppInstallation#createToken(java.util.Map)
*/
public GitHubBuilder withAppInstallationToken(String appInstallationToken) {
return withOAuthToken(appInstallationToken, "");
return withAuthorizationProvider(ImmutableAuthorizationProvider.fromAppInstallationToken(appInstallationToken));
}
/**
@@ -298,8 +319,7 @@ public class GitHubBuilder implements Cloneable {
* @return the git hub builder
*/
public GitHubBuilder withJwtToken(String jwtToken) {
this.jwtToken = jwtToken;
return this;
return withAuthorizationProvider(ImmutableAuthorizationProvider.fromJwtToken(jwtToken));
}
/**
@@ -358,6 +378,18 @@ public class GitHubBuilder implements Cloneable {
return this;
}
/**
* Adds a {@link RateLimitChecker} for the Core API for this {@link GitHubBuilder}.
*
* @param coreRateLimitChecker
* the {@link RateLimitChecker} for core GitHub API requests
* @return the git hub builder
* @see #withRateLimitChecker(RateLimitChecker, RateLimitTarget)
*/
public GitHubBuilder withRateLimitChecker(@Nonnull RateLimitChecker coreRateLimitChecker) {
return withRateLimitChecker(coreRateLimitChecker, RateLimitTarget.CORE);
}
/**
* Adds a {@link RateLimitChecker} to this {@link GitHubBuilder}.
* <p>
@@ -376,15 +408,15 @@ public class GitHubBuilder implements Cloneable {
* request.
* </p>
*
* @param coreRateLimitChecker
* the {@link RateLimitChecker} for core GitHub API requests
* @param rateLimitChecker
* the {@link RateLimitChecker} for requests
* @param rateLimitTarget
* the {@link RateLimitTarget} specifying which rate limit record to check
* @return the git hub builder
*/
public GitHubBuilder withRateLimitChecker(@Nonnull RateLimitChecker coreRateLimitChecker) {
this.rateLimitChecker = new GitHubRateLimitChecker(coreRateLimitChecker,
RateLimitChecker.NONE,
RateLimitChecker.NONE,
RateLimitChecker.NONE);
public GitHubBuilder withRateLimitChecker(@Nonnull RateLimitChecker rateLimitChecker,
@Nonnull RateLimitTarget rateLimitTarget) {
this.rateLimitChecker = this.rateLimitChecker.with(rateLimitChecker, rateLimitTarget);
return this;
}
@@ -409,14 +441,11 @@ public class GitHubBuilder implements Cloneable {
*/
public GitHub build() throws IOException {
return new GitHub(endpoint,
user,
oauthToken,
jwtToken,
password,
connector,
rateLimitHandler,
abuseLimitHandler,
rateLimitChecker);
rateLimitChecker,
authorizationProvider);
}
@Override

View File

@@ -1,33 +1,19 @@
package org.kohsuke.github;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.InjectableValues;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.introspect.VisibilityChecker;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.io.IOUtils;
import org.kohsuke.github.authorization.AuthorizationProvider;
import org.kohsuke.github.authorization.UserAuthorizationProvider;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.TimeZone;
import java.net.*;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.function.Consumer;
import java.util.logging.Logger;
@@ -44,7 +30,7 @@ import static java.util.logging.Level.*;
* A GitHub API Client
* <p>
* A GitHubClient can be used to send requests and retrieve their responses. GitHubClient is thread-safe and can be used
* to send multiple requests. GitHubClient also track some GitHub API information such as {@link #rateLimit()}.
* to send multiple requests. GitHubClient also track some GitHub API information such as {@link GHRateLimit}.
* </p>
*/
abstract class GitHubClient {
@@ -56,32 +42,28 @@ abstract class GitHubClient {
static final int retryTimeoutMillis = 100;
/* private */ final String login;
/**
* Value of the authorization header to be sent with the request.
*/
/* private */ final String encodedAuthorization;
// Cache of myself object.
private final String apiUrl;
protected final RateLimitHandler rateLimitHandler;
protected final AbuseLimitHandler abuseLimitHandler;
private final GitHubRateLimitChecker rateLimitChecker;
private final AuthorizationProvider authorizationProvider;
private HttpConnector connector;
private final Object headerRateLimitLock = new Object();
private GHRateLimit headerRateLimit = null;
private volatile GHRateLimit rateLimit = null;
private final Object rateLimitLock = new Object();
@Nonnull
private GHRateLimit rateLimit = GHRateLimit.DEFAULT;
private static final Logger LOGGER = Logger.getLogger(GitHubClient.class.getName());
private static final ObjectMapper MAPPER = new ObjectMapper();
static final String GITHUB_URL = "https://api.github.com";
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
};
private static final DateTimeFormatter DATE_TIME_PARSER_SLASHES = DateTimeFormatter
.ofPattern("yyyy/MM/dd HH:mm:ss Z");
static {
MAPPER.setVisibility(new VisibilityChecker.Std(NONE, NONE, NONE, NONE, ANY));
@@ -91,15 +73,12 @@ abstract class GitHubClient {
}
GitHubClient(String apiUrl,
String login,
String oauthAccessToken,
String jwtToken,
String password,
HttpConnector connector,
RateLimitHandler rateLimitHandler,
AbuseLimitHandler abuseLimitHandler,
GitHubRateLimitChecker rateLimitChecker,
Consumer<GHMyself> myselfConsumer) throws IOException {
Consumer<GHMyself> myselfConsumer,
AuthorizationProvider authorizationProvider) throws IOException {
if (apiUrl.endsWith("/")) {
apiUrl = apiUrl.substring(0, apiUrl.length() - 1); // normalize
@@ -111,40 +90,43 @@ abstract class GitHubClient {
this.apiUrl = apiUrl;
this.connector = connector;
if (oauthAccessToken != null) {
encodedAuthorization = "token " + oauthAccessToken;
} else {
if (jwtToken != null) {
encodedAuthorization = "Bearer " + jwtToken;
} else if (password != null) {
String authorization = (login + ':' + password);
String charsetName = StandardCharsets.UTF_8.name();
encodedAuthorization = "Basic "
+ Base64.getEncoder().encodeToString(authorization.getBytes(charsetName));
} else {// anonymous access
encodedAuthorization = null;
}
}
// Prefer credential configuration via provider
this.authorizationProvider = authorizationProvider;
this.rateLimitHandler = rateLimitHandler;
this.abuseLimitHandler = abuseLimitHandler;
this.rateLimitChecker = rateLimitChecker;
if (login == null && encodedAuthorization != null && jwtToken == null) {
GHMyself myself = fetch(GHMyself.class, "/user");
login = myself.getLogin();
if (myselfConsumer != null) {
myselfConsumer.accept(myself);
this.login = getCurrentUser(myselfConsumer);
}
private String getCurrentUser(Consumer<GHMyself> myselfConsumer) throws IOException {
String login = null;
if (this.authorizationProvider instanceof UserAuthorizationProvider
&& this.authorizationProvider.getEncodedAuthorization() != null) {
UserAuthorizationProvider userAuthorizationProvider = (UserAuthorizationProvider) this.authorizationProvider;
login = userAuthorizationProvider.getLogin();
if (login == null) {
try {
GHMyself myself = fetch(GHMyself.class, "/user");
if (myselfConsumer != null) {
myselfConsumer.accept(myself);
}
login = myself.getLogin();
} catch (IOException e) {
return null;
}
}
}
this.login = login;
return login;
}
private <T> T fetch(Class<T> type, String urlPath) throws IOException {
return this
.sendRequest(GitHubRequest.newBuilder().withApiUrl(getApiUrl()).withUrlPath(urlPath).build(),
(responseInfo) -> GitHubResponse.parseBody(responseInfo, type))
.body();
GitHubRequest request = GitHubRequest.newBuilder().withApiUrl(getApiUrl()).withUrlPath(urlPath).build();
return this.sendRequest(request, (responseInfo) -> GitHubResponse.parseBody(responseInfo, type)).body();
}
/**
@@ -204,15 +186,24 @@ abstract class GitHubClient {
* @return {@code true} if operations that require authentication will fail.
*/
public boolean isAnonymous() {
return login == null && encodedAuthorization == null;
try {
return login == null && this.authorizationProvider.getEncodedAuthorization() == null;
} catch (IOException e) {
// An exception here means that the provider failed to provide authorization parameters,
// basically meaning the same as "no auth"
return false;
}
}
/**
* Gets the current rate limit from the server.
* Gets the current full rate limit information from the server.
*
* For some versions of GitHub Enterprise, the {@code /rate_limit} endpoint returns a {@code 404 Not Found}. In
* that, if {@link #lastRateLimit()} is not {@code null} and is not expired, it will be returned. Otherwise, a
* placeholder {@link GHRateLimit} instance with {@link GHRateLimit.UnknownLimitRecord}s will be returned.
* For some versions of GitHub Enterprise, the {@code /rate_limit} endpoint returns a {@code 404 Not Found}. In that
* case, the most recent {@link GHRateLimit} information will be returned, including rate limit information returned
* in the response header for this request in if was present.
*
* For most use cases it would be better to implement a {@link RateLimitChecker} and add it via
* {@link GitHubBuilder#withRateLimitChecker(RateLimitChecker)}.
*
* @return the rate limit
* @throws IOException
@@ -220,59 +211,100 @@ abstract class GitHubClient {
*/
@Nonnull
public GHRateLimit getRateLimit() throws IOException {
return getRateLimit(RateLimitTarget.NONE);
}
@CheckForNull
protected String getEncodedAuthorization() throws IOException {
return authorizationProvider.getEncodedAuthorization();
}
@Nonnull
GHRateLimit getRateLimit(@Nonnull RateLimitTarget rateLimitTarget) throws IOException {
GHRateLimit result;
try {
result = fetch(JsonRateLimit.class, "/rate_limit").resources;
GitHubRequest request = GitHubRequest.newBuilder()
.rateLimit(RateLimitTarget.NONE)
.withApiUrl(getApiUrl())
.withUrlPath("/rate_limit")
.build();
result = this
.sendRequest(request, (responseInfo) -> GitHubResponse.parseBody(responseInfo, JsonRateLimit.class))
.body().resources;
} catch (FileNotFoundException e) {
// For some versions of GitHub Enterprise, the rate_limit endpoint returns a 404.
LOGGER.log(FINE, "/rate_limit returned 404 Not Found.");
// However some newer versions of GHE include rate limit header information
// Use that if available
result = lastRateLimit();
if (result == null || result.isExpired()) {
// return a default rate limit
result = GHRateLimit.Unknown();
}
// If the header info is missing and the endpoint returns 404, fill the rate limit
// with unknown
result = GHRateLimit.fromRecord(GHRateLimit.UnknownLimitRecord.current(), rateLimitTarget);
}
return rateLimit = result;
return updateRateLimit(result);
}
/**
* Returns the most recently observed rate limit data or {@code null} if either there is no rate limit (for example
* GitHub Enterprise) or if no requests have been made.
* Returns the most recently observed rate limit data.
*
* @return the most recently observed rate limit data or {@code null}.
* Generally, instead of calling this you should implement a {@link RateLimitChecker} or call
*
* @return the most recently observed rate limit data. This may include expired or
* {@link GHRateLimit.UnknownLimitRecord} entries.
* @deprecated implement a {@link RateLimitChecker} and add it via
* {@link GitHubBuilder#withRateLimitChecker(RateLimitChecker)}.
*/
@CheckForNull
public GHRateLimit lastRateLimit() {
synchronized (headerRateLimitLock) {
return headerRateLimit;
@Nonnull
@Deprecated
GHRateLimit lastRateLimit() {
synchronized (rateLimitLock) {
return rateLimit;
}
}
/**
* Gets the current rate limit while trying not to actually make any remote requests unless absolutely necessary.
* Gets the current rate limit for an endpoint while trying not to actually make any remote requests unless
* absolutely necessary.
*
* If {@link #lastRateLimit()} is not {@code null} and is not expired, it will be returned. If the information
* returned from the last call to {@link #getRateLimit()} is not {@code null} and is not expired, then it will be
* returned. Otherwise, the result of a call to {@link #getRateLimit()} will be returned.
* If the {@link GHRateLimit.Record} for {@code urlPath} is not expired, it is returned. If the
* {@link GHRateLimit.Record} for {@code urlPath} is expired, {@link #getRateLimit()} will be called to get the
* current rate limit.
*
* @return the current rate limit data.
* @param rateLimitTarget
* the endpoint to get the rate limit for.
*
* @return the current rate limit data. {@link GHRateLimit.Record}s in this instance may be expired when returned.
* @throws IOException
* if there was an error getting current rate limit data.
*/
@Nonnull
public GHRateLimit rateLimit() throws IOException {
synchronized (headerRateLimitLock) {
if (headerRateLimit != null && !headerRateLimit.isExpired()) {
return headerRateLimit;
GHRateLimit rateLimit(@Nonnull RateLimitTarget rateLimitTarget) throws IOException {
synchronized (rateLimitLock) {
if (rateLimit.getRecord(rateLimitTarget).isExpired()) {
getRateLimit(rateLimitTarget);
}
return rateLimit;
}
GHRateLimit result = this.rateLimit;
if (result == null || result.isExpired()) {
result = getRateLimit();
}
/**
* Update the Rate Limit with the latest info from response header.
*
* Due to multi-threading, requests might complete out of order. This method calls
* {@link GHRateLimit#getMergedRateLimit(GHRateLimit)} to ensure the most current records are used.
*
* @param observed
* {@link GHRateLimit.Record} constructed from the response header information
*/
private GHRateLimit updateRateLimit(@Nonnull GHRateLimit observed) {
synchronized (rateLimitLock) {
observed = rateLimit.getMergedRateLimit(observed);
if (rateLimit != observed) {
rateLimit = observed;
LOGGER.log(FINE, "Rate limit now: {0}", rateLimit);
}
return rateLimit;
}
return result;
}
/**
@@ -351,39 +383,42 @@ abstract class GitHubClient {
GitHubResponse.ResponseInfo responseInfo = null;
try {
if (LOGGER.isLoggable(FINE)) {
LOGGER.log(FINE,
"GitHub API request [" + (login == null ? "anonymous" : login) + "]: " + request.method()
+ " " + request.url().toString());
try {
if (LOGGER.isLoggable(FINE)) {
LOGGER.log(FINE,
"GitHub API request [" + (login == null ? "anonymous" : login) + "]: "
+ request.method() + " " + request.url().toString());
}
rateLimitChecker.checkRateLimit(this, request);
responseInfo = getResponseInfo(request);
noteRateLimit(responseInfo);
detectOTPRequired(responseInfo);
if (isInvalidCached404Response(responseInfo)) {
// Setting "Cache-Control" to "no-cache" stops the cache from supplying
// "If-Modified-Since" or "If-None-Match" values.
// This makes GitHub give us current data (not incorrectly cached data)
request = request.toBuilder().setHeader("Cache-Control", "no-cache").build();
continue;
}
if (!(isRateLimitResponse(responseInfo) || isAbuseLimitResponse(responseInfo))) {
return createResponse(responseInfo, handler);
}
} catch (IOException e) {
// For transient errors, retry
if (retryConnectionError(e, request.url(), retries)) {
continue;
}
throw interpretApiError(e, request, responseInfo);
}
rateLimitChecker.checkRateLimit(this, request);
responseInfo = getResponseInfo(request);
noteRateLimit(responseInfo);
detectOTPRequired(responseInfo);
if (isInvalidCached404Response(responseInfo)) {
// Setting "Cache-Control" to "no-cache" stops the cache from supplying
// "If-Modified-Since" or "If-None-Match" values.
// This makes GitHub give us current data (not incorrectly cached data)
request = request.toBuilder().withHeader("Cache-Control", "no-cache").build();
continue;
}
if (!(isRateLimitResponse(responseInfo) || isAbuseLimitResponse(responseInfo))) {
return createResponse(responseInfo, handler);
}
} catch (IOException e) {
// For transient errors, retry
if (retryConnectionError(e, request.url(), retries)) {
continue;
}
throw interpretApiError(e, request, responseInfo);
handleLimitingErrors(responseInfo);
} finally {
IOUtils.closeQuietly(responseInfo);
}
handleLimitingErrors(responseInfo);
} while (--retries >= 0);
throw new GHIOException("Ran out of retries for URL: " + request.url().toString());
@@ -512,58 +547,25 @@ abstract class GitHubClient {
}
private void noteRateLimit(@Nonnull GitHubResponse.ResponseInfo responseInfo) {
if (responseInfo.request().urlPath().startsWith("/search")) {
// the search API uses a different rate limit
return;
}
String limitString = responseInfo.headerField("X-RateLimit-Limit");
if (StringUtils.isBlank(limitString)) {
// if we are missing a header, return fast
return;
}
String remainingString = responseInfo.headerField("X-RateLimit-Remaining");
if (StringUtils.isBlank(remainingString)) {
// if we are missing a header, return fast
return;
}
String resetString = responseInfo.headerField("X-RateLimit-Reset");
if (StringUtils.isBlank(resetString)) {
// if we are missing a header, return fast
return;
}
int limit, remaining;
long reset;
try {
String limitString = Objects.requireNonNull(responseInfo.headerField("X-RateLimit-Limit"),
"Missing X-RateLimit-Limit");
String remainingString = Objects.requireNonNull(responseInfo.headerField("X-RateLimit-Remaining"),
"Missing X-RateLimit-Remaining");
String resetString = Objects.requireNonNull(responseInfo.headerField("X-RateLimit-Reset"),
"Missing X-RateLimit-Reset");
int limit, remaining;
long reset;
limit = Integer.parseInt(limitString);
} catch (NumberFormatException e) {
if (LOGGER.isLoggable(FINEST)) {
LOGGER.log(FINEST, "Malformed X-RateLimit-Limit header value " + limitString, e);
}
return;
}
try {
remaining = Integer.parseInt(remainingString);
} catch (NumberFormatException e) {
if (LOGGER.isLoggable(FINEST)) {
LOGGER.log(FINEST, "Malformed X-RateLimit-Remaining header value " + remainingString, e);
}
return;
}
try {
reset = Long.parseLong(resetString);
} catch (NumberFormatException e) {
GHRateLimit.Record observed = new GHRateLimit.Record(limit, remaining, reset, responseInfo);
updateRateLimit(GHRateLimit.fromRecord(observed, responseInfo.request().rateLimitTarget()));
} catch (NumberFormatException | NullPointerException e) {
if (LOGGER.isLoggable(FINEST)) {
LOGGER.log(FINEST, "Malformed X-RateLimit-Reset header value " + resetString, e);
LOGGER.log(FINEST, "Missing or malformed X-RateLimit header: ", e);
}
return;
}
GHRateLimit.Record observed = new GHRateLimit.Record(limit, remaining, reset, responseInfo);
updateCoreRateLimit(observed);
}
private static void detectOTPRequired(@Nonnull GitHubResponse.ResponseInfo responseInfo) throws GHIOException {
@@ -583,23 +585,6 @@ abstract class GitHubClient {
"This operation requires a credential but none is given to the GitHub constructor");
}
/**
* Update the Rate Limit with the latest info from response header. Due to multi-threading requests might complete
* out of order, we want to pick the one with the most recent info from the server. Calls
* {@link #shouldReplace(GHRateLimit.Record, GHRateLimit.Record)}
*
* @param observed
* {@link GHRateLimit.Record} constructed from the response header information
*/
private void updateCoreRateLimit(@Nonnull GHRateLimit.Record observed) {
synchronized (headerRateLimitLock) {
if (headerRateLimit == null || shouldReplace(observed, headerRateLimit.getCore())) {
headerRateLimit = GHRateLimit.fromHeaderRecord(observed);
LOGGER.log(FINE, "Rate limit now: {0}", headerRateLimit);
}
}
}
private static class GHApiInfo {
private String rate_limit_url;
@@ -649,37 +634,6 @@ abstract class GitHubClient {
}
}
/**
* Determine if one {@link GHRateLimit.Record} should replace another. Header date is only accurate to the second,
* so we look at the information in the record itself.
*
* {@link GHRateLimit.UnknownLimitRecord}s are always replaced by regular {@link GHRateLimit.Record}s. Regular
* {@link GHRateLimit.Record}s are never replaced by {@link GHRateLimit.UnknownLimitRecord}s. Candidates with
* resetEpochSeconds later than current record are more recent. Candidates with the same reset and a lower remaining
* count are more recent. Candidates with an earlier reset are older.
*
* @param candidate
* {@link GHRateLimit.Record} constructed from the response header information
* @param current
* the current {@link GHRateLimit.Record} record
*/
static boolean shouldReplace(@Nonnull GHRateLimit.Record candidate, @Nonnull GHRateLimit.Record current) {
if (candidate instanceof GHRateLimit.UnknownLimitRecord
&& !(current instanceof GHRateLimit.UnknownLimitRecord)) {
// Unknown candidate never replaces a regular record
return false;
} else if (current instanceof GHRateLimit.UnknownLimitRecord
&& !(candidate instanceof GHRateLimit.UnknownLimitRecord)) {
// Any real record should replace an unknown Record.
return true;
} else {
// records of the same type compare to each other as normal.
return current.getResetEpochSeconds() < candidate.getResetEpochSeconds()
|| (current.getResetEpochSeconds() == candidate.getResetEpochSeconds()
&& current.getRemaining() > candidate.getRemaining());
}
}
static URL parseURL(String s) {
try {
return s == null ? null : new URL(s);
@@ -691,22 +645,24 @@ abstract class GitHubClient {
static Date parseDate(String timestamp) {
if (timestamp == null)
return null;
for (String f : TIME_FORMATS) {
try {
SimpleDateFormat df = new SimpleDateFormat(f);
df.setTimeZone(TimeZone.getTimeZone("GMT"));
return df.parse(timestamp);
} catch (ParseException e) {
// try next
}
return Date.from(parseInstant(timestamp));
}
static Instant parseInstant(String timestamp) {
if (timestamp == null)
return null;
if (timestamp.charAt(4) == '/') {
// Unsure where this is used, but retained for compatibility.
return Instant.from(DATE_TIME_PARSER_SLASHES.parse(timestamp));
} else {
return Instant.from(DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(timestamp));
}
throw new IllegalStateException("Unable to parse the timestamp: " + timestamp);
}
static String printDate(Date dt) {
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
df.setTimeZone(TimeZone.getTimeZone("GMT"));
return df.format(dt);
return DateTimeFormatter.ISO_INSTANT.format(Instant.ofEpochMilli(dt.getTime()).truncatedTo(ChronoUnit.SECONDS));
}
/**

View File

@@ -1,6 +1,7 @@
package org.kohsuke.github;
import org.apache.commons.io.IOUtils;
import org.kohsuke.github.authorization.AuthorizationProvider;
import java.io.IOException;
import java.io.InputStream;
@@ -24,7 +25,7 @@ import static org.apache.commons.lang3.StringUtils.defaultString;
* A GitHub API Client for HttpUrlConnection
* <p>
* A GitHubClient can be used to send requests and retrieve their responses. GitHubClient is thread-safe and can be used
* to send multiple requests. GitHubClient also track some GitHub API information such as {@link #rateLimit()}.
* to send multiple requests. GitHubClient also track some GitHub API information such as {@link GHRateLimit}.
* </p>
* <p>
* GitHubHttpUrlConnectionClient gets a new {@link HttpURLConnection} for each call to send.
@@ -33,25 +34,19 @@ import static org.apache.commons.lang3.StringUtils.defaultString;
class GitHubHttpUrlConnectionClient extends GitHubClient {
GitHubHttpUrlConnectionClient(String apiUrl,
String login,
String oauthAccessToken,
String jwtToken,
String password,
HttpConnector connector,
RateLimitHandler rateLimitHandler,
AbuseLimitHandler abuseLimitHandler,
GitHubRateLimitChecker rateLimitChecker,
Consumer<GHMyself> myselfConsumer) throws IOException {
Consumer<GHMyself> myselfConsumer,
AuthorizationProvider authorizationProvider) throws IOException {
super(apiUrl,
login,
oauthAccessToken,
jwtToken,
password,
connector,
rateLimitHandler,
abuseLimitHandler,
rateLimitChecker,
myselfConsumer);
myselfConsumer,
authorizationProvider);
}
@Nonnull
@@ -114,8 +109,12 @@ class GitHubHttpUrlConnectionClient extends GitHubClient {
// if the authentication is needed but no credential is given, try it anyway (so that some calls
// that do work with anonymous access in the reduced form should still work.)
if (client.encodedAuthorization != null)
connection.setRequestProperty("Authorization", client.encodedAuthorization);
if (!request.headers().containsKey("Authorization")) {
String authorization = client.getEncodedAuthorization();
if (authorization != null) {
connection.setRequestProperty("Authorization", client.getEncodedAuthorization());
}
}
setRequestMethod(request.method(), connection);
buildRequest(request, connection);
@@ -235,6 +234,10 @@ class GitHubHttpUrlConnectionClient extends GitHubClient {
private static final Logger LOGGER = Logger.getLogger(GitHubClient.class.getName());
@Override
public void close() throws IOException {
IOUtils.closeQuietly(connection.getInputStream());
}
}
}

View File

@@ -0,0 +1,23 @@
package org.kohsuke.github;
import com.fasterxml.jackson.annotation.JacksonInject;
/**
* Defines a base class that all classes in this library that interact with GitHub inherit from.
*
* Ensures that all data references to GitHub connection are transient.
*
* Classes that do not need to interact with GitHub after they are instantiated do not need to inherit from this class.
*/
abstract class GitHubInteractiveObject {
@JacksonInject
/* package almost final */ transient GitHub root;
GitHubInteractiveObject() {
root = null;
}
GitHubInteractiveObject(GitHub root) {
this.root = root;
}
}

View File

@@ -3,26 +3,28 @@ package org.kohsuke.github;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.util.Objects;
import java.util.logging.Logger;
import javax.annotation.Nonnull;
/**
* A GitHub API Rate Limit Checker called before each request. This class provides the basic infrastructure for calling
* the appropriate {@link RateLimitChecker} for a request and retrying as many times as needed. This class supports more
* complex throttling strategies and polling, but leaves the specifics to the {@link RateLimitChecker} implementations.
* A GitHub API Rate Limit Checker called before each request.
*
* <p>
* GitHub allots a certain number of requests to each user or application per period of time (usually per hour). The
* number of requests remaining is returned in the response header and can also be requested using
* {@link GitHub#getRateLimit()}. This requests per interval is referred to as the "rate limit".
* GitHub allots a certain number of requests to each user or application per period of time. The number of requests
* remaining and the time when the number will be reset is returned in the response header and can also be requested
* using {@link GitHub#getRateLimit()}. The "requests per interval" is referred to as the "rate limit".
* </p>
* <p>
* GitHub prefers that clients stop before exceeding their rate limit rather than stopping after they exceed it. The
* {@link RateLimitChecker} is called before each request to check the rate limit and wait if the checker criteria are
* met.
* Different parts of the GitHub API have separate rate limits, but most of REST API uses {@link RateLimitTarget#CORE}.
* Checking your rate limit using {@link GitHub#getRateLimit()} does not effect your rate limit. GitHub prefers that
* clients stop before exceeding their rate limit rather than stopping after they exceed it.
* </p>
* <p>
* Checking your rate limit using {@link GitHub#getRateLimit()} does not effect your rate limit, but each {@link GitHub}
* instance will attempt to cache and reuse the last see rate limit rather than making a new request.
* This class provides the infrastructure for calling the appropriate {@link RateLimitChecker} before each request and
* retrying than call many times as needed. Each {@link RateLimitChecker} decides whether to wait and for how long. This
* allows for a wide range of {@link RateLimitChecker} implementations, including complex throttling strategies and
* polling.
* </p>
*/
class GitHubRateLimitChecker {
@@ -39,6 +41,8 @@ class GitHubRateLimitChecker {
@Nonnull
private final RateLimitChecker integrationManifest;
private static final Logger LOGGER = Logger.getLogger(GitHubRateLimitChecker.class.getName());
GitHubRateLimitChecker() {
this(RateLimitChecker.NONE, RateLimitChecker.NONE, RateLimitChecker.NONE, RateLimitChecker.NONE);
}
@@ -48,40 +52,57 @@ class GitHubRateLimitChecker {
@Nonnull RateLimitChecker graphql,
@Nonnull RateLimitChecker integrationManifest) {
this.core = Objects.requireNonNull(core);
// for now only support rate limiting on core
// remove these asserts when that changes
assert search == RateLimitChecker.NONE;
assert graphql == RateLimitChecker.NONE;
assert integrationManifest == RateLimitChecker.NONE;
this.search = Objects.requireNonNull(search);
this.graphql = Objects.requireNonNull(graphql);
this.integrationManifest = Objects.requireNonNull(integrationManifest);
}
/**
* Constructs a new {@link GitHubRateLimitChecker} with a new checker for a particular target.
*
* Only one {@link RateLimitChecker} is allowed per target.
*
* @param checker
* the {@link RateLimitChecker} to apply.
* @param rateLimitTarget
* the {@link RateLimitTarget} for this checker. If {@link RateLimitTarget#NONE}, checker will be ignored
* and no change will be made.
* @return a new {@link GitHubRateLimitChecker}
*/
GitHubRateLimitChecker with(@Nonnull RateLimitChecker checker, @Nonnull RateLimitTarget rateLimitTarget) {
return new GitHubRateLimitChecker(rateLimitTarget == RateLimitTarget.CORE ? checker : core,
rateLimitTarget == RateLimitTarget.SEARCH ? checker : search,
rateLimitTarget == RateLimitTarget.GRAPHQL ? checker : graphql,
rateLimitTarget == RateLimitTarget.INTEGRATION_MANIFEST ? checker : integrationManifest);
}
/**
* Checks whether there is sufficient requests remaining within this client's rate limit quota to make the current
* request.
* <p>
* This method does not do the actual check. Instead it select the appropriate {@link RateLimitChecker} and
* {@link GHRateLimit.Record} for the current request's urlPath. If the {@link RateLimitChecker} for this the
* current request's urlPath is {@link RateLimitChecker#NONE} the rate limit is not checked. If not, it calls
* {@link RateLimitChecker#checkRateLimit(GHRateLimit.Record, long)}. which decides if the rate limit has been
* exceeded and then sleeps for as long is it choose.
* This method does not do the actual check. Instead it selects the appropriate {@link RateLimitChecker} and
* {@link GHRateLimit.Record} for the current request's {@link RateLimitTarget}. It then calls
* {@link RateLimitChecker#checkRateLimit(GHRateLimit.Record, long)}.
* </p>
* <p>
* It is up to the {@link RateLimitChecker#checkRateLimit(GHRateLimit.Record, long)} which decide if the rate limit
* has been exceeded. If it has, that method will sleep for as long is it chooses and then return {@code true}. If
* not, that method will return {@code false}.
* It is up to {@link RateLimitChecker#checkRateLimit(GHRateLimit.Record, long)} to which decide if the rate limit
* has been exceeded. If it has, {@link RateLimitChecker#checkRateLimit(GHRateLimit.Record, long)} will sleep for as
* long is it chooses and then return {@code true}. If not, that method will return {@code false}.
* </p>
* <p>
* As long as {@link RateLimitChecker#checkRateLimit(GHRateLimit.Record, long)} returns {@code true}, this method
* will request updated rate limit information and call
* {@link RateLimitChecker#checkRateLimit(GHRateLimit.Record, long)} again. This looping allows implementers of
* {@link RateLimitChecker#checkRateLimit(GHRateLimit.Record, long)} to apply any number of strategies to
* controlling the speed at which requests are made. When it returns {@code false} this method will return and the
* request will be sent.
* {@link RateLimitChecker#checkRateLimit(GHRateLimit.Record, long)} again. This looping allows different
* {@link RateLimitChecker} implementations to apply any number of strategies to controlling the speed at which
* requests are made.
* </p>
* <p>
* When the {@link RateLimitChecker} returns {@code false} this method will return and the request processing will
* continue.
* </p>
* <p>
* If the {@link RateLimitChecker} for this the current request's urlPath is {@link RateLimitChecker#NONE} the rate
* limit is not checked.
* </p>
*
* @param client
@@ -92,14 +113,14 @@ class GitHubRateLimitChecker {
* if there is an I/O error
*/
void checkRateLimit(GitHubClient client, GitHubRequest request) throws IOException {
RateLimitChecker guard = selectChecker(request.urlPath());
RateLimitChecker guard = selectChecker(request.rateLimitTarget());
if (guard == RateLimitChecker.NONE) {
return;
}
// For the first rate limit, accept the current limit if a valid one is already present.
GHRateLimit rateLimit = client.rateLimit();
GHRateLimit.Record rateLimitRecord = rateLimit.getRecordForUrlPath(request.urlPath());
GHRateLimit rateLimit = client.rateLimit(request.rateLimitTarget());
GHRateLimit.Record rateLimitRecord = rateLimit.getRecord(request.rateLimitTarget());
long waitCount = 0;
try {
while (guard.checkRateLimit(rateLimitRecord, waitCount)) {
@@ -112,8 +133,8 @@ class GitHubRateLimitChecker {
Thread.sleep(1000);
// After the first wait, always request a new rate limit from the server.
rateLimit = client.getRateLimit();
rateLimitRecord = rateLimit.getRecordForUrlPath(request.urlPath());
rateLimit = client.getRateLimit(request.rateLimitTarget());
rateLimitRecord = rateLimit.getRecord(request.rateLimitTarget());
}
} catch (InterruptedException e) {
throw (IOException) new InterruptedIOException(e.getMessage()).initCause(e);
@@ -121,25 +142,28 @@ class GitHubRateLimitChecker {
}
/**
* Gets the appropriate {@link RateLimitChecker} for a particular url path. Similar to
* {@link GHRateLimit#getRecordForUrlPath(String)}.
* Gets the appropriate {@link RateLimitChecker} for a particular target.
*
* @param urlPath
* the url path of the request
* @return the {@link RateLimitChecker} for a url path.
* Analogous with {@link GHRateLimit#getRecord(RateLimitTarget)}.
*
* @param rateLimitTarget
* the rate limit to check
* @return the {@link RateLimitChecker} for a particular target
*/
@Nonnull
private RateLimitChecker selectChecker(@Nonnull String urlPath) {
if (urlPath.equals("/rate_limit")) {
private RateLimitChecker selectChecker(@Nonnull RateLimitTarget rateLimitTarget) {
if (rateLimitTarget == RateLimitTarget.NONE) {
return RateLimitChecker.NONE;
} else if (urlPath.startsWith("/search")) {
} else if (rateLimitTarget == RateLimitTarget.CORE) {
return core;
} else if (rateLimitTarget == RateLimitTarget.SEARCH) {
return search;
} else if (urlPath.startsWith("/graphql")) {
} else if (rateLimitTarget == RateLimitTarget.GRAPHQL) {
return graphql;
} else if (urlPath.startsWith("/app-manifests")) {
} else if (rateLimitTarget == RateLimitTarget.INTEGRATION_MANIFEST) {
return integrationManifest;
} else {
return core;
throw new IllegalArgumentException("Unknown rate limit target: " + rateLimitTarget.toString());
}
}
}

View File

@@ -2,6 +2,7 @@ package org.kohsuke.github;
import edu.umd.cs.findbugs.annotations.NonNull;
import org.apache.commons.lang3.StringUtils;
import org.kohsuke.github.internal.Previews;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
@@ -45,6 +46,7 @@ class GitHubRequest {
private final String apiUrl;
private final String urlPath;
private final String method;
private final RateLimitTarget rateLimitTarget;
private final InputStream body;
private final boolean forceBody;
@@ -56,6 +58,7 @@ class GitHubRequest {
@Nonnull String apiUrl,
@Nonnull String urlPath,
@Nonnull String method,
@Nonnull RateLimitTarget rateLimitTarget,
@CheckForNull InputStream body,
boolean forceBody) throws MalformedURLException {
this.args = Collections.unmodifiableList(new ArrayList<>(args));
@@ -64,6 +67,7 @@ class GitHubRequest {
this.apiUrl = apiUrl;
this.urlPath = urlPath;
this.method = method;
this.rateLimitTarget = rateLimitTarget;
this.body = body;
this.forceBody = forceBody;
String tailApiUrl = buildTailApiUrl();
@@ -72,7 +76,7 @@ class GitHubRequest {
/**
* Create a new {@link Builder}.
*
*
* @return a new {@link Builder}.
*/
public static Builder<?> newBuilder() {
@@ -119,6 +123,16 @@ class GitHubRequest {
return method;
}
/**
* The rate limit target for this request.
*
* @return the rate limit to use for this request.
*/
@Nonnull
public RateLimitTarget rateLimitTarget() {
return rateLimitTarget;
}
/**
* The arguments for this request. Depending on the {@link #method()} and {@code #inBody()} these maybe added to the
* url or to the request body.
@@ -152,7 +166,7 @@ class GitHubRequest {
/**
* The base GitHub API URL for this request represented as a {@link String}
*
*
* @return the url string
*/
@Nonnull
@@ -163,7 +177,7 @@ class GitHubRequest {
/**
* The url path to be added to the {@link #apiUrl()} for this request. If this does not start with a "/", it instead
* represents the full url string for this request.
*
*
* @return a url path or full url string
*/
@Nonnull
@@ -173,7 +187,7 @@ class GitHubRequest {
/**
* The content type to to be sent by this request.
*
*
* @return the content type.
*/
@Nonnull
@@ -183,7 +197,7 @@ class GitHubRequest {
/**
* The {@link InputStream} to be sent as the body of this request.
*
*
* @return the {@link InputStream}.
*/
@CheckForNull
@@ -193,7 +207,7 @@ class GitHubRequest {
/**
* The {@link URL} for this request. This is the actual URL the {@link GitHubClient} will send this request to.
*
*
* @return the request {@link URL}
*/
@Nonnull
@@ -203,7 +217,7 @@ class GitHubRequest {
/**
* Whether arguments for this request should be included in the URL or in the body of the request.
*
*
* @return true if the arguements should be sent in the body of the request.
*/
public boolean inBody() {
@@ -213,11 +227,19 @@ class GitHubRequest {
/**
* Create a {@link Builder} from this request. Initial values of the builder will be the same as this
* {@link GitHubRequest}.
*
*
* @return a {@link Builder} based on this request.
*/
public Builder<?> toBuilder() {
return new Builder<>(args, headers, injectedMappingValues, apiUrl, urlPath, method, body, forceBody);
return new Builder<>(args,
headers,
injectedMappingValues,
apiUrl,
urlPath,
method,
rateLimitTarget,
body,
forceBody);
}
private String buildTailApiUrl() {
@@ -281,6 +303,10 @@ class GitHubRequest {
*/
@Nonnull
private String method;
@Nonnull
private RateLimitTarget rateLimitTarget;
private InputStream body;
private boolean forceBody;
@@ -294,6 +320,7 @@ class GitHubRequest {
GitHubClient.GITHUB_URL,
"/",
"GET",
RateLimitTarget.CORE,
null,
false);
}
@@ -304,6 +331,7 @@ class GitHubRequest {
@Nonnull String apiUrl,
@Nonnull String urlPath,
@Nonnull String method,
@Nonnull RateLimitTarget rateLimitTarget,
@CheckForNull @WillClose InputStream body,
boolean forceBody) {
this.args = new ArrayList<>(args);
@@ -312,19 +340,28 @@ class GitHubRequest {
this.apiUrl = apiUrl;
this.urlPath = urlPath;
this.method = method;
this.rateLimitTarget = rateLimitTarget;
this.body = body;
this.forceBody = forceBody;
}
/**
* Builds a {@link GitHubRequest} from this builder.
*
*
* @return a {@link GitHubRequest}
* @throws MalformedURLException
* if the GitHub API URL cannot be constructed
*/
public GitHubRequest build() throws MalformedURLException {
return new GitHubRequest(args, headers, injectedMappingValues, apiUrl, urlPath, method, body, forceBody);
return new GitHubRequest(args,
headers,
injectedMappingValues,
apiUrl,
urlPath,
method,
rateLimitTarget,
body,
forceBody);
}
/**
@@ -348,9 +385,11 @@ class GitHubRequest {
* the name
* @param value
* the value
* @return the request builder
*/
public void setHeader(String name, String value) {
public B setHeader(String name, String value) {
headers.put(name, value);
return (B) this;
}
/**
@@ -363,8 +402,11 @@ class GitHubRequest {
* @return the request builder
*/
public B withHeader(String name, String value) {
setHeader(name, value);
return (B) this;
String oldValue = headers.get(name);
if (!StringUtils.isBlank(oldValue)) {
value = oldValue + ", " + value;
}
return setHeader(name, value);
}
/**
@@ -396,6 +438,25 @@ class GitHubRequest {
return withHeader("Accept", name);
}
public B withPreview(Previews preview) {
return withPreview(preview.mediaType());
}
/**
* With requester.
*
* @param Map
* map of key value pairs to add
* @return the request builder
*/
public B with(Map<String, Object> map) {
for (Map.Entry<String, Object> entry : map.entrySet()) {
with(entry.getKey(), entry.getValue());
}
return (B) this;
}
/**
* With requester.
*
@@ -562,6 +623,18 @@ class GitHubRequest {
return (B) this;
}
/**
* Method requester.
*
* @param rateLimitTarget
* the rate limit target for this request. Default is {@link RateLimitTarget#CORE}.
* @return the request builder
*/
public B rateLimit(@Nonnull RateLimitTarget rateLimitTarget) {
this.rateLimitTarget = rateLimitTarget;
return (B) this;
}
/**
* Content type requester.
*
@@ -623,13 +696,9 @@ class GitHubRequest {
tailUrlPath += "/" + String.join("/", urlPathItems);
}
if (this.urlPath.endsWith("/")) {
tailUrlPath = StringUtils.stripStart(tailUrlPath, "/");
} else {
tailUrlPath = StringUtils.prependIfMissing(tailUrlPath, "/");
}
tailUrlPath = StringUtils.prependIfMissing(tailUrlPath, "/");
this.urlPath += urlPathEncode(tailUrlPath);
this.urlPath = urlPathEncode(tailUrlPath);
return (B) this;
}

View File

@@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.InjectableValues;
import com.fasterxml.jackson.databind.JsonMappingException;
import org.apache.commons.io.IOUtils;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@@ -77,9 +78,14 @@ class GitHubResponse<T> {
@CheckForNull
static <T> T parseBody(ResponseInfo responseInfo, Class<T> type) throws IOException {
if (responseInfo.statusCode() == HttpURLConnection.HTTP_NO_CONTENT && type != null && type.isArray()) {
// no content
return type.cast(Array.newInstance(type.getComponentType(), 0));
if (responseInfo.statusCode() == HttpURLConnection.HTTP_NO_CONTENT) {
if (type != null && type.isArray()) {
// no content for array should be empty array
return type.cast(Array.newInstance(type.getComponentType(), 0));
} else {
// no content for object should be null
return null;
}
}
String data = responseInfo.getBodyAsString();
@@ -212,7 +218,7 @@ class GitHubResponse<T> {
* Initial response information supplied to a {@link BodyHandler} when a response is initially received and before
* the body is processed.
*/
static abstract class ResponseInfo {
static abstract class ResponseInfo implements Closeable {
private static final Comparator<String> nullableCaseInsensitiveComparator = Comparator
.nullsFirst(String.CASE_INSENSITIVE_ORDER);
@@ -317,12 +323,8 @@ class GitHubResponse<T> {
@Nonnull
String getBodyAsString() throws IOException {
InputStreamReader r = null;
try {
r = new InputStreamReader(this.bodyStream(), StandardCharsets.UTF_8);
return IOUtils.toString(r);
} finally {
IOUtils.closeQuietly(r);
}
r = new InputStreamReader(this.bodyStream(), StandardCharsets.UTF_8);
return IOUtils.toString(r);
}
}

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