mirror of
https://github.com/jlengrand/github-api.git
synced 2026-03-11 00:11:25 +00:00
Compare commits
373 Commits
github-api
...
github-api
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
188245fa7f | ||
|
|
e10b747d6a | ||
|
|
f2bb6a05a5 | ||
|
|
f41da19db5 | ||
|
|
d0a56dbb21 | ||
|
|
acbf286e59 | ||
|
|
0f7c160409 | ||
|
|
5113aacb89 | ||
|
|
4e31636181 | ||
|
|
1e497d2c44 | ||
|
|
70bd4fa161 | ||
|
|
65996050d5 | ||
|
|
7f52031199 | ||
|
|
5430f3d33c | ||
|
|
43075faaf8 | ||
|
|
f3a1272e31 | ||
|
|
41c028d4d9 | ||
|
|
a17ce04552 | ||
|
|
d0b4652dcd | ||
|
|
e66f71c76e | ||
|
|
6effd4b846 | ||
|
|
df861f5403 | ||
|
|
e74346fed6 | ||
|
|
6961c467a6 | ||
|
|
892d305165 | ||
|
|
fa16261d7a | ||
|
|
15991fd2f7 | ||
|
|
c80b8f60f8 | ||
|
|
ab6253cbd0 | ||
|
|
35ba267115 | ||
|
|
ab24e6e1c1 | ||
|
|
e25ae27a15 | ||
|
|
2b7c524908 | ||
|
|
0b069df9ce | ||
|
|
60c9ba88ae | ||
|
|
cc2e60c84a | ||
|
|
83c2c4e92e | ||
|
|
ab3d9e82ef | ||
|
|
b6063dd534 | ||
|
|
e6754354e4 | ||
|
|
4849619d67 | ||
|
|
9b0ace242a | ||
|
|
e94ba74058 | ||
|
|
569fa06d2d | ||
|
|
46dce17abc | ||
|
|
40d8f4a352 | ||
|
|
6415785220 | ||
|
|
7735edeae8 | ||
|
|
0f81d1dbb3 | ||
|
|
ae1ec8b558 | ||
|
|
09c2b39530 | ||
|
|
b443e866f9 | ||
|
|
d4404713a8 | ||
|
|
47409a9a99 | ||
|
|
60bfea2d3b | ||
|
|
d3ed8eaed5 | ||
|
|
692dccf110 | ||
|
|
92caf98683 | ||
|
|
6178d38895 | ||
|
|
40fb38a9ba | ||
|
|
20e68d53fd | ||
|
|
2d3557e049 | ||
|
|
d8f4bc7395 | ||
|
|
353f9bb809 | ||
|
|
ccfe3ad4f7 | ||
|
|
9012820c03 | ||
|
|
fe2af19e42 | ||
|
|
f721e053f1 | ||
|
|
e6ad9feb84 | ||
|
|
635350c40e | ||
|
|
17edd33703 | ||
|
|
b0f2a871c6 | ||
|
|
8928a8a1dc | ||
|
|
2b6f37a6cc | ||
|
|
f3a3b87861 | ||
|
|
9cf6ee78d4 | ||
|
|
bbc2f3962f | ||
|
|
be49eb22d2 | ||
|
|
fb47067215 | ||
|
|
2c80ef178d | ||
|
|
9af8112148 | ||
|
|
57c36f437a | ||
|
|
5ed8a34566 | ||
|
|
ea8df9bd61 | ||
|
|
b0c51e03b7 | ||
|
|
336924ef23 | ||
|
|
ad28ca4a90 | ||
|
|
aebbe86cfc | ||
|
|
df9faf4943 | ||
|
|
3e295b6be4 | ||
|
|
8dd6dbf995 | ||
|
|
612139f2ff | ||
|
|
240bcabb76 | ||
|
|
cda27d5963 | ||
|
|
2f8c3997f7 | ||
|
|
46b89a48db | ||
|
|
5c9cbee2f9 | ||
|
|
ee8973c239 | ||
|
|
2e2813f363 | ||
|
|
7ceca0769f | ||
|
|
4d277cc61f | ||
|
|
e67fbb4621 | ||
|
|
29b5357ceb | ||
|
|
9dabec107b | ||
|
|
f2a2ad90b7 | ||
|
|
cfe4c0c510 | ||
|
|
23cd51a6da | ||
|
|
7396395f90 | ||
|
|
a1819bf232 | ||
|
|
8accf07d46 | ||
|
|
6dcbace572 | ||
|
|
e90c86ec2f | ||
|
|
971ae1fa4d | ||
|
|
4abe87036c | ||
|
|
8d1b44db97 | ||
|
|
b537f9925b | ||
|
|
f2fe8eaf86 | ||
|
|
46715cac08 | ||
|
|
0f21eba57f | ||
|
|
cb7620395a | ||
|
|
3a40af8871 | ||
|
|
c9b5074bc4 | ||
|
|
44d4d0d767 | ||
|
|
64af13f40d | ||
|
|
e9b59c6bef | ||
|
|
3d03659508 | ||
|
|
0374d2de48 | ||
|
|
2627dc5ee4 | ||
|
|
5554332b5b | ||
|
|
1cffea892b | ||
|
|
fd859815b0 | ||
|
|
166e26d101 | ||
|
|
be081eec3f | ||
|
|
55b00a87f6 | ||
|
|
429b26cee8 | ||
|
|
fafe6b0ff7 | ||
|
|
5b156006fb | ||
|
|
75f0c08ca4 | ||
|
|
1f4325e7db | ||
|
|
f2e7b40425 | ||
|
|
4ee3086b6d | ||
|
|
a3a715c3ba | ||
|
|
6cad4a3c33 | ||
|
|
b9b6f4fd44 | ||
|
|
17d1994a53 | ||
|
|
13184e72e1 | ||
|
|
911e8d21a7 | ||
|
|
5b69a2925f | ||
|
|
d1c900a620 | ||
|
|
6bfeb54f3c | ||
|
|
198fede915 | ||
|
|
1212ae3eb3 | ||
|
|
1266dcc0c7 | ||
|
|
6fcddf4a47 | ||
|
|
dfea424b94 | ||
|
|
9d03435aa1 | ||
|
|
26c20a7a22 | ||
|
|
470da06ecf | ||
|
|
a746a310bc | ||
|
|
3dbb516084 | ||
|
|
32177283b3 | ||
|
|
3a66e90b7a | ||
|
|
a454fb10ec | ||
|
|
b5386a35ee | ||
|
|
11651da411 | ||
|
|
88d52c44ad | ||
|
|
2d7d4bbd4e | ||
|
|
e6ee278fde | ||
|
|
6380cf9ed0 | ||
|
|
2e78dc52c7 | ||
|
|
ccb42d3249 | ||
|
|
c5009ab44b | ||
|
|
9d15cd43a3 | ||
|
|
0780e10fa2 | ||
|
|
0731f63237 | ||
|
|
a29896042b | ||
|
|
68ebc08c9d | ||
|
|
a1df526f93 | ||
|
|
0023ecefa4 | ||
|
|
511f156603 | ||
|
|
3f223b1ba0 | ||
|
|
a1528a1a63 | ||
|
|
b8bfddbf3a | ||
|
|
47fc813027 | ||
|
|
c7f2228a44 | ||
|
|
b0e0f045f8 | ||
|
|
1296514794 | ||
|
|
18e797095f | ||
|
|
85aa2ad4e6 | ||
|
|
818f6dc045 | ||
|
|
1c162c6390 | ||
|
|
def3a28fb5 | ||
|
|
d1378a0236 | ||
|
|
e544c7a65a | ||
|
|
24f48f668c | ||
|
|
9988a090ac | ||
|
|
d36e145d06 | ||
|
|
498d63ea00 | ||
|
|
7dc620a3ba | ||
|
|
66145e1d23 | ||
|
|
7bf8621afe | ||
|
|
ce3f74232e | ||
|
|
5b92d4b88c | ||
|
|
4daf6ba057 | ||
|
|
955e9899af | ||
|
|
fa3d0887ef | ||
|
|
5d5c6cf71c | ||
|
|
89aac45f41 | ||
|
|
4965fd5f4c | ||
|
|
87fbb8ec98 | ||
|
|
0d92d4ba61 | ||
|
|
b0df93bbcb | ||
|
|
290d0b226a | ||
|
|
8b3469610c | ||
|
|
50b47fb73b | ||
|
|
5334cb8688 | ||
|
|
38983df42d | ||
|
|
df963cb71c | ||
|
|
e9368fb04e | ||
|
|
e2a1630cf4 | ||
|
|
4f15b7c9fa | ||
|
|
63f500ad7f | ||
|
|
a9fb4546e1 | ||
|
|
cabbbf7f02 | ||
|
|
59324b0082 | ||
|
|
6a356c82a5 | ||
|
|
70f0f5714a | ||
|
|
07b527a0f2 | ||
|
|
80aa75aab1 | ||
|
|
0cf4211aa5 | ||
|
|
1de02a5099 | ||
|
|
bb1cecb95b | ||
|
|
d82397a173 | ||
|
|
856cf5e568 | ||
|
|
9f5a6ee549 | ||
|
|
9f3f644b83 | ||
|
|
a2f0837d14 | ||
|
|
c7f6889534 | ||
|
|
0415326d09 | ||
|
|
16a0f8ece0 | ||
|
|
5d1ef296b3 | ||
|
|
16dbcde90b | ||
|
|
50cbf25c72 | ||
|
|
7b87de2b4c | ||
|
|
1ce54a7925 | ||
|
|
1bfe7dd99b | ||
|
|
27e855ddbd | ||
|
|
3d1bed0f8f | ||
|
|
5c9ea9b63a | ||
|
|
7c034f5670 | ||
|
|
3b9f5a417a | ||
|
|
cde501af8d | ||
|
|
3c5592c1c8 | ||
|
|
2508e022bb | ||
|
|
01fcbc24e8 | ||
|
|
204e639679 | ||
|
|
3d301ec730 | ||
|
|
9ab6d57019 | ||
|
|
37c473130f | ||
|
|
5f95987a48 | ||
|
|
d530b34073 | ||
|
|
35dec7a5ec | ||
|
|
baedad8124 | ||
|
|
007378c3a6 | ||
|
|
beae9fd6ec | ||
|
|
b7507076c6 | ||
|
|
6b5ade3ca0 | ||
|
|
715192d26c | ||
|
|
ce140460af | ||
|
|
d30b0403ce | ||
|
|
255c993548 | ||
|
|
557ae4165c | ||
|
|
a31395ed80 | ||
|
|
397886d289 | ||
|
|
7307bec2ae | ||
|
|
0cd5147e1a | ||
|
|
36d5b092d7 | ||
|
|
f9014dbab3 | ||
|
|
755d5f77ea | ||
|
|
906d9af7b7 | ||
|
|
14dcb37ee1 | ||
|
|
c1c2a27358 | ||
|
|
5ab9657f9c | ||
|
|
7a78f9f5aa | ||
|
|
ac8c65f062 | ||
|
|
1954a9f3f8 | ||
|
|
3b764f9c90 | ||
|
|
10f55cc549 | ||
|
|
cd8d955646 | ||
|
|
bba07c9080 | ||
|
|
dba84a33b9 | ||
|
|
ae49166aa2 | ||
|
|
e09185fd0e | ||
|
|
56379bb3b9 | ||
|
|
027e4b4f25 | ||
|
|
ba951cb6e3 | ||
|
|
ae85cf4b6c | ||
|
|
dbc79f8c42 | ||
|
|
54c3070607 | ||
|
|
013eaa30b6 | ||
|
|
751043bf81 | ||
|
|
14f7198a07 | ||
|
|
94af819ae5 | ||
|
|
dbcc9afbc7 | ||
|
|
8556033ae6 | ||
|
|
650493f863 | ||
|
|
d80ad77871 | ||
|
|
f4b129b9f1 | ||
|
|
c0a05e0650 | ||
|
|
33d95d3e3a | ||
|
|
f573f83fb9 | ||
|
|
e94c36b7e6 | ||
|
|
c879e9e34d | ||
|
|
ac39b564a8 | ||
|
|
d91388aba4 | ||
|
|
733d78abdd | ||
|
|
d5809e375c | ||
|
|
2440a676bd | ||
|
|
03ac6c72e7 | ||
|
|
1bbbcabae0 | ||
|
|
9149b6b998 | ||
|
|
2603b5a402 | ||
|
|
dbddf5b9eb | ||
|
|
841f77bac2 | ||
|
|
83ffe75baa | ||
|
|
8cb7094803 | ||
|
|
ed8cd0ad19 | ||
|
|
90d8e65a3b | ||
|
|
b24fcb18af | ||
|
|
c2f2d0f8af | ||
|
|
e33bdd7e62 | ||
|
|
402adc3559 | ||
|
|
0f45d03c51 | ||
|
|
0397d7ab53 | ||
|
|
79f86b82e4 | ||
|
|
261a7a34e3 | ||
|
|
723bb89e10 | ||
|
|
832e4f3c37 | ||
|
|
75a4081549 | ||
|
|
f9291f9fd1 | ||
|
|
c3b4ee9321 | ||
|
|
f86896943d | ||
|
|
c33f05e8ca | ||
|
|
52727ded03 | ||
|
|
f7d132758e | ||
|
|
bb17ca9a53 | ||
|
|
aab21c5b17 | ||
|
|
acbafee02a | ||
|
|
c6d2b1a222 | ||
|
|
b037f75fb0 | ||
|
|
a1e79d3050 | ||
|
|
354969d5fa | ||
|
|
a371892409 | ||
|
|
b0789a7ce7 | ||
|
|
08be8eb4f8 | ||
|
|
bafddf4baf | ||
|
|
75b9184a00 | ||
|
|
5dc83cf2bf | ||
|
|
092e9062c8 | ||
|
|
512c921a81 | ||
|
|
defcd6fe26 | ||
|
|
025b6cbfb7 | ||
|
|
b0687dbeb5 | ||
|
|
e0b109cba6 | ||
|
|
adaa8ece89 | ||
|
|
6516b20e16 | ||
|
|
839cb03690 | ||
|
|
2bcd99b14f | ||
|
|
e3ebf6e8a1 | ||
|
|
76d28314b0 | ||
|
|
ec450b8fd8 | ||
|
|
79c5b2edd5 | ||
|
|
2d45ac51ef |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -3,3 +3,7 @@ target
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.classpath
|
||||
.project
|
||||
.settings/
|
||||
.DS_Store
|
||||
|
||||
71
pom.xml
71
pom.xml
@@ -3,11 +3,11 @@
|
||||
<parent>
|
||||
<groupId>org.kohsuke</groupId>
|
||||
<artifactId>pom</artifactId>
|
||||
<version>14</version>
|
||||
<version>17</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>github-api</artifactId>
|
||||
<version>1.69</version>
|
||||
<version>1.91</version>
|
||||
<name>GitHub API for Java</name>
|
||||
<url>http://github-api.kohsuke.org/</url>
|
||||
<description>GitHub API for Java</description>
|
||||
@@ -16,7 +16,7 @@
|
||||
<connection>scm:git:git@github.com/kohsuke/${project.artifactId}.git</connection>
|
||||
<developerConnection>scm:git:ssh://git@github.com/kohsuke/${project.artifactId}.git</developerConnection>
|
||||
<url>http://${project.artifactId}.kohsuke.org/</url>
|
||||
<tag>github-api-1.69</tag>
|
||||
<tag>github-api-1.91</tag>
|
||||
</scm>
|
||||
|
||||
<distributionManagement>
|
||||
@@ -28,14 +28,43 @@
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<findbugs-maven-plugin.version>3.0.2</findbugs-maven-plugin.version>
|
||||
<findbugs-maven-plugin.failOnError>true</findbugs-maven-plugin.failOnError>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<rerunFailingTestsCount>2</rerunFailingTestsCount>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>animal-sniffer-maven-plugin</artifactId>
|
||||
<version>1.15</version>
|
||||
<configuration>
|
||||
<signature>
|
||||
<groupId>org.codehaus.mojo.signature</groupId>
|
||||
<artifactId>java15</artifactId>
|
||||
<version>1.0</version>
|
||||
</signature>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>ensure-java-1.5-class-library</id>
|
||||
<phase>test</phase>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>com.infradna.tool</groupId>
|
||||
<artifactId>bridge-method-injector</artifactId>
|
||||
<version>1.14</version>
|
||||
<version>1.18</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
@@ -47,11 +76,10 @@
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>findbugs-maven-plugin</artifactId>
|
||||
<version>3.0.1</version>
|
||||
<version>${findbugs-maven-plugin.version}</version>
|
||||
<configuration>
|
||||
<xmlOutput>true</xmlOutput>
|
||||
<findbugsXmlWithMessages>true</findbugsXmlWithMessages>
|
||||
<failOnError>false</failOnError>
|
||||
<failOnError>${findbugs-maven-plugin.failOnError}</failOnError>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
@@ -80,13 +108,19 @@
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.11</version>
|
||||
<version>4.12</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hamcrest</groupId>
|
||||
<artifactId>hamcrest-all</artifactId>
|
||||
<version>1.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>2.2.3</version>
|
||||
<version>2.9.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
@@ -96,7 +130,8 @@
|
||||
<dependency>
|
||||
<groupId>com.infradna.tool</groupId>
|
||||
<artifactId>bridge-method-annotation</artifactId>
|
||||
<version>1.14</version>
|
||||
<version>1.17</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.kohsuke.stapler</groupId>
|
||||
@@ -107,31 +142,37 @@
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jgit</groupId>
|
||||
<artifactId>org.eclipse.jgit</artifactId>
|
||||
<version>3.1.0.201310021548-r</version>
|
||||
<version>4.9.0.201710071750-r</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp</groupId>
|
||||
<artifactId>okhttp-urlconnection</artifactId>
|
||||
<version>2.0.0</version>
|
||||
<version>2.7.5</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp-urlconnection</artifactId>
|
||||
<version>3.9.0</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.kohsuke</groupId>
|
||||
<artifactId>wordnet-random-name</artifactId>
|
||||
<version>1.2</version>
|
||||
<version>1.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-all</artifactId>
|
||||
<version>1.9.5</version>
|
||||
<version>1.10.19</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>annotations</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<version>3.0.1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
63
src/main/java/org/kohsuke/github/AbuseLimitHandler.java
Normal file
63
src/main/java/org/kohsuke/github/AbuseLimitHandler.java
Normal file
@@ -0,0 +1,63 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.net.HttpURLConnection;
|
||||
|
||||
/**
|
||||
* Pluggable strategy to determine what to do when the API abuse limit is hit.
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
* @see GitHubBuilder#withAbuseLimitHandler(AbuseLimitHandler)
|
||||
* @see <a href="https://developer.github.com/v3/#abuse-rate-limits">documentation</a>
|
||||
* @see RateLimitHandler
|
||||
*/
|
||||
public abstract class AbuseLimitHandler {
|
||||
/**
|
||||
* Called when the library encounters HTTP error indicating that the API abuse limit is reached.
|
||||
*
|
||||
* <p>
|
||||
* Any exception thrown from this method will cause the request to fail, and the caller of github-api
|
||||
* will receive an exception. If this method returns normally, another request will be attempted.
|
||||
* For that to make sense, the implementation needs to wait for some time.
|
||||
*
|
||||
* @see <a href="https://developer.github.com/v3/#abuse-rate-limits">API documentation from GitHub</a>
|
||||
* @param e
|
||||
* Exception from Java I/O layer. If you decide to fail the processing, you can throw
|
||||
* this exception (or wrap this exception into another exception and throw it.)
|
||||
* @param uc
|
||||
* Connection that resulted in an error. Useful for accessing other response headers.
|
||||
*/
|
||||
public abstract void onError(IOException e, HttpURLConnection uc) throws IOException;
|
||||
|
||||
/**
|
||||
* Wait until the API abuse "wait time" is passed.
|
||||
*/
|
||||
public static final AbuseLimitHandler WAIT = new AbuseLimitHandler() {
|
||||
@Override
|
||||
public void onError(IOException e, HttpURLConnection uc) throws IOException {
|
||||
try {
|
||||
Thread.sleep(parseWaitTime(uc));
|
||||
} catch (InterruptedException _) {
|
||||
throw (InterruptedIOException)new InterruptedIOException().initCause(e);
|
||||
}
|
||||
}
|
||||
|
||||
private long parseWaitTime(HttpURLConnection uc) {
|
||||
String v = uc.getHeaderField("Retry-After");
|
||||
if (v==null) return 60 * 1000; // can't tell, return 1 min
|
||||
|
||||
return Math.max(1000, Long.parseLong(v)*1000);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Fail immediately.
|
||||
*/
|
||||
public static final AbuseLimitHandler FAIL = new AbuseLimitHandler() {
|
||||
@Override
|
||||
public void onError(IOException e, HttpURLConnection uc) throws IOException {
|
||||
throw (IOException)new IOException("Abust limit reached").initCause(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -23,9 +23,13 @@
|
||||
*/
|
||||
package org.kohsuke.github;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
|
||||
/**
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
@SuppressFBWarnings(value = "UUF_UNUSED_PUBLIC_OR_PROTECTED_FIELD",
|
||||
justification = "Being constructed by JSON deserialization")
|
||||
class DeleteToken {
|
||||
public String delete_token;
|
||||
}
|
||||
|
||||
17
src/main/java/org/kohsuke/github/EnforcementLevel.java
Normal file
17
src/main/java/org/kohsuke/github/EnforcementLevel.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* This was added during preview API period but it has changed since then.
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
@Deprecated
|
||||
public enum EnforcementLevel {
|
||||
OFF, NON_ADMINS, EVERYONE;
|
||||
|
||||
public String toString() {
|
||||
return name().toLowerCase(Locale.ENGLISH);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -35,9 +36,14 @@ public class GHAuthorization extends GHObject {
|
||||
private GitHub root;
|
||||
private List<String> scopes;
|
||||
private String token;
|
||||
private String token_last_eight;
|
||||
private String hashed_token;
|
||||
private App app;
|
||||
private String note;
|
||||
private String note_url;
|
||||
private String fingerprint;
|
||||
//TODO add some user class for https://developer.github.com/v3/oauth_authorizations/#check-an-authorization ?
|
||||
//private GHUser user;
|
||||
|
||||
public GitHub getRoot() {
|
||||
return root;
|
||||
@@ -51,6 +57,14 @@ public class GHAuthorization extends GHObject {
|
||||
return token;
|
||||
}
|
||||
|
||||
public String getTokenLastEight() {
|
||||
return token_last_eight;
|
||||
}
|
||||
|
||||
public String getHashedToken() {
|
||||
return hashed_token;
|
||||
}
|
||||
|
||||
public URL getAppUrl() {
|
||||
return GitHub.parseURL(app.url);
|
||||
}
|
||||
@@ -59,6 +73,8 @@ public class GHAuthorization extends GHObject {
|
||||
return app.name;
|
||||
}
|
||||
|
||||
@SuppressFBWarnings(value = "NM_CONFUSING",
|
||||
justification = "It's a part of the library API, cannot be changed")
|
||||
public URL getApiURL() {
|
||||
return GitHub.parseURL(url);
|
||||
}
|
||||
@@ -79,14 +95,20 @@ public class GHAuthorization extends GHObject {
|
||||
return GitHub.parseURL(note_url);
|
||||
}
|
||||
|
||||
public String getFingerprint() {
|
||||
return fingerprint;
|
||||
}
|
||||
|
||||
/*package*/ GHAuthorization wrap(GitHub root) {
|
||||
this.root = root;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD"},
|
||||
justification = "JSON API")
|
||||
private static class App {
|
||||
private String url;
|
||||
private String name;
|
||||
// private String client_id; not yet used
|
||||
}
|
||||
}
|
||||
|
||||
64
src/main/java/org/kohsuke/github/GHBlob.java
Normal file
64
src/main/java/org/kohsuke/github/GHBlob.java
Normal file
@@ -0,0 +1,64 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64InputStream;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* @author Kanstantsin Shautsou
|
||||
* @author Kohsuke Kawaguchi
|
||||
* @see GHTreeEntry#asBlob()
|
||||
* @see GHRepository#getBlob(String)
|
||||
* @see <a href="https://developer.github.com/v3/git/blobs/#get-a-blob">Get a blob</a>
|
||||
*/
|
||||
public class GHBlob {
|
||||
private String content, encoding, url, sha;
|
||||
private long size;
|
||||
|
||||
/**
|
||||
* API URL of this blob.
|
||||
*/
|
||||
public URL getUrl() {
|
||||
return GitHub.parseURL(url);
|
||||
}
|
||||
|
||||
public String getSha() {
|
||||
return sha;
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of bytes in this blob.
|
||||
*/
|
||||
public long getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public String getEncoding() {
|
||||
return encoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encoded content. You probably want {@link #read()}
|
||||
*/
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the actual bytes of the blob.
|
||||
*/
|
||||
public InputStream read() {
|
||||
if (encoding.equals("base64")) {
|
||||
try {
|
||||
return new Base64InputStream(new ByteArrayInputStream(content.getBytes("US-ASCII")), false);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new AssertionError(e); // US-ASCII is mandatory
|
||||
}
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException("Unrecognized encoding: "+encoding);
|
||||
}
|
||||
}
|
||||
49
src/main/java/org/kohsuke/github/GHBlobBuilder.java
Normal file
49
src/main/java/org/kohsuke/github/GHBlobBuilder.java
Normal file
@@ -0,0 +1,49 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Builder pattern for creating a new blob.
|
||||
* Based on https://developer.github.com/v3/git/blobs/#create-a-blob
|
||||
*/
|
||||
public class GHBlobBuilder {
|
||||
private final GHRepository repo;
|
||||
private final Requester req;
|
||||
|
||||
GHBlobBuilder(GHRepository repo) {
|
||||
this.repo = repo;
|
||||
req = new Requester(repo.root);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures a blob with the specified text {@code content}.
|
||||
*/
|
||||
public GHBlobBuilder textContent(String content) {
|
||||
req.with("content", content);
|
||||
req.with("encoding", "utf-8");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures a blob with the specified binary {@code content}.
|
||||
*/
|
||||
public GHBlobBuilder binaryContent(byte[] content) {
|
||||
String base64Content = Base64.encodeBase64String(content);
|
||||
req.with("content", base64Content);
|
||||
req.with("encoding", "base64");
|
||||
return this;
|
||||
}
|
||||
|
||||
private String getApiTail() {
|
||||
return String.format("/repos/%s/%s/git/blobs", repo.getOwnerName(), repo.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a blob based on the parameters specified thus far.
|
||||
*/
|
||||
public GHBlob create() throws IOException {
|
||||
return req.method("POST").to(getApiTail(), GHBlob.class);
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,37 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.Collection;
|
||||
|
||||
import static org.kohsuke.github.Previews.*;
|
||||
|
||||
/**
|
||||
* A branch in a repository.
|
||||
*
|
||||
*
|
||||
* @author Yusuke Kokubo
|
||||
*/
|
||||
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
|
||||
"NP_UNWRITTEN_FIELD", "URF_UNREAD_FIELD"}, justification = "JSON API")
|
||||
public class GHBranch {
|
||||
private GitHub root;
|
||||
private GHRepository owner;
|
||||
|
||||
private String name;
|
||||
private Commit commit;
|
||||
@JsonProperty("protected")
|
||||
private boolean protection;
|
||||
private String protection_url;
|
||||
|
||||
|
||||
public static class Commit {
|
||||
String sha,url;
|
||||
String sha;
|
||||
|
||||
@SuppressFBWarnings(value = "UUF_UNUSED_FIELD", justification = "We don't provide it in API now")
|
||||
String url;
|
||||
}
|
||||
|
||||
public GitHub getRoot() {
|
||||
@@ -31,6 +49,27 @@ public class GHBranch {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the push to this branch is restricted via branch protection.
|
||||
*/
|
||||
@Preview @Deprecated
|
||||
public boolean isProtected() {
|
||||
return protection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns API URL that deals with the protection of this branch.
|
||||
*/
|
||||
@Preview @Deprecated
|
||||
public URL getProtectionUrl() {
|
||||
return GitHub.parseURL(protection_url);
|
||||
}
|
||||
|
||||
@Preview @Deprecated
|
||||
public GHBranchProtection getProtection() throws IOException {
|
||||
return root.retrieve().withPreview(LOKI).to(protection_url, GHBranchProtection.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* The commit that this branch currently points to.
|
||||
*/
|
||||
@@ -38,6 +77,45 @@ public class GHBranch {
|
||||
return commit.sha;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables branch protection and allows anyone with push access to push changes.
|
||||
*/
|
||||
@Preview @Deprecated
|
||||
public void disableProtection() throws IOException {
|
||||
new Requester(root).method("DELETE").withPreview(LOKI).to(protection_url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables branch protection to control what commit statuses are required to push.
|
||||
*
|
||||
* @see GHCommitStatus#getContext()
|
||||
*/
|
||||
@Preview @Deprecated
|
||||
public GHBranchProtectionBuilder enableProtection() {
|
||||
return new GHBranchProtectionBuilder(this);
|
||||
}
|
||||
|
||||
// backward compatibility with previous signature
|
||||
@Deprecated
|
||||
public void enableProtection(EnforcementLevel level, Collection<String> contexts) throws IOException {
|
||||
switch (level) {
|
||||
case OFF:
|
||||
disableProtection();
|
||||
break;
|
||||
case NON_ADMINS:
|
||||
case EVERYONE:
|
||||
enableProtection()
|
||||
.addRequiredChecks(contexts)
|
||||
.includeAdmins(level==EnforcementLevel.EVERYONE)
|
||||
.enable();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
String getApiRoute() {
|
||||
return owner.getApiTailUrl("/branches/"+name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final String url = owner != null ? owner.getUrl().toString() : "unknown";
|
||||
|
||||
151
src/main/java/org/kohsuke/github/GHBranchProtection.java
Normal file
151
src/main/java/org/kohsuke/github/GHBranchProtection.java
Normal file
@@ -0,0 +1,151 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
@SuppressFBWarnings(value = { "UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD",
|
||||
"URF_UNREAD_FIELD" }, justification = "JSON API")
|
||||
public class GHBranchProtection {
|
||||
@JsonProperty("enforce_admins")
|
||||
private EnforceAdmins enforceAdmins;
|
||||
|
||||
@JsonProperty("required_pull_request_reviews")
|
||||
private RequiredReviews requiredReviews;
|
||||
|
||||
@JsonProperty("required_status_checks")
|
||||
private RequiredStatusChecks requiredStatusChecks;
|
||||
|
||||
@JsonProperty
|
||||
private Restrictions restrictions;
|
||||
|
||||
@JsonProperty
|
||||
private String url;
|
||||
|
||||
public EnforceAdmins getEnforceAdmins() {
|
||||
return enforceAdmins;
|
||||
}
|
||||
|
||||
public RequiredReviews getRequiredReviews() {
|
||||
return requiredReviews;
|
||||
}
|
||||
|
||||
public RequiredStatusChecks getRequiredStatusChecks() {
|
||||
return requiredStatusChecks;
|
||||
}
|
||||
|
||||
public Restrictions getRestrictions() {
|
||||
return restrictions;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public static class EnforceAdmins {
|
||||
@JsonProperty
|
||||
private boolean enabled;
|
||||
|
||||
@JsonProperty
|
||||
private String url;
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
}
|
||||
|
||||
public static class RequiredReviews {
|
||||
@JsonProperty("dismissal_restrictions")
|
||||
private Restrictions dismissalRestriction;
|
||||
|
||||
@JsonProperty("dismiss_stale_reviews")
|
||||
private boolean dismissStaleReviews;
|
||||
|
||||
@JsonProperty("require_code_owner_reviews")
|
||||
private boolean requireCodeOwnerReviews;
|
||||
|
||||
@JsonProperty
|
||||
private String url;
|
||||
|
||||
public Restrictions getDismissalRestrictions() {
|
||||
return dismissalRestriction;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public boolean isDismissStaleReviews() {
|
||||
return dismissStaleReviews;
|
||||
}
|
||||
|
||||
public boolean isRequireCodeOwnerReviews() {
|
||||
return requireCodeOwnerReviews;
|
||||
}
|
||||
}
|
||||
|
||||
public static class RequiredStatusChecks {
|
||||
@JsonProperty
|
||||
private Collection<String> contexts;
|
||||
|
||||
@JsonProperty
|
||||
private boolean strict;
|
||||
|
||||
@JsonProperty
|
||||
private String url;
|
||||
|
||||
public Collection<String> getContexts() {
|
||||
return contexts;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public boolean isRequiresBranchUpToDate() {
|
||||
return strict;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Restrictions {
|
||||
@JsonProperty
|
||||
private Collection<GHTeam> teams;
|
||||
|
||||
@JsonProperty("teams_url")
|
||||
private String teamsUrl;
|
||||
|
||||
@JsonProperty
|
||||
private String url;
|
||||
|
||||
@JsonProperty
|
||||
private Collection<GHUser> users;
|
||||
|
||||
@JsonProperty("users_url")
|
||||
private String usersUrl;
|
||||
|
||||
public Collection<GHTeam> getTeams() {
|
||||
return teams;
|
||||
}
|
||||
|
||||
public String getTeamsUrl() {
|
||||
return teamsUrl;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public Collection<GHUser> getUsers() {
|
||||
return users;
|
||||
}
|
||||
|
||||
public String getUsersUrl() {
|
||||
return usersUrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
203
src/main/java/org/kohsuke/github/GHBranchProtectionBuilder.java
Normal file
203
src/main/java/org/kohsuke/github/GHBranchProtectionBuilder.java
Normal file
@@ -0,0 +1,203 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.kohsuke.github.Previews.*;
|
||||
|
||||
/**
|
||||
* Builder to configure the branch protection settings.
|
||||
*
|
||||
* @see GHBranch#enableProtection()
|
||||
*/
|
||||
@SuppressFBWarnings(value = { "UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD",
|
||||
"URF_UNREAD_FIELD" }, justification = "JSON API")
|
||||
public class GHBranchProtectionBuilder {
|
||||
private final GHBranch branch;
|
||||
|
||||
private boolean enforceAdmins;
|
||||
private Map<String, Object> prReviews;
|
||||
private Restrictions restrictions;
|
||||
private StatusChecks statusChecks;
|
||||
|
||||
GHBranchProtectionBuilder(GHBranch branch) {
|
||||
this.branch = branch;
|
||||
}
|
||||
|
||||
public GHBranchProtectionBuilder addRequiredChecks(Collection<String> checks) {
|
||||
getStatusChecks().contexts.addAll(checks);
|
||||
return this;
|
||||
}
|
||||
|
||||
public GHBranchProtectionBuilder addRequiredChecks(String... checks) {
|
||||
addRequiredChecks(Arrays.asList(checks));
|
||||
return this;
|
||||
}
|
||||
|
||||
public GHBranchProtectionBuilder dismissStaleReviews() {
|
||||
getPrReviews().put("dismiss_stale_reviews", true);
|
||||
return this;
|
||||
}
|
||||
|
||||
public GHBranchProtection enable() throws IOException {
|
||||
return requester().method("PUT")
|
||||
.withNullable("required_status_checks", statusChecks)
|
||||
.withNullable("required_pull_request_reviews", prReviews)
|
||||
.withNullable("restrictions", restrictions)
|
||||
.withNullable("enforce_admins", enforceAdmins)
|
||||
.to(branch.getProtectionUrl().toString(), GHBranchProtection.class);
|
||||
}
|
||||
|
||||
public GHBranchProtectionBuilder includeAdmins() {
|
||||
return includeAdmins(true);
|
||||
}
|
||||
|
||||
public GHBranchProtectionBuilder includeAdmins(boolean v) {
|
||||
enforceAdmins = v;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GHBranchProtectionBuilder requireBranchIsUpToDate() {
|
||||
return requireBranchIsUpToDate(true);
|
||||
}
|
||||
|
||||
public GHBranchProtectionBuilder requireBranchIsUpToDate(boolean v) {
|
||||
getStatusChecks().strict = v;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GHBranchProtectionBuilder requireCodeOwnReviews() {
|
||||
return requireCodeOwnReviews(true);
|
||||
}
|
||||
|
||||
public GHBranchProtectionBuilder requireCodeOwnReviews(boolean v) {
|
||||
getPrReviews().put("require_code_owner_reviews", v);
|
||||
return this;
|
||||
}
|
||||
|
||||
public GHBranchProtectionBuilder requireReviews() {
|
||||
getPrReviews();
|
||||
return this;
|
||||
}
|
||||
|
||||
public GHBranchProtectionBuilder restrictPushAccess() {
|
||||
getRestrictions();
|
||||
return this;
|
||||
}
|
||||
|
||||
public GHBranchProtectionBuilder teamPushAccess(Collection<GHTeam> teams) {
|
||||
for (GHTeam team : teams) {
|
||||
teamPushAccess(team);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public GHBranchProtectionBuilder teamPushAccess(GHTeam... teams) {
|
||||
for (GHTeam team : teams) {
|
||||
getRestrictions().teams.add(team.getSlug());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public GHBranchProtectionBuilder teamReviewDismissals(Collection<GHTeam> teams) {
|
||||
for (GHTeam team : teams) {
|
||||
teamReviewDismissals(team);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public GHBranchProtectionBuilder teamReviewDismissals(GHTeam... teams) {
|
||||
for (GHTeam team : teams) {
|
||||
addReviewRestriction(team.getSlug(), true);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public GHBranchProtectionBuilder userPushAccess(Collection<GHUser> users) {
|
||||
for (GHUser user : users) {
|
||||
userPushAccess(user);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public GHBranchProtectionBuilder userPushAccess(GHUser... users) {
|
||||
for (GHUser user : users) {
|
||||
getRestrictions().users.add(user.getLogin());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public GHBranchProtectionBuilder userReviewDismissals(Collection<GHUser> users) {
|
||||
for (GHUser team : users) {
|
||||
userReviewDismissals(team);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public GHBranchProtectionBuilder userReviewDismissals(GHUser... users) {
|
||||
for (GHUser user : users) {
|
||||
addReviewRestriction(user.getLogin(), false);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private void addReviewRestriction(String restriction, boolean isTeam) {
|
||||
getPrReviews();
|
||||
|
||||
if (!prReviews.containsKey("dismissal_restrictions")) {
|
||||
prReviews.put("dismissal_restrictions", new Restrictions());
|
||||
}
|
||||
|
||||
Restrictions restrictions = (Restrictions) prReviews.get("dismissal_restrictions");
|
||||
|
||||
if (isTeam) {
|
||||
restrictions.teams.add(restriction);
|
||||
} else {
|
||||
restrictions.users.add(restriction);
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Object> getPrReviews() {
|
||||
if (prReviews == null) {
|
||||
prReviews = new HashMap<String, Object>();
|
||||
}
|
||||
return prReviews;
|
||||
}
|
||||
|
||||
private Restrictions getRestrictions() {
|
||||
if (restrictions == null) {
|
||||
restrictions = new Restrictions();
|
||||
}
|
||||
return restrictions;
|
||||
}
|
||||
|
||||
private StatusChecks getStatusChecks() {
|
||||
if (statusChecks == null) {
|
||||
statusChecks = new StatusChecks();
|
||||
}
|
||||
return statusChecks;
|
||||
}
|
||||
|
||||
private Requester requester() {
|
||||
return new Requester(branch.getRoot()).withPreview(LOKI);
|
||||
}
|
||||
|
||||
private static class Restrictions {
|
||||
private Set<String> teams = new HashSet<String>();
|
||||
private Set<String> users = new HashSet<String>();
|
||||
}
|
||||
|
||||
private static class StatusChecks {
|
||||
final List<String> contexts = new ArrayList<String>();
|
||||
boolean strict;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,14 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.AbstractList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -16,6 +18,8 @@ import java.util.List;
|
||||
* @see GHRepository#getCommit(String)
|
||||
* @see GHCommitComment#getCommit()
|
||||
*/
|
||||
@SuppressFBWarnings(value = {"NP_UNWRITTEN_FIELD", "UWF_UNWRITTEN_FIELD"},
|
||||
justification = "JSON API")
|
||||
public class GHCommit {
|
||||
private GHRepository owner;
|
||||
|
||||
@@ -24,6 +28,8 @@ public class GHCommit {
|
||||
/**
|
||||
* Short summary of this commit.
|
||||
*/
|
||||
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
|
||||
"NP_UNWRITTEN_FIELD", "UWF_UNWRITTEN_FIELD"}, justification = "JSON API")
|
||||
public static class ShortInfo {
|
||||
private GHAuthor author;
|
||||
private GHAuthor committer;
|
||||
@@ -32,16 +38,30 @@ public class GHCommit {
|
||||
|
||||
private int comment_count;
|
||||
|
||||
static class Tree {
|
||||
String sha;
|
||||
}
|
||||
|
||||
private Tree tree;
|
||||
|
||||
@WithBridgeMethods(value = GHAuthor.class, castRequired = true)
|
||||
public GitUser getAuthor() {
|
||||
return author;
|
||||
}
|
||||
|
||||
public Date getAuthoredDate() {
|
||||
return GitHub.parseDate(author.date);
|
||||
}
|
||||
|
||||
@WithBridgeMethods(value = GHAuthor.class, castRequired = true)
|
||||
public GitUser getCommitter() {
|
||||
return committer;
|
||||
}
|
||||
|
||||
public Date getCommitDate() {
|
||||
return GitHub.parseDate(committer.date);
|
||||
}
|
||||
|
||||
/**
|
||||
* Commit message.
|
||||
*/
|
||||
@@ -58,6 +78,7 @@ public class GHCommit {
|
||||
* @deprecated Use {@link GitUser} instead.
|
||||
*/
|
||||
public static class GHAuthor extends GitUser {
|
||||
private String date;
|
||||
}
|
||||
|
||||
public static class Stats {
|
||||
@@ -67,6 +88,8 @@ public class GHCommit {
|
||||
/**
|
||||
* A file that was modified.
|
||||
*/
|
||||
@SuppressFBWarnings(value = "UWF_UNWRITTEN_FIELD",
|
||||
justification = "It's being initilized by JSON deserialization")
|
||||
public static class File {
|
||||
String status;
|
||||
int changes,additions,deletions;
|
||||
@@ -95,7 +118,7 @@ public class GHCommit {
|
||||
}
|
||||
|
||||
/**
|
||||
* "modified", "added", or "deleted"
|
||||
* "modified", "added", or "removed"
|
||||
*/
|
||||
public String getStatus() {
|
||||
return status;
|
||||
@@ -104,6 +127,8 @@ public class GHCommit {
|
||||
/**
|
||||
* Full path in the repository.
|
||||
*/
|
||||
@SuppressFBWarnings(value = "NM_CONFUSING",
|
||||
justification = "It's a part of the library's API and cannot be renamed")
|
||||
public String getFileName() {
|
||||
return filename;
|
||||
}
|
||||
@@ -147,23 +172,31 @@ public class GHCommit {
|
||||
}
|
||||
|
||||
public static class Parent {
|
||||
String url,sha;
|
||||
@SuppressFBWarnings(value = "UUF_UNUSED_FIELD", justification = "We don't provide it in API now")
|
||||
String url;
|
||||
String sha;
|
||||
}
|
||||
|
||||
static class User {
|
||||
// TODO: what if someone who doesn't have an account on GitHub makes a commit?
|
||||
String url,avatar_url,login,gravatar_id;
|
||||
@SuppressFBWarnings(value = "UUF_UNUSED_FIELD", justification = "We don't provide it in API now")
|
||||
String url,avatar_url,gravatar_id;
|
||||
@SuppressFBWarnings(value = "UUF_UNUSED_FIELD", justification = "We don't provide it in API now")
|
||||
int id;
|
||||
|
||||
String login;
|
||||
}
|
||||
|
||||
String url,sha;
|
||||
String url,html_url,sha;
|
||||
List<File> files;
|
||||
Stats stats;
|
||||
List<Parent> parents;
|
||||
User author,committer;
|
||||
|
||||
|
||||
public ShortInfo getCommitShortInfo() {
|
||||
public ShortInfo getCommitShortInfo() throws IOException {
|
||||
if (commit==null)
|
||||
populate();
|
||||
return commit;
|
||||
}
|
||||
|
||||
@@ -177,24 +210,41 @@ public class GHCommit {
|
||||
/**
|
||||
* Number of lines added + removed.
|
||||
*/
|
||||
public int getLinesChanged() {
|
||||
public int getLinesChanged() throws IOException {
|
||||
populate();
|
||||
return stats.total;
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of lines added.
|
||||
*/
|
||||
public int getLinesAdded() {
|
||||
public int getLinesAdded() throws IOException {
|
||||
populate();
|
||||
return stats.additions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of lines removed.
|
||||
*/
|
||||
public int getLinesDeleted() {
|
||||
public int getLinesDeleted() throws IOException {
|
||||
populate();
|
||||
return stats.deletions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this method to walk the tree
|
||||
*/
|
||||
public GHTree getTree() throws IOException {
|
||||
return owner.getTree(getCommitShortInfo().tree.sha);
|
||||
}
|
||||
|
||||
/**
|
||||
* URL of this commit like "https://github.com/kohsuke/sandbox-ant/commit/8ae38db0ea5837313ab5f39d43a6f73de3bd9000"
|
||||
*/
|
||||
public URL getHtmlUrl() {
|
||||
return GitHub.parseURL(html_url);
|
||||
}
|
||||
|
||||
/**
|
||||
* [0-9a-f]{40} SHA1 checksum.
|
||||
*/
|
||||
@@ -208,7 +258,8 @@ public class GHCommit {
|
||||
* @return
|
||||
* Can be empty but never null.
|
||||
*/
|
||||
public List<File> getFiles() {
|
||||
public List<File> getFiles() throws IOException {
|
||||
populate();
|
||||
return files!=null ? Collections.unmodifiableList(files) : Collections.<File>emptyList();
|
||||
}
|
||||
|
||||
@@ -244,10 +295,29 @@ public class GHCommit {
|
||||
return resolveUser(author);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the date the change was authored on.
|
||||
* @return the date the change was authored on.
|
||||
* @throws IOException if the information was not already fetched and an attempt at fetching the information failed.
|
||||
*/
|
||||
public Date getAuthoredDate() throws IOException {
|
||||
return getCommitShortInfo().getAuthoredDate();
|
||||
}
|
||||
|
||||
public GHUser getCommitter() throws IOException {
|
||||
return resolveUser(committer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the date the change was committed on.
|
||||
*
|
||||
* @return the date the change was committed on.
|
||||
* @throws IOException if the information was not already fetched and an attempt at fetching the information failed.
|
||||
*/
|
||||
public Date getCommitDate() throws IOException {
|
||||
return getCommitShortInfo().getCommitDate();
|
||||
}
|
||||
|
||||
private GHUser resolveUser(User author) throws IOException {
|
||||
if (author==null || author.login==null) return null;
|
||||
return owner.root.getUser(author.login);
|
||||
@@ -258,8 +328,8 @@ public class GHCommit {
|
||||
*/
|
||||
public PagedIterable<GHCommitComment> listComments() {
|
||||
return new PagedIterable<GHCommitComment>() {
|
||||
public PagedIterator<GHCommitComment> iterator() {
|
||||
return new PagedIterator<GHCommitComment>(owner.root.retrieve().asIterator(String.format("/repos/%s/%s/commits/%s/comments", owner.getOwnerName(), owner.getName(), sha), GHCommitComment[].class)) {
|
||||
public PagedIterator<GHCommitComment> _iterator(int pageSize) {
|
||||
return new PagedIterator<GHCommitComment>(owner.root.retrieve().asIterator(String.format("/repos/%s/%s/commits/%s/comments", owner.getOwnerName(), owner.getName(), sha), GHCommitComment[].class, pageSize)) {
|
||||
@Override
|
||||
protected void wrapUp(GHCommitComment[] page) {
|
||||
for (GHCommitComment c : page)
|
||||
@@ -286,7 +356,7 @@ public class GHCommit {
|
||||
}
|
||||
|
||||
public GHCommitComment createComment(String body) throws IOException {
|
||||
return createComment(body,null,null,null);
|
||||
return createComment(body, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -303,6 +373,14 @@ public class GHCommit {
|
||||
return owner.getLastCommitStatus(sha);
|
||||
}
|
||||
|
||||
/**
|
||||
* Some of the fields are not always filled in when this object is retrieved as a part of another API call.
|
||||
*/
|
||||
void populate() throws IOException {
|
||||
if (files==null && stats==null)
|
||||
owner.root.retrieve().to(owner.getApiTailUrl("commits/" + sha), this);
|
||||
}
|
||||
|
||||
GHCommit wrapUp(GHRepository owner) {
|
||||
this.owner = owner;
|
||||
return this;
|
||||
|
||||
92
src/main/java/org/kohsuke/github/GHCommitBuilder.java
Normal file
92
src/main/java/org/kohsuke/github/GHCommitBuilder.java
Normal file
@@ -0,0 +1,92 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* Builder pattern for creating a new commit.
|
||||
* Based on https://developer.github.com/v3/git/commits/#create-a-commit
|
||||
*/
|
||||
public class GHCommitBuilder {
|
||||
private final GHRepository repo;
|
||||
private final Requester req;
|
||||
|
||||
private final List<String> parents = new ArrayList<String>();
|
||||
|
||||
private static final class UserInfo {
|
||||
private final String name;
|
||||
private final String email;
|
||||
private final String date;
|
||||
|
||||
private UserInfo(String name, String email, Date date) {
|
||||
this.name = name;
|
||||
this.email = email;
|
||||
TimeZone tz = TimeZone.getTimeZone("UTC");
|
||||
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
|
||||
df.setTimeZone(tz);
|
||||
this.date = df.format((date != null) ? date : new Date());
|
||||
}
|
||||
}
|
||||
|
||||
GHCommitBuilder(GHRepository repo) {
|
||||
this.repo = repo;
|
||||
req = new Requester(repo.root);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param message the commit message
|
||||
*/
|
||||
public GHCommitBuilder message(String message) {
|
||||
req.with("message", message);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param tree the SHA of the tree object this commit points to
|
||||
*/
|
||||
public GHCommitBuilder tree(String tree) {
|
||||
req.with("tree", tree);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param parent the SHA of a parent commit.
|
||||
*/
|
||||
public GHCommitBuilder parent(String parent) {
|
||||
parents.add(parent);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the author of this commit.
|
||||
*/
|
||||
public GHCommitBuilder author(String name, String email, Date date) {
|
||||
req._with("author", new UserInfo(name, email, date));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the committer of this commit.
|
||||
*/
|
||||
public GHCommitBuilder committer(String name, String email, Date date) {
|
||||
req._with("committer", new UserInfo(name, email, date));
|
||||
return this;
|
||||
}
|
||||
|
||||
private String getApiTail() {
|
||||
return String.format("/repos/%s/%s/git/commits", repo.getOwnerName(), repo.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a blob based on the parameters specified thus far.
|
||||
*/
|
||||
public GHCommit create() throws IOException {
|
||||
req._with("parents", parents);
|
||||
return req.method("POST").to(getApiTail(), GHCommit.class).wrapUp(repo);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.Date;
|
||||
|
||||
import static org.kohsuke.github.Previews.*;
|
||||
|
||||
/**
|
||||
* A comment attached to a commit (or a specific line in a specific file of a commit.)
|
||||
@@ -12,19 +15,15 @@ import java.util.Date;
|
||||
* @see GHCommit#listComments()
|
||||
* @see GHCommit#createComment(String, String, Integer, Integer)
|
||||
*/
|
||||
public class GHCommitComment extends GHObject {
|
||||
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
|
||||
"NP_UNWRITTEN_FIELD"}, justification = "JSON API")
|
||||
public class GHCommitComment extends GHObject implements Reactable {
|
||||
private GHRepository owner;
|
||||
|
||||
String body, html_url, commit_id;
|
||||
Integer line;
|
||||
String path;
|
||||
User user;
|
||||
|
||||
static class User {
|
||||
// TODO: what if someone who doesn't have an account on GitHub makes a commit?
|
||||
String url,avatar_url,login,gravatar_id;
|
||||
int id;
|
||||
}
|
||||
GHUser user; // not fully populated. beware.
|
||||
|
||||
public GHRepository getOwner() {
|
||||
return owner;
|
||||
@@ -69,7 +68,7 @@ public class GHCommitComment extends GHObject {
|
||||
* Gets the user who put this comment.
|
||||
*/
|
||||
public GHUser getUser() throws IOException {
|
||||
return owner.root.getUser(user.login);
|
||||
return owner == null || owner.root.isOffline() ? user : owner.root.getUser(user.login);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,12 +82,35 @@ public class GHCommitComment extends GHObject {
|
||||
* Updates the body of the commit message.
|
||||
*/
|
||||
public void update(String body) throws IOException {
|
||||
GHCommitComment r = new Requester(owner.root)
|
||||
new Requester(owner.root)
|
||||
.with("body", body)
|
||||
.method("PATCH").to(getApiTail(), GHCommitComment.class);
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
@Preview @Deprecated
|
||||
public GHReaction createReaction(ReactionContent content) throws IOException {
|
||||
return new Requester(owner.root)
|
||||
.withPreview(SQUIRREL_GIRL)
|
||||
.with("content", content.getContent())
|
||||
.to(getApiTail()+"/reactions", GHReaction.class).wrap(owner.root);
|
||||
}
|
||||
|
||||
@Preview @Deprecated
|
||||
public PagedIterable<GHReaction> listReactions() {
|
||||
return new PagedIterable<GHReaction>() {
|
||||
public PagedIterator<GHReaction> _iterator(int pageSize) {
|
||||
return new PagedIterator<GHReaction>(owner.root.retrieve().withPreview(SQUIRREL_GIRL).asIterator(getApiTail()+"/reactions", GHReaction[].class, pageSize)) {
|
||||
@Override
|
||||
protected void wrapUp(GHReaction[] page) {
|
||||
for (GHReaction c : page)
|
||||
c.wrap(owner.root);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes this comment.
|
||||
*/
|
||||
@@ -103,6 +125,9 @@ public class GHCommitComment extends GHObject {
|
||||
|
||||
GHCommitComment wrap(GHRepository owner) {
|
||||
this.owner = owner;
|
||||
if (owner.root.isOffline()) {
|
||||
user.wrapUp(owner.root);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,8 @@ public class GHCommitPointer {
|
||||
* This points to the user who owns
|
||||
* the {@link #getRepository()}.
|
||||
*/
|
||||
public GHUser getUser() {
|
||||
public GHUser getUser() throws IOException {
|
||||
if (user != null) return user.root.intern(user);
|
||||
return user;
|
||||
}
|
||||
|
||||
|
||||
@@ -92,8 +92,8 @@ public class GHCommitQueryBuilder {
|
||||
*/
|
||||
public PagedIterable<GHCommit> list() {
|
||||
return new PagedIterable<GHCommit>() {
|
||||
public PagedIterator<GHCommit> iterator() {
|
||||
return new PagedIterator<GHCommit>(req.asIterator(repo.getApiTailUrl("commits"), GHCommit[].class)) {
|
||||
public PagedIterator<GHCommit> _iterator(int pageSize) {
|
||||
return new PagedIterator<GHCommit>(req.asIterator(repo.getApiTailUrl("commits"), GHCommit[].class, pageSize)) {
|
||||
protected void wrapUp(GHCommit[] page) {
|
||||
for (GHCommit c : page)
|
||||
c.wrapUp(repo);
|
||||
|
||||
137
src/main/java/org/kohsuke/github/GHCommitSearchBuilder.java
Normal file
137
src/main/java/org/kohsuke/github/GHCommitSearchBuilder.java
Normal file
@@ -0,0 +1,137 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Search commits.
|
||||
*
|
||||
* @author Marc de Verdelhan
|
||||
* @see GitHub#searchCommits()
|
||||
*/
|
||||
@Preview @Deprecated
|
||||
public class GHCommitSearchBuilder extends GHSearchBuilder<GHCommit> {
|
||||
/*package*/ GHCommitSearchBuilder(GitHub root) {
|
||||
super(root,CommitSearchResult.class);
|
||||
req.withPreview(Previews.CLOAK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search terms.
|
||||
*/
|
||||
public GHCommitSearchBuilder q(String term) {
|
||||
super.q(term);
|
||||
return this;
|
||||
}
|
||||
|
||||
public GHCommitSearchBuilder author(String v) {
|
||||
return q("author:"+v);
|
||||
}
|
||||
|
||||
public GHCommitSearchBuilder committer(String v) {
|
||||
return q("committer:"+v);
|
||||
}
|
||||
|
||||
public GHCommitSearchBuilder authorName(String v) {
|
||||
return q("author-name:"+v);
|
||||
}
|
||||
|
||||
public GHCommitSearchBuilder committerName(String v) {
|
||||
return q("committer-name:"+v);
|
||||
}
|
||||
|
||||
public GHCommitSearchBuilder authorEmail(String v) {
|
||||
return q("author-email:"+v);
|
||||
}
|
||||
|
||||
public GHCommitSearchBuilder committerEmail(String v) {
|
||||
return q("committer-email:"+v);
|
||||
}
|
||||
|
||||
public GHCommitSearchBuilder authorDate(String v) {
|
||||
return q("author-date:"+v);
|
||||
}
|
||||
|
||||
public GHCommitSearchBuilder committerDate(String v) {
|
||||
return q("committer-date:"+v);
|
||||
}
|
||||
|
||||
public GHCommitSearchBuilder merge(boolean merge) {
|
||||
return q("merge:"+Boolean.valueOf(merge).toString().toLowerCase());
|
||||
}
|
||||
|
||||
public GHCommitSearchBuilder hash(String v) {
|
||||
return q("hash:"+v);
|
||||
}
|
||||
|
||||
public GHCommitSearchBuilder parent(String v) {
|
||||
return q("parent:"+v);
|
||||
}
|
||||
|
||||
public GHCommitSearchBuilder tree(String v) {
|
||||
return q("tree:"+v);
|
||||
}
|
||||
|
||||
public GHCommitSearchBuilder is(String v) {
|
||||
return q("is:"+v);
|
||||
}
|
||||
|
||||
public GHCommitSearchBuilder user(String v) {
|
||||
return q("user:"+v);
|
||||
}
|
||||
|
||||
public GHCommitSearchBuilder org(String v) {
|
||||
return q("org:"+v);
|
||||
}
|
||||
|
||||
public GHCommitSearchBuilder repo(String v) {
|
||||
return q("repo:"+v);
|
||||
}
|
||||
|
||||
public GHCommitSearchBuilder order(GHDirection v) {
|
||||
req.with("order",v);
|
||||
return this;
|
||||
}
|
||||
|
||||
public GHCommitSearchBuilder sort(Sort sort) {
|
||||
req.with("sort",sort);
|
||||
return this;
|
||||
}
|
||||
|
||||
public enum Sort { AUTHOR_DATE, COMMITTER_DATE }
|
||||
|
||||
private static class CommitSearchResult extends SearchResult<GHCommit> {
|
||||
private GHCommit[] items;
|
||||
|
||||
@Override
|
||||
/*package*/ GHCommit[] getItems(GitHub root) {
|
||||
for (GHCommit commit : items) {
|
||||
String repoName = getRepoName(commit.url);
|
||||
try {
|
||||
GHRepository repo = root.getRepository(repoName);
|
||||
commit.wrapUp(repo);
|
||||
} catch (IOException ioe) {}
|
||||
}
|
||||
return items;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param commitUrl a commit URL
|
||||
* @return the repo name ("username/reponame")
|
||||
*/
|
||||
private static String getRepoName(String commitUrl) {
|
||||
if (StringUtils.isBlank(commitUrl)) {
|
||||
return null;
|
||||
}
|
||||
int indexOfUsername = (GitHub.GITHUB_URL + "/repos/").length();
|
||||
String[] tokens = commitUrl.substring(indexOfUsername).split("/", 3);
|
||||
return tokens[0] + '/' + tokens[1];
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getApiUrl() {
|
||||
return "/search/commits";
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package org.kohsuke.github;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Represents a status of a commit.
|
||||
@@ -10,6 +9,7 @@ import java.util.Date;
|
||||
* @author Kohsuke Kawaguchi
|
||||
* @see GHRepository#getLastCommitStatus(String)
|
||||
* @see GHCommit#getLastStatus()
|
||||
* @see GHRepository#createCommitStatus(String, GHCommitState, String, String)
|
||||
*/
|
||||
public class GHCommitStatus extends GHObject {
|
||||
String state;
|
||||
@@ -46,8 +46,8 @@ public class GHCommitStatus extends GHObject {
|
||||
return description;
|
||||
}
|
||||
|
||||
public GHUser getCreator() {
|
||||
return creator;
|
||||
public GHUser getCreator() throws IOException {
|
||||
return root.intern(creator);
|
||||
}
|
||||
|
||||
public String getContext() {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* The model user for comparing 2 commits in the GitHub API.
|
||||
@@ -65,12 +65,24 @@ public class GHCompare {
|
||||
return merge_base_commit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an array of commits.
|
||||
* @return A copy of the array being stored in the class.
|
||||
*/
|
||||
public Commit[] getCommits() {
|
||||
return commits;
|
||||
Commit[] newValue = new Commit[commits.length];
|
||||
System.arraycopy(commits, 0, newValue, 0, commits.length);
|
||||
return newValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an array of commits.
|
||||
* @return A copy of the array being stored in the class.
|
||||
*/
|
||||
public GHCommit.File[] getFiles() {
|
||||
return files;
|
||||
GHCommit.File[] newValue = new GHCommit.File[files.length];
|
||||
System.arraycopy(files, 0, newValue, 0, files.length);
|
||||
return newValue;
|
||||
}
|
||||
|
||||
public GHCompare wrap(GHRepository owner) {
|
||||
@@ -87,6 +99,8 @@ public class GHCompare {
|
||||
* Compare commits had a child commit element with additional details we want to capture.
|
||||
* This extenstion of GHCommit provides that.
|
||||
*/
|
||||
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD"},
|
||||
justification = "JSON API")
|
||||
public static class Commit extends GHCommit {
|
||||
|
||||
private InnerCommit commit;
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
|
||||
/**
|
||||
* A Content of a repository.
|
||||
*
|
||||
@@ -75,8 +74,9 @@ public class GHContent {
|
||||
* @deprecated
|
||||
* Use {@link #read()}
|
||||
*/
|
||||
@SuppressFBWarnings("DM_DEFAULT_ENCODING")
|
||||
public String getContent() throws IOException {
|
||||
return new String(DatatypeConverter.parseBase64Binary(getEncodedContent()));
|
||||
return new String(Base64.decodeBase64(getEncodedContent()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -113,7 +113,8 @@ public class GHContent {
|
||||
* Retrieves the actual content stored here.
|
||||
*/
|
||||
public InputStream read() throws IOException {
|
||||
return new Requester(root).asStream(getDownloadUrl());
|
||||
// if the download link is encoded with a token on the query string, the default behavior of POST will fail
|
||||
return new Requester(root).method("GET").asStream(getDownloadUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -150,8 +151,8 @@ public class GHContent {
|
||||
throw new IllegalStateException(path+" is not a directory");
|
||||
|
||||
return new PagedIterable<GHContent>() {
|
||||
public PagedIterator<GHContent> iterator() {
|
||||
return new PagedIterator<GHContent>(root.retrieve().asIterator(url, GHContent[].class)) {
|
||||
public PagedIterator<GHContent> _iterator(int pageSize) {
|
||||
return new PagedIterator<GHContent>(root.retrieve().asIterator(url, GHContent[].class, pageSize)) {
|
||||
@Override
|
||||
protected void wrapUp(GHContent[] page) {
|
||||
GHContent.wrap(page, repository);
|
||||
@@ -161,10 +162,12 @@ public class GHContent {
|
||||
};
|
||||
}
|
||||
|
||||
@SuppressFBWarnings("DM_DEFAULT_ENCODING")
|
||||
public GHContentUpdateResponse update(String newContent, String commitMessage) throws IOException {
|
||||
return update(newContent.getBytes(), commitMessage, null);
|
||||
}
|
||||
|
||||
@SuppressFBWarnings("DM_DEFAULT_ENCODING")
|
||||
public GHContentUpdateResponse update(String newContent, String commitMessage, String branch) throws IOException {
|
||||
return update(newContent.getBytes(), commitMessage, branch);
|
||||
}
|
||||
@@ -174,7 +177,7 @@ public class GHContent {
|
||||
}
|
||||
|
||||
public GHContentUpdateResponse update(byte[] newContentBytes, String commitMessage, String branch) throws IOException {
|
||||
String encodedContent = DatatypeConverter.printBase64Binary(newContentBytes);
|
||||
String encodedContent = Base64.encodeBase64String(newContentBytes);
|
||||
|
||||
Requester requester = new Requester(root)
|
||||
.with("path", path)
|
||||
|
||||
21
src/main/java/org/kohsuke/github/GHContentWithLicense.java
Normal file
21
src/main/java/org/kohsuke/github/GHContentWithLicense.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
/**
|
||||
* {@link GHContent} with license information.
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
* @see <a href="https://developer.github.com/v3/licenses/#get-a-repositorys-license">documentation</a>
|
||||
* @see GHRepository#getLicense()
|
||||
*/
|
||||
@Preview @Deprecated
|
||||
class GHContentWithLicense extends GHContent {
|
||||
GHLicense license;
|
||||
|
||||
@Override
|
||||
GHContentWithLicense wrap(GHRepository owner) {
|
||||
super.wrap(owner);
|
||||
if (license!=null)
|
||||
license.wrap(owner.root);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
138
src/main/java/org/kohsuke/github/GHCreateRepositoryBuilder.java
Normal file
138
src/main/java/org/kohsuke/github/GHCreateRepositoryBuilder.java
Normal file
@@ -0,0 +1,138 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* Creates a repository
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
public class GHCreateRepositoryBuilder {
|
||||
private final GitHub root;
|
||||
protected final Requester builder;
|
||||
private final String apiUrlTail;
|
||||
|
||||
/*package*/ GHCreateRepositoryBuilder(GitHub root, String apiUrlTail, String name) {
|
||||
this.root = root;
|
||||
this.apiUrlTail = apiUrlTail;
|
||||
this.builder = new Requester(root);
|
||||
this.builder.with("name",name);
|
||||
}
|
||||
|
||||
public GHCreateRepositoryBuilder description(String description) {
|
||||
this.builder.with("description",description);
|
||||
return this;
|
||||
}
|
||||
|
||||
public GHCreateRepositoryBuilder homepage(URL homepage) {
|
||||
return homepage(homepage.toExternalForm());
|
||||
}
|
||||
|
||||
public GHCreateRepositoryBuilder homepage(String homepage) {
|
||||
this.builder.with("homepage",homepage);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a private repository
|
||||
*/
|
||||
public GHCreateRepositoryBuilder private_(boolean b) {
|
||||
this.builder.with("private",b);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables issue tracker
|
||||
*/
|
||||
public GHCreateRepositoryBuilder issues(boolean b) {
|
||||
this.builder.with("has_issues",b);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables wiki
|
||||
*/
|
||||
public GHCreateRepositoryBuilder wiki(boolean b) {
|
||||
this.builder.with("has_wiki",b);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables downloads
|
||||
*/
|
||||
public GHCreateRepositoryBuilder downloads(boolean b) {
|
||||
this.builder.with("has_downloads",b);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true, create an initial commit with empty README.
|
||||
*/
|
||||
public GHCreateRepositoryBuilder autoInit(boolean b) {
|
||||
this.builder.with("auto_init",b);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow or disallow squash-merging pull requests.
|
||||
*/
|
||||
public GHCreateRepositoryBuilder allowSquashMerge(boolean b) {
|
||||
this.builder.with("allow_squash_merge",b);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow or disallow merging pull requests with a merge commit.
|
||||
*/
|
||||
public GHCreateRepositoryBuilder allowMergeCommit(boolean b) {
|
||||
this.builder.with("allow_merge_commit",b);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow or disallow rebase-merging pull requests.
|
||||
*/
|
||||
public GHCreateRepositoryBuilder allowRebaseMerge(boolean b) {
|
||||
this.builder.with("allow_rebase_merge",b);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a default .gitignore
|
||||
*
|
||||
* See https://developer.github.com/v3/repos/#create
|
||||
*/
|
||||
public GHCreateRepositoryBuilder gitignoreTemplate(String language) {
|
||||
this.builder.with("gitignore_template",language);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Desired license template to apply
|
||||
*
|
||||
* See https://developer.github.com/v3/repos/#create
|
||||
*/
|
||||
public GHCreateRepositoryBuilder licenseTemplate(String license) {
|
||||
this.builder.with("license_template",license);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The team that gets granted access to this repository. Only valid for creating a repository in
|
||||
* an organization.
|
||||
*/
|
||||
public GHCreateRepositoryBuilder team(GHTeam team) {
|
||||
if (team!=null)
|
||||
this.builder.with("team_id",team.getId());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a repository with all the parameters.
|
||||
*/
|
||||
public GHRepository create() throws IOException {
|
||||
return builder.method("POST").to(apiUrlTail, GHRepository.class).wrap(root);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.commons.lang.builder.ToStringBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class GHDeployKey {
|
||||
|
||||
protected String url, key, title;
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* Represents a deployment
|
||||
*
|
||||
* @see <a href="https://developer.github.com/v3/repos/deployments/">documentation</a>
|
||||
* @see GHRepository#listDeployments(String, String, String, String)
|
||||
* @see GHRepository#getDeployment(long)
|
||||
*/
|
||||
public class GHDeployment extends GHObject {
|
||||
private GHRepository owner;
|
||||
private GitHub root;
|
||||
@@ -41,8 +48,8 @@ public class GHDeployment extends GHObject {
|
||||
public String getEnvironment() {
|
||||
return environment;
|
||||
}
|
||||
public GHUser getCreator() {
|
||||
return creator;
|
||||
public GHUser getCreator() throws IOException {
|
||||
return root.intern(creator);
|
||||
}
|
||||
public String getRef() {
|
||||
return ref;
|
||||
@@ -58,4 +65,23 @@ public class GHDeployment extends GHObject {
|
||||
public URL getHtmlUrl() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public GHDeploymentStatusBuilder createStatus(GHDeploymentState state) {
|
||||
return new GHDeploymentStatusBuilder(owner,id,state);
|
||||
}
|
||||
|
||||
public PagedIterable<GHDeploymentStatus> listStatuses() {
|
||||
return new PagedIterable<GHDeploymentStatus>() {
|
||||
public PagedIterator<GHDeploymentStatus> _iterator(int pageSize) {
|
||||
return new PagedIterator<GHDeploymentStatus>(root.retrieve().asIterator(statuses_url, GHDeploymentStatus[].class, pageSize)) {
|
||||
@Override
|
||||
protected void wrapUp(GHDeploymentStatus[] page) {
|
||||
for (GHDeploymentStatus c : page)
|
||||
c.wrap(owner);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.Locale;
|
||||
|
||||
public class GHDeploymentStatus extends GHObject {
|
||||
private GHRepository owner;
|
||||
@@ -28,8 +29,9 @@ public class GHDeploymentStatus extends GHObject {
|
||||
public URL getRepositoryUrl() {
|
||||
return GitHub.parseURL(repository_url);
|
||||
}
|
||||
|
||||
public GHDeploymentState getState() {
|
||||
return GHDeploymentState.valueOf(state.toUpperCase());
|
||||
return GHDeploymentState.valueOf(state.toUpperCase(Locale.ENGLISH));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,16 +2,30 @@ package org.kohsuke.github;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Creates a new deployment status.
|
||||
*
|
||||
* @see
|
||||
* GHDeployment#createStatus(GHDeploymentState)
|
||||
*/
|
||||
public class GHDeploymentStatusBuilder {
|
||||
private final Requester builder;
|
||||
private GHRepository repo;
|
||||
private int deploymentId;
|
||||
private long deploymentId;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* Use {@link GHDeployment#createStatus(GHDeploymentState)}
|
||||
*/
|
||||
public GHDeploymentStatusBuilder(GHRepository repo, int deploymentId, GHDeploymentState state) {
|
||||
this(repo,(long)deploymentId,state);
|
||||
}
|
||||
|
||||
/*package*/ GHDeploymentStatusBuilder(GHRepository repo, long deploymentId, GHDeploymentState state) {
|
||||
this.repo = repo;
|
||||
this.deploymentId = deploymentId;
|
||||
this.builder = new Requester(repo.root);
|
||||
this.builder.with("state",state.toString().toLowerCase());
|
||||
this.builder.with("state",state);
|
||||
}
|
||||
|
||||
public GHDeploymentStatusBuilder description(String description) {
|
||||
@@ -25,6 +39,6 @@ public class GHDeploymentStatusBuilder {
|
||||
}
|
||||
|
||||
public GHDeploymentStatus create() throws IOException {
|
||||
return builder.to(repo.getApiTailUrl("deployments")+"/"+deploymentId+"/statuses",GHDeploymentStatus.class).wrap(repo);
|
||||
return builder.to(repo.getApiTailUrl("deployments/"+deploymentId+"/statuses"),GHDeploymentStatus.class).wrap(repo);
|
||||
}
|
||||
}
|
||||
|
||||
10
src/main/java/org/kohsuke/github/GHDirection.java
Normal file
10
src/main/java/org/kohsuke/github/GHDirection.java
Normal file
@@ -0,0 +1,10 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
/**
|
||||
* Sort direction
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
public enum GHDirection {
|
||||
ASC, DESC
|
||||
}
|
||||
@@ -23,12 +23,16 @@
|
||||
*/
|
||||
package org.kohsuke.github;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
|
||||
|
||||
/**
|
||||
* Represents an email of GitHub.
|
||||
*
|
||||
* @author Kelly Campbell
|
||||
*/
|
||||
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
|
||||
"NP_UNWRITTEN_FIELD", "NP_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD"}, justification = "JSON API")
|
||||
public class GHEmail {
|
||||
|
||||
protected String email;
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Hook event type.
|
||||
*
|
||||
* See http://developer.github.com/v3/events/types/
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
* @see GHEventInfo
|
||||
* @see <a href="https://developer.github.com/v3/activity/events/types/">Event type reference</a>
|
||||
*/
|
||||
public enum GHEvent {
|
||||
COMMIT_COMMENT,
|
||||
@@ -20,18 +21,44 @@ public enum GHEvent {
|
||||
FORK_APPLY,
|
||||
GIST,
|
||||
GOLLUM,
|
||||
INSTALLATION,
|
||||
INSTALLATION_REPOSITORIES,
|
||||
ISSUE_COMMENT,
|
||||
ISSUES,
|
||||
LABEL,
|
||||
MARKETPLACE_PURCHASE,
|
||||
MEMBER,
|
||||
MEMBERSHIP,
|
||||
MILESTONE,
|
||||
ORGANIZATION,
|
||||
ORG_BLOCK,
|
||||
PAGE_BUILD,
|
||||
PROJECT_CARD,
|
||||
PROJECT_COLUMN,
|
||||
PROJECT,
|
||||
PUBLIC,
|
||||
PULL_REQUEST,
|
||||
PULL_REQUEST_REVIEW,
|
||||
PULL_REQUEST_REVIEW_COMMENT,
|
||||
PUSH,
|
||||
RELEASE,
|
||||
REPOSITORY, // only valid for org hooks
|
||||
STATUS,
|
||||
TEAM,
|
||||
TEAM_ADD,
|
||||
WATCH,
|
||||
PING
|
||||
PING,
|
||||
/**
|
||||
* Special event type that means "every possible event"
|
||||
*/
|
||||
ALL;
|
||||
|
||||
|
||||
/**
|
||||
* Returns GitHub's internal representation of this event.
|
||||
*/
|
||||
String symbol() {
|
||||
if (this==ALL) return "*";
|
||||
return name().toLowerCase(Locale.ENGLISH);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
|
||||
/**
|
||||
* Represents an event.
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
@SuppressFBWarnings(value = "UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", justification = "JSON API")
|
||||
public class GHEventInfo {
|
||||
private GitHub root;
|
||||
|
||||
// we don't want to expose Jackson dependency to the user. This needs databinding
|
||||
private ObjectNode payload;
|
||||
|
||||
private long id;
|
||||
private String created_at;
|
||||
private String type;
|
||||
|
||||
@@ -27,8 +30,12 @@ public class GHEventInfo {
|
||||
/**
|
||||
* Inside the event JSON model, GitHub uses a slightly different format.
|
||||
*/
|
||||
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
|
||||
"UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR" }, justification = "JSON API")
|
||||
public static class GHEventRepository {
|
||||
@SuppressFBWarnings(value = "UUF_UNUSED_FIELD", justification = "We don't provide it in API now")
|
||||
private int id;
|
||||
@SuppressFBWarnings(value = "UUF_UNUSED_FIELD", justification = "We don't provide it in API now")
|
||||
private String url; // repository API URL
|
||||
private String name; // owner/repo
|
||||
}
|
||||
@@ -48,6 +55,10 @@ public class GHEventInfo {
|
||||
return this;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Date getCreatedAt() {
|
||||
return GitHub.parseDate(created_at);
|
||||
}
|
||||
@@ -55,10 +66,14 @@ public class GHEventInfo {
|
||||
/**
|
||||
* Repository where the change was made.
|
||||
*/
|
||||
@SuppressFBWarnings(value = {"UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR" },
|
||||
justification = "The field comes from JSON deserialization")
|
||||
public GHRepository getRepository() throws IOException {
|
||||
return root.getRepository(repo.name);
|
||||
}
|
||||
|
||||
@SuppressFBWarnings(value = {"UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR" },
|
||||
justification = "The field comes from JSON deserialization")
|
||||
public GHUser getActor() throws IOException {
|
||||
return root.getUser(actor.getLogin());
|
||||
}
|
||||
@@ -70,6 +85,8 @@ public class GHEventInfo {
|
||||
return actor.getLogin();
|
||||
}
|
||||
|
||||
@SuppressFBWarnings(value = {"UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR" },
|
||||
justification = "The field comes from JSON deserialization")
|
||||
public GHOrganization getOrganization() throws IOException {
|
||||
return (org==null || org.getLogin()==null) ? null : root.getOrganization(org.getLogin());
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonSetter;
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
|
||||
import java.io.Reader;
|
||||
import java.util.List;
|
||||
|
||||
@@ -13,11 +17,28 @@ import java.util.List;
|
||||
public abstract class GHEventPayload {
|
||||
protected GitHub root;
|
||||
|
||||
private GHUser sender;
|
||||
|
||||
/*package*/ GHEventPayload() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the sender or {@code null} if accessed via the events API.
|
||||
* @return the sender or {@code null} if accessed via the events API.
|
||||
*/
|
||||
public GHUser getSender() {
|
||||
return sender;
|
||||
}
|
||||
|
||||
public void setSender(GHUser sender) {
|
||||
this.sender = sender;
|
||||
}
|
||||
|
||||
/*package*/ void wrapUp(GitHub root) {
|
||||
this.root = root;
|
||||
if (sender != null) {
|
||||
sender.wrapUp(root);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -25,6 +46,8 @@ public abstract class GHEventPayload {
|
||||
*
|
||||
* @see <a href="http://developer.github.com/v3/activity/events/types/#pullrequestevent">authoritative source</a>
|
||||
*/
|
||||
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
|
||||
"NP_UNWRITTEN_FIELD"}, justification = "JSON API")
|
||||
public static class PullRequest extends GHEventPayload {
|
||||
private String action;
|
||||
private int number;
|
||||
@@ -55,7 +78,96 @@ public abstract class GHEventPayload {
|
||||
throw new IllegalStateException("Expected pull_request payload, but got something else. Maybe we've got another type of event?");
|
||||
if (repository!=null) {
|
||||
repository.wrap(root);
|
||||
pull_request.wrap(repository);
|
||||
pull_request.wrapUp(repository);
|
||||
} else {
|
||||
pull_request.wrapUp(root);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A review was added to a pull request
|
||||
*
|
||||
* @see <a href="https://developer.github.com/v3/activity/events/types/#pullrequestreviewevent">authoritative source</a>
|
||||
*/
|
||||
public static class PullRequestReview extends GHEventPayload {
|
||||
private String action;
|
||||
private GHPullRequestReview review;
|
||||
private GHPullRequest pull_request;
|
||||
private GHRepository repository;
|
||||
|
||||
public String getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
public GHPullRequestReview getReview() {
|
||||
return review;
|
||||
}
|
||||
|
||||
public GHPullRequest getPullRequest() {
|
||||
return pull_request;
|
||||
}
|
||||
|
||||
public GHRepository getRepository() {
|
||||
return repository;
|
||||
}
|
||||
|
||||
@Override
|
||||
void wrapUp(GitHub root) {
|
||||
super.wrapUp(root);
|
||||
if (review==null)
|
||||
throw new IllegalStateException("Expected pull_request_review payload, but got something else. Maybe we've got another type of event?");
|
||||
|
||||
review.wrapUp(pull_request);
|
||||
|
||||
if (repository!=null) {
|
||||
repository.wrap(root);
|
||||
pull_request.wrapUp(repository);
|
||||
} else {
|
||||
pull_request.wrapUp(root);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A review comment was added to a pull request
|
||||
*
|
||||
* @see <a href="https://developer.github.com/v3/activity/events/types/#pullrequestreviewcommentevent">authoritative source</a>
|
||||
*/
|
||||
public static class PullRequestReviewComment extends GHEventPayload {
|
||||
private String action;
|
||||
private GHPullRequestReviewComment comment;
|
||||
private GHPullRequest pull_request;
|
||||
private GHRepository repository;
|
||||
|
||||
public String getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
public GHPullRequestReviewComment getComment() {
|
||||
return comment;
|
||||
}
|
||||
|
||||
public GHPullRequest getPullRequest() {
|
||||
return pull_request;
|
||||
}
|
||||
|
||||
public GHRepository getRepository() {
|
||||
return repository;
|
||||
}
|
||||
|
||||
@Override
|
||||
void wrapUp(GitHub root) {
|
||||
super.wrapUp(root);
|
||||
if (comment==null)
|
||||
throw new IllegalStateException("Expected pull_request_review_comment payload, but got something else. Maybe we've got another type of event?");
|
||||
|
||||
comment.wrapUp(pull_request);
|
||||
|
||||
if (repository!=null) {
|
||||
repository.wrap(root);
|
||||
pull_request.wrapUp(repository);
|
||||
} else {
|
||||
pull_request.wrapUp(root);
|
||||
}
|
||||
@@ -67,12 +179,15 @@ public abstract class GHEventPayload {
|
||||
*
|
||||
* @see <a href="http://developer.github.com/v3/activity/events/types/#issuecommentevent">authoritative source</a>
|
||||
*/
|
||||
@SuppressFBWarnings(value = {"UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR", "NP_UNWRITTEN_FIELD" },
|
||||
justification = "Constructed by JSON deserialization")
|
||||
public static class IssueComment extends GHEventPayload {
|
||||
private String action;
|
||||
private GHIssueComment comment;
|
||||
private GHIssue issue;
|
||||
private GHRepository repository;
|
||||
|
||||
@SuppressFBWarnings(value = "UWF_UNWRITTEN_FIELD", justification = "Comes from JSON deserialization")
|
||||
public String getAction() {
|
||||
return action;
|
||||
}
|
||||
@@ -114,17 +229,340 @@ public abstract class GHEventPayload {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A comment was added to a commit
|
||||
*
|
||||
* @see <a href="http://developer.github.com/v3/activity/events/types/#commitcommentevent">authoritative source</a>
|
||||
*/
|
||||
@SuppressFBWarnings(value = {"UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR", "NP_UNWRITTEN_FIELD" },
|
||||
justification = "Constructed by JSON deserialization")
|
||||
public static class CommitComment extends GHEventPayload {
|
||||
private String action;
|
||||
private GHCommitComment comment;
|
||||
private GHRepository repository;
|
||||
|
||||
@SuppressFBWarnings(value = "UWF_UNWRITTEN_FIELD", justification = "Comes from JSON deserialization")
|
||||
public String getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
public GHCommitComment getComment() {
|
||||
return comment;
|
||||
}
|
||||
|
||||
public void setComment(GHCommitComment comment) {
|
||||
this.comment = comment;
|
||||
}
|
||||
|
||||
public GHRepository getRepository() {
|
||||
return repository;
|
||||
}
|
||||
|
||||
public void setRepository(GHRepository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
@Override
|
||||
void wrapUp(GitHub root) {
|
||||
super.wrapUp(root);
|
||||
if (repository != null) {
|
||||
repository.wrap(root);
|
||||
comment.wrap(repository);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A repository, branch, or tag was created
|
||||
*
|
||||
* @see <a href="http://developer.github.com/v3/activity/events/types/#createevent">authoritative source</a>
|
||||
*/
|
||||
@SuppressFBWarnings(value = {"UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR", "NP_UNWRITTEN_FIELD" },
|
||||
justification = "Constructed by JSON deserialization")
|
||||
public static class Create extends GHEventPayload {
|
||||
private String ref;
|
||||
@JsonProperty("ref_type")
|
||||
private String refType;
|
||||
@JsonProperty("master_branch")
|
||||
private String masterBranch;
|
||||
private String description;
|
||||
private GHRepository repository;
|
||||
|
||||
@SuppressFBWarnings(value = "UWF_UNWRITTEN_FIELD", justification = "Comes from JSON deserialization")
|
||||
public String getRef() {
|
||||
return ref;
|
||||
}
|
||||
|
||||
@SuppressFBWarnings(value = "UWF_UNWRITTEN_FIELD", justification = "Comes from JSON deserialization")
|
||||
public String getRefType() {
|
||||
return refType;
|
||||
}
|
||||
|
||||
@SuppressFBWarnings(value = "UWF_UNWRITTEN_FIELD", justification = "Comes from JSON deserialization")
|
||||
public String getMasterBranch() {
|
||||
return masterBranch;
|
||||
}
|
||||
|
||||
@SuppressFBWarnings(value = "UWF_UNWRITTEN_FIELD", justification = "Comes from JSON deserialization")
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public GHRepository getRepository() {
|
||||
return repository;
|
||||
}
|
||||
|
||||
public void setRepository(GHRepository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
@Override
|
||||
void wrapUp(GitHub root) {
|
||||
super.wrapUp(root);
|
||||
if (repository != null) {
|
||||
repository.wrap(root);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A branch, or tag was deleted
|
||||
*
|
||||
* @see <a href="http://developer.github.com/v3/activity/events/types/#deleteevent">authoritative source</a>
|
||||
*/
|
||||
@SuppressFBWarnings(value = {"UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR", "NP_UNWRITTEN_FIELD" },
|
||||
justification = "Constructed by JSON deserialization")
|
||||
public static class Delete extends GHEventPayload {
|
||||
private String ref;
|
||||
@JsonProperty("ref_type")
|
||||
private String refType;
|
||||
private GHRepository repository;
|
||||
|
||||
@SuppressFBWarnings(value = "UWF_UNWRITTEN_FIELD", justification = "Comes from JSON deserialization")
|
||||
public String getRef() {
|
||||
return ref;
|
||||
}
|
||||
|
||||
@SuppressFBWarnings(value = "UWF_UNWRITTEN_FIELD", justification = "Comes from JSON deserialization")
|
||||
public String getRefType() {
|
||||
return refType;
|
||||
}
|
||||
|
||||
public GHRepository getRepository() {
|
||||
return repository;
|
||||
}
|
||||
|
||||
public void setRepository(GHRepository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
@Override
|
||||
void wrapUp(GitHub root) {
|
||||
super.wrapUp(root);
|
||||
if (repository != null) {
|
||||
repository.wrap(root);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A deployment
|
||||
*
|
||||
* @see <a href="http://developer.github.com/v3/activity/events/types/#deploymentevent">authoritative source</a>
|
||||
*/
|
||||
@SuppressFBWarnings(value = {"UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR", "NP_UNWRITTEN_FIELD" },
|
||||
justification = "Constructed by JSON deserialization")
|
||||
public static class Deployment extends GHEventPayload {
|
||||
private GHDeployment deployment;
|
||||
private GHRepository repository;
|
||||
|
||||
public GHDeployment getDeployment() {
|
||||
return deployment;
|
||||
}
|
||||
|
||||
public void setDeployment(GHDeployment deployment) {
|
||||
this.deployment = deployment;
|
||||
}
|
||||
|
||||
public GHRepository getRepository() {
|
||||
return repository;
|
||||
}
|
||||
|
||||
public void setRepository(GHRepository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
@Override
|
||||
void wrapUp(GitHub root) {
|
||||
super.wrapUp(root);
|
||||
if (repository != null) {
|
||||
repository.wrap(root);
|
||||
deployment.wrap(repository);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A deployment
|
||||
*
|
||||
* @see <a href="http://developer.github.com/v3/activity/events/types/#deploymentstatusevent">authoritative source</a>
|
||||
*/
|
||||
@SuppressFBWarnings(value = {"UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR", "NP_UNWRITTEN_FIELD" },
|
||||
justification = "Constructed by JSON deserialization")
|
||||
public static class DeploymentStatus extends GHEventPayload {
|
||||
@JsonProperty("deployment_status")
|
||||
private GHDeploymentStatus deploymentStatus;
|
||||
private GHDeployment deployment;
|
||||
private GHRepository repository;
|
||||
|
||||
public GHDeploymentStatus getDeploymentStatus() {
|
||||
return deploymentStatus;
|
||||
}
|
||||
|
||||
public void setDeploymentStatus(GHDeploymentStatus deploymentStatus) {
|
||||
this.deploymentStatus = deploymentStatus;
|
||||
}
|
||||
|
||||
public GHDeployment getDeployment() {
|
||||
return deployment;
|
||||
}
|
||||
|
||||
public void setDeployment(GHDeployment deployment) {
|
||||
this.deployment = deployment;
|
||||
}
|
||||
|
||||
public GHRepository getRepository() {
|
||||
return repository;
|
||||
}
|
||||
|
||||
public void setRepository(GHRepository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
@Override
|
||||
void wrapUp(GitHub root) {
|
||||
super.wrapUp(root);
|
||||
if (repository != null) {
|
||||
repository.wrap(root);
|
||||
deployment.wrap(repository);
|
||||
deploymentStatus.wrap(repository);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A user forked a repository
|
||||
*
|
||||
* @see <a href="http://developer.github.com/v3/activity/events/types/#forkevent">authoritative source</a>
|
||||
*/
|
||||
@SuppressFBWarnings(value = {"UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR", "NP_UNWRITTEN_FIELD" },
|
||||
justification = "Constructed by JSON deserialization")
|
||||
public static class Fork extends GHEventPayload {
|
||||
private GHRepository forkee;
|
||||
private GHRepository repository;
|
||||
|
||||
|
||||
public GHRepository getForkee() {
|
||||
return forkee;
|
||||
}
|
||||
|
||||
public void setForkee(GHRepository forkee) {
|
||||
this.forkee = forkee;
|
||||
}
|
||||
|
||||
public GHRepository getRepository() {
|
||||
return repository;
|
||||
}
|
||||
|
||||
public void setRepository(GHRepository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
@Override
|
||||
void wrapUp(GitHub root) {
|
||||
super.wrapUp(root);
|
||||
forkee.wrap(root);
|
||||
if (repository != null) {
|
||||
repository.wrap(root);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A ping.
|
||||
*/
|
||||
public static class Ping extends GHEventPayload {
|
||||
private GHRepository repository;
|
||||
private GHOrganization organization;
|
||||
|
||||
public void setRepository(GHRepository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
public GHRepository getRepository() {
|
||||
return repository;
|
||||
}
|
||||
|
||||
public GHOrganization getOrganization() {
|
||||
return organization;
|
||||
}
|
||||
|
||||
public void setOrganization(GHOrganization organization) {
|
||||
this.organization = organization;
|
||||
}
|
||||
|
||||
@Override
|
||||
void wrapUp(GitHub root) {
|
||||
super.wrapUp(root);
|
||||
if (repository!=null)
|
||||
repository.wrap(root);
|
||||
if (organization != null) {
|
||||
organization.wrapUp(root);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A repository was made public.
|
||||
*
|
||||
* @see <a href="http://developer.github.com/v3/activity/events/types/#publicevent">authoritative source</a>
|
||||
*/
|
||||
public static class Public extends GHEventPayload {
|
||||
private GHRepository repository;
|
||||
|
||||
public void setRepository(GHRepository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
public GHRepository getRepository() {
|
||||
return repository;
|
||||
}
|
||||
|
||||
@Override
|
||||
void wrapUp(GitHub root) {
|
||||
super.wrapUp(root);
|
||||
if (repository!=null)
|
||||
repository.wrap(root);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A commit was pushed.
|
||||
*
|
||||
* @see <a href="http://developer.github.com/v3/activity/events/types/#pushevent">authoritative source</a>
|
||||
*/
|
||||
@SuppressFBWarnings(value = {"UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR", "NP_UNWRITTEN_FIELD", "UUF_UNUSED_FIELD"},
|
||||
justification = "Constructed by JSON deserialization")
|
||||
public static class Push extends GHEventPayload {
|
||||
private String head, before;
|
||||
private boolean created, deleted, forced;
|
||||
private String ref;
|
||||
private int size;
|
||||
private List<PushCommit> commits;
|
||||
private GHRepository repository;
|
||||
private Pusher pusher;
|
||||
|
||||
/**
|
||||
* The SHA of the HEAD commit on the repository
|
||||
@@ -141,6 +579,11 @@ public abstract class GHEventPayload {
|
||||
return before;
|
||||
}
|
||||
|
||||
@JsonSetter // alias
|
||||
private void setAfter(String after) {
|
||||
head = after;
|
||||
}
|
||||
|
||||
/**
|
||||
* The full Git ref that was pushed. Example: “refs/heads/master”
|
||||
*/
|
||||
@@ -156,6 +599,18 @@ public abstract class GHEventPayload {
|
||||
return size;
|
||||
}
|
||||
|
||||
public boolean isCreated() {
|
||||
return created;
|
||||
}
|
||||
|
||||
public boolean isDeleted() {
|
||||
return deleted;
|
||||
}
|
||||
|
||||
public boolean isForced() {
|
||||
return forced;
|
||||
}
|
||||
|
||||
/**
|
||||
* The list of pushed commits.
|
||||
*/
|
||||
@@ -167,6 +622,14 @@ public abstract class GHEventPayload {
|
||||
return repository;
|
||||
}
|
||||
|
||||
public Pusher getPusher() {
|
||||
return pusher;
|
||||
}
|
||||
|
||||
public void setPusher(Pusher pusher) {
|
||||
this.pusher = pusher;
|
||||
}
|
||||
|
||||
@Override
|
||||
void wrapUp(GitHub root) {
|
||||
super.wrapUp(root);
|
||||
@@ -174,18 +637,44 @@ public abstract class GHEventPayload {
|
||||
repository.wrap(root);
|
||||
}
|
||||
|
||||
public static class Pusher {
|
||||
private String name, email;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Commit in a push
|
||||
*/
|
||||
public static class PushCommit {
|
||||
private GitUser author;
|
||||
private GitUser committer;
|
||||
private String url, sha, message;
|
||||
private boolean distinct;
|
||||
private List<String> added, removed, modified;
|
||||
|
||||
public GitUser getAuthor() {
|
||||
return author;
|
||||
}
|
||||
|
||||
public GitUser getCommitter() {
|
||||
return committer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Points to the commit API resource.
|
||||
*/
|
||||
@@ -197,6 +686,11 @@ public abstract class GHEventPayload {
|
||||
return sha;
|
||||
}
|
||||
|
||||
@JsonSetter
|
||||
private void setId(String id) {
|
||||
sha = id;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
@@ -207,6 +701,61 @@ public abstract class GHEventPayload {
|
||||
public boolean isDistinct() {
|
||||
return distinct;
|
||||
}
|
||||
|
||||
public List<String> getAdded() {
|
||||
return added;
|
||||
}
|
||||
|
||||
public List<String> getRemoved() {
|
||||
return removed;
|
||||
}
|
||||
|
||||
public List<String> getModified() {
|
||||
return modified;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A repository was created, deleted, made public, or made private.
|
||||
*
|
||||
* @see <a href="http://developer.github.com/v3/activity/events/types/#repositoryevent">authoritative source</a>
|
||||
*/
|
||||
@SuppressFBWarnings(value = {"UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR", "NP_UNWRITTEN_FIELD", "UWF_UNWRITTEN_FIELD"},
|
||||
justification = "Constructed by JSON deserialization")
|
||||
public static class Repository extends GHEventPayload {
|
||||
private String action;
|
||||
private GHRepository repository;
|
||||
private GHOrganization organization;
|
||||
|
||||
public String getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
public void setRepository(GHRepository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
public GHRepository getRepository() {
|
||||
return repository;
|
||||
}
|
||||
|
||||
public GHOrganization getOrganization() {
|
||||
return organization;
|
||||
}
|
||||
|
||||
public void setOrganization(GHOrganization organization) {
|
||||
this.organization = organization;
|
||||
}
|
||||
|
||||
@Override
|
||||
void wrapUp(GitHub root) {
|
||||
super.wrapUp(root);
|
||||
repository.wrap(root);
|
||||
if (organization != null) {
|
||||
organization.wrapUp(root);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import javax.annotation.CheckForNull;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Request/responce contains useful metadata.
|
||||
* Custom exception allows store info for next diagnostics.
|
||||
*
|
||||
* @author Kanstantsin Shautsou
|
||||
*/
|
||||
public class GHFileNotFoundException extends FileNotFoundException {
|
||||
protected Map<String, List<String>> responseHeaderFields;
|
||||
|
||||
public GHFileNotFoundException() {
|
||||
}
|
||||
|
||||
public GHFileNotFoundException(String s) {
|
||||
super(s);
|
||||
}
|
||||
|
||||
@CheckForNull
|
||||
public Map<String, List<String>> getResponseHeaderFields() {
|
||||
return responseHeaderFields;
|
||||
}
|
||||
|
||||
GHFileNotFoundException withResponseHeaderFields(HttpURLConnection urlConnection) {
|
||||
this.responseHeaderFields = urlConnection.getHeaderFields();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -38,8 +38,8 @@ public class GHGist extends GHObject {
|
||||
/**
|
||||
* User that owns this Gist.
|
||||
*/
|
||||
public GHUser getOwner() {
|
||||
return owner;
|
||||
public GHUser getOwner() throws IOException {
|
||||
return root.intern(owner);
|
||||
}
|
||||
|
||||
public String getForksUrl() {
|
||||
@@ -103,7 +103,7 @@ public class GHGist extends GHObject {
|
||||
* Used when caller obtains {@link GHGist} without knowing its owner.
|
||||
* A partially constructed owner object is interned.
|
||||
*/
|
||||
/*package*/ GHGist wrapUp(GitHub root) throws IOException {
|
||||
/*package*/ GHGist wrapUp(GitHub root) {
|
||||
this.owner = root.getUser(owner);
|
||||
this.root = root;
|
||||
wrapUp();
|
||||
@@ -140,16 +140,12 @@ public class GHGist extends GHObject {
|
||||
|
||||
public PagedIterable<GHGist> listForks() {
|
||||
return new PagedIterable<GHGist>() {
|
||||
public PagedIterator<GHGist> iterator() {
|
||||
return new PagedIterator<GHGist>(root.retrieve().asIterator(getApiTailUrl("forks"), GHGist[].class)) {
|
||||
public PagedIterator<GHGist> _iterator(int pageSize) {
|
||||
return new PagedIterator<GHGist>(root.retrieve().asIterator(getApiTailUrl("forks"), GHGist[].class, pageSize)) {
|
||||
@Override
|
||||
protected void wrapUp(GHGist[] page) {
|
||||
try {
|
||||
for (GHGist c : page)
|
||||
c.wrapUp(root);
|
||||
} catch (IOException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
for (GHGist c : page)
|
||||
c.wrapUp(root);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.Collections;
|
||||
@@ -11,6 +13,8 @@ import java.util.Map;
|
||||
/**
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
|
||||
"NP_UNWRITTEN_FIELD"}, justification = "JSON API")
|
||||
public abstract class GHHook extends GHObject {
|
||||
String name;
|
||||
List<String> events;
|
||||
@@ -23,8 +27,10 @@ public abstract class GHHook extends GHObject {
|
||||
|
||||
public EnumSet<GHEvent> getEvents() {
|
||||
EnumSet<GHEvent> s = EnumSet.noneOf(GHEvent.class);
|
||||
for (String e : events)
|
||||
s.add(Enum.valueOf(GHEvent.class,e.toUpperCase(Locale.ENGLISH)));
|
||||
for (String e : events) {
|
||||
if (e.equals("*")) s.add(GHEvent.ALL);
|
||||
else s.add(Enum.valueOf(GHEvent.class, e.toUpperCase(Locale.ENGLISH)));
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -36,6 +42,13 @@ public abstract class GHHook extends GHObject {
|
||||
return Collections.unmodifiableMap(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see <a href="https://developer.github.com/v3/repos/hooks/#ping-a-hook">Ping hook</a>
|
||||
*/
|
||||
public void ping() throws IOException {
|
||||
new Requester(getRoot()).method("POST").to(getApiRoute() + "/pings");
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes this hook.
|
||||
*/
|
||||
|
||||
@@ -5,7 +5,6 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -21,8 +20,9 @@ class GHHooks {
|
||||
}
|
||||
|
||||
public List<GHHook> getHooks() throws IOException {
|
||||
List<GHHook> list = new ArrayList<GHHook>(Arrays.asList(
|
||||
root.retrieve().to(collection(), collectionClass())));
|
||||
|
||||
GHHook [] hookArray = root.retrieve().to(collection(),collectionClass()); // jdk/eclipse bug requires this to be on separate line
|
||||
List<GHHook> list = new ArrayList<GHHook>(Arrays.asList(hookArray));
|
||||
for (GHHook h : list)
|
||||
wrap(h);
|
||||
return list;
|
||||
@@ -38,7 +38,7 @@ class GHHooks {
|
||||
if (events!=null) {
|
||||
ea = new ArrayList<String>();
|
||||
for (GHEvent e : events)
|
||||
ea.add(e.name().toLowerCase(Locale.ENGLISH));
|
||||
ea.add(e.symbol());
|
||||
}
|
||||
|
||||
GHHook hook = new Requester(root)
|
||||
|
||||
34
src/main/java/org/kohsuke/github/GHIOException.java
Normal file
34
src/main/java/org/kohsuke/github/GHIOException.java
Normal file
@@ -0,0 +1,34 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import javax.annotation.CheckForNull;
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Request/responce contains useful metadata.
|
||||
* Custom exception allows store info for next diagnostics.
|
||||
*
|
||||
* @author Kanstantsin Shautsou
|
||||
*/
|
||||
public class GHIOException extends IOException {
|
||||
protected Map<String, List<String>> responseHeaderFields;
|
||||
|
||||
public GHIOException() {
|
||||
}
|
||||
|
||||
public GHIOException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
@CheckForNull
|
||||
public Map<String, List<String>> getResponseHeaderFields() {
|
||||
return responseHeaderFields;
|
||||
}
|
||||
|
||||
GHIOException withResponseHeaderFields(HttpURLConnection urlConnection) {
|
||||
this.responseHeaderFields = urlConnection.getHeaderFields();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -25,15 +25,20 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import static org.kohsuke.github.Previews.*;
|
||||
|
||||
/**
|
||||
* Represents an issue on GitHub.
|
||||
*
|
||||
@@ -43,16 +48,18 @@ import java.util.Locale;
|
||||
* @see GitHub#searchIssues()
|
||||
* @see GHIssueSearchBuilder
|
||||
*/
|
||||
public class GHIssue extends GHObject {
|
||||
public class GHIssue extends GHObject implements Reactable{
|
||||
GitHub root;
|
||||
GHRepository owner;
|
||||
|
||||
// API v3
|
||||
protected GHUser assignee;
|
||||
protected GHUser assignee; // not sure what this field is now that 'assignees' exist
|
||||
protected GHUser[] assignees;
|
||||
protected String state;
|
||||
protected int number;
|
||||
protected String closed_at;
|
||||
protected int comments;
|
||||
@SkipFromToString
|
||||
protected String body;
|
||||
// for backward compatibility with < 1.63, this collection needs to hold instances of Label, not GHLabel
|
||||
protected List<Label> labels;
|
||||
@@ -61,6 +68,7 @@ public class GHIssue extends GHObject {
|
||||
protected GHIssue.PullRequest pull_request;
|
||||
protected GHMilestone milestone;
|
||||
protected GHUser closed_by;
|
||||
protected boolean locked;
|
||||
|
||||
/**
|
||||
* @deprecated use {@link GHLabel}
|
||||
@@ -77,6 +85,7 @@ public class GHIssue extends GHObject {
|
||||
/*package*/ GHIssue wrap(GitHub root) {
|
||||
this.root = root;
|
||||
if(assignee != null) assignee.wrapUp(root);
|
||||
if(assignees!=null) GHUser.wrap(assignees,root);
|
||||
if(user != null) user.wrapUp(root);
|
||||
if(closed_by != null) closed_by.wrapUp(root);
|
||||
return this;
|
||||
@@ -121,6 +130,10 @@ public class GHIssue extends GHObject {
|
||||
return title;
|
||||
}
|
||||
|
||||
public boolean isLocked() {
|
||||
return locked;
|
||||
}
|
||||
|
||||
public GHIssueState getState() {
|
||||
return Enum.valueOf(GHIssueState.class, state.toUpperCase(Locale.ENGLISH));
|
||||
}
|
||||
@@ -140,6 +153,14 @@ public class GHIssue extends GHObject {
|
||||
return GitHub.parseURL(url);
|
||||
}
|
||||
|
||||
public void lock() throws IOException {
|
||||
new Requester(root).method("PUT").to(getApiRoute()+"/lock");
|
||||
}
|
||||
|
||||
public void unlock() throws IOException {
|
||||
new Requester(root).method("PUT").to(getApiRoute()+"/lock");
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the issue by adding a comment.
|
||||
*
|
||||
@@ -182,8 +203,12 @@ public class GHIssue extends GHObject {
|
||||
edit("body",body);
|
||||
}
|
||||
|
||||
public void setMilestone(GHMilestone milestone) throws IOException {
|
||||
edit("milestone",milestone.getNumber());
|
||||
}
|
||||
|
||||
public void assignTo(GHUser user) throws IOException {
|
||||
editIssue("assignee", user.getLogin());
|
||||
setAssignees(user);
|
||||
}
|
||||
|
||||
public void setLabels(String... labels) throws IOException {
|
||||
@@ -204,8 +229,8 @@ public class GHIssue extends GHObject {
|
||||
*/
|
||||
public PagedIterable<GHIssueComment> listComments() throws IOException {
|
||||
return new PagedIterable<GHIssueComment>() {
|
||||
public PagedIterator<GHIssueComment> iterator() {
|
||||
return new PagedIterator<GHIssueComment>(root.retrieve().asIterator(getIssuesApiRoute() + "/comments", GHIssueComment[].class)) {
|
||||
public PagedIterator<GHIssueComment> _iterator(int pageSize) {
|
||||
return new PagedIterator<GHIssueComment>(root.retrieve().asIterator(getIssuesApiRoute() + "/comments", GHIssueComment[].class, pageSize)) {
|
||||
protected void wrapUp(GHIssueComment[] page) {
|
||||
for (GHIssueComment c : page)
|
||||
c.wrapUp(GHIssue.this);
|
||||
@@ -215,6 +240,63 @@ public class GHIssue extends GHObject {
|
||||
};
|
||||
}
|
||||
|
||||
@Preview @Deprecated
|
||||
public GHReaction createReaction(ReactionContent content) throws IOException {
|
||||
return new Requester(owner.root)
|
||||
.withPreview(SQUIRREL_GIRL)
|
||||
.with("content", content.getContent())
|
||||
.to(getApiRoute()+"/reactions", GHReaction.class).wrap(root);
|
||||
}
|
||||
|
||||
@Preview @Deprecated
|
||||
public PagedIterable<GHReaction> listReactions() {
|
||||
return new PagedIterable<GHReaction>() {
|
||||
public PagedIterator<GHReaction> _iterator(int pageSize) {
|
||||
return new PagedIterator<GHReaction>(owner.root.retrieve().withPreview(SQUIRREL_GIRL).asIterator(getApiRoute()+"/reactions", GHReaction[].class, pageSize)) {
|
||||
@Override
|
||||
protected void wrapUp(GHReaction[] page) {
|
||||
for (GHReaction c : page)
|
||||
c.wrap(owner.root);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void addAssignees(GHUser... assignees) throws IOException {
|
||||
addAssignees(Arrays.asList(assignees));
|
||||
}
|
||||
|
||||
public void addAssignees(Collection<GHUser> assignees) throws IOException {
|
||||
List<String> names = toLogins(assignees);
|
||||
root.retrieve().method("POST").with("assignees",names).to(getIssuesApiRoute()+"/assignees",this);
|
||||
}
|
||||
|
||||
public void setAssignees(GHUser... assignees) throws IOException {
|
||||
setAssignees(Arrays.asList(assignees));
|
||||
}
|
||||
|
||||
public void setAssignees(Collection<GHUser> assignees) throws IOException {
|
||||
editIssue("assignees",toLogins(assignees));
|
||||
}
|
||||
|
||||
public void removeAssignees(GHUser... assignees) throws IOException {
|
||||
removeAssignees(Arrays.asList(assignees));
|
||||
}
|
||||
|
||||
public void removeAssignees(Collection<GHUser> assignees) throws IOException {
|
||||
List<String> names = toLogins(assignees);
|
||||
root.retrieve().method("DELETE").with("assignees",names).inBody().to(getIssuesApiRoute()+"/assignees",this);
|
||||
}
|
||||
|
||||
private List<String> toLogins(Collection<GHUser> assignees) {
|
||||
List<String> names = new ArrayList<String>(assignees.size());
|
||||
for (GHUser a : assignees) {
|
||||
names.add(a.getLogin());
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
protected String getApiRoute() {
|
||||
return getIssuesApiRoute();
|
||||
}
|
||||
@@ -223,15 +305,19 @@ public class GHIssue extends GHObject {
|
||||
return "/repos/"+owner.getOwnerName()+"/"+owner.getName()+"/issues/"+number;
|
||||
}
|
||||
|
||||
public GHUser getAssignee() {
|
||||
return assignee;
|
||||
public GHUser getAssignee() throws IOException {
|
||||
return root.intern(assignee);
|
||||
}
|
||||
|
||||
|
||||
public List<GHUser> getAssignees() {
|
||||
return Collections.unmodifiableList(Arrays.asList(assignees));
|
||||
}
|
||||
|
||||
/**
|
||||
* User who submitted the issue.
|
||||
*/
|
||||
public GHUser getUser() {
|
||||
return user;
|
||||
public GHUser getUser() throws IOException {
|
||||
return root.intern(user);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -242,12 +328,16 @@ public class GHIssue extends GHObject {
|
||||
* even for an issue that's already closed. See
|
||||
* https://github.com/kohsuke/github-api/issues/60.
|
||||
*/
|
||||
public GHUser getClosedBy() {
|
||||
public GHUser getClosedBy() throws IOException {
|
||||
if(!"closed".equals(state)) return null;
|
||||
if(closed_by != null) return closed_by;
|
||||
|
||||
//TODO closed_by = owner.getIssue(number).getClosed_by();
|
||||
return closed_by;
|
||||
|
||||
//TODO
|
||||
/*
|
||||
if (closed_by==null) {
|
||||
closed_by = owner.getIssue(number).getClosed_by();
|
||||
}
|
||||
*/
|
||||
return root.intern(closed_by);
|
||||
}
|
||||
|
||||
public int getCommentsCount(){
|
||||
@@ -269,6 +359,8 @@ public class GHIssue extends GHObject {
|
||||
return milestone;
|
||||
}
|
||||
|
||||
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD"},
|
||||
justification = "JSON API")
|
||||
public static class PullRequest{
|
||||
private String diff_url, patch_url, html_url;
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ public class GHIssueBuilder {
|
||||
private final GHRepository repo;
|
||||
private final Requester builder;
|
||||
private List<String> labels = new ArrayList<String>();
|
||||
private List<String> assignees = new ArrayList<String>();
|
||||
|
||||
GHIssueBuilder(GHRepository repo, String title) {
|
||||
this.repo = repo;
|
||||
@@ -28,13 +29,13 @@ public class GHIssueBuilder {
|
||||
|
||||
public GHIssueBuilder assignee(GHUser user) {
|
||||
if (user!=null)
|
||||
builder.with("assignee",user.getLogin());
|
||||
assignees.add(user.getLogin());
|
||||
return this;
|
||||
}
|
||||
|
||||
public GHIssueBuilder assignee(String user) {
|
||||
if (user!=null)
|
||||
builder.with("assignee",user);
|
||||
assignees.add(user);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -54,6 +55,6 @@ public class GHIssueBuilder {
|
||||
* Creates a new issue.
|
||||
*/
|
||||
public GHIssue create() throws IOException {
|
||||
return builder.with("labels",labels).to(repo.getApiTailUrl("issues"),GHIssue.class).wrap(repo);
|
||||
return builder.with("labels",labels).with("assignees",assignees).to(repo.getApiTailUrl("issues"),GHIssue.class).wrap(repo);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,16 +26,18 @@ package org.kohsuke.github;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
import static org.kohsuke.github.Previews.*;
|
||||
|
||||
/**
|
||||
* Comment to the issue
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
public class GHIssueComment extends GHObject {
|
||||
public class GHIssueComment extends GHObject implements Reactable {
|
||||
GHIssue owner;
|
||||
|
||||
private String body, gravatar_id;
|
||||
private GHUser user;
|
||||
private GHUser user; // not fully populated. beware.
|
||||
|
||||
/*package*/ GHIssueComment wrapUp(GHIssue owner) {
|
||||
this.owner = owner;
|
||||
@@ -68,7 +70,7 @@ public class GHIssueComment extends GHObject {
|
||||
* Gets the user who posted this comment.
|
||||
*/
|
||||
public GHUser getUser() throws IOException {
|
||||
return owner.root.getUser(user.getLogin());
|
||||
return owner == null || owner.root.isOffline() ? user : owner.root.getUser(user.getLogin());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -93,7 +95,30 @@ public class GHIssueComment extends GHObject {
|
||||
public void delete() throws IOException {
|
||||
new Requester(owner.root).method("DELETE").to(getApiRoute());
|
||||
}
|
||||
|
||||
|
||||
@Preview @Deprecated
|
||||
public GHReaction createReaction(ReactionContent content) throws IOException {
|
||||
return new Requester(owner.root)
|
||||
.withPreview(SQUIRREL_GIRL)
|
||||
.with("content", content.getContent())
|
||||
.to(getApiRoute()+"/reactions", GHReaction.class).wrap(owner.root);
|
||||
}
|
||||
|
||||
@Preview @Deprecated
|
||||
public PagedIterable<GHReaction> listReactions() {
|
||||
return new PagedIterable<GHReaction>() {
|
||||
public PagedIterator<GHReaction> _iterator(int pageSize) {
|
||||
return new PagedIterator<GHReaction>(owner.root.retrieve().withPreview(SQUIRREL_GIRL).asIterator(getApiRoute()+"/reactions", GHReaction[].class, pageSize)) {
|
||||
@Override
|
||||
protected void wrapUp(GHReaction[] page) {
|
||||
for (GHReaction c : page)
|
||||
c.wrap(owner.root);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private String getApiRoute() {
|
||||
return "/repos/"+owner.getRepository().getOwnerName()+"/"+owner.getRepository().getName()+"/issues/comments/" + id;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Search issues.
|
||||
*
|
||||
@@ -41,8 +39,13 @@ public class GHIssueSearchBuilder extends GHSearchBuilder<GHIssue> {
|
||||
return q("is:merged");
|
||||
}
|
||||
|
||||
public GHIssueSearchBuilder order(GHDirection v) {
|
||||
req.with("order",v);
|
||||
return this;
|
||||
}
|
||||
|
||||
public GHIssueSearchBuilder sort(Sort sort) {
|
||||
req.with("sort",sort.toString().toLowerCase(Locale.ENGLISH));
|
||||
req.with("sort",sort);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,11 @@
|
||||
|
||||
package org.kohsuke.github;
|
||||
|
||||
/**
|
||||
* @see GHPullRequestQueryBuilder#state(GHIssueState)
|
||||
*/
|
||||
public enum GHIssueState {
|
||||
OPEN,
|
||||
CLOSED
|
||||
CLOSED,
|
||||
ALL
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import org.apache.commons.lang.builder.ToStringBuilder;
|
||||
|
||||
/**
|
||||
@@ -7,6 +8,7 @@ import org.apache.commons.lang.builder.ToStringBuilder;
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
@SuppressFBWarnings(value = "UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", justification = "JSON API")
|
||||
public class GHKey {
|
||||
/*package almost final*/ GitHub root;
|
||||
|
||||
@@ -33,6 +35,10 @@ public class GHKey {
|
||||
return url;
|
||||
}
|
||||
|
||||
public GitHub getRoot() {
|
||||
return root;
|
||||
}
|
||||
|
||||
public boolean isVerified() {
|
||||
return verified;
|
||||
}
|
||||
|
||||
@@ -34,4 +34,12 @@ public class GHLabel {
|
||||
public void delete() throws IOException {
|
||||
repo.root.retrieve().method("DELETE").to(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param newColor
|
||||
* 6-letter hex color code, like "f29513"
|
||||
*/
|
||||
public void setColor(String newColor) throws IOException {
|
||||
repo.root.retrieve().method("PATCH").with("name", name).with("color", newColor).to(url);
|
||||
}
|
||||
}
|
||||
|
||||
168
src/main/java/org/kohsuke/github/GHLicense.java
Normal file
168
src/main/java/org/kohsuke/github/GHLicense.java
Normal file
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2016, Duncan Dickinson
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package org.kohsuke.github;
|
||||
|
||||
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.kohsuke.github.Previews.*;
|
||||
|
||||
/**
|
||||
* The GitHub Preview API's license information
|
||||
* <p>
|
||||
* WARNING: This uses a PREVIEW API - subject to change.
|
||||
*
|
||||
* @author Duncan Dickinson
|
||||
* @see GitHub#getLicense(String)
|
||||
* @see GHRepository#getLicense()
|
||||
* @see <a href="https://developer.github.com/v3/licenses/">https://developer.github.com/v3/licenses/</a>
|
||||
*/
|
||||
@Preview @Deprecated
|
||||
@SuppressWarnings({"UnusedDeclaration"})
|
||||
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
|
||||
"NP_UNWRITTEN_FIELD"}, justification = "JSON API")
|
||||
public class GHLicense extends GHObject {
|
||||
@SuppressFBWarnings("IS2_INCONSISTENT_SYNC") // root is set before the object is returned to the app
|
||||
/*package almost final*/ GitHub root;
|
||||
|
||||
// these fields are always present, even in the short form
|
||||
protected String key, name;
|
||||
|
||||
// the rest is only after populated
|
||||
protected Boolean featured;
|
||||
|
||||
protected String html_url, description, category, implementation, body;
|
||||
|
||||
protected List<String> required = new ArrayList<String>();
|
||||
protected List<String> permitted = new ArrayList<String>();
|
||||
protected List<String> forbidden = new ArrayList<String>();
|
||||
|
||||
/**
|
||||
* @return a mnemonic for the license
|
||||
*/
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the license name
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return API URL of this object.
|
||||
*/
|
||||
@WithBridgeMethods(value = String.class, adapterMethod = "urlToString")
|
||||
public URL getUrl() {
|
||||
return GitHub.parseURL(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Featured licenses are bold in the new repository drop-down
|
||||
*
|
||||
* @return True if the license is featured, false otherwise
|
||||
*/
|
||||
public Boolean isFeatured() throws IOException {
|
||||
populate();
|
||||
return featured;
|
||||
}
|
||||
|
||||
public URL getHtmlUrl() throws IOException {
|
||||
populate();
|
||||
return GitHub.parseURL(html_url);
|
||||
}
|
||||
|
||||
public String getDescription() throws IOException {
|
||||
populate();
|
||||
return description;
|
||||
}
|
||||
|
||||
public String getCategory() throws IOException {
|
||||
populate();
|
||||
return category;
|
||||
}
|
||||
|
||||
public String getImplementation() throws IOException {
|
||||
populate();
|
||||
return implementation;
|
||||
}
|
||||
|
||||
public List<String> getRequired() throws IOException {
|
||||
populate();
|
||||
return required;
|
||||
}
|
||||
|
||||
public List<String> getPermitted() throws IOException {
|
||||
populate();
|
||||
return permitted;
|
||||
}
|
||||
|
||||
public List<String> getForbidden() throws IOException {
|
||||
populate();
|
||||
return forbidden;
|
||||
}
|
||||
|
||||
public String getBody() throws IOException {
|
||||
populate();
|
||||
return body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fully populate the data by retrieving missing data.
|
||||
*
|
||||
* Depending on the original API call where this object is created, it may not contain everything.
|
||||
*/
|
||||
protected synchronized void populate() throws IOException {
|
||||
if (description!=null) return; // already populated
|
||||
|
||||
root.retrieve().withPreview(DRAX).to(url, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof GHLicense)) return false;
|
||||
|
||||
GHLicense that = (GHLicense) o;
|
||||
return this.url.equals(that.url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return url.hashCode();
|
||||
}
|
||||
|
||||
/*package*/ GHLicense wrap(GitHub root) {
|
||||
this.root = root;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
84
src/main/java/org/kohsuke/github/GHMembership.java
Normal file
84
src/main/java/org/kohsuke/github/GHMembership.java
Normal file
@@ -0,0 +1,84 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Represents a membership of a user in an organization.
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
* @see GHMyself#listOrgMemberships()
|
||||
*/
|
||||
public class GHMembership /* extends GHObject --- but it doesn't have id, created_at, etc. */ {
|
||||
GitHub root;
|
||||
|
||||
String url;
|
||||
String state;
|
||||
String role;
|
||||
GHUser user;
|
||||
GHOrganization organization;
|
||||
|
||||
public URL getUrl() {
|
||||
return GitHub.parseURL(url);
|
||||
}
|
||||
|
||||
public State getState() {
|
||||
return Enum.valueOf(State.class, state.toUpperCase(Locale.ENGLISH));
|
||||
}
|
||||
|
||||
public Role getRole() {
|
||||
return Enum.valueOf(Role.class, role.toUpperCase(Locale.ENGLISH));
|
||||
}
|
||||
|
||||
public GHUser getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public GHOrganization getOrganization() {
|
||||
return organization;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepts a pending invitation to an organization.
|
||||
*
|
||||
* @see GHMyself#getMembership(GHOrganization)
|
||||
*/
|
||||
public void activate() throws IOException {
|
||||
root.retrieve().method("PATCH").with("state",State.ACTIVE).to(url,this);
|
||||
}
|
||||
|
||||
/*package*/ GHMembership wrap(GitHub root) {
|
||||
this.root = root;
|
||||
if (user!=null) user = root.getUser(user.wrapUp(root));
|
||||
if (organization!=null) organization.wrapUp(root);
|
||||
return this;
|
||||
}
|
||||
|
||||
/*package*/ static void wrap(GHMembership[] page, GitHub root) {
|
||||
for (GHMembership m : page)
|
||||
m.wrap(root);
|
||||
}
|
||||
|
||||
/**
|
||||
* Role of a user in an organization.
|
||||
*/
|
||||
public enum Role {
|
||||
/**
|
||||
* Organization owner.
|
||||
*/
|
||||
ADMIN,
|
||||
/**
|
||||
* Non-owner organization member.
|
||||
*/
|
||||
MEMBER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a role is currently active or waiting for acceptance (pending)
|
||||
*/
|
||||
public enum State {
|
||||
ACTIVE,
|
||||
PENDING;
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ public class GHMilestone extends GHObject {
|
||||
GHUser creator;
|
||||
private String state, due_on, title, description, html_url;
|
||||
private int closed_issues, open_issues, number;
|
||||
protected String closed_at;
|
||||
|
||||
public GitHub getRoot() {
|
||||
return root;
|
||||
@@ -26,15 +27,22 @@ public class GHMilestone extends GHObject {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public GHUser getCreator() {
|
||||
return creator;
|
||||
public GHUser getCreator() throws IOException {
|
||||
return root.intern(creator);
|
||||
}
|
||||
|
||||
public Date getDueOn() {
|
||||
if (due_on == null) return null;
|
||||
return GitHub.parseDate(due_on);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* When was this milestone closed?
|
||||
*/
|
||||
public Date getClosedAt() throws IOException {
|
||||
return GitHub.parseDate(closed_at);
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
@@ -64,12 +72,19 @@ public class GHMilestone extends GHObject {
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes this issue.
|
||||
* Closes this milestone.
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
edit("state", "closed");
|
||||
}
|
||||
|
||||
/**
|
||||
* Reopens this milestone.
|
||||
*/
|
||||
public void reopen() throws IOException {
|
||||
edit("state", "open");
|
||||
}
|
||||
|
||||
private void edit(String key, Object value) throws IOException {
|
||||
new Requester(root)._with(key, value).method("PATCH").to(getApiRoute());
|
||||
}
|
||||
|
||||
@@ -157,9 +157,8 @@ public class GHMyself extends GHUser {
|
||||
*/
|
||||
public PagedIterable<GHRepository> listRepositories(final int pageSize, final RepositoryListFilter repoType) {
|
||||
return new PagedIterable<GHRepository>() {
|
||||
public PagedIterator<GHRepository> iterator() {
|
||||
return new PagedIterator<GHRepository>(root.retrieve().asIterator("/user/repos?per_page=" + pageSize +
|
||||
"&type=" + repoType.name().toLowerCase(), GHRepository[].class)) {
|
||||
public PagedIterator<GHRepository> _iterator(int pageSize) {
|
||||
return new PagedIterator<GHRepository>(root.retrieve().with("type",repoType).asIterator("/user/repos", GHRepository[].class, pageSize)) {
|
||||
@Override
|
||||
protected void wrapUp(GHRepository[] page) {
|
||||
for (GHRepository c : page)
|
||||
@@ -167,7 +166,7 @@ public class GHMyself extends GHUser {
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}.withPageSize(pageSize);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,6 +177,39 @@ public class GHMyself extends GHUser {
|
||||
return listRepositories();
|
||||
}
|
||||
|
||||
/**
|
||||
* List your organization memberships
|
||||
*/
|
||||
public PagedIterable<GHMembership> listOrgMemberships() {
|
||||
return listOrgMemberships(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* List your organization memberships
|
||||
*
|
||||
* @param state
|
||||
* Filter by a specific state
|
||||
*/
|
||||
public PagedIterable<GHMembership> listOrgMemberships(final GHMembership.State state) {
|
||||
return new PagedIterable<GHMembership>() {
|
||||
public PagedIterator<GHMembership> _iterator(int pageSize) {
|
||||
return new PagedIterator<GHMembership>(root.retrieve().with("state",state).asIterator("/user/memberships/orgs", GHMembership[].class, pageSize)) {
|
||||
@Override
|
||||
protected void wrapUp(GHMembership[] page) {
|
||||
GHMembership.wrap(page,root);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets your membership in a specific organization.
|
||||
*/
|
||||
public GHMembership getMembership(GHOrganization o) throws IOException {
|
||||
return root.retrieve().to("/user/memberships/orgs/"+o.getLogin(),GHMembership.class).wrap(root);
|
||||
}
|
||||
|
||||
// public void addEmails(Collection<String> emails) throws IOException {
|
||||
//// new Requester(root,ApiVersion.V3).withCredential().to("/user/emails");
|
||||
// root.retrieveWithAuth3()
|
||||
|
||||
@@ -152,7 +152,7 @@ public class GHNotificationStream implements Iterable<GHThread> {
|
||||
while (true) {
|
||||
long now = System.currentTimeMillis();
|
||||
if (nextCheckTime < now) break;
|
||||
long waitTime = Math.max(Math.min(nextCheckTime - now, 1000), 60 * 1000);
|
||||
long waitTime = Math.min(Math.max(nextCheckTime - now, 1000), 60 * 1000);
|
||||
Thread.sleep(waitTime);
|
||||
}
|
||||
|
||||
@@ -180,7 +180,8 @@ public class GHNotificationStream implements Iterable<GHThread> {
|
||||
private long calcNextCheckTime() {
|
||||
String v = req.getResponseHeader("X-Poll-Interval");
|
||||
if (v==null) v="60";
|
||||
return System.currentTimeMillis()+Integer.parseInt(v)*1000;
|
||||
long seconds = Integer.parseInt(v);
|
||||
return System.currentTimeMillis() + seconds*1000;
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
|
||||
@@ -1,23 +1,52 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import org.apache.commons.lang.builder.ReflectionToStringBuilder;
|
||||
import org.apache.commons.lang.builder.ToStringStyle;
|
||||
|
||||
import javax.annotation.CheckForNull;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.URL;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Most (all?) domain objects in GitHub seems to have these 4 properties.
|
||||
*/
|
||||
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
|
||||
"NP_UNWRITTEN_FIELD"}, justification = "JSON API")
|
||||
public abstract class GHObject {
|
||||
/**
|
||||
* Capture response HTTP headers on the state object.
|
||||
*/
|
||||
protected Map<String, List<String>> responseHeaderFields;
|
||||
|
||||
protected String url;
|
||||
protected int id;
|
||||
protected long id;
|
||||
protected String created_at;
|
||||
protected String updated_at;
|
||||
|
||||
/*package*/ GHObject() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the HTTP response headers given along with the state of this object.
|
||||
*
|
||||
* <p>
|
||||
* Some of the HTTP headers have nothing to do with the object, for example "Cache-Control"
|
||||
* and others are different depending on how this object was retrieved.
|
||||
*
|
||||
* This method was added as a kind of hack to allow the caller to retrieve OAuth scopes and such.
|
||||
* Use with caution. The method might be removed in the future.
|
||||
*/
|
||||
@CheckForNull @Deprecated
|
||||
public Map<String, List<String>> getResponseHeaderFields() {
|
||||
return responseHeaderFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* When was this resource created?
|
||||
*/
|
||||
@@ -26,6 +55,7 @@ public abstract class GHObject {
|
||||
return GitHub.parseDate(created_at);
|
||||
}
|
||||
|
||||
@SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD", justification = "Bridge method of getCreatedAt")
|
||||
private Object createdAtStr(Date id, Class type) {
|
||||
return created_at;
|
||||
}
|
||||
@@ -42,7 +72,7 @@ public abstract class GHObject {
|
||||
* URL of this object for humans, which renders some HTML.
|
||||
*/
|
||||
@WithBridgeMethods(value=String.class, adapterMethod="urlToString")
|
||||
public abstract URL getHtmlUrl();
|
||||
public abstract URL getHtmlUrl() throws IOException;
|
||||
|
||||
/**
|
||||
* When was this resource last updated?
|
||||
@@ -54,16 +84,57 @@ public abstract class GHObject {
|
||||
/**
|
||||
* Unique ID number of this resource.
|
||||
*/
|
||||
@WithBridgeMethods(value=String.class, adapterMethod="intToString")
|
||||
public int getId() {
|
||||
@WithBridgeMethods(value={String.class,int.class}, adapterMethod="longToStringOrInt")
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
private Object intToString(int id, Class type) {
|
||||
return String.valueOf(id);
|
||||
@SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD", justification = "Bridge method of getId")
|
||||
private Object longToStringOrInt(long id, Class type) {
|
||||
if (type==String.class)
|
||||
return String.valueOf(id);
|
||||
if (type==int.class)
|
||||
return (int)id;
|
||||
throw new AssertionError("Unexpected type: "+type);
|
||||
}
|
||||
|
||||
@SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD", justification = "Bridge method of getHtmlUrl")
|
||||
private Object urlToString(URL url, Class type) {
|
||||
return url==null ? null : url.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* String representation to assist debugging and inspection. The output format of this string
|
||||
* is not a committed part of the API and is subject to change.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ReflectionToStringBuilder(this, TOSTRING_STYLE, null, null, false, false) {
|
||||
@Override
|
||||
protected boolean accept(Field field) {
|
||||
return super.accept(field) && !field.isAnnotationPresent(SkipFromToString.class);
|
||||
}
|
||||
}.toString();
|
||||
}
|
||||
|
||||
private static final ToStringStyle TOSTRING_STYLE = new ToStringStyle() {
|
||||
{
|
||||
this.setUseShortClassName(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void append(StringBuffer buffer, String fieldName, Object value, Boolean fullDetail) {
|
||||
// skip unimportant properties. '_' is a heuristics as important properties tend to have short names
|
||||
if (fieldName.contains("_"))
|
||||
return;
|
||||
// avoid recursing other GHObject
|
||||
if (value instanceof GHObject)
|
||||
return;
|
||||
// likewise no point in showing root
|
||||
if (value instanceof GitHub)
|
||||
return;
|
||||
|
||||
super.append(buffer,fieldName,value,fullDetail);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
@@ -24,6 +23,8 @@ public class GHOrganization extends GHPerson {
|
||||
*
|
||||
* @return
|
||||
* Newly created repository.
|
||||
* @deprecated
|
||||
* Use {@link #createRepository(String)} that uses a builder pattern to let you control every aspect.
|
||||
*/
|
||||
public GHRepository createRepository(String name, String description, String homepage, String team, boolean isPublic) throws IOException {
|
||||
GHTeam t = getTeams().get(team);
|
||||
@@ -32,13 +33,25 @@ public class GHOrganization extends GHPerson {
|
||||
return createRepository(name, description, homepage, t, isPublic);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* Use {@link #createRepository(String)} that uses a builder pattern to let you control every aspect.
|
||||
*/
|
||||
public GHRepository createRepository(String name, String description, String homepage, GHTeam team, boolean isPublic) throws IOException {
|
||||
if (team==null)
|
||||
throw new IllegalArgumentException("Invalid team");
|
||||
// such API doesn't exist, so fall back to HTML scraping
|
||||
return new Requester(root)
|
||||
.with("name", name).with("description", description).with("homepage", homepage)
|
||||
.with("public", isPublic).with("team_id",team.getId()).to("/orgs/"+login+"/repos", GHRepository.class).wrap(root);
|
||||
return createRepository(name).description(description).homepage(homepage).private_(!isPublic).team(team).create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a builder that creates a new repository.
|
||||
*
|
||||
* <p>
|
||||
* You use the returned builder to set various properties, then call {@link GHCreateRepositoryBuilder#create()}
|
||||
* to finally createa repository.
|
||||
*/
|
||||
public GHCreateRepositoryBuilder createRepository(String name) throws IOException {
|
||||
return new GHCreateRepositoryBuilder(root,"/orgs/"+login+"/repos",name);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -57,8 +70,8 @@ public class GHOrganization extends GHPerson {
|
||||
*/
|
||||
public PagedIterable<GHTeam> listTeams() throws IOException {
|
||||
return new PagedIterable<GHTeam>() {
|
||||
public PagedIterator<GHTeam> iterator() {
|
||||
return new PagedIterator<GHTeam>(root.retrieve().asIterator(String.format("/orgs/%s/teams", login), GHTeam[].class)) {
|
||||
public PagedIterator<GHTeam> _iterator(int pageSize) {
|
||||
return new PagedIterator<GHTeam>(root.retrieve().asIterator(String.format("/orgs/%s/teams", login), GHTeam[].class, pageSize)) {
|
||||
@Override
|
||||
protected void wrapUp(GHTeam[] page) {
|
||||
for (GHTeam c : page)
|
||||
@@ -80,6 +93,17 @@ public class GHOrganization extends GHPerson {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a team that has the given slug in its {@link GHTeam#getSlug()}
|
||||
*/
|
||||
public GHTeam getTeamBySlug(String slug) throws IOException {
|
||||
for (GHTeam t : listTeams()) {
|
||||
if(t.getSlug().equals(slug))
|
||||
return t;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this organization has the specified user as a member.
|
||||
*/
|
||||
@@ -150,9 +174,9 @@ public class GHOrganization extends GHPerson {
|
||||
|
||||
private PagedIterable<GHUser> listMembers(final String suffix, final String filter) throws IOException {
|
||||
return new PagedIterable<GHUser>() {
|
||||
public PagedIterator<GHUser> iterator() {
|
||||
public PagedIterator<GHUser> _iterator(int pageSize) {
|
||||
String filterParams = (filter == null) ? "" : ("?filter=" + filter);
|
||||
return new PagedIterator<GHUser>(root.retrieve().asIterator(String.format("/orgs/%s/%s%s", login, suffix, filterParams), GHUser[].class)) {
|
||||
return new PagedIterator<GHUser>(root.retrieve().asIterator(String.format("/orgs/%s/%s%s", login, suffix, filterParams), GHUser[].class, pageSize)) {
|
||||
@Override
|
||||
protected void wrapUp(GHUser[] users) {
|
||||
GHUser.wrap(users, root);
|
||||
@@ -175,7 +199,7 @@ public class GHOrganization extends GHPerson {
|
||||
* Creates a new team and assigns the repositories.
|
||||
*/
|
||||
public GHTeam createTeam(String name, Permission p, Collection<GHRepository> repositories) throws IOException {
|
||||
Requester post = new Requester(root).with("name", name).with("permission", p.name().toLowerCase());
|
||||
Requester post = new Requester(root).with("name", name).with("permission", p);
|
||||
List<String> repo_names = new ArrayList<String>();
|
||||
for (GHRepository r : repositories) {
|
||||
repo_names.add(r.getName());
|
||||
@@ -185,7 +209,7 @@ public class GHOrganization extends GHPerson {
|
||||
}
|
||||
|
||||
public GHTeam createTeam(String name, Permission p, GHRepository... repositories) throws IOException {
|
||||
return createTeam(name,p, Arrays.asList(repositories));
|
||||
return createTeam(name, p, Arrays.asList(repositories));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -196,7 +220,7 @@ public class GHOrganization extends GHPerson {
|
||||
*/
|
||||
public List<GHRepository> getRepositoriesWithOpenPullRequests() throws IOException {
|
||||
List<GHRepository> r = new ArrayList<GHRepository>();
|
||||
for (GHRepository repository : listRepositories()) {
|
||||
for (GHRepository repository : listRepositories(100)) {
|
||||
repository.wrap(root);
|
||||
List<GHPullRequest> pullRequests = repository.getPullRequests(GHIssueState.OPEN);
|
||||
if (pullRequests.size() > 0) {
|
||||
@@ -222,8 +246,8 @@ public class GHOrganization extends GHPerson {
|
||||
*/
|
||||
public PagedIterable<GHEventInfo> listEvents() throws IOException {
|
||||
return new PagedIterable<GHEventInfo>() {
|
||||
public PagedIterator<GHEventInfo> iterator() {
|
||||
return new PagedIterator<GHEventInfo>(root.retrieve().asIterator(String.format("/orgs/%s/events", login), GHEventInfo[].class)) {
|
||||
public PagedIterator<GHEventInfo> _iterator(int pageSize) {
|
||||
return new PagedIterator<GHEventInfo>(root.retrieve().asIterator(String.format("/orgs/%s/events", login), GHEventInfo[].class, pageSize)) {
|
||||
@Override
|
||||
protected void wrapUp(GHEventInfo[] page) {
|
||||
for (GHEventInfo c : page)
|
||||
@@ -244,8 +268,8 @@ public class GHOrganization extends GHPerson {
|
||||
@Override
|
||||
public PagedIterable<GHRepository> listRepositories(final int pageSize) {
|
||||
return new PagedIterable<GHRepository>() {
|
||||
public PagedIterator<GHRepository> iterator() {
|
||||
return new PagedIterator<GHRepository>(root.retrieve().asIterator("/orgs/" + login + "/repos?per_page=" + pageSize, GHRepository[].class)) {
|
||||
public PagedIterator<GHRepository> _iterator(int pageSize) {
|
||||
return new PagedIterator<GHRepository>(root.retrieve().asIterator("/orgs/" + login + "/repos?per_page=" + pageSize, GHRepository[].class, pageSize)) {
|
||||
@Override
|
||||
protected void wrapUp(GHRepository[] page) {
|
||||
for (GHRepository c : page)
|
||||
|
||||
59
src/main/java/org/kohsuke/github/GHPermission.java
Normal file
59
src/main/java/org/kohsuke/github/GHPermission.java
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright 2016 CloudBees, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package org.kohsuke.github;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Permission for a user in a repository.
|
||||
* @see <a href="https://developer.github.com/v3/repos/collaborators/#review-a-users-permission-level">API</a>
|
||||
*/
|
||||
/*package*/ class GHPermission {
|
||||
|
||||
private String permission;
|
||||
private GHUser user;
|
||||
|
||||
/**
|
||||
* @return one of {@code admin}, {@code write}, {@code read}, or {@code none}
|
||||
*/
|
||||
public String getPermission() {
|
||||
return permission;
|
||||
}
|
||||
|
||||
public GHPermissionType getPermissionType() {
|
||||
return Enum.valueOf(GHPermissionType.class, permission.toUpperCase(Locale.ENGLISH));
|
||||
}
|
||||
|
||||
public GHUser getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
void wrapUp(GitHub root) {
|
||||
if (user != null) {
|
||||
user.root = root;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
11
src/main/java/org/kohsuke/github/GHPermissionType.java
Normal file
11
src/main/java/org/kohsuke/github/GHPermissionType.java
Normal file
@@ -0,0 +1,11 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
/**
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
public enum GHPermissionType {
|
||||
ADMIN,
|
||||
WRITE,
|
||||
READ,
|
||||
NONE
|
||||
}
|
||||
@@ -38,8 +38,12 @@ public abstract class GHPerson extends GHObject {
|
||||
* Depending on the original API call where this object is created, it may not contain everything.
|
||||
*/
|
||||
protected synchronized void populate() throws IOException {
|
||||
if (created_at!=null) return; // already populated
|
||||
|
||||
if (created_at!=null) {
|
||||
return; // already populated
|
||||
}
|
||||
if (root == null || root.isOffline()) {
|
||||
return; // cannot populate, will have to live with what we have
|
||||
}
|
||||
root.retrieve().to(url, this);
|
||||
}
|
||||
|
||||
@@ -52,7 +56,7 @@ public abstract class GHPerson extends GHObject {
|
||||
*/
|
||||
public synchronized Map<String,GHRepository> getRepositories() throws IOException {
|
||||
Map<String,GHRepository> repositories = new TreeMap<String, GHRepository>();
|
||||
for (GHRepository r : listRepositories()) {
|
||||
for (GHRepository r : listRepositories(100)) {
|
||||
repositories.put(r.getName(),r);
|
||||
}
|
||||
return Collections.unmodifiableMap(repositories);
|
||||
@@ -76,8 +80,8 @@ public abstract class GHPerson extends GHObject {
|
||||
*/
|
||||
public PagedIterable<GHRepository> listRepositories(final int pageSize) {
|
||||
return new PagedIterable<GHRepository>() {
|
||||
public PagedIterator<GHRepository> iterator() {
|
||||
return new PagedIterator<GHRepository>(root.retrieve().asIterator("/users/" + login + "/repos?per_page=" + pageSize, GHRepository[].class)) {
|
||||
public PagedIterator<GHRepository> _iterator(int pageSize) {
|
||||
return new PagedIterator<GHRepository>(root.retrieve().asIterator("/users/" + login + "/repos?per_page=" + pageSize, GHRepository[].class, pageSize)) {
|
||||
@Override
|
||||
protected void wrapUp(GHRepository[] page) {
|
||||
for (GHRepository c : page)
|
||||
@@ -89,10 +93,10 @@ public abstract class GHPerson extends GHObject {
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads repository list in a pagenated fashion.
|
||||
* Loads repository list in a paginated fashion.
|
||||
*
|
||||
* <p>
|
||||
* For a person with a lot of repositories, GitHub returns the list of repositories in a pagenated fashion.
|
||||
* For a person with a lot of repositories, GitHub returns the list of repositories in a paginated fashion.
|
||||
* Unlike {@link #getRepositories()}, this method allows the caller to start processing data as it arrives.
|
||||
*
|
||||
* Every {@link Iterator#next()} call results in I/O. Exceptions that occur during the processing is wrapped
|
||||
@@ -104,7 +108,7 @@ public abstract class GHPerson extends GHObject {
|
||||
public synchronized Iterable<List<GHRepository>> iterateRepositories(final int pageSize) {
|
||||
return new Iterable<List<GHRepository>>() {
|
||||
public Iterator<List<GHRepository>> iterator() {
|
||||
final Iterator<GHRepository[]> pager = root.retrieve().asIterator("/users/" + login + "/repos?per_page="+pageSize,GHRepository[].class);
|
||||
final Iterator<GHRepository[]> pager = root.retrieve().asIterator("/users/" + login + "/repos?per_page="+pageSize,GHRepository[].class, pageSize);
|
||||
|
||||
return new Iterator<List<GHRepository>>() {
|
||||
public boolean hasNext() {
|
||||
@@ -204,7 +208,7 @@ public abstract class GHPerson extends GHObject {
|
||||
|
||||
public Date getUpdatedAt() throws IOException {
|
||||
populate();
|
||||
return super.getCreatedAt();
|
||||
return super.getUpdatedAt();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,6 +10,8 @@ import java.util.HashSet;
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
public class GHPersonSet<T extends GHPerson> extends HashSet<T> {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public GHPersonSet() {
|
||||
}
|
||||
|
||||
|
||||
@@ -23,20 +23,25 @@
|
||||
*/
|
||||
package org.kohsuke.github;
|
||||
|
||||
import javax.annotation.CheckForNull;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A pull request.
|
||||
*
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
* @see GHRepository#getPullRequest(int)
|
||||
*/
|
||||
@SuppressWarnings({"UnusedDeclaration"})
|
||||
public class GHPullRequest extends GHIssue {
|
||||
|
||||
private static final String COMMENTS_ACTION = "/comments";
|
||||
|
||||
private String patch_url, diff_url, issue_url;
|
||||
private GHCommitPointer base;
|
||||
private String merged_at;
|
||||
@@ -44,12 +49,13 @@ public class GHPullRequest extends GHIssue {
|
||||
|
||||
// details that are only available when obtained from ID
|
||||
private GHUser merged_by;
|
||||
private int review_comments, additions;
|
||||
private boolean merged;
|
||||
private int review_comments, additions, commits;
|
||||
private boolean merged, maintainer_can_modify;
|
||||
private Boolean mergeable;
|
||||
private int deletions;
|
||||
private String mergeable_state;
|
||||
private int changed_files;
|
||||
private String merge_commit_sha;
|
||||
|
||||
/**
|
||||
* GitHub doesn't return some properties of {@link GHIssue} when requesting the GET on the 'pulls' API
|
||||
@@ -84,7 +90,7 @@ public class GHPullRequest extends GHIssue {
|
||||
public URL getPatchUrl() {
|
||||
return GitHub.parseURL(patch_url);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The URL of the patch file.
|
||||
* like https://github.com/jenkinsci/jenkins/pull/100.patch
|
||||
@@ -107,7 +113,7 @@ public class GHPullRequest extends GHIssue {
|
||||
public GHCommitPointer getHead() {
|
||||
return head;
|
||||
}
|
||||
|
||||
|
||||
@Deprecated
|
||||
public Date getIssueUpdatedAt() throws IOException {
|
||||
return super.getUpdatedAt();
|
||||
@@ -142,9 +148,9 @@ public class GHPullRequest extends GHIssue {
|
||||
}
|
||||
|
||||
//
|
||||
// details that are only available via get with ID
|
||||
//
|
||||
//
|
||||
// details that are only available via get with ID
|
||||
//
|
||||
|
||||
public GHUser getMergedBy() throws IOException {
|
||||
populate();
|
||||
return merged_by;
|
||||
@@ -160,13 +166,32 @@ public class GHPullRequest extends GHIssue {
|
||||
return additions;
|
||||
}
|
||||
|
||||
public int getCommits() throws IOException {
|
||||
populate();
|
||||
return commits;
|
||||
}
|
||||
|
||||
public boolean isMerged() throws IOException {
|
||||
populate();
|
||||
return merged;
|
||||
}
|
||||
|
||||
public Boolean getMergeable() throws IOException {
|
||||
public boolean canMaintainerModify() throws IOException {
|
||||
populate();
|
||||
return maintainer_can_modify;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this PR mergeable?
|
||||
*
|
||||
* @return
|
||||
* null if the state has not been determined yet, for example when a PR is newly created.
|
||||
* If this method is called on an instance whose mergeable state is not yet known,
|
||||
* API call is made to retrieve the latest state.
|
||||
*/
|
||||
public Boolean getMergeable() throws IOException {
|
||||
if (mergeable==null)
|
||||
refresh();
|
||||
return mergeable;
|
||||
}
|
||||
|
||||
@@ -185,25 +210,42 @@ public class GHPullRequest extends GHIssue {
|
||||
return changed_files;
|
||||
}
|
||||
|
||||
/**
|
||||
* See <a href="https://developer.github.com/changes/2013-04-25-deprecating-merge-commit-sha">GitHub blog post</a>
|
||||
*/
|
||||
public String getMergeCommitSha() throws IOException {
|
||||
populate();
|
||||
return merge_commit_sha;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fully populate the data by retrieving missing data.
|
||||
*
|
||||
* Depending on the original API call where this object is created, it may not contain everything.
|
||||
*/
|
||||
private void populate() throws IOException {
|
||||
if (merged_by!=null) return; // already populated
|
||||
if (mergeable_state!=null) return; // already populated
|
||||
refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Repopulates this object.
|
||||
*/
|
||||
public void refresh() throws IOException {
|
||||
if (root.isOffline()) {
|
||||
return; // cannot populate, will have to live with what we have
|
||||
}
|
||||
root.retrieve().to(url, this).wrapUp(owner);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all the commits associated to this pull request.
|
||||
* Retrieves all the files associated to this pull request.
|
||||
*/
|
||||
public PagedIterable<GHPullRequestFileDetail> listFiles() {
|
||||
return new PagedIterable<GHPullRequestFileDetail>() {
|
||||
public PagedIterator<GHPullRequestFileDetail> iterator() {
|
||||
return new PagedIterator<GHPullRequestFileDetail>(root.retrieve().asIterator(String.format("%s/files", getApiURL()),
|
||||
GHPullRequestFileDetail[].class)) {
|
||||
public PagedIterator<GHPullRequestFileDetail> _iterator(int pageSize) {
|
||||
return new PagedIterator<GHPullRequestFileDetail>(root.retrieve().asIterator(String.format("%s/files", getApiRoute()),
|
||||
GHPullRequestFileDetail[].class, pageSize)) {
|
||||
@Override
|
||||
protected void wrapUp(GHPullRequestFileDetail[] page) {
|
||||
}
|
||||
@@ -212,14 +254,34 @@ public class GHPullRequest extends GHIssue {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all the reviews associated to this pull request.
|
||||
*/
|
||||
public PagedIterable<GHPullRequestReview> listReviews() {
|
||||
return new PagedIterable<GHPullRequestReview>() {
|
||||
public PagedIterator<GHPullRequestReview> _iterator(int pageSize) {
|
||||
return new PagedIterator<GHPullRequestReview>(root.retrieve()
|
||||
.asIterator(String.format("%s/reviews", getApiRoute()),
|
||||
GHPullRequestReview[].class, pageSize)) {
|
||||
@Override
|
||||
protected void wrapUp(GHPullRequestReview[] page) {
|
||||
for (GHPullRequestReview r: page) {
|
||||
r.wrapUp(GHPullRequest.this);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains all the review comments associated with this pull request.
|
||||
*/
|
||||
public PagedIterable<GHPullRequestReviewComment> listReviewComments() throws IOException {
|
||||
return new PagedIterable<GHPullRequestReviewComment>() {
|
||||
public PagedIterator<GHPullRequestReviewComment> iterator() {
|
||||
return new PagedIterator<GHPullRequestReviewComment>(root.retrieve().asIterator(getApiRoute() + "/comments",
|
||||
GHPullRequestReviewComment[].class)) {
|
||||
public PagedIterator<GHPullRequestReviewComment> _iterator(int pageSize) {
|
||||
return new PagedIterator<GHPullRequestReviewComment>(root.retrieve().asIterator(getApiRoute() + COMMENTS_ACTION,
|
||||
GHPullRequestReviewComment[].class, pageSize)) {
|
||||
protected void wrapUp(GHPullRequestReviewComment[] page) {
|
||||
for (GHPullRequestReviewComment c : page)
|
||||
c.wrapUp(GHPullRequest.this);
|
||||
@@ -234,10 +296,10 @@ public class GHPullRequest extends GHIssue {
|
||||
*/
|
||||
public PagedIterable<GHPullRequestCommitDetail> listCommits() {
|
||||
return new PagedIterable<GHPullRequestCommitDetail>() {
|
||||
public PagedIterator<GHPullRequestCommitDetail> iterator() {
|
||||
public PagedIterator<GHPullRequestCommitDetail> _iterator(int pageSize) {
|
||||
return new PagedIterator<GHPullRequestCommitDetail>(root.retrieve().asIterator(
|
||||
String.format("%s/commits", getApiURL()),
|
||||
GHPullRequestCommitDetail[].class)) {
|
||||
String.format("%s/commits", getApiRoute()),
|
||||
GHPullRequestCommitDetail[].class, pageSize)) {
|
||||
@Override
|
||||
protected void wrapUp(GHPullRequestCommitDetail[] page) {
|
||||
for (GHPullRequestCommitDetail c : page)
|
||||
@@ -248,13 +310,39 @@ public class GHPullRequest extends GHIssue {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* Use {@link #createReview()}
|
||||
*/
|
||||
public GHPullRequestReview createReview(String body, @CheckForNull GHPullRequestReviewState event,
|
||||
GHPullRequestReviewComment... comments) throws IOException {
|
||||
return createReview(body, event, Arrays.asList(comments));
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* Use {@link #createReview()}
|
||||
*/
|
||||
public GHPullRequestReview createReview(String body, @CheckForNull GHPullRequestReviewState event,
|
||||
List<GHPullRequestReviewComment> comments) throws IOException {
|
||||
GHPullRequestReviewBuilder b = createReview().body(body);
|
||||
for (GHPullRequestReviewComment c : comments) {
|
||||
b.comment(c.getBody(), c.getPath(), c.getPosition());
|
||||
}
|
||||
return b.create();
|
||||
}
|
||||
|
||||
public GHPullRequestReviewBuilder createReview() {
|
||||
return new GHPullRequestReviewBuilder(this);
|
||||
}
|
||||
|
||||
public GHPullRequestReviewComment createReviewComment(String body, String sha, String path, int position) throws IOException {
|
||||
return new Requester(root).method("POST")
|
||||
.with("body", body)
|
||||
.with("commit_id", sha)
|
||||
.with("path", path)
|
||||
.with("position", position)
|
||||
.to(getApiRoute() + "/comments", GHPullRequestReviewComment.class).wrapUp(this);
|
||||
.to(getApiRoute() + COMMENTS_ACTION, GHPullRequestReviewComment.class).wrapUp(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -280,9 +368,29 @@ public class GHPullRequest extends GHIssue {
|
||||
* SHA that pull request head must match to allow merge.
|
||||
*/
|
||||
public void merge(String msg, String sha) throws IOException {
|
||||
new Requester(root).method("PUT").with("commit_message",msg).with("sha",sha).to(getApiRoute()+"/merge");
|
||||
merge(msg, sha, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge this pull request, using the specified merge method.
|
||||
*
|
||||
* The equivalent of the big green "Merge pull request" button.
|
||||
*
|
||||
* @param msg
|
||||
* Commit message. If null, the default one will be used.
|
||||
* @param method
|
||||
* SHA that pull request head must match to allow merge.
|
||||
*/
|
||||
public void merge(String msg, String sha, MergeMethod method) throws IOException {
|
||||
new Requester(root).method("PUT")
|
||||
.with("commit_message", msg)
|
||||
.with("sha", sha)
|
||||
.with("merge_method", method)
|
||||
.to(getApiRoute() + "/merge");
|
||||
}
|
||||
|
||||
public enum MergeMethod{ MERGE, SQUASH, REBASE }
|
||||
|
||||
private void fetchIssue() throws IOException {
|
||||
if (!fetchedIssueDetails) {
|
||||
new Requester(root).to(getIssuesApiRoute(), this);
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
@@ -33,6 +34,8 @@ import java.net.URL;
|
||||
* @author Luca Milanesio
|
||||
* @see GHPullRequest#listCommits()
|
||||
*/
|
||||
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
|
||||
"NP_UNWRITTEN_FIELD", "URF_UNREAD_FIELD"}, justification = "JSON API")
|
||||
public class GHPullRequestCommitDetail {
|
||||
private GHPullRequest owner;
|
||||
|
||||
@@ -88,6 +91,10 @@ public class GHPullRequestCommitDetail {
|
||||
public int getComment_count() {
|
||||
return comment_count;
|
||||
}
|
||||
|
||||
public Tree getTree() {
|
||||
return tree;
|
||||
}
|
||||
}
|
||||
|
||||
public static class CommitPointer {
|
||||
@@ -136,6 +143,8 @@ public class GHPullRequestCommitDetail {
|
||||
}
|
||||
|
||||
public CommitPointer[] getParents() {
|
||||
return parents;
|
||||
CommitPointer[] newValue = new CommitPointer[parents.length];
|
||||
System.arraycopy(parents, 0, newValue, 0, parents.length);
|
||||
return newValue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
/**
|
||||
* Lists up pull requests with some filtering and sorting.
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
* @see GHRepository#queryPullRequests()
|
||||
*/
|
||||
public class GHPullRequestQueryBuilder extends GHQueryBuilder<GHPullRequest> {
|
||||
private final GHRepository repo;
|
||||
|
||||
/*package*/ GHPullRequestQueryBuilder(GHRepository repo) {
|
||||
super(repo.root);
|
||||
this.repo = repo;
|
||||
}
|
||||
|
||||
public GHPullRequestQueryBuilder state(GHIssueState state) {
|
||||
req.with("state",state);
|
||||
return this;
|
||||
}
|
||||
|
||||
public GHPullRequestQueryBuilder head(String head) {
|
||||
req.with("head",head);
|
||||
return this;
|
||||
}
|
||||
|
||||
public GHPullRequestQueryBuilder base(String base) {
|
||||
req.with("base",base);
|
||||
return this;
|
||||
}
|
||||
|
||||
public GHPullRequestQueryBuilder sort(Sort sort) {
|
||||
req.with("sort",sort);
|
||||
return this;
|
||||
}
|
||||
|
||||
public enum Sort { CREATED, UPDATED, POPULARITY, LONG_RUNNING }
|
||||
|
||||
public GHPullRequestQueryBuilder direction(GHDirection d) {
|
||||
req.with("direction",d);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PagedIterable<GHPullRequest> list() {
|
||||
return new PagedIterable<GHPullRequest>() {
|
||||
public PagedIterator<GHPullRequest> _iterator(int pageSize) {
|
||||
return new PagedIterator<GHPullRequest>(req.asIterator(repo.getApiTailUrl("pulls"), GHPullRequest[].class, pageSize)) {
|
||||
@Override
|
||||
protected void wrapUp(GHPullRequest[] page) {
|
||||
for (GHPullRequest pr : page)
|
||||
pr.wrapUp(repo);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
148
src/main/java/org/kohsuke/github/GHPullRequestReview.java
Normal file
148
src/main/java/org/kohsuke/github/GHPullRequestReview.java
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, CloudBees, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package org.kohsuke.github;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
|
||||
import javax.annotation.CheckForNull;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* Review to a pull request.
|
||||
*
|
||||
* @see GHPullRequest#listReviews()
|
||||
* @see GHPullRequestReviewBuilder
|
||||
*/
|
||||
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_FIELD"}, justification = "JSON API")
|
||||
public class GHPullRequestReview extends GHObject {
|
||||
GHPullRequest owner;
|
||||
|
||||
private String body;
|
||||
private GHUser user;
|
||||
private String commit_id;
|
||||
private GHPullRequestReviewState state;
|
||||
|
||||
/*package*/ GHPullRequestReview wrapUp(GHPullRequest owner) {
|
||||
this.owner = owner;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the pull request to which this review is associated.
|
||||
*/
|
||||
public GHPullRequest getParent() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
/**
|
||||
* The comment itself.
|
||||
*/
|
||||
public String getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the user who posted this review.
|
||||
*/
|
||||
public GHUser getUser() throws IOException {
|
||||
return owner.root.getUser(user.getLogin());
|
||||
}
|
||||
|
||||
public String getCommitId() {
|
||||
return commit_id;
|
||||
}
|
||||
|
||||
@CheckForNull
|
||||
public GHPullRequestReviewState getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getHtmlUrl() {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected String getApiRoute() {
|
||||
return owner.getApiRoute()+"/reviews/"+id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* Former preview method that changed when it got public. Left here for backward compatibility.
|
||||
* Use {@link #submit(String, GHPullRequestReviewEvent)}
|
||||
*/
|
||||
public void submit(String body, GHPullRequestReviewState state) throws IOException {
|
||||
submit(body,state.toEvent());
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the comment.
|
||||
*/
|
||||
public void submit(String body, GHPullRequestReviewEvent event) throws IOException {
|
||||
new Requester(owner.root).method("POST")
|
||||
.with("body", body)
|
||||
.with("event", event.action())
|
||||
.to(getApiRoute()+"/events",this);
|
||||
this.body = body;
|
||||
this.state = event.toState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes this review.
|
||||
*/
|
||||
public void delete() throws IOException {
|
||||
new Requester(owner.root).method("DELETE")
|
||||
.to(getApiRoute());
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismisses this review.
|
||||
*/
|
||||
public void dismiss(String message) throws IOException {
|
||||
new Requester(owner.root).method("PUT")
|
||||
.with("message", message)
|
||||
.to(getApiRoute()+"/dismissals");
|
||||
state = GHPullRequestReviewState.DISMISSED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains all the review comments associated with this pull request review.
|
||||
*/
|
||||
public PagedIterable<GHPullRequestReviewComment> listReviewComments() throws IOException {
|
||||
return new PagedIterable<GHPullRequestReviewComment>() {
|
||||
public PagedIterator<GHPullRequestReviewComment> _iterator(int pageSize) {
|
||||
return new PagedIterator<GHPullRequestReviewComment>(
|
||||
owner.root.retrieve()
|
||||
.asIterator(getApiRoute() + "/comments",
|
||||
GHPullRequestReviewComment[].class, pageSize)) {
|
||||
protected void wrapUp(GHPullRequestReviewComment[] page) {
|
||||
for (GHPullRequestReviewComment c : page)
|
||||
c.wrapUp(owner);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Builds up a creation of new {@link GHPullRequestReview}.
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
* @see GHPullRequest#createReview()
|
||||
*/
|
||||
public class GHPullRequestReviewBuilder {
|
||||
private final GHPullRequest pr;
|
||||
private final Requester builder;
|
||||
private final List<DraftReviewComment> comments = new ArrayList<DraftReviewComment>();
|
||||
|
||||
/*package*/ GHPullRequestReviewBuilder(GHPullRequest pr) {
|
||||
this.pr = pr;
|
||||
this.builder = new Requester(pr.root);
|
||||
}
|
||||
|
||||
// public GHPullRequestReview createReview(@Nullable String commitId, String body, GHPullRequestReviewEvent event,
|
||||
// List<GHPullRequestReviewComment> comments) throws IOException
|
||||
|
||||
/**
|
||||
* The SHA of the commit that needs a review. Not using the latest commit SHA may render your review comment outdated if a subsequent commit modifies the line you specify as the position. Defaults to the most recent commit in the pull request when you do not specify a value.
|
||||
*/
|
||||
public GHPullRequestReviewBuilder commitId(String commitId) {
|
||||
builder.with("commit_id",commitId);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Required when using REQUEST_CHANGES or COMMENT for the event parameter. The body text of the pull request review.
|
||||
*/
|
||||
public GHPullRequestReviewBuilder body(String body) {
|
||||
builder.with("body",body);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The review action you want to perform. The review actions include: APPROVE, REQUEST_CHANGES, or COMMENT.
|
||||
* By leaving this blank, you set the review action state to PENDING,
|
||||
* which means you will need to {@linkplain GHPullRequestReview#submit() submit the pull request review} when you are ready.
|
||||
*/
|
||||
public GHPullRequestReviewBuilder event(GHPullRequestReviewEvent event) {
|
||||
builder.with("event",event.action());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param body The relative path to the file that necessitates a review comment.
|
||||
* @param path The position in the diff where you want to add a review comment. Note this value is not the same as the line number in the file. For help finding the position value, read the note below.
|
||||
* @param position Text of the review comment.
|
||||
*/
|
||||
public GHPullRequestReviewBuilder comment(String body, String path, int position) {
|
||||
comments.add(new DraftReviewComment(body,path,position));
|
||||
return this;
|
||||
}
|
||||
|
||||
public GHPullRequestReview create() throws IOException {
|
||||
return builder.method("POST")._with("comments",comments)
|
||||
.to(pr.getApiRoute() + "/reviews", GHPullRequestReview.class)
|
||||
.wrapUp(pr);
|
||||
}
|
||||
|
||||
private static class DraftReviewComment {
|
||||
private String body;
|
||||
private String path;
|
||||
private int position;
|
||||
|
||||
DraftReviewComment(String body, String path, int position) {
|
||||
this.body = body;
|
||||
this.path = path;
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
public String getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public int getPosition() {
|
||||
return position;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,9 @@ package org.kohsuke.github;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import javax.annotation.CheckForNull;
|
||||
|
||||
import static org.kohsuke.github.Previews.*;
|
||||
|
||||
/**
|
||||
* Review comment to the pull request
|
||||
@@ -33,14 +36,28 @@ import java.net.URL;
|
||||
* @see GHPullRequest#listReviewComments()
|
||||
* @see GHPullRequest#createReviewComment(String, String, String, int)
|
||||
*/
|
||||
public class GHPullRequestReviewComment extends GHObject {
|
||||
public class GHPullRequestReviewComment extends GHObject implements Reactable {
|
||||
GHPullRequest owner;
|
||||
|
||||
private String body;
|
||||
private GHUser user;
|
||||
private String path;
|
||||
private int position;
|
||||
private int originalPosition;
|
||||
private int position = -1;
|
||||
private int original_position = -1;
|
||||
private long in_reply_to_id = -1L;
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* You should be using {@link GHPullRequestReviewBuilder#comment(String, String, int)}
|
||||
*/
|
||||
public static GHPullRequestReviewComment draft(String body, String path, int position) {
|
||||
GHPullRequestReviewComment result = new GHPullRequestReviewComment();
|
||||
result.body = body;
|
||||
result.path = path;
|
||||
result.position = position;
|
||||
return result;
|
||||
}
|
||||
|
||||
/*package*/ GHPullRequestReviewComment wrapUp(GHPullRequest owner) {
|
||||
this.owner = owner;
|
||||
@@ -72,12 +89,18 @@ public class GHPullRequestReviewComment extends GHObject {
|
||||
return path;
|
||||
}
|
||||
|
||||
@CheckForNull
|
||||
public int getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
public int getOriginalPosition() {
|
||||
return originalPosition;
|
||||
return original_position;
|
||||
}
|
||||
|
||||
@CheckForNull
|
||||
public long getInReplyToId() {
|
||||
return in_reply_to_id;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -103,4 +126,38 @@ public class GHPullRequestReviewComment extends GHObject {
|
||||
public void delete() throws IOException {
|
||||
new Requester(owner.root).method("DELETE").to(getApiRoute());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new comment that replies to this comment.
|
||||
*/
|
||||
public GHPullRequestReviewComment reply(String body) throws IOException {
|
||||
return new Requester(owner.root).method("POST")
|
||||
.with("body", body)
|
||||
.with("in_reply_to", getId())
|
||||
.to(getApiRoute() + "/comments", GHPullRequestReviewComment.class)
|
||||
.wrapUp(owner);
|
||||
}
|
||||
|
||||
@Preview @Deprecated
|
||||
public GHReaction createReaction(ReactionContent content) throws IOException {
|
||||
return new Requester(owner.root)
|
||||
.withPreview(SQUIRREL_GIRL)
|
||||
.with("content", content.getContent())
|
||||
.to(getApiRoute()+"/reactions", GHReaction.class).wrap(owner.root);
|
||||
}
|
||||
|
||||
@Preview @Deprecated
|
||||
public PagedIterable<GHReaction> listReactions() {
|
||||
return new PagedIterable<GHReaction>() {
|
||||
public PagedIterator<GHReaction> _iterator(int pageSize) {
|
||||
return new PagedIterator<GHReaction>(owner.root.retrieve().withPreview(SQUIRREL_GIRL).asIterator(getApiRoute() + "/reactions", GHReaction[].class, pageSize)) {
|
||||
@Override
|
||||
protected void wrapUp(GHReaction[] page) {
|
||||
for (GHReaction c : page)
|
||||
c.wrap(owner.root);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2011, Eric Maupin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package org.kohsuke.github;
|
||||
|
||||
/**
|
||||
* Action to perform on {@link GHPullRequestReview}.
|
||||
*/
|
||||
public enum GHPullRequestReviewEvent {
|
||||
PENDING,
|
||||
APPROVE,
|
||||
REQUEST_CHANGES,
|
||||
COMMENT;
|
||||
|
||||
/*package*/ String action() {
|
||||
return this==PENDING ? null : name();
|
||||
}
|
||||
|
||||
/**
|
||||
* When a {@link GHPullRequestReview} is submitted with this event, it should transition to this state.
|
||||
*/
|
||||
/*package*/ GHPullRequestReviewState toState() {
|
||||
switch (this) {
|
||||
case PENDING: return GHPullRequestReviewState.PENDING;
|
||||
case APPROVE: return GHPullRequestReviewState.APPROVED;
|
||||
case REQUEST_CHANGES: return GHPullRequestReviewState.CHANGES_REQUESTED;
|
||||
case COMMENT: return GHPullRequestReviewState.COMMENTED;
|
||||
}
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
/**
|
||||
* Current state of {@link GHPullRequestReview}
|
||||
*/
|
||||
public enum GHPullRequestReviewState {
|
||||
PENDING,
|
||||
APPROVED,
|
||||
CHANGES_REQUESTED,
|
||||
/**
|
||||
* @deprecated
|
||||
* This was the thing when this API was in preview, but it changed when it became public.
|
||||
* Use {@link #CHANGES_REQUESTED}. Left here for compatibility.
|
||||
*/
|
||||
REQUEST_CHANGES,
|
||||
COMMENTED,
|
||||
DISMISSED;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* This was an internal method accidentally exposed.
|
||||
* Left here for compatibility.
|
||||
*/
|
||||
public String action() {
|
||||
GHPullRequestReviewEvent e = toEvent();
|
||||
return e==null ? null : e.action();
|
||||
}
|
||||
|
||||
/*package*/ GHPullRequestReviewEvent toEvent() {
|
||||
switch (this) {
|
||||
case PENDING: return GHPullRequestReviewEvent.PENDING;
|
||||
case APPROVED: return GHPullRequestReviewEvent.APPROVE;
|
||||
case CHANGES_REQUESTED: return GHPullRequestReviewEvent.REQUEST_CHANGES;
|
||||
case REQUEST_CHANGES: return GHPullRequestReviewEvent.REQUEST_CHANGES;
|
||||
case COMMENTED: return GHPullRequestReviewEvent.COMMENT;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
21
src/main/java/org/kohsuke/github/GHQueryBuilder.java
Normal file
21
src/main/java/org/kohsuke/github/GHQueryBuilder.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
/**
|
||||
* Used to specify filters, sort order, etc for listing items in a collection.
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
public abstract class GHQueryBuilder<T> {
|
||||
protected final GitHub root;
|
||||
protected final Requester req;
|
||||
|
||||
/*package*/ GHQueryBuilder(GitHub root) {
|
||||
this.root = root;
|
||||
this.req = root.retrieve();
|
||||
}
|
||||
|
||||
/**
|
||||
* Start listing items by using the settings built up on this object.
|
||||
*/
|
||||
public abstract PagedIterable<T> list();
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
@@ -24,6 +26,8 @@ public class GHRateLimit {
|
||||
/**
|
||||
* Non-epoch date
|
||||
*/
|
||||
@SuppressFBWarnings(value = "UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR",
|
||||
justification = "The value comes from JSON deserialization")
|
||||
public Date getResetDate() {
|
||||
return new Date(reset.getTime() * 1000);
|
||||
}
|
||||
|
||||
55
src/main/java/org/kohsuke/github/GHReaction.java
Normal file
55
src/main/java/org/kohsuke/github/GHReaction.java
Normal file
@@ -0,0 +1,55 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
import static org.kohsuke.github.Previews.*;
|
||||
|
||||
/**
|
||||
* Reaction to issue, comment, PR, and so on.
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
* @see Reactable
|
||||
*/
|
||||
@Preview @Deprecated
|
||||
public class GHReaction extends GHObject {
|
||||
private GitHub root;
|
||||
|
||||
private GHUser user;
|
||||
private ReactionContent content;
|
||||
|
||||
/*package*/ GHReaction wrap(GitHub root) {
|
||||
this.root = root;
|
||||
user.wrapUp(root);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The kind of reaction left.
|
||||
*/
|
||||
public ReactionContent getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
/**
|
||||
* User who left the reaction.
|
||||
*/
|
||||
public GHUser getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reaction has no HTML URL. Don't call this method.
|
||||
*/
|
||||
@Deprecated
|
||||
public URL getHtmlUrl() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes this reaction.
|
||||
*/
|
||||
public void delete() throws IOException {
|
||||
new Requester(root).method("DELETE").withPreview(SQUIRREL_GIRL).to("/reactions/"+id);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
@@ -77,7 +79,8 @@ public class GHRef {
|
||||
return in;
|
||||
}
|
||||
|
||||
|
||||
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
|
||||
"NP_UNWRITTEN_FIELD"}, justification = "JSON API")
|
||||
public static class GHObject {
|
||||
private String type, sha, url;
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static java.lang.String.*;
|
||||
|
||||
/**
|
||||
* Release in a github repository.
|
||||
@@ -45,6 +45,14 @@ public class GHRelease extends GHObject {
|
||||
return draft;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* Use {@link #update()}
|
||||
*/
|
||||
public GHRelease setDraft(boolean draft) throws IOException {
|
||||
return update().draft(draft).update();
|
||||
}
|
||||
|
||||
public URL getHtmlUrl() {
|
||||
return GitHub.parseURL(html_url);
|
||||
}
|
||||
@@ -70,7 +78,7 @@ public class GHRelease extends GHObject {
|
||||
}
|
||||
|
||||
public Date getPublished_at() {
|
||||
return published_at;
|
||||
return new Date(published_at.getTime());
|
||||
}
|
||||
|
||||
public GitHub getRoot() {
|
||||
@@ -115,9 +123,7 @@ public class GHRelease extends GHObject {
|
||||
* Java 7 or greater. Options for fixing this for earlier JVMs can be found here
|
||||
* http://stackoverflow.com/questions/12361090/server-name-indication-sni-on-java but involve more complicated
|
||||
* handling of the HTTP requests to github's API.
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
*/
|
||||
public GHAsset uploadAsset(File file, String contentType) throws IOException {
|
||||
Requester builder = new Requester(owner.root);
|
||||
|
||||
@@ -144,6 +150,13 @@ public class GHRelease extends GHObject {
|
||||
new Requester(root).method("DELETE").to(owner.getApiTailUrl("releases/"+id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates this release via a builder.
|
||||
*/
|
||||
public GHReleaseUpdater update() {
|
||||
return new GHReleaseUpdater(this);
|
||||
}
|
||||
|
||||
private String getApiTailUrl(String end) {
|
||||
return owner.getApiTailUrl(format("releases/%s/%s",id,end));
|
||||
}
|
||||
|
||||
@@ -21,9 +21,7 @@ public class GHReleaseBuilder {
|
||||
* @param body The release notes body.
|
||||
*/
|
||||
public GHReleaseBuilder body(String body) {
|
||||
if (body != null) {
|
||||
builder.with("body", body);
|
||||
}
|
||||
builder.with("body", body);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -33,12 +31,9 @@ public class GHReleaseBuilder {
|
||||
*
|
||||
* @param commitish Defaults to the repository’s default branch (usually "master"). Unused if the Git tag
|
||||
* already exists.
|
||||
* @return
|
||||
*/
|
||||
public GHReleaseBuilder commitish(String commitish) {
|
||||
if (commitish != null) {
|
||||
builder.with("target_commitish", commitish);
|
||||
}
|
||||
builder.with("target_commitish", commitish);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -57,9 +52,7 @@ public class GHReleaseBuilder {
|
||||
* @param name the name of the release
|
||||
*/
|
||||
public GHReleaseBuilder name(String name) {
|
||||
if (name != null) {
|
||||
builder.with("name", name);
|
||||
}
|
||||
builder.with("name", name);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
81
src/main/java/org/kohsuke/github/GHReleaseUpdater.java
Normal file
81
src/main/java/org/kohsuke/github/GHReleaseUpdater.java
Normal file
@@ -0,0 +1,81 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Modifies {@link GHRelease}.
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
* @see GHRelease#update()
|
||||
*/
|
||||
public class GHReleaseUpdater {
|
||||
private final GHRelease base;
|
||||
private final Requester builder;
|
||||
|
||||
GHReleaseUpdater(GHRelease base) {
|
||||
this.base = base;
|
||||
this.builder = new Requester(base.root);
|
||||
}
|
||||
|
||||
public GHReleaseUpdater tag(String tag) {
|
||||
builder.with("tag_name",tag);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param body The release notes body.
|
||||
*/
|
||||
public GHReleaseUpdater body(String body) {
|
||||
builder.with("body", body);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the commitish value that determines where the Git tag is created from. Can be any branch or
|
||||
* commit SHA.
|
||||
*
|
||||
* @param commitish Defaults to the repository’s default branch (usually "master"). Unused if the Git tag
|
||||
* already exists.
|
||||
*/
|
||||
public GHReleaseUpdater commitish(String commitish) {
|
||||
builder.with("target_commitish", commitish);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional.
|
||||
*
|
||||
* @param draft {@code true} to create a draft (unpublished) release, {@code false} to create a published one.
|
||||
* Default is {@code false}.
|
||||
*/
|
||||
public GHReleaseUpdater draft(boolean draft) {
|
||||
builder.with("draft", draft);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name the name of the release
|
||||
*/
|
||||
public GHReleaseUpdater name(String name) {
|
||||
builder.with("name", name);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional
|
||||
*
|
||||
* @param prerelease {@code true} to identify the release as a prerelease. {@code false} to identify the release
|
||||
* as a full release. Default is {@code false}.
|
||||
*/
|
||||
public GHReleaseUpdater prerelease(boolean prerelease) {
|
||||
builder.with("prerelease", prerelease);
|
||||
return this;
|
||||
}
|
||||
|
||||
public GHRelease update() throws IOException {
|
||||
return builder
|
||||
.method("PATCH")
|
||||
.to(base.owner.getApiTailUrl("releases/"+base.id), GHRelease.class).wrap(base.owner);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -25,18 +25,34 @@ package org.kohsuke.github;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.io.Reader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URL;
|
||||
import java.util.*;
|
||||
import java.util.AbstractSet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.Arrays.*;
|
||||
import static org.kohsuke.github.Previews.*;
|
||||
|
||||
/**
|
||||
* A repository on GitHub.
|
||||
@@ -44,23 +60,33 @@ import static java.util.Arrays.asList;
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
@SuppressWarnings({"UnusedDeclaration"})
|
||||
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
|
||||
"NP_UNWRITTEN_FIELD"}, justification = "JSON API")
|
||||
public class GHRepository extends GHObject {
|
||||
/*package almost final*/ GitHub root;
|
||||
|
||||
private String description, homepage, name, full_name;
|
||||
private String html_url; // this is the UI
|
||||
private String git_url, ssh_url, clone_url, svn_url;
|
||||
/*
|
||||
* The license information makes use of the preview API.
|
||||
*
|
||||
* See: https://developer.github.com/v3/licenses/
|
||||
*/
|
||||
private GHLicense license;
|
||||
|
||||
private String git_url, ssh_url, clone_url, svn_url, mirror_url;
|
||||
private GHUser owner; // not fully populated. beware.
|
||||
private boolean has_issues, has_wiki, fork, has_downloads;
|
||||
private boolean has_issues, has_wiki, fork, has_downloads, has_pages;
|
||||
@JsonProperty("private")
|
||||
private boolean _private;
|
||||
private int watchers,forks,open_issues,size,network_count,subscribers_count;
|
||||
private int forks_count, stargazers_count, watchers_count, size, open_issues_count, subscribers_count;
|
||||
private String pushed_at;
|
||||
private Map<Integer,GHMilestone> milestones = new HashMap<Integer, GHMilestone>();
|
||||
|
||||
private String default_branch,language;
|
||||
private Map<String,GHCommit> commits = new HashMap<String, GHCommit>();
|
||||
|
||||
@SkipFromToString
|
||||
private GHRepoPermission permissions;
|
||||
|
||||
private GHRepository source, parent;
|
||||
@@ -69,26 +95,20 @@ public class GHRepository extends GHObject {
|
||||
return new GHDeploymentBuilder(this,ref);
|
||||
}
|
||||
|
||||
public PagedIterable<GHDeploymentStatus> getDeploymentStatuses(final int id) {
|
||||
return new PagedIterable<GHDeploymentStatus>() {
|
||||
public PagedIterator<GHDeploymentStatus> iterator() {
|
||||
return new PagedIterator<GHDeploymentStatus>(root.retrieve().asIterator(getApiTailUrl("deployments")+"/"+id+"/statuses", GHDeploymentStatus[].class)) {
|
||||
@Override
|
||||
protected void wrapUp(GHDeploymentStatus[] page) {
|
||||
for (GHDeploymentStatus c : page)
|
||||
c.wrap(GHRepository.this);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
/**
|
||||
* @deprecated
|
||||
* Use {@code getDeployment(id).listStatuses()}
|
||||
*/
|
||||
public PagedIterable<GHDeploymentStatus> getDeploymentStatuses(final int id) throws IOException {
|
||||
return getDeployment(id).listStatuses();
|
||||
}
|
||||
|
||||
public PagedIterable<GHDeployment> listDeployments(String sha,String ref,String task,String environment){
|
||||
List<String> params = Arrays.asList(getParam("sha", sha), getParam("ref", ref), getParam("task", task), getParam("environment", environment));
|
||||
final String deploymentsUrl = getApiTailUrl("deployments") + "?"+ join(params,"&");
|
||||
return new PagedIterable<GHDeployment>() {
|
||||
public PagedIterator<GHDeployment> iterator() {
|
||||
return new PagedIterator<GHDeployment>(root.retrieve().asIterator(deploymentsUrl, GHDeployment[].class)) {
|
||||
public PagedIterator<GHDeployment> _iterator(int pageSize) {
|
||||
return new PagedIterator<GHDeployment>(root.retrieve().asIterator(deploymentsUrl, GHDeployment[].class, pageSize)) {
|
||||
@Override
|
||||
protected void wrapUp(GHDeployment[] page) {
|
||||
for (GHDeployment c : page)
|
||||
@@ -97,7 +117,13 @@ public class GHRepository extends GHObject {
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains a single {@link GHDeployment} by its ID.
|
||||
*/
|
||||
public GHDeployment getDeployment(long id) throws IOException {
|
||||
return root.retrieve().to("deployments/" + id, GHDeployment.class).wrap(this);
|
||||
}
|
||||
|
||||
private String join(List<String> params, String joinStr) {
|
||||
@@ -114,8 +140,12 @@ public class GHRepository extends GHObject {
|
||||
return StringUtils.trimToNull(value)== null? null: name+"="+value;
|
||||
}
|
||||
|
||||
public GHDeploymentStatusBuilder createDeployStatus(int deploymentId, GHDeploymentState ghDeploymentState) {
|
||||
return new GHDeploymentStatusBuilder(this,deploymentId,ghDeploymentState);
|
||||
/**
|
||||
* @deprecated
|
||||
* Use {@code getDeployment(deploymentId).createStatus(ghDeploymentState)}
|
||||
*/
|
||||
public GHDeploymentStatusBuilder createDeployStatus(int deploymentId, GHDeploymentState ghDeploymentState) throws IOException {
|
||||
return getDeployment(deploymentId).createStatus(ghDeploymentState);
|
||||
}
|
||||
|
||||
private static class GHRepoPermission {
|
||||
@@ -143,6 +173,14 @@ public class GHRepository extends GHObject {
|
||||
* Gets the HTTPS URL to this repository, such as "https://github.com/kohsuke/jenkins.git"
|
||||
* This URL is read-only.
|
||||
*/
|
||||
public String getHttpTransportUrl() {
|
||||
return clone_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* Typo of {@link #getHttpTransportUrl()}
|
||||
*/
|
||||
public String gitHttpTransportUrl() {
|
||||
return clone_url;
|
||||
}
|
||||
@@ -154,6 +192,14 @@ public class GHRepository extends GHObject {
|
||||
return svn_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Mirror URL to access this repository: https://github.com/apache/tomee
|
||||
* mirrored from git://git.apache.org/tomee.git
|
||||
*/
|
||||
public String getMirrorUrl() {
|
||||
return mirror_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the SSH URL to access this repository, such as git@github.com:rails/rails.git
|
||||
*/
|
||||
@@ -199,7 +245,7 @@ public class GHRepository extends GHObject {
|
||||
}
|
||||
|
||||
public GHUser getOwner() throws IOException {
|
||||
return root.getUser(owner.login); // because 'owner' isn't fully populated
|
||||
return root.isOffline() ? owner : root.getUser(getOwnerName()); // because 'owner' isn't fully populated
|
||||
}
|
||||
|
||||
public GHIssue getIssue(int id) throws IOException {
|
||||
@@ -216,10 +262,10 @@ public class GHRepository extends GHObject {
|
||||
|
||||
public List<GHIssue> getIssues(GHIssueState state, GHMilestone milestone) throws IOException {
|
||||
return Arrays.asList(GHIssue.wrap(root.retrieve()
|
||||
.to(getApiTailUrl(String.format("issues?state=%s&milestone=%s",
|
||||
state.toString().toLowerCase(), milestone == null ? "none" : "" + milestone.getNumber())),
|
||||
GHIssue[].class
|
||||
), this));
|
||||
.with("state", state)
|
||||
.with("milestone", milestone == null ? "none" : "" + milestone.getNumber())
|
||||
.to(getApiTailUrl("issues"),
|
||||
GHIssue[].class), this));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -227,8 +273,8 @@ public class GHRepository extends GHObject {
|
||||
*/
|
||||
public PagedIterable<GHIssue> listIssues(final GHIssueState state) {
|
||||
return new PagedIterable<GHIssue>() {
|
||||
public PagedIterator<GHIssue> iterator() {
|
||||
return new PagedIterator<GHIssue>(root.retrieve().asIterator(getApiTailUrl("issues?state="+state.toString().toLowerCase(Locale.ENGLISH)), GHIssue[].class)) {
|
||||
public PagedIterator<GHIssue> _iterator(int pageSize) {
|
||||
return new PagedIterator<GHIssue>(root.retrieve().with("state",state).asIterator(getApiTailUrl("issues"), GHIssue[].class, pageSize)) {
|
||||
@Override
|
||||
protected void wrapUp(GHIssue[] page) {
|
||||
for (GHIssue c : page)
|
||||
@@ -264,11 +310,19 @@ public class GHRepository extends GHObject {
|
||||
public List<GHRelease> getReleases() throws IOException {
|
||||
return listReleases().asList();
|
||||
}
|
||||
|
||||
public GHRelease getLatestRelease() throws IOException {
|
||||
try {
|
||||
return root.retrieve().to(getApiTailUrl("releases/latest"), GHRelease.class).wrap(this);
|
||||
} catch (FileNotFoundException e) {
|
||||
return null; // no latest release
|
||||
}
|
||||
}
|
||||
|
||||
public PagedIterable<GHRelease> listReleases() throws IOException {
|
||||
return new PagedIterable<GHRelease>() {
|
||||
public PagedIterator<GHRelease> iterator() {
|
||||
return new PagedIterator<GHRelease>(root.retrieve().asIterator(getApiTailUrl("releases"), GHRelease[].class)) {
|
||||
public PagedIterator<GHRelease> _iterator(int pageSize) {
|
||||
return new PagedIterator<GHRelease>(root.retrieve().asIterator(getApiTailUrl("releases"), GHRelease[].class, pageSize)) {
|
||||
@Override
|
||||
protected void wrapUp(GHRelease[] page) {
|
||||
for (GHRelease c : page)
|
||||
@@ -281,8 +335,8 @@ public class GHRepository extends GHObject {
|
||||
|
||||
public PagedIterable<GHTag> listTags() throws IOException {
|
||||
return new PagedIterable<GHTag>() {
|
||||
public PagedIterator<GHTag> iterator() {
|
||||
return new PagedIterator<GHTag>(root.retrieve().asIterator(getApiTailUrl("tags"), GHTag[].class)) {
|
||||
public PagedIterator<GHTag> _iterator(int pageSize) {
|
||||
return new PagedIterator<GHTag>(root.retrieve().asIterator(getApiTailUrl("tags"), GHTag[].class, pageSize)) {
|
||||
@Override
|
||||
protected void wrapUp(GHTag[] page) {
|
||||
for (GHTag c : page)
|
||||
@@ -306,7 +360,11 @@ public class GHRepository extends GHObject {
|
||||
}
|
||||
|
||||
public String getOwnerName() {
|
||||
return owner.login;
|
||||
// consistency of the GitHub API is super... some serialized forms of GHRepository populate
|
||||
// a full GHUser while others populate only the owner and email. This later form is super helpful
|
||||
// in putting the login in owner.name not owner.login... thankfully we can easily identify this
|
||||
// second set because owner.login will be null
|
||||
return owner.login != null ? owner.login : owner.name;
|
||||
}
|
||||
|
||||
public boolean hasIssues() {
|
||||
@@ -326,7 +384,11 @@ public class GHRepository extends GHObject {
|
||||
* This not only counts direct forks, but also forks of forks, and so on.
|
||||
*/
|
||||
public int getForks() {
|
||||
return forks;
|
||||
return forks_count;
|
||||
}
|
||||
|
||||
public int getStargazersCount() {
|
||||
return stargazers_count;
|
||||
}
|
||||
|
||||
public boolean isPrivate() {
|
||||
@@ -337,16 +399,25 @@ public class GHRepository extends GHObject {
|
||||
return has_downloads;
|
||||
}
|
||||
|
||||
public boolean hasPages() {
|
||||
return has_pages;
|
||||
}
|
||||
|
||||
public int getWatchers() {
|
||||
return watchers;
|
||||
return watchers_count;
|
||||
}
|
||||
|
||||
public int getOpenIssueCount() {
|
||||
return open_issues;
|
||||
return open_issues_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* This no longer exists in the official API documentation.
|
||||
* Use {@link #getForks()}
|
||||
*/
|
||||
public int getNetworkCount() {
|
||||
return network_count;
|
||||
return forks_count;
|
||||
}
|
||||
|
||||
public int getSubscribersCount() {
|
||||
@@ -384,6 +455,7 @@ public class GHRepository extends GHObject {
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the collaborators on this repository.
|
||||
* This set always appear to include the owner.
|
||||
@@ -397,25 +469,24 @@ public class GHRepository extends GHObject {
|
||||
* Lists up the collaborators on this repository.
|
||||
*
|
||||
* @return Users
|
||||
* @throws IOException
|
||||
*/
|
||||
public PagedIterable<GHUser> listCollaborators() throws IOException {
|
||||
return new PagedIterable<GHUser>() {
|
||||
public PagedIterator<GHUser> iterator() {
|
||||
return listUsers("collaborators");
|
||||
}
|
||||
|
||||
return new PagedIterator<GHUser>(root.retrieve().asIterator(getApiTailUrl("collaborators"), GHUser[].class)) {
|
||||
|
||||
@Override
|
||||
protected void wrapUp(GHUser[] users) {
|
||||
for (GHUser user : users) {
|
||||
user.wrapUp(root);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Lists all <a href="https://help.github.com/articles/assigning-issues-and-pull-requests-to-other-github-users/">the available assignees</a>
|
||||
* to which issues may be assigned.
|
||||
*/
|
||||
public PagedIterable<GHUser> listAssignees() throws IOException {
|
||||
return listUsers("assignees");
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given user is an assignee for this repository.
|
||||
*/
|
||||
public boolean hasAssignee(GHUser u) throws IOException {
|
||||
return root.retrieve().asHttpStatusCode(getApiTailUrl("assignees/" + u.getLogin()))/100==2;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -429,11 +500,32 @@ public class GHRepository extends GHObject {
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain permission for a given user in this repository.
|
||||
* @param user a {@link GHUser#getLogin}
|
||||
* @throws FileNotFoundException under some conditions (e.g., private repo you can see but are not an admin of); treat as unknown
|
||||
* @throws HttpException with a 403 under other conditions (e.g., public repo you have no special rights to); treat as unknown
|
||||
*/
|
||||
public GHPermissionType getPermission(String user) throws IOException {
|
||||
GHPermission perm = root.retrieve().to(getApiTailUrl("collaborators/" + user + "/permission"), GHPermission.class);
|
||||
perm.wrapUp(root);
|
||||
return perm.getPermissionType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain permission for a given user in this repository.
|
||||
* @throws FileNotFoundException under some conditions (e.g., private repo you can see but are not an admin of); treat as unknown
|
||||
* @throws HttpException with a 403 under other conditions (e.g., public repo you have no special rights to); treat as unknown
|
||||
*/
|
||||
public GHPermissionType getPermission(GHUser u) throws IOException {
|
||||
return getPermission(u.getLogin());
|
||||
}
|
||||
|
||||
/**
|
||||
* If this repository belongs to an organization, return a set of teams.
|
||||
*/
|
||||
public Set<GHTeam> getTeams() throws IOException {
|
||||
return Collections.unmodifiableSet(new HashSet<GHTeam>(Arrays.asList(GHTeam.wrapUp(root.retrieve().to(getApiTailUrl("teams"), GHTeam[].class), root.getOrganization(owner.login)))));
|
||||
return Collections.unmodifiableSet(new HashSet<GHTeam>(Arrays.asList(GHTeam.wrapUp(root.retrieve().to(getApiTailUrl("teams"), GHTeam[].class), root.getOrganization(getOwnerName())))));
|
||||
}
|
||||
|
||||
public void addCollaborators(GHUser... users) throws IOException {
|
||||
@@ -453,7 +545,6 @@ public class GHRepository extends GHObject {
|
||||
}
|
||||
|
||||
private void modifyCollaborators(Collection<GHUser> users, String method) throws IOException {
|
||||
verifyMine();
|
||||
for (GHUser user : users) {
|
||||
new Requester(root).method(method).to(getApiTailUrl("collaborators/" + user.getLogin()));
|
||||
}
|
||||
@@ -462,7 +553,7 @@ public class GHRepository extends GHObject {
|
||||
public void setEmailServiceHook(String address) throws IOException {
|
||||
Map<String, String> config = new HashMap<String, String>();
|
||||
config.put("address", address);
|
||||
new Requester(root).method("POST").with("name", "email").with("config", config).with("active", "true")
|
||||
new Requester(root).method("POST").with("name", "email").with("config", config).with("active", true)
|
||||
.to(getApiTailUrl("hooks"));
|
||||
}
|
||||
|
||||
@@ -517,14 +608,14 @@ public class GHRepository extends GHObject {
|
||||
try {
|
||||
new Requester(root).method("DELETE").to(getApiTailUrl(""));
|
||||
} catch (FileNotFoundException x) {
|
||||
throw (FileNotFoundException) new FileNotFoundException("Failed to delete " + owner.login + "/" + name + "; might not exist, or you might need the delete_repo scope in your token: http://stackoverflow.com/a/19327004/12916").initCause(x);
|
||||
throw (FileNotFoundException) new FileNotFoundException("Failed to delete " + getOwnerName() + "/" + name + "; might not exist, or you might need the delete_repo scope in your token: http://stackoverflow.com/a/19327004/12916").initCause(x);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort orders for listing forks
|
||||
*/
|
||||
public static enum ForkSort { NEWEST, OLDEST, STARGAZERS }
|
||||
public enum ForkSort { NEWEST, OLDEST, STARGAZERS }
|
||||
|
||||
/**
|
||||
* Lists all the direct forks of this repository, sorted by
|
||||
@@ -541,12 +632,8 @@ public class GHRepository extends GHObject {
|
||||
*/
|
||||
public PagedIterable<GHRepository> listForks(final ForkSort sort) {
|
||||
return new PagedIterable<GHRepository>() {
|
||||
public PagedIterator<GHRepository> iterator() {
|
||||
String sortParam = "";
|
||||
if (sort != null) {
|
||||
sortParam = "?sort=" + sort.toString().toLowerCase(Locale.ENGLISH);
|
||||
}
|
||||
return new PagedIterator<GHRepository>(root.retrieve().asIterator(getApiTailUrl("forks" + sortParam), GHRepository[].class)) {
|
||||
public PagedIterator<GHRepository> _iterator(int pageSize) {
|
||||
return new PagedIterator<GHRepository>(root.retrieve().with("sort",sort).asIterator(getApiTailUrl("forks"), GHRepository[].class, pageSize)) {
|
||||
@Override
|
||||
protected void wrapUp(GHRepository[] page) {
|
||||
for (GHRepository c : page) {
|
||||
@@ -565,7 +652,19 @@ public class GHRepository extends GHObject {
|
||||
* Newly forked repository that belong to you.
|
||||
*/
|
||||
public GHRepository fork() throws IOException {
|
||||
return new Requester(root).method("POST").to(getApiTailUrl("forks"), GHRepository.class).wrap(root);
|
||||
new Requester(root).method("POST").to(getApiTailUrl("forks"), null);
|
||||
|
||||
// this API is asynchronous. we need to wait for a bit
|
||||
for (int i=0; i<10; i++) {
|
||||
GHRepository r = root.getMyself().getRepository(name);
|
||||
if (r!=null) return r;
|
||||
try {
|
||||
Thread.sleep(3000);
|
||||
} catch (InterruptedException e) {
|
||||
throw (IOException)new InterruptedIOException().initCause(e);
|
||||
}
|
||||
}
|
||||
throw new IOException(this+" was forked but can't find the new repository");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -603,24 +702,24 @@ public class GHRepository extends GHObject {
|
||||
* @see #listPullRequests(GHIssueState)
|
||||
*/
|
||||
public List<GHPullRequest> getPullRequests(GHIssueState state) throws IOException {
|
||||
return listPullRequests(state).asList();
|
||||
return queryPullRequests().state(state).list().asList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all the pull requests of a particular state.
|
||||
*
|
||||
* @deprecated
|
||||
* Use {@link #queryPullRequests()}
|
||||
*/
|
||||
public PagedIterable<GHPullRequest> listPullRequests(final GHIssueState state) {
|
||||
return new PagedIterable<GHPullRequest>() {
|
||||
public PagedIterator<GHPullRequest> iterator() {
|
||||
return new PagedIterator<GHPullRequest>(root.retrieve().asIterator(getApiTailUrl("pulls?state="+state.name().toLowerCase(Locale.ENGLISH)), GHPullRequest[].class)) {
|
||||
@Override
|
||||
protected void wrapUp(GHPullRequest[] page) {
|
||||
for (GHPullRequest pr : page)
|
||||
pr.wrapUp(GHRepository.this);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
public PagedIterable<GHPullRequest> listPullRequests(GHIssueState state) {
|
||||
return queryPullRequests().state(state).list();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves pull requests.
|
||||
*/
|
||||
public GHPullRequestQueryBuilder queryPullRequests() {
|
||||
return new GHPullRequestQueryBuilder(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -675,7 +774,23 @@ public class GHRepository extends GHObject {
|
||||
}
|
||||
|
||||
public GHCompare getCompare(GHBranch id1, GHBranch id2) throws IOException {
|
||||
return getCompare(id1.getName(),id2.getName());
|
||||
|
||||
GHRepository owner1 = id1.getOwner();
|
||||
GHRepository owner2 = id2.getOwner();
|
||||
|
||||
// If the owner of the branches is different, we have a cross-fork compare.
|
||||
if (owner1!=null && owner2!=null) {
|
||||
String ownerName1 = owner1.getOwnerName();
|
||||
String ownerName2 = owner2.getOwnerName();
|
||||
if (!StringUtils.equals(ownerName1, ownerName2)) {
|
||||
String qualifiedName1 = String.format("%s:%s", ownerName1, id1.getName());
|
||||
String qualifiedName2 = String.format("%s:%s", ownerName2, id2.getName());
|
||||
return getCompare(qualifiedName1, qualifiedName2);
|
||||
}
|
||||
}
|
||||
|
||||
return getCompare(id1.getName(), id2.getName());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -684,7 +799,27 @@ public class GHRepository extends GHObject {
|
||||
* @throws IOException on failure communicating with GitHub
|
||||
*/
|
||||
public GHRef[] getRefs() throws IOException {
|
||||
return GHRef.wrap(root.retrieve().to(String.format("/repos/%s/%s/git/refs", owner.login, name), GHRef[].class),root);
|
||||
return GHRef.wrap(root.retrieve().to(String.format("/repos/%s/%s/git/refs", getOwnerName(), name), GHRef[].class), root);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves all refs for the github repository.
|
||||
*
|
||||
* @return paged iterable of all refs
|
||||
* @throws IOException on failure communicating with GitHub, potentially due to an invalid ref type being requested
|
||||
*/
|
||||
public PagedIterable<GHRef> listRefs() throws IOException {
|
||||
final String url = String.format("/repos/%s/%s/git/refs", getOwnerName(), name);
|
||||
return new PagedIterable<GHRef>() {
|
||||
public PagedIterator<GHRef> _iterator(int pageSize) {
|
||||
return new PagedIterator<GHRef>(root.retrieve().asIterator(url, GHRef[].class, pageSize)) {
|
||||
protected void wrapUp(GHRef[] page) {
|
||||
// no-op
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -694,8 +829,29 @@ public class GHRepository extends GHObject {
|
||||
* @throws IOException on failure communicating with GitHub, potentially due to an invalid ref type being requested
|
||||
*/
|
||||
public GHRef[] getRefs(String refType) throws IOException {
|
||||
return GHRef.wrap(root.retrieve().to(String.format("/repos/%s/%s/git/refs/%s", owner.login, name, refType), GHRef[].class),root);
|
||||
return GHRef.wrap(root.retrieve().to(String.format("/repos/%s/%s/git/refs/%s", getOwnerName(), name, refType), GHRef[].class),root);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all refs of the given type for the current GitHub repository.
|
||||
*
|
||||
* @param refType the type of reg to search for e.g. <tt>tags</tt> or <tt>commits</tt>
|
||||
* @return paged iterable of all refs of the specified type
|
||||
* @throws IOException on failure communicating with GitHub, potentially due to an invalid ref type being requested
|
||||
*/
|
||||
public PagedIterable<GHRef> listRefs(String refType) throws IOException {
|
||||
final String url = String.format("/repos/%s/%s/git/refs/%s", getOwnerName(), name, refType);
|
||||
return new PagedIterable<GHRef>() {
|
||||
public PagedIterator<GHRef> _iterator(int pageSize) {
|
||||
return new PagedIterator<GHRef>(root.retrieve().asIterator(url, GHRef[].class, pageSize)) {
|
||||
protected void wrapUp(GHRef[] page) {
|
||||
// no-op
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrive a ref of the given type for the current GitHub repository.
|
||||
*
|
||||
@@ -707,8 +863,24 @@ public class GHRepository extends GHObject {
|
||||
* invalid ref type being requested
|
||||
*/
|
||||
public GHRef getRef(String refName) throws IOException {
|
||||
return root.retrieve().to(String.format("/repos/%s/%s/git/refs/%s", owner.login, name, refName), GHRef.class).wrap(root);
|
||||
// hashes in branch names must be replaced with the url encoded equivalent or this call will fail
|
||||
// FIXME: how about other URL unsafe characters, like space, @, : etc? do we need to be using URLEncoder.encode()?
|
||||
// OTOH, '/' need no escaping
|
||||
refName = refName.replaceAll("#", "%23");
|
||||
return root.retrieve().to(String.format("/repos/%s/%s/git/refs/%s", getOwnerName(), name, refName), GHRef.class).wrap(root);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the <strong>annotated</strong> tag object. Only valid if the {@link GHRef#getObject()} has a
|
||||
* {@link GHRef.GHObject#getType()} of {@code tag}.
|
||||
*
|
||||
* @param sha the sha of the tag object
|
||||
* @return the annotated tag object
|
||||
*/
|
||||
public GHTagObject getTagObject(String sha) throws IOException {
|
||||
return root.retrieve().to(getApiTailUrl("git/tags/" + sha), GHTagObject.class).wrap(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrive a tree of the given type for the current GitHub repository.
|
||||
*
|
||||
@@ -719,8 +891,12 @@ public class GHRepository extends GHObject {
|
||||
* invalid tree type being requested
|
||||
*/
|
||||
public GHTree getTree(String sha) throws IOException {
|
||||
String url = String.format("/repos/%s/%s/git/trees/%s", owner.login, name, sha);
|
||||
return root.retrieve().to(url, GHTree.class).wrap(root);
|
||||
String url = String.format("/repos/%s/%s/git/trees/%s", getOwnerName(), name, sha);
|
||||
return root.retrieve().to(url, GHTree.class).wrap(this);
|
||||
}
|
||||
|
||||
public GHTreeBuilder createTree() {
|
||||
return new GHTreeBuilder(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -734,8 +910,37 @@ public class GHRepository extends GHObject {
|
||||
* invalid tree type being requested
|
||||
*/
|
||||
public GHTree getTreeRecursive(String sha, int recursive) throws IOException {
|
||||
String url = String.format("/repos/%s/%s/git/trees/%s?recursive=%d", owner.login, name, sha, recursive);
|
||||
return root.retrieve().to(url, GHTree.class).wrap(root);
|
||||
String url = String.format("/repos/%s/%s/git/trees/%s?recursive=%d", getOwnerName(), name, sha, recursive);
|
||||
return root.retrieve().to(url, GHTree.class).wrap(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the metadata & the content of a blob.
|
||||
*
|
||||
* <p>
|
||||
* This method retrieves the whole content in memory, so beware when you are dealing with large BLOB.
|
||||
*
|
||||
* @see <a href="https://developer.github.com/v3/git/blobs/#get-a-blob">Get a blob</a>
|
||||
* @see #readBlob(String)
|
||||
*/
|
||||
public GHBlob getBlob(String blobSha) throws IOException {
|
||||
String target = getApiTailUrl("git/blobs/" + blobSha);
|
||||
return root.retrieve().to(target, GHBlob.class);
|
||||
}
|
||||
|
||||
public GHBlobBuilder createBlob() {
|
||||
return new GHBlobBuilder(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the content of a blob as a stream for better efficiency.
|
||||
*
|
||||
* @see <a href="https://developer.github.com/v3/git/blobs/#get-a-blob">Get a blob</a>
|
||||
* @see #getBlob(String)
|
||||
*/
|
||||
public InputStream readBlob(String blobSha) throws IOException {
|
||||
String target = getApiTailUrl("git/blobs/" + blobSha);
|
||||
return root.retrieve().withHeader("Accept","application/vnd.github.VERSION.raw").asStream(target);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -744,19 +949,23 @@ public class GHRepository extends GHObject {
|
||||
public GHCommit getCommit(String sha1) throws IOException {
|
||||
GHCommit c = commits.get(sha1);
|
||||
if (c==null) {
|
||||
c = root.retrieve().to(String.format("/repos/%s/%s/commits/%s", owner.login, name, sha1), GHCommit.class).wrapUp(this);
|
||||
c = root.retrieve().to(String.format("/repos/%s/%s/commits/%s", getOwnerName(), name, sha1), GHCommit.class).wrapUp(this);
|
||||
commits.put(sha1,c);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
public GHCommitBuilder createCommit() {
|
||||
return new GHCommitBuilder(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists all the commits.
|
||||
*/
|
||||
public PagedIterable<GHCommit> listCommits() {
|
||||
return new PagedIterable<GHCommit>() {
|
||||
public PagedIterator<GHCommit> iterator() {
|
||||
return new PagedIterator<GHCommit>(root.retrieve().asIterator(String.format("/repos/%s/%s/commits", owner.login, name), GHCommit[].class)) {
|
||||
public PagedIterator<GHCommit> _iterator(int pageSize) {
|
||||
return new PagedIterator<GHCommit>(root.retrieve().asIterator(String.format("/repos/%s/%s/commits", getOwnerName(), name), GHCommit[].class, pageSize)) {
|
||||
protected void wrapUp(GHCommit[] page) {
|
||||
for (GHCommit c : page)
|
||||
c.wrapUp(GHRepository.this);
|
||||
@@ -778,8 +987,8 @@ public class GHRepository extends GHObject {
|
||||
*/
|
||||
public PagedIterable<GHCommitComment> listCommitComments() {
|
||||
return new PagedIterable<GHCommitComment>() {
|
||||
public PagedIterator<GHCommitComment> iterator() {
|
||||
return new PagedIterator<GHCommitComment>(root.retrieve().asIterator(String.format("/repos/%s/%s/comments", owner.login, name), GHCommitComment[].class)) {
|
||||
public PagedIterator<GHCommitComment> _iterator(int pageSize) {
|
||||
return new PagedIterator<GHCommitComment>(root.retrieve().asIterator(String.format("/repos/%s/%s/comments", getOwnerName(), name), GHCommitComment[].class, pageSize)) {
|
||||
@Override
|
||||
protected void wrapUp(GHCommitComment[] page) {
|
||||
for (GHCommitComment c : page)
|
||||
@@ -790,13 +999,53 @@ public class GHRepository extends GHObject {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the basic license details for the repository.
|
||||
* <p>
|
||||
* This is a preview item and subject to change.
|
||||
*
|
||||
* @throws IOException as usual but also if you don't use the preview connector
|
||||
* @return null if there's no license.
|
||||
*/
|
||||
@Preview @Deprecated
|
||||
public GHLicense getLicense() throws IOException{
|
||||
GHContentWithLicense lic = getLicenseContent_();
|
||||
return lic!=null ? lic.license : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the contents of the repository's license file - makes an additional API call
|
||||
* <p>
|
||||
* This is a preview item and subject to change.
|
||||
*
|
||||
* @return details regarding the license contents, or null if there's no license.
|
||||
* @throws IOException as usual but also if you don't use the preview connector
|
||||
*/
|
||||
@Preview @Deprecated
|
||||
public GHContent getLicenseContent() throws IOException {
|
||||
return getLicenseContent_();
|
||||
}
|
||||
|
||||
@Preview @Deprecated
|
||||
private GHContentWithLicense getLicenseContent_() throws IOException {
|
||||
try {
|
||||
return root.retrieve()
|
||||
.withPreview(DRAX)
|
||||
.to(getApiTailUrl("license"), GHContentWithLicense.class).wrap(this);
|
||||
} catch (FileNotFoundException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
/**
|
||||
* Lists all the commit statues attached to the given commit, newer ones first.
|
||||
*/
|
||||
public PagedIterable<GHCommitStatus> listCommitStatuses(final String sha1) throws IOException {
|
||||
return new PagedIterable<GHCommitStatus>() {
|
||||
public PagedIterator<GHCommitStatus> iterator() {
|
||||
return new PagedIterator<GHCommitStatus>(root.retrieve().asIterator(String.format("/repos/%s/%s/statuses/%s", owner.login, name, sha1), GHCommitStatus[].class)) {
|
||||
public PagedIterator<GHCommitStatus> _iterator(int pageSize) {
|
||||
return new PagedIterator<GHCommitStatus>(root.retrieve().asIterator(String.format("/repos/%s/%s/statuses/%s", getOwnerName(), name, sha1), GHCommitStatus[].class, pageSize)) {
|
||||
@Override
|
||||
protected void wrapUp(GHCommitStatus[] page) {
|
||||
for (GHCommitStatus c : page)
|
||||
@@ -827,18 +1076,18 @@ public class GHRepository extends GHObject {
|
||||
*/
|
||||
public GHCommitStatus createCommitStatus(String sha1, GHCommitState state, String targetUrl, String description, String context) throws IOException {
|
||||
return new Requester(root)
|
||||
.with("state", state.name().toLowerCase(Locale.ENGLISH))
|
||||
.with("state", state)
|
||||
.with("target_url", targetUrl)
|
||||
.with("description", description)
|
||||
.with("context", context)
|
||||
.to(String.format("/repos/%s/%s/statuses/%s",owner.login,this.name,sha1),GHCommitStatus.class).wrapUp(root);
|
||||
.to(String.format("/repos/%s/%s/statuses/%s",getOwnerName(),this.name,sha1),GHCommitStatus.class).wrapUp(root);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #createCommitStatus(String, GHCommitState,String,String,String)
|
||||
*/
|
||||
public GHCommitStatus createCommitStatus(String sha1, GHCommitState state, String targetUrl, String description) throws IOException {
|
||||
return createCommitStatus(sha1, state, targetUrl, description,null);
|
||||
return createCommitStatus(sha1, state, targetUrl, description, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -846,8 +1095,8 @@ public class GHRepository extends GHObject {
|
||||
*/
|
||||
public PagedIterable<GHEventInfo> listEvents() throws IOException {
|
||||
return new PagedIterable<GHEventInfo>() {
|
||||
public PagedIterator<GHEventInfo> iterator() {
|
||||
return new PagedIterator<GHEventInfo>(root.retrieve().asIterator(String.format("/repos/%s/%s/events", owner.login, name), GHEventInfo[].class)) {
|
||||
public PagedIterator<GHEventInfo> _iterator(int pageSize) {
|
||||
return new PagedIterator<GHEventInfo>(root.retrieve().asIterator(String.format("/repos/%s/%s/events", getOwnerName(), name), GHEventInfo[].class, pageSize)) {
|
||||
@Override
|
||||
protected void wrapUp(GHEventInfo[] page) {
|
||||
for (GHEventInfo c : page)
|
||||
@@ -865,8 +1114,8 @@ public class GHRepository extends GHObject {
|
||||
*/
|
||||
public PagedIterable<GHLabel> listLabels() throws IOException {
|
||||
return new PagedIterable<GHLabel>() {
|
||||
public PagedIterator<GHLabel> iterator() {
|
||||
return new PagedIterator<GHLabel>(root.retrieve().asIterator(getApiTailUrl("labels"), GHLabel[].class)) {
|
||||
public PagedIterator<GHLabel> _iterator(int pageSize) {
|
||||
return new PagedIterator<GHLabel>(root.retrieve().asIterator(getApiTailUrl("labels"), GHLabel[].class, pageSize)) {
|
||||
@Override
|
||||
protected void wrapUp(GHLabel[] page) {
|
||||
for (GHLabel c : page)
|
||||
@@ -884,7 +1133,7 @@ public class GHRepository extends GHObject {
|
||||
public GHLabel createLabel(String name, String color) throws IOException {
|
||||
return root.retrieve().method("POST")
|
||||
.with("name",name)
|
||||
.with("color",color)
|
||||
.with("color", color)
|
||||
.to(getApiTailUrl("labels"), GHLabel.class).wrapUp(this);
|
||||
}
|
||||
|
||||
@@ -894,9 +1143,44 @@ public class GHRepository extends GHObject {
|
||||
* https://developer.github.com/v3/activity/watching/
|
||||
*/
|
||||
public PagedIterable<GHUser> listSubscribers() {
|
||||
return listUsers("subscribers");
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists all the users who have starred this repo based on the old version of the API. For
|
||||
* additional information, like date when the repository was starred, see {@link #listStargazers2()}
|
||||
*/
|
||||
public PagedIterable<GHUser> listStargazers() {
|
||||
return listUsers("stargazers");
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists all the users who have starred this repo based on new version of the API, having extended
|
||||
* information like the time when the repository was starred. For compatibility with the old API
|
||||
* see {@link #listStargazers()}
|
||||
*/
|
||||
public PagedIterable<GHStargazer> listStargazers2() {
|
||||
return new PagedIterable<GHStargazer>() {
|
||||
@Override
|
||||
public PagedIterator<GHStargazer> _iterator(int pageSize) {
|
||||
Requester requester = root.retrieve();
|
||||
requester.setHeader("Accept", "application/vnd.github.v3.star+json");
|
||||
return new PagedIterator<GHStargazer>(requester.asIterator(getApiTailUrl("stargazers"), GHStargazer[].class, pageSize)) {
|
||||
@Override
|
||||
protected void wrapUp(GHStargazer[] page) {
|
||||
for (GHStargazer c : page) {
|
||||
c.wrapUp(GHRepository.this);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private PagedIterable<GHUser> listUsers(final String suffix) {
|
||||
return new PagedIterable<GHUser>() {
|
||||
public PagedIterator<GHUser> iterator() {
|
||||
return new PagedIterator<GHUser>(root.retrieve().asIterator(getApiTailUrl("subscribers"), GHUser[].class)) {
|
||||
public PagedIterator<GHUser> _iterator(int pageSize) {
|
||||
return new PagedIterator<GHUser>(root.retrieve().asIterator(getApiTailUrl(suffix), GHUser[].class, pageSize)) {
|
||||
protected void wrapUp(GHUser[] page) {
|
||||
for (GHUser c : page)
|
||||
c.wrapUp(root);
|
||||
@@ -938,11 +1222,6 @@ public class GHRepository extends GHObject {
|
||||
// return root.retrieveWithAuth("/pulls/"+owner+'/'+name,JsonPullRequests.class).wrap(root);
|
||||
// }
|
||||
|
||||
private void verifyMine() throws IOException {
|
||||
if (!root.login.equals(owner.login))
|
||||
throw new IOException("Operation not applicable to a repository owned by someone else: "+owner.login);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a set that represents the post-commit hook URLs.
|
||||
* The returned set is live, and changes made to them are reflected to GitHub.
|
||||
@@ -950,6 +1229,8 @@ public class GHRepository extends GHObject {
|
||||
* @deprecated
|
||||
* Use {@link #getHooks()} and {@link #createHook(String, Map, Collection, boolean)}
|
||||
*/
|
||||
@SuppressFBWarnings(value = "DMI_COLLECTION_OF_URLS",
|
||||
justification = "It causes a performance degradation, but we have already exposed it to the API")
|
||||
public Set<URL> getPostCommitHooks() {
|
||||
return postCommitHooks;
|
||||
}
|
||||
@@ -957,6 +1238,9 @@ public class GHRepository extends GHObject {
|
||||
/**
|
||||
* Live set view of the post-commit hook.
|
||||
*/
|
||||
@SuppressFBWarnings(value = "DMI_COLLECTION_OF_URLS",
|
||||
justification = "It causes a performance degradation, but we have already exposed it to the API")
|
||||
@SkipFromToString
|
||||
private final Set<URL> postCommitHooks = new AbstractSet<URL>() {
|
||||
private List<URL> getPostCommitHooks() {
|
||||
try {
|
||||
@@ -1011,6 +1295,9 @@ public class GHRepository extends GHObject {
|
||||
|
||||
/*package*/ GHRepository wrap(GitHub root) {
|
||||
this.root = root;
|
||||
if (root.isOffline()) {
|
||||
owner.wrapUp(root);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -1019,13 +1306,17 @@ public class GHRepository extends GHObject {
|
||||
*/
|
||||
public Map<String,GHBranch> getBranches() throws IOException {
|
||||
Map<String,GHBranch> r = new TreeMap<String,GHBranch>();
|
||||
for (GHBranch p : root.retrieve().to(getApiTailUrl("branches"), GHBranch[].class)) {
|
||||
for (GHBranch p : root.retrieve().withPreview(LOKI).to(getApiTailUrl("branches"), GHBranch[].class)) {
|
||||
p.wrap(this);
|
||||
r.put(p.getName(),p);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
public GHBranch getBranch(String name) throws IOException {
|
||||
return root.retrieve().withPreview(LOKI).to(getApiTailUrl("branches/"+name),GHBranch.class).wrap(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* Use {@link #listMilestones(GHIssueState)}
|
||||
@@ -1043,8 +1334,8 @@ public class GHRepository extends GHObject {
|
||||
*/
|
||||
public PagedIterable<GHMilestone> listMilestones(final GHIssueState state) {
|
||||
return new PagedIterable<GHMilestone>() {
|
||||
public PagedIterator<GHMilestone> iterator() {
|
||||
return new PagedIterator<GHMilestone>(root.retrieve().asIterator(getApiTailUrl("milestones?state="+state.toString().toLowerCase(Locale.ENGLISH)), GHMilestone[].class)) {
|
||||
public PagedIterator<GHMilestone> _iterator(int pageSize) {
|
||||
return new PagedIterator<GHMilestone>(root.retrieve().with("state",state).asIterator(getApiTailUrl("milestones"), GHMilestone[].class, pageSize)) {
|
||||
@Override
|
||||
protected void wrapUp(GHMilestone[] page) {
|
||||
for (GHMilestone c : page)
|
||||
@@ -1083,6 +1374,9 @@ public class GHRepository extends GHObject {
|
||||
|
||||
public List<GHContent> getDirectoryContent(String path, String ref) throws IOException {
|
||||
Requester requester = root.retrieve();
|
||||
while (path.endsWith("/")) {
|
||||
path = path.substring(0, path.length() - 1);
|
||||
}
|
||||
String target = getApiTailUrl("contents/" + path);
|
||||
|
||||
GHContent[] files = requester.with("ref",ref).to(target, GHContent[].class);
|
||||
@@ -1101,11 +1395,17 @@ public class GHRepository extends GHObject {
|
||||
}
|
||||
|
||||
public GHContentUpdateResponse createContent(String content, String commitMessage, String path) throws IOException {
|
||||
return createContent(content.getBytes(), commitMessage, path, null);
|
||||
return createContent(content, commitMessage, path, null);
|
||||
}
|
||||
|
||||
public GHContentUpdateResponse createContent(String content, String commitMessage, String path, String branch) throws IOException {
|
||||
return createContent(content.getBytes(), commitMessage, path, branch);
|
||||
final byte[] payload;
|
||||
try {
|
||||
payload = content.getBytes("UTF-8");
|
||||
} catch (UnsupportedEncodingException ex) {
|
||||
throw (IOException) new IOException("UTF-8 encoding is not supported").initCause(ex);
|
||||
}
|
||||
return createContent(payload, commitMessage, path, branch);
|
||||
}
|
||||
|
||||
public GHContentUpdateResponse createContent(byte[] contentBytes, String commitMessage, String path) throws IOException {
|
||||
@@ -1116,7 +1416,7 @@ public class GHRepository extends GHObject {
|
||||
Requester requester = new Requester(root)
|
||||
.with("path", path)
|
||||
.with("message", commitMessage)
|
||||
.with("content", DatatypeConverter.printBase64Binary(contentBytes))
|
||||
.with("content", Base64.encodeBase64String(contentBytes))
|
||||
.method("PUT");
|
||||
|
||||
if (branch != null) {
|
||||
@@ -1207,8 +1507,8 @@ public class GHRepository extends GHObject {
|
||||
|
||||
public PagedIterable<Contributor> listContributors() throws IOException {
|
||||
return new PagedIterable<Contributor>() {
|
||||
public PagedIterator<Contributor> iterator() {
|
||||
return new PagedIterator<Contributor>(root.retrieve().asIterator(getApiTailUrl("contributors"), Contributor[].class)) {
|
||||
public PagedIterator<Contributor> _iterator(int pageSize) {
|
||||
return new PagedIterator<Contributor>(root.retrieve().asIterator(getApiTailUrl("contributors"), Contributor[].class, pageSize)) {
|
||||
@Override
|
||||
protected void wrapUp(Contributor[] page) {
|
||||
for (Contributor c : page)
|
||||
@@ -1225,6 +1525,18 @@ public class GHRepository extends GHObject {
|
||||
public int getContributions() {
|
||||
return contributions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
// We ignore contributions in the calculation
|
||||
return super.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
// We ignore contributions in the calculation
|
||||
return super.equals(obj);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1252,22 +1564,30 @@ public class GHRepository extends GHObject {
|
||||
return new GHNotificationStream(root,getApiTailUrl("/notifications"));
|
||||
}
|
||||
|
||||
/**
|
||||
* <a href="https://developer.github.com/v3/repos/traffic/#views">https://developer.github.com/v3/repos/traffic/#views</a>
|
||||
*/
|
||||
public GHRepositoryViewTraffic getViewTraffic() throws IOException{
|
||||
return root.retrieve().to(getApiTailUrl("/traffic/views"), GHRepositoryViewTraffic.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Repository:"+owner.login+":"+name;
|
||||
/**
|
||||
* <a href="https://developer.github.com/v3/repos/traffic/#clones">https://developer.github.com/v3/repos/traffic/#clones</a>
|
||||
*/
|
||||
public GHRepositoryCloneTraffic getCloneTraffic() throws IOException{
|
||||
return root.retrieve().to(getApiTailUrl("/traffic/clones"), GHRepositoryCloneTraffic.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return toString().hashCode();
|
||||
return ("Repository:"+getOwnerName()+":"+name).hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof GHRepository) {
|
||||
GHRepository that = (GHRepository) obj;
|
||||
return this.owner.login.equals(that.owner.login)
|
||||
return this.getOwnerName().equals(that.getOwnerName())
|
||||
&& this.name.equals(that.name);
|
||||
}
|
||||
return false;
|
||||
@@ -1275,6 +1595,6 @@ public class GHRepository extends GHObject {
|
||||
|
||||
String getApiTailUrl(String tail) {
|
||||
if (tail.length()>0 && !tail.startsWith("/")) tail='/'+tail;
|
||||
return "/repos/" + owner.login + "/" + name +tail;
|
||||
return "/repos/" + getOwnerName() + "/" + name +tail;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Repository clone statistics.
|
||||
*
|
||||
* @see GHRepository#getCloneTraffic()
|
||||
*/
|
||||
public class GHRepositoryCloneTraffic extends GHRepositoryTraffic {
|
||||
private List<DailyInfo> clones;
|
||||
|
||||
/*package*/ GHRepositoryCloneTraffic() {
|
||||
}
|
||||
|
||||
/*package*/ GHRepositoryCloneTraffic(Integer count, Integer uniques, List<DailyInfo> clones) {
|
||||
super(count, uniques);
|
||||
this.clones = clones;
|
||||
}
|
||||
|
||||
public List<DailyInfo> getClones() {
|
||||
return clones;
|
||||
}
|
||||
|
||||
public List<DailyInfo> getDailyInfo() {
|
||||
return getClones();
|
||||
}
|
||||
|
||||
public static class DailyInfo extends GHRepositoryTraffic.DailyInfo {
|
||||
/*package*/ DailyInfo() {
|
||||
}
|
||||
|
||||
/*package*/ DailyInfo(String timestamp, int count, int uniques) {
|
||||
super(timestamp, count, uniques);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Search repositories.
|
||||
*
|
||||
@@ -57,8 +55,13 @@ public class GHRepositorySearchBuilder extends GHSearchBuilder<GHRepository> {
|
||||
return q("stars:"+v);
|
||||
}
|
||||
|
||||
public GHRepositorySearchBuilder order(GHDirection v) {
|
||||
req.with("order",v);
|
||||
return this;
|
||||
}
|
||||
|
||||
public GHRepositorySearchBuilder sort(Sort sort) {
|
||||
req.with("sort",sort.toString().toLowerCase(Locale.ENGLISH));
|
||||
req.with("sort",sort);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
54
src/main/java/org/kohsuke/github/GHRepositoryTraffic.java
Normal file
54
src/main/java/org/kohsuke/github/GHRepositoryTraffic.java
Normal file
@@ -0,0 +1,54 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class GHRepositoryTraffic implements TrafficInfo {
|
||||
private int count;
|
||||
private int uniques;
|
||||
|
||||
/*package*/ GHRepositoryTraffic() {
|
||||
}
|
||||
|
||||
/*package*/ GHRepositoryTraffic(int count, int uniques) {
|
||||
this.count = count;
|
||||
this.uniques = uniques;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public int getUniques() {
|
||||
return uniques;
|
||||
}
|
||||
|
||||
public abstract List<? extends DailyInfo> getDailyInfo();
|
||||
|
||||
public static abstract class DailyInfo implements TrafficInfo {
|
||||
private String timestamp;
|
||||
private int count;
|
||||
private int uniques;
|
||||
|
||||
public Date getTimestamp() {
|
||||
return GitHub.parseDate(timestamp);
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public int getUniques() {
|
||||
return uniques;
|
||||
}
|
||||
|
||||
/*package*/ DailyInfo() {
|
||||
}
|
||||
|
||||
/*package*/ DailyInfo(String timestamp, Integer count, Integer uniques) {
|
||||
this.timestamp = timestamp;
|
||||
this.count = count;
|
||||
this.uniques = uniques;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Repository view statistics.
|
||||
*
|
||||
* @see GHRepository#getViewTraffic()
|
||||
*/
|
||||
public class GHRepositoryViewTraffic extends GHRepositoryTraffic {
|
||||
private List<DailyInfo> views;
|
||||
|
||||
/*package*/ GHRepositoryViewTraffic() {
|
||||
}
|
||||
|
||||
/*package*/ GHRepositoryViewTraffic(int count, int uniques, List<DailyInfo> views) {
|
||||
super(count, uniques);
|
||||
this.views = views;
|
||||
}
|
||||
|
||||
public List<DailyInfo> getViews() {
|
||||
return views;
|
||||
}
|
||||
|
||||
public List<DailyInfo> getDailyInfo() {
|
||||
return getViews();
|
||||
}
|
||||
|
||||
public static class DailyInfo extends GHRepositoryTraffic.DailyInfo {
|
||||
/*package*/ DailyInfo() {
|
||||
}
|
||||
|
||||
/*package*/ DailyInfo(String timestamp, int count, int uniques) {
|
||||
super(timestamp, count, uniques);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,9 +10,7 @@ import java.util.List;
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
public abstract class GHSearchBuilder<T> {
|
||||
protected final GitHub root;
|
||||
protected final Requester req;
|
||||
public abstract class GHSearchBuilder<T> extends GHQueryBuilder<T> {
|
||||
protected final List<String> terms = new ArrayList<String>();
|
||||
|
||||
/**
|
||||
@@ -21,15 +19,14 @@ public abstract class GHSearchBuilder<T> {
|
||||
private final Class<? extends SearchResult<T>> receiverType;
|
||||
|
||||
/*package*/ GHSearchBuilder(GitHub root, Class<? extends SearchResult<T>> receiverType) {
|
||||
this.root = root;
|
||||
this.req = root.retrieve();
|
||||
super(root);
|
||||
this.receiverType = receiverType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search terms.
|
||||
*/
|
||||
public GHSearchBuilder q(String term) {
|
||||
public GHQueryBuilder<T> q(String term) {
|
||||
terms.add(term);
|
||||
return this;
|
||||
}
|
||||
@@ -37,11 +34,12 @@ public abstract class GHSearchBuilder<T> {
|
||||
/**
|
||||
* Performs the search.
|
||||
*/
|
||||
@Override
|
||||
public PagedSearchIterable<T> list() {
|
||||
return new PagedSearchIterable<T>(root) {
|
||||
public PagedIterator<T> iterator() {
|
||||
public PagedIterator<T> _iterator(int pageSize) {
|
||||
req.set("q", StringUtils.join(terms, " "));
|
||||
return new PagedIterator<T>(adapt(req.asIterator(getApiUrl(), receiverType))) {
|
||||
return new PagedIterator<T>(adapt(req.asIterator(getApiUrl(), receiverType, pageSize))) {
|
||||
protected void wrapUp(T[] page) {
|
||||
// SearchResult.getItems() should do it
|
||||
}
|
||||
|
||||
51
src/main/java/org/kohsuke/github/GHStargazer.java
Normal file
51
src/main/java/org/kohsuke/github/GHStargazer.java
Normal file
@@ -0,0 +1,51 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* A stargazer at a repository on GitHub.
|
||||
*
|
||||
* @author noctarius
|
||||
*/
|
||||
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD"}, justification = "JSON API")
|
||||
public class GHStargazer {
|
||||
|
||||
private GHRepository repository;
|
||||
private String starred_at;
|
||||
private GHUser user;
|
||||
|
||||
/**
|
||||
* Gets the repository that is stargazed
|
||||
*
|
||||
* @return the starred repository
|
||||
*/
|
||||
public GHRepository getRepository() {
|
||||
return repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the date when the repository was starred, however old stars before
|
||||
* August 2012, will all show the date the API was changed to support starred_at.
|
||||
*
|
||||
* @return the date the stargazer was added
|
||||
*/
|
||||
public Date getStarredAt() {
|
||||
return GitHub.parseDate(starred_at);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the user that starred the repository
|
||||
*
|
||||
* @return the stargazer user
|
||||
*/
|
||||
public GHUser getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
void wrapUp(GHRepository repository) {
|
||||
this.repository = repository;
|
||||
user.wrapUp(repository.root);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,14 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
|
||||
/**
|
||||
* Represents a tag in {@link GHRepository}
|
||||
*
|
||||
* @see GHRepository#listTags()
|
||||
*/
|
||||
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
|
||||
"NP_UNWRITTEN_FIELD"}, justification = "JSON API")
|
||||
public class GHTag {
|
||||
private GHRepository owner;
|
||||
private GitHub root;
|
||||
|
||||
60
src/main/java/org/kohsuke/github/GHTagObject.java
Normal file
60
src/main/java/org/kohsuke/github/GHTagObject.java
Normal file
@@ -0,0 +1,60 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
|
||||
/**
|
||||
* Represents an annotated tag in a {@link GHRepository}
|
||||
*
|
||||
* @see GHRepository#getTagObject(String)
|
||||
*/
|
||||
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
|
||||
"NP_UNWRITTEN_FIELD"}, justification = "JSON API")
|
||||
public class GHTagObject {
|
||||
private GHRepository owner;
|
||||
private GitHub root;
|
||||
|
||||
private String tag;
|
||||
private String sha;
|
||||
private String url;
|
||||
private String message;
|
||||
private GitUser tagger;
|
||||
private GHRef.GHObject object;
|
||||
|
||||
/*package*/ GHTagObject wrap(GHRepository owner) {
|
||||
this.owner = owner;
|
||||
this.root = owner.root;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GHRepository getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public GitHub getRoot() {
|
||||
return root;
|
||||
}
|
||||
|
||||
public String getTag() {
|
||||
return tag;
|
||||
}
|
||||
|
||||
public String getSha() {
|
||||
return sha;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public GitUser getTagger() {
|
||||
return tagger;
|
||||
}
|
||||
|
||||
public GHRef.GHObject getObject() {
|
||||
return object;
|
||||
}
|
||||
}
|
||||
@@ -12,12 +12,24 @@ import java.util.TreeMap;
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
public class GHTeam {
|
||||
private String name,permission;
|
||||
private String name,permission,slug;
|
||||
private int id;
|
||||
private GHOrganization organization; // populated by GET /user/teams where Teams+Orgs are returned together
|
||||
|
||||
protected /*final*/ GHOrganization org;
|
||||
|
||||
/** Member's role in a team */
|
||||
public enum Role {
|
||||
/**
|
||||
* A normal member of the team
|
||||
*/
|
||||
MEMBER,
|
||||
/**
|
||||
* Able to add/remove other team members, promote other team members to team maintainer, and edit the team's name and description.
|
||||
*/
|
||||
MAINTAINER
|
||||
}
|
||||
|
||||
/*package*/ GHTeam wrapUp(GHOrganization owner) {
|
||||
this.org = owner;
|
||||
return this;
|
||||
@@ -43,6 +55,10 @@ public class GHTeam {
|
||||
return permission;
|
||||
}
|
||||
|
||||
public String getSlug() {
|
||||
return slug;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
@@ -52,8 +68,8 @@ public class GHTeam {
|
||||
*/
|
||||
public PagedIterable<GHUser> listMembers() throws IOException {
|
||||
return new PagedIterable<GHUser>() {
|
||||
public PagedIterator<GHUser> iterator() {
|
||||
return new PagedIterator<GHUser>(org.root.retrieve().asIterator(api("/members"), GHUser[].class)) {
|
||||
public PagedIterator<GHUser> _iterator(int pageSize) {
|
||||
return new PagedIterator<GHUser>(org.root.retrieve().asIterator(api("/members"), GHUser[].class, pageSize)) {
|
||||
@Override
|
||||
protected void wrapUp(GHUser[] page) {
|
||||
GHUser.wrap(page, org.root);
|
||||
@@ -89,8 +105,8 @@ public class GHTeam {
|
||||
|
||||
public PagedIterable<GHRepository> listRepositories() {
|
||||
return new PagedIterable<GHRepository>() {
|
||||
public PagedIterator<GHRepository> iterator() {
|
||||
return new PagedIterator<GHRepository>(org.root.retrieve().asIterator(api("/repos"), GHRepository[].class)) {
|
||||
public PagedIterator<GHRepository> _iterator(int pageSize) {
|
||||
return new PagedIterator<GHRepository>(org.root.retrieve().asIterator(api("/repos"), GHRepository[].class, pageSize)) {
|
||||
@Override
|
||||
protected void wrapUp(GHRepository[] page) {
|
||||
for (GHRepository r : page)
|
||||
@@ -112,6 +128,22 @@ public class GHTeam {
|
||||
org.root.retrieve().method("PUT").to(api("/memberships/" + u.getLogin()), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a member to the team
|
||||
*
|
||||
* The user will be invited to the organization if required.
|
||||
*
|
||||
* @param user github user
|
||||
* @param role role for the new member
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public void add(GHUser user, Role role) throws IOException {
|
||||
org.root.retrieve().method("PUT")
|
||||
.with("role", role.name())
|
||||
.to(api("/memberships/" + user.getLogin()), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a member to the team.
|
||||
*/
|
||||
@@ -120,15 +152,28 @@ public class GHTeam {
|
||||
}
|
||||
|
||||
public void add(GHRepository r) throws IOException {
|
||||
org.root.retrieve().method("PUT").to(api("/repos/" + r.getOwnerName() + '/' + r.getName()), null);
|
||||
add(r,null);
|
||||
}
|
||||
|
||||
public void add(GHRepository r, GHOrganization.Permission permission) throws IOException {
|
||||
org.root.retrieve().method("PUT")
|
||||
.with("permission", permission)
|
||||
.to(api("/repos/" + r.getOwnerName() + '/' + r.getName()), null);
|
||||
}
|
||||
|
||||
public void remove(GHRepository r) throws IOException {
|
||||
org.root.retrieve().method("DELETE").to(api("/repos/" + r.getOwnerName() + '/' + r.getName()), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes this team.
|
||||
*/
|
||||
public void delete() throws IOException {
|
||||
org.root.retrieve().method("DELETE").to(api(""));
|
||||
}
|
||||
|
||||
private String api(String tail) {
|
||||
return "/teams/"+id+tail;
|
||||
return "/teams/" + id + tail;
|
||||
}
|
||||
|
||||
public GHOrganization getOrganization() {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
@@ -12,6 +14,8 @@ import java.util.Date;
|
||||
* @see GHNotificationStream
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
|
||||
"NP_UNWRITTEN_FIELD"}, justification = "JSON API")
|
||||
public class GHThread extends GHObject {
|
||||
private GitHub root;
|
||||
private GHRepository repository;
|
||||
@@ -67,6 +71,10 @@ public class GHThread extends GHObject {
|
||||
public String getType() {
|
||||
return subject.type;
|
||||
}
|
||||
|
||||
public String getLastCommentUrl() {
|
||||
return subject.latest_comment_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this thread is about an issue, return that issue.
|
||||
|
||||
@@ -10,10 +10,12 @@ import java.util.List;
|
||||
* https://developer.github.com/v3/git/trees/
|
||||
*
|
||||
* @author Daniel Teixeira - https://github.com/ddtxra
|
||||
* @see GHCommit#getTree()
|
||||
* @see GHRepository#getTree(String)
|
||||
* @see GHTreeEntry#asTree()
|
||||
*/
|
||||
public class GHTree {
|
||||
/* package almost final */GitHub root;
|
||||
/* package almost final */GHRepository repo;
|
||||
|
||||
private boolean truncated;
|
||||
private String sha, url;
|
||||
@@ -28,12 +30,24 @@ public class GHTree {
|
||||
|
||||
/**
|
||||
* Return an array of entries of the trees
|
||||
* @return
|
||||
*/
|
||||
public List<GHTreeEntry> getTree() {
|
||||
return Collections.unmodifiableList(Arrays.asList(tree));
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a tree entry by its name.
|
||||
*
|
||||
* IOW, find a directory entry by a file name.
|
||||
*/
|
||||
public GHTreeEntry getEntry(String path) {
|
||||
for (GHTreeEntry e : tree) {
|
||||
if (e.getPath().equals(path))
|
||||
return e;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the number of items in the tree array exceeded the GitHub maximum limit.
|
||||
* @return true true if the number of items in the tree array exceeded the GitHub maximum limit otherwise false.
|
||||
@@ -50,8 +64,11 @@ public class GHTree {
|
||||
return GitHub.parseURL(url);
|
||||
}
|
||||
|
||||
/* package */GHTree wrap(GitHub root) {
|
||||
this.root = root;
|
||||
/* package */GHTree wrap(GHRepository repo) {
|
||||
this.repo = repo;
|
||||
for (GHTreeEntry e : tree) {
|
||||
e.tree = this;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
90
src/main/java/org/kohsuke/github/GHTreeBuilder.java
Normal file
90
src/main/java/org/kohsuke/github/GHTreeBuilder.java
Normal file
@@ -0,0 +1,90 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Builder pattern for creating a new tree.
|
||||
* Based on https://developer.github.com/v3/git/trees/#create-a-tree
|
||||
*/
|
||||
public class GHTreeBuilder {
|
||||
private final GHRepository repo;
|
||||
private final Requester req;
|
||||
|
||||
private final List<TreeEntry> treeEntries = new ArrayList<TreeEntry>();
|
||||
|
||||
@SuppressFBWarnings("URF_UNREAD_FIELD")
|
||||
private static final class TreeEntry {
|
||||
private final String path;
|
||||
private final String mode;
|
||||
private final String type;
|
||||
private String sha;
|
||||
private String content;
|
||||
|
||||
private TreeEntry(String path, String mode, String type) {
|
||||
this.path = path;
|
||||
this.mode = mode;
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
|
||||
GHTreeBuilder(GHRepository repo) {
|
||||
this.repo = repo;
|
||||
req = new Requester(repo.root);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param baseTree the SHA of tree you want to update with new data
|
||||
*/
|
||||
public GHTreeBuilder baseTree(String baseTree) {
|
||||
req.with("base_tree", baseTree);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new entry to the tree.
|
||||
* Exactly one of the parameters {@code sha} and {@code content} must be non-null.
|
||||
*/
|
||||
public GHTreeBuilder entry(String path, String mode, String type, String sha, String content) {
|
||||
TreeEntry entry = new TreeEntry(path, mode, type);
|
||||
entry.sha = sha;
|
||||
entry.content = content;
|
||||
treeEntries.add(entry);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specialized version of {@link #entry(String, String, String, String, String)} for adding an existing blob referred by its SHA.
|
||||
*/
|
||||
public GHTreeBuilder shaEntry(String path, String sha, boolean executable) {
|
||||
TreeEntry entry = new TreeEntry(path, executable ? "100755" : "100644", "blob");
|
||||
entry.sha = sha;
|
||||
treeEntries.add(entry);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specialized version of {@link #entry(String, String, String, String, String)} for adding a text file with the specified {@code content}.
|
||||
*/
|
||||
public GHTreeBuilder textEntry(String path, String content, boolean executable) {
|
||||
TreeEntry entry = new TreeEntry(path, executable ? "100755" : "100644", "blob");
|
||||
entry.content = content;
|
||||
treeEntries.add(entry);
|
||||
return this;
|
||||
}
|
||||
|
||||
private String getApiTail() {
|
||||
return String.format("/repos/%s/%s/git/trees", repo.getOwnerName(), repo.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a tree based on the parameters specified thus far.
|
||||
*/
|
||||
public GHTree create() throws IOException {
|
||||
req._with("tree", treeEntries);
|
||||
return req.method("POST").to(getApiTail(), GHTree.class).wrap(repo);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
@@ -10,6 +12,8 @@ import java.net.URL;
|
||||
* @see GHTree
|
||||
*/
|
||||
public class GHTreeEntry {
|
||||
/* package almost final */GHTree tree;
|
||||
|
||||
private String path, mode, type, sha, url;
|
||||
private long size;
|
||||
|
||||
@@ -44,7 +48,7 @@ public class GHTreeEntry {
|
||||
|
||||
/**
|
||||
* Gets the type such as:
|
||||
* "blob"
|
||||
* "blob", "tree", etc.
|
||||
*
|
||||
* @return The type
|
||||
*/
|
||||
@@ -68,4 +72,37 @@ public class GHTreeEntry {
|
||||
public URL getUrl() {
|
||||
return GitHub.parseURL(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* If this tree entry represents a file, then return its information.
|
||||
* Otherwise null.
|
||||
*/
|
||||
public GHBlob asBlob() throws IOException {
|
||||
if (type.equals("blob"))
|
||||
return tree.repo.getBlob(sha);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this tree entry represents a file, then return its content.
|
||||
* Otherwise null.
|
||||
*/
|
||||
public InputStream readAsBlob() throws IOException {
|
||||
if (type.equals("blob"))
|
||||
return tree.repo.readBlob(sha);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this tree entry represents a directory, then return it.
|
||||
* Otherwise null.
|
||||
*/
|
||||
public GHTree asTree() throws IOException {
|
||||
if (type.equals("tree"))
|
||||
return tree.repo.getTree(sha);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ package org.kohsuke.github;
|
||||
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -56,8 +55,14 @@ public class GHUser extends GHPerson {
|
||||
*/
|
||||
@WithBridgeMethods(Set.class)
|
||||
public GHPersonSet<GHUser> getFollows() throws IOException {
|
||||
GHUser[] followers = root.retrieve().to("/users/" + login + "/following", GHUser[].class);
|
||||
return new GHPersonSet<GHUser>(Arrays.asList(wrap(followers,root)));
|
||||
return new GHPersonSet<GHUser>(listFollows().asList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists the users that this user is following
|
||||
*/
|
||||
public PagedIterable<GHUser> listFollows() {
|
||||
return listUser("following");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,8 +70,26 @@ public class GHUser extends GHPerson {
|
||||
*/
|
||||
@WithBridgeMethods(Set.class)
|
||||
public GHPersonSet<GHUser> getFollowers() throws IOException {
|
||||
GHUser[] followers = root.retrieve().to("/users/" + login + "/followers", GHUser[].class);
|
||||
return new GHPersonSet<GHUser>(Arrays.asList(wrap(followers,root)));
|
||||
return new GHPersonSet<GHUser>(listFollowers().asList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists the users who are following this user.
|
||||
*/
|
||||
public PagedIterable<GHUser> listFollowers() {
|
||||
return listUser("followers");
|
||||
}
|
||||
|
||||
private PagedIterable<GHUser> listUser(final String suffix) {
|
||||
return new PagedIterable<GHUser>() {
|
||||
public PagedIterator<GHUser> _iterator(int pageSize) {
|
||||
return new PagedIterator<GHUser>(root.retrieve().asIterator(getApiTailUrl(suffix), GHUser[].class, pageSize)) {
|
||||
protected void wrapUp(GHUser[] page) {
|
||||
GHUser.wrap(page,root);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,9 +98,20 @@ public class GHUser extends GHPerson {
|
||||
* https://developer.github.com/v3/activity/watching/
|
||||
*/
|
||||
public PagedIterable<GHRepository> listSubscriptions() {
|
||||
return listRepositories("subscriptions");
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists all the repositories that this user has starred.
|
||||
*/
|
||||
public PagedIterable<GHRepository> listStarredRepositories() {
|
||||
return listRepositories("starred");
|
||||
}
|
||||
|
||||
private PagedIterable<GHRepository> listRepositories(final String suffix) {
|
||||
return new PagedIterable<GHRepository>() {
|
||||
public PagedIterator<GHRepository> iterator() {
|
||||
return new PagedIterator<GHRepository>(root.retrieve().asIterator(getApiTailUrl("subscriptions"), GHRepository[].class)) {
|
||||
public PagedIterator<GHRepository> _iterator(int pageSize) {
|
||||
return new PagedIterator<GHRepository>(root.retrieve().asIterator(getApiTailUrl(suffix), GHRepository[].class, pageSize)) {
|
||||
protected void wrapUp(GHRepository[] page) {
|
||||
for (GHRepository c : page)
|
||||
c.wrap(root);
|
||||
@@ -133,8 +167,8 @@ public class GHUser extends GHPerson {
|
||||
*/
|
||||
public PagedIterable<GHEventInfo> listEvents() throws IOException {
|
||||
return new PagedIterable<GHEventInfo>() {
|
||||
public PagedIterator<GHEventInfo> iterator() {
|
||||
return new PagedIterator<GHEventInfo>(root.retrieve().asIterator(String.format("/users/%s/events", login), GHEventInfo[].class)) {
|
||||
public PagedIterator<GHEventInfo> _iterator(int pageSize) {
|
||||
return new PagedIterator<GHEventInfo>(root.retrieve().asIterator(String.format("/users/%s/events", login), GHEventInfo[].class, pageSize)) {
|
||||
@Override
|
||||
protected void wrapUp(GHEventInfo[] page) {
|
||||
for (GHEventInfo c : page)
|
||||
@@ -150,8 +184,8 @@ public class GHUser extends GHPerson {
|
||||
*/
|
||||
public PagedIterable<GHGist> listGists() throws IOException {
|
||||
return new PagedIterable<GHGist>() {
|
||||
public PagedIterator<GHGist> iterator() {
|
||||
return new PagedIterator<GHGist>(root.retrieve().asIterator(String.format("/users/%s/gists", login), GHGist[].class)) {
|
||||
public PagedIterator<GHGist> _iterator(int pageSize) {
|
||||
return new PagedIterator<GHGist>(root.retrieve().asIterator(String.format("/users/%s/gists", login), GHGist[].class, pageSize)) {
|
||||
@Override
|
||||
protected void wrapUp(GHGist[] page) {
|
||||
for (GHGist c : page)
|
||||
@@ -162,11 +196,6 @@ public class GHUser extends GHPerson {
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "User:"+login;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return login.hashCode();
|
||||
@@ -185,4 +214,9 @@ public class GHUser extends GHPerson {
|
||||
if (tail.length()>0 && !tail.startsWith("/")) tail='/'+tail;
|
||||
return "/users/" + login + tail;
|
||||
}
|
||||
|
||||
/*package*/ GHUser wrapUp(GitHub root) {
|
||||
super.wrapUp(root);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Search users.
|
||||
*
|
||||
@@ -49,8 +47,13 @@ public class GHUserSearchBuilder extends GHSearchBuilder<GHUser> {
|
||||
return q("followers:"+v);
|
||||
}
|
||||
|
||||
public GHUserSearchBuilder order(GHDirection v) {
|
||||
req.with("order",v);
|
||||
return this;
|
||||
}
|
||||
|
||||
public GHUserSearchBuilder sort(Sort sort) {
|
||||
req.with("sort",sort.toString().toLowerCase(Locale.ENGLISH));
|
||||
req.with("sort",sort);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,14 +23,22 @@
|
||||
*/
|
||||
package org.kohsuke.github;
|
||||
|
||||
import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.ANY;
|
||||
import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.NONE;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.introspect.VisibilityChecker.Std;
|
||||
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
|
||||
import org.apache.commons.codec.Charsets;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import javax.annotation.CheckForNull;
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.text.ParseException;
|
||||
@@ -40,19 +48,18 @@ import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Hashtable;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TimeZone;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.introspect.VisibilityChecker.Std;
|
||||
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
|
||||
import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.*;
|
||||
import static java.net.HttpURLConnection.*;
|
||||
import static java.util.logging.Level.*;
|
||||
import static org.kohsuke.github.Previews.*;
|
||||
|
||||
/**
|
||||
* Root of the GitHub API.
|
||||
@@ -73,15 +80,21 @@ public class GitHub {
|
||||
*/
|
||||
/*package*/ final String encodedAuthorization;
|
||||
|
||||
private final Map<String,GHUser> users = new Hashtable<String, GHUser>();
|
||||
private final Map<String,GHOrganization> orgs = new Hashtable<String, GHOrganization>();
|
||||
|
||||
private final ConcurrentMap<String,GHUser> users;
|
||||
private final ConcurrentMap<String,GHOrganization> orgs;
|
||||
// Cache of myself object.
|
||||
private GHMyself myself;
|
||||
private final String apiUrl;
|
||||
|
||||
/*package*/ final RateLimitHandler rateLimitHandler;
|
||||
/*package*/ final AbuseLimitHandler abuseLimitHandler;
|
||||
|
||||
private HttpConnector connector = HttpConnector.DEFAULT;
|
||||
|
||||
private final Object headerRateLimitLock = new Object();
|
||||
private GHRateLimit headerRateLimit = null;
|
||||
private volatile GHRateLimit rateLimit = null;
|
||||
|
||||
/**
|
||||
* Creates a client API root object.
|
||||
*
|
||||
@@ -118,7 +131,7 @@ public class GitHub {
|
||||
* @param connector
|
||||
* HttpConnector to use. Pass null to use default connector.
|
||||
*/
|
||||
/* package */ GitHub(String apiUrl, String login, String oauthAccessToken, String password, HttpConnector connector, RateLimitHandler rateLimitHandler) throws IOException {
|
||||
/* package */ GitHub(String apiUrl, String login, String oauthAccessToken, String password, HttpConnector connector, RateLimitHandler rateLimitHandler, AbuseLimitHandler abuseLimitHandler) throws IOException {
|
||||
if (apiUrl.endsWith("/")) apiUrl = apiUrl.substring(0, apiUrl.length()-1); // normalize
|
||||
this.apiUrl = apiUrl;
|
||||
if (null != connector) this.connector = connector;
|
||||
@@ -128,13 +141,17 @@ public class GitHub {
|
||||
} else {
|
||||
if (password!=null) {
|
||||
String authorization = (login + ':' + password);
|
||||
encodedAuthorization = "Basic "+new String(Base64.encodeBase64(authorization.getBytes()));
|
||||
String charsetName = Charsets.UTF_8.name();
|
||||
encodedAuthorization = "Basic "+new String(Base64.encodeBase64(authorization.getBytes(charsetName)), charsetName);
|
||||
} else {// anonymous access
|
||||
encodedAuthorization = null;
|
||||
}
|
||||
}
|
||||
|
||||
users = new ConcurrentHashMap<String, GHUser>();
|
||||
orgs = new ConcurrentHashMap<String, GHOrganization>();
|
||||
this.rateLimitHandler = rateLimitHandler;
|
||||
this.abuseLimitHandler = abuseLimitHandler;
|
||||
|
||||
if (login==null && encodedAuthorization!=null)
|
||||
login = getMyself().getLogin();
|
||||
@@ -148,6 +165,16 @@ public class GitHub {
|
||||
return GitHubBuilder.fromCredentials().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Version that connects to GitHub Enterprise.
|
||||
*
|
||||
* @deprecated
|
||||
* Use {@link #connectToEnterpriseWithOAuth(String, String, String)}
|
||||
*/
|
||||
public static GitHub connectToEnterprise(String apiUrl, String oauthAccessToken) throws IOException {
|
||||
return connectToEnterpriseWithOAuth(apiUrl,null,oauthAccessToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* Version that connects to GitHub Enterprise.
|
||||
*
|
||||
@@ -156,10 +183,16 @@ public class GitHub {
|
||||
* "http://ghe.acme.com/api/v3". Note that GitHub Enterprise has <tt>/api/v3</tt> in the URL.
|
||||
* For historical reasons, this parameter still accepts the bare domain name, but that's considered deprecated.
|
||||
*/
|
||||
public static GitHub connectToEnterprise(String apiUrl, String oauthAccessToken) throws IOException {
|
||||
return new GitHubBuilder().withEndpoint(apiUrl).withOAuthToken(oauthAccessToken).build();
|
||||
public static GitHub connectToEnterpriseWithOAuth(String apiUrl, String login, String oauthAccessToken) throws IOException {
|
||||
return new GitHubBuilder().withEndpoint(apiUrl).withOAuthToken(oauthAccessToken, login).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Version that connects to GitHub Enterprise.
|
||||
*
|
||||
* @deprecated
|
||||
* Use with caution. Login with password is not a preferred method.
|
||||
*/
|
||||
public static GitHub connectToEnterprise(String apiUrl, String login, String password) throws IOException {
|
||||
return new GitHubBuilder().withEndpoint(apiUrl).withPassword(login, password).build();
|
||||
}
|
||||
@@ -197,6 +230,33 @@ public class GitHub {
|
||||
return new GitHubBuilder().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects to GitHub Enterprise anonymously.
|
||||
*
|
||||
* All operations that requires authentication will fail.
|
||||
*/
|
||||
public static GitHub connectToEnterpriseAnonymously(String apiUrl) throws IOException {
|
||||
return new GitHubBuilder().withEndpoint(apiUrl).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* An offline-only {@link GitHub} useful for parsing event notification from an unknown source.
|
||||
*
|
||||
* All operations that require a connection will fail.
|
||||
*
|
||||
* @return An offline-only {@link GitHub}.
|
||||
*/
|
||||
public static GitHub offline() {
|
||||
try {
|
||||
return new GitHubBuilder()
|
||||
.withEndpoint("https://api.github.invalid")
|
||||
.withConnector(HttpConnector.OFFLINE)
|
||||
.build();
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("The offline implementation constructor should not connect", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this an anonymous connection
|
||||
* @return {@code true} if operations that require authentication will fail.
|
||||
@@ -205,10 +265,22 @@ public class GitHub {
|
||||
return login==null && encodedAuthorization==null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this an always offline "connection".
|
||||
* @return {@code true} if this is an always offline "connection".
|
||||
*/
|
||||
public boolean isOffline() {
|
||||
return connector == HttpConnector.OFFLINE;
|
||||
}
|
||||
|
||||
public HttpConnector getConnector() {
|
||||
return connector;
|
||||
}
|
||||
|
||||
public String getApiUrl() {
|
||||
return apiUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the custom connector used to make requests to GitHub.
|
||||
*/
|
||||
@@ -242,31 +314,78 @@ public class GitHub {
|
||||
*/
|
||||
public GHRateLimit getRateLimit() throws IOException {
|
||||
try {
|
||||
return retrieve().to("/rate_limit", JsonRateLimit.class).rate;
|
||||
return rateLimit = retrieve().to("/rate_limit", JsonRateLimit.class).rate;
|
||||
} catch (FileNotFoundException e) {
|
||||
// GitHub Enterprise doesn't have the rate limit, so in that case
|
||||
// return some big number that's not too big.
|
||||
// see issue #78
|
||||
GHRateLimit r = new GHRateLimit();
|
||||
r.limit = r.remaining = 1000000;
|
||||
r.reset = new Date(System.currentTimeMillis() + TimeUnit.HOURS.toMillis(1));
|
||||
return r;
|
||||
long hour = 60L * 60L; // this is madness, storing the date as seconds in a Date object
|
||||
r.reset = new Date(System.currentTimeMillis() / 1000L + hour);
|
||||
return rateLimit = r;
|
||||
}
|
||||
}
|
||||
|
||||
/*package*/ void updateRateLimit(@Nonnull GHRateLimit observed) {
|
||||
synchronized (headerRateLimitLock) {
|
||||
if (headerRateLimit == null
|
||||
|| headerRateLimit.getResetDate().getTime() < observed.getResetDate().getTime()
|
||||
|| headerRateLimit.remaining > observed.remaining) {
|
||||
headerRateLimit = observed;
|
||||
LOGGER.log(FINE, "Rate limit now: {0}", headerRateLimit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the most recently observed rate limit data or {@code null} if either there is no rate limit
|
||||
* (for example GitHub Enterprise) or if no requests have been made.
|
||||
*
|
||||
* @return the most recently observed rate limit data or {@code null}.
|
||||
*/
|
||||
@CheckForNull
|
||||
public GHRateLimit lastRateLimit() {
|
||||
synchronized (headerRateLimitLock) {
|
||||
return headerRateLimit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current rate limit while trying not to actually make any remote requests unless absolutely necessary.
|
||||
*
|
||||
* @return the current rate limit data.
|
||||
* @throws IOException if we couldn't get the current rate limit data.
|
||||
*/
|
||||
@Nonnull
|
||||
public GHRateLimit rateLimit() throws IOException {
|
||||
synchronized (headerRateLimitLock) {
|
||||
if (headerRateLimit != null) {
|
||||
return headerRateLimit;
|
||||
}
|
||||
}
|
||||
GHRateLimit rateLimit = this.rateLimit;
|
||||
if (rateLimit == null || rateLimit.getResetDate().getTime() < System.currentTimeMillis()) {
|
||||
rateLimit = getRateLimit();
|
||||
}
|
||||
return rateLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link GHUser} that represents yourself.
|
||||
*/
|
||||
@WithBridgeMethods(GHUser.class)
|
||||
public GHMyself getMyself() throws IOException {
|
||||
requireCredential();
|
||||
synchronized (this) {
|
||||
if (this.myself != null) return myself;
|
||||
|
||||
GHMyself u = retrieve().to("/user", GHMyself.class);
|
||||
|
||||
GHMyself u = retrieve().to("/user", GHMyself.class);
|
||||
|
||||
u.root = this;
|
||||
users.put(u.getLogin(), u);
|
||||
|
||||
return u;
|
||||
u.root = this;
|
||||
this.myself = u;
|
||||
return u;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -282,7 +401,7 @@ public class GitHub {
|
||||
return u;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* clears all cached data in order for external changes (modifications and del
|
||||
*/
|
||||
@@ -294,7 +413,7 @@ public class GitHub {
|
||||
/**
|
||||
* Interns the given {@link GHUser}.
|
||||
*/
|
||||
protected GHUser getUser(GHUser orig) throws IOException {
|
||||
protected GHUser getUser(GHUser orig) {
|
||||
GHUser u = users.get(orig.getLogin());
|
||||
if (u==null) {
|
||||
orig.root = this;
|
||||
@@ -304,6 +423,9 @@ public class GitHub {
|
||||
return u;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets {@link GHOrganization} specified by name.
|
||||
*/
|
||||
public GHOrganization getOrganization(String name) throws IOException {
|
||||
GHOrganization o = orgs.get(name);
|
||||
if (o==null) {
|
||||
@@ -313,6 +435,35 @@ public class GitHub {
|
||||
return o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of all organizations.
|
||||
*/
|
||||
public PagedIterable<GHOrganization> listOrganizations() {
|
||||
return listOrganizations(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of all organizations starting after the organization identifier specified by 'since'.
|
||||
*
|
||||
* @see <a href="https://developer.github.com/v3/orgs/#parameters">List All Orgs - Parameters</a>
|
||||
*/
|
||||
public PagedIterable<GHOrganization> listOrganizations(final String since) {
|
||||
return new PagedIterable<GHOrganization>() {
|
||||
@Override
|
||||
public PagedIterator<GHOrganization> _iterator(int pageSize) {
|
||||
System.out.println("page size: " + pageSize);
|
||||
return new PagedIterator<GHOrganization>(retrieve().with("since",since)
|
||||
.asIterator("/organizations", GHOrganization[].class, pageSize)) {
|
||||
@Override
|
||||
protected void wrapUp(GHOrganization[] page) {
|
||||
for (GHOrganization c : page)
|
||||
c.wrapUp(GitHub.this);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the repository object from 'user/reponame' string that GitHub calls as "repository name"
|
||||
*
|
||||
@@ -322,6 +473,62 @@ public class GitHub {
|
||||
String[] tokens = name.split("/");
|
||||
return retrieve().to("/repos/" + tokens[0] + '/' + tokens[1], GHRepository.class).wrap(this);
|
||||
}
|
||||
/**
|
||||
* Returns a list of popular open source licenses
|
||||
*
|
||||
* WARNING: This uses a PREVIEW API.
|
||||
*
|
||||
* @see <a href="https://developer.github.com/v3/licenses/">GitHub API - Licenses</a>
|
||||
*
|
||||
* @return a list of popular open source licenses
|
||||
*/
|
||||
@Preview @Deprecated
|
||||
public PagedIterable<GHLicense> listLicenses() throws IOException {
|
||||
return new PagedIterable<GHLicense>() {
|
||||
public PagedIterator<GHLicense> _iterator(int pageSize) {
|
||||
return new PagedIterator<GHLicense>(retrieve().withPreview(DRAX).asIterator("/licenses", GHLicense[].class, pageSize)) {
|
||||
@Override
|
||||
protected void wrapUp(GHLicense[] page) {
|
||||
for (GHLicense c : page)
|
||||
c.wrap(GitHub.this);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all users.
|
||||
*/
|
||||
public PagedIterable<GHUser> listUsers() throws IOException {
|
||||
return new PagedIterable<GHUser>() {
|
||||
public PagedIterator<GHUser> _iterator(int pageSize) {
|
||||
return new PagedIterator<GHUser>(retrieve().asIterator("/users", GHUser[].class, pageSize)) {
|
||||
@Override
|
||||
protected void wrapUp(GHUser[] page) {
|
||||
for (GHUser u : page)
|
||||
u.wrapUp(GitHub.this);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the full details for a license
|
||||
*
|
||||
* WARNING: This uses a PREVIEW API.
|
||||
*
|
||||
* @param key The license key provided from the API
|
||||
* @return The license details
|
||||
* @see GHLicense#getKey()
|
||||
*/
|
||||
@Preview @Deprecated
|
||||
public GHLicense getLicense(String key) throws IOException {
|
||||
return retrieve().withPreview(DRAX).to("/licenses/" + key, GHLicense.class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
/**
|
||||
* This method returns a shallowly populated organizations.
|
||||
@@ -398,17 +605,28 @@ public class GitHub {
|
||||
/**
|
||||
* Creates a new repository.
|
||||
*
|
||||
* To create a repository in an organization, see
|
||||
* {@link GHOrganization#createRepository(String, String, String, GHTeam, boolean)}
|
||||
*
|
||||
* @return
|
||||
* Newly created repository.
|
||||
* @deprecated
|
||||
* Use {@link #createRepository(String)} that uses a builder pattern to let you control every aspect.
|
||||
*/
|
||||
public GHRepository createRepository(String name, String description, String homepage, boolean isPublic) throws IOException {
|
||||
Requester requester = new Requester(this)
|
||||
.with("name", name).with("description", description).with("homepage", homepage)
|
||||
.with("public", isPublic ? 1 : 0);
|
||||
return requester.method("POST").to("/user/repos", GHRepository.class).wrap(this);
|
||||
return createRepository(name).description(description).homepage(homepage).private_(!isPublic).create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a builder that creates a new repository.
|
||||
*
|
||||
* <p>
|
||||
* You use the returned builder to set various properties, then call {@link GHCreateRepositoryBuilder#create()}
|
||||
* to finally createa repository.
|
||||
*
|
||||
* <p>
|
||||
* To create a repository in an organization, see
|
||||
* {@link GHOrganization#createRepository(String, String, String, GHTeam, boolean)}
|
||||
*/
|
||||
public GHCreateRepositoryBuilder createRepository(String name) {
|
||||
return new GHCreateRepositoryBuilder(this,"/user/repos",name);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -427,6 +645,42 @@ public class GitHub {
|
||||
return requester.method("POST").to("/authorizations", GHAuthorization.class).wrap(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see <a href="https://developer.github.com/v3/oauth_authorizations/#get-or-create-an-authorization-for-a-specific-app">docs</a>
|
||||
*/
|
||||
public GHAuthorization createOrGetAuth(String clientId, String clientSecret, List<String> scopes, String note,
|
||||
String note_url)
|
||||
throws IOException {
|
||||
Requester requester = new Requester(this)
|
||||
.with("client_secret", clientSecret)
|
||||
.with("scopes", scopes)
|
||||
.with("note", note)
|
||||
.with("note_url", note_url);
|
||||
|
||||
return requester.method("PUT").to("/authorizations/clients/" + clientId, GHAuthorization.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see <a href="https://developer.github.com/v3/oauth_authorizations/#delete-an-authorization">Delete an authorization</a>
|
||||
*/
|
||||
public void deleteAuth(long id) throws IOException {
|
||||
retrieve().method("DELETE").to("/authorizations/" + id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see <a href="https://developer.github.com/v3/oauth_authorizations/#check-an-authorization">Check an authorization</a>
|
||||
*/
|
||||
public GHAuthorization checkAuth(@Nonnull String clientId, @Nonnull String accessToken) throws IOException {
|
||||
return retrieve().to("/applications/" + clientId + "/tokens/" + accessToken, GHAuthorization.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see <a href="https://developer.github.com/v3/oauth_authorizations/#reset-an-authorization">Reset an authorization</a>
|
||||
*/
|
||||
public GHAuthorization resetAuth(@Nonnull String clientId, @Nonnull String accessToken) throws IOException {
|
||||
return retrieve().method("POST").to("/applications/" + clientId + "/tokens/" + accessToken, GHAuthorization.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the credential is valid.
|
||||
*/
|
||||
@@ -435,10 +689,110 @@ public class GitHub {
|
||||
retrieve().to("/user", GHUser.class);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
if (LOGGER.isLoggable(FINE))
|
||||
LOGGER.log(FINE, "Exception validating credentials on " + this.apiUrl + " with login '" + this.login + "' " + e, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*package*/ GHUser intern(GHUser user) throws IOException {
|
||||
if (user==null) return user;
|
||||
|
||||
// if we already have this user in our map, use it
|
||||
GHUser u = users.get(user.getLogin());
|
||||
if (u!=null) return u;
|
||||
|
||||
// if not, remember this new user
|
||||
users.putIfAbsent(user.getLogin(),user);
|
||||
return user;
|
||||
}
|
||||
|
||||
private static class GHApiInfo {
|
||||
private String rate_limit_url;
|
||||
|
||||
void check(String apiUrl) throws IOException {
|
||||
if (rate_limit_url==null)
|
||||
throw new IOException(apiUrl+" doesn't look like GitHub API URL");
|
||||
|
||||
// make sure that the URL is legitimate
|
||||
new URL(rate_limit_url);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the connection.
|
||||
*
|
||||
* <p>
|
||||
* Verify that the API URL and credentials are valid to access this GitHub.
|
||||
*
|
||||
* <p>
|
||||
* This method returns normally if the endpoint is reachable and verified to be GitHub API URL.
|
||||
* Otherwise this method throws {@link IOException} to indicate the problem.
|
||||
*/
|
||||
public void checkApiUrlValidity() throws IOException {
|
||||
try {
|
||||
retrieve().to("/", GHApiInfo.class).check(apiUrl);
|
||||
} catch (IOException e) {
|
||||
if (isPrivateModeEnabled()) {
|
||||
throw (IOException)new IOException("GitHub Enterprise server (" + apiUrl + ") with private mode enabled").initCause(e);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures if a GitHub Enterprise server is configured in private mode.
|
||||
*
|
||||
* @return {@code true} if private mode is enabled. If it tries to use this method with GitHub, returns {@code
|
||||
* false}.
|
||||
*/
|
||||
private boolean isPrivateModeEnabled() {
|
||||
try {
|
||||
HttpURLConnection uc = getConnector().connect(getApiURL("/"));
|
||||
/*
|
||||
$ curl -i https://github.mycompany.com/api/v3/
|
||||
HTTP/1.1 401 Unauthorized
|
||||
Server: GitHub.com
|
||||
Date: Sat, 05 Mar 2016 19:45:01 GMT
|
||||
Content-Type: application/json; charset=utf-8
|
||||
Content-Length: 130
|
||||
Status: 401 Unauthorized
|
||||
X-GitHub-Media-Type: github.v3
|
||||
X-XSS-Protection: 1; mode=block
|
||||
X-Frame-Options: deny
|
||||
Content-Security-Policy: default-src 'none'
|
||||
Access-Control-Allow-Credentials: true
|
||||
Access-Control-Expose-Headers: ETag, Link, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval
|
||||
Access-Control-Allow-Origin: *
|
||||
X-GitHub-Request-Id: dbc70361-b11d-4131-9a7f-674b8edd0411
|
||||
Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
|
||||
X-Content-Type-Options: nosniff
|
||||
*/
|
||||
try {
|
||||
return uc.getResponseCode() == HTTP_UNAUTHORIZED
|
||||
&& uc.getHeaderField("X-GitHub-Media-Type") != null;
|
||||
} finally {
|
||||
// ensure that the connection opened by getResponseCode gets closed
|
||||
try {
|
||||
IOUtils.closeQuietly(uc.getInputStream());
|
||||
} catch (IOException ignore) {
|
||||
// ignore
|
||||
}
|
||||
IOUtils.closeQuietly(uc.getErrorStream());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Search commits.
|
||||
*/
|
||||
@Preview @Deprecated
|
||||
public GHCommitSearchBuilder searchCommits() {
|
||||
return new GHCommitSearchBuilder(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search issues.
|
||||
*/
|
||||
@@ -486,13 +840,13 @@ public class GitHub {
|
||||
* This provides a dump of every public repository, in the order that they were created.
|
||||
*
|
||||
* @param since
|
||||
* The integer ID of the last Repository that you’ve seen. See {@link GHRepository#getId()}
|
||||
* The numeric ID of the last Repository that you’ve seen. See {@link GHRepository#getId()}
|
||||
* @see <a href="https://developer.github.com/v3/repos/#list-all-public-repositories">documentation</a>
|
||||
*/
|
||||
public PagedIterable<GHRepository> listAllPublicRepositories(final String since) {
|
||||
return new PagedIterable<GHRepository>() {
|
||||
public PagedIterator<GHRepository> iterator() {
|
||||
return new PagedIterator<GHRepository>(retrieve().with("since",since).asIterator("/repositories", GHRepository[].class)) {
|
||||
public PagedIterator<GHRepository> _iterator(int pageSize) {
|
||||
return new PagedIterator<GHRepository>(retrieve().with("since",since).asIterator("/repositories", GHRepository[].class, pageSize)) {
|
||||
@Override
|
||||
protected void wrapUp(GHRepository[] page) {
|
||||
for (GHRepository c : page)
|
||||
@@ -558,4 +912,6 @@ public class GitHub {
|
||||
}
|
||||
|
||||
/* package */ static final String GITHUB_URL = "https://api.github.com";
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(GitHub.class.getName());
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.kohsuke.github.extras.ImpatientHttpConnector;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
@@ -14,7 +15,7 @@ import java.util.Map.Entry;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
*
|
||||
* Configures connection details and produces {@link GitHub}.
|
||||
*
|
||||
* @since 1.59
|
||||
*/
|
||||
@@ -29,6 +30,7 @@ public class GitHubBuilder {
|
||||
private HttpConnector connector;
|
||||
|
||||
private RateLimitHandler rateLimitHandler = RateLimitHandler.WAIT;
|
||||
private AbuseLimitHandler abuseLimitHandler = AbuseLimitHandler.WAIT;
|
||||
|
||||
public GitHubBuilder() {
|
||||
}
|
||||
@@ -51,7 +53,7 @@ public class GitHubBuilder {
|
||||
try {
|
||||
builder = fromPropertyFile();
|
||||
|
||||
if (builder.user != null)
|
||||
if (builder.oauthToken != null || builder.user != null)
|
||||
return builder;
|
||||
} catch (FileNotFoundException e) {
|
||||
// fall through
|
||||
@@ -60,7 +62,7 @@ public class GitHubBuilder {
|
||||
|
||||
builder = fromEnvironment();
|
||||
|
||||
if (builder.user != null)
|
||||
if (builder.oauthToken != null || builder.user != null)
|
||||
return builder;
|
||||
else
|
||||
throw (IOException)new IOException("Failed to resolve credentials from ~/.github or the environment.").initCause(cause);
|
||||
@@ -152,6 +154,12 @@ public class GitHubBuilder {
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param endpoint
|
||||
* The URL of GitHub (or GitHub enterprise) API endpoint, such as "https://api.github.com" or
|
||||
* "http://ghe.acme.com/api/v3". Note that GitHub Enterprise has <tt>/api/v3</tt> in the URL.
|
||||
* For historical reasons, this parameter still accepts the bare domain name, but that's considered deprecated.
|
||||
*/
|
||||
public GitHubBuilder withEndpoint(String endpoint) {
|
||||
this.endpoint = endpoint;
|
||||
return this;
|
||||
@@ -177,6 +185,10 @@ public class GitHubBuilder {
|
||||
this.rateLimitHandler = handler;
|
||||
return this;
|
||||
}
|
||||
public GitHubBuilder withAbuseLimitHandler(AbuseLimitHandler handler) {
|
||||
this.abuseLimitHandler = handler;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures {@linkplain #withConnector(HttpConnector) connector}
|
||||
@@ -184,14 +196,14 @@ public class GitHubBuilder {
|
||||
* the system default one.
|
||||
*/
|
||||
public GitHubBuilder withProxy(final Proxy p) {
|
||||
return withConnector(new HttpConnector() {
|
||||
return withConnector(new ImpatientHttpConnector(new HttpConnector() {
|
||||
public HttpURLConnection connect(URL url) throws IOException {
|
||||
return (HttpURLConnection) url.openConnection(p);
|
||||
}
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
public GitHub build() throws IOException {
|
||||
return new GitHub(endpoint, user, oauthToken, password, connector, rateLimitHandler);
|
||||
return new GitHub(endpoint, user, oauthToken, password, connector, rateLimitHandler, abuseLimitHandler);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
@@ -11,6 +13,8 @@ import java.util.Date;
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
|
||||
"NP_UNWRITTEN_FIELD"}, justification = "JSON API")
|
||||
public class GitUser {
|
||||
private String name, email, date;
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import org.kohsuke.github.extras.ImpatientHttpConnector;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
@@ -21,9 +23,18 @@ public interface HttpConnector {
|
||||
/**
|
||||
* Default implementation that uses {@link URL#openConnection()}.
|
||||
*/
|
||||
HttpConnector DEFAULT = new HttpConnector() {
|
||||
HttpConnector DEFAULT = new ImpatientHttpConnector(new HttpConnector() {
|
||||
public HttpURLConnection connect(URL url) throws IOException {
|
||||
return (HttpURLConnection) url.openConnection();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Stub implementation that is always off-line.
|
||||
*/
|
||||
HttpConnector OFFLINE = new HttpConnector() {
|
||||
public HttpURLConnection connect(URL url) throws IOException {
|
||||
throw new IOException("Offline");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
117
src/main/java/org/kohsuke/github/HttpException.java
Normal file
117
src/main/java/org/kohsuke/github/HttpException.java
Normal file
@@ -0,0 +1,117 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import javax.annotation.CheckForNull;
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* {@link IOException} for http exceptions because {@link HttpURLConnection} throws un-discerned
|
||||
* {@link IOException} and it can help to know the http response code to decide how to handle an
|
||||
* http exceptions.
|
||||
*
|
||||
* @author <a href="mailto:cleclerc@cloudbees.com">Cyrille Le Clerc</a>
|
||||
*/
|
||||
public class HttpException extends IOException {
|
||||
static final long serialVersionUID = 1L;
|
||||
|
||||
private final int responseCode;
|
||||
private final String responseMessage;
|
||||
private final String url;
|
||||
|
||||
/**
|
||||
* @param message The detail message (which is saved for later retrieval
|
||||
* by the {@link #getMessage()} method)
|
||||
* @param responseCode Http response code. {@code -1} if no code can be discerned.
|
||||
* @param responseMessage Http response message
|
||||
* @param url The url that was invoked
|
||||
* @see HttpURLConnection#getResponseCode()
|
||||
* @see HttpURLConnection#getResponseMessage()
|
||||
*/
|
||||
public HttpException(String message, int responseCode, String responseMessage, String url) {
|
||||
super(message);
|
||||
this.responseCode = responseCode;
|
||||
this.responseMessage = responseMessage;
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param message The detail message (which is saved for later retrieval
|
||||
* by the {@link #getMessage()} method)
|
||||
* @param responseCode Http response code. {@code -1} if no code can be discerned.
|
||||
* @param responseMessage Http response message
|
||||
* @param url The url that was invoked
|
||||
* @param cause The cause (which is saved for later retrieval by the
|
||||
* {@link #getCause()} method). (A null value is permitted,
|
||||
* and indicates that the cause is nonexistent or unknown.)
|
||||
* @see HttpURLConnection#getResponseCode()
|
||||
* @see HttpURLConnection#getResponseMessage()
|
||||
*/
|
||||
public HttpException(String message, int responseCode, String responseMessage, String url, Throwable cause) {
|
||||
super(message);
|
||||
initCause(cause);
|
||||
this.responseCode = responseCode;
|
||||
this.responseMessage = responseMessage;
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param responseCode Http response code. {@code -1} if no code can be discerned.
|
||||
* @param responseMessage Http response message
|
||||
* @param url The url that was invoked
|
||||
* @param cause The cause (which is saved for later retrieval by the
|
||||
* {@link #getCause()} method). (A null value is permitted,
|
||||
* and indicates that the cause is nonexistent or unknown.)
|
||||
* @see HttpURLConnection#getResponseCode()
|
||||
* @see HttpURLConnection#getResponseMessage()
|
||||
*/
|
||||
public HttpException(int responseCode, String responseMessage, String url, Throwable cause) {
|
||||
super("Server returned HTTP response code: " + responseCode + ", message: '" + responseMessage + "'" +
|
||||
" for URL: " + url);
|
||||
initCause(cause);
|
||||
this.responseCode = responseCode;
|
||||
this.responseMessage = responseMessage;
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param responseCode Http response code. {@code -1} if no code can be discerned.
|
||||
* @param responseMessage Http response message
|
||||
* @param url The url that was invoked
|
||||
* @param cause The cause (which is saved for later retrieval by the
|
||||
* {@link #getCause()} method). (A null value is permitted,
|
||||
* and indicates that the cause is nonexistent or unknown.)
|
||||
* @see HttpURLConnection#getResponseCode()
|
||||
* @see HttpURLConnection#getResponseMessage()
|
||||
*/
|
||||
public HttpException(int responseCode, String responseMessage, @CheckForNull URL url, Throwable cause) {
|
||||
this(responseCode, responseMessage, url == null ? null : url.toString(), cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Http response code of the request that cause the exception
|
||||
*
|
||||
* @return {@code -1} if no code can be discerned.
|
||||
*/
|
||||
public int getResponseCode() {
|
||||
return responseCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Http response message of the request that cause the exception
|
||||
*
|
||||
* @return {@code null} if no response message can be discerned.
|
||||
*/
|
||||
public String getResponseMessage() {
|
||||
return responseMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* The http URL that caused the exception
|
||||
*
|
||||
* @return url
|
||||
*/
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,27 @@ import java.util.Set;
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
public abstract class PagedIterable<T> implements Iterable<T> {
|
||||
public abstract PagedIterator<T> iterator();
|
||||
/**
|
||||
* Page size. 0 is default.
|
||||
*/
|
||||
private int size = 0;
|
||||
|
||||
/**
|
||||
* Sets the pagination size.
|
||||
*
|
||||
* <p>
|
||||
* When set to non-zero, each API call will retrieve this many entries.
|
||||
*/
|
||||
public PagedIterable<T> withPageSize(int size) {
|
||||
this.size = size;
|
||||
return this;
|
||||
}
|
||||
|
||||
public final PagedIterator<T> iterator() {
|
||||
return _iterator(size);
|
||||
}
|
||||
|
||||
public abstract PagedIterator<T> _iterator(int pageSize);
|
||||
|
||||
/**
|
||||
* Eagerly walk {@link Iterable} and return the result in a list.
|
||||
|
||||
@@ -6,7 +6,7 @@ import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
/**
|
||||
* Iterator over a pagenated data source.
|
||||
* Iterator over a paginated data source.
|
||||
*
|
||||
* Aside from the normal iterator operation, this method exposes {@link #nextPage()}
|
||||
* that allows the caller to retrieve items per page.
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
@@ -7,6 +9,8 @@ import java.util.Iterator;
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
|
||||
"UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR"}, justification = "Constructed by JSON API")
|
||||
public abstract class PagedSearchIterable<T> extends PagedIterable<T> {
|
||||
private final GitHub root;
|
||||
|
||||
@@ -19,6 +23,11 @@ public abstract class PagedSearchIterable<T> extends PagedIterable<T> {
|
||||
this.root = root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PagedSearchIterable<T> withPageSize(int size) {
|
||||
return (PagedSearchIterable<T>)super.withPageSize(size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of hit, including the results that's not yet fetched.
|
||||
*/
|
||||
|
||||
18
src/main/java/org/kohsuke/github/Preview.java
Normal file
18
src/main/java/org/kohsuke/github/Preview.java
Normal file
@@ -0,0 +1,18 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* Indicates that the method/class/etc marked maps to GitHub API in the preview period.
|
||||
*
|
||||
* These APIs are subject to change and not a part of the backward compatibility commitment.
|
||||
* Always used in conjunction with 'deprecated' to raise awareness to clients.
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface Preview {
|
||||
}
|
||||
11
src/main/java/org/kohsuke/github/Previews.java
Normal file
11
src/main/java/org/kohsuke/github/Previews.java
Normal file
@@ -0,0 +1,11 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
/**
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
/*package*/ class Previews {
|
||||
static final String LOKI = "application/vnd.github.loki-preview+json";
|
||||
static final String DRAX = "application/vnd.github.drax-preview+json";
|
||||
static final String SQUIRREL_GIRL = "application/vnd.github.squirrel-girl-preview";
|
||||
static final String CLOAK = "application/vnd.github.cloak-preview";
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import java.net.HttpURLConnection;
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
* @see GitHubBuilder#withRateLimitHandler(RateLimitHandler)
|
||||
* @see AbuseLimitHandler
|
||||
*/
|
||||
public abstract class RateLimitHandler {
|
||||
/**
|
||||
|
||||
23
src/main/java/org/kohsuke/github/Reactable.java
Normal file
23
src/main/java/org/kohsuke/github/Reactable.java
Normal file
@@ -0,0 +1,23 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Those {@link GHObject}s that can have {@linkplain GHReaction reactions}.
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
@Preview @Deprecated
|
||||
public interface Reactable {
|
||||
/**
|
||||
* List all the reactions left to this object.
|
||||
*/
|
||||
@Preview @Deprecated
|
||||
PagedIterable<GHReaction> listReactions();
|
||||
|
||||
/**
|
||||
* Leaves a reaction to this object.
|
||||
*/
|
||||
@Preview @Deprecated
|
||||
GHReaction createReaction(ReactionContent content) throws IOException;
|
||||
}
|
||||
40
src/main/java/org/kohsuke/github/ReactionContent.java
Normal file
40
src/main/java/org/kohsuke/github/ReactionContent.java
Normal file
@@ -0,0 +1,40 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
|
||||
/**
|
||||
* Content of reactions.
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
* @see <a href="https://developer.github.com/v3/reactions/">API documentation</a>
|
||||
* @see GHReaction
|
||||
*/
|
||||
public enum ReactionContent {
|
||||
PLUS_ONE("+1"),
|
||||
MINUS_ONE("-1"),
|
||||
LAUGH("laugh"),
|
||||
CONFUSED("confused"),
|
||||
HEART("heart"),
|
||||
HOORAY("hooray");
|
||||
|
||||
private final String content;
|
||||
|
||||
ReactionContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
@JsonValue
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
@JsonCreator
|
||||
public static ReactionContent forContent(String content) {
|
||||
for (ReactionContent c : ReactionContent.values()) {
|
||||
if (c.getContent().equals(content))
|
||||
return c;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -24,8 +24,12 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import javax.annotation.CheckForNull;
|
||||
import javax.annotation.WillClose;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@@ -37,24 +41,30 @@ import java.lang.reflect.Field;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.ProtocolException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.URL;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.kohsuke.github.GitHub.*;
|
||||
import static java.util.logging.Level.FINE;
|
||||
import static java.util.logging.Level.FINEST;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static org.apache.commons.lang.StringUtils.defaultString;
|
||||
import static org.kohsuke.github.GitHub.MAPPER;
|
||||
|
||||
/**
|
||||
* A builder pattern for making HTTP call and parsing its output.
|
||||
@@ -62,8 +72,6 @@ import static org.kohsuke.github.GitHub.*;
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
class Requester {
|
||||
private static final List<String> METHODS_WITHOUT_BODY = asList("GET", "DELETE");
|
||||
|
||||
private final GitHub root;
|
||||
private final List<Entry> args = new ArrayList<Entry>();
|
||||
private final Map<String,String> headers = new LinkedHashMap<String, String>();
|
||||
@@ -72,13 +80,14 @@ class Requester {
|
||||
* Request method.
|
||||
*/
|
||||
private String method = "POST";
|
||||
private String contentType = "application/x-www-form-urlencoded";
|
||||
private String contentType = null;
|
||||
private InputStream body;
|
||||
|
||||
/**
|
||||
* Current connection.
|
||||
*/
|
||||
private HttpURLConnection uc;
|
||||
private boolean forceBody;
|
||||
|
||||
private static class Entry {
|
||||
String key;
|
||||
@@ -103,6 +112,15 @@ class Requester {
|
||||
headers.put(name,value);
|
||||
}
|
||||
|
||||
public Requester withHeader(String name, String value) {
|
||||
setHeader(name,value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/*package*/ Requester withPreview(String name) {
|
||||
return withHeader("Accept",name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a request with authentication credential.
|
||||
*/
|
||||
@@ -117,9 +135,13 @@ class Requester {
|
||||
return _with(key, value);
|
||||
}
|
||||
|
||||
public Requester with(String key, long value) {
|
||||
return _with(key, value);
|
||||
}
|
||||
|
||||
public Requester with(String key, Integer value) {
|
||||
if (value!=null)
|
||||
_with(key, value.intValue());
|
||||
_with(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -130,6 +152,14 @@ class Requester {
|
||||
return _with(key, value);
|
||||
}
|
||||
|
||||
public Requester with(String key, Enum e) {
|
||||
if (e==null) return _with(key, null);
|
||||
|
||||
// by convention Java constant names are upper cases, but github uses
|
||||
// lower-case constants. GitHub also uses '-', which in Java we always
|
||||
// replace by '_'
|
||||
return with(key, e.toString().toLowerCase(Locale.ENGLISH).replace('_', '-'));
|
||||
}
|
||||
|
||||
public Requester with(String key, String value) {
|
||||
return _with(key, value);
|
||||
@@ -143,11 +173,16 @@ class Requester {
|
||||
return _with(key, value);
|
||||
}
|
||||
|
||||
public Requester with(InputStream body) {
|
||||
public Requester with(@WillClose/*later*/ InputStream body) {
|
||||
this.body = body;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Requester withNullable(String key, Object value) {
|
||||
args.add(new Entry(key, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Requester _with(String key, Object value) {
|
||||
if (value!=null) {
|
||||
args.add(new Entry(key,value));
|
||||
@@ -178,6 +213,16 @@ class Requester {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Small number of GitHub APIs use HTTP methods somewhat inconsistently, and use a body where it's not expected.
|
||||
* Normally whether parameters go as query parameters or a body depends on the HTTP verb in use,
|
||||
* but this method forces the parameters to be sent as a body.
|
||||
*/
|
||||
/*package*/ Requester inBody() {
|
||||
forceBody = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void to(String tailApiUrl) throws IOException {
|
||||
to(tailApiUrl,null);
|
||||
}
|
||||
@@ -206,19 +251,24 @@ class Requester {
|
||||
*/
|
||||
@Deprecated
|
||||
public <T> T to(String tailApiUrl, Class<T> type, String method) throws IOException {
|
||||
return method(method).to(tailApiUrl,type);
|
||||
return method(method).to(tailApiUrl, type);
|
||||
}
|
||||
|
||||
@SuppressFBWarnings("SBSC_USE_STRINGBUFFER_CONCATENATION")
|
||||
private <T> T _to(String tailApiUrl, Class<T> type, T instance) throws IOException {
|
||||
while (true) {// loop while API rate limit is hit
|
||||
if (METHODS_WITHOUT_BODY.contains(method) && !args.isEmpty()) {
|
||||
StringBuilder qs=new StringBuilder();
|
||||
for (Entry arg : args) {
|
||||
qs.append(qs.length()==0 ? '?' : '&');
|
||||
qs.append(arg.key).append('=').append(URLEncoder.encode(arg.value.toString(),"UTF-8"));
|
||||
if (!isMethodWithBody() && !args.isEmpty()) {
|
||||
boolean questionMarkFound = tailApiUrl.indexOf('?') != -1;
|
||||
tailApiUrl += questionMarkFound ? '&' : '?';
|
||||
for (Iterator<Entry> it = args.listIterator(); it.hasNext();) {
|
||||
Entry arg = it.next();
|
||||
tailApiUrl += arg.key + '=' + URLEncoder.encode(arg.value.toString(),"UTF-8");
|
||||
if (it.hasNext()) {
|
||||
tailApiUrl += '&';
|
||||
}
|
||||
tailApiUrl += qs.toString();
|
||||
}
|
||||
}
|
||||
|
||||
while (true) {// loop while API rate limit is hit
|
||||
setupConnection(root.getApiURL(tailApiUrl));
|
||||
|
||||
buildRequest();
|
||||
@@ -233,7 +283,7 @@ class Requester {
|
||||
if (nextLinkMatcher.find()) {
|
||||
final String link = nextLinkMatcher.group(1);
|
||||
T nextResult = _to(link, type, instance);
|
||||
|
||||
setResponseHeaders(nextResult);
|
||||
final int resultLength = Array.getLength(result);
|
||||
final int nextResultLength = Array.getLength(nextResult);
|
||||
T concatResult = (T) Array.newInstance(type.getComponentType(), resultLength + nextResultLength);
|
||||
@@ -243,9 +293,11 @@ class Requester {
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
return setResponseHeaders(result);
|
||||
} catch (IOException e) {
|
||||
handleApiError(e);
|
||||
} finally {
|
||||
noteRateLimit(tailApiUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -255,6 +307,7 @@ class Requester {
|
||||
*/
|
||||
public int asHttpStatusCode(String tailApiUrl) throws IOException {
|
||||
while (true) {// loop while API rate limit is hit
|
||||
method("GET");
|
||||
setupConnection(root.getApiURL(tailApiUrl));
|
||||
|
||||
buildRequest();
|
||||
@@ -263,6 +316,8 @@ class Requester {
|
||||
return uc.getResponseCode();
|
||||
} catch (IOException e) {
|
||||
handleApiError(e);
|
||||
} finally {
|
||||
noteRateLimit(tailApiUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -277,6 +332,59 @@ class Requester {
|
||||
return wrapStream(uc.getInputStream());
|
||||
} catch (IOException e) {
|
||||
handleApiError(e);
|
||||
} finally {
|
||||
noteRateLimit(tailApiUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void noteRateLimit(String tailApiUrl) {
|
||||
if ("/rate_limit".equals(tailApiUrl)) {
|
||||
// the rate_limit API is "free"
|
||||
return;
|
||||
}
|
||||
if (tailApiUrl.startsWith("/search")) {
|
||||
// the search API uses a different rate limit
|
||||
return;
|
||||
}
|
||||
String limit = uc.getHeaderField("X-RateLimit-Limit");
|
||||
if (StringUtils.isBlank(limit)) {
|
||||
// if we are missing a header, return fast
|
||||
return;
|
||||
}
|
||||
String remaining = uc.getHeaderField("X-RateLimit-Remaining");
|
||||
if (StringUtils.isBlank(remaining)) {
|
||||
// if we are missing a header, return fast
|
||||
return;
|
||||
}
|
||||
String reset = uc.getHeaderField("X-RateLimit-Reset");
|
||||
if (StringUtils.isBlank(reset)) {
|
||||
// if we are missing a header, return fast
|
||||
return;
|
||||
}
|
||||
GHRateLimit observed = new GHRateLimit();
|
||||
try {
|
||||
observed.limit = Integer.parseInt(limit);
|
||||
} catch (NumberFormatException e) {
|
||||
if (LOGGER.isLoggable(FINEST)) {
|
||||
LOGGER.log(FINEST, "Malformed X-RateLimit-Limit header value " + limit, e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
try {
|
||||
observed.remaining = Integer.parseInt(remaining);
|
||||
} catch (NumberFormatException e) {
|
||||
if (LOGGER.isLoggable(FINEST)) {
|
||||
LOGGER.log(FINEST, "Malformed X-RateLimit-Remaining header value " + remaining, e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
try {
|
||||
observed.reset = new Date(Long.parseLong(reset)); // this is madness, storing the date as seconds
|
||||
root.updateRateLimit(observed);
|
||||
} catch (NumberFormatException e) {
|
||||
if (LOGGER.isLoggable(FINEST)) {
|
||||
LOGGER.log(FINEST, "Malformed X-RateLimit-Reset header value " + reset, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -292,18 +400,19 @@ class Requester {
|
||||
private void buildRequest() throws IOException {
|
||||
if (isMethodWithBody()) {
|
||||
uc.setDoOutput(true);
|
||||
uc.setRequestProperty("Content-type", contentType);
|
||||
|
||||
if (body == null) {
|
||||
uc.setRequestProperty("Content-type", defaultString(contentType,"application/json"));
|
||||
Map json = new HashMap();
|
||||
for (Entry e : args) {
|
||||
json.put(e.key, e.value);
|
||||
}
|
||||
MAPPER.writeValue(uc.getOutputStream(), json);
|
||||
} else {
|
||||
uc.setRequestProperty("Content-type", defaultString(contentType,"application/x-www-form-urlencoded"));
|
||||
try {
|
||||
byte[] bytes = new byte[32768];
|
||||
int read = 0;
|
||||
int read;
|
||||
while ((read = body.read(bytes)) != -1) {
|
||||
uc.getOutputStream().write(bytes, 0, read);
|
||||
}
|
||||
@@ -315,109 +424,124 @@ class Requester {
|
||||
}
|
||||
|
||||
private boolean isMethodWithBody() {
|
||||
return !METHODS_WITHOUT_BODY.contains(method);
|
||||
return forceBody || !METHODS_WITHOUT_BODY.contains(method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads pagenated resources.
|
||||
* Loads paginated resources.
|
||||
*
|
||||
* Every iterator call reports a new batch.
|
||||
*/
|
||||
/*package*/ <T> Iterator<T> asIterator(String _tailApiUrl, final Class<T> type) {
|
||||
/*package*/ <T> Iterator<T> asIterator(String tailApiUrl, Class<T> type, int pageSize) {
|
||||
method("GET");
|
||||
|
||||
if (pageSize!=0)
|
||||
args.add(new Entry("per_page",pageSize));
|
||||
|
||||
StringBuilder s = new StringBuilder(tailApiUrl);
|
||||
if (!args.isEmpty()) {
|
||||
boolean first=true;
|
||||
boolean first = true;
|
||||
try {
|
||||
for (Entry a : args) {
|
||||
_tailApiUrl += first ? '?' : '&';
|
||||
s.append(first ? '?' : '&');
|
||||
first = false;
|
||||
_tailApiUrl += URLEncoder.encode(a.key,"UTF-8")+'='+URLEncoder.encode(a.value.toString(),"UTF-8");
|
||||
s.append(URLEncoder.encode(a.key, "UTF-8"));
|
||||
s.append('=');
|
||||
s.append(URLEncoder.encode(a.value.toString(), "UTF-8"));
|
||||
}
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new AssertionError(e); // UTF-8 is mandatory
|
||||
}
|
||||
}
|
||||
|
||||
final String tailApiUrl = _tailApiUrl;
|
||||
try {
|
||||
return new PagingIterator<T>(type, tailApiUrl, root.getApiURL(s.toString()));
|
||||
} catch (IOException e) {
|
||||
throw new GHException("Unable to build github Api URL",e);
|
||||
}
|
||||
}
|
||||
|
||||
return new Iterator<T>() {
|
||||
/**
|
||||
* The next batch to be returned from {@link #next()}.
|
||||
*/
|
||||
T next;
|
||||
/**
|
||||
* URL of the next resource to be retrieved, or null if no more data is available.
|
||||
*/
|
||||
URL url;
|
||||
class PagingIterator<T> implements Iterator<T> {
|
||||
|
||||
{
|
||||
try {
|
||||
url = root.getApiURL(tailApiUrl);
|
||||
} catch (IOException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
private final Class<T> type;
|
||||
private final String tailApiUrl;
|
||||
|
||||
public boolean hasNext() {
|
||||
fetch();
|
||||
return next!=null;
|
||||
}
|
||||
/**
|
||||
* The next batch to be returned from {@link #next()}.
|
||||
*/
|
||||
private T next;
|
||||
|
||||
public T next() {
|
||||
fetch();
|
||||
T r = next;
|
||||
if (r==null) throw new NoSuchElementException();
|
||||
next = null;
|
||||
return r;
|
||||
}
|
||||
/**
|
||||
* URL of the next resource to be retrieved, or null if no more data is available.
|
||||
*/
|
||||
private URL url;
|
||||
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
PagingIterator(Class<T> type, String tailApiUrl, URL url) {
|
||||
this.type = type;
|
||||
this.tailApiUrl = tailApiUrl;
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
private void fetch() {
|
||||
if (next!=null) return; // already fetched
|
||||
if (url==null) return; // no more data to fetch
|
||||
public boolean hasNext() {
|
||||
fetch();
|
||||
return next!=null;
|
||||
}
|
||||
|
||||
try {
|
||||
while (true) {// loop while API rate limit is hit
|
||||
setupConnection(url);
|
||||
try {
|
||||
next = parse(type,null);
|
||||
assert next!=null;
|
||||
findNextURL();
|
||||
return;
|
||||
} catch (IOException e) {
|
||||
handleApiError(e);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
public T next() {
|
||||
fetch();
|
||||
T r = next;
|
||||
if (r==null) throw new NoSuchElementException();
|
||||
next = null;
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locate the next page from the pagination "Link" tag.
|
||||
*/
|
||||
private void findNextURL() throws MalformedURLException {
|
||||
url = null; // start defensively
|
||||
String link = uc.getHeaderField("Link");
|
||||
if (link==null) return;
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
for (String token : link.split(", ")) {
|
||||
if (token.endsWith("rel=\"next\"")) {
|
||||
// found the next page. This should look something like
|
||||
// <https://api.github.com/repos?page=3&per_page=100>; rel="next"
|
||||
int idx = token.indexOf('>');
|
||||
url = new URL(token.substring(1,idx));
|
||||
private void fetch() {
|
||||
if (next!=null) return; // already fetched
|
||||
if (url==null) return; // no more data to fetch
|
||||
|
||||
try {
|
||||
while (true) {// loop while API rate limit is hit
|
||||
setupConnection(url);
|
||||
try {
|
||||
next = parse(type,null);
|
||||
assert next!=null;
|
||||
findNextURL();
|
||||
return;
|
||||
} catch (IOException e) {
|
||||
handleApiError(e);
|
||||
} finally {
|
||||
noteRateLimit(tailApiUrl);
|
||||
}
|
||||
}
|
||||
|
||||
// no more "next" link. we are done.
|
||||
} catch (IOException e) {
|
||||
throw new GHException("Failed to retrieve "+url);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Locate the next page from the pagination "Link" tag.
|
||||
*/
|
||||
private void findNextURL() throws MalformedURLException {
|
||||
url = null; // start defensively
|
||||
String link = uc.getHeaderField("Link");
|
||||
if (link==null) return;
|
||||
|
||||
for (String token : link.split(", ")) {
|
||||
if (token.endsWith("rel=\"next\"")) {
|
||||
// found the next page. This should look something like
|
||||
// <https://api.github.com/repos?page=3&per_page=100>; rel="next"
|
||||
int idx = token.indexOf('>');
|
||||
url = new URL(token.substring(1,idx));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// no more "next" link. we are done.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -435,6 +559,11 @@ class Requester {
|
||||
uc.setRequestProperty(e.getKey(), v);
|
||||
}
|
||||
|
||||
setRequestMethod(uc);
|
||||
uc.setRequestProperty("Accept-Encoding", "gzip");
|
||||
}
|
||||
|
||||
private void setRequestMethod(HttpURLConnection uc) throws IOException {
|
||||
try {
|
||||
uc.setRequestMethod(method);
|
||||
} catch (ProtocolException e) {
|
||||
@@ -446,31 +575,87 @@ class Requester {
|
||||
} catch (Exception x) {
|
||||
throw (IOException)new IOException("Failed to set the custom verb").initCause(x);
|
||||
}
|
||||
// sun.net.www.protocol.https.DelegatingHttpsURLConnection delegates to another HttpURLConnection
|
||||
try {
|
||||
Field $delegate = uc.getClass().getDeclaredField("delegate");
|
||||
$delegate.setAccessible(true);
|
||||
Object delegate = $delegate.get(uc);
|
||||
if (delegate instanceof HttpURLConnection) {
|
||||
HttpURLConnection nested = (HttpURLConnection) delegate;
|
||||
setRequestMethod(nested);
|
||||
}
|
||||
} catch (NoSuchFieldException x) {
|
||||
// no problem
|
||||
} catch (IllegalAccessException x) {
|
||||
throw (IOException)new IOException("Failed to set the custom verb").initCause(x);
|
||||
}
|
||||
}
|
||||
uc.setRequestProperty("Accept-Encoding", "gzip");
|
||||
if (!uc.getRequestMethod().equals(method))
|
||||
throw new IllegalStateException("Failed to set the request method to "+method);
|
||||
}
|
||||
|
||||
@CheckForNull
|
||||
private <T> T parse(Class<T> type, T instance) throws IOException {
|
||||
if (uc.getResponseCode()==304)
|
||||
return null; // special case handling for 304 unmodified, as the content will be ""
|
||||
return parse(type, instance, 2);
|
||||
}
|
||||
|
||||
private <T> T parse(Class<T> type, T instance, int timeouts) throws IOException {
|
||||
InputStreamReader r = null;
|
||||
int responseCode = -1;
|
||||
String responseMessage = null;
|
||||
try {
|
||||
responseCode = uc.getResponseCode();
|
||||
responseMessage = uc.getResponseMessage();
|
||||
if (responseCode == 304) {
|
||||
return null; // special case handling for 304 unmodified, as the content will be ""
|
||||
}
|
||||
if (responseCode == 204 && type!=null && type.isArray()) {
|
||||
// no content
|
||||
return type.cast(Array.newInstance(type.getComponentType(),0));
|
||||
}
|
||||
|
||||
r = new InputStreamReader(wrapStream(uc.getInputStream()), "UTF-8");
|
||||
String data = IOUtils.toString(r);
|
||||
if (type!=null)
|
||||
try {
|
||||
return MAPPER.readValue(data,type);
|
||||
return setResponseHeaders(MAPPER.readValue(data, type));
|
||||
} catch (JsonMappingException e) {
|
||||
throw (IOException)new IOException("Failed to deserialize "+data).initCause(e);
|
||||
throw (IOException)new IOException("Failed to deserialize " +data).initCause(e);
|
||||
}
|
||||
if (instance!=null)
|
||||
return MAPPER.readerForUpdating(instance).<T>readValue(data);
|
||||
if (instance!=null) {
|
||||
return setResponseHeaders(MAPPER.readerForUpdating(instance).<T>readValue(data));
|
||||
}
|
||||
return null;
|
||||
} catch (FileNotFoundException e) {
|
||||
// java.net.URLConnection handles 404 exception has FileNotFoundException, don't wrap exception in HttpException
|
||||
// to preserve backward compatibility
|
||||
throw e;
|
||||
} catch (IOException e) {
|
||||
if (e instanceof SocketTimeoutException && timeouts > 0) {
|
||||
LOGGER.log(INFO, "timed out accessing " + uc.getURL() + "; will try " + timeouts + " more time(s)", e);
|
||||
return parse(type, instance, timeouts - 1);
|
||||
}
|
||||
throw new HttpException(responseCode, responseMessage, uc.getURL(), e);
|
||||
} finally {
|
||||
IOUtils.closeQuietly(r);
|
||||
}
|
||||
}
|
||||
|
||||
private <T> T setResponseHeaders(T readValue) {
|
||||
if (readValue instanceof GHObject[]) {
|
||||
for (GHObject ghObject : (GHObject[]) readValue) {
|
||||
setResponseHeaders(ghObject);
|
||||
}
|
||||
} else if (readValue instanceof GHObject) {
|
||||
setResponseHeaders((GHObject) readValue);
|
||||
}
|
||||
return readValue;
|
||||
}
|
||||
|
||||
private void setResponseHeaders(GHObject readValue) {
|
||||
readValue.responseHeaderFields = uc.getHeaderFields();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the "Content-Encoding" header.
|
||||
*/
|
||||
@@ -486,32 +671,53 @@ class Requester {
|
||||
* Handle API error by either throwing it or by returning normally to retry.
|
||||
*/
|
||||
/*package*/ void handleApiError(IOException e) throws IOException {
|
||||
if (uc.getResponseCode() == 401) // Unauthorized == bad creds
|
||||
int responseCode;
|
||||
try {
|
||||
responseCode = uc.getResponseCode();
|
||||
} catch (IOException e2) {
|
||||
// likely to be a network exception (e.g. SSLHandshakeException),
|
||||
// uc.getResponseCode() and any other getter on the response will cause an exception
|
||||
if (LOGGER.isLoggable(FINE))
|
||||
LOGGER.log(FINE, "Silently ignore exception retrieving response code for '" + uc.getURL() + "'" +
|
||||
" handling exception " + e, e);
|
||||
throw e;
|
||||
}
|
||||
InputStream es = wrapStream(uc.getErrorStream());
|
||||
if (es != null) {
|
||||
try {
|
||||
String error = IOUtils.toString(es, "UTF-8");
|
||||
if (e instanceof FileNotFoundException) {
|
||||
// pass through 404 Not Found to allow the caller to handle it intelligently
|
||||
e = (IOException) new GHFileNotFoundException(error).withResponseHeaderFields(uc).initCause(e);
|
||||
} else if (e instanceof HttpException) {
|
||||
HttpException http = (HttpException) e;
|
||||
e = new HttpException(error, http.getResponseCode(), http.getResponseMessage(),
|
||||
http.getUrl(), e);
|
||||
} else {
|
||||
e = (IOException) new GHIOException(error).withResponseHeaderFields(uc).initCause(e);
|
||||
}
|
||||
} finally {
|
||||
IOUtils.closeQuietly(es);
|
||||
}
|
||||
}
|
||||
if (responseCode == HttpURLConnection.HTTP_UNAUTHORIZED) // 401 / Unauthorized == bad creds
|
||||
throw e;
|
||||
|
||||
if ("0".equals(uc.getHeaderField("X-RateLimit-Remaining"))) {
|
||||
root.rateLimitHandler.onError(e,uc);
|
||||
return;
|
||||
}
|
||||
|
||||
InputStream es = wrapStream(uc.getErrorStream());
|
||||
try {
|
||||
if (es!=null) {
|
||||
if (e instanceof FileNotFoundException) {
|
||||
// pass through 404 Not Found to allow the caller to handle it intelligently
|
||||
throw (IOException) new FileNotFoundException(IOUtils.toString(es, "UTF-8")).initCause(e);
|
||||
} else
|
||||
throw (IOException) new IOException(IOUtils.toString(es, "UTF-8")).initCause(e);
|
||||
} else
|
||||
throw e;
|
||||
} finally {
|
||||
IOUtils.closeQuietly(es);
|
||||
// Retry-After is not documented but apparently that field exists
|
||||
if (responseCode == HttpURLConnection.HTTP_FORBIDDEN &&
|
||||
uc.getHeaderField("Retry-After") != null) {
|
||||
this.root.abuseLimitHandler.onError(e,uc);
|
||||
return;
|
||||
}
|
||||
|
||||
throw e;
|
||||
}
|
||||
|
||||
private Set<String> toSet(String s) {
|
||||
Set<String> r = new HashSet<String>();
|
||||
for (String t : s.split(","))
|
||||
r.add(t.trim());
|
||||
return r;
|
||||
}
|
||||
private static final List<String> METHODS_WITHOUT_BODY = asList("GET", "DELETE");
|
||||
private static final Logger LOGGER = Logger.getLogger(Requester.class.getName());
|
||||
}
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
package org.kohsuke.github;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
|
||||
/**
|
||||
* Represents the result of a search
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
abstract class SearchResult<T> {
|
||||
@SuppressFBWarnings(value = "UWF_UNWRITTEN_FIELD", justification = "Field comes from JSON deserialization")
|
||||
int total_count;
|
||||
|
||||
@SuppressFBWarnings(value = "UWF_UNWRITTEN_FIELD", justification = "Field comes from JSON deserialization")
|
||||
boolean incomplete_results;
|
||||
|
||||
/**
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user