Compare commits

...

135 Commits

Author SHA1 Message Date
Liam Newman
ae69324196 [maven-release-plugin] prepare release github-api-1.124 2021-03-19 13:48:39 -07:00
Liam Newman
5194c2d9bc Merge pull request #1061 from gsmet/remove-add-label-payload
Implement getLabel() and getChanges() for GHEventPayload.Issue
2021-03-19 13:45:12 -07:00
Liam Newman
daf5c5eb98 Merge branch 'master' into remove-add-label-payload 2021-03-19 13:23:36 -07:00
Liam Newman
a7b4c97020 Merge pull request #1062 from gsmet/add-missing-label-info
Add the missing fields for GHLabel
2021-03-19 13:17:31 -07:00
Liam Newman
420d5d06f3 Merge branch 'master' into add-missing-label-info 2021-03-19 12:54:22 -07:00
Liam Newman
a7cd052b7c Merge pull request #1063 from gsmet/update-ci-jdks
Fix CI issues
2021-03-19 12:54:05 -07:00
Guillaume Smet
6e1b943823 Disable tests messing with the environment for Java 16+
It might be possible to make them work again but it will require some
more advanced surgery.
2021-03-19 18:22:39 +01:00
Guillaume Smet
8a3559ada5 Open java.net to unnamed modules
This is needed to inject the unsupported HTTP verb. I don't know really
if we will be able to find a better option than that...
2021-03-19 18:22:39 +01:00
Guillaume Smet
ea3cbd4c71 Pass appropriate MAVEN_OPTS for JDK 11+
They are required by spotless starting JDK 16+
2021-03-19 18:22:39 +01:00
Guillaume Smet
34a1f9d6e4 Disable fail-fast for matrix builds 2021-03-19 16:49:35 +01:00
Guillaume Smet
629bd510c1 Update JDKs used by CI to the latest versions 2021-03-19 16:49:33 +01:00
Guillaume Smet
40937a5cc6 Add the missing fields for GHLabel
Fixes #1059
2021-03-19 14:56:05 +01:00
Guillaume Smet
8509957102 Implement getChanges() for GHEventPayload.Issue 2021-03-19 13:39:26 +01:00
Guillaume Smet
b0aea0c575 Expose getLabel() for GHEventPayload.Issue
It was exposed on pull requests but not issues.
2021-03-19 13:11:38 +01:00
Liam Newman
1f7f646bec Merge pull request #1054 from gsmet/fix-add-remove-labels-concurrency
Fix concurrency issues with GHIssue addLabels and removeLabels
2021-03-15 10:56:45 -07:00
Liam Newman
a59ee6a82d Add removeLabel() that throws when label missing 2021-03-12 17:56:34 -08:00
Guillaume Smet
1fefc77582 Fix concurrency issues with GHIssue addLabels and removeLabels
Fixes #1049
2021-03-10 14:03:20 +01:00
Liam Newman
199eee4e25 Merge pull request #1055 from gsmet/update-contributing
Adjust wording used to create the token and give a bit more guidance
2021-03-09 18:45:38 -08:00
Guillaume Smet
854df5321b Adjust wording used to create the token and give a bit more guidance 2021-03-09 16:20:55 +01:00
Liam Newman
bd509070ac Merge pull request #1052 from bitwiseman/task/test-void-bridge
Test that void bridge methods are created
2021-03-05 11:34:10 -08:00
Liam Newman
a8c7c97d06 Test that void bridge methods are created 2021-03-05 11:16:06 -08:00
Liam Newman
6d86cfb4f6 Merge pull request #1047 from hub4j/dependabot/maven/junit-junit-4.13.2
Chore(deps-dev): Bump junit from 4.13.1 to 4.13.2
2021-03-01 09:20:28 -08:00
dependabot[bot]
fb3e956502 Chore(deps-dev): Bump junit from 4.13.1 to 4.13.2
Bumps [junit](https://github.com/junit-team/junit4) from 4.13.1 to 4.13.2.
- [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.1...r4.13.2)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-01 17:19:55 +00:00
Liam Newman
9b0dbe6f34 Merge pull request #1044 from hub4j/dependabot/maven/org.codehaus.mojo-animal-sniffer-maven-plugin-1.20
Chore(deps): Bump animal-sniffer-maven-plugin from 1.19 to 1.20
2021-03-01 09:19:28 -08:00
dependabot[bot]
c10c7237a7 Chore(deps): Bump animal-sniffer-maven-plugin from 1.19 to 1.20
Bumps [animal-sniffer-maven-plugin](https://github.com/mojohaus/animal-sniffer) from 1.19 to 1.20.
- [Release notes](https://github.com/mojohaus/animal-sniffer/releases)
- [Commits](https://github.com/mojohaus/animal-sniffer/compare/animal-sniffer-parent-1.19...animal-sniffer-parent-1.20)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-01 17:19:16 +00:00
Liam Newman
36612fe97f Merge pull request #1045 from hub4j/dependabot/maven/spotbugs.version-4.2.1
Chore(deps): Bump spotbugs.version from 4.1.3 to 4.2.1
2021-03-01 09:18:58 -08:00
Liam Newman
18e2056a10 Merge pull request #1042 from hub4j/dependabot/maven/com.tngtech.archunit-archunit-0.17.0
Chore(deps-dev): Bump archunit from 0.16.0 to 0.17.0
2021-03-01 09:18:32 -08:00
Liam Newman
8c8f1451d4 Merge pull request #1043 from hub4j/dependabot/github_actions/actions/cache-v2.1.4
Chore(deps): Bump actions/cache from v2 to v2.1.4
2021-03-01 09:18:14 -08:00
dependabot[bot]
be67f1d9e2 Chore(deps): Bump spotbugs.version from 4.1.3 to 4.2.1
Bumps `spotbugs.version` from 4.1.3 to 4.2.1.

Updates `spotbugs` from 4.1.3 to 4.2.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.1.3...4.2.1)

Updates `spotbugs-annotations` from 4.1.3 to 4.2.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.1.3...4.2.1)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-01 02:00:31 +00:00
dependabot[bot]
90bc250269 Chore(deps): Bump actions/cache from v2 to v2.1.4
Bumps [actions/cache](https://github.com/actions/cache) from v2 to v2.1.4.
- [Release notes](https://github.com/actions/cache/releases)
- [Commits](https://github.com/actions/cache/compare/v2...26968a09c0ea4f3e233fdddbafd1166051a095f6)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-01 02:00:20 +00:00
dependabot[bot]
1bd178654f Chore(deps-dev): Bump archunit from 0.16.0 to 0.17.0
Bumps [archunit](https://github.com/TNG/ArchUnit) from 0.16.0 to 0.17.0.
- [Release notes](https://github.com/TNG/ArchUnit/releases)
- [Commits](https://github.com/TNG/ArchUnit/compare/v0.16.0...v0.17.0)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-01 02:00:18 +00:00
Liam Newman
f22bf160f9 [maven-release-plugin] prepare for next development iteration 2021-02-26 15:20:10 -08:00
Liam Newman
4261c42949 [maven-release-plugin] prepare release github-api-1.123 2021-02-26 15:20:00 -08:00
Liam Newman
40cfb85a8e Merge pull request #1041 from bitwiseman/task/eol
Only run spotless:check as part of the lifecyle
2021-02-26 15:18:19 -08:00
Liam Newman
f08299b134 Only run spotless:check as part of the lifecyle
Users can run spotless:apply as they see fit.
2021-02-26 14:51:53 -08:00
Liam Newman
a04ab45abc Merge pull request #1040 from bitwiseman/bugfix/reposity-id-type
Fix the type of the id parameter of Github#getRepositoryById
2021-02-26 14:27:41 -08:00
Liam Newman
0647df2d2b Add tests for getRepositoryById 2021-02-26 13:02:12 -08:00
Liam Newman
d4cc3af1e9 Merge pull request #967 from chids/download-repository-archives
Add support for downloading zip and tar archives of repositories.
2021-02-26 12:48:42 -08:00
Liam Newman
936ab499ce Formatting 2021-02-26 12:08:14 -08:00
Liam Newman
453f475b4e Merge pull request #1029 from hub4j/dependabot/maven/com.fasterxml.jackson.core-jackson-databind-2.12.1
Chore(deps): Bump jackson-databind from 2.10.2 to 2.12.1
2021-02-26 11:59:24 -08:00
Liam Newman
bda3855b86 Merge pull request #1039 from bitwiseman/task/jwt
Allow for time skew in JWT authentication
2021-02-26 11:52:22 -08:00
Liam Newman
772a6c112b Remove consumers, use FunctionThrows 2021-02-26 11:12:51 -08:00
Liam Newman
9b4134cada Allow for time skew in JWT authentication 2021-02-26 10:51:07 -08:00
Liam Newman
ed9f54006d Merge branch 'master' into dependabot/maven/com.fasterxml.jackson.core-jackson-databind-2.12.1 2021-02-25 02:04:36 -08:00
Liam Newman
3b1f176544 Merge remote-tracking branch 'upstream/master' into download-repository-archives 2021-02-11 17:12:11 -08:00
Liam Newman
d2732bcf54 Merge pull request #1033 from uhafner/missing-text-in-extra-annotations
Make sure that `output.text` is set in each checks call
2021-02-08 03:16:24 -08:00
M. Abdullah Onus
a1461f401a Update src/main/java/org/kohsuke/github/GitHub.java
Co-authored-by: Liam Newman <bitwiseman@gmail.com>
2021-02-06 20:13:33 +03:00
Ulli Hafner
f9fd30275c Make sure that output.text is set in each checks call.
If a GitHub checks contains more than 50 annotations, then the
check is split into several calls. This fix makes sure that all
additional calls will not only copy the properties `title` and
`summary` but also the previously missing property `text`.
2021-02-04 19:47:49 +01:00
Liam Newman
eeea14dab4 Merge pull request #1028 from hub4j/dependabot/maven/com.tngtech.archunit-archunit-0.16.0
Chore(deps-dev): Bump archunit from 0.15.0 to 0.16.0
2021-02-01 14:26:20 -08:00
dependabot[bot]
1df807a198 Chore(deps-dev): Bump archunit from 0.15.0 to 0.16.0
Bumps [archunit](https://github.com/TNG/ArchUnit) from 0.15.0 to 0.16.0.
- [Release notes](https://github.com/TNG/ArchUnit/releases)
- [Commits](https://github.com/TNG/ArchUnit/compare/v0.15.0...v0.16.0)

Signed-off-by: dependabot[bot] <support@github.com>
2021-02-01 21:27:56 +00:00
Liam Newman
0848287069 Merge pull request #1027 from hub4j/dependabot/maven/org.mockito-mockito-core-3.7.7
Chore(deps-dev): Bump mockito-core from 3.6.28 to 3.7.7
2021-02-01 13:27:25 -08:00
dependabot[bot]
334b37a256 Chore(deps-dev): Bump mockito-core from 3.6.28 to 3.7.7
Bumps [mockito-core](https://github.com/mockito/mockito) from 3.6.28 to 3.7.7.
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v3.6.28...v3.7.7)

Signed-off-by: dependabot[bot] <support@github.com>
2021-02-01 21:27:13 +00:00
Liam Newman
8776a3b672 Merge pull request #1031 from hub4j/dependabot/maven/com.diffplug.spotless-spotless-maven-plugin-2.7.0
Chore(deps): Bump spotless-maven-plugin from 2.6.1 to 2.7.0
2021-02-01 13:26:34 -08:00
Liam Newman
657550f767 Merge pull request #1030 from hub4j/dependabot/maven/com.github.spotbugs-spotbugs-maven-plugin-4.2.0
Chore(deps): Bump spotbugs-maven-plugin from 4.1.4 to 4.2.0
2021-02-01 13:26:16 -08:00
dependabot[bot]
45a0114f75 Chore(deps): Bump spotless-maven-plugin from 2.6.1 to 2.7.0
Bumps [spotless-maven-plugin](https://github.com/diffplug/project) from 2.6.1 to 2.7.0.
- [Release notes](https://github.com/diffplug/project/releases)
- [Commits](https://github.com/diffplug/project/commits)

Signed-off-by: dependabot[bot] <support@github.com>
2021-02-01 02:01:26 +00:00
dependabot[bot]
a8ddd3e12a Chore(deps): Bump spotbugs-maven-plugin from 4.1.4 to 4.2.0
Bumps [spotbugs-maven-plugin](https://github.com/spotbugs/spotbugs-maven-plugin) from 4.1.4 to 4.2.0.
- [Release notes](https://github.com/spotbugs/spotbugs-maven-plugin/releases)
- [Commits](https://github.com/spotbugs/spotbugs-maven-plugin/compare/spotbugs-maven-plugin-4.1.4...spotbugs-maven-plugin-4.2.0)

Signed-off-by: dependabot[bot] <support@github.com>
2021-02-01 02:00:31 +00:00
dependabot[bot]
b668396151 Chore(deps): Bump jackson-databind from 2.10.2 to 2.12.1
Bumps [jackson-databind](https://github.com/FasterXML/jackson) from 2.10.2 to 2.12.1.
- [Release notes](https://github.com/FasterXML/jackson/releases)
- [Commits](https://github.com/FasterXML/jackson/commits)

Signed-off-by: dependabot[bot] <support@github.com>
2021-02-01 02:00:29 +00:00
Liam Newman
9e7c33369c Merge pull request #1026 from jordiolivares/feature/timestamp-support
Add timestamp field support for the commits sent by GHEventPayload.Push
2021-01-28 18:00:48 -08:00
Jordi Olivares Provencio
8943ca6d1a Reformat the JavaDoc 2021-01-28 22:36:24 +01:00
Jordi Olivares Provencio
b3460c1f9d Add timestamp field support for the commits sent by GHEventPayload.Push 2021-01-28 22:34:09 +01:00
Liam Newman
5166c9265f Merge pull request #1022 from bitwiseman/task/enum-coverage
Code Coverage update
2021-01-25 12:24:24 -08:00
Liam Newman
35c8cfa01d Push method code coverage bar to 50 percent
This change adds or update a swath of tests to push method code coverage numbers up.
Yes, method coverage is not super meaningful, but it is one metric that we can use to
ensure at least minimal coverage of this library.

Almost no product changes in here.
2021-01-25 12:08:57 -08:00
Liam Newman
8e6dbf3772 [maven-release-plugin] prepare for next development iteration 2021-01-14 20:14:08 -08:00
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
4d9aed90d6 Merge branch 'master' into download-repository-archives 2021-01-04 09:24:25 -08:00
Liam Newman
4bec27fd49 [maven-release-plugin] prepare for next development iteration 2021-01-04 01:48:27 -08:00
Liam Newman
f1720b7bbc Move archive readers to use new functional interfaces 2021-01-04 01:32:36 -08:00
Liam Newman
7a79a18d8f Add functional interfaces 2021-01-04 01:30:59 -08:00
Liam Newman
472034c950 Add codeload and fix redirects 2021-01-04 01:27:47 -08:00
M. Abdullah Onus
b50ab56f9e Fix linting 2021-01-03 15:52:06 +03:00
M. Abdullah Onus
26d30663c4 Add deprecated not to Github#getRepositoryById 2021-01-03 15:44:28 +03:00
M. Abdullah Onus
ffecc390eb Fix the type of the id parameter of Github#getRepositoryById 2021-01-03 15:35:00 +03:00
Liam Newman
aae5c56a31 Merge remote-tracking branch 'upstream/master' into download-repository-archives 2020-12-31 09:55:28 -08: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
d7931777bc Merge branch 'master' into download-repository-archives 2020-11-05 08:38:31 -08:00
Mårten Gustafson
bb48d55bd4 Add support for downloading zip and tar archives of repositories. 2020-10-20 21:45:27 +02: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
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
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
Marcos.Cela
4688870984 add CredentialProvider interface 2020-09-24 09:01:52 +02:00
433 changed files with 61650 additions and 2504 deletions

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
*.java text eol=lf

View File

@@ -7,8 +7,7 @@ We love getting PRs, but we hate asking people for the same basic changes every
- [ ] Push your changes to a branch other than `master`. Create your PR from that branch.
- [ ] Add JavaDocs and other comments
- [ ] Write tests that run and pass in CI. See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to capture snapshot data.
- [ ] 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.
- [ ] 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:

View File

@@ -2,14 +2,18 @@ name: CI
on: [push, pull_request]
# this is required by spotless for JDK 16+
env:
JAVA_11_PLUS_MAVEN_OPTS: "--add-opens jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-opens jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED --add-opens jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED --add-opens jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED --add-opens jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED"
jobs:
build:
name: build-only (Java ${{ matrix.java }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
java: [ 13 ]
java: [ 16 ]
steps:
- uses: actions/checkout@v2
- name: Set up JDK
@@ -17,18 +21,21 @@ jobs:
with:
java-version: ${{ matrix.java }}
- name: Cached .m2
uses: actions/cache@v2
uses: actions/cache@v2.1.4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
- name: Maven Install (skipTests)
env:
MAVEN_OPTS: ${{ env.JAVA_11_PLUS_MAVEN_OPTS }}
run: mvn -B install -DskipTests -D enable-ci --file pom.xml
site:
name: site (Java ${{ matrix.java }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
java: [ 8, 11 ]
steps:
@@ -37,7 +44,7 @@ jobs:
uses: actions/setup-java@v1
with:
java-version: ${{ matrix.java }}
- uses: actions/cache@v2
- uses: actions/cache@v2.1.4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
@@ -49,24 +56,37 @@ jobs:
name: test (${{ matrix.os }}, Java ${{ matrix.java }})
runs-on: ${{ matrix.os }}-latest
strategy:
fail-fast: false
matrix:
os: [ ubuntu, windows ]
java: [ 8, 11, 13, 15-ea ]
java: [ 8, 11, 16 ]
steps:
- uses: actions/checkout@v2
- name: Set up JDK
uses: actions/setup-java@v1
with:
java-version: ${{ matrix.java }}
- uses: actions/cache@v2
- uses: actions/cache@v2.1.4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
# JDK 8
- name: Maven Install without Code Coverage
if: matrix.os == 'windows'
if: matrix.os == 'windows' && matrix.java == '8'
run: mvn -B install --file pom.xml
- name: Maven Install with Code Coverage
if: matrix.os != 'windows'
if: matrix.os != 'windows' && matrix.java == '8'
run: mvn -B install -D enable-ci --file pom.xml
# JDK 11+
- name: Maven Install without Code Coverage
if: matrix.os == 'windows' && matrix.java != '8'
env:
MAVEN_OPTS: ${{ env.JAVA_11_PLUS_MAVEN_OPTS }}
run: mvn -B install --file pom.xml
- name: Maven Install with Code Coverage
if: matrix.os != 'windows' && matrix.java != '8'
env:
MAVEN_OPTS: ${{ env.JAVA_11_PLUS_MAVEN_OPTS }}
run: mvn -B install -D enable-ci --file pom.xml

View File

@@ -21,7 +21,7 @@ Example for a single test case:
### Setting up credential
1. Create an OAuth token on github.com
1. Create a "Personal access token" on https://github.com/ (`Settings` > `Developer settings` > `Personal access tokens`)
2. Set the GITHUB_OAUTH environment variable to the value of that token
3. Set the system property `test.github.useProxy` (usually like "-Dtest.github.useProxy" as a Java VM option)

112
pom.xml
View File

@@ -2,7 +2,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.kohsuke</groupId>
<artifactId>github-api</artifactId>
<version>1.118</version>
<version>1.124</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.118</tag>
<tag>github-api-1.124</tag>
</scm>
<distributionManagement>
@@ -33,18 +33,20 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spotbugs-maven-plugin.version>4.1.4</spotbugs-maven-plugin.version>
<spotbugs.version>4.1.3</spotbugs.version>
<spotbugs-maven-plugin.version>4.2.0</spotbugs-maven-plugin.version>
<spotbugs.version>4.2.1</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>
<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>
<jacoco.coverage.target.bundle.method>0.70</jacoco.coverage.target.bundle.method>
<jacoco.coverage.target.class.method>0.50</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>
<surefire.argLine />
</properties>
<build>
@@ -152,59 +154,39 @@
<!-- Sample only -->
<exclude>org.kohsuke.github.example.*</exclude>
<!-- No methods -->
<exclude>org.kohsuke.github.Previews</exclude>
<!-- Deprecated -->
<exclude>org.kohsuke.github.extras.OkHttp3Connector</exclude>
<exclude>org.kohsuke.github.EnforcementLevel</exclude>
<exclude>org.kohsuke.github.GHPerson.1</exclude>
<!-- These fail coverage on windows because tests are disabled -->
<exclude>org.kohsuke.github.GHAsset</exclude>
<exclude>org.kohsuke.github.GHReleaseBuilder</exclude>
<exclude>org.kohsuke.github.GHRelease</exclude>
<!-- TODO: Some coverage, but more needed -->
<exclude>org.kohsuke.github.GHPullRequestReviewBuilder.DraftReviewComment</exclude>
<exclude>org.kohsuke.github.GHIssue.PullRequest</exclude>
<exclude>org.kohsuke.github.GHCommitSearchBuilder</exclude>
<exclude>org.kohsuke.github.GHRepositorySearchBuilder</exclude>
<exclude>org.kohsuke.github.GHUserSearchBuilder</exclude>
<!-- TODO: These still need test coverage -->
<exclude>org.kohsuke.github.GHBranchProtection.RequiredSignatures</exclude>
<exclude>org.kohsuke.github.GHBranchProtectionBuilder.Restrictions</exclude>
<exclude>org.kohsuke.github.GHBranchProtection.Restrictions</exclude>
<exclude>org.kohsuke.github.GHCommentAuthorAssociation</exclude>
<exclude>org.kohsuke.github.GHCommitBuilder.UserInfo</exclude>
<exclude>org.kohsuke.github.GHCommitState</exclude>
<exclude>org.kohsuke.github.GHCompare.Commit</exclude>
<exclude>org.kohsuke.github.GHCompare.InnerCommit</exclude>
<exclude>org.kohsuke.github.GHCompare.Status</exclude>
<exclude>org.kohsuke.github.GHCompare.Tree</exclude>
<exclude>org.kohsuke.github.GHCompare.User</exclude>
<exclude>org.kohsuke.github.GHCompare</exclude>
<exclude>org.kohsuke.github.GHDeployKey</exclude>
<exclude>org.kohsuke.github.GHDeploymentStatusBuilder</exclude>
<exclude>org.kohsuke.github.GHDirection</exclude>
<exclude>org.kohsuke.github.GHEmail</exclude>
<exclude>org.kohsuke.github.GHEventPayload.Ping</exclude>
<exclude>org.kohsuke.github.GHEventPayload.Release</exclude>
<exclude>org.kohsuke.github.GHException</exclude>
<exclude>org.kohsuke.github.GHHook</exclude>
<exclude>org.kohsuke.github.GHHooks.OrgContext</exclude>
<exclude>org.kohsuke.github.GHInvitation</exclude>
<exclude>org.kohsuke.github.GHMilestoneState</exclude>
<exclude>org.kohsuke.github.GHOrgHook</exclude>
<exclude>org.kohsuke.github.GHProject.ProjectStateFilter</exclude>
<exclude>org.kohsuke.github.GHPullRequestCommitDetail.Authorship</exclude>
<exclude>org.kohsuke.github.GHPullRequestCommitDetail.Commit</exclude>
<exclude>org.kohsuke.github.GHPullRequestCommitDetail.CommitPointer</exclude>
<exclude>org.kohsuke.github.GHPullRequestCommitDetail.Tree</exclude>
<exclude>org.kohsuke.github.GHPullRequestCommitDetail</exclude>
<exclude>org.kohsuke.github.GHPullRequestFileDetail</exclude>
<exclude>org.kohsuke.github.GHPullRequestQueryBuilder.Sort</exclude>
<exclude>org.kohsuke.github.GHReleaseUpdater</exclude>
<exclude>org.kohsuke.github.GHRepository.ForkSort</exclude>
<exclude>org.kohsuke.github.GHRequestedAction</exclude>
<exclude>org.kohsuke.github.GHStargazer</exclude>
<exclude>org.kohsuke.github.GHTagObject</exclude>
<exclude>org.kohsuke.github.GHTeam.Role</exclude>
<exclude>org.kohsuke.github.GHUserSearchBuilder.Sort</exclude>
<exclude>org.kohsuke.github.GHVerifiedKey</exclude>
</excludes>
</rule>
@@ -292,6 +274,7 @@
<id>default-test</id>
<configuration>
<excludesFile>src/test/resources/slow-or-flaky-tests.txt</excludesFile>
<argLine>${surefire.argLine}</argLine>
</configuration>
</execution>
<execution>
@@ -304,6 +287,7 @@
<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>
<argLine>${surefire.argLine}</argLine>
</configuration>
</execution>
</executions>
@@ -311,7 +295,7 @@
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>animal-sniffer-maven-plugin</artifactId>
<version>1.19</version>
<version>1.20</version>
<configuration>
<signature>
<groupId>org.codehaus.mojo.signature</groupId>
@@ -344,13 +328,14 @@
<plugin>
<groupId>com.diffplug.spotless</groupId>
<artifactId>spotless-maven-plugin</artifactId>
<version>2.6.1</version>
<version>2.8.1</version>
<executions>
<execution>
<id>spotless-check</id>
<phase>process-sources</phase>
<!-- runs in verify phase by default -->
<goals>
<goal>${spotless-maven-plugin.goal}</goal>
<!-- can be disabled using -Dspotless.check.skip=true -->
<goal>check</goal>
</goals>
</execution>
</executions>
@@ -409,7 +394,7 @@
<dependency>
<groupId>com.tngtech.archunit</groupId>
<artifactId>archunit</artifactId>
<version>0.15.0</version>
<version>0.17.0</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -434,13 +419,13 @@
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.2</version>
<version>2.12.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
@@ -489,20 +474,20 @@
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.2</version>
<scope>test</scope>
<version>${jjwt.suite.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.2</version>
<scope>test</scope>
<version>${jjwt.suite.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.2</version>
<scope>test</scope>
<version>${jjwt.suite.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.squareup.okio</groupId>
@@ -539,7 +524,7 @@
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.6.28</version>
<version>3.7.7</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -580,6 +565,16 @@
</pluginRepository>
</pluginRepositories>
<profiles>
<profile>
<id>jdk11+</id>
<activation>
<jdk>[11,)</jdk>
</activation>
<properties>
<!-- this is required for GithubHttpUrlConnectionClient#setRequestMethod() to work with JDK 16+ -->
<surefire.argLine>--add-opens java.base/java.net=ALL-UNNAMED</surefire.argLine>
</properties>
</profile>
<profile>
<id>ci-non-windows</id>
<activation>
@@ -591,7 +586,8 @@
</os>
</activation>
<properties>
<spotless-maven-plugin.goal>check</spotless-maven-plugin.goal>
<!-- Only fail code coverage on non-windows machines -->
<jacoco.haltOnFailure>true</jacoco.haltOnFailure>
</properties>
</profile>
<profile>
@@ -601,23 +597,31 @@
<name>enable-ci</name>
</property>
</activation>
<properties>
<jacoco.haltOnFailure>true</jacoco.haltOnFailure>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>com.diffplug.spotless</groupId>
<artifactId>spotless-maven-plugin</artifactId>
<executions>
<execution>
<id>spotless-check</id>
<!-- In CI, run check early in the build -->
<phase>process-sources</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>release</id>
<properties>
<spotless-maven-plugin.goal>check</spotless-maven-plugin.goal>
</properties>
<build>
<plugins>
<plugin>

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.

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

View File

@@ -8,8 +8,8 @@ import java.net.URL;
import java.util.List;
import java.util.Map;
import static org.kohsuke.github.Previews.GAMBIT;
import static org.kohsuke.github.Previews.MACHINE_MAN;
import static org.kohsuke.github.internal.Previews.GAMBIT;
import static org.kohsuke.github.internal.Previews.MACHINE_MAN;
/**
* A Github App Installation.

View File

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

View File

@@ -6,7 +6,7 @@ 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.

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

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

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;
@@ -151,7 +152,7 @@ public final class GHCheckRunBuilder {
}
GHCheckRun run = requester.with("output", output).with("actions", actions).fetch(GHCheckRun.class).wrap(repo);
while (!extraAnnotations.isEmpty()) {
Output output2 = new Output(output.title, output.summary);
Output output2 = new Output(output.title, output.summary).withText(output.text);
int i = Math.min(extraAnnotations.size(), MAX_ANNOTATIONS);
output2.annotations = extraAnnotations.subList(0, i);
extraAnnotations = extraAnnotations.subList(i, extraAnnotations.size());

View File

@@ -11,8 +11,8 @@ import java.util.Collections;
import java.util.Date;
import java.util.List;
import static org.kohsuke.github.Previews.ANTIOPE;
import static org.kohsuke.github.Previews.GROOT;
import static org.kohsuke.github.internal.Previews.ANTIOPE;
import static org.kohsuke.github.internal.Previews.GROOT;
/**
* A commit in a repository.

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.)

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;

View File

@@ -2,7 +2,7 @@ package org.kohsuke.github;
import java.io.IOException;
import static org.kohsuke.github.Previews.BAPTISTE;
import static org.kohsuke.github.internal.Previews.BAPTISTE;
/**
* Creates a repository

View File

@@ -1,5 +1,7 @@
package org.kohsuke.github;
import org.kohsuke.github.internal.Previews;
import java.io.IOException;
import java.net.URL;
import java.util.Map;

View File

@@ -1,5 +1,7 @@
package org.kohsuke.github;
import org.kohsuke.github.internal.Previews;
import java.io.IOException;
import java.util.List;

View File

@@ -1,5 +1,7 @@
package org.kohsuke.github;
import org.kohsuke.github.internal.Previews;
/**
* Represents the state of deployment
*/

View File

@@ -1,5 +1,7 @@
package org.kohsuke.github;
import org.kohsuke.github.internal.Previews;
import java.net.URL;
import java.util.Locale;

View File

@@ -1,5 +1,7 @@
package org.kohsuke.github;
import org.kohsuke.github.internal.Previews;
import java.io.IOException;
/**

View File

@@ -1,6 +1,7 @@
package org.kohsuke.github;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.kohsuke.github.internal.Previews;
import java.io.IOException;
import java.net.URL;

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,

View File

@@ -5,6 +5,7 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.IOException;
import java.io.Reader;
import java.util.Date;
import java.util.List;
/**
@@ -380,9 +381,9 @@ public class GHEventPayload extends GitHubInteractiveObject {
}
/**
* Gets label.
* Gets the added or removed label for labeled/unlabeled events.
*
* @return the label
* @return label the added or removed label
*/
public GHLabel getLabel() {
return label;
@@ -519,6 +520,10 @@ public class GHEventPayload extends GitHubInteractiveObject {
public static class Issue extends GHEventPayload {
private GHIssue issue;
private GHLabel label;
private GHIssueChanges changes;
/**
* Gets issue.
*
@@ -538,6 +543,24 @@ public class GHEventPayload extends GitHubInteractiveObject {
this.issue = issue;
}
/**
* Gets the added or removed label for labeled/unlabeled events.
*
* @return label the added or removed label
*/
public GHLabel getLabel() {
return label;
}
/**
* Get changes (for action="edited")
*
* @return changes
*/
public GHIssueChanges getChanges() {
return changes;
}
@Override
void wrapUp(GitHub root) {
super.wrapUp(root);
@@ -1067,7 +1090,7 @@ public class GHEventPayload extends GitHubInteractiveObject {
public static class PushCommit {
private GitUser author;
private GitUser committer;
private String url, sha, message;
private String url, sha, message, timestamp;
private boolean distinct;
private List<String> added, removed, modified;
@@ -1156,6 +1179,15 @@ public class GHEventPayload extends GitHubInteractiveObject {
public List<String> getModified() {
return modified;
}
/**
* Obtains the timestamp of the commit
*
* @return the timestamp
*/
public Date getTimestamp() {
return GitHubClient.parseDate(timestamp);
}
}
}

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.
@@ -312,7 +312,7 @@ public class GHIssue extends GHObject implements Reactable {
}
/**
* Sets labels.
* Sets labels on the target to a specific list.
*
* @param labels
* the labels
@@ -326,6 +326,8 @@ public class GHIssue extends GHObject implements Reactable {
/**
* Adds labels to the issue.
*
* Labels that are already present on the target are ignored.
*
* @param names
* Names of the label
* @throws IOException
@@ -338,6 +340,8 @@ public class GHIssue extends GHObject implements Reactable {
/**
* Add labels.
*
* Labels that are already present on the target are ignored.
*
* @param labels
* the labels
* @throws IOException
@@ -350,6 +354,8 @@ public class GHIssue extends GHObject implements Reactable {
/**
* Add labels.
*
* Labels that are already present on the target are ignored.
*
* @param labels
* the labels
* @throws IOException
@@ -360,21 +366,27 @@ public class GHIssue extends GHObject implements Reactable {
}
private void _addLabels(Collection<String> names) throws IOException {
List<String> newLabels = new ArrayList<String>();
for (GHLabel label : getLabels()) {
newLabels.add(label.getName());
}
for (String name : names) {
if (!newLabels.contains(name)) {
newLabels.add(name);
}
}
setLabels(newLabels.toArray(new String[0]));
root.createRequest().with("labels", names).method("POST").withUrlPath(getIssuesApiRoute() + "/labels").send();
}
/**
* Remove a given label by name from this issue.
* Remove a single label.
*
* Attempting to remove a label that is not present throws {@link GHFileNotFoundException}.
*
* @param name
* the name
* @throws IOException
* the io exception, throws {@link GHFileNotFoundException} if label was not present.
*/
public void removeLabel(String name) throws IOException {
root.createRequest().method("DELETE").withUrlPath(getIssuesApiRoute() + "/labels", name).send();
}
/**
* Remove a collection of labels.
*
* Attempting to remove labels that are not present on the target are ignored.
*
* @param names
* the names
@@ -386,7 +398,9 @@ public class GHIssue extends GHObject implements Reactable {
}
/**
* Remove labels.
* Remove a collection of labels.
*
* Attempting to remove labels that are not present on the target are ignored.
*
* @param labels
* the labels
@@ -399,7 +413,9 @@ public class GHIssue extends GHObject implements Reactable {
}
/**
* Remove labels.
* Remove a collection of labels.
*
* Attempting to remove labels that are not present on the target are ignored.
*
* @param labels
* the labels
@@ -411,15 +427,13 @@ public class GHIssue extends GHObject implements Reactable {
}
private void _removeLabels(Collection<String> names) throws IOException {
List<String> newLabels = new ArrayList<String>();
for (GHLabel l : getLabels()) {
if (!names.contains(l.getName())) {
newLabels.add(l.getName());
for (String name : names) {
try {
removeLabel(name);
} catch (GHFileNotFoundException e) {
// when trying to remove multiple labels, we ignore already removed
}
}
setLabels(newLabels.toArray(new String[0]));
}
/**

View File

@@ -0,0 +1,49 @@
package org.kohsuke.github;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
/**
* Wrapper to define changed fields on issues action="edited"
*
* @see GHEventPayload.Issue
*/
@SuppressFBWarnings("UWF_UNWRITTEN_FIELD")
public class GHIssueChanges {
private GHFrom title;
private GHFrom body;
/**
* Old issue title.
*
* @return old issue title (or null if not changed)
*/
public GHFrom getTitle() {
return title;
}
/**
* Old issue body.
*
* @return old issue body (or null if not changed)
*/
public GHFrom getBody() {
return body;
}
/**
* 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

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

View File

@@ -2,6 +2,7 @@ package org.kohsuke.github;
import com.fasterxml.jackson.annotation.JacksonInject;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.IOException;
import java.util.ArrayList;
@@ -22,6 +23,11 @@ import javax.annotation.Nonnull;
*/
public class GHLabel extends GitHubInteractiveObject {
private long id;
private String nodeId;
@JsonProperty("default")
private boolean default_;
@Nonnull
private String url, name, color;
@@ -42,6 +48,24 @@ public class GHLabel extends GitHubInteractiveObject {
return Objects.requireNonNull(root);
}
/**
* Gets id.
*
* @return the id
*/
public long getId() {
return id;
}
/**
* Gets node id.
*
* @return the node id.
*/
public String getNodeId() {
return nodeId;
}
/**
* Gets url.
*
@@ -82,6 +106,15 @@ public class GHLabel extends GitHubInteractiveObject {
return description;
}
/**
* If the label is one of the default labels created by GitHub automatically.
*
* @return true if the label is a default one
*/
public boolean isDefault() {
return default_;
}
/**
* Sets color.
*

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.

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.

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.

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.

View File

@@ -36,8 +36,8 @@ import java.util.Objects;
import javax.annotation.CheckForNull;
import static org.kohsuke.github.Previews.LYDIAN;
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.

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

@@ -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
@@ -153,7 +153,20 @@ public class GHPullRequestReviewComment extends GHObject implements Reactable {
* @return the api route
*/
protected String getApiRoute() {
return "/repos/" + owner.getRepository().getFullName() + "/pulls/comments/" + getId();
return getApiRoute(false);
}
/**
* Gets api route.
*
* @param includePullNumber
* if true, includes the owning pull request's number in the route.
*
* @return the api route
*/
protected String getApiRoute(boolean includePullNumber) {
return "/repos/" + owner.getRepository().getFullName() + "/pulls"
+ (includePullNumber ? "/" + owner.getNumber() : "") + "/comments/" + getId();
}
/**
@@ -192,8 +205,7 @@ public class GHPullRequestReviewComment extends GHObject implements Reactable {
return owner.root.createRequest()
.method("POST")
.with("body", body)
.with("in_reply_to", getId())
.withUrlPath(getApiRoute() + "/comments")
.withUrlPath(getApiRoute(true) + "/replies")
.fetch(GHPullRequestReviewComment.class)
.wrapUp(owner);
}

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.

View File

@@ -30,6 +30,7 @@ import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.apache.commons.lang3.StringUtils;
import org.kohsuke.github.function.InputStreamFunction;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -49,7 +50,6 @@ import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;
import java.util.WeakHashMap;
@@ -57,7 +57,8 @@ import java.util.WeakHashMap;
import javax.annotation.Nonnull;
import static java.util.Arrays.*;
import static org.kohsuke.github.Previews.*;
import static java.util.Objects.requireNonNull;
import static org.kohsuke.github.internal.Previews.*;
/**
* A repository on GitHub.
@@ -1782,7 +1783,7 @@ public class GHRepository extends GHObject {
return root.createRequest()
.withHeader("Accept", "application/vnd.github.v3.raw")
.withUrlPath(target)
.fetchStream();
.fetchStream(Requester::copyInputStream);
}
/**
@@ -2809,7 +2810,7 @@ public class GHRepository extends GHObject {
.with("mode", mode == null ? null : mode.toString())
.with("context", getFullName())
.withUrlPath("/markdown")
.fetchStream(),
.fetchStream(Requester::copyInputStream),
"UTF-8");
}
@@ -2963,6 +2964,52 @@ public class GHRepository extends GHObject {
.wrap(this);
}
/**
* Streams a zip archive of the repository, optionally at a given <code>ref</code>.
*
* @param <T>
* the type of result
* @param streamFunction
* The {@link InputStreamFunction} that will process the stream
* @param ref
* if <code>null</code> the repository's default branch, usually <code>master</code>,
* @throws IOException
* The IO exception.
* @return the result of reading the stream.
*/
public <T> T readZip(InputStreamFunction<T> streamFunction, String ref) throws IOException {
return downloadArchive("zip", ref, streamFunction);
}
/**
* Streams a tar archive of the repository, optionally at a given <code>ref</code>.
*
* @param <T>
* the type of result
* @param streamFunction
* The {@link InputStreamFunction} that will process the stream
* @param ref
* if <code>null</code> the repository's default branch, usually <code>master</code>,
* @throws IOException
* The IO exception.
* @return the result of reading the stream.
*/
public <T> T readTar(InputStreamFunction<T> streamFunction, String ref) throws IOException {
return downloadArchive("tar", ref, streamFunction);
}
private <T> T downloadArchive(@Nonnull String type,
@CheckForNull String ref,
@Nonnull InputStreamFunction<T> streamFunction) throws IOException {
requireNonNull(streamFunction, "Sink must not be null");
String tailUrl = getApiTailUrl(type + "ball");
if (ref != null) {
tailUrl += "/" + ref;
}
final Requester builder = root.createRequest().method("GET").withUrlPath(tailUrl);
return builder.fetchStream(streamFunction);
}
/**
* Populate this object.
*
@@ -2974,7 +3021,7 @@ public class GHRepository extends GHObject {
return; // can't populate if the root is offline
}
final URL url = Objects.requireNonNull(getUrl(), "Missing instance URL!");
final URL url = requireNonNull(getUrl(), "Missing instance URL!");
try {
// IMPORTANT: the url for repository records does not reliably point to the API url.

View File

@@ -3,7 +3,7 @@ package org.kohsuke.github;
import java.io.IOException;
import java.net.URL;
import static org.kohsuke.github.Previews.BAPTISTE;
import static org.kohsuke.github.internal.Previews.BAPTISTE;
abstract class GHRepositoryBuilder<S> extends AbstractBuilder<GHRepository, S> {

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.
*
@@ -557,11 +632,28 @@ public class GitHub {
* @return the repository by id
* @throws IOException
* the io exception
*
* @deprecated Do not use this method. It was added due to misunderstanding of the type of parameter. Use
* {@link #getRepositoryById(long)} instead
*/
@Deprecated
public GHRepository getRepositoryById(String id) throws IOException {
return createRequest().withUrlPath("/repositories/" + id).fetch(GHRepository.class).wrap(this);
}
/**
* Gets the repository object from its ID
*
* @param id
* the id
* @return the repository by id
* @throws IOException
* the io exception
*/
public GHRepository getRepositoryById(long id) throws IOException {
return createRequest().withUrlPath("/repositories/" + id).fetch(GHRepository.class).wrap(this);
}
/**
* Returns a list of popular open source licenses
*
@@ -1203,7 +1295,7 @@ public class GitHub {
.with(new ByteArrayInputStream(text.getBytes("UTF-8")))
.contentType("text/plain;charset=UTF-8")
.withUrlPath("/markdown/raw")
.fetchStream(),
.fetchStream(Requester::copyInputStream),
"UTF-8");
}

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));
}
/**
@@ -421,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.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.net.*;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
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.*;
import java.util.function.Consumer;
import java.util.logging.Logger;
@@ -56,17 +42,13 @@ 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;
@@ -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,33 +90,38 @@ 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 {
@@ -202,7 +186,13 @@ 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;
}
}
/**
@@ -224,6 +214,11 @@ abstract class GitHubClient {
return getRateLimit(RateLimitTarget.NONE);
}
@CheckForNull
protected String getEncodedAuthorization() throws IOException {
return authorizationProvider.getEncodedAuthorization();
}
@Nonnull
GHRateLimit getRateLimit(@Nonnull RateLimitTarget rateLimitTarget) throws IOException {
GHRateLimit result;
@@ -394,7 +389,6 @@ abstract class GitHubClient {
"GitHub API request [" + (login == null ? "anonymous" : login) + "]: "
+ request.method() + " " + request.url().toString());
}
rateLimitChecker.checkRateLimit(this, request);
responseInfo = getResponseInfo(request);

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

View File

@@ -20,4 +20,8 @@ abstract class GitHubInteractiveObject {
GitHubInteractiveObject(GitHub root) {
this.root = root;
}
GitHub getRoot() {
return root;
}
}

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;

View File

@@ -4,6 +4,7 @@ import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.InjectableValues;
import com.fasterxml.jackson.databind.JsonMappingException;
import org.apache.commons.io.IOUtils;
import org.kohsuke.github.function.FunctionThrows;
import java.io.Closeable;
import java.io.IOException;
@@ -194,24 +195,11 @@ class GitHubResponse<T> {
/**
* Represents a supplier of results that can throw.
*
* <p>
* This is a <a href="package-summary.html">functional interface</a> whose functional method is
* {@link #apply(ResponseInfo)}.
*
* @param <T>
* the type of results supplied by this supplier
*/
@FunctionalInterface
interface BodyHandler<T> {
/**
* Gets a result.
*
* @return a result
* @throws IOException
* if an I/O Exception occurs.
*/
T apply(ResponseInfo input) throws IOException;
interface BodyHandler<T> extends FunctionThrows<ResponseInfo, T, IOException> {
}
/**

View File

@@ -1,5 +1,7 @@
package org.kohsuke.github;
import org.kohsuke.github.internal.Previews;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

View File

@@ -2,7 +2,7 @@ package org.kohsuke.github;
import java.io.IOException;
import static org.kohsuke.github.Previews.SQUIRREL_GIRL;
import static org.kohsuke.github.internal.Previews.SQUIRREL_GIRL;
/**
* Those {@link GHObject}s that can have {@linkplain GHReaction reactions}.

View File

@@ -23,7 +23,9 @@
*/
package org.kohsuke.github;
import edu.umd.cs.findbugs.annotations.NonNull;
import org.apache.commons.io.IOUtils;
import org.kohsuke.github.function.InputStreamFunction;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@@ -106,15 +108,31 @@ class Requester extends GitHubRequest.Builder<Requester> {
* Response input stream. There are scenarios where direct stream reading is needed, however it is better to use
* {@link #fetch(Class)} where possible.
*
* @return the input stream
* @throws IOException
* the io exception
*/
public InputStream fetchStream() throws IOException {
return client
.sendRequest(this,
(responseInfo) -> new ByteArrayInputStream(IOUtils.toByteArray(responseInfo.bodyStream())))
.body();
public <T> T fetchStream(@Nonnull InputStreamFunction<T> handler) throws IOException {
return client.sendRequest(this, (responseInfo) -> handler.apply(responseInfo.bodyStream())).body();
}
/**
* Helper function to make it easy to pull streams.
*
* Copies an input stream to an in-memory input stream. The performance on this is not great but
* {@link GitHubResponse.ResponseInfo#bodyStream()} is closed at the end of every call to
* {@link GitHubClient#sendRequest(GitHubRequest, GitHubResponse.BodyHandler)}, so any reads to the original input
* stream must be completed before then. There are a number of deprecated methods that return {@link InputStream}.
* This method keeps all of them using the same code path.
*
* @param inputStream
* the input stream to be copied
* @return an in-memory copy of the passed input stream
* @throws IOException
* if an error occurs while copying the stream
*/
@NonNull
public static InputStream copyInputStream(InputStream inputStream) throws IOException {
return new ByteArrayInputStream(IOUtils.toByteArray(inputStream));
}
/**

View File

@@ -0,0 +1,43 @@
package org.kohsuke.github.authorization;
import java.io.IOException;
/**
* Provides a functional interface that returns a valid encodedAuthorization. This strategy allows for a provider that
* dynamically changes the credentials. Each request will request the credentials from the provider.
*/
public interface AuthorizationProvider {
/**
* An static instance for an ANONYMOUS authorization provider
*/
AuthorizationProvider ANONYMOUS = new AnonymousAuthorizationProvider();
/**
* Returns the credentials to be used with a given request. As an example, a authorization provider for a bearer
* token will return something like:
*
* <pre>
* {@code
* &#64;Override
* public String getEncodedAuthorization() {
* return "Bearer myBearerToken";
* }
* }
* </pre>
*
* @return encoded authorization string, can be null
* @throws IOException
* on any error that prevents the provider from getting a valid authorization
*/
String getEncodedAuthorization() throws IOException;
/**
* A {@link AuthorizationProvider} that ensures that no credentials are returned
*/
class AnonymousAuthorizationProvider implements AuthorizationProvider {
@Override
public String getEncodedAuthorization() throws IOException {
return null;
}
}
}

View File

@@ -0,0 +1,123 @@
package org.kohsuke.github.authorization;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import javax.annotation.CheckForNull;
/**
* A {@link AuthorizationProvider} that always returns the same credentials
*/
public class ImmutableAuthorizationProvider implements AuthorizationProvider {
private final String authorization;
public ImmutableAuthorizationProvider(String authorization) {
this.authorization = authorization;
}
/**
* Builds and returns a {@link AuthorizationProvider} from a given oauthAccessToken
*
* @param oauthAccessToken
* The token
* @return a correctly configured {@link AuthorizationProvider} that will always return the same provided
* oauthAccessToken
*/
public static AuthorizationProvider fromOauthToken(String oauthAccessToken) {
return new UserProvider(String.format("token %s", oauthAccessToken));
}
/**
* Builds and returns a {@link AuthorizationProvider} from a given oauthAccessToken
*
* @param oauthAccessToken
* The token
* @param login
* The login for this token
*
* @return a correctly configured {@link AuthorizationProvider} that will always return the same provided
* oauthAccessToken
*/
public static AuthorizationProvider fromOauthToken(String oauthAccessToken, String login) {
return new UserProvider(String.format("token %s", oauthAccessToken), login);
}
/**
* Builds and returns a {@link AuthorizationProvider} from a given App Installation Token
*
* @param appInstallationToken
* A string containing the GitHub App installation token
* @return the configured Builder from given GitHub App installation token.
*/
public static AuthorizationProvider fromAppInstallationToken(String appInstallationToken) {
return fromOauthToken(appInstallationToken, "");
}
/**
* Builds and returns a {@link AuthorizationProvider} from a given jwtToken
*
* @param jwtToken
* The JWT token
* @return a correctly configured {@link AuthorizationProvider} that will always return the same provided jwtToken
*/
public static AuthorizationProvider fromJwtToken(String jwtToken) {
return new ImmutableAuthorizationProvider(String.format("Bearer %s", jwtToken));
}
/**
* Builds and returns a {@link AuthorizationProvider} from the given user/password pair
*
* @param login
* The login for the user, usually the same as the username
* @param password
* The password for the associated user
* @return a correctly configured {@link AuthorizationProvider} that will always return the credentials for the same
* user and password combo
* @deprecated Login with password credentials are no longer supported by GitHub
*/
@Deprecated
public static AuthorizationProvider fromLoginAndPassword(String login, String password) {
try {
String authorization = (String.format("%s:%s", login, password));
String charsetName = StandardCharsets.UTF_8.name();
String b64encoded = Base64.getEncoder().encodeToString(authorization.getBytes(charsetName));
String encodedAuthorization = String.format("Basic %s", b64encoded);
return new UserProvider(encodedAuthorization, login);
} catch (UnsupportedEncodingException e) {
// If UTF-8 isn't supported, there are bigger problems
throw new IllegalStateException("Could not generate encoded authorization", e);
}
}
@Override
public String getEncodedAuthorization() {
return this.authorization;
}
/**
* An internal class representing all user-related credentials, which are credentials that have a login or should
* query the user endpoint for the login matching this credential.
*/
private static class UserProvider extends ImmutableAuthorizationProvider implements UserAuthorizationProvider {
private final String login;
UserProvider(String authorization) {
this(authorization, null);
}
UserProvider(String authorization, String login) {
super(authorization);
this.login = login;
}
@CheckForNull
@Override
public String getLogin() {
return login;
}
}
}

View File

@@ -0,0 +1,63 @@
package org.kohsuke.github.authorization;
import org.kohsuke.github.BetaApi;
import org.kohsuke.github.GHAppInstallation;
import org.kohsuke.github.GHAppInstallationToken;
import org.kohsuke.github.GitHub;
import java.io.IOException;
import java.time.Duration;
import java.time.Instant;
import java.util.Objects;
import javax.annotation.Nonnull;
/**
* Provides an AuthorizationProvider that performs automatic token refresh.
*/
public class OrgAppInstallationAuthorizationProvider extends GitHub.DependentAuthorizationProvider {
private final String organizationName;
private String latestToken;
@Nonnull
private Instant validUntil = Instant.MIN;
/**
* Provides an AuthorizationProvider that performs automatic token refresh, based on an previously authenticated
* github client.
*
* @param organizationName
* The name of the organization where the application is installed
* @param authorizationProvider
* A authorization provider that returns a JWT token that can be used to refresh the App Installation
* token from GitHub.
*/
@BetaApi
@Deprecated
public OrgAppInstallationAuthorizationProvider(String organizationName,
AuthorizationProvider authorizationProvider) {
super(authorizationProvider);
this.organizationName = organizationName;
}
@Override
public String getEncodedAuthorization() throws IOException {
synchronized (this) {
if (latestToken == null || Instant.now().isAfter(this.validUntil)) {
refreshToken();
}
return String.format("token %s", latestToken);
}
}
private void refreshToken() throws IOException {
GitHub gitHub = this.gitHub();
GHAppInstallation installationByOrganization = gitHub.getApp()
.getInstallationByOrganization(this.organizationName);
GHAppInstallationToken ghAppInstallationToken = installationByOrganization.createToken().create();
this.validUntil = ghAppInstallationToken.getExpiresAt().toInstant().minus(Duration.ofMinutes(5));
this.latestToken = Objects.requireNonNull(ghAppInstallationToken.getToken());
}
}

View File

@@ -0,0 +1,22 @@
package org.kohsuke.github.authorization;
import javax.annotation.CheckForNull;
/**
* Interface for all user-related authorization providers.
*
* {@link AuthorizationProvider}s can apply to a number of different account types. This interface applies to providers
* for user accounts, ones that have a login or should query the "/user" endpoint for the login matching this
* credential.
*/
public interface UserAuthorizationProvider extends AuthorizationProvider {
/**
* Gets the user login name.
*
* @return the user login for this provider, or {@code null} if the login value should be queried from the "/user"
* endpoint.
*/
@CheckForNull
String getLogin();
}

View File

@@ -0,0 +1,130 @@
package org.kohsuke.github.extras.authorization;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.kohsuke.github.authorization.AuthorizationProvider;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.time.Duration;
import java.time.Instant;
import java.util.Base64;
import java.util.Date;
import javax.annotation.Nonnull;
/**
* A authorization provider that gives valid JWT tokens. These tokens are then used to create a time-based token to
* authenticate as an application. This token provider does not provide any kind of caching, and will always request a
* new token to the API.
*/
public class JWTTokenProvider implements AuthorizationProvider {
private final PrivateKey privateKey;
@Nonnull
private Instant validUntil = Instant.MIN;
private String token;
/**
* The identifier for the application
*/
private final String applicationId;
public JWTTokenProvider(String applicationId, File keyFile) throws GeneralSecurityException, IOException {
this(applicationId, keyFile.toPath());
}
public JWTTokenProvider(String applicationId, Path keyPath) throws GeneralSecurityException, IOException {
this(applicationId, new String(Files.readAllBytes(keyPath), StandardCharsets.UTF_8));
}
public JWTTokenProvider(String applicationId, String keyString) throws GeneralSecurityException {
this(applicationId, getPrivateKeyFromString(keyString));
}
public JWTTokenProvider(String applicationId, PrivateKey privateKey) {
this.privateKey = privateKey;
this.applicationId = applicationId;
}
@Override
public String getEncodedAuthorization() throws IOException {
synchronized (this) {
if (Instant.now().isAfter(validUntil)) {
token = refreshJWT();
}
return String.format("Bearer %s", token);
}
}
/**
* Convert a PKCS#8 formatted private key in string format into a java PrivateKey
*
* @param key
* PCKS#8 string
* @return private key
* @throws GeneralSecurityException
* if we couldn't parse the string
*/
private static PrivateKey getPrivateKeyFromString(final String key) throws GeneralSecurityException {
if (key.contains(" RSA ")) {
throw new InvalidKeySpecException(
"Private key must be a PKCS#8 formatted string, to convert it from PKCS#1 use: "
+ "openssl pkcs8 -topk8 -inform PEM -outform PEM -in current-key.pem -out new-key.pem -nocrypt");
}
// Remove all comments and whitespace from PEM
// such as "-----BEGIN PRIVATE KEY-----" and newlines
String privateKeyContent = key.replaceAll("(?m)^--.*", "").replaceAll("\\s", "");
KeyFactory kf = KeyFactory.getInstance("RSA");
try {
byte[] decode = Base64.getDecoder().decode(privateKeyContent);
PKCS8EncodedKeySpec keySpecPKCS8 = new PKCS8EncodedKeySpec(decode);
return kf.generatePrivate(keySpecPKCS8);
} catch (IllegalArgumentException e) {
throw new InvalidKeySpecException("Failed to decode private key: " + e.getMessage(), e);
}
}
private String refreshJWT() {
Instant now = Instant.now();
// Max token expiration is 10 minutes for GitHub
// We use a smaller window since we likely will not need more than a few seconds
Instant expiration = now.plus(Duration.ofMinutes(8));
// Setting the issued at to a time in the past to allow for clock skew
Instant issuedAt = getIssuedAt(now);
// Let's set the JWT Claims
JwtBuilder builder = Jwts.builder()
.setIssuedAt(Date.from(issuedAt))
.setExpiration(Date.from(expiration))
.setIssuer(this.applicationId)
.signWith(privateKey, SignatureAlgorithm.RS256);
// Token will refresh 2 minutes before it expires
validUntil = expiration.minus(Duration.ofMinutes(2));
// Builds the JWT and serializes it to a compact, URL-safe string
return builder.compact();
}
Instant getIssuedAt(Instant now) {
return now.minus(Duration.ofMinutes(2));
}
}

View File

@@ -0,0 +1,25 @@
package org.kohsuke.github.function;
/**
* A functional interface, equivalent to {@link java.util.function.Function} but that allows throwing {@link Throwable}
*
* @param <T>
* the type of input
* @param <R>
* the type of output
* @param <E>
* the type of error
*/
@FunctionalInterface
public interface FunctionThrows<T, R, E extends Throwable> {
/**
* Apply r.
*
* @param input
* the input
* @return the r
* @throws E
* the e
*/
R apply(T input) throws E;
}

View File

@@ -0,0 +1,14 @@
package org.kohsuke.github.function;
import java.io.IOException;
import java.io.InputStream;
/**
* A functional interface, equivalent to {@link java.util.function.Function} but that allows throwing {@link Throwable}
*
* @param <R>
* the type to of object to be returned
*/
@FunctionalInterface
public interface InputStreamFunction<R> extends FunctionThrows<InputStream, R, IOException> {
}

View File

@@ -1,4 +1,4 @@
package org.kohsuke.github;
package org.kohsuke.github.internal;
/**
* Provides the media type strings for GitHub API previews
@@ -7,7 +7,7 @@ package org.kohsuke.github;
*
* @author Kohsuke Kawaguchi
*/
enum Previews {
public enum Previews {
/**
* Check-runs and check-suites

View File

@@ -2,8 +2,14 @@ package org.kohsuke.github;
import io.jsonwebtoken.Jwts;
import org.apache.commons.io.IOUtils;
import org.kohsuke.github.authorization.AuthorizationProvider;
import org.kohsuke.github.extras.authorization.JWTTokenProvider;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
@@ -21,6 +27,26 @@ public class AbstractGHAppInstallationTest extends AbstractGitHubWireMockTest {
private static String PRIVATE_KEY_FILE_APP_2 = "/ghapi-test-app-2.private-key.pem";
private static String PRIVATE_KEY_FILE_APP_3 = "/ghapi-test-app-3.private-key.pem";
private static AuthorizationProvider JWT_PROVIDER_1;
private static AuthorizationProvider JWT_PROVIDER_2;
private static AuthorizationProvider JWT_PROVIDER_3;
AbstractGHAppInstallationTest() {
try {
JWT_PROVIDER_1 = new JWTTokenProvider(TEST_APP_ID_1,
new File(this.getClass().getResource(PRIVATE_KEY_FILE_APP_1).getFile()));
JWT_PROVIDER_2 = new JWTTokenProvider(TEST_APP_ID_2,
new File(this.getClass().getResource(PRIVATE_KEY_FILE_APP_2).getFile()).toPath());
JWT_PROVIDER_3 = new JWTTokenProvider(TEST_APP_ID_3,
new String(
Files.readAllBytes(
new File(this.getClass().getResource(PRIVATE_KEY_FILE_APP_3).getFile()).toPath()),
StandardCharsets.UTF_8));
} catch (GeneralSecurityException | IOException e) {
throw new RuntimeException("These should never fail", e);
}
}
private String createJwtToken(String keyFileResouceName, String appId) {
try {
String keyPEM = IOUtils.toString(this.getClass().getResource(keyFileResouceName), "US-ASCII")
@@ -54,24 +80,25 @@ public class AbstractGHAppInstallationTest extends AbstractGitHubWireMockTest {
.findFirst()
.get();
appInstallation
.setRoot(getGitHubBuilder().withAppInstallationToken(appInstallation.createToken().create().getToken())
.withEndpoint(mockGitHub.apiServer().baseUrl())
.build());
// TODO: this is odd
// appInstallation
// .setRoot(getGitHubBuilder().withAppInstallationToken(appInstallation.createToken().create().getToken())
// .withEndpoint(mockGitHub.apiServer().baseUrl())
// .build());
return appInstallation;
}
protected GHAppInstallation getAppInstallationWithTokenApp1() throws IOException {
return getAppInstallationWithToken(createJwtToken(PRIVATE_KEY_FILE_APP_1, TEST_APP_ID_1));
return getAppInstallationWithToken(JWT_PROVIDER_1.getEncodedAuthorization());
}
protected GHAppInstallation getAppInstallationWithTokenApp2() throws IOException {
return getAppInstallationWithToken(createJwtToken(PRIVATE_KEY_FILE_APP_2, TEST_APP_ID_2));
return getAppInstallationWithToken(JWT_PROVIDER_2.getEncodedAuthorization());
}
protected GHAppInstallation getAppInstallationWithTokenApp3() throws IOException {
return getAppInstallationWithToken(createJwtToken(PRIVATE_KEY_FILE_APP_3, TEST_APP_ID_3));
return getAppInstallationWithToken(JWT_PROVIDER_3.getEncodedAuthorization());
}
}

View File

@@ -100,7 +100,6 @@ public abstract class AbstractGitHubWireMockTest extends Assert {
// This sets the user and password to a placeholder for wiremock testing
// This makes the tests believe they are running with permissions
// The recorded stubs will behave like they running with permissions
builder.oauthToken = null;
builder.withPassword(STUBBED_USER_LOGIN, STUBBED_USER_PASSWORD);
}

View File

@@ -19,10 +19,7 @@ import java.util.*;
import java.util.Map.Entry;
import java.util.regex.Pattern;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.CoreMatchers.sameInstance;
import static org.hamcrest.Matchers.hasProperty;
import static org.hamcrest.Matchers.oneOf;
import static org.hamcrest.Matchers.*;
/**
* Unit test for simple App.
@@ -37,10 +34,11 @@ public class AppTest extends AbstractGitHubWireMockTest {
cleanupUserRepository("github-api-test-rename");
cleanupUserRepository(targetName);
GHRepository r = gitHub.createRepository("github-api-test-rename",
"a test repository",
"http://github-api.kohsuke.org/",
true);
GHRepository r = gitHub.createRepository("github-api-test-rename")
.description("a test repository")
.homepage("http://github-api.kohsuke.org/")
.private_(false)
.create();
assertThat(r.hasIssues(), is(true));
assertThat(r.hasWiki(), is(true));
@@ -137,13 +135,36 @@ public class AppTest extends AbstractGitHubWireMockTest {
@Test
public void testIssueWithNoComment() throws IOException {
GHRepository repository = gitHub.getRepository("kohsuke/test");
List<GHIssueComment> v = repository.getIssue(4).getComments();
GHIssue i = repository.getIssue(4);
List<GHIssueComment> v = i.getComments();
// System.out.println(v);
assertTrue(v.isEmpty());
v = repository.getIssue(3).getComments();
i = repository.getIssue(3);
v = i.getComments();
// System.out.println(v);
assertTrue(v.size() == 3);
assertThat(v.size(), equalTo(3));
assertThat(v.get(0).getHtmlUrl().toString(),
equalTo("https://github.com/kohsuke/test/issues/3#issuecomment-8547249"));
assertThat(v.get(0).getUrl().toString(), endsWith("/repos/kohsuke/test/issues/comments/8547249"));
assertThat(v.get(0).getNodeId(), equalTo("MDEyOklzc3VlQ29tbWVudDg1NDcyNDk="));
assertThat(v.get(0).getParent().getNumber(), equalTo(3));
assertThat(v.get(0).getParent().getId(), equalTo(6863845L));
assertThat(v.get(0).getUser().getLogin(), equalTo("kohsuke"));
assertThat(v.get(0).listReactions().toList().size(), equalTo(0));
assertThat(v.get(1).getHtmlUrl().toString(),
equalTo("https://github.com/kohsuke/test/issues/3#issuecomment-8547251"));
assertThat(v.get(1).getUrl().toString(), endsWith("/repos/kohsuke/test/issues/comments/8547251"));
assertThat(v.get(1).getNodeId(), equalTo("MDEyOklzc3VlQ29tbWVudDg1NDcyNTE="));
assertThat(v.get(1).getParent().getNumber(), equalTo(3));
assertThat(v.get(1).getUser().getLogin(), equalTo("kohsuke"));
List<GHReaction> reactions = v.get(1).listReactions().toList();
assertThat(reactions.size(), equalTo(3));
// TODO: Add comment CRUD test
// TODO: Add reactions CRUD test
}
@Test
@@ -165,49 +186,60 @@ public class AppTest extends AbstractGitHubWireMockTest {
@Test
public void testCreateAndListDeployments() throws IOException {
GHRepository repository = getTestRepository();
GHDeployment deployment = repository.createDeployment("master")
GHDeployment deployment = repository.createDeployment("main")
.payload("{\"user\":\"atmos\",\"room_id\":123456}")
.description("question")
.environment("unittest")
.create();
assertNotNull(deployment.getCreator());
assertNotNull(deployment.getId());
List<GHDeployment> deployments = repository.listDeployments(null, "master", null, "unittest").toList();
assertNotNull(deployments);
assertFalse(Iterables.isEmpty(deployments));
GHDeployment unitTestDeployment = deployments.get(0);
assertEquals("unittest", unitTestDeployment.getEnvironment());
assertEquals("unittest", unitTestDeployment.getOriginalEnvironment());
assertEquals(false, unitTestDeployment.isProductionEnvironment());
assertEquals(true, unitTestDeployment.isTransientEnvironment());
assertEquals("master", unitTestDeployment.getRef());
try {
assertNotNull(deployment.getCreator());
assertNotNull(deployment.getId());
List<GHDeployment> deployments = repository.listDeployments(null, "main", null, "unittest").toList();
assertNotNull(deployments);
assertFalse(Iterables.isEmpty(deployments));
GHDeployment unitTestDeployment = deployments.get(0);
assertEquals("unittest", unitTestDeployment.getEnvironment());
assertEquals("unittest", unitTestDeployment.getOriginalEnvironment());
assertEquals(false, unitTestDeployment.isProductionEnvironment());
assertEquals(false, unitTestDeployment.isTransientEnvironment());
assertEquals("main", unitTestDeployment.getRef());
} finally {
// deployment.delete();
assert true;
}
}
@Ignore("Needs mocking check")
@Test
public void testGetDeploymentStatuses() throws IOException {
GHRepository repository = getTestRepository();
GHDeployment deployment = repository.createDeployment("master")
GHDeployment deployment = repository.createDeployment("main")
.description("question")
.payload("{\"user\":\"atmos\",\"room_id\":123456}")
.create();
GHDeploymentStatus ghDeploymentStatus = deployment.createStatus(GHDeploymentState.QUEUED)
.description("success")
.targetUrl("http://www.github.com")
.logUrl("http://www.github.com/logurl")
.environmentUrl("http://www.github.com/envurl")
.environment("new-ci-env")
.create();
Iterable<GHDeploymentStatus> deploymentStatuses = deployment.listStatuses();
assertNotNull(deploymentStatuses);
assertEquals(1, Iterables.size(deploymentStatuses));
GHDeploymentStatus actualStatus = Iterables.get(deploymentStatuses, 0);
assertEquals(ghDeploymentStatus.getId(), actualStatus.getId());
assertEquals(ghDeploymentStatus.getState(), actualStatus.getState());
assertEquals(ghDeploymentStatus.getLogUrl(), actualStatus.getLogUrl());
// Target url was deprecated and replaced with log url. The gh api will
// prefer the log url value and return it in place of target url.
assertEquals(ghDeploymentStatus.getTargetUrl(), actualStatus.getLogUrl());
try {
GHDeploymentStatus ghDeploymentStatus = deployment.createStatus(GHDeploymentState.QUEUED)
.description("success")
.targetUrl("http://www.github.com")
.logUrl("http://www.github.com/logurl")
.environmentUrl("http://www.github.com/envurl")
.environment("new-ci-env")
.create();
Iterable<GHDeploymentStatus> deploymentStatuses = deployment.listStatuses();
assertNotNull(deploymentStatuses);
assertEquals(1, Iterables.size(deploymentStatuses));
GHDeploymentStatus actualStatus = Iterables.get(deploymentStatuses, 0);
assertEquals(ghDeploymentStatus.getId(), actualStatus.getId());
assertEquals(ghDeploymentStatus.getState(), actualStatus.getState());
assertEquals(ghDeploymentStatus.getLogUrl(), actualStatus.getLogUrl());
// Target url was deprecated and replaced with log url. The gh api will
// prefer the log url value and return it in place of target url.
assertEquals(ghDeploymentStatus.getTargetUrl(), actualStatus.getLogUrl());
assertThat(ghDeploymentStatus.getDeploymentUrl(), equalTo(deployment.getUrl()));
assertThat(ghDeploymentStatus.getRepositoryUrl(), equalTo(repository.getUrl()));
} finally {
// deployment.delete();
assert true;
}
}
@Test
@@ -324,7 +356,7 @@ public class AppTest extends AbstractGitHubWireMockTest {
assertEquals(teamByName.getId(), teamById.getId());
assertEquals(teamByName.getDescription(), teamById.getDescription());
GHTeam teamById2 = organization.getTeam((int) teamByName.getId());
GHTeam teamById2 = organization.getTeam(teamByName.getId());
assertNotNull(teamById2);
assertEquals(teamByName.getId(), teamById2.getId());
@@ -445,6 +477,16 @@ public class AppTest extends AbstractGitHubWireMockTest {
File f = commit.getFiles().get(0);
assertEquals(48, f.getLinesChanged());
assertThat(f.getLinesAdded(), equalTo(40));
assertThat(f.getLinesDeleted(), equalTo(8));
assertThat(f.getPreviousFilename(), nullValue());
assertThat(f.getPatch(), startsWith("@@ -54,6 +54,14 @@\n"));
assertThat(f.getSha(), equalTo("04d3e54017542ad0ff46355eababacd4850ccba5"));
assertThat(f.getBlobUrl().toString(),
equalTo("https://github.com/jenkinsci/jenkins/blob/08c1c9970af4d609ae754fbe803e06186e3206f7/changelog.html"));
assertThat(f.getRawUrl().toString(),
equalTo("https://github.com/jenkinsci/jenkins/raw/08c1c9970af4d609ae754fbe803e06186e3206f7/changelog.html"));
assertEquals("modified", f.getStatus());
assertEquals("changelog.html", f.getFileName());
@@ -464,22 +506,6 @@ public class AppTest extends AbstractGitHubWireMockTest {
assertEquals(1, sha1.size());
}
public void testQueryCommits() throws Exception {
List<String> sha1 = new ArrayList<String>();
for (GHCommit c : gitHub.getUser("jenkinsci")
.getRepository("jenkins")
.queryCommits()
.since(new Date(1199174400000L))
.until(1201852800000L)
.path("pom.xml")
.list()) {
// System.out.println(c.getSHA1());
sha1.add(c.getSHA1());
}
assertEquals("1cccddb22e305397151b2b7b87b4b47d74ca337b", sha1.get(0));
assertEquals(29, sha1.size());
}
@Ignore("Needs mocking check")
@Test
public void testBranches() throws Exception {
@@ -504,23 +530,65 @@ public class AppTest extends AbstractGitHubWireMockTest {
.getRepository("sandbox-ant")
.getCommit("8ae38db0ea5837313ab5f39d43a6f73de3bd9000");
GHCommitComment c = commit.createComment("[testing](http://kohsuse.org/)");
// System.out.println(c);
c.update("updated text");
// System.out.println(c);
c.delete();
try {
assertThat(c.getPath(), nullValue());
assertThat(c.getLine(), equalTo(-1));
assertThat(c.getHtmlUrl().toString(),
containsString(
"kohsuke/sandbox-ant/commit/8ae38db0ea5837313ab5f39d43a6f73de3bd9000#commitcomment-"));
assertThat(c.listReactions().toList(), is(empty()));
c.update("updated text");
assertThat(c.getBody(), equalTo("updated text"));
} finally {
c.delete();
}
}
@Test
public void tryHook() throws Exception {
kohsuke();
GHRepository r = gitHub.getOrganization(GITHUB_API_TEST_ORG).getRepository("github-api");
GHHook hook = r.createWebHook(new URL("http://www.google.com/"));
// System.out.println(hook);
GHOrganization o = gitHub.getOrganization(GITHUB_API_TEST_ORG);
GHRepository r = o.getRepository("github-api");
try {
GHHook hook = r.createWebHook(new URL("http://www.google.com/"));
assertThat(hook.getName(), equalTo("web"));
assertThat(hook.getEvents().size(), equalTo(1));
assertThat(hook.getEvents(), contains(GHEvent.PUSH));
assertThat(hook.getConfig().size(), equalTo(3));
assertThat(hook.isActive(), equalTo(true));
if (mockGitHub.isUseProxy()) {
r = getGitHubBeforeAfter().getOrganization(GITHUB_API_TEST_ORG).getRepository("github-api");
for (GHHook h : r.getHooks()) {
h.delete();
GHHook hook2 = r.getHook((int) hook.getId());
assertThat(hook2.getName(), equalTo("web"));
assertThat(hook2.getEvents().size(), equalTo(1));
assertThat(hook2.getEvents(), contains(GHEvent.PUSH));
assertThat(hook2.getConfig().size(), equalTo(3));
assertThat(hook2.isActive(), equalTo(true));
hook2.ping();
hook2.delete();
hook = o.createWebHook(new URL("http://www.google.com/"));
assertThat(hook.getName(), equalTo("web"));
assertThat(hook.getEvents().size(), equalTo(1));
assertThat(hook.getEvents(), contains(GHEvent.PUSH));
assertThat(hook.getConfig().size(), equalTo(3));
assertThat(hook.isActive(), equalTo(true));
hook2 = o.getHook((int) hook.getId());
assertThat(hook2.getName(), equalTo("web"));
assertThat(hook2.getEvents().size(), equalTo(1));
assertThat(hook2.getEvents(), contains(GHEvent.PUSH));
assertThat(hook2.getConfig().size(), equalTo(3));
assertThat(hook2.isActive(), equalTo(true));
hook2.ping();
hook2.delete();
// System.out.println(hook);
} finally {
if (mockGitHub.isUseProxy()) {
r = getGitHubBeforeAfter().getOrganization(GITHUB_API_TEST_ORG).getRepository("github-api");
for (GHHook h : r.getHooks()) {
h.delete();
}
}
}
}
@@ -529,6 +597,14 @@ public class AppTest extends AbstractGitHubWireMockTest {
public void testEventApi() throws Exception {
for (GHEventInfo ev : gitHub.getEvents()) {
if (ev.getType() == GHEvent.PULL_REQUEST) {
if (ev.getId() == 10680625394L) {
assertThat(ev.getActorLogin(), equalTo("pull[bot]"));
assertThat(ev.getOrganization(), nullValue());
assertThat(ev.getRepository().getFullName(), equalTo("daddyfatstacksBIG/lerna"));
assertThat(ev.getCreatedAt(), equalTo(GitHubClient.parseDate("2019-10-21T21:54:52Z")));
assertThat(ev.getType(), equalTo(GHEvent.PULL_REQUEST));
}
GHEventPayload.PullRequest pr = ev.getPayload(GHEventPayload.PullRequest.class);
assertThat(pr.getNumber(), is(pr.getPullRequest().getNumber()));
}
@@ -859,8 +935,18 @@ public class AppTest extends AbstractGitHubWireMockTest {
for (GHTreeEntry e : masterTree.getTree()) {
if (e.getPath().endsWith(AppTest.class.getSimpleName() + ".java")) {
foundThisFile = true;
assertThat(e.getPath(), equalTo("src/test/java/org/kohsuke/github/AppTest.java"));
assertThat(e.getSha(), equalTo("baad7a7c4cf409f610a0e8c7eba17664eb655c44"));
assertThat(e.getMode(), equalTo("100755"));
assertThat(e.getSize(), greaterThan(30000L));
assertThat(e.getUrl().toString(),
containsString("/repos/hub4j/github-api/git/blobs/baad7a7c4cf409f610a0e8c7eba17664eb655c44"));
GHBlob blob = e.asBlob();
assertThat(e.asBlob().getUrl().toString(),
containsString("/repos/hub4j/github-api/git/blobs/baad7a7c4cf409f610a0e8c7eba17664eb655c44"));
break;
}
}
assertTrue(foundThisFile);
}
@@ -879,6 +965,9 @@ public class AppTest extends AbstractGitHubWireMockTest {
GHLabel e = r.getLabel("enhancement");
assertEquals("enhancement", e.getName());
assertNotNull(e.getUrl());
assertEquals(177339106, e.getId());
assertEquals("MDU6TGFiZWwxNzczMzkxMDY=", e.getNodeId());
assertTrue(e.isDefault());
assertTrue(Pattern.matches("[0-9a-fA-F]{6}", e.getColor()));
GHLabel t = null;
@@ -890,12 +979,17 @@ public class AppTest extends AbstractGitHubWireMockTest {
assertThat(t, not(sameInstance(t2)));
assertThat(t, equalTo(t2));
assertFalse(t2.isDefault());
assertEquals(t.getId(), t2.getId());
assertEquals(t.getNodeId(), t2.getNodeId());
assertEquals(t.getName(), t2.getName());
assertEquals(t.getColor(), "123456");
assertEquals(t.getColor(), t2.getColor());
assertEquals(t.getDescription(), "");
assertEquals(t.getDescription(), t2.getDescription());
assertEquals(t.getUrl(), t2.getUrl());
assertEquals(t.isDefault(), t2.isDefault());
// update works on multiple changes in one call
t3 = t.update().color("000000").description("It is dark!").done();

View File

@@ -33,6 +33,7 @@ public class BridgeMethodTest extends Assert {
verifyBridgeMethods(GHIssue.class, "getCreatedAt", Date.class, String.class);
verifyBridgeMethods(GHIssue.class, "getId", int.class, long.class, String.class);
verifyBridgeMethods(GHIssue.class, "getUrl", String.class, URL.class);
verifyBridgeMethods(GHIssue.class, "comment", 1, void.class, GHIssueComment.class);
verifyBridgeMethods(GHOrganization.class, "getHtmlUrl", String.class, URL.class);
verifyBridgeMethods(GHOrganization.class, "getId", int.class, long.class, String.class);
@@ -55,12 +56,17 @@ public class BridgeMethodTest extends Assert {
}
void verifyBridgeMethods(@Nonnull Class<?> targetClass, @Nonnull String methodName, Class<?>... returnTypes) {
verifyBridgeMethods(targetClass, methodName, 0, returnTypes);
}
void verifyBridgeMethods(@Nonnull Class<?> targetClass,
@Nonnull String methodName,
int parameterCount,
Class<?>... returnTypes) {
List<Class<?>> foundMethods = new ArrayList<>();
Method[] methods = targetClass.getMethods();
for (Method method : methods) {
if (method.getName().equalsIgnoreCase(methodName)) {
// Bridge methods are only
assertThat(method.getParameterCount(), equalTo(0));
if (method.getName().equalsIgnoreCase(methodName) && method.getParameterCount() == parameterCount) {
foundMethods.add(method.getReturnType());
}
}

View File

@@ -4,7 +4,9 @@ import com.google.common.collect.Iterables;
import org.junit.Test;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import static org.hamcrest.Matchers.equalTo;
@@ -29,6 +31,85 @@ public class CommitTest extends AbstractGitHubWireMockTest {
}
}
@Test
public void testQueryCommits() throws Exception {
List<String> sha1 = new ArrayList<String>();
List<GHCommit> commits = gitHub.getUser("jenkinsci")
.getRepository("jenkins")
.queryCommits()
.since(1199174400000L)
.until(1201852800000L)
.path("pom.xml")
.pageSize(100)
.list()
.toList();
assertThat(commits.get(0).getSHA1(), equalTo("1cccddb22e305397151b2b7b87b4b47d74ca337b"));
assertThat(commits.size(), equalTo(29));
commits = gitHub.getUser("jenkinsci")
.getRepository("jenkins")
.queryCommits()
.since(new Date(1199174400000L))
.until(new Date(1201852800000L))
.path("pom.xml")
.pageSize(100)
.list()
.toList();
assertThat(commits.get(0).getSHA1(), equalTo("1cccddb22e305397151b2b7b87b4b47d74ca337b"));
assertThat(commits.get(15).getSHA1(), equalTo("a5259970acaec9813e2a12a91f37dfc7871a5ef5"));
assertThat(commits.size(), equalTo(29));
commits = gitHub.getUser("jenkinsci")
.getRepository("jenkins")
.queryCommits()
.since(new Date(1199174400000L))
.until(new Date(1201852800000L))
.path("pom.xml")
.from("a5259970acaec9813e2a12a91f37dfc7871a5ef5")
.list()
.toList();
assertThat(commits.get(0).getSHA1(), equalTo("a5259970acaec9813e2a12a91f37dfc7871a5ef5"));
assertThat(commits.size(), equalTo(14));
commits = gitHub.getUser("jenkinsci")
.getRepository("jenkins")
.queryCommits()
.until(new Date(1201852800000L))
.path("pom.xml")
.author("kohsuke")
.list()
.toList();
assertThat(commits.size(), equalTo(0));
commits = gitHub.getUser("jenkinsci")
.getRepository("jenkins")
.queryCommits()
.until(new Date(1201852800000L))
.path("pom.xml")
.pageSize(100)
.author("kohsuke@71c3de6d-444a-0410-be80-ed276b4c234a")
.list()
.toList();
assertThat(commits.size(), equalTo(266));
commits = gitHub.getUser("jenkinsci")
.getRepository("jenkins")
.queryCommits()
.path("pom.xml")
.pageSize(100)
.author("kohsuke@71c3de6d-444a-0410-be80-ed276b4c234a")
.list()
.toList();
assertThat(commits.size(), equalTo(648));
}
@Test
public void listPullRequestsOfNotIncludedCommit() throws Exception {
GHRepository repo = gitHub.getOrganization("hub4j-test-org").getRepository("listPrsListHeads");

View File

@@ -0,0 +1,84 @@
package org.kohsuke.github;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.*;
/**
* Unit test for {@link GitHub} static helpers.
*
* @author Liam Newman
*/
public class EnumTest extends AbstractGitHubWireMockTest {
@Test
public void touchEnums() {
assertThat(GHCheckRun.AnnotationLevel.values().length, equalTo(3));
assertThat(GHCheckRun.Conclusion.values().length, equalTo(7));
assertThat(GHCheckRun.Status.values().length, equalTo(3));
assertThat(GHCommentAuthorAssociation.values().length, equalTo(7));
assertThat(GHCommitState.values().length, equalTo(4));
assertThat(GHCompare.Status.values().length, equalTo(4));
assertThat(GHDeploymentState.values().length, equalTo(7));
assertThat(GHDirection.values().length, equalTo(2));
assertThat(GHEvent.values().length, equalTo(56));
assertThat(GHEvent.ALL.symbol(), equalTo("*"));
assertThat(GHEvent.PULL_REQUEST.symbol(), equalTo(GHEvent.PULL_REQUEST.toString().toLowerCase()));
assertThat(GHIssueSearchBuilder.Sort.values().length, equalTo(3));
assertThat(GHIssueState.values().length, equalTo(3));
assertThat(GHMarketplaceAccountType.values().length, equalTo(2));
assertThat(GHMarketplaceListAccountBuilder.Sort.values().length, equalTo(2));
assertThat(GHMarketplacePriceModel.values().length, equalTo(3));
assertThat(GHMembership.Role.values().length, equalTo(2));
assertThat(GHMilestoneState.values().length, equalTo(2));
assertThat(GHMyself.RepositoryListFilter.values().length, equalTo(5));
assertThat(GHOrganization.Role.values().length, equalTo(2));
assertThat(GHOrganization.Permission.values().length, equalTo(5));
assertThat(GHPermissionType.values().length, equalTo(4));
assertThat(GHProject.ProjectState.values().length, equalTo(2));
assertThat(GHProject.ProjectStateFilter.values().length, equalTo(3));
assertThat(GHPullRequest.MergeMethod.values().length, equalTo(3));
assertThat(GHPullRequestQueryBuilder.Sort.values().length, equalTo(4));
assertThat(GHPullRequestReviewEvent.values().length, equalTo(4));
assertThat(GHPullRequestReviewEvent.PENDING.toState(), equalTo(GHPullRequestReviewState.PENDING));
assertThat(GHPullRequestReviewEvent.PENDING.action(), nullValue());
assertThat(GHPullRequestReviewState.values().length, equalTo(6));
assertThat(GHPullRequestReviewState.PENDING.toEvent(), equalTo(GHPullRequestReviewEvent.PENDING));
assertThat(GHPullRequestReviewState.APPROVED.action(), equalTo(GHPullRequestReviewEvent.APPROVE.action()));
assertThat(GHPullRequestReviewState.DISMISSED.toEvent(), nullValue());
assertThat(GHRepository.CollaboratorAffiliation.values().length, equalTo(3));
assertThat(GHRepository.ForkSort.values().length, equalTo(3));
assertThat(GHRepositorySearchBuilder.Sort.values().length, equalTo(3));
assertThat(GHRepositorySelection.values().length, equalTo(2));
assertThat(GHTeam.Role.values().length, equalTo(2));
assertThat(GHTeam.Privacy.values().length, equalTo(2));
assertThat(GHUserSearchBuilder.Sort.values().length, equalTo(3));
}
}

View File

@@ -103,7 +103,8 @@ public class GHAppTest extends AbstractGitHubWireMockTest {
permissions.put("metadata", GHPermissionType.READ);
// Create token specifying both permissions and repository ids
GHAppInstallationToken installationToken = installation.createToken(permissions)
GHAppInstallationToken installationToken = installation.createToken()
.permissions(permissions)
.repositoryIds(Collections.singletonList((long) 111111111))
.create();

View File

@@ -30,6 +30,7 @@ import java.io.IOException;
import java.util.Date;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
@SuppressWarnings("deprecation") // preview
public class GHCheckRunBuilderTest extends AbstractGHAppInstallationTest {
@@ -48,7 +49,7 @@ public class GHCheckRunBuilderTest extends AbstractGHAppInstallationTest {
.withExternalID("whatever")
.withStartedAt(new Date(999_999_000))
.withCompletedAt(new Date(999_999_999))
.add(new GHCheckRunBuilder.Output("Some Title", "what happened…")
.add(new GHCheckRunBuilder.Output("Some Title", "what happened…").withText("Hello Text!")
.add(new GHCheckRunBuilder.Annotation("stuff.txt",
1,
GHCheckRun.AnnotationLevel.NOTICE,
@@ -61,11 +62,14 @@ public class GHCheckRunBuilderTest extends AbstractGHAppInstallationTest {
assertEquals("completed", checkRun.getStatus());
assertEquals(1, checkRun.getOutput().getAnnotationsCount());
assertEquals(1424883286, checkRun.getId());
assertEquals("Hello Text!", checkRun.getOutput().getText());
}
@Test
public void createCheckRunManyAnnotations() throws Exception {
GHCheckRunBuilder.Output output = new GHCheckRunBuilder.Output("Big Run", "Lots of stuff here »");
GHCheckRunBuilder.Output output = new GHCheckRunBuilder.Output("Big Run", "Lots of stuff here »")
.withText("Hello Text!");
for (int i = 0; i < 101; i++) {
output.add(
new GHCheckRunBuilder.Annotation("stuff.txt", 1, GHCheckRun.AnnotationLevel.NOTICE, "hello #" + i));
@@ -79,6 +83,7 @@ public class GHCheckRunBuilderTest extends AbstractGHAppInstallationTest {
assertEquals("Big Run", checkRun.getOutput().getTitle());
assertEquals("Lots of stuff here »", checkRun.getOutput().getSummary());
assertEquals(101, checkRun.getOutput().getAnnotationsCount());
assertEquals("Hello Text!", checkRun.getOutput().getText());
assertEquals(1424883599, checkRun.getId());
}
@@ -116,6 +121,8 @@ public class GHCheckRunBuilderTest extends AbstractGHAppInstallationTest {
} catch (HttpException x) {
assertEquals(422, x.getResponseCode());
assertThat(x.getMessage(), containsString("\\\"conclusion\\\" wasn't supplied"));
assertThat(x.getUrl(), containsString("/repos/hub4j-test-org/test-checks/check-runs"));
assertThat(x.getResponseMessage(), equalTo("422 Unprocessable Entity"));
}
}

View File

@@ -45,6 +45,14 @@ public class GHContentIntegrationTest extends AbstractGitHubWireMockTest {
repo = gitHub.getRepository("hub4j-test-org/GHContentIntegrationTest");
}
@Test
public void testGetRepository() throws Exception {
GHRepository testRepo = gitHub.getRepositoryById(repo.getId());
assertThat(testRepo.getName(), equalTo(repo.getName()));
testRepo = gitHub.getRepositoryById(Long.toString(repo.getId()));
assertThat(testRepo.getName(), equalTo(repo.getName()));
}
@Test
public void testGetFileContent() throws Exception {
repo = gitHub.getRepository("hub4j-test-org/GHContentIntegrationTest");

View File

@@ -141,6 +141,45 @@ public class GHEventPayloadTest extends AbstractGitHubWireMockTest {
assertThat(event.getSender().getLogin(), is("baxterthehacker"));
}
@Test
public void issue_labeled() throws Exception {
GHEventPayload.Issue event = GitHub.offline().parseEventPayload(payload.asReader(), GHEventPayload.Issue.class);
assertThat(event.getAction(), is("labeled"));
assertThat(event.getIssue().getNumber(), is(42));
assertThat(event.getIssue().getTitle(), is("Test GHEventPayload.Issue label/unlabel"));
assertThat(event.getIssue().getLabels().size(), is(1));
assertThat(event.getIssue().getLabels().iterator().next().getName(), is("enhancement"));
assertThat(event.getLabel().getName(), is("enhancement"));
}
@Test
public void issue_unlabeled() throws Exception {
GHEventPayload.Issue event = GitHub.offline().parseEventPayload(payload.asReader(), GHEventPayload.Issue.class);
assertThat(event.getAction(), is("unlabeled"));
assertThat(event.getIssue().getNumber(), is(42));
assertThat(event.getIssue().getTitle(), is("Test GHEventPayload.Issue label/unlabel"));
assertThat(event.getIssue().getLabels().size(), is(0));
assertThat(event.getLabel().getName(), is("enhancement"));
}
@Test
public void issue_title_edited() throws Exception {
GHEventPayload.Issue event = GitHub.offline().parseEventPayload(payload.asReader(), GHEventPayload.Issue.class);
assertThat(event.getAction(), is("edited"));
assertThat(event.getIssue().getNumber(), is(43));
assertThat(event.getIssue().getTitle(), is("Test GHEventPayload.Issue changes [updated]"));
assertThat(event.getChanges().getTitle().getFrom(), is("Test GHEventPayload.Issue changes"));
}
@Test
public void issue_body_edited() throws Exception {
GHEventPayload.Issue event = GitHub.offline().parseEventPayload(payload.asReader(), GHEventPayload.Issue.class);
assertThat(event.getAction(), is("edited"));
assertThat(event.getIssue().getNumber(), is(43));
assertThat(event.getIssue().getBody(), is("Description [updated]."));
assertThat(event.getChanges().getBody().getFrom(), is("Description."));
}
// TODO implement support classes and write test
// @Test
// public void label() throws Exception {}
@@ -361,6 +400,9 @@ public class GHEventPayloadTest extends AbstractGitHubWireMockTest {
assertThat(event.getCommits().get(0).getRemoved().size(), is(0));
assertThat(event.getCommits().get(0).getModified().size(), is(1));
assertThat(event.getCommits().get(0).getModified().get(0), is("README.md"));
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
assertThat(formatter.format(event.getCommits().get(0).getTimestamp()), is("2015-05-05T23:40:15Z"));
assertThat(event.getRepository().getName(), is("public-repo"));
assertThat(event.getRepository().getOwnerName(), is("baxterthehacker"));
assertThat(event.getRepository().getUrl().toExternalForm(),
@@ -515,6 +557,7 @@ public class GHEventPayloadTest extends AbstractGitHubWireMockTest {
assertThat(event.getRepository().getName(), is("Hello-World"));
assertThat(event.getRepository().getOwner().getLogin(), is("Codertocat"));
assertThat(event.getAction(), is("created"));
assertThat(event.getRequestedAction(), nullValue());
// Checks the deserialization of check_run
GHCheckRun checkRun = event.getCheckRun();

View File

@@ -18,7 +18,7 @@ public class GHIssueEventTest extends AbstractGitHubWireMockTest {
GHIssue issue = builder.create();
// Generate some events.
issue.addLabels("test-label");
issue.setLabels("test-label");
// Test that the events are present.
List<GHIssueEvent> list = issue.listEvents().toList();

View File

@@ -79,9 +79,19 @@ public class GHLicenseTest extends AbstractGitHubWireMockTest {
String key = "mit";
GHLicense license = gitHub.getLicense(key);
assertNotNull(license);
assertTrue("The name is correct", license.getName().equals("MIT License"));
assertTrue("The HTML URL is correct",
license.getHtmlUrl().equals(new URL("http://choosealicense.com/licenses/mit/")));
assertThat("The name is correct", license.getName(), equalTo("MIT License"));
assertThat("The HTML URL is correct",
license.getHtmlUrl(),
equalTo(new URL("http://choosealicense.com/licenses/mit/")));
assertThat(license.getBody(), startsWith("MIT License\n" + "\n" + "Copyright (c) [year] [fullname]\n\n"));
assertThat(license.getForbidden().size(), equalTo(0));
assertThat(license.getPermitted().size(), equalTo(0));
assertThat(license.getImplementation(),
equalTo("Create a text file (typically named LICENSE or LICENSE.txt) in the root of your source code and copy the text of the license into the file. Replace [year] with the current year and [fullname] with the name (or names) of the copyright holders."));
assertThat(license.getCategory(), nullValue());
assertThat(license.isFeatured(), equalTo(true));
assertThat(license.equals(null), equalTo(false));
assertThat(license.equals(gitHub.getLicense(key)), equalTo(true));
}
/**

View File

@@ -36,11 +36,12 @@ public class GHOrganizationTest extends AbstractGitHubWireMockTest {
cleanupRepository(GITHUB_API_TEST_ORG + '/' + GITHUB_API_TEST);
GHOrganization org = gitHub.getOrganization(GITHUB_API_TEST_ORG);
GHRepository repository = org.createRepository(GITHUB_API_TEST,
"a test repository used to test kohsuke's github-api",
"http://github-api.kohsuke.org/",
"Core Developers",
true);
GHRepository repository = org.createRepository(GITHUB_API_TEST)
.description("a test repository used to test kohsuke's github-api")
.homepage("http://github-api.kohsuke.org/")
.team(org.getTeamByName("Core Developers"))
.private_(false)
.create();
Assert.assertNotNull(repository);
}
@@ -72,7 +73,7 @@ public class GHOrganizationTest extends AbstractGitHubWireMockTest {
.homepage("http://github-api.kohsuke.org/")
.team(team)
.autoInit(true)
.templateRepository(true)
.isTemplate(true)
.create();
Assert.assertNotNull(repository);
assertThat(mockGitHub.getRequestCount(), equalTo(requestCount + 1));
@@ -136,7 +137,7 @@ public class GHOrganizationTest extends AbstractGitHubWireMockTest {
public void testListMembersWithFilter() throws IOException {
GHOrganization org = gitHub.getOrganization(GITHUB_API_TEST_ORG);
List<GHUser> admins = org.listMembersWithFilter("all").asList();
List<GHUser> admins = org.listMembersWithFilter("all").toList();
assertNotNull(admins);
assertTrue(admins.size() >= 12); // In case more are added in the future
@@ -158,7 +159,7 @@ public class GHOrganizationTest extends AbstractGitHubWireMockTest {
public void testListMembersWithRole() throws IOException {
GHOrganization org = gitHub.getOrganization(GITHUB_API_TEST_ORG);
List<GHUser> admins = org.listMembersWithRole("admin").asList();
List<GHUser> admins = org.listMembersWithRole("admin").toList();
assertNotNull(admins);
assertTrue(admins.size() >= 12); // In case more are added in the future

View File

@@ -0,0 +1,21 @@
package org.kohsuke.github;
import org.junit.Test;
import java.io.IOException;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class GHPullRequestMockTest {
@Test
public void shouldMockGHPullRequest() throws IOException {
GHPullRequest pullRequest = mock(GHPullRequest.class);
when(pullRequest.isDraft()).thenReturn(true);
assertTrue("Mock should return true", pullRequest.isDraft());
}
}

View File

@@ -5,12 +5,18 @@ import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.net.URL;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasProperty;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
/**
* @author Kohsuke Kawaguchi
@@ -112,28 +118,52 @@ public class GHPullRequestTest extends AbstractGitHubWireMockTest {
public void pullRequestReviewComments() throws Exception {
String name = "pullRequestReviewComments";
GHPullRequest p = getRepository().createPullRequest(name, "test/stable", "master", "## test");
// System.out.println(p.getUrl());
assertTrue(p.listReviewComments().toList().isEmpty());
p.createReviewComment("Sample review comment", p.getHead().getSha(), "README.md", 1);
List<GHPullRequestReviewComment> comments = p.listReviewComments().toList();
assertEquals(1, comments.size());
GHPullRequestReviewComment comment = comments.get(0);
assertEquals("Sample review comment", comment.getBody());
try {
// System.out.println(p.getUrl());
assertTrue(p.listReviewComments().toList().isEmpty());
p.createReviewComment("Sample review comment", p.getHead().getSha(), "README.md", 1);
List<GHPullRequestReviewComment> comments = p.listReviewComments().toList();
assertEquals(1, comments.size());
GHPullRequestReviewComment comment = comments.get(0);
assertEquals("Sample review comment", comment.getBody());
assertThat(comment.getInReplyToId(), equalTo(-1L));
assertThat(comment.getPath(), equalTo("README.md"));
assertThat(comment.getPosition(), equalTo(1));
assertThat(comment.getUser(), notNullValue());
// Assert htmlUrl is not null
assertThat(comment.getHtmlUrl(), notNullValue());
assertThat(comment.getHtmlUrl().toString(),
containsString("hub4j-test-org/github-api/pull/" + p.getNumber()));
// Assert htmlUrl is not null
assertNotNull(comment.getHtmlUrl());
assertEquals(new URL("https://github.com/hub4j-test-org/github-api/pull/266#discussion_r321995146"),
comment.getHtmlUrl());
List<GHReaction> reactions = comment.listReactions().toList();
assertThat(reactions.size(), equalTo(0));
comment.update("Updated review comment");
comments = p.listReviewComments().toList();
assertEquals(1, comments.size());
comment = comments.get(0);
assertEquals("Updated review comment", comment.getBody());
GHReaction reaction = comment.createReaction(ReactionContent.CONFUSED);
assertThat(reaction.getContent(), equalTo(ReactionContent.CONFUSED));
comment.delete();
comments = p.listReviewComments().toList();
assertTrue(comments.isEmpty());
reactions = comment.listReactions().toList();
assertThat(reactions.size(), equalTo(1));
GHPullRequestReviewComment reply = comment.reply("This is a reply.");
assertThat(reply.getInReplyToId(), equalTo(comment.getId()));
comments = p.listReviewComments().toList();
assertEquals(2, comments.size());
comment.update("Updated review comment");
comments = p.listReviewComments().toList();
comment = comments.get(0);
assertEquals("Updated review comment", comment.getBody());
comment.delete();
comments = p.listReviewComments().toList();
// Reply is still present after delete of original comment, but no longer has replyToId
assertThat(comments.size(), equalTo(1));
assertThat(comments.get(0).getId(), equalTo(reply.getId()));
assertThat(comments.get(0).getInReplyToId(), equalTo(-1L));
} finally {
p.close();
}
}
@Test
@@ -395,7 +425,93 @@ public class GHPullRequestTest extends AbstractGitHubWireMockTest {
Collection<GHLabel> labels = getRepository().getPullRequest(p.getNumber()).getLabels();
assertEquals(1, labels.size());
assertEquals(label, labels.iterator().next().getName());
GHLabel savedLabel = labels.iterator().next();
assertEquals(label, savedLabel.getName());
assertNotNull(savedLabel.getId());
assertNotNull(savedLabel.getNodeId());
assertFalse(savedLabel.isDefault());
}
@Test
// Requires push access to the test repo to pass
public void addLabels() throws Exception {
GHPullRequest p = getRepository().createPullRequest("addLabels", "test/stable", "master", "## test");
String addedLabel1 = "addLabels_label_name_1";
String addedLabel2 = "addLabels_label_name_2";
String addedLabel3 = "addLabels_label_name_3";
p.addLabels(addedLabel1);
int requestCount = mockGitHub.getRequestCount();
p.addLabels(addedLabel2, addedLabel3);
// multiple labels can be added with one api call
assertThat(mockGitHub.getRequestCount(), equalTo(requestCount + 1));
Collection<GHLabel> labels = getRepository().getPullRequest(p.getNumber()).getLabels();
assertEquals(3, labels.size());
assertThat(labels,
containsInAnyOrder(hasProperty("name", equalTo(addedLabel1)),
hasProperty("name", equalTo(addedLabel2)),
hasProperty("name", equalTo(addedLabel3))));
// Adding a label which is already present does not throw an error
p.addLabels(addedLabel1);
}
@Test
// Requires push access to the test repo to pass
public void addLabelsConcurrencyIssue() throws Exception {
String addedLabel1 = "addLabelsConcurrencyIssue_label_name_1";
String addedLabel2 = "addLabelsConcurrencyIssue_label_name_2";
GHPullRequest p1 = getRepository()
.createPullRequest("addLabelsConcurrencyIssue", "test/stable", "master", "## test");
p1.getLabels();
GHPullRequest p2 = getRepository().getPullRequest(p1.getNumber());
p2.addLabels(addedLabel2);
p1.addLabels(addedLabel1);
Collection<GHLabel> labels = getRepository().getPullRequest(p1.getNumber()).getLabels();
assertEquals(2, labels.size());
assertThat(labels,
containsInAnyOrder(hasProperty("name", equalTo(addedLabel1)),
hasProperty("name", equalTo(addedLabel2))));
}
@Test
// Requires push access to the test repo to pass
public void removeLabels() throws Exception {
GHPullRequest p = getRepository().createPullRequest("removeLabels", "test/stable", "master", "## test");
String label1 = "removeLabels_label_name_1";
String label2 = "removeLabels_label_name_2";
String label3 = "removeLabels_label_name_3";
p.setLabels(label1, label2, label3);
Collection<GHLabel> labels = getRepository().getPullRequest(p.getNumber()).getLabels();
assertEquals(3, labels.size());
int requestCount = mockGitHub.getRequestCount();
p.removeLabels(label2, label3);
// each label deleted is a separate api call
assertThat(mockGitHub.getRequestCount(), equalTo(requestCount + 2));
labels = getRepository().getPullRequest(p.getNumber()).getLabels();
assertEquals(1, labels.size());
assertEquals(label1, labels.iterator().next().getName());
// Removing some labels that are not present does not throw
// This is consistent with earlier behavior and with addLabels()
p.removeLabels(label3);
// Calling removeLabel() on label that is not present will throw
try {
p.removeLabel(label3);
fail("Expected GHFileNotFoundException");
} catch (GHFileNotFoundException e) {
assertThat(e.getMessage(), containsString("Label does not exist"));
}
}
@Test
@@ -412,15 +528,15 @@ public class GHPullRequestTest extends AbstractGitHubWireMockTest {
public void getUserTest() throws IOException {
GHPullRequest p = getRepository().createPullRequest("getUserTest", "test/stable", "master", "## test");
GHPullRequest prSingle = getRepository().getPullRequest(p.getNumber());
assertNotNull(prSingle.getUser().root);
assertNotNull(prSingle.getUser().getRoot());
prSingle.getMergeable();
assertNotNull(prSingle.getUser().root);
assertNotNull(prSingle.getUser().getRoot());
PagedIterable<GHPullRequest> ghPullRequests = getRepository().listPullRequests(GHIssueState.OPEN);
for (GHPullRequest pr : ghPullRequests) {
assertNotNull(pr.getUser().root);
assertNotNull(pr.getUser().getRoot());
pr.getMergeable();
assertNotNull(pr.getUser().root);
assertNotNull(pr.getUser().getRoot());
}
}

View File

@@ -312,9 +312,6 @@ public class GHRateLimitTest extends AbstractGitHubWireMockTest {
// Give this a moment
Thread.sleep(1500);
// lastRateLimit the same as rateLimit
assertThat(gitHub.lastRateLimit(), sameInstance(rateLimit));
// ratelimit() tries not to make additional requests, uses queried rate limit since header not available
Thread.sleep(1500);
assertThat(gitHub.rateLimit(), sameInstance(rateLimit));
@@ -490,9 +487,6 @@ public class GHRateLimitTest extends AbstractGitHubWireMockTest {
assertThat("rateLimit() selects header instance when not expired, does not ask server",
gitHub.rateLimit(),
sameInstance(headerRateLimit));
assertThat("lastRateLimit() always selects header instance, does not ask server",
gitHub.lastRateLimit(),
sameInstance(headerRateLimit));
assertThat(mockGitHub.getRequestCount(), equalTo(1));

View File

@@ -34,6 +34,7 @@ public class GHRepositoryStatisticsTest extends AbstractGitHubWireMockTest {
for (GHRepositoryStatistics.ContributorStats statsForAuthor : list) {
if (authorLogin.equals(statsForAuthor.getAuthor().getLogin())) {
assertEquals(715, statsForAuthor.getTotal());
assertEquals("kohsuke made 715 contributions over 494 weeks", statsForAuthor.toString());
List<GHRepositoryStatistics.ContributorStats.Week> weeks = statsForAuthor.getWeeks();
assertEquals(494, weeks.size());
@@ -46,6 +47,8 @@ public class GHRepositoryStatisticsTest extends AbstractGitHubWireMockTest {
assertEquals(63, week.getNumberOfAdditions());
assertEquals(56, week.getNumberOfDeletions());
assertEquals(5, week.getNumberOfCommits());
assertEquals("Week starting 1541289600 - Additions: 63, Deletions: 56, Commits: 5",
week.toString());
} catch (NoSuchElementException e) {
fail("Did not find week 1546128000");
}
@@ -131,6 +134,7 @@ public class GHRepositoryStatisticsTest extends AbstractGitHubWireMockTest {
if (item.getWeekTimestamp() == 1535241600) {
assertEquals(185, item.getAdditions());
assertEquals(-243, item.getDeletions());
assertEquals("Week starting 1535241600 has 185 additions and 243 deletions", item.toString());
foundWeek = true;
break;
}
@@ -196,6 +200,7 @@ public class GHRepositoryStatisticsTest extends AbstractGitHubWireMockTest {
// TODO: Make an easier access method. Perhaps wrap in an
// object and have a method such as GetCommits(1, 16).
assertEquals(16, item.getNumberOfCommits());
assertEquals("Day 2 Hour 10: 16 commits", item.toString());
hourFound = true;
break;
}

View File

@@ -4,10 +4,13 @@ import com.fasterxml.jackson.databind.JsonMappingException;
import org.apache.commons.io.IOUtils;
import org.junit.Test;
import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
@@ -28,6 +31,20 @@ public class GHRepositoryTest extends AbstractGitHubWireMockTest {
return gitHub.getOrganization("hub4j-test-org").getRepository("github-api");
}
@Test
public void testZipball() throws IOException {
getTempRepository().readZip((InputStream inputstream) -> {
return new ByteArrayInputStream(IOUtils.toByteArray(inputstream));
}, null);
}
@Test
public void testTarball() throws IOException {
getTempRepository().readTar((InputStream inputstream) -> {
return new ByteArrayInputStream(IOUtils.toByteArray(inputstream));
}, null);
}
@Test
public void testGetters() throws IOException {
GHRepository r = getTempRepository();
@@ -110,6 +127,19 @@ public class GHRepositoryTest extends AbstractGitHubWireMockTest {
assertEquals(UNKNOWN_SIGNATURE_TYPE, verification.getReason());
}
@Test
public void listStargazers() throws IOException {
GHRepository repository = getRepository();
assertThat(repository.listStargazers2().toList(), empty());
repository = gitHub.getOrganization("hub4j").getRepository("github-api");
Iterable<GHStargazer> stargazers = repository.listStargazers2();
GHStargazer stargazer = stargazers.iterator().next();
assertThat(stargazer.getStarredAt(), equalTo(new Date(1271650383000L)));
assertThat(stargazer.getUser().getLogin(), equalTo("nielswind"));
assertThat(stargazer.getRepository(), sameInstance(repository));
}
// Issue #607
@Test
public void getBranchNonExistentBut200Status() throws Exception {
@@ -135,11 +165,20 @@ public class GHRepositoryTest extends AbstractGitHubWireMockTest {
public void subscription() throws Exception {
GHRepository r = getRepository();
assertNull(r.getSubscription());
GHSubscription s = r.subscribe(true, false);
assertEquals(s.getRepository(), r);
try {
s.delete();
assertEquals(s.getRepository(), r);
assertThat(s.isIgnored(), equalTo(false));
assertThat(s.isSubscribed(), equalTo(true));
assertThat(s.getRepositoryUrl().toString(), containsString("/repos/hub4j-test-org/github-api"));
assertThat(s.getUrl().toString(), containsString("/repos/hub4j-test-org/github-api/subscription"));
assertThat(s.getReason(), nullValue());
assertThat(s.getCreatedAt(), equalTo(new Date(1611377286000L)));
} finally {
s.delete();
}
assertNull(r.getSubscription());
}
@@ -641,17 +680,6 @@ public class GHRepositoryTest extends AbstractGitHubWireMockTest {
assertThat(e, instanceOf(GHFileNotFoundException.class));
assertThat(e.getMessage(), containsString("/repos/hub4j-test-org/temp-listRefsEmptyTags/git/refs/tags"));
}
try {
GHRepository repo = getTempRepository();
repo.listRefs("tags").asList();
fail();
} catch (Exception e) {
assertThat(e, instanceOf(GHException.class));
assertThat(e.getMessage(), containsString("Failed to retrieve "));
assertThat(e.getMessage(), containsString("/repos/hub4j-test-org/temp-listRefsEmptyTags/git/refs/tags"));
assertThat(e.getCause(), instanceOf(GHFileNotFoundException.class));
}
}
@Test

View File

@@ -6,6 +6,7 @@ import org.junit.Test;
import java.io.IOException;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -49,6 +50,10 @@ public class GHTagTest extends AbstractGitHubWireMockTest {
assertEquals(commitSha, tag.getObject().getSha());
assertFalse(tag.getVerification().isVerified());
assertEquals(tag.getVerification().getReason(), GHVerification.Reason.UNSIGNED);
assertThat(tag.getUrl(),
containsString("/repos/hub4j-test-org/github-api/git/tags/e7aa6d4afbaa48669f0bbe11ca3c4d787b2b153c"));
assertThat(tag.getOwner().getId(), equalTo(repo.getId()));
assertThat(tag.getTagger().getEmail(), equalTo("martin.vanzijl@gmail.com"));
// Make a reference to the newly created tag.
GHRef ref = repo.createRef("refs/tags/" + tagName, tag.getSha());

View File

@@ -41,7 +41,7 @@ public class GHTeamTest extends AbstractGitHubWireMockTest {
GHTeam team = gitHub.getOrganization(GITHUB_API_TEST_ORG).getTeamBySlug(teamSlug);
List<GHUser> admins = team.listMembers("admin").asList();
List<GHUser> admins = team.listMembers("admin").toList();
assertNotNull(admins);
assertThat("One admin in dummy team", admins.size() == 1);
@@ -55,7 +55,7 @@ public class GHTeamTest extends AbstractGitHubWireMockTest {
GHTeam team = gitHub.getOrganization(GITHUB_API_TEST_ORG).getTeamBySlug(teamSlug);
List<GHUser> justMembers = team.listMembers("member").asList();
List<GHUser> justMembers = team.listMembers("member").toList();
assertThat("No regular members in team", justMembers.isEmpty());
}

View File

@@ -7,7 +7,9 @@ import org.junit.Test;
import java.io.IOException;
import java.util.Arrays;
import java.util.Date;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertEquals;
public class GHTreeBuilderTest extends AbstractGitHubWireMockTest {
@@ -87,22 +89,30 @@ public class GHTreeBuilderTest extends AbstractGitHubWireMockTest {
treeBuilder.add(PATH_DATA1, CONTENT_DATA1, false);
treeBuilder.add(PATH_DATA2, CONTENT_DATA2, false);
updateTree();
GHCommit commit = updateTree();
assertEquals(CONTENT_SCRIPT.length(), getFileSize(PATH_SCRIPT));
assertEquals(CONTENT_README.length(), getFileSize(PATH_README));
assertEquals(CONTENT_DATA1.length, getFileSize(PATH_DATA1));
assertEquals(CONTENT_DATA2.length, getFileSize(PATH_DATA2));
assertThat(commit.getCommitShortInfo().getAuthor().getEmail(), equalTo("author@author.com"));
assertThat(commit.getCommitShortInfo().getCommitter().getEmail(), equalTo("committer@committer.com"));
}
private void updateTree() throws IOException {
private GHCommit updateTree() throws IOException {
String treeSha = treeBuilder.create().getSha();
String commitSha = new GHCommitBuilder(repo).message("Add files")
GHCommit commit = new GHCommitBuilder(repo).message("Add files")
.tree(treeSha)
.author("author", "author@author.com", new Date(1611433225969L))
.committer("committer", "committer@committer.com", new Date(1611433225968L))
.parent(masterRef.getObject().getSha())
.create()
.getSHA1();
.create();
String commitSha = commit.getSHA1();
masterRef.updateTo(commitSha);
return commit;
}
private long getFileSize(String path) throws IOException {

View File

@@ -39,14 +39,27 @@ public class GHUserTest extends AbstractGitHubWireMockTest {
}
});
assertEquals(1066173, ghKeys.get(0).getId());
assertThat(ghKeys.get(0).getTitle(), nullValue());
assertThat(ghKeys.get(0).getUrl(), nullValue());
assertThat(ghKeys.get(0).isVerified(), equalTo(false));
assertThat(ghKeys.get(0).toString(),
containsString(
"title=<null>,id=1066173,key=ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAueiy12T5bvFhsc9YjfLc3aVIxgySd3gDxQWy/bletIoZL8omKmzocBYJ7F58U1asoyfWsy2ToTOY8jJp1eToXmbD6L5+xvHba0A7djYh9aQRrFam7doKQ0zp0ZSUF6+R1v0OM4nnWqK4n2ECIYd+Bdzrp+xA5+XlW3ZSNzlnW2BeWznzmgRMcp6wI+zQ9GMHWviR1cxpml5Z6wrxTZ0aX91btvnNPqoOGva976B6e6403FOEkkIFTk6CC1TFKwc/VjbqxYBg4kU0JhiTP+iEZibcQrYjWdYUgAotYbFVe5/DneHMLNsMPdeihba4PUwt62rXyNegenuCRmCntLcaFQ=="));
assertEquals(
"ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAueiy12T5bvFhsc9YjfLc3aVIxgySd3gDxQWy/bletIoZL8omKmzocBYJ7F58U1asoyfWsy2ToTOY8jJp1eToXmbD6L5+xvHba0A7djYh9aQRrFam7doKQ0zp0ZSUF6+R1v0OM4nnWqK4n2ECIYd+Bdzrp+xA5+XlW3ZSNzlnW2BeWznzmgRMcp6wI+zQ9GMHWviR1cxpml5Z6wrxTZ0aX91btvnNPqoOGva976B6e6403FOEkkIFTk6CC1TFKwc/VjbqxYBg4kU0JhiTP+iEZibcQrYjWdYUgAotYbFVe5/DneHMLNsMPdeihba4PUwt62rXyNegenuCRmCntLcaFQ==",
ghKeys.get(0).getKey());
assertEquals(28136459, ghKeys.get(1).getId());
assertThat(ghKeys.get(1).getTitle(), nullValue());
assertThat(ghKeys.get(1).getUrl(), nullValue());
assertThat(ghKeys.get(1).isVerified(), equalTo(false));
assertEquals(
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDTU0s5OKCC6VpKZGL9NJD4mNLY0AtujkVB1JkkuQ4OkMi2YGUHJtGhTbTwEVhNxpm0x2dM5KSzse6MLDYuGBW0qkE/VVuD9+9I73hbq461KqP0+WlupNh+Qc86kbiLBDv64+vWc+50mp1dbINpoM5xvaPYxgjnemydPv7vu5bhCHBugW7aN8VcLgfFgcp8vZCEanMtd3hIRjRU8v8Skk233ZGu1bXkG8iIOBQPabvEtZ0VDMg9pT3Q1R6lnnKqfCwHXd6zP6uAtejFSxvKRGKpu3OLGQMHwk7NlImVuhkVdaEFBq7pQtpOaGuP2eLKcN1wy5jsTYE+ZB6pvHCi2ecb",
ghKeys.get(1).getKey());
assertEquals(31452581, ghKeys.get(2).getId());
assertThat(ghKeys.get(2).getTitle(), nullValue());
assertThat(ghKeys.get(2).getUrl(), nullValue());
assertThat(ghKeys.get(2).isVerified(), equalTo(false));
assertEquals(
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC3JhH2FZBDmHLjXTcBoV6tdcYKmsQ7sgu8k1RsUhwxGsXm65+Cuas6GcMVoA1DncKfJGQkulHDFiTxIROIBmedh9/otHWBlZ4HqYZ4MQ1A8W5quULkXwX/kF+UdRBUxFvjigibEbuHB+LARVxRRzFlPnTSE9rAfAv8OOEsb3lNUGT/IGhN8w1vwe8GclB90tgqN1RBDgrVqwLFwn5AfrW9kUIa2f2oT4RjYu1OrhKhVIIzfHADo85aD+s8wEhqwI96BCJG3qTWrypoHwBUoj1O6Ak5CGc1iKz9o8XyTMjudRt2ddCjfOtxsuwSlTbVtQXJGIpgKviX1sgh4pPvGh7BVAFP+mdAK4F+mEugDnuj47GO/K5KGGDRCL56kh9+h28l4q/+fZvp7DhtmSN2EzrVAdQFskF8yY/6Xit/aAvjeKm03DcjbylSXbG26EJefaLHlwYFq2mUFRMak25wuuCZS71GF3RC3Sl/bMoxBKRYkyfYtGafeaYTFNGn8Dbd+hfVUCz31ebI8cvmlQR5b5AbCre3T7HTVgw8FKbAxWRf1Fio56PnqHsj+sT1KVj255Zo1F8iD9GrgERSVAlkh5bY/CKszQ8ZSd01c9Qp2a47/gR7XAAbxhzGHP+cSOlrqDlJ24fbPtcpVsM0llqKUcxpmoOBFNboRmE1QqnSmAf9ww==",
ghKeys.get(2).getKey());
@@ -109,5 +122,11 @@ public class GHUserTest extends AbstractGitHubWireMockTest {
assertThat(u.getBio(), equalTo("I like to program things and I hope to program something cool one day :D"));
assertTrue(u.isHireable());
assertNotNull(u.getTwitterUsername());
assertThat(u.getBlog(), equalTo("https://chew.pw"));
assertThat(u.getCompany(), equalTo("@Memerator"));
assertThat(u.getFollowersCount(), equalTo(29));
assertThat(u.getFollowingCount(), equalTo(3));
assertThat(u.getPublicGistCount(), equalTo(4));
assertThat(u.getPublicRepoCount(), equalTo(96));
}
}

View File

@@ -1,10 +1,20 @@
package org.kohsuke.github;
import org.junit.Assume;
import org.junit.Test;
import org.kohsuke.github.authorization.UserAuthorizationProvider;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.*;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.lessThan;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
/**
* Unit test for {@link GitHub}.
@@ -53,55 +63,97 @@ public class GitHubConnectionTest extends AbstractGitHubWireMockTest {
@Test
public void testGitHubBuilderFromEnvironment() throws IOException {
// we disable this test for JDK 16+ as the current hacks in setupEnvironment() don't work with JDK 16+
Assume.assumeThat(Double.valueOf(System.getProperty("java.specification.version")), lessThan(16.0));
Map<String, String> props = new HashMap<String, String>();
props.put("login", "bogus");
props.put("oauth", "bogus");
props.put("password", "bogus");
props.put("jwt", "bogus");
props.put("endpoint", "bogus endpoint url");
props.put("oauth", "bogus oauth token string");
setupEnvironment(props);
GitHubBuilder builder = GitHubBuilder.fromEnvironment();
assertEquals("bogus", builder.user);
assertEquals("bogus", builder.oauthToken);
assertEquals("bogus", builder.password);
assertEquals("bogus", builder.jwtToken);
assertThat(builder.endpoint, equalTo("bogus endpoint url"));
assertThat(builder.authorizationProvider, instanceOf(UserAuthorizationProvider.class));
assertThat(builder.authorizationProvider.getEncodedAuthorization(), equalTo("token bogus oauth token string"));
assertThat(((UserAuthorizationProvider) builder.authorizationProvider).getLogin(), nullValue());
props.put("login", "bogus login");
setupEnvironment(props);
builder = GitHubBuilder.fromEnvironment();
assertThat(builder.authorizationProvider, instanceOf(UserAuthorizationProvider.class));
assertThat(builder.authorizationProvider.getEncodedAuthorization(), equalTo("token bogus oauth token string"));
assertThat(((UserAuthorizationProvider) builder.authorizationProvider).getLogin(), equalTo("bogus login"));
props.put("jwt", "bogus jwt token string");
setupEnvironment(props);
builder = GitHubBuilder.fromEnvironment();
assertThat(builder.authorizationProvider, not(instanceOf(UserAuthorizationProvider.class)));
assertThat(builder.authorizationProvider.getEncodedAuthorization(), equalTo("Bearer bogus jwt token string"));
props.put("password", "bogus weak password");
setupEnvironment(props);
builder = GitHubBuilder.fromEnvironment();
assertThat(builder.authorizationProvider, instanceOf(UserAuthorizationProvider.class));
assertThat(builder.authorizationProvider.getEncodedAuthorization(),
equalTo("Basic Ym9ndXMgbG9naW46Ym9ndXMgd2VhayBwYXNzd29yZA=="));
assertThat(((UserAuthorizationProvider) builder.authorizationProvider).getLogin(), equalTo("bogus login"));
}
@Test
public void testGitHubBuilderFromCustomEnvironment() throws IOException {
// we disable this test for JDK 16+ as the current hacks in setupEnvironment() don't work with JDK 16+
Assume.assumeThat(Double.valueOf(System.getProperty("java.specification.version")), lessThan(16.0));
Map<String, String> props = new HashMap<String, String>();
props.put("customLogin", "bogusLogin");
props.put("customOauth", "bogusOauth");
props.put("customPassword", "bogusPassword");
props.put("customEndpoint", "bogusEndpoint");
props.put("customEndpoint", "bogus endpoint url");
props.put("customOauth", "bogus oauth token string");
setupEnvironment(props);
GitHubBuilder builder = GitHubBuilder
.fromEnvironment("customLogin", "customPassword", "customOauth", "customEndpoint");
assertEquals("bogusLogin", builder.user);
assertEquals("bogusOauth", builder.oauthToken);
assertEquals("bogusPassword", builder.password);
assertEquals("bogusEndpoint", builder.endpoint);
assertThat(builder.endpoint, equalTo("bogus endpoint url"));
assertThat(builder.authorizationProvider, instanceOf(UserAuthorizationProvider.class));
assertThat(builder.authorizationProvider.getEncodedAuthorization(), equalTo("token bogus oauth token string"));
assertThat(((UserAuthorizationProvider) builder.authorizationProvider).getLogin(), nullValue());
props.put("customLogin", "bogus login");
setupEnvironment(props);
builder = GitHubBuilder.fromEnvironment("customLogin", "customPassword", "customOauth", "customEndpoint");
assertThat(builder.authorizationProvider, instanceOf(UserAuthorizationProvider.class));
assertThat(builder.authorizationProvider.getEncodedAuthorization(), equalTo("token bogus oauth token string"));
assertThat(((UserAuthorizationProvider) builder.authorizationProvider).getLogin(), equalTo("bogus login"));
props.put("customPassword", "bogus weak password");
setupEnvironment(props);
builder = GitHubBuilder.fromEnvironment("customLogin", "customPassword", "customOauth", "customEndpoint");
assertThat(builder.authorizationProvider, instanceOf(UserAuthorizationProvider.class));
assertThat(builder.authorizationProvider.getEncodedAuthorization(),
equalTo("Basic Ym9ndXMgbG9naW46Ym9ndXMgd2VhayBwYXNzd29yZA=="));
assertThat(((UserAuthorizationProvider) builder.authorizationProvider).getLogin(), equalTo("bogus login"));
}
@Test
public void testGithubBuilderWithAppInstallationToken() throws Exception {
GitHubBuilder builder = new GitHubBuilder().withAppInstallationToken("bogus");
assertEquals("bogus", builder.oauthToken);
assertEquals("", builder.user);
GitHubBuilder builder = new GitHubBuilder().withAppInstallationToken("bogus app token");
assertThat(builder.authorizationProvider, instanceOf(UserAuthorizationProvider.class));
assertThat(builder.authorizationProvider.getEncodedAuthorization(), equalTo("token bogus app token"));
assertThat(((UserAuthorizationProvider) builder.authorizationProvider).getLogin(), equalTo(""));
// test authorization header is set as in the RFC6749
GitHub github = builder.build();
// change this to get a request
assertEquals("token bogus", github.getClient().encodedAuthorization);
assertEquals("token bogus app token", github.getClient().getEncodedAuthorization());
assertEquals("", github.getClient().login);
}

View File

@@ -2,7 +2,9 @@ package org.kohsuke.github;
import org.junit.Test;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.Instant;
import java.time.format.DateTimeParseException;
import java.time.temporal.ChronoUnit;
@@ -10,6 +12,9 @@ import java.util.Date;
import java.util.TimeZone;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.core.IsInstanceOf.instanceOf;
import static org.junit.Assert.fail;
/**
@@ -19,6 +24,34 @@ import static org.junit.Assert.fail;
*/
public class GitHubStaticTest extends AbstractGitHubWireMockTest {
@Test
public void testParseURL() throws Exception {
assertThat(GitHubClient.parseURL("https://api.github.com"), equalTo(new URL("https://api.github.com")));
assertThat(GitHubClient.parseURL(null), nullValue());
try {
GitHubClient.parseURL("bogus");
fail();
} catch (IllegalStateException e) {
assertThat(e.getMessage(), equalTo("Invalid URL: bogus"));
}
}
@Test
public void testParseInstant() throws Exception {
assertThat(GitHubClient.parseInstant(null), nullValue());
}
@Test
public void testRawUrlPathInvalid() throws Exception {
try {
gitHub.createRequest().setRawUrlPath("invalid.path.com");
fail();
} catch (GHException e) {
assertThat(e.getMessage(), equalTo("Raw URL must start with 'http'"));
}
}
@Test
public void timeRoundTrip() throws Exception {
final long stableInstantEpochMilli = 1533721222255L;
@@ -78,6 +111,62 @@ public class GitHubStaticTest extends AbstractGitHubWireMockTest {
}
}
@Test
public void testFromRecord() throws Exception {
final long stableInstantEpochSeconds = 11610674762L;
GHRateLimit rateLimit_none = GHRateLimit.fromRecord(new GHRateLimit.Record(9876,
5432,
(stableInstantEpochSeconds + Duration.ofMinutes(30).toMillis()) / 1000L), RateLimitTarget.NONE);
GHRateLimit rateLimit_core = GHRateLimit.fromRecord(new GHRateLimit.Record(9876,
5432,
(stableInstantEpochSeconds + Duration.ofMinutes(30).toMillis()) / 1000L), RateLimitTarget.CORE);
GHRateLimit rateLimit_search = GHRateLimit.fromRecord(new GHRateLimit.Record(19876,
15432,
(stableInstantEpochSeconds + Duration.ofHours(1).toMillis()) / 1000L), RateLimitTarget.SEARCH);
GHRateLimit rateLimit_graphql = GHRateLimit.fromRecord(new GHRateLimit.Record(29876,
25432,
(stableInstantEpochSeconds + Duration.ofHours(2).toMillis()) / 1000L), RateLimitTarget.GRAPHQL);
GHRateLimit rateLimit_integration = GHRateLimit.fromRecord(
new GHRateLimit.Record(39876,
35432,
(stableInstantEpochSeconds + Duration.ofHours(3).toMillis()) / 1000L),
RateLimitTarget.INTEGRATION_MANIFEST);
assertThat(rateLimit_none, equalTo(rateLimit_core));
assertThat(rateLimit_none, not(sameInstance(rateLimit_core)));
assertTrue(rateLimit_none.hashCode() == rateLimit_core.hashCode());
assertTrue(rateLimit_none.equals(rateLimit_core));
assertThat(rateLimit_none, not(equalTo(rateLimit_search)));
assertThat(rateLimit_none.getCore(), not(sameInstance(rateLimit_core.getCore())));
assertThat(rateLimit_core.getRecord(RateLimitTarget.NONE), instanceOf(GHRateLimit.UnknownLimitRecord.class));
assertThat(rateLimit_core.getRecord(RateLimitTarget.NONE),
sameInstance(rateLimit_none.getRecord(RateLimitTarget.NONE)));
assertThat(rateLimit_core.getRecord(RateLimitTarget.SEARCH), sameInstance(rateLimit_search.getGraphQL()));
assertThat(rateLimit_search.getRecord(RateLimitTarget.GRAPHQL),
sameInstance(rateLimit_graphql.getIntegrationManifest()));
assertThat(rateLimit_graphql.getRecord(RateLimitTarget.INTEGRATION_MANIFEST),
sameInstance(rateLimit_integration.getCore()));
assertThat(rateLimit_integration.getRecord(RateLimitTarget.CORE), sameInstance(rateLimit_core.getSearch()));
assertThat(rateLimit_none.getRecord(RateLimitTarget.CORE).getLimit(), equalTo(9876));
assertThat(rateLimit_core.getRecord(RateLimitTarget.CORE).getLimit(), equalTo(9876));
assertThat(rateLimit_search.getRecord(RateLimitTarget.SEARCH).getLimit(), equalTo(19876));
assertThat(rateLimit_graphql.getRecord(RateLimitTarget.GRAPHQL).getLimit(), equalTo(29876));
assertThat(rateLimit_integration.getRecord(RateLimitTarget.INTEGRATION_MANIFEST).getLimit(), equalTo(39876));
assertThat(rateLimit_core.toString(), containsString("GHRateLimit {core {remaining=5432, limit=9876"));
assertThat(rateLimit_core.toString(), containsString("search {remaining=999999, limit=1000000"));
}
@Test
public void testGitHubRateLimitShouldReplaceRateLimit() throws Exception {
@@ -225,7 +314,7 @@ public class GitHubStaticTest extends AbstractGitHubWireMockTest {
// this makes sure they don't break.
GHRepository repo = getTempRepository();
assertThat(repo.root, not(nullValue()));
assertThat(repo.getRoot(), not(nullValue()));
assertThat(repo.getResponseHeaderFields(), not(nullValue()));
String repoString = GitHub.getMappingObjectWriter().writeValueAsString(repo);
@@ -237,13 +326,13 @@ public class GitHubStaticTest extends AbstractGitHubWireMockTest {
.readValue(repoString);
// This should never happen if the internal method isn't used
assertThat(readRepo.root, nullValue());
assertThat(readRepo.getRoot(), nullValue());
assertThat(readRepo.getResponseHeaderFields(), nullValue());
readRepo = GitHub.getMappingObjectReader().forType(GHRepository.class).readValue(repoString);
// This should never happen if the internal method isn't used
assertThat(readRepo.root.getConnector(), equalTo(HttpConnector.OFFLINE));
assertThat(readRepo.getRoot().getConnector(), equalTo(HttpConnector.OFFLINE));
assertThat(readRepo.getResponseHeaderFields(), nullValue());
String readRepoString = GitHub.getMappingObjectWriter().writeValueAsString(readRepo);

View File

@@ -25,6 +25,22 @@ public class GitHubTest extends AbstractGitHubWireMockTest {
}
}
@Test
public void getRepository() throws IOException {
GHRepository repo = gitHub.getRepository("hub4j/github-api");
assertThat(repo.getFullName(), equalTo("hub4j/github-api"));
GHRepository repo2 = gitHub.getRepositoryById(Long.toString(repo.getId()));
assertThat(repo2.getFullName(), equalTo("hub4j/github-api"));
try {
gitHub.getRepository("hub4j_github-api");
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), equalTo("Repository name must be in format owner/repo"));
}
}
@Test
public void getOrgs() throws IOException {
int iterations = 10;
@@ -34,6 +50,18 @@ public class GitHubTest extends AbstractGitHubWireMockTest {
// System.out.println(org.getName());
}
assertThat(orgIds.size(), equalTo(iterations));
GHOrganization org = gitHub.getOrganization("hub4j");
GHOrganization org2 = gitHub.getOrganization("hub4j");
assertThat(org.getLogin(), equalTo("hub4j"));
// caching
assertThat(org, sameInstance(org2));
gitHub.refreshCache();
org2 = gitHub.getOrganization("hub4j");
assertThat(org2.getLogin(), equalTo("hub4j"));
// cache cleared
assertThat(org, not(sameInstance(org2)));
}
@Test

View File

@@ -6,6 +6,7 @@ import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -24,19 +25,27 @@ public class Github2faTest extends AbstractGitHubWireMockTest {
// collide with older tokens
GHAuthorization token = gitHub
.createToken(asList, nameOfToken, "this is a test token created by a unit test", () -> {
.createToken(asList, nameOfToken, "https://localhost/this/is/a/test/token", () -> {
String data = "111878";
// TO UPDATE run this in debugger mode, put a breakpoint here, and enter the OTP you get into the
// value of Data
return data;
});
assert token != null;
assertThat(token, notNullValue());
for (int i = 0; i < asList.size(); i++) {
assertTrue(token.getScopes().get(i).contentEquals(asList.get(i)));
}
String p = token.getToken();
assertThat(token.getToken(), equalTo("63042a99d88bf138e6d6cf5788e0dc4e7a5d7309"));
assertThat(token.getTokenLastEight(), equalTo("7a5d7309"));
assertThat(token.getHashedToken(), equalTo("12b727a23cad7c5a5caabb806d88e722794dede98464aed7f77cbc00dbf031a2"));
assertThat(token.getNote(), equalTo("Test2faTokenCreate"));
assertThat(token.getNoteUrl().toString(), equalTo("https://localhost/this/is/a/test/token"));
assertThat(token.getAppUrl().toString(), equalTo("https://localhost/this/is/a/test/app/token"));
assertThat(token.getFingerprint(), nullValue());
assertThat(token.getHtmlUrl(), nullValue());
assert p != null;
}
}

View File

@@ -10,7 +10,7 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.core.Is.is;
public class LifecycleTest extends AbstractGitHubWireMockTest {
@@ -61,6 +61,13 @@ public class LifecycleTest extends AbstractGitHubWireMockTest {
List<GHAsset> assets = release.getAssets();
assertEquals(1, assets.size());
assertEquals("LICENSE.txt", assets.get(0).getName());
assertThat(assets.get(0).getSize(), equalTo(1104L));
assertThat(assets.get(0).getContentType(), equalTo("application/text"));
assertThat(assets.get(0).getState(), equalTo("uploaded"));
assertThat(assets.get(0).getDownloadCount(), equalTo(0L));
assertThat(assets.get(0).getOwner(), sameInstance(release.getOwner()));
assertThat(assets.get(0).getBrowserDownloadUrl(),
containsString("/temp-testCreateRepository/releases/download/release_tag/LICENSE.txt"));
return asset;
}
@@ -74,6 +81,16 @@ public class LifecycleTest extends AbstractGitHubWireMockTest {
assertEquals(1, releases.size());
GHRelease release = releases.get(0);
assertEquals("Test Release", release.getName());
assertThat(release.getBody(), startsWith("How exciting!"));
assertThat(release.getOwner(), sameInstance(repository));
assertThat(release.getZipballUrl(),
endsWith("/repos/hub4j-test-org/temp-testCreateRepository/zipball/release_tag"));
assertThat(release.getTarballUrl(),
endsWith("/repos/hub4j-test-org/temp-testCreateRepository/tarball/release_tag"));
assertThat(release.getTargetCommitish(), equalTo("master"));
assertThat(release.getHtmlUrl().toString(),
endsWith("/hub4j-test-org/temp-testCreateRepository/releases/tag/release_tag"));
return release;
}

View File

@@ -0,0 +1,43 @@
package org.kohsuke.github;
import org.junit.Test;
import org.kohsuke.github.authorization.ImmutableAuthorizationProvider;
import org.kohsuke.github.authorization.OrgAppInstallationAuthorizationProvider;
import java.io.IOException;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.notNullValue;
public class OrgAppInstallationAuthorizationProviderTest extends AbstractGHAppInstallationTest {
public OrgAppInstallationAuthorizationProviderTest() {
useDefaultGitHub = false;
}
@Test(expected = HttpException.class)
public void invalidJWTTokenRaisesException() throws IOException {
OrgAppInstallationAuthorizationProvider provider = new OrgAppInstallationAuthorizationProvider(
"testOrganization",
ImmutableAuthorizationProvider.fromJwtToken("myToken"));
gitHub = getGitHubBuilder().withAuthorizationProvider(provider)
.withEndpoint(mockGitHub.apiServer().baseUrl())
.build();
provider.getEncodedAuthorization();
}
@Test
public void validJWTTokenAllowsOauthTokenRequest() throws IOException {
OrgAppInstallationAuthorizationProvider provider = new OrgAppInstallationAuthorizationProvider("hub4j-test-org",
ImmutableAuthorizationProvider.fromJwtToken("bogus-valid-token"));
gitHub = getGitHubBuilder().withAuthorizationProvider(provider)
.withEndpoint(mockGitHub.apiServer().baseUrl())
.build();
String encodedAuthorization = provider.getEncodedAuthorization();
assertThat(encodedAuthorization, notNullValue());
assertThat(encodedAuthorization, equalTo("token v1.9a12d913f980a45a16ac9c3a9d34d9b7sa314cb6"));
}
}

View File

@@ -0,0 +1,87 @@
package org.kohsuke.github.extras.authorization;
import org.junit.Test;
import org.kohsuke.github.AbstractGitHubWireMockTest;
import org.kohsuke.github.GitHub;
import org.kohsuke.github.HttpException;
import java.io.File;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.time.Duration;
import java.time.Instant;
import static org.hamcrest.Matchers.*;
/*
* This test will request an application ensuring that the header for the "Authorization" matches a valid JWT token.
* A JWT token in the Authorization header will always start with "ey" which is always the start of the base64
* encoding of the JWT Header , so a valid header will look like this:
*
* <pre>
* Authorization: Bearer ey{rest of the header}.{payload}.{signature}
* </pre>
*
* Matched by the regular expression:
*
* <pre>
* ^Bearer (?<JWTHeader>ey\S*)\.(?<JWTPayload>\S*)\.(?<JWTSignature>\S*)$
* </pre>
*
* Which is present in the wiremock matcher. Note that we need to use a matcher because the JWT token is encoded
* with a private key and a random nonce, so it will never be the same (under normal conditions). For more
* information on the format of a JWT token, see: https://jwt.io/introduction/
*/
public class JWTTokenProviderTest extends AbstractGitHubWireMockTest {
private static String TEST_APP_ID_2 = "83009";
private static String PRIVATE_KEY_FILE_APP_2 = "/ghapi-test-app-2.private-key.pem";
@Test
public void testAuthorizationHeaderPattern() throws GeneralSecurityException, IOException {
// authorization header check is custom
snapshotNotAllowed();
JWTTokenProvider jwtTokenProvider = new JWTTokenProvider(TEST_APP_ID_2,
new File(this.getClass().getResource(PRIVATE_KEY_FILE_APP_2).getFile()));
GitHub gh = getGitHubBuilder().withEndpoint(mockGitHub.apiServer().baseUrl())
.withAuthorizationProvider(jwtTokenProvider)
.build();
// Request the application, the wiremock matcher will ensure that the header
// for the authorization is present and has a the format of a valid JWT token
gh.getApp();
}
@Test
public void testIssuedAtSkew() throws GeneralSecurityException, IOException {
// TODO: This isn't a great test as it doesn't really check anything in CI
// This test was accurate when recorded but it doesn't verify that the jwt token is different
// or accurate in anyway.
JWTTokenProvider jwtTokenProvider = new JWTTokenProvider(TEST_APP_ID_2,
new File(this.getClass().getResource(PRIVATE_KEY_FILE_APP_2).getFile())) {
@Override
Instant getIssuedAt(Instant now) {
return now.plus(Duration.ofMinutes(2));
}
};
GitHub gh = getGitHubBuilder().withEndpoint(mockGitHub.apiServer().baseUrl())
.withAuthorizationProvider(jwtTokenProvider)
.build();
try {
// Request the application, the wiremock matcher will ensure that the header
// for the authorization is present and has a the format of a valid JWT token
gh.getApp();
fail();
} catch (HttpException e) {
assertThat(e.getResponseCode(), equalTo(401));
assertThat(e.getMessage(),
containsString(
"'Issued at' claim ('iat') must be an Integer representing the time that the assertion was issued"));
}
}
}

View File

@@ -8,6 +8,7 @@ import com.github.tomakehurst.wiremock.extension.ResponseTransformer;
import com.github.tomakehurst.wiremock.http.*;
import com.github.tomakehurst.wiremock.matching.RequestPatternBuilder;
import com.google.gson.*;
import edu.umd.cs.findbugs.annotations.NonNull;
import java.io.File;
import java.io.IOException;
@@ -64,6 +65,10 @@ public class GitHubWireMockRule extends WireMockMultiServerRule {
return servers.get("uploads");
}
public WireMockServer codeloadServer() {
return servers.get("codeload");
}
public boolean isUseProxy() {
return GitHubWireMockRule.useProxy;
}
@@ -88,6 +93,10 @@ public class GitHubWireMockRule extends WireMockMultiServerRule {
if (new File(apiServer().getOptions().filesRoot().getPath() + "_uploads").exists() || isUseProxy()) {
initializeServer("uploads");
}
if (new File(apiServer().getOptions().filesRoot().getPath() + "_codeload").exists() || isUseProxy()) {
initializeServer("codeload");
}
}
@Override
@@ -106,6 +115,11 @@ public class GitHubWireMockRule extends WireMockMultiServerRule {
if (this.uploadsServer() != null) {
this.uploadsServer().stubFor(proxyAllTo("https://uploads.github.com").atPriority(100));
}
if (this.codeloadServer() != null) {
this.codeloadServer().stubFor(proxyAllTo("https://codeload.github.com").atPriority(100));
}
}
@Override
@@ -121,6 +135,8 @@ public class GitHubWireMockRule extends WireMockMultiServerRule {
recordSnapshot(this.rawServer(), "https://raw.githubusercontent.com", true);
recordSnapshot(this.uploadsServer(), "https://uploads.github.com", false);
recordSnapshot(this.codeloadServer(), "https://codeload.github.com", true);
}
private void recordSnapshot(WireMockServer server, String target, boolean isRawServer) {
@@ -141,7 +157,7 @@ public class GitHubWireMockRule extends WireMockMultiServerRule {
.extractTextBodiesOver(255));
// After taking the snapshot, format the output
formatTestResources(new File(this.apiServer().getOptions().filesRoot().getPath()).toPath(), false);
formatTestResources(new File(server.getOptions().filesRoot().getPath()).toPath(), isRawServer);
}
}
@@ -213,6 +229,10 @@ public class GitHubWireMockRule extends WireMockMultiServerRule {
fileText = fileText.replace(this.uploadsServer().baseUrl(), "https://uploads.github.com");
}
if (this.codeloadServer() != null) {
fileText = fileText.replace(this.codeloadServer().baseUrl(), "https://codeload.github.com");
}
// point bodyFile in the mapping to the renamed body file
if (entry != null && filePath.toString().contains("mappings")) {
fileText = fileText.replace("-" + entry.getKey(), "-" + entry.getValue());
@@ -263,16 +283,23 @@ public class GitHubWireMockRule extends WireMockMultiServerRule {
public String mapToMockGitHub(String body) {
body = body.replace("https://api.github.com", this.apiServer().baseUrl());
if (this.rawServer() != null) {
body = body.replace("https://raw.githubusercontent.com", this.rawServer().baseUrl());
} else {
body = body.replace("https://raw.githubusercontent.com", this.apiServer().baseUrl() + "/raw");
}
body = replaceTargetServerUrl(body, this.rawServer(), "https://raw.githubusercontent.com", "/raw");
if (this.uploadsServer() != null) {
body = body.replace("https://uploads.github.com", this.uploadsServer().baseUrl());
body = replaceTargetServerUrl(body, this.uploadsServer(), "https://uploads.github.com", "/uploads");
body = replaceTargetServerUrl(body, this.codeloadServer(), "https://codeload.github.com", "/codeload");
return body;
}
@NonNull
private String replaceTargetServerUrl(String body,
WireMockServer wireMockServer,
String rawTarget,
String inactiveTarget) {
if (wireMockServer != null) {
body = body.replace(rawTarget, wireMockServer.baseUrl());
} else {
body = body.replace("https://uploads.github.com", this.apiServer().baseUrl() + "/uploads");
body = body.replace(rawTarget, this.apiServer().baseUrl() + inactiveTarget);
}
return body;
}
@@ -294,6 +321,7 @@ public class GitHubWireMockRule extends WireMockMultiServerRule {
Collection<HttpHeader> headers = response.getHeaders().all();
fixListTraversalHeader(response, headers);
fixLocationHeader(response, headers);
if ("application/json".equals(response.getHeaders().getContentTypeHeader().mimeTypePart())) {
@@ -321,11 +349,20 @@ public class GitHubWireMockRule extends WireMockMultiServerRule {
}
private void fixListTraversalHeader(Response response, Collection<HttpHeader> headers) {
// Lists are broken up into pages. The Link header contains urls for previous and next pages.
HttpHeader linkHeader = response.getHeaders().getHeader("Link");
if (linkHeader.isPresent()) {
headers.removeIf(item -> item.keyEquals("Link"));
headers.add(HttpHeader.httpHeader("Link",
linkHeader.firstValue().replace("https://api.github.com", rule.apiServer().baseUrl())));
headers.add(HttpHeader.httpHeader("Link", rule.mapToMockGitHub(linkHeader.firstValue())));
}
}
private void fixLocationHeader(Response response, Collection<HttpHeader> headers) {
// For redirects, the Location header points to the new target.
HttpHeader linkHeader = response.getHeaders().getHeader("Location");
if (linkHeader.isPresent()) {
headers.removeIf(item -> item.keyEquals("Location"));
headers.add(HttpHeader.httpHeader("Location", rule.mapToMockGitHub(linkHeader.firstValue())));
}
}

View File

@@ -1,6 +1,6 @@
{
"id": 212656166,
"node_id": "MDEwOlJlcG9zaXRvcnkyMTI2NTYxNjY=",
"id": 332135400,
"node_id": "MDEwOlJlcG9zaXRvcnkzMzIxMzU0MDA=",
"name": "github-api-test",
"full_name": "hub4j-test-org/github-api-test",
"private": false,
@@ -8,7 +8,7 @@
"login": "hub4j-test-org",
"id": 7544739,
"node_id": "MDEyOk9yZ2FuaXphdGlvbjc1NDQ3Mzk=",
"avatar_url": "https://avatars3.githubusercontent.com/u/7544739?v=4",
"avatar_url": "https://avatars.githubusercontent.com/u/7544739?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/hub4j-test-org",
"html_url": "https://github.com/hub4j-test-org",
@@ -25,7 +25,7 @@
"site_admin": false
},
"html_url": "https://github.com/hub4j-test-org/github-api-test",
"description": "A test repository for testing the github-api project",
"description": "A test repository for testing the github-api project: github-api-test",
"fork": false,
"url": "https://api.github.com/repos/hub4j-test-org/github-api-test",
"forks_url": "https://api.github.com/repos/hub4j-test-org/github-api-test/forks",
@@ -64,9 +64,9 @@
"labels_url": "https://api.github.com/repos/hub4j-test-org/github-api-test/labels{/name}",
"releases_url": "https://api.github.com/repos/hub4j-test-org/github-api-test/releases{/id}",
"deployments_url": "https://api.github.com/repos/hub4j-test-org/github-api-test/deployments",
"created_at": "2019-10-03T18:57:53Z",
"updated_at": "2019-10-03T18:57:57Z",
"pushed_at": "2019-10-03T18:57:54Z",
"created_at": "2021-01-23T05:30:23Z",
"updated_at": "2021-01-23T05:30:27Z",
"pushed_at": "2021-01-23T05:30:25Z",
"git_url": "git://github.com/hub4j-test-org/github-api-test.git",
"ssh_url": "git@github.com:hub4j-test-org/github-api-test.git",
"clone_url": "https://github.com/hub4j-test-org/github-api-test.git",
@@ -90,20 +90,22 @@
"forks": 0,
"open_issues": 0,
"watchers": 0,
"default_branch": "master",
"default_branch": "main",
"permissions": {
"admin": true,
"push": true,
"pull": true
},
"temp_clone_token": "",
"allow_squash_merge": true,
"allow_merge_commit": true,
"allow_rebase_merge": true,
"delete_branch_on_merge": false,
"organization": {
"login": "hub4j-test-org",
"id": 7544739,
"node_id": "MDEyOk9yZ2FuaXphdGlvbjc1NDQ3Mzk=",
"avatar_url": "https://avatars3.githubusercontent.com/u/7544739?v=4",
"avatar_url": "https://avatars.githubusercontent.com/u/7544739?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/hub4j-test-org",
"html_url": "https://github.com/hub4j-test-org",
@@ -120,5 +122,5 @@
"site_admin": false
},
"network_count": 0,
"subscribers_count": 2
"subscribers_count": 9
}

View File

@@ -1,38 +1,39 @@
{
"url": "http://localhost:62379/repos/hub4j-test-org/github-api-test/deployments/173089055",
"id": 173089055,
"node_id": "MDEwOkRlcGxveW1lbnQxNzMwODkwNTU=",
"sha": "a446d9fa5c6f43d5f9333b625606909cd4635071",
"ref": "master",
"url": "https://api.github.com/repos/hub4j-test-org/github-api-test/deployments/315601563",
"id": 315601563,
"node_id": "MDEwOkRlcGxveW1lbnQzMTU2MDE1NjM=",
"task": "deploy",
"payload": "{\"user\":\"atmos\",\"room_id\":123456}",
"original_environment": "unittest",
"environment": "unittest",
"description": "question",
"created_at": "2021-01-23T05:30:28Z",
"updated_at": "2021-01-23T05:30:28Z",
"statuses_url": "https://api.github.com/repos/hub4j-test-org/github-api-test/deployments/315601563/statuses",
"repository_url": "https://api.github.com/repos/hub4j-test-org/github-api-test",
"creator": {
"login": "bitwiseman",
"id": 1958953,
"node_id": "MDQ6VXNlcjE5NTg5NTM=",
"avatar_url": "https://avatars3.githubusercontent.com/u/1958953?v=4",
"avatar_url": "https://avatars.githubusercontent.com/u/1958953?v=4",
"gravatar_id": "",
"url": "http://localhost:62379/users/bitwiseman",
"url": "https://api.github.com/users/bitwiseman",
"html_url": "https://github.com/bitwiseman",
"followers_url": "http://localhost:62379/users/bitwiseman/followers",
"following_url": "http://localhost:62379/users/bitwiseman/following{/other_user}",
"gists_url": "http://localhost:62379/users/bitwiseman/gists{/gist_id}",
"starred_url": "http://localhost:62379/users/bitwiseman/starred{/owner}{/repo}",
"subscriptions_url": "http://localhost:62379/users/bitwiseman/subscriptions",
"organizations_url": "http://localhost:62379/users/bitwiseman/orgs",
"repos_url": "http://localhost:62379/users/bitwiseman/repos",
"events_url": "http://localhost:62379/users/bitwiseman/events{/privacy}",
"received_events_url": "http://localhost:62379/users/bitwiseman/received_events",
"followers_url": "https://api.github.com/users/bitwiseman/followers",
"following_url": "https://api.github.com/users/bitwiseman/following{/other_user}",
"gists_url": "https://api.github.com/users/bitwiseman/gists{/gist_id}",
"starred_url": "https://api.github.com/users/bitwiseman/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/bitwiseman/subscriptions",
"organizations_url": "https://api.github.com/users/bitwiseman/orgs",
"repos_url": "https://api.github.com/users/bitwiseman/repos",
"events_url": "https://api.github.com/users/bitwiseman/events{/privacy}",
"received_events_url": "https://api.github.com/users/bitwiseman/received_events",
"type": "User",
"site_admin": false
},
"created_at": "2019-10-03T18:57:57Z",
"updated_at": "2019-10-03T18:57:57Z",
"statuses_url": "http://localhost:62379/repos/hub4j-test-org/github-api-test/deployments/173089055/statuses",
"repository_url": "http://localhost:62379/repos/hub4j-test-org/github-api-test",
"transient_environment": true,
"production_environment": false
"sha": "e53a04ca0ba2fbe6ea3fd590e8ea1391ac61630f",
"ref": "main",
"payload": "{\"user\":\"atmos\",\"room_id\":123456}",
"transient_environment": false,
"production_environment": false,
"performed_via_github_app": null
}

View File

@@ -1,20 +1,21 @@
[
{
"url": "https://api.github.com/repos/hub4j-test-org/github-api-test/deployments/173089055",
"id": 173089055,
"node_id": "MDEwOkRlcGxveW1lbnQxNzMwODkwNTU=",
"sha": "a446d9fa5c6f43d5f9333b625606909cd4635071",
"ref": "master",
"url": "https://api.github.com/repos/hub4j-test-org/github-api-test/deployments/315601563",
"id": 315601563,
"node_id": "MDEwOkRlcGxveW1lbnQzMTU2MDE1NjM=",
"task": "deploy",
"payload": "{\"user\":\"atmos\",\"room_id\":123456}",
"original_environment": "unittest",
"environment": "unittest",
"description": "question",
"created_at": "2021-01-23T05:30:28Z",
"updated_at": "2021-01-23T05:30:28Z",
"statuses_url": "https://api.github.com/repos/hub4j-test-org/github-api-test/deployments/315601563/statuses",
"repository_url": "https://api.github.com/repos/hub4j-test-org/github-api-test",
"creator": {
"login": "bitwiseman",
"id": 1958953,
"node_id": "MDQ6VXNlcjE5NTg5NTM=",
"avatar_url": "https://avatars3.githubusercontent.com/u/1958953?v=4",
"avatar_url": "https://avatars.githubusercontent.com/u/1958953?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/bitwiseman",
"html_url": "https://github.com/bitwiseman",
@@ -30,11 +31,11 @@
"type": "User",
"site_admin": false
},
"created_at": "2019-10-03T18:57:57Z",
"updated_at": "2019-10-03T18:57:57Z",
"statuses_url": "https://api.github.com/repos/hub4j-test-org/github-api-test/deployments/173089055/statuses",
"repository_url": "https://api.github.com/repos/hub4j-test-org/github-api-test",
"transient_environment": true,
"production_environment": false
"sha": "e53a04ca0ba2fbe6ea3fd590e8ea1391ac61630f",
"ref": "main",
"payload": "{\"user\":\"atmos\",\"room_id\":123456}",
"transient_environment": false,
"production_environment": false,
"performed_via_github_app": null
}
]

View File

@@ -2,7 +2,7 @@
"login": "bitwiseman",
"id": 1958953,
"node_id": "MDQ6VXNlcjE5NTg5NTM=",
"avatar_url": "https://avatars3.githubusercontent.com/u/1958953?v=4",
"avatar_url": "https://avatars.githubusercontent.com/u/1958953?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/bitwiseman",
"html_url": "https://github.com/bitwiseman",
@@ -23,17 +23,18 @@
"location": "Seattle, WA, USA",
"email": "bitwiseman@gmail.com",
"hireable": null,
"bio": "https://twitter.com/bitwiseman",
"public_repos": 167,
"public_gists": 4,
"followers": 136,
"following": 9,
"bio": null,
"twitter_username": "bitwiseman",
"public_repos": 201,
"public_gists": 7,
"followers": 176,
"following": 11,
"created_at": "2012-07-11T20:38:33Z",
"updated_at": "2019-09-24T19:32:29Z",
"private_gists": 7,
"total_private_repos": 9,
"updated_at": "2021-01-22T16:38:42Z",
"private_gists": 19,
"total_private_repos": 17,
"owned_private_repos": 0,
"disk_usage": 33697,
"disk_usage": 33700,
"collaborators": 0,
"two_factor_authentication": true,
"plan": {

View File

@@ -1,5 +1,5 @@
{
"id": "31a86a0d-5120-47b8-a9bc-ec4b34a08080",
"id": "b0faf3a5-5d1d-4f5f-bcc0-32f5c182f4c4",
"name": "repos_hub4j-test-org_github-api-test",
"request": {
"url": "/repos/hub4j-test-org/github-api-test",
@@ -14,35 +14,35 @@
"status": 200,
"bodyFileName": "repos_hub4j-test-org_github-api-test-2.json",
"headers": {
"Date": "Thu, 03 Oct 2019 18:57:57 GMT",
"Date": "Sat, 23 Jan 2021 05:30:28 GMT",
"Content-Type": "application/json; charset=utf-8",
"Server": "GitHub.com",
"Status": "200 OK",
"X-RateLimit-Limit": "5000",
"X-RateLimit-Remaining": "4750",
"X-RateLimit-Reset": "1570132527",
"Cache-Control": "private, max-age=60, s-maxage=60",
"Vary": [
"Accept, Authorization, Cookie, X-GitHub-OTP",
"Accept-Encoding, Accept, X-Requested-With",
"Accept-Encoding"
],
"ETag": "W/\"65ee4cf55cf4d084267c80a5d39244c6\"",
"Last-Modified": "Thu, 03 Oct 2019 18:57:57 GMT",
"X-OAuth-Scopes": "admin:org, admin:org_hook, admin:public_key, admin:repo_hook, delete_repo, gist, notifications, repo, user, write:discussion",
"ETag": "W/\"10026c1f1a22ffaba0888b2d3e2501ab45ade2789609aa725a767f8593b8f060\"",
"last-modified": "Sat, 23 Jan 2021 05:30:27 GMT",
"X-OAuth-Scopes": "admin:org, admin:org_hook, admin:public_key, admin:repo_hook, delete_repo, gist, notifications, repo, user, workflow, write:discussion",
"X-Accepted-OAuth-Scopes": "repo",
"X-GitHub-Media-Type": "unknown, github.v3",
"Access-Control-Expose-Headers": "ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type",
"Access-Control-Allow-Origin": "*",
"X-RateLimit-Limit": "5000",
"X-RateLimit-Remaining": "4904",
"X-RateLimit-Reset": "1611382753",
"x-ratelimit-used": "96",
"Strict-Transport-Security": "max-age=31536000; includeSubdomains; preload",
"X-Frame-Options": "deny",
"X-Content-Type-Options": "nosniff",
"X-XSS-Protection": "1; mode=block",
"Referrer-Policy": "origin-when-cross-origin, strict-origin-when-cross-origin",
"Content-Security-Policy": "default-src 'none'",
"X-GitHub-Request-Id": "F3AD:3618:10E8DB:149FB6:5D9644B0"
"X-GitHub-Request-Id": "D9F6:8870:9D4A2:B9689:600BB46D"
}
},
"uuid": "31a86a0d-5120-47b8-a9bc-ec4b34a08080",
"uuid": "b0faf3a5-5d1d-4f5f-bcc0-32f5c182f4c4",
"persistent": true,
"insertionIndex": 2
}

View File

@@ -1,56 +1,56 @@
{
"id": "702b7123-86c1-41e0-bee7-f3cb89a3236d",
"id": "794202d0-93a7-4526-b91b-00059c377ee0",
"name": "repos_hub4j-test-org_github-api-test_deployments",
"request": {
"url": "/repos/hub4j-test-org/github-api-test/deployments",
"method": "POST",
"bodyPatterns": [
{
"equalToJson": "{\"ref\":\"master\",\"environment\":\"unittest\",\"payload\":\"{\\\"user\\\":\\\"atmos\\\",\\\"room_id\\\":123456}\",\"description\":\"question\"}",
"ignoreArrayOrder": true,
"ignoreExtraElements": true
}
],
"headers": {
"Accept": {
"equalTo": "application/vnd.github.ant-man-preview+json, application/vnd.github.flash-preview+json"
}
}
},
"bodyPatterns": [
{
"equalToJson": "{\"ref\":\"main\",\"environment\":\"unittest\",\"payload\":\"{\\\"user\\\":\\\"atmos\\\",\\\"room_id\\\":123456}\",\"description\":\"question\"}",
"ignoreArrayOrder": true,
"ignoreExtraElements": false
}
]
},
"response": {
"status": 201,
"bodyFileName": "repos_hub4j-test-org_github-api-test_deployments-3.json",
"headers": {
"Date": "Thu, 03 Oct 2019 18:57:57 GMT",
"Date": "Sat, 23 Jan 2021 05:30:28 GMT",
"Content-Type": "application/json; charset=utf-8",
"Server": "GitHub.com",
"Status": "201 Created",
"X-RateLimit-Limit": "5000",
"X-RateLimit-Remaining": "4749",
"X-RateLimit-Reset": "1570132527",
"Cache-Control": "private, max-age=60, s-maxage=60",
"Vary": [
"Accept, Authorization, Cookie, X-GitHub-OTP",
"Accept-Encoding, Accept, X-Requested-With",
"Accept-Encoding"
],
"ETag": "\"c76b22eae3a1d091db2c789a39fecda9\"",
"Last-Modified": "Thu, 03 Oct 2019 18:57:57 GMT",
"X-OAuth-Scopes": "admin:org, admin:org_hook, admin:public_key, admin:repo_hook, delete_repo, gist, notifications, repo, user, write:discussion",
"ETag": "\"d256e649a7f1dfa04498ff9107cd30d1f42dcf80793952739c20e04436165cf3\"",
"last-modified": "Sat, 23 Jan 2021 05:30:28 GMT",
"X-OAuth-Scopes": "admin:org, admin:org_hook, admin:public_key, admin:repo_hook, delete_repo, gist, notifications, repo, user, workflow, write:discussion",
"X-Accepted-OAuth-Scopes": "",
"Location": "https://api.github.com/repos/hub4j-test-org/github-api-test/deployments/173089055",
"X-GitHub-Media-Type": "unknown, github.v3",
"Access-Control-Expose-Headers": "ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type",
"Access-Control-Allow-Origin": "*",
"Location": "https://api.github.com/repos/hub4j-test-org/github-api-test/deployments/315601563",
"X-GitHub-Media-Type": "github.ant-man-preview; format=json, github.flash-preview; format=json",
"X-RateLimit-Limit": "5000",
"X-RateLimit-Remaining": "4903",
"X-RateLimit-Reset": "1611382753",
"x-ratelimit-used": "97",
"Strict-Transport-Security": "max-age=31536000; includeSubdomains; preload",
"X-Frame-Options": "deny",
"X-Content-Type-Options": "nosniff",
"X-XSS-Protection": "1; mode=block",
"Referrer-Policy": "origin-when-cross-origin, strict-origin-when-cross-origin",
"Content-Security-Policy": "default-src 'none'",
"X-GitHub-Request-Id": "F3AD:3618:10E8DF:149FEF:5D9644B5"
"X-GitHub-Request-Id": "D9F6:8870:9D4A9:B9794:600BB474"
}
},
"uuid": "702b7123-86c1-41e0-bee7-f3cb89a3236d",
"uuid": "794202d0-93a7-4526-b91b-00059c377ee0",
"persistent": true,
"insertionIndex": 3
}

View File

@@ -1,8 +1,8 @@
{
"id": "b4bcdadb-a708-4509-9382-479f300eb172",
"id": "ad687411-71ba-4851-91b0-2ace8f6111a2",
"name": "repos_hub4j-test-org_github-api-test_deployments",
"request": {
"url": "/repos/hub4j-test-org/github-api-test/deployments?ref=master&environment=unittest",
"url": "/repos/hub4j-test-org/github-api-test/deployments?ref=main&environment=unittest",
"method": "GET",
"headers": {
"Accept": {
@@ -14,34 +14,34 @@
"status": 200,
"bodyFileName": "repos_hub4j-test-org_github-api-test_deployments-4.json",
"headers": {
"Date": "Thu, 03 Oct 2019 18:57:58 GMT",
"Date": "Sat, 23 Jan 2021 05:30:29 GMT",
"Content-Type": "application/json; charset=utf-8",
"Server": "GitHub.com",
"Status": "200 OK",
"X-RateLimit-Limit": "5000",
"X-RateLimit-Remaining": "4748",
"X-RateLimit-Reset": "1570132527",
"Cache-Control": "private, max-age=60, s-maxage=60",
"Vary": [
"Accept, Authorization, Cookie, X-GitHub-OTP",
"Accept-Encoding, Accept, X-Requested-With",
"Accept-Encoding"
],
"ETag": "W/\"b47f03577f96c1081341944009009f7f\"",
"X-OAuth-Scopes": "admin:org, admin:org_hook, admin:public_key, admin:repo_hook, delete_repo, gist, notifications, repo, user, write:discussion",
"ETag": "W/\"37d9142674c76d3eb0593d8a62756ab18011fd64315c81dbfa2c7f7dd7596dd7\"",
"X-OAuth-Scopes": "admin:org, admin:org_hook, admin:public_key, admin:repo_hook, delete_repo, gist, notifications, repo, user, workflow, write:discussion",
"X-Accepted-OAuth-Scopes": "repo, repo_deployment",
"X-GitHub-Media-Type": "unknown, github.v3",
"Access-Control-Expose-Headers": "ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type",
"Access-Control-Allow-Origin": "*",
"X-GitHub-Media-Type": "github.ant-man-preview; format=json, github.flash-preview; format=json",
"X-RateLimit-Limit": "5000",
"X-RateLimit-Remaining": "4902",
"X-RateLimit-Reset": "1611382753",
"x-ratelimit-used": "98",
"Strict-Transport-Security": "max-age=31536000; includeSubdomains; preload",
"X-Frame-Options": "deny",
"X-Content-Type-Options": "nosniff",
"X-XSS-Protection": "1; mode=block",
"Referrer-Policy": "origin-when-cross-origin, strict-origin-when-cross-origin",
"Content-Security-Policy": "default-src 'none'",
"X-GitHub-Request-Id": "F3AD:3618:10E8E2:149FF2:5D9644B5"
"X-GitHub-Request-Id": "D9F6:8870:9D4AF:B979B:600BB474"
}
},
"uuid": "b4bcdadb-a708-4509-9382-479f300eb172",
"uuid": "ad687411-71ba-4851-91b0-2ace8f6111a2",
"persistent": true,
"insertionIndex": 4
}

View File

@@ -0,0 +1,42 @@
{
"id": "3360bee1-e184-49de-8434-9fabafc6e075",
"name": "repos_hub4j-test-org_github-api-test_deployments_315601563",
"request": {
"url": "/repos/hub4j-test-org/github-api-test/deployments/315601563",
"method": "DELETE",
"headers": {
"Accept": {
"equalTo": "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2"
}
}
},
"response": {
"status": 204,
"headers": {
"Date": "Sat, 23 Jan 2021 05:30:29 GMT",
"Server": "GitHub.com",
"Status": "204 No Content",
"X-OAuth-Scopes": "admin:org, admin:org_hook, admin:public_key, admin:repo_hook, delete_repo, gist, notifications, repo, user, workflow, write:discussion",
"X-Accepted-OAuth-Scopes": "",
"X-GitHub-Media-Type": "unknown, github.v3",
"X-RateLimit-Limit": "5000",
"X-RateLimit-Remaining": "4901",
"X-RateLimit-Reset": "1611382753",
"x-ratelimit-used": "99",
"Strict-Transport-Security": "max-age=31536000; includeSubdomains; preload",
"X-Frame-Options": "deny",
"X-Content-Type-Options": "nosniff",
"X-XSS-Protection": "1; mode=block",
"Referrer-Policy": "origin-when-cross-origin, strict-origin-when-cross-origin",
"Content-Security-Policy": "default-src 'none'",
"Vary": [
"Accept-Encoding, Accept, X-Requested-With",
"Accept-Encoding"
],
"X-GitHub-Request-Id": "D9F6:8870:9D4B5:B97A0:600BB475"
}
},
"uuid": "3360bee1-e184-49de-8434-9fabafc6e075",
"persistent": true,
"insertionIndex": 5
}

View File

@@ -1,5 +1,5 @@
{
"id": "6d2dcbf4-4abf-4180-9b55-ca2931ca31c5",
"id": "9e2336dc-4e61-49ef-a171-2d408b3bf5ac",
"name": "user",
"request": {
"url": "/user",
@@ -14,35 +14,35 @@
"status": 200,
"bodyFileName": "user-1.json",
"headers": {
"Date": "Thu, 03 Oct 2019 18:57:52 GMT",
"Date": "Sat, 23 Jan 2021 05:30:21 GMT",
"Content-Type": "application/json; charset=utf-8",
"Server": "GitHub.com",
"Status": "200 OK",
"X-RateLimit-Limit": "5000",
"X-RateLimit-Remaining": "4759",
"X-RateLimit-Reset": "1570132527",
"Cache-Control": "private, max-age=60, s-maxage=60",
"Vary": [
"Accept, Authorization, Cookie, X-GitHub-OTP",
"Accept-Encoding, Accept, X-Requested-With",
"Accept-Encoding"
],
"ETag": "W/\"cf6199fecf47b59c42190e1e11147ee2\"",
"Last-Modified": "Tue, 24 Sep 2019 19:32:29 GMT",
"X-OAuth-Scopes": "admin:org, admin:org_hook, admin:public_key, admin:repo_hook, delete_repo, gist, notifications, repo, user, write:discussion",
"ETag": "W/\"c8b61de8f7b00ef1a040d10e88b51dd065defb82f7d94a95a97b3dbab636edbe\"",
"last-modified": "Fri, 22 Jan 2021 16:38:42 GMT",
"X-OAuth-Scopes": "admin:org, admin:org_hook, admin:public_key, admin:repo_hook, delete_repo, gist, notifications, repo, user, workflow, write:discussion",
"X-Accepted-OAuth-Scopes": "",
"X-GitHub-Media-Type": "unknown, github.v3",
"Access-Control-Expose-Headers": "ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type",
"Access-Control-Allow-Origin": "*",
"X-RateLimit-Limit": "5000",
"X-RateLimit-Remaining": "4909",
"X-RateLimit-Reset": "1611382753",
"x-ratelimit-used": "91",
"Strict-Transport-Security": "max-age=31536000; includeSubdomains; preload",
"X-Frame-Options": "deny",
"X-Content-Type-Options": "nosniff",
"X-XSS-Protection": "1; mode=block",
"Referrer-Policy": "origin-when-cross-origin, strict-origin-when-cross-origin",
"Content-Security-Policy": "default-src 'none'",
"X-GitHub-Request-Id": "F3AD:3618:10E8AA:149FB4:5D9644B0"
"X-GitHub-Request-Id": "D9F6:8870:9D3CD:B9683:600BB46D"
}
},
"uuid": "6d2dcbf4-4abf-4180-9b55-ca2931ca31c5",
"uuid": "9e2336dc-4e61-49ef-a171-2d408b3bf5ac",
"persistent": true,
"insertionIndex": 1
}

View File

@@ -8,7 +8,7 @@
"login": "kohsuke",
"id": 50003,
"node_id": "MDQ6VXNlcjUwMDAz",
"avatar_url": "https://avatars1.githubusercontent.com/u/50003?v=4",
"avatar_url": "https://avatars.githubusercontent.com/u/50003?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/kohsuke",
"html_url": "https://github.com/kohsuke",
@@ -96,6 +96,7 @@
"push": false,
"pull": true
},
"temp_clone_token": "",
"parent": {
"id": 3231216,
"node_id": "MDEwOlJlcG9zaXRvcnkzMjMxMjE2",
@@ -106,7 +107,7 @@
"login": "kohsuke2",
"id": 1329242,
"node_id": "MDQ6VXNlcjEzMjkyNDI=",
"avatar_url": "https://avatars2.githubusercontent.com/u/1329242?v=4",
"avatar_url": "https://avatars.githubusercontent.com/u/1329242?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/kohsuke2",
"html_url": "https://github.com/kohsuke2",
@@ -200,7 +201,7 @@
"login": "kohsuke2",
"id": 1329242,
"node_id": "MDQ6VXNlcjEzMjkyNDI=",
"avatar_url": "https://avatars2.githubusercontent.com/u/1329242?v=4",
"avatar_url": "https://avatars.githubusercontent.com/u/1329242?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/kohsuke2",
"html_url": "https://github.com/kohsuke2",

View File

@@ -1,13 +1,13 @@
{
"url": "https://api.github.com/repos/kohsuke/sandbox-ant/comments/35673784",
"html_url": "https://github.com/kohsuke/sandbox-ant/commit/8ae38db0ea5837313ab5f39d43a6f73de3bd9000#commitcomment-35673784",
"id": 35673784,
"node_id": "MDEzOkNvbW1pdENvbW1lbnQzNTY3Mzc4NA==",
"url": "https://api.github.com/repos/kohsuke/sandbox-ant/comments/46267761",
"html_url": "https://github.com/kohsuke/sandbox-ant/commit/8ae38db0ea5837313ab5f39d43a6f73de3bd9000#commitcomment-46267761",
"id": 46267761,
"node_id": "MDEzOkNvbW1pdENvbW1lbnQ0NjI2Nzc2MQ==",
"user": {
"login": "bitwiseman",
"id": 1958953,
"node_id": "MDQ6VXNlcjE5NTg5NTM=",
"avatar_url": "https://avatars3.githubusercontent.com/u/1958953?v=4",
"avatar_url": "https://avatars.githubusercontent.com/u/1958953?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/bitwiseman",
"html_url": "https://github.com/bitwiseman",
@@ -27,8 +27,8 @@
"line": null,
"path": null,
"commit_id": "8ae38db0ea5837313ab5f39d43a6f73de3bd9000",
"created_at": "2019-10-26T01:28:59Z",
"updated_at": "2019-10-26T01:28:59Z",
"created_at": "2021-01-23T06:39:40Z",
"updated_at": "2021-01-23T06:39:40Z",
"author_association": "NONE",
"body": "updated text"
}

View File

@@ -33,7 +33,7 @@
"login": "kohsuke",
"id": 50003,
"node_id": "MDQ6VXNlcjUwMDAz",
"avatar_url": "https://avatars1.githubusercontent.com/u/50003?v=4",
"avatar_url": "https://avatars.githubusercontent.com/u/50003?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/kohsuke",
"html_url": "https://github.com/kohsuke",
@@ -53,7 +53,7 @@
"login": "kohsuke",
"id": 50003,
"node_id": "MDQ6VXNlcjUwMDAz",
"avatar_url": "https://avatars1.githubusercontent.com/u/50003?v=4",
"avatar_url": "https://avatars.githubusercontent.com/u/50003?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/kohsuke",
"html_url": "https://github.com/kohsuke",

View File

@@ -1,13 +1,13 @@
{
"url": "https://api.github.com/repos/kohsuke/sandbox-ant/comments/35673784",
"html_url": "https://github.com/kohsuke/sandbox-ant/commit/8ae38db0ea5837313ab5f39d43a6f73de3bd9000#commitcomment-35673784",
"id": 35673784,
"node_id": "MDEzOkNvbW1pdENvbW1lbnQzNTY3Mzc4NA==",
"url": "https://api.github.com/repos/kohsuke/sandbox-ant/comments/46267761",
"html_url": "https://github.com/kohsuke/sandbox-ant/commit/8ae38db0ea5837313ab5f39d43a6f73de3bd9000#commitcomment-46267761",
"id": 46267761,
"node_id": "MDEzOkNvbW1pdENvbW1lbnQ0NjI2Nzc2MQ==",
"user": {
"login": "bitwiseman",
"id": 1958953,
"node_id": "MDQ6VXNlcjE5NTg5NTM=",
"avatar_url": "https://avatars3.githubusercontent.com/u/1958953?v=4",
"avatar_url": "https://avatars.githubusercontent.com/u/1958953?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/bitwiseman",
"html_url": "https://github.com/bitwiseman",
@@ -27,8 +27,8 @@
"line": null,
"path": null,
"commit_id": "8ae38db0ea5837313ab5f39d43a6f73de3bd9000",
"created_at": "2019-10-26T01:28:59Z",
"updated_at": "2019-10-26T01:28:59Z",
"created_at": "2021-01-23T06:39:40Z",
"updated_at": "2021-01-23T06:39:40Z",
"author_association": "NONE",
"body": "[testing](http://kohsuse.org/)"
}

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