mirror of
https://github.com/jlengrand/github-api.git
synced 2026-03-11 00:11:25 +00:00
Compare commits
679 Commits
github-api
...
github-api
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e92f1321d4 | ||
|
|
208904b634 | ||
|
|
a433bcda2e | ||
|
|
77baafa643 | ||
|
|
224d8c7cb4 | ||
|
|
0feb520549 | ||
|
|
ca365b12f6 | ||
|
|
bde6ad9a06 | ||
|
|
4953f4500d | ||
|
|
4415ac8fd2 | ||
|
|
8c81e48a31 | ||
|
|
9ad0329c56 | ||
|
|
78f533bbfc | ||
|
|
79c7dd9ecf | ||
|
|
5d796d1f79 | ||
|
|
68a82be6c4 | ||
|
|
2676ef2b73 | ||
|
|
04b283c539 | ||
|
|
98b067937a | ||
|
|
8ababb60bf | ||
|
|
b51d655f77 | ||
|
|
74496d32da | ||
|
|
316e278be1 | ||
|
|
d881bf6504 | ||
|
|
c74fbbe1fd | ||
|
|
929d9fb7bd | ||
|
|
5d069d0531 | ||
|
|
dd9e6dc5d3 | ||
|
|
d22c77c41d | ||
|
|
3a11b7ccbf | ||
|
|
9d161b28bb | ||
|
|
9b16a1caa0 | ||
|
|
9a918e3bac | ||
|
|
d4c5c6a1e0 | ||
|
|
63fda3555c | ||
|
|
6a2381c06b | ||
|
|
e9c0a16c26 | ||
|
|
2101a67ac1 | ||
|
|
ddac568aaa | ||
|
|
262ae9f635 | ||
|
|
381502fb80 | ||
|
|
92fb441eb2 | ||
|
|
29e08037a8 | ||
|
|
84cc6d9315 | ||
|
|
b8d5a1c732 | ||
|
|
0197ab9661 | ||
|
|
b7915e61a6 | ||
|
|
586db99450 | ||
|
|
5377d0dd18 | ||
|
|
c5d3a7d573 | ||
|
|
8267050f06 | ||
|
|
c21bd5765a | ||
|
|
b78c37a695 | ||
|
|
2f151d45c3 | ||
|
|
3ebe3afdbd | ||
|
|
f4845df6c0 | ||
|
|
272b87f04d | ||
|
|
6e3f754366 | ||
|
|
6360112432 | ||
|
|
f1ca0b5417 | ||
|
|
0894c8007c | ||
|
|
0456f10709 | ||
|
|
b7d03f7463 | ||
|
|
07a392c2a7 | ||
|
|
5b69de770f | ||
|
|
bf67069768 | ||
|
|
91764c1c74 | ||
|
|
8b2a3e1221 | ||
|
|
def2f0b37d | ||
|
|
5d7479a3dd | ||
|
|
ceb2d35f9f | ||
|
|
fc38dba59a | ||
|
|
75b383d398 | ||
|
|
ee2d9491fb | ||
|
|
bf86a7c75a | ||
|
|
70f6d129e2 | ||
|
|
a4ac2aa99a | ||
|
|
ae3b6fbe6b | ||
|
|
e357fca963 | ||
|
|
c84cc89805 | ||
|
|
181238cd50 | ||
|
|
214c24c736 | ||
|
|
cf51ce8f26 | ||
|
|
2b7ed40d01 | ||
|
|
349ef7a54c | ||
|
|
94df5fc389 | ||
|
|
906238a297 | ||
|
|
7963fa82b5 | ||
|
|
1aba6012fb | ||
|
|
ff4324ac67 | ||
|
|
11bc669e1d | ||
|
|
dcf26d58e4 | ||
|
|
4d46872c35 | ||
|
|
4f0d62f421 | ||
|
|
f7ad1f517b | ||
|
|
345d6197f3 | ||
|
|
bb4d44138a | ||
|
|
a8ef0cde53 | ||
|
|
77dc009c95 | ||
|
|
aa298c93cc | ||
|
|
dfb0a5240e | ||
|
|
9cfc3c22b5 | ||
|
|
b177d98e29 | ||
|
|
5405fb0370 | ||
|
|
72a1c24b3b | ||
|
|
f146ae94ec | ||
|
|
a0bbba748a | ||
|
|
81bf818573 | ||
|
|
d5913dc292 | ||
|
|
e1e901b794 | ||
|
|
2f2f26767e | ||
|
|
bffa78c1b8 | ||
|
|
c55719c67a | ||
|
|
cb3b4a6642 | ||
|
|
92c141cee6 | ||
|
|
fd1a1a1c23 | ||
|
|
b835884b2e | ||
|
|
660763908d | ||
|
|
fe8bdb755a | ||
|
|
67dc6d2d23 | ||
|
|
9c8d73cbe2 | ||
|
|
5db97d92dd | ||
|
|
ac470dddb5 | ||
|
|
43063fe8ce | ||
|
|
59e0046c1e | ||
|
|
36ab05c265 | ||
|
|
2b2be05dae | ||
|
|
fb1adbd1ef | ||
|
|
ab68a59b25 | ||
|
|
9c7de767e9 | ||
|
|
8ba5cf7c2e | ||
|
|
b194a19b98 | ||
|
|
1d344b016f | ||
|
|
474f3ef4ca | ||
|
|
9830927020 | ||
|
|
727932a442 | ||
|
|
cd92b51845 | ||
|
|
fe26d16411 | ||
|
|
d68c66ce2b | ||
|
|
e7bfbfb48f | ||
|
|
f2a88ae61c | ||
|
|
e2113f6ee5 | ||
|
|
3867224024 | ||
|
|
9ee0bf43bc | ||
|
|
2844542efa | ||
|
|
e3fcae9392 | ||
|
|
c6ccfa91f3 | ||
|
|
b6fcee1cb9 | ||
|
|
9071befb04 | ||
|
|
bdd5fe98f3 | ||
|
|
a3d3e83a49 | ||
|
|
08bde72028 | ||
|
|
108a136368 | ||
|
|
57d87ad6b1 | ||
|
|
0c22815ff7 | ||
|
|
0ca792ecfd | ||
|
|
987c34c69e | ||
|
|
c1c02bc8ab | ||
|
|
4ee369f27c | ||
|
|
c9012efdcb | ||
|
|
41524fc67d | ||
|
|
04ff61e981 | ||
|
|
532468dc67 | ||
|
|
9c9a2dae47 | ||
|
|
c8a868b57f | ||
|
|
4b3f81ee34 | ||
|
|
afa170ba7c | ||
|
|
46e3b2272e | ||
|
|
52472e90ec | ||
|
|
4ef0d00846 | ||
|
|
580f2537f2 | ||
|
|
3d9fd96026 | ||
|
|
f449b92721 | ||
|
|
3b0216b023 | ||
|
|
98cf839737 | ||
|
|
0bb0846505 | ||
|
|
70969400a3 | ||
|
|
147e8d5d12 | ||
|
|
cacc3e6edd | ||
|
|
a284eca147 | ||
|
|
0d3ba9d7f0 | ||
|
|
be8064d642 | ||
|
|
e30dba742d | ||
|
|
44b72ed647 | ||
|
|
666bd77dac | ||
|
|
0a6613e60d | ||
|
|
62e186c123 | ||
|
|
50dd8f5bcc | ||
|
|
d5fcac9c45 | ||
|
|
c2bed85190 | ||
|
|
183b463ef2 | ||
|
|
92fdac44a0 | ||
|
|
12829ecc73 | ||
|
|
51319c3b26 | ||
|
|
8fd827040b | ||
|
|
5ec46eae0d | ||
|
|
32c03301be | ||
|
|
df7f29b2ab | ||
|
|
e863113c36 | ||
|
|
8e2c1d7382 | ||
|
|
ab7b9cccba | ||
|
|
81bf61a161 | ||
|
|
b40f008647 | ||
|
|
734e41702b | ||
|
|
038dd20a91 | ||
|
|
1dd62b8550 | ||
|
|
715deebe05 | ||
|
|
b3fe3d8590 | ||
|
|
f74c3ed3ea | ||
|
|
2c9aebeeed | ||
|
|
7474f1e11f | ||
|
|
dba9c55b64 | ||
|
|
b432364397 | ||
|
|
696967bdd1 | ||
|
|
b76889efc3 | ||
|
|
e6a7b64ebe | ||
|
|
9daa0df311 | ||
|
|
612800bda5 | ||
|
|
a6bbb1dec9 | ||
|
|
873c93ab64 | ||
|
|
d15242e2d2 | ||
|
|
992d2b937c | ||
|
|
1e05ddad4b | ||
|
|
4f8a64610b | ||
|
|
b82366218c | ||
|
|
acbe1f4cb3 | ||
|
|
4c5e018583 | ||
|
|
6c0380e85c | ||
|
|
fde48e604f | ||
|
|
e83a4de5fb | ||
|
|
927d2799dc | ||
|
|
1ad701fe5d | ||
|
|
086425d2da | ||
|
|
beca54416a | ||
|
|
c92f5c5713 | ||
|
|
dee4e6caff | ||
|
|
dd5a39e72e | ||
|
|
e5ed52165c | ||
|
|
9484f8e0f5 | ||
|
|
947caffe0a | ||
|
|
870090e8df | ||
|
|
73f07f13c5 | ||
|
|
d1952bf591 | ||
|
|
5a612e1332 | ||
|
|
b00a9faea6 | ||
|
|
74db42a703 | ||
|
|
ddf625ca04 | ||
|
|
eca2f017d8 | ||
|
|
3190bde343 | ||
|
|
c6ebf42a47 | ||
|
|
c116b60d12 | ||
|
|
5d09e6d9ab | ||
|
|
2613ce0ac9 | ||
|
|
a88e9b28ea | ||
|
|
f0a3c26ee6 | ||
|
|
84c87ecb32 | ||
|
|
6573f44d41 | ||
|
|
3cacbc552c | ||
|
|
343d623e02 | ||
|
|
6b80bb2b11 | ||
|
|
56fe7452eb | ||
|
|
d3a66f6605 | ||
|
|
dd7b4712f1 | ||
|
|
9df5871f6b | ||
|
|
29aab9e9f4 | ||
|
|
af67eb7f0b | ||
|
|
10482c0141 | ||
|
|
a7a792251a | ||
|
|
aec2308144 | ||
|
|
0741b8aa6a | ||
|
|
3082622394 | ||
|
|
965c9cb0af | ||
|
|
495a46e2d8 | ||
|
|
05bda1192e | ||
|
|
6058af0ca1 | ||
|
|
1eb8bf9719 | ||
|
|
afc02faeda | ||
|
|
66f22de90f | ||
|
|
2949a2e0ff | ||
|
|
ba12efea9d | ||
|
|
e1180a12fb | ||
|
|
1393706f13 | ||
|
|
6f994f31f7 | ||
|
|
38aa99a063 | ||
|
|
85c44b3529 | ||
|
|
e1a2768de5 | ||
|
|
e1c9b27203 | ||
|
|
969f6ef826 | ||
|
|
7abc4d4e76 | ||
|
|
ac97147c1f | ||
|
|
dbd20fe396 | ||
|
|
44e57c9c4b | ||
|
|
488e5e531f | ||
|
|
42a6a8d770 | ||
|
|
f8e877ea05 | ||
|
|
65d6fc7272 | ||
|
|
63ce8e461b | ||
|
|
fbf4c48461 | ||
|
|
81a55db644 | ||
|
|
4d4edfa181 | ||
|
|
6f9182f1f6 | ||
|
|
fa600c03e2 | ||
|
|
4a53301e9f | ||
|
|
676984b3d5 | ||
|
|
e6d7f7248b | ||
|
|
50903b5c4a | ||
|
|
01e399fb91 | ||
|
|
911aeb7af0 | ||
|
|
7e5cd9abbc | ||
|
|
115527a21a | ||
|
|
eff4f4f601 | ||
|
|
16e0099a0d | ||
|
|
2c8c678275 | ||
|
|
3b51e87fbf | ||
|
|
6c6eef5e2b | ||
|
|
6e5910f44c | ||
|
|
a967189bc6 | ||
|
|
7069176cf6 | ||
|
|
44dcbe773d | ||
|
|
ca76975461 | ||
|
|
83122ac99e | ||
|
|
c3e9458555 | ||
|
|
057ba38873 | ||
|
|
81d7d6236b | ||
|
|
191dd49653 | ||
|
|
21e9dd6f51 | ||
|
|
cc2d14acc6 | ||
|
|
87f37e9f1c | ||
|
|
d536a9f874 | ||
|
|
b45f353fa9 | ||
|
|
a3073ec14e | ||
|
|
f77eb33029 | ||
|
|
c1c919097a | ||
|
|
e05348463c | ||
|
|
fdcf74eaf2 | ||
|
|
6d57a3e3b9 | ||
|
|
1f449c866e | ||
|
|
e12deccd24 | ||
|
|
3184ebb5ee | ||
|
|
4a35ed2b35 | ||
|
|
5c9474d1c8 | ||
|
|
2321dc50c5 | ||
|
|
4efd2e8184 | ||
|
|
e30e153bfa | ||
|
|
2724211535 | ||
|
|
81068de0f1 | ||
|
|
7d842175f7 | ||
|
|
e0aee9f361 | ||
|
|
df576e2738 | ||
|
|
bb1356b25d | ||
|
|
1b67960da4 | ||
|
|
d76718e8b2 | ||
|
|
76c51922f1 | ||
|
|
f95e89a136 | ||
|
|
2dff60a23c | ||
|
|
95f83d1a29 | ||
|
|
b875ccecc1 | ||
|
|
e4c3802f16 | ||
|
|
081e485f4f | ||
|
|
4adf88da19 | ||
|
|
31e2b1b8d3 | ||
|
|
bd28abd343 | ||
|
|
955690b124 | ||
|
|
fa6f06ae15 | ||
|
|
263de140c5 | ||
|
|
ed85d06d69 | ||
|
|
4ff0870df8 | ||
|
|
410bac2040 | ||
|
|
38b1e367b1 | ||
|
|
3cddffa37f | ||
|
|
ea7a1a7175 | ||
|
|
36b5601588 | ||
|
|
7fc68f2969 | ||
|
|
c5ee07add4 | ||
|
|
32ff315b6b | ||
|
|
f919346f8f | ||
|
|
279df00404 | ||
|
|
bfd4b17fa0 | ||
|
|
5fe2817164 | ||
|
|
b337bb39bc | ||
|
|
65ae41c5f1 | ||
|
|
796c644c4a | ||
|
|
bfd9023a27 | ||
|
|
c9cdf5d03e | ||
|
|
f60bb41ad9 | ||
|
|
c333903b4a | ||
|
|
dd55e8a22c | ||
|
|
3ab9381d0a | ||
|
|
58f1fe0671 | ||
|
|
82b9c05d0f | ||
|
|
7c9397f7f6 | ||
|
|
6214b6a3ff | ||
|
|
883c8cc4c8 | ||
|
|
8d47c72913 | ||
|
|
89a6664e45 | ||
|
|
30d792d6e1 | ||
|
|
3745bf3157 | ||
|
|
a7fda3e50d | ||
|
|
7f07204fef | ||
|
|
8b51a44b7c | ||
|
|
c499c73dcc | ||
|
|
c01f3f5e8a | ||
|
|
2aef35655f | ||
|
|
7ddf1f5830 | ||
|
|
b2c513ea42 | ||
|
|
4c30f94355 | ||
|
|
e911e86c4c | ||
|
|
ca640b3f64 | ||
|
|
b4c4a05f3b | ||
|
|
fd3c36a259 | ||
|
|
d8274ac2d4 | ||
|
|
9c7479f953 | ||
|
|
b5dc3c4366 | ||
|
|
26b8082155 | ||
|
|
418df15f7b | ||
|
|
31ed0125b8 | ||
|
|
494318b879 | ||
|
|
f554ddc372 | ||
|
|
03de12c221 | ||
|
|
6c41f22b57 | ||
|
|
7ae96388e3 | ||
|
|
e8b4de00d2 | ||
|
|
cd7963b30d | ||
|
|
0dc44cffcf | ||
|
|
7a650132c5 | ||
|
|
c7fb390c38 | ||
|
|
572ff9df19 | ||
|
|
b715e0cef7 | ||
|
|
36ab2a889f | ||
|
|
a78d2f28d7 | ||
|
|
7d5a39ed89 | ||
|
|
772272ff36 | ||
|
|
2ab4eafee9 | ||
|
|
b15e0d4c45 | ||
|
|
b8180314d8 | ||
|
|
fcb8d03a0f | ||
|
|
09ec89bc2e | ||
|
|
863ad0f486 | ||
|
|
79a1bb3571 | ||
|
|
9f1d7323c7 | ||
|
|
64a82f4785 | ||
|
|
f37e4bd76e | ||
|
|
98ef2cc640 | ||
|
|
134222fd69 | ||
|
|
0cb2371517 | ||
|
|
b7de4359fd | ||
|
|
2607d6a107 | ||
|
|
db46b1ce13 | ||
|
|
d7b08d5207 | ||
|
|
29fbba832c | ||
|
|
fd621a442a | ||
|
|
a1a73568ae | ||
|
|
3daccbd6ec | ||
|
|
293deadb48 | ||
|
|
452b56c47b | ||
|
|
5cb6bfa633 | ||
|
|
0515cee6f3 | ||
|
|
4247112539 | ||
|
|
8d3374f574 | ||
|
|
26833e5f7c | ||
|
|
6752b46f67 | ||
|
|
b9429ffcaa | ||
|
|
10827c7e21 | ||
|
|
23cb4a34a4 | ||
|
|
adfd09565f | ||
|
|
78b9ff49d4 | ||
|
|
fca425d25e | ||
|
|
1a4238156c | ||
|
|
f6210cc014 | ||
|
|
6c8b466e59 | ||
|
|
2aebe97f9f | ||
|
|
157724bff8 | ||
|
|
6cbb1a0bee | ||
|
|
960a13dd38 | ||
|
|
9213f80435 | ||
|
|
bccae94c7a | ||
|
|
d71f77ce06 | ||
|
|
2787f3dc71 | ||
|
|
fb00baab5b | ||
|
|
9e22155d31 | ||
|
|
963373435d | ||
|
|
377987fa92 | ||
|
|
0b6980639e | ||
|
|
4f1cc9f94f | ||
|
|
6e5434a0ec | ||
|
|
3244f7c38f | ||
|
|
f27b676e89 | ||
|
|
4f2a80a4a3 | ||
|
|
a51bc27829 | ||
|
|
4fd321c93d | ||
|
|
bbd62bdef5 | ||
|
|
4bb1d78939 | ||
|
|
53c37ef413 | ||
|
|
a6511b6c5a | ||
|
|
829e96a2d0 | ||
|
|
2e25f37433 | ||
|
|
fbf6c73226 | ||
|
|
aab54e3f23 | ||
|
|
a6eff7fbfb | ||
|
|
d5667d2473 | ||
|
|
a42305dd59 | ||
|
|
c22a718d14 | ||
|
|
d0b23c79e2 | ||
|
|
76da04afd8 | ||
|
|
768f60709f | ||
|
|
8cd3acd318 | ||
|
|
ce7cfc0648 | ||
|
|
8b6cf55473 | ||
|
|
75d95d844c | ||
|
|
f54bfd3fb5 | ||
|
|
f8a8ee9b9d | ||
|
|
16faaae199 | ||
|
|
375417527b | ||
|
|
10b01ca6b3 | ||
|
|
f9006af04c | ||
|
|
57f947576e | ||
|
|
5a8f8c345b | ||
|
|
e96067e3c8 | ||
|
|
2242174515 | ||
|
|
73179c118b | ||
|
|
5b575134fc | ||
|
|
c11c06b896 | ||
|
|
ba8d2a251f | ||
|
|
c9589b73f4 | ||
|
|
32f4425100 | ||
|
|
05e81484f1 | ||
|
|
10cc79f737 | ||
|
|
957d9b180d | ||
|
|
883204fc43 | ||
|
|
6d3904fbbd | ||
|
|
56a51f18e7 | ||
|
|
307508b7a0 | ||
|
|
66fce79427 | ||
|
|
5e5708d8d4 | ||
|
|
944d92bbb4 | ||
|
|
0155d5aa39 | ||
|
|
fe4f45c2b0 | ||
|
|
1b63a58e63 | ||
|
|
46a141db9c | ||
|
|
66de06956c | ||
|
|
713dd62bd1 | ||
|
|
5ac65aafad | ||
|
|
4aef92e6fe | ||
|
|
a1b0e771e5 | ||
|
|
5baeac4706 | ||
|
|
87aa9bd673 | ||
|
|
2ec5ca56d5 | ||
|
|
b5c7f83ec8 | ||
|
|
eb3ebdbf52 | ||
|
|
c60698ff7e | ||
|
|
f8c2cda257 | ||
|
|
48f6c195e0 | ||
|
|
804fa60317 | ||
|
|
d77b99d3d4 | ||
|
|
006f1271d6 | ||
|
|
0d14514712 | ||
|
|
f25e5f9488 | ||
|
|
9e8bbfd175 | ||
|
|
3d11c96e23 | ||
|
|
a670737ca5 | ||
|
|
9fdd982e73 | ||
|
|
8024918e08 | ||
|
|
cda7607e1c | ||
|
|
816c83c80a | ||
|
|
0c3c490d58 | ||
|
|
99da6fb66f | ||
|
|
fa2601386c | ||
|
|
122833b0e3 | ||
|
|
8618dbf0d5 | ||
|
|
a0baf33459 | ||
|
|
0ee66ea928 | ||
|
|
f68d4aaf5b | ||
|
|
888abc9e2a | ||
|
|
c8115b1c10 | ||
|
|
137d4f591f | ||
|
|
337d49770d | ||
|
|
614c5578b0 | ||
|
|
d456e60800 | ||
|
|
064206fb9a | ||
|
|
a68fe3b39d | ||
|
|
1904c82941 | ||
|
|
6fc9dd4b30 | ||
|
|
158a31e924 | ||
|
|
b70b924db4 | ||
|
|
9018d72e97 | ||
|
|
5c395138ed | ||
|
|
af157adc1b | ||
|
|
1db4fca9db | ||
|
|
013f475859 | ||
|
|
b5bc38fa52 | ||
|
|
bd0e0cdfa4 | ||
|
|
dade4c4cc4 | ||
|
|
acc5a89dff | ||
|
|
d34881aa25 | ||
|
|
b8ad48997b | ||
|
|
77754b7246 | ||
|
|
6d5bf49a51 | ||
|
|
b7af635a9a | ||
|
|
f53b4e959c | ||
|
|
6716d156bb | ||
|
|
dc33e28452 | ||
|
|
9da4781759 | ||
|
|
0c6959cb4a | ||
|
|
ff3136df70 | ||
|
|
326c627221 | ||
|
|
075f382a8f | ||
|
|
dabb8fe49e | ||
|
|
90489e4392 | ||
|
|
ad45a74f87 | ||
|
|
60c045a713 | ||
|
|
f6c75e1f99 | ||
|
|
dd9245f6f2 | ||
|
|
7310a70743 | ||
|
|
82276837ac | ||
|
|
bd68252b44 | ||
|
|
6b1258e33a | ||
|
|
0400032923 | ||
|
|
d9563322f1 | ||
|
|
ab965969dd | ||
|
|
2f32e034d8 | ||
|
|
d7bb171c1e | ||
|
|
1cf7931f43 | ||
|
|
edc697dd73 | ||
|
|
54a059ff68 | ||
|
|
289282e235 | ||
|
|
825c36c15e | ||
|
|
1234c2e99e | ||
|
|
b8fae1308d | ||
|
|
dddcf624e6 | ||
|
|
b33fe9f7fe | ||
|
|
5a799400a9 | ||
|
|
76919a819f | ||
|
|
9c30f846b2 | ||
|
|
9230f51988 | ||
|
|
712035dc9a | ||
|
|
32e5c5b4ad | ||
|
|
134a6fab7e | ||
|
|
82e27cb962 | ||
|
|
8bcad7b3f9 | ||
|
|
d767575f76 | ||
|
|
7214c7d393 | ||
|
|
205e5ab03d | ||
|
|
6576beae76 | ||
|
|
001f8f1f50 | ||
|
|
3b694a87ef | ||
|
|
84dd06d769 | ||
|
|
c5cb16abfb | ||
|
|
79fb34324d | ||
|
|
303aef3548 | ||
|
|
fd278f8c32 | ||
|
|
53041a4117 | ||
|
|
9b3fe3b13a | ||
|
|
5c6c5081e9 | ||
|
|
e087ea0ac7 | ||
|
|
71c44dc805 | ||
|
|
c5c8596664 | ||
|
|
92a86f4d1c | ||
|
|
8098b68b8e | ||
|
|
7356001723 | ||
|
|
aba60587ab | ||
|
|
936a6a04fb | ||
|
|
9675126298 | ||
|
|
6a5886ea1c | ||
|
|
648c6a5a8f | ||
|
|
14b7bf4753 | ||
|
|
0e310fa96a | ||
|
|
0f6c282c80 | ||
|
|
4c3a0d329b | ||
|
|
7c495c2177 | ||
|
|
2f86a9e534 | ||
|
|
12c3a0b1fa | ||
|
|
58c069ec5c | ||
|
|
7916600a7b | ||
|
|
3e4f160c5d | ||
|
|
ad683fee89 | ||
|
|
3bf8baee85 | ||
|
|
8792213594 | ||
|
|
9ab8bdfe4a | ||
|
|
90301ae9ee |
8
.github/PULL_REQUEST_TEMPLATE.md
vendored
8
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -7,4 +7,10 @@ We love getting PRs, but we hate asking people for the same basic changes every
|
|||||||
- [ ] Push your changes to a branch other than `master`. Create your PR from that branch.
|
- [ ] Push your changes to a branch other than `master`. Create your PR from that branch.
|
||||||
- [ ] Add JavaDocs and other comments
|
- [ ] Add JavaDocs and other comments
|
||||||
- [ ] Write tests that run and pass in CI. See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to capture snapshot data.
|
- [ ] Write tests that run and pass in CI. See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to capture snapshot data.
|
||||||
- [ ] Run `mvn -P ci install site` locally. This may reformat your code, commit those changes. If this command doesn't succeed, your change will not pass CI.
|
- [ ] Run `mvn clean compile` locally. This may reformat your code, commit those changes.
|
||||||
|
- [ ] Run `mvn -D enable-ci clean install site` locally. If this command doesn't succeed, your change will not pass CI.
|
||||||
|
|
||||||
|
# When creating a PR:
|
||||||
|
|
||||||
|
- [ ] Fill in the "Description" above.
|
||||||
|
- [ ] Enable "Allow edits from maintainers".
|
||||||
|
|||||||
12
.github/dependabot.yml
vendored
Normal file
12
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "maven"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "monthly"
|
||||||
|
time: "02:00"
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "monthly"
|
||||||
|
time: "02:00"
|
||||||
5
.github/release-drafter.yml
vendored
5
.github/release-drafter.yml
vendored
@@ -1,5 +1,6 @@
|
|||||||
name-template: 'v$NEXT_PATCH_VERSION 🌈'
|
name-template: 'v$NEXT_MINOR_VERSION 🌈'
|
||||||
tag-template: 'v$NEXT_PATCH_VERSION'
|
tag-template: 'github-api-$NEXT_MINOR_VERSION'
|
||||||
|
version-template: '$MAJOR.$MINOR'
|
||||||
categories:
|
categories:
|
||||||
- title: '🚀 Features'
|
- title: '🚀 Features'
|
||||||
labels:
|
labels:
|
||||||
|
|||||||
64
.github/workflows/maven-build.yml
vendored
64
.github/workflows/maven-build.yml
vendored
@@ -1,22 +1,72 @@
|
|||||||
name: Java CI Build and Test
|
name: CI
|
||||||
|
|
||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
name: build-only (Java ${{ matrix.java }})
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
java: [ '1.8.0', '11.0.x', '13.0.x' ]
|
java: [ 13 ]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Set up JDK
|
||||||
|
uses: actions/setup-java@v1
|
||||||
|
with:
|
||||||
|
java-version: ${{ matrix.java }}
|
||||||
|
- name: Cached .m2
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: ~/.m2/repository
|
||||||
|
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-maven-
|
||||||
|
- name: Maven Install (skipTests)
|
||||||
|
run: mvn -B install -DskipTests -D enable-ci --file pom.xml
|
||||||
|
site:
|
||||||
|
name: site (Java ${{ matrix.java }})
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
java: [ 8, 11 ]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Set up JDK
|
||||||
|
uses: actions/setup-java@v1
|
||||||
|
with:
|
||||||
|
java-version: ${{ matrix.java }}
|
||||||
|
- uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: ~/.m2/repository
|
||||||
|
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-maven-
|
||||||
|
- name: Maven Site
|
||||||
|
run: mvn -B site -D enable-ci --file pom.xml
|
||||||
|
test:
|
||||||
|
name: test (${{ matrix.os }}, Java ${{ matrix.java }})
|
||||||
|
runs-on: ${{ matrix.os }}-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ ubuntu, windows ]
|
||||||
|
java: [ 8, 11, 13, 15-ea ]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Set up JDK
|
- name: Set up JDK
|
||||||
uses: actions/setup-java@v1
|
uses: actions/setup-java@v1
|
||||||
with:
|
with:
|
||||||
java-version: ${{ matrix.java }}
|
java-version: ${{ matrix.java }}
|
||||||
- name: Maven Download all dependencies
|
- uses: actions/cache@v2
|
||||||
run: mvn -B org.apache.maven.plugins:maven-dependency-plugin:3.1.1:go-offline -P ci
|
with:
|
||||||
- name: Maven Build
|
path: ~/.m2/repository
|
||||||
run: mvn -B install site -P ci --file pom.xml
|
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-maven-
|
||||||
|
- name: Maven Install without Code Coverage
|
||||||
|
if: matrix.os == 'windows'
|
||||||
|
run: mvn -B install --file pom.xml
|
||||||
|
- name: Maven Install with Code Coverage
|
||||||
|
if: matrix.os != 'windows'
|
||||||
|
run: mvn -B install -D enable-ci --file pom.xml
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -9,3 +9,5 @@ target
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
dependency-reduced-pom.xml
|
dependency-reduced-pom.xml
|
||||||
|
.factorypath
|
||||||
|
.vscode/settings.json
|
||||||
|
|||||||
1326
CHANGELOG.md
1326
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,9 @@
|
|||||||
# Java API for GitHub
|
# Java API for GitHub
|
||||||
|
|
||||||
[](https://mvnrepository.com/artifact/org.kohsuke/github-api)
|
[](https://mvnrepository.com/artifact/org.kohsuke/github-api)
|
||||||
[](https://gitter.im/github-api/github-api?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
[](https://gitter.im/hub4j/github-api?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
See https://github-api.kohsuke.org/ for more details
|
See https://github-api.kohsuke.org/ for more details
|
||||||
|
|||||||
200
pom.xml
200
pom.xml
@@ -2,16 +2,16 @@
|
|||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>org.kohsuke</groupId>
|
<groupId>org.kohsuke</groupId>
|
||||||
<artifactId>github-api</artifactId>
|
<artifactId>github-api</artifactId>
|
||||||
<version>1.106</version>
|
<version>1.117</version>
|
||||||
<name>GitHub API for Java</name>
|
<name>GitHub API for Java</name>
|
||||||
<url>https://github-api.kohsuke.org/</url>
|
<url>https://github-api.kohsuke.org/</url>
|
||||||
<description>GitHub API for Java</description>
|
<description>GitHub API for Java</description>
|
||||||
|
|
||||||
<scm>
|
<scm>
|
||||||
<connection>scm:git:git@github.com/github-api/${project.artifactId}.git</connection>
|
<connection>scm:git:git@github.com/hub4j/${project.artifactId}.git</connection>
|
||||||
<developerConnection>scm:git:ssh://git@github.com/github-api/${project.artifactId}.git</developerConnection>
|
<developerConnection>scm:git:ssh://git@github.com/hub4j/${project.artifactId}.git</developerConnection>
|
||||||
<url>https://${project.artifactId}.kohsuke.org/</url>
|
<url>https://github.com/hub4j/github-api/</url>
|
||||||
<tag>github-api-1.106</tag>
|
<tag>github-api-1.117</tag>
|
||||||
</scm>
|
</scm>
|
||||||
|
|
||||||
<distributionManagement>
|
<distributionManagement>
|
||||||
@@ -27,22 +27,22 @@
|
|||||||
</repository>
|
</repository>
|
||||||
<site>
|
<site>
|
||||||
<id>github-pages</id>
|
<id>github-pages</id>
|
||||||
<url>gitsite:git@github.com/github-api/${project.artifactId}.git</url>
|
<url>gitsite:git@github.com/hub4j/${project.artifactId}.git</url>
|
||||||
</site>
|
</site>
|
||||||
</distributionManagement>
|
</distributionManagement>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<spotbugs-maven-plugin.version>3.1.12.2</spotbugs-maven-plugin.version>
|
<spotbugs-maven-plugin.version>4.0.4</spotbugs-maven-plugin.version>
|
||||||
<spotbugs.version>3.1.12</spotbugs.version>
|
<spotbugs.version>4.1.2</spotbugs.version>
|
||||||
<spotbugs-maven-plugin.failOnError>true</spotbugs-maven-plugin.failOnError>
|
<spotbugs-maven-plugin.failOnError>true</spotbugs-maven-plugin.failOnError>
|
||||||
<hamcrest.version>2.2</hamcrest.version>
|
<hamcrest.version>2.2</hamcrest.version>
|
||||||
<okhttp3.version>4.3.1</okhttp3.version>
|
<okhttp3.version>4.4.1</okhttp3.version>
|
||||||
<okio.version>2.4.3</okio.version>
|
<okio.version>2.5.0</okio.version>
|
||||||
<formatter-maven-plugin.goal>format</formatter-maven-plugin.goal>
|
<formatter-maven-plugin.goal>format</formatter-maven-plugin.goal>
|
||||||
<impsort-maven-plugin.goal>sort</impsort-maven-plugin.goal>
|
<impsort-maven-plugin.goal>sort</impsort-maven-plugin.goal>
|
||||||
<!-- Using this as the minimum bar for code coverage. Adding methods without covering them will fail this. -->
|
<!-- Using this as the minimum bar for code coverage. Adding methods without covering them will fail this. -->
|
||||||
<jacoco.coverage.target.bundle.method>0.556</jacoco.coverage.target.bundle.method>
|
<jacoco.coverage.target.bundle.method>0.60</jacoco.coverage.target.bundle.method>
|
||||||
<jacoco.coverage.target.class.method>0.25</jacoco.coverage.target.class.method>
|
<jacoco.coverage.target.class.method>0.25</jacoco.coverage.target.class.method>
|
||||||
<!-- For non-ci builds we'd like the build to still complete if jacoco metrics aren't met. -->
|
<!-- For non-ci builds we'd like the build to still complete if jacoco metrics aren't met. -->
|
||||||
<jacoco.haltOnFailure>false</jacoco.haltOnFailure>
|
<jacoco.haltOnFailure>false</jacoco.haltOnFailure>
|
||||||
@@ -80,52 +80,12 @@
|
|||||||
<pluginManagement>
|
<pluginManagement>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
<artifactId>maven-shade-plugin</artifactId>
|
<version>2.22.2</version>
|
||||||
<version>3.2.1</version>
|
<configuration>
|
||||||
<executions>
|
<!-- SUREFIRE-1226 workaround -->
|
||||||
<execution>
|
<trimStackTrace>false</trimStackTrace>
|
||||||
<id>shaded-jar</id>
|
</configuration>
|
||||||
<phase>package</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>shade</goal>
|
|
||||||
</goals>
|
|
||||||
<configuration>
|
|
||||||
<relocations>
|
|
||||||
<relocation>
|
|
||||||
<pattern>com.fasterxml.jackson</pattern>
|
|
||||||
<shadedPattern>hidden.com.fasterxml.jackson</shadedPattern>
|
|
||||||
</relocation>
|
|
||||||
<relocation>
|
|
||||||
<pattern>org.apache</pattern>
|
|
||||||
<shadedPattern>hidden.org.apache</shadedPattern>
|
|
||||||
</relocation>
|
|
||||||
</relocations>
|
|
||||||
<artifactSet>
|
|
||||||
<excludes>
|
|
||||||
<exclude>com.infradna.tool:bridge-method-annotation</exclude>
|
|
||||||
<exclude>org.jenkins-ci:annotation-indexer</exclude>
|
|
||||||
<exclude>com.squareup.*:*</exclude>
|
|
||||||
<exclude>org.jetbrains*:*</exclude>
|
|
||||||
<exclude>com.github.spotbugs:*</exclude>
|
|
||||||
<exclude>com.google.code.findbugs:*</exclude>
|
|
||||||
</excludes>
|
|
||||||
</artifactSet>
|
|
||||||
<shadedArtifactAttached>true</shadedArtifactAttached>
|
|
||||||
<filters>
|
|
||||||
<filter>
|
|
||||||
<artifact>*:*</artifact>
|
|
||||||
<excludes>
|
|
||||||
<exclude>module-info.class</exclude>
|
|
||||||
<exclude>META-INF/*.SF</exclude>
|
|
||||||
<exclude>META-INF/*.DSA</exclude>
|
|
||||||
<exclude>META-INF/*.RSA</exclude>
|
|
||||||
</excludes>
|
|
||||||
</filter>
|
|
||||||
</filters>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
@@ -140,7 +100,7 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.jacoco</groupId>
|
<groupId>org.jacoco</groupId>
|
||||||
<artifactId>jacoco-maven-plugin</artifactId>
|
<artifactId>jacoco-maven-plugin</artifactId>
|
||||||
<version>0.8.5</version>
|
<version>0.8.6</version>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<goals>
|
<goals>
|
||||||
@@ -194,25 +154,24 @@
|
|||||||
<exclude>org.kohsuke.github.example.*</exclude>
|
<exclude>org.kohsuke.github.example.*</exclude>
|
||||||
|
|
||||||
<!-- No methods -->
|
<!-- No methods -->
|
||||||
<exclude>org.kohsuke.github.DeleteToken</exclude>
|
|
||||||
<exclude>org.kohsuke.github.Previews</exclude>
|
<exclude>org.kohsuke.github.Previews</exclude>
|
||||||
|
|
||||||
<!-- Deprecated -->
|
<!-- Deprecated -->
|
||||||
<exclude>org.kohsuke.github.extras.OkHttp3Connector</exclude>
|
<exclude>org.kohsuke.github.extras.OkHttp3Connector</exclude>
|
||||||
<exclude>org.kohsuke.github.EnforcementLevel</exclude>
|
<exclude>org.kohsuke.github.EnforcementLevel</exclude>
|
||||||
<exclude>org.kohsuke.github.GHPerson.1</exclude>
|
<exclude>org.kohsuke.github.GHPerson.1</exclude>
|
||||||
<exclude>org.kohsuke.github.GHPerson.1.1</exclude>
|
|
||||||
|
<!-- These fail coverage on windows because tests are disabled -->
|
||||||
|
<exclude>org.kohsuke.github.GHAsset</exclude>
|
||||||
|
<exclude>org.kohsuke.github.GHReleaseBuilder</exclude>
|
||||||
|
<exclude>org.kohsuke.github.GHRelease</exclude>
|
||||||
|
|
||||||
<!-- TODO: These still need test coverage -->
|
<!-- TODO: These still need test coverage -->
|
||||||
<exclude>org.kohsuke.github.GitHub.GHApiInfo</exclude>
|
|
||||||
<exclude>org.kohsuke.github.GHBranchProtection.RequiredSignatures</exclude>
|
<exclude>org.kohsuke.github.GHBranchProtection.RequiredSignatures</exclude>
|
||||||
<exclude>org.kohsuke.github.GHBranchProtectionBuilder.Restrictions</exclude>
|
<exclude>org.kohsuke.github.GHBranchProtectionBuilder.Restrictions</exclude>
|
||||||
<exclude>org.kohsuke.github.GHBranchProtection.Restrictions</exclude>
|
<exclude>org.kohsuke.github.GHBranchProtection.Restrictions</exclude>
|
||||||
<exclude>org.kohsuke.github.GHCommentAuthorAssociation</exclude>
|
<exclude>org.kohsuke.github.GHCommentAuthorAssociation</exclude>
|
||||||
<exclude>org.kohsuke.github.GHCommitBuilder.UserInfo</exclude>
|
<exclude>org.kohsuke.github.GHCommitBuilder.UserInfo</exclude>
|
||||||
<exclude>org.kohsuke.github.GHCommitSearchBuilder.CommitSearchResult</exclude>
|
|
||||||
<exclude>org.kohsuke.github.GHCommitSearchBuilder.Sort</exclude>
|
|
||||||
<exclude>org.kohsuke.github.GHCommitSearchBuilder</exclude>
|
|
||||||
<exclude>org.kohsuke.github.GHCommitState</exclude>
|
<exclude>org.kohsuke.github.GHCommitState</exclude>
|
||||||
<exclude>org.kohsuke.github.GHCompare.Commit</exclude>
|
<exclude>org.kohsuke.github.GHCompare.Commit</exclude>
|
||||||
<exclude>org.kohsuke.github.GHCompare.InnerCommit</exclude>
|
<exclude>org.kohsuke.github.GHCompare.InnerCommit</exclude>
|
||||||
@@ -230,9 +189,6 @@
|
|||||||
<exclude>org.kohsuke.github.GHHook</exclude>
|
<exclude>org.kohsuke.github.GHHook</exclude>
|
||||||
<exclude>org.kohsuke.github.GHHooks.OrgContext</exclude>
|
<exclude>org.kohsuke.github.GHHooks.OrgContext</exclude>
|
||||||
<exclude>org.kohsuke.github.GHInvitation</exclude>
|
<exclude>org.kohsuke.github.GHInvitation</exclude>
|
||||||
<exclude>org.kohsuke.github.GHIssueSearchBuilder.IssueSearchResult</exclude>
|
|
||||||
<exclude>org.kohsuke.github.GHIssueSearchBuilder.Sort</exclude>
|
|
||||||
<exclude>org.kohsuke.github.GHIssueSearchBuilder</exclude>
|
|
||||||
<exclude>org.kohsuke.github.GHMilestoneState</exclude>
|
<exclude>org.kohsuke.github.GHMilestoneState</exclude>
|
||||||
<exclude>org.kohsuke.github.GHOrgHook</exclude>
|
<exclude>org.kohsuke.github.GHOrgHook</exclude>
|
||||||
<exclude>org.kohsuke.github.GHProject.ProjectStateFilter</exclude>
|
<exclude>org.kohsuke.github.GHProject.ProjectStateFilter</exclude>
|
||||||
@@ -251,7 +207,6 @@
|
|||||||
<exclude>org.kohsuke.github.GHTeam.Role</exclude>
|
<exclude>org.kohsuke.github.GHTeam.Role</exclude>
|
||||||
<exclude>org.kohsuke.github.GHUserSearchBuilder.Sort</exclude>
|
<exclude>org.kohsuke.github.GHUserSearchBuilder.Sort</exclude>
|
||||||
<exclude>org.kohsuke.github.GHVerifiedKey</exclude>
|
<exclude>org.kohsuke.github.GHVerifiedKey</exclude>
|
||||||
<exclude>org.kohsuke.github.GitHubBuilder.1</exclude>
|
|
||||||
</excludes>
|
</excludes>
|
||||||
</rule>
|
</rule>
|
||||||
</rules>
|
</rules>
|
||||||
@@ -262,7 +217,7 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-javadoc-plugin</artifactId>
|
<artifactId>maven-javadoc-plugin</artifactId>
|
||||||
<version>3.1.1</version>
|
<version>3.2.0</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<source>8</source>
|
<source>8</source>
|
||||||
<failOnWarnings>true</failOnWarnings>
|
<failOnWarnings>true</failOnWarnings>
|
||||||
@@ -286,7 +241,7 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-site-plugin</artifactId>
|
<artifactId>maven-site-plugin</artifactId>
|
||||||
<version>3.8.2</version>
|
<version>3.9.1</version>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
@@ -306,12 +261,12 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-project-info-reports-plugin</artifactId>
|
<artifactId>maven-project-info-reports-plugin</artifactId>
|
||||||
<version>3.0.0</version>
|
<version>3.1.0</version>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.bcel</groupId>
|
<groupId>org.apache.bcel</groupId>
|
||||||
<artifactId>bcel</artifactId>
|
<artifactId>bcel</artifactId>
|
||||||
<version>6.4.1</version>
|
<version>6.5.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</plugin>
|
</plugin>
|
||||||
@@ -333,12 +288,31 @@
|
|||||||
|
|
||||||
<plugin>
|
<plugin>
|
||||||
<artifactId>maven-surefire-plugin</artifactId>
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
<version>2.22.2</version>
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>default-test</id>
|
||||||
|
<configuration>
|
||||||
|
<excludesFile>src/test/resources/slow-or-flaky-tests.txt</excludesFile>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
<execution>
|
||||||
|
<id>slow-or-flaky-test</id>
|
||||||
|
<phase>test</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>test</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<rerunFailingTestsCount>2</rerunFailingTestsCount>
|
||||||
|
<!-- There are some tests that take longer or are a little flaky. Run them here. -->
|
||||||
|
<includesFile>src/test/resources/slow-or-flaky-tests.txt</includesFile>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.codehaus.mojo</groupId>
|
<groupId>org.codehaus.mojo</groupId>
|
||||||
<artifactId>animal-sniffer-maven-plugin</artifactId>
|
<artifactId>animal-sniffer-maven-plugin</artifactId>
|
||||||
<version>1.18</version>
|
<version>1.19</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<signature>
|
<signature>
|
||||||
<groupId>org.codehaus.mojo.signature</groupId>
|
<groupId>org.codehaus.mojo.signature</groupId>
|
||||||
@@ -371,7 +345,7 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>net.revelc.code.formatter</groupId>
|
<groupId>net.revelc.code.formatter</groupId>
|
||||||
<artifactId>formatter-maven-plugin</artifactId>
|
<artifactId>formatter-maven-plugin</artifactId>
|
||||||
<version>2.11.0</version>
|
<version>2.12.1</version>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<goals>
|
<goals>
|
||||||
@@ -379,6 +353,7 @@
|
|||||||
</goals>
|
</goals>
|
||||||
<configuration>
|
<configuration>
|
||||||
<configFile>src/main/resources/eclipse/formatter.xml</configFile>
|
<configFile>src/main/resources/eclipse/formatter.xml</configFile>
|
||||||
|
<cachedir>${project.build.directory}/.cache</cachedir>
|
||||||
</configuration>
|
</configuration>
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
@@ -386,7 +361,7 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>net.revelc.code</groupId>
|
<groupId>net.revelc.code</groupId>
|
||||||
<artifactId>impsort-maven-plugin</artifactId>
|
<artifactId>impsort-maven-plugin</artifactId>
|
||||||
<version>1.3.2</version>
|
<version>1.4.1</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<groups>*,java.,javax.</groups>
|
<groups>*,java.,javax.</groups>
|
||||||
<removeUnused>true</removeUnused>
|
<removeUnused>true</removeUnused>
|
||||||
@@ -457,7 +432,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>junit</groupId>
|
<groupId>junit</groupId>
|
||||||
<artifactId>junit</artifactId>
|
<artifactId>junit</artifactId>
|
||||||
<version>4.13</version>
|
<version>4.13.1</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -494,7 +469,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.kohsuke.stapler</groupId>
|
<groupId>org.kohsuke.stapler</groupId>
|
||||||
<artifactId>stapler</artifactId>
|
<artifactId>stapler</artifactId>
|
||||||
<version>1.258</version>
|
<version>1.260</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -506,7 +481,25 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.eclipse.jgit</groupId>
|
<groupId>org.eclipse.jgit</groupId>
|
||||||
<artifactId>org.eclipse.jgit</artifactId>
|
<artifactId>org.eclipse.jgit</artifactId>
|
||||||
<version>5.6.0.201912101111-r</version>
|
<version>5.9.0.202009080501-r</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.jsonwebtoken</groupId>
|
||||||
|
<artifactId>jjwt-api</artifactId>
|
||||||
|
<version>0.11.2</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.jsonwebtoken</groupId>
|
||||||
|
<artifactId>jjwt-impl</artifactId>
|
||||||
|
<version>0.11.2</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.jsonwebtoken</groupId>
|
||||||
|
<artifactId>jjwt-jackson</artifactId>
|
||||||
|
<version>0.11.2</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -544,7 +537,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.mockito</groupId>
|
<groupId>org.mockito</groupId>
|
||||||
<artifactId>mockito-core</artifactId>
|
<artifactId>mockito-core</artifactId>
|
||||||
<version>3.2.4</version>
|
<version>3.6.0</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -556,7 +549,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.tomakehurst</groupId>
|
<groupId>com.github.tomakehurst</groupId>
|
||||||
<artifactId>wiremock-jre8-standalone</artifactId>
|
<artifactId>wiremock-jre8-standalone</artifactId>
|
||||||
<version>2.25.1</version>
|
<version>2.27.2</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -565,6 +558,12 @@
|
|||||||
<version>2.8.6</version>
|
<version>2.8.6</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-simple</artifactId>
|
||||||
|
<version>1.7.2</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<repositories>
|
<repositories>
|
||||||
<repository>
|
<repository>
|
||||||
@@ -580,23 +579,32 @@
|
|||||||
</pluginRepositories>
|
</pluginRepositories>
|
||||||
<profiles>
|
<profiles>
|
||||||
<profile>
|
<profile>
|
||||||
<id>ci</id>
|
<id>ci-non-windows</id>
|
||||||
|
<activation>
|
||||||
|
<property>
|
||||||
|
<name>enable-ci</name>
|
||||||
|
</property>
|
||||||
|
<os>
|
||||||
|
<family>!windows</family>
|
||||||
|
</os>
|
||||||
|
</activation>
|
||||||
|
<properties>
|
||||||
|
<formatter-maven-plugin.goal>validate</formatter-maven-plugin.goal>
|
||||||
|
<impsort-maven-plugin.goal>check</impsort-maven-plugin.goal>
|
||||||
|
</properties>
|
||||||
|
</profile>
|
||||||
|
<profile>
|
||||||
|
<id>ci-all</id>
|
||||||
<activation>
|
<activation>
|
||||||
<property>
|
<property>
|
||||||
<name>enable-ci</name>
|
<name>enable-ci</name>
|
||||||
</property>
|
</property>
|
||||||
</activation>
|
</activation>
|
||||||
<properties>
|
<properties>
|
||||||
<formatter-maven-plugin.goal>validate</formatter-maven-plugin.goal>
|
|
||||||
<impsort-maven-plugin.goal>check</impsort-maven-plugin.goal>
|
|
||||||
<jacoco.haltOnFailure>true</jacoco.haltOnFailure>
|
<jacoco.haltOnFailure>true</jacoco.haltOnFailure>
|
||||||
</properties>
|
</properties>
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-shade-plugin</artifactId>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.jacoco</groupId>
|
<groupId>org.jacoco</groupId>
|
||||||
<artifactId>jacoco-maven-plugin</artifactId>
|
<artifactId>jacoco-maven-plugin</artifactId>
|
||||||
@@ -613,8 +621,8 @@
|
|||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.jacoco</groupId>
|
||||||
<artifactId>maven-shade-plugin</artifactId>
|
<artifactId>jacoco-maven-plugin</artifactId>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
@@ -665,6 +673,18 @@
|
|||||||
</profiles>
|
</profiles>
|
||||||
<reporting>
|
<reporting>
|
||||||
<plugins>
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.jacoco</groupId>
|
||||||
|
<artifactId>jacoco-maven-plugin</artifactId>
|
||||||
|
<reportSets>
|
||||||
|
<reportSet>
|
||||||
|
<reports>
|
||||||
|
<!-- select non-aggregate reports -->
|
||||||
|
<report>report</report>
|
||||||
|
</reports>
|
||||||
|
</reportSet>
|
||||||
|
</reportSets>
|
||||||
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-javadoc-plugin</artifactId>
|
<artifactId>maven-javadoc-plugin</artifactId>
|
||||||
|
|||||||
165
src/main/java/org/kohsuke/github/AbstractBuilder.java
Normal file
165
src/main/java/org/kohsuke/github/AbstractBuilder.java
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.annotation.CheckForNull;
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An abstract data object builder/updater.
|
||||||
|
*
|
||||||
|
* This class can be use to make a Builder that supports both batch and single property changes.
|
||||||
|
* <p>
|
||||||
|
* Batching looks like this:
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* update().someName(value).otherName(value).done()
|
||||||
|
* </pre>
|
||||||
|
* <p>
|
||||||
|
* Single changes look like this:
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* set().someName(value);
|
||||||
|
* set().otherName(value);
|
||||||
|
* </pre>
|
||||||
|
* <p>
|
||||||
|
* If {@link S} is the same as {@link R}, {@link #with(String, Object)} will commit changes after the first value change
|
||||||
|
* and return a {@link R} from {@link #done()}.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* If {@link S} is not the same as {@link R}, {@link #with(String, Object)} will batch together multiple changes and let
|
||||||
|
* the user call {@link #done()} when they are ready.
|
||||||
|
*
|
||||||
|
* @param <R>
|
||||||
|
* Final return type built by this builder returned when {@link #done()}} is called.
|
||||||
|
* @param <S>
|
||||||
|
* Intermediate return type for this builder returned by calls to {@link #with(String, Object)}. If {@link S}
|
||||||
|
* the same as {@link R}, this builder will commit changes after each call to {@link #with(String, Object)}.
|
||||||
|
*/
|
||||||
|
abstract class AbstractBuilder<R, S> {
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
private final Class<R> returnType;
|
||||||
|
|
||||||
|
private final boolean commitChangesImmediately;
|
||||||
|
|
||||||
|
@CheckForNull
|
||||||
|
private final R baseInstance;
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
protected final Requester requester;
|
||||||
|
|
||||||
|
// TODO: Not sure how update-in-place behavior should be controlled
|
||||||
|
// However, it certainly can be controlled dynamically down to the instance level or inherited for all children of
|
||||||
|
// some
|
||||||
|
// connection.
|
||||||
|
protected boolean updateInPlace;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a builder.
|
||||||
|
*
|
||||||
|
* @param root
|
||||||
|
* the GitHub instance to connect to.
|
||||||
|
* @param intermediateReturnType
|
||||||
|
* the intermediate return type of type {@link S} returned by calls to {@link #with(String, Object)}.
|
||||||
|
* Must either be equal to {@code builtReturnType} or this instance must be castable to this class. If
|
||||||
|
* not, the constructor will throw {@link IllegalArgumentException}.
|
||||||
|
* @param finalReturnType
|
||||||
|
* the final return type for built by this builder returned when {@link #done()}} is called.
|
||||||
|
* @param baseInstance
|
||||||
|
* optional instance on which to base this builder.
|
||||||
|
*/
|
||||||
|
protected AbstractBuilder(@Nonnull Class<R> finalReturnType,
|
||||||
|
@Nonnull Class<S> intermediateReturnType,
|
||||||
|
@Nonnull GitHub root,
|
||||||
|
@CheckForNull R baseInstance) {
|
||||||
|
this.requester = root.createRequest();
|
||||||
|
this.returnType = finalReturnType;
|
||||||
|
this.commitChangesImmediately = returnType.equals(intermediateReturnType);
|
||||||
|
if (!commitChangesImmediately && !intermediateReturnType.isInstance(this)) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Argument \"intermediateReturnType\": This instance must be castable to intermediateReturnType or finalReturnType must be equal to intermediateReturnType.");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.baseInstance = baseInstance;
|
||||||
|
this.updateInPlace = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finishes an update, committing changes.
|
||||||
|
*
|
||||||
|
* This method may update-in-place or not. Either way it returns the resulting instance.
|
||||||
|
*
|
||||||
|
* @return an instance with updated current data
|
||||||
|
* @throws IOException
|
||||||
|
* if there is an I/O Exception
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
@Preview
|
||||||
|
@Deprecated
|
||||||
|
public R done() throws IOException {
|
||||||
|
R result;
|
||||||
|
if (updateInPlace && baseInstance != null) {
|
||||||
|
result = requester.fetchInto(baseInstance);
|
||||||
|
} else {
|
||||||
|
result = requester.fetch(returnType);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies a value to a name for this builder.
|
||||||
|
*
|
||||||
|
* If {@link S} is the same as {@link R}, this method will commit changes after the first value change and return a
|
||||||
|
* {@link R} from {@link #done()}.
|
||||||
|
*
|
||||||
|
* If {@link S} is not the same as {@link R}, this method will return an {@link S} and letting the caller batch
|
||||||
|
* together multiple changes and call {@link #done()} when they are ready.
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* the name of the field
|
||||||
|
* @param value
|
||||||
|
* the value of the field
|
||||||
|
* @return either a continuing builder or an updated data record
|
||||||
|
* @throws IOException
|
||||||
|
* if an I/O error occurs
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
@Preview
|
||||||
|
@Deprecated
|
||||||
|
protected S with(@Nonnull String name, Object value) throws IOException {
|
||||||
|
requester.with(name, value);
|
||||||
|
return continueOrDone();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Chooses whether to return a continuing builder or an updated data record
|
||||||
|
*
|
||||||
|
* If {@link S} is the same as {@link R}, this method will commit changes after the first value change and return a
|
||||||
|
* {@link R} from {@link #done()}.
|
||||||
|
*
|
||||||
|
* If {@link S} is not the same as {@link R}, this method will return an {@link S} and letting the caller batch
|
||||||
|
* together multiple changes and call {@link #done()} when they are ready.
|
||||||
|
*
|
||||||
|
* @return either a continuing builder or an updated data record
|
||||||
|
* @throws IOException
|
||||||
|
* if an I/O error occurs
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
@Preview
|
||||||
|
@Deprecated
|
||||||
|
protected S continueOrDone() throws IOException {
|
||||||
|
// This little bit of roughness in this base class means all inheriting builders get to create Updater and
|
||||||
|
// Setter classes from almost identical code. Creator can often be implemented with significant code reuse as
|
||||||
|
// well.
|
||||||
|
if (commitChangesImmediately) {
|
||||||
|
// These casts look strange and risky, but they they're actually guaranteed safe due to the return path
|
||||||
|
// being based on the previous comparison of class instances passed to the constructor.
|
||||||
|
return (S) done();
|
||||||
|
} else {
|
||||||
|
return (S) this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,6 +29,10 @@ public abstract class AbuseLimitHandler {
|
|||||||
* @throws IOException
|
* @throws IOException
|
||||||
* on failure
|
* on failure
|
||||||
* @see <a href="https://developer.github.com/v3/#abuse-rate-limits">API documentation from GitHub</a>
|
* @see <a href="https://developer.github.com/v3/#abuse-rate-limits">API documentation from GitHub</a>
|
||||||
|
* @see <a href=
|
||||||
|
* "https://developer.github.com/v3/guides/best-practices-for-integrators/#dealing-with-abuse-rate-limits">Dealing
|
||||||
|
* with abuse rate limits</a>
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
public abstract void onError(IOException e, HttpURLConnection uc) throws IOException;
|
public abstract void onError(IOException e, HttpURLConnection uc) throws IOException;
|
||||||
|
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
/*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2010, Kohsuke Kawaguchi
|
|
||||||
*
|
|
||||||
* 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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Kohsuke Kawaguchi
|
|
||||||
*/
|
|
||||||
@SuppressFBWarnings(value = "UUF_UNUSED_PUBLIC_OR_PROTECTED_FIELD",
|
|
||||||
justification = "Being constructed by JSON deserialization")
|
|
||||||
class DeleteToken {
|
|
||||||
public String delete_token;
|
|
||||||
}
|
|
||||||
@@ -39,7 +39,9 @@ public class GHApp extends GHObject {
|
|||||||
*
|
*
|
||||||
* @param owner
|
* @param owner
|
||||||
* the owner
|
* the owner
|
||||||
|
* @deprecated Do not use this method. It was added due to incomplete understanding of Jackson binding.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void setOwner(GHUser owner) {
|
public void setOwner(GHUser owner) {
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
}
|
}
|
||||||
@@ -58,7 +60,9 @@ public class GHApp extends GHObject {
|
|||||||
*
|
*
|
||||||
* @param name
|
* @param name
|
||||||
* the name
|
* the name
|
||||||
|
* @deprecated Do not use this method. It was added due to incomplete understanding of Jackson binding.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void setName(String name) {
|
public void setName(String name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
@@ -77,7 +81,9 @@ public class GHApp extends GHObject {
|
|||||||
*
|
*
|
||||||
* @param description
|
* @param description
|
||||||
* the description
|
* the description
|
||||||
|
* @deprecated Do not use this method. It was added due to incomplete understanding of Jackson binding.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void setDescription(String description) {
|
public void setDescription(String description) {
|
||||||
this.description = description;
|
this.description = description;
|
||||||
}
|
}
|
||||||
@@ -96,7 +102,9 @@ public class GHApp extends GHObject {
|
|||||||
*
|
*
|
||||||
* @param externalUrl
|
* @param externalUrl
|
||||||
* the external url
|
* the external url
|
||||||
|
* @deprecated Do not use this method. It was added due to incomplete understanding of Jackson binding.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void setExternalUrl(String externalUrl) {
|
public void setExternalUrl(String externalUrl) {
|
||||||
this.externalUrl = externalUrl;
|
this.externalUrl = externalUrl;
|
||||||
}
|
}
|
||||||
@@ -115,7 +123,9 @@ public class GHApp extends GHObject {
|
|||||||
*
|
*
|
||||||
* @param events
|
* @param events
|
||||||
* the events
|
* the events
|
||||||
|
* @deprecated Do not use this method. It was added due to incomplete understanding of Jackson binding.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void setEvents(List<GHEvent> events) {
|
public void setEvents(List<GHEvent> events) {
|
||||||
this.events = events;
|
this.events = events;
|
||||||
}
|
}
|
||||||
@@ -134,13 +144,15 @@ public class GHApp extends GHObject {
|
|||||||
*
|
*
|
||||||
* @param installationsCount
|
* @param installationsCount
|
||||||
* the installations count
|
* the installations count
|
||||||
|
* @deprecated Do not use this method. It was added due to incomplete understanding of Jackson binding.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void setInstallationsCount(long installationsCount) {
|
public void setInstallationsCount(long installationsCount) {
|
||||||
this.installationsCount = installationsCount;
|
this.installationsCount = installationsCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public URL getHtmlUrl() {
|
public URL getHtmlUrl() {
|
||||||
return GitHub.parseURL(htmlUrl);
|
return GitHubClient.parseURL(htmlUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -157,7 +169,9 @@ public class GHApp extends GHObject {
|
|||||||
*
|
*
|
||||||
* @param permissions
|
* @param permissions
|
||||||
* the permissions
|
* the permissions
|
||||||
|
* @deprecated Do not use this method. It was added due to incomplete understanding of Jackson binding.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void setPermissions(Map<String, String> permissions) {
|
public void setPermissions(Map<String, String> permissions) {
|
||||||
this.permissions = permissions;
|
this.permissions = permissions;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ public class GHAppCreateTokenBuilder {
|
|||||||
public GHAppCreateTokenBuilder permissions(Map<String, GHPermissionType> permissions) {
|
public GHAppCreateTokenBuilder permissions(Map<String, GHPermissionType> permissions) {
|
||||||
Map<String, String> retMap = new HashMap<>();
|
Map<String, String> retMap = new HashMap<>();
|
||||||
for (Map.Entry<String, GHPermissionType> entry : permissions.entrySet()) {
|
for (Map.Entry<String, GHPermissionType> entry : permissions.entrySet()) {
|
||||||
retMap.put(entry.getKey(), Requester.transformEnum(entry.getValue()));
|
retMap.put(entry.getKey(), GitHubRequest.transformEnum(entry.getValue()));
|
||||||
}
|
}
|
||||||
builder.with("permissions", retMap);
|
builder.with("permissions", retMap);
|
||||||
return this;
|
return this;
|
||||||
|
|||||||
@@ -3,11 +3,13 @@ package org.kohsuke.github;
|
|||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static org.kohsuke.github.Previews.GAMBIT;
|
import static org.kohsuke.github.Previews.GAMBIT;
|
||||||
|
import static org.kohsuke.github.Previews.MACHINE_MAN;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Github App Installation.
|
* A Github App Installation.
|
||||||
@@ -42,7 +44,7 @@ public class GHAppInstallation extends GHObject {
|
|||||||
private String htmlUrl;
|
private String htmlUrl;
|
||||||
|
|
||||||
public URL getHtmlUrl() {
|
public URL getHtmlUrl() {
|
||||||
return GitHub.parseURL(htmlUrl);
|
return GitHubClient.parseURL(htmlUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -59,7 +61,9 @@ public class GHAppInstallation extends GHObject {
|
|||||||
*
|
*
|
||||||
* @param root
|
* @param root
|
||||||
* the root
|
* the root
|
||||||
|
* @deprecated Do not use this method. It was added due to incomplete understanding of Jackson binding.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void setRoot(GitHub root) {
|
public void setRoot(GitHub root) {
|
||||||
this.root = root;
|
this.root = root;
|
||||||
}
|
}
|
||||||
@@ -78,7 +82,9 @@ public class GHAppInstallation extends GHObject {
|
|||||||
*
|
*
|
||||||
* @param account
|
* @param account
|
||||||
* the account
|
* the account
|
||||||
|
* @deprecated Do not use this method. It was added due to incomplete understanding of Jackson binding.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void setAccount(GHUser account) {
|
public void setAccount(GHUser account) {
|
||||||
this.account = account;
|
this.account = account;
|
||||||
}
|
}
|
||||||
@@ -97,7 +103,9 @@ public class GHAppInstallation extends GHObject {
|
|||||||
*
|
*
|
||||||
* @param accessTokenUrl
|
* @param accessTokenUrl
|
||||||
* the access token url
|
* the access token url
|
||||||
|
* @deprecated Do not use this method. It was added due to incomplete understanding of Jackson binding.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void setAccessTokenUrl(String accessTokenUrl) {
|
public void setAccessTokenUrl(String accessTokenUrl) {
|
||||||
this.accessTokenUrl = accessTokenUrl;
|
this.accessTokenUrl = accessTokenUrl;
|
||||||
}
|
}
|
||||||
@@ -111,12 +119,44 @@ public class GHAppInstallation extends GHObject {
|
|||||||
return repositoriesUrl;
|
return repositoriesUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List repositories that this app installation can access.
|
||||||
|
*
|
||||||
|
* @return the paged iterable
|
||||||
|
*/
|
||||||
|
@Preview
|
||||||
|
@Deprecated
|
||||||
|
public PagedSearchIterable<GHRepository> listRepositories() {
|
||||||
|
GitHubRequest request;
|
||||||
|
|
||||||
|
try {
|
||||||
|
request = root.createRequest().withPreview(MACHINE_MAN).withUrlPath("/installation/repositories").build();
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
throw new GHException("", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new PagedSearchIterable<>(root, request, GHAppInstallationRepositoryResult.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class GHAppInstallationRepositoryResult extends SearchResult<GHRepository> {
|
||||||
|
private GHRepository[] repositories;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
GHRepository[] getItems(GitHub root) {
|
||||||
|
for (GHRepository item : repositories)
|
||||||
|
item.wrap(root);
|
||||||
|
return repositories;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets repositories url.
|
* Sets repositories url.
|
||||||
*
|
*
|
||||||
* @param repositoriesUrl
|
* @param repositoriesUrl
|
||||||
* the repositories url
|
* the repositories url
|
||||||
|
* @deprecated Do not use this method. It was added due to incomplete understanding of Jackson binding.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void setRepositoriesUrl(String repositoriesUrl) {
|
public void setRepositoriesUrl(String repositoriesUrl) {
|
||||||
this.repositoriesUrl = repositoriesUrl;
|
this.repositoriesUrl = repositoriesUrl;
|
||||||
}
|
}
|
||||||
@@ -135,7 +175,9 @@ public class GHAppInstallation extends GHObject {
|
|||||||
*
|
*
|
||||||
* @param appId
|
* @param appId
|
||||||
* the app id
|
* the app id
|
||||||
|
* @deprecated Do not use this method. It was added due to incomplete understanding of Jackson binding.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void setAppId(long appId) {
|
public void setAppId(long appId) {
|
||||||
this.appId = appId;
|
this.appId = appId;
|
||||||
}
|
}
|
||||||
@@ -154,7 +196,9 @@ public class GHAppInstallation extends GHObject {
|
|||||||
*
|
*
|
||||||
* @param targetId
|
* @param targetId
|
||||||
* the target id
|
* the target id
|
||||||
|
* @deprecated Do not use this method. It was added due to incomplete understanding of Jackson binding.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void setTargetId(long targetId) {
|
public void setTargetId(long targetId) {
|
||||||
this.targetId = targetId;
|
this.targetId = targetId;
|
||||||
}
|
}
|
||||||
@@ -173,7 +217,9 @@ public class GHAppInstallation extends GHObject {
|
|||||||
*
|
*
|
||||||
* @param targetType
|
* @param targetType
|
||||||
* the target type
|
* the target type
|
||||||
|
* @deprecated Do not use this method. It was added due to incomplete understanding of Jackson binding.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void setTargetType(GHTargetType targetType) {
|
public void setTargetType(GHTargetType targetType) {
|
||||||
this.targetType = targetType;
|
this.targetType = targetType;
|
||||||
}
|
}
|
||||||
@@ -192,7 +238,9 @@ public class GHAppInstallation extends GHObject {
|
|||||||
*
|
*
|
||||||
* @param permissions
|
* @param permissions
|
||||||
* the permissions
|
* the permissions
|
||||||
|
* @deprecated Do not use this method. It was added due to incomplete understanding of Jackson binding.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void setPermissions(Map<String, GHPermissionType> permissions) {
|
public void setPermissions(Map<String, GHPermissionType> permissions) {
|
||||||
this.permissions = permissions;
|
this.permissions = permissions;
|
||||||
}
|
}
|
||||||
@@ -211,7 +259,9 @@ public class GHAppInstallation extends GHObject {
|
|||||||
*
|
*
|
||||||
* @param events
|
* @param events
|
||||||
* the events
|
* the events
|
||||||
|
* @deprecated Do not use this method. It was added due to incomplete understanding of Jackson binding.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void setEvents(List<GHEvent> events) {
|
public void setEvents(List<GHEvent> events) {
|
||||||
this.events = events;
|
this.events = events;
|
||||||
}
|
}
|
||||||
@@ -230,7 +280,9 @@ public class GHAppInstallation extends GHObject {
|
|||||||
*
|
*
|
||||||
* @param singleFileName
|
* @param singleFileName
|
||||||
* the single file name
|
* the single file name
|
||||||
|
* @deprecated Do not use this method. It was added due to incomplete understanding of Jackson binding.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void setSingleFileName(String singleFileName) {
|
public void setSingleFileName(String singleFileName) {
|
||||||
this.singleFileName = singleFileName;
|
this.singleFileName = singleFileName;
|
||||||
}
|
}
|
||||||
@@ -249,7 +301,9 @@ public class GHAppInstallation extends GHObject {
|
|||||||
*
|
*
|
||||||
* @param repositorySelection
|
* @param repositorySelection
|
||||||
* the repository selection
|
* the repository selection
|
||||||
|
* @deprecated Do not use this method. It was added due to incomplete understanding of Jackson binding.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void setRepositorySelection(GHRepositorySelection repositorySelection) {
|
public void setRepositorySelection(GHRepositorySelection repositorySelection) {
|
||||||
this.repositorySelection = repositorySelection;
|
this.repositorySelection = repositorySelection;
|
||||||
}
|
}
|
||||||
@@ -274,7 +328,7 @@ public class GHAppInstallation extends GHObject {
|
|||||||
root.createRequest()
|
root.createRequest()
|
||||||
.method("DELETE")
|
.method("DELETE")
|
||||||
.withPreview(GAMBIT)
|
.withPreview(GAMBIT)
|
||||||
.withUrlPath(String.format("/app/installations/%d", id))
|
.withUrlPath(String.format("/app/installations/%d", getId()))
|
||||||
.send();
|
.send();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,7 +347,9 @@ public class GHAppInstallation extends GHObject {
|
|||||||
@Preview
|
@Preview
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public GHAppCreateTokenBuilder createToken(Map<String, GHPermissionType> permissions) {
|
public GHAppCreateTokenBuilder createToken(Map<String, GHPermissionType> permissions) {
|
||||||
return new GHAppCreateTokenBuilder(root, String.format("/app/installations/%d/access_tokens", id), permissions);
|
return new GHAppCreateTokenBuilder(root,
|
||||||
|
String.format("/app/installations/%d/access_tokens", getId()),
|
||||||
|
permissions);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -308,6 +364,6 @@ public class GHAppInstallation extends GHObject {
|
|||||||
@Preview
|
@Preview
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public GHAppCreateTokenBuilder createToken() {
|
public GHAppCreateTokenBuilder createToken() {
|
||||||
return new GHAppCreateTokenBuilder(root, String.format("/app/installations/%d/access_tokens", id));
|
return new GHAppCreateTokenBuilder(root, String.format("/app/installations/%d/access_tokens", getId()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,9 @@ public class GHAppInstallationToken {
|
|||||||
*
|
*
|
||||||
* @param root
|
* @param root
|
||||||
* the root
|
* the root
|
||||||
|
* @deprecated Do not use this method. It was added due to incomplete understanding of Jackson binding.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void setRoot(GitHub root) {
|
public void setRoot(GitHub root) {
|
||||||
this.root = root;
|
this.root = root;
|
||||||
}
|
}
|
||||||
@@ -56,7 +58,9 @@ public class GHAppInstallationToken {
|
|||||||
*
|
*
|
||||||
* @param permissions
|
* @param permissions
|
||||||
* the permissions
|
* the permissions
|
||||||
|
* @deprecated Do not use this method. It was added due to incomplete understanding of Jackson binding.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void setPermissions(Map<String, String> permissions) {
|
public void setPermissions(Map<String, String> permissions) {
|
||||||
this.permissions = permissions;
|
this.permissions = permissions;
|
||||||
}
|
}
|
||||||
@@ -75,7 +79,9 @@ public class GHAppInstallationToken {
|
|||||||
*
|
*
|
||||||
* @param token
|
* @param token
|
||||||
* the token
|
* the token
|
||||||
|
* @deprecated Do not use this method. It was added due to incomplete understanding of Jackson binding.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void setToken(String token) {
|
public void setToken(String token) {
|
||||||
this.token = token;
|
this.token = token;
|
||||||
}
|
}
|
||||||
@@ -94,7 +100,9 @@ public class GHAppInstallationToken {
|
|||||||
*
|
*
|
||||||
* @param repositories
|
* @param repositories
|
||||||
* the repositories
|
* the repositories
|
||||||
|
* @deprecated Do not use this method. It was added due to incomplete understanding of Jackson binding.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void setRepositories(List<GHRepository> repositories) {
|
public void setRepositories(List<GHRepository> repositories) {
|
||||||
this.repositories = repositories;
|
this.repositories = repositories;
|
||||||
}
|
}
|
||||||
@@ -113,7 +121,9 @@ public class GHAppInstallationToken {
|
|||||||
*
|
*
|
||||||
* @param repositorySelection
|
* @param repositorySelection
|
||||||
* the repository selection
|
* the repository selection
|
||||||
|
* @deprecated Do not use this method. It was added due to incomplete understanding of Jackson binding.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void setRepositorySelection(GHRepositorySelection repositorySelection) {
|
public void setRepositorySelection(GHRepositorySelection repositorySelection) {
|
||||||
this.repositorySelection = repositorySelection;
|
this.repositorySelection = repositorySelection;
|
||||||
}
|
}
|
||||||
@@ -127,7 +137,7 @@ public class GHAppInstallationToken {
|
|||||||
*/
|
*/
|
||||||
@WithBridgeMethods(value = String.class, adapterMethod = "expiresAtStr")
|
@WithBridgeMethods(value = String.class, adapterMethod = "expiresAtStr")
|
||||||
public Date getExpiresAt() throws IOException {
|
public Date getExpiresAt() throws IOException {
|
||||||
return GitHub.parseDate(expires_at);
|
return GitHubClient.parseDate(expires_at);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD", justification = "Bridge method of getExpiresAt")
|
@SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD", justification = "Bridge method of getExpiresAt")
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ public class GHAsset extends GHObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String getApiRoute() {
|
private String getApiRoute() {
|
||||||
return "/repos/" + owner.getOwnerName() + "/" + owner.getName() + "/releases/assets/" + id;
|
return "/repos/" + owner.getOwnerName() + "/" + owner.getName() + "/releases/assets/" + getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
GHAsset wrap(GHRelease release) {
|
GHAsset wrap(GHRelease release) {
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ public class GHAuthorization extends GHObject {
|
|||||||
* @return the app url
|
* @return the app url
|
||||||
*/
|
*/
|
||||||
public URL getAppUrl() {
|
public URL getAppUrl() {
|
||||||
return GitHub.parseURL(app.url);
|
return GitHubClient.parseURL(app.url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -112,10 +112,12 @@ public class GHAuthorization extends GHObject {
|
|||||||
* Gets api url.
|
* Gets api url.
|
||||||
*
|
*
|
||||||
* @return the api url
|
* @return the api url
|
||||||
|
* @deprecated use {@link #getUrl()}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
@SuppressFBWarnings(value = "NM_CONFUSING", justification = "It's a part of the library API, cannot be changed")
|
@SuppressFBWarnings(value = "NM_CONFUSING", justification = "It's a part of the library API, cannot be changed")
|
||||||
public URL getApiURL() {
|
public URL getApiURL() {
|
||||||
return GitHub.parseURL(url);
|
return getUrl();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -141,7 +143,7 @@ public class GHAuthorization extends GHObject {
|
|||||||
* @return the note url
|
* @return the note url
|
||||||
*/
|
*/
|
||||||
public URL getNoteUrl() {
|
public URL getNoteUrl() {
|
||||||
return GitHub.parseURL(note_url);
|
return GitHubClient.parseURL(note_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ public class GHBlob {
|
|||||||
* @return API URL of this blob.
|
* @return API URL of this blob.
|
||||||
*/
|
*/
|
||||||
public URL getUrl() {
|
public URL getUrl() {
|
||||||
return GitHub.parseURL(url);
|
return GitHubClient.parseURL(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import java.net.URL;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import javax.annotation.CheckForNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A branch in a repository.
|
* A branch in a repository.
|
||||||
*
|
*
|
||||||
@@ -90,7 +92,7 @@ public class GHBranch {
|
|||||||
@Preview
|
@Preview
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public URL getProtectionUrl() {
|
public URL getProtectionUrl() {
|
||||||
return GitHub.parseURL(protection_url);
|
return GitHubClient.parseURL(protection_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -101,7 +103,11 @@ public class GHBranch {
|
|||||||
* the io exception
|
* the io exception
|
||||||
*/
|
*/
|
||||||
public GHBranchProtection getProtection() throws IOException {
|
public GHBranchProtection getProtection() throws IOException {
|
||||||
return root.createRequest().withUrlPath(protection_url).fetch(GHBranchProtection.class).wrap(this);
|
return root.createRequest()
|
||||||
|
.withPreview(Previews.LUKE_CAGE)
|
||||||
|
.setRawUrlPath(protection_url)
|
||||||
|
.fetch(GHBranchProtection.class)
|
||||||
|
.wrap(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -120,7 +126,7 @@ public class GHBranch {
|
|||||||
* if disabling protection fails
|
* if disabling protection fails
|
||||||
*/
|
*/
|
||||||
public void disableProtection() throws IOException {
|
public void disableProtection() throws IOException {
|
||||||
root.createRequest().method("DELETE").withUrlPath(protection_url).send();
|
root.createRequest().method("DELETE").setRawUrlPath(protection_url).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -161,6 +167,59 @@ public class GHBranch {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merge a branch into this branch.
|
||||||
|
*
|
||||||
|
* @param headBranch
|
||||||
|
* the branch whose head will be merged
|
||||||
|
*
|
||||||
|
* @param commitMessage
|
||||||
|
* the commit message
|
||||||
|
*
|
||||||
|
* @return the merge {@link GHCommit} created, or {@code null} if the base already contains the head (nothing to
|
||||||
|
* merge).
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
* if merging fails
|
||||||
|
*/
|
||||||
|
@CheckForNull
|
||||||
|
public GHCommit merge(GHBranch headBranch, String commitMessage) throws IOException {
|
||||||
|
return merge(headBranch.getName(), commitMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merge a ref into this branch.
|
||||||
|
*
|
||||||
|
* @param head
|
||||||
|
* the ref name that will be merged into this branch. Follows the usual ref naming rules, could be a
|
||||||
|
* branch name, tag, or commit sha.
|
||||||
|
*
|
||||||
|
* @param commitMessage
|
||||||
|
* the commit message
|
||||||
|
*
|
||||||
|
* @return the merge {@link GHCommit} created, or {@code null} if the base already contains the head (nothing to
|
||||||
|
* merge).
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
* if merging fails
|
||||||
|
*/
|
||||||
|
@CheckForNull
|
||||||
|
public GHCommit merge(String head, String commitMessage) throws IOException {
|
||||||
|
GHCommit result = root.createRequest()
|
||||||
|
.withUrlPath(owner.getApiTailUrl("merges"))
|
||||||
|
.method("POST")
|
||||||
|
.with("commit_message", commitMessage)
|
||||||
|
.with("base", this.name)
|
||||||
|
.with("head", head)
|
||||||
|
.fetch(GHCommit.class);
|
||||||
|
|
||||||
|
if (result != null) {
|
||||||
|
result.wrapUp(owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
String getApiRoute() {
|
String getApiRoute() {
|
||||||
return owner.getApiTailUrl("/branches/" + name);
|
return owner.getApiTailUrl("/branches/" + name);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import static org.kohsuke.github.Previews.ZZZAX;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The type GHBranchProtection.
|
* The type GHBranchProtection.
|
||||||
|
*
|
||||||
|
* @see <a href="https://docs.github.com/en/rest/reference/repos#get-branch-protection">GitHub Branch Protection</a>
|
||||||
*/
|
*/
|
||||||
@SuppressFBWarnings(
|
@SuppressFBWarnings(
|
||||||
value = { "UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD",
|
value = { "UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD",
|
||||||
|
|||||||
@@ -1,30 +1,47 @@
|
|||||||
package org.kohsuke.github;
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import edu.umd.cs.findbugs.annotations.NonNull;
|
||||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a deployment
|
* Represents a check run.
|
||||||
*
|
*
|
||||||
* @see <a href="https://developer.github.com/v3/checks/runs/">documentation</a>
|
* @see <a href="https://developer.github.com/v3/checks/runs/">documentation</a>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@SuppressFBWarnings(value = { "UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD", "URF_UNREAD_FIELD" },
|
@SuppressFBWarnings(value = { "UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD", "URF_UNREAD_FIELD" },
|
||||||
justification = "JSON API")
|
justification = "JSON API")
|
||||||
public class GHCheckRun extends GHObject {
|
public class GHCheckRun extends GHObject {
|
||||||
|
|
||||||
|
@JsonProperty("repository")
|
||||||
GHRepository owner;
|
GHRepository owner;
|
||||||
GitHub root;
|
GitHub root;
|
||||||
|
|
||||||
private String status;
|
private String status;
|
||||||
private String conclusion;
|
private String conclusion;
|
||||||
private String name;
|
private String name;
|
||||||
|
private String headSha;
|
||||||
|
private String nodeId;
|
||||||
|
private String externalId;
|
||||||
|
private String startedAt;
|
||||||
|
private String completedAt;
|
||||||
|
private URL htmlUrl;
|
||||||
|
private URL detailsUrl;
|
||||||
|
private Output output;
|
||||||
|
private GHApp app;
|
||||||
private GHPullRequest[] pullRequests;
|
private GHPullRequest[] pullRequests;
|
||||||
|
private GHCheckSuite checkSuite;
|
||||||
|
|
||||||
GHCheckRun wrap(GHRepository owner) {
|
GHCheckRun wrap(GHRepository owner) {
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
this.root = owner.root;
|
wrap(owner.root);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,7 +49,24 @@ public class GHCheckRun extends GHObject {
|
|||||||
this.root = root;
|
this.root = root;
|
||||||
if (owner != null) {
|
if (owner != null) {
|
||||||
owner.wrap(root);
|
owner.wrap(root);
|
||||||
|
if (pullRequests != null && pullRequests.length != 0) {
|
||||||
|
for (GHPullRequest singlePull : pullRequests) {
|
||||||
|
singlePull.wrap(owner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
if (checkSuite != null) {
|
||||||
|
if (owner != null) {
|
||||||
|
checkSuite.wrap(owner);
|
||||||
|
} else {
|
||||||
|
checkSuite.wrap(root);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (app != null) {
|
||||||
|
app.wrapUp(root);
|
||||||
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,33 +74,234 @@ public class GHCheckRun extends GHObject {
|
|||||||
return pullRequests;
|
return pullRequests;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets status of the check run.
|
||||||
|
*
|
||||||
|
* @return Status of the check run
|
||||||
|
* @see Status
|
||||||
|
*/
|
||||||
public String getStatus() {
|
public String getStatus() {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static enum Status {
|
||||||
|
QUEUED, IN_PROGRESS, COMPLETED
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets conclusion of a completed check run.
|
||||||
|
*
|
||||||
|
* @return Status of the check run
|
||||||
|
* @see Conclusion
|
||||||
|
*/
|
||||||
public String getConclusion() {
|
public String getConclusion() {
|
||||||
return conclusion;
|
return conclusion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Final conclusion of the check.
|
||||||
|
*
|
||||||
|
* From <a href="https://docs.github.com/en/rest/reference/checks#create-a-check-run--parameters">Check Run
|
||||||
|
* Parameters - <code>conclusion</code></a>.
|
||||||
|
*/
|
||||||
|
public static enum Conclusion {
|
||||||
|
SUCCESS, FAILURE, NEUTRAL, CANCELLED, TIMED_OUT, ACTION_REQUIRED, SKIPPED
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the custom name of this check run.
|
||||||
|
*
|
||||||
|
* @return Name of the check run
|
||||||
|
*/
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
GHPullRequest[] getPullRequests() throws IOException {
|
/**
|
||||||
if (pullRequests != null && pullRequests.length != 0) {
|
* Gets the HEAD SHA.
|
||||||
for (GHPullRequest singlePull : pullRequests) {
|
*
|
||||||
singlePull.refresh();
|
* @return sha for the HEAD commit
|
||||||
}
|
*/
|
||||||
}
|
public String getHeadSha() {
|
||||||
return pullRequests;
|
return headSha;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated This object has no HTML URL.
|
* Gets the pull requests participated in this check run.
|
||||||
|
*
|
||||||
|
* Note this field is only populated for events. When getting a {@link GHCheckRun} outside of an event, this is
|
||||||
|
* always empty.
|
||||||
|
*
|
||||||
|
* @return the list of {@link GHPullRequest}s for this check run. Only populated for events.
|
||||||
|
* @throws IOException
|
||||||
|
* the io exception
|
||||||
|
*/
|
||||||
|
public List<GHPullRequest> getPullRequests() throws IOException {
|
||||||
|
if (pullRequests != null && pullRequests.length != 0) {
|
||||||
|
for (GHPullRequest singlePull : pullRequests) {
|
||||||
|
// Only refresh if we haven't do so before
|
||||||
|
singlePull.refresh(singlePull.getTitle());
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableList(Arrays.asList(pullRequests));
|
||||||
|
}
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the HTML URL: https://github.com/[owner]/[repo-name]/runs/[check-run-id], usually an GitHub Action page of
|
||||||
|
* the check run.
|
||||||
|
*
|
||||||
|
* @return HTML URL
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public URL getHtmlUrl() {
|
public URL getHtmlUrl() {
|
||||||
return null;
|
return htmlUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the global node id to access most objects in GitHub.
|
||||||
|
*
|
||||||
|
* @see <a href="https://developer.github.com/v4/guides/using-global-node-ids/">documentation</a>
|
||||||
|
* @return Global node id
|
||||||
|
*/
|
||||||
|
public String getNodeId() {
|
||||||
|
return nodeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a reference for the check run on the integrator's system.
|
||||||
|
*
|
||||||
|
* @return Reference id
|
||||||
|
*/
|
||||||
|
public String getExternalId() {
|
||||||
|
return externalId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the details URL from which to find full details of the check run on the integrator's site.
|
||||||
|
*
|
||||||
|
* @return Details URL
|
||||||
|
*/
|
||||||
|
public URL getDetailsUrl() {
|
||||||
|
return detailsUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the start time of the check run in ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ.
|
||||||
|
*
|
||||||
|
* @return Timestamp of the start time
|
||||||
|
*/
|
||||||
|
public Date getStartedAt() {
|
||||||
|
return GitHubClient.parseDate(startedAt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the completed time of the check run in ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ.
|
||||||
|
*
|
||||||
|
* @return Timestamp of the completed time
|
||||||
|
*/
|
||||||
|
public Date getCompletedAt() {
|
||||||
|
return GitHubClient.parseDate(completedAt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the GitHub app this check run belongs to, included in response.
|
||||||
|
*
|
||||||
|
* @return GitHub App
|
||||||
|
*/
|
||||||
|
public GHApp getApp() {
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the check suite this check run belongs to
|
||||||
|
*
|
||||||
|
* @return Check suite
|
||||||
|
*/
|
||||||
|
public GHCheckSuite getCheckSuite() {
|
||||||
|
return checkSuite;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an output for a check run.
|
||||||
|
*
|
||||||
|
* @return Output of a check run
|
||||||
|
*/
|
||||||
|
public Output getOutput() {
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an output in a check run to include summary and other results.
|
||||||
|
*
|
||||||
|
* @see <a href="https://developer.github.com/v3/checks/runs/#output-object">documentation</a>
|
||||||
|
*/
|
||||||
|
public static class Output {
|
||||||
|
private String title;
|
||||||
|
private String summary;
|
||||||
|
private String text;
|
||||||
|
private int annotationsCount;
|
||||||
|
private URL annotationsUrl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the title of check run.
|
||||||
|
*
|
||||||
|
* @return title of check run
|
||||||
|
*/
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the summary of the check run, note that it supports Markdown.
|
||||||
|
*
|
||||||
|
* @return summary of check run
|
||||||
|
*/
|
||||||
|
public String getSummary() {
|
||||||
|
return summary;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the details of the check run, note that it supports Markdown.
|
||||||
|
*
|
||||||
|
* @return Details of the check run
|
||||||
|
*/
|
||||||
|
public String getText() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the annotation count of a check run.
|
||||||
|
*
|
||||||
|
* @return annotation count of a check run
|
||||||
|
*/
|
||||||
|
public int getAnnotationsCount() {
|
||||||
|
return annotationsCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the URL of annotations.
|
||||||
|
*
|
||||||
|
* @return URL of annotations
|
||||||
|
*/
|
||||||
|
public URL getAnnotationsUrl() {
|
||||||
|
return annotationsUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static enum AnnotationLevel {
|
||||||
|
NOTICE, WARNING, FAILURE
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates this check run.
|
||||||
|
*
|
||||||
|
* @return a builder which you should customize, then call {@link GHCheckRunBuilder#create}
|
||||||
|
*/
|
||||||
|
@Preview
|
||||||
|
@Deprecated
|
||||||
|
public @NonNull GHCheckRunBuilder update() {
|
||||||
|
return new GHCheckRunBuilder(owner, getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
306
src/main/java/org/kohsuke/github/GHCheckRunBuilder.java
Normal file
306
src/main/java/org/kohsuke/github/GHCheckRunBuilder.java
Normal file
@@ -0,0 +1,306 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
*
|
||||||
|
* Copyright 2020 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 com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import edu.umd.cs.findbugs.annotations.CheckForNull;
|
||||||
|
import edu.umd.cs.findbugs.annotations.NonNull;
|
||||||
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Drafts or updates a check run.
|
||||||
|
*
|
||||||
|
* @see GHCheckRun
|
||||||
|
* @see GHRepository#createCheckRun
|
||||||
|
* @see <a href="https://developer.github.com/v3/checks/runs/#create-a-check-run">documentation</a>
|
||||||
|
* @see GHCheckRun#update()
|
||||||
|
* @see <a href="https://developer.github.com/v3/checks/runs/#update-a-check-run">documentation</a>
|
||||||
|
*/
|
||||||
|
@SuppressFBWarnings(value = "URF_UNREAD_FIELD", justification = "Jackson serializes these even without a getter")
|
||||||
|
@Preview
|
||||||
|
@Deprecated
|
||||||
|
public final class GHCheckRunBuilder {
|
||||||
|
|
||||||
|
protected final GHRepository repo;
|
||||||
|
protected final Requester requester;
|
||||||
|
private Output output;
|
||||||
|
private List<Action> actions;
|
||||||
|
|
||||||
|
private GHCheckRunBuilder(GHRepository repo, Requester requester) {
|
||||||
|
this.repo = repo;
|
||||||
|
this.requester = requester;
|
||||||
|
}
|
||||||
|
|
||||||
|
GHCheckRunBuilder(GHRepository repo, String name, String headSHA) {
|
||||||
|
this(repo,
|
||||||
|
repo.root.createRequest()
|
||||||
|
.withPreview(Previews.ANTIOPE)
|
||||||
|
.method("POST")
|
||||||
|
.with("name", name)
|
||||||
|
.with("head_sha", headSHA)
|
||||||
|
.withUrlPath(repo.getApiTailUrl("check-runs")));
|
||||||
|
}
|
||||||
|
|
||||||
|
GHCheckRunBuilder(GHRepository repo, long checkId) {
|
||||||
|
this(repo,
|
||||||
|
repo.root.createRequest()
|
||||||
|
.withPreview(Previews.ANTIOPE)
|
||||||
|
.method("PATCH")
|
||||||
|
.withUrlPath(repo.getApiTailUrl("check-runs/" + checkId)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NonNull GHCheckRunBuilder withDetailsURL(@CheckForNull String detailsURL) {
|
||||||
|
requester.with("details_url", detailsURL);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NonNull GHCheckRunBuilder withExternalID(@CheckForNull String externalID) {
|
||||||
|
requester.with("external_id", externalID);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NonNull GHCheckRunBuilder withStatus(@CheckForNull GHCheckRun.Status status) {
|
||||||
|
if (status != null) {
|
||||||
|
// Do *not* use the overload taking Enum, as that s/_/-/g which would be wrong here.
|
||||||
|
requester.with("status", status.toString().toLowerCase(Locale.ROOT));
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NonNull GHCheckRunBuilder withConclusion(@CheckForNull GHCheckRun.Conclusion conclusion) {
|
||||||
|
if (conclusion != null) {
|
||||||
|
requester.with("conclusion", conclusion.toString().toLowerCase(Locale.ROOT));
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NonNull GHCheckRunBuilder withStartedAt(@CheckForNull Date startedAt) {
|
||||||
|
if (startedAt != null) {
|
||||||
|
requester.with("started_at", GitHubClient.printDate(startedAt));
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NonNull GHCheckRunBuilder withCompletedAt(@CheckForNull Date completedAt) {
|
||||||
|
if (completedAt != null) {
|
||||||
|
requester.with("completed_at", GitHubClient.printDate(completedAt));
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NonNull GHCheckRunBuilder add(@NonNull Output output) {
|
||||||
|
if (this.output != null) {
|
||||||
|
throw new IllegalStateException("cannot add Output twice");
|
||||||
|
}
|
||||||
|
this.output = output;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NonNull GHCheckRunBuilder add(@NonNull Action action) {
|
||||||
|
if (actions == null) {
|
||||||
|
actions = new LinkedList<>();
|
||||||
|
}
|
||||||
|
actions.add(action);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final int MAX_ANNOTATIONS = 50;
|
||||||
|
/**
|
||||||
|
* Actually creates the check run. (If more than fifty annotations were requested, this is done in batches.)
|
||||||
|
*
|
||||||
|
* @return the resulting run
|
||||||
|
* @throws IOException
|
||||||
|
* for the usual reasons
|
||||||
|
*/
|
||||||
|
public @NonNull GHCheckRun create() throws IOException {
|
||||||
|
List<Annotation> extraAnnotations;
|
||||||
|
if (output != null && output.annotations != null && output.annotations.size() > MAX_ANNOTATIONS) {
|
||||||
|
extraAnnotations = output.annotations.subList(MAX_ANNOTATIONS, output.annotations.size());
|
||||||
|
output.annotations = output.annotations.subList(0, MAX_ANNOTATIONS);
|
||||||
|
} else {
|
||||||
|
extraAnnotations = Collections.emptyList();
|
||||||
|
}
|
||||||
|
GHCheckRun run = requester.with("output", output).with("actions", actions).fetch(GHCheckRun.class).wrap(repo);
|
||||||
|
while (!extraAnnotations.isEmpty()) {
|
||||||
|
Output output2 = new Output(output.title, output.summary);
|
||||||
|
int i = Math.min(extraAnnotations.size(), MAX_ANNOTATIONS);
|
||||||
|
output2.annotations = extraAnnotations.subList(0, i);
|
||||||
|
extraAnnotations = extraAnnotations.subList(i, extraAnnotations.size());
|
||||||
|
run = repo.root.createRequest()
|
||||||
|
.withPreview(Previews.ANTIOPE)
|
||||||
|
.method("PATCH")
|
||||||
|
.with("output", output2)
|
||||||
|
.withUrlPath(repo.getApiTailUrl("check-runs/" + run.getId()))
|
||||||
|
.fetch(GHCheckRun.class)
|
||||||
|
.wrap(repo);
|
||||||
|
}
|
||||||
|
return run;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see <a href="https://developer.github.com/v3/checks/runs/#output-object">documentation</a>
|
||||||
|
*/
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
|
public static final class Output {
|
||||||
|
|
||||||
|
private final String title;
|
||||||
|
private final String summary;
|
||||||
|
private String text;
|
||||||
|
private List<Annotation> annotations;
|
||||||
|
private List<Image> images;
|
||||||
|
|
||||||
|
public Output(@NonNull String title, @NonNull String summary) {
|
||||||
|
this.title = title;
|
||||||
|
this.summary = summary;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NonNull Output withText(@CheckForNull String text) {
|
||||||
|
this.text = text;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NonNull Output add(@NonNull Annotation annotation) {
|
||||||
|
if (annotations == null) {
|
||||||
|
annotations = new LinkedList<>();
|
||||||
|
}
|
||||||
|
annotations.add(annotation);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NonNull Output add(@NonNull Image image) {
|
||||||
|
if (images == null) {
|
||||||
|
images = new LinkedList<>();
|
||||||
|
}
|
||||||
|
images.add(image);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see <a href="https://developer.github.com/v3/checks/runs/#annotations-object">documentation</a>
|
||||||
|
*/
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
|
public static final class Annotation {
|
||||||
|
|
||||||
|
private final String path;
|
||||||
|
private final int start_line;
|
||||||
|
private final int end_line;
|
||||||
|
private final String annotation_level;
|
||||||
|
private final String message;
|
||||||
|
private Integer start_column;
|
||||||
|
private Integer end_column;
|
||||||
|
private String title;
|
||||||
|
private String raw_details;
|
||||||
|
|
||||||
|
public Annotation(@NonNull String path,
|
||||||
|
int line,
|
||||||
|
@NonNull GHCheckRun.AnnotationLevel annotationLevel,
|
||||||
|
@NonNull String message) {
|
||||||
|
this(path, line, line, annotationLevel, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Annotation(@NonNull String path,
|
||||||
|
int startLine,
|
||||||
|
int endLine,
|
||||||
|
@NonNull GHCheckRun.AnnotationLevel annotationLevel,
|
||||||
|
@NonNull String message) {
|
||||||
|
this.path = path;
|
||||||
|
start_line = startLine;
|
||||||
|
end_line = endLine;
|
||||||
|
annotation_level = annotationLevel.toString().toLowerCase(Locale.ROOT);
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NonNull Annotation withStartColumn(@CheckForNull Integer startColumn) {
|
||||||
|
start_column = startColumn;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NonNull Annotation withEndColumn(@CheckForNull Integer endColumn) {
|
||||||
|
end_column = endColumn;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NonNull Annotation withTitle(@CheckForNull String title) {
|
||||||
|
this.title = title;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NonNull Annotation withRawDetails(@CheckForNull String rawDetails) {
|
||||||
|
raw_details = rawDetails;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see <a href="https://developer.github.com/v3/checks/runs/#images-object">documentation</a>
|
||||||
|
*/
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
|
public static final class Image {
|
||||||
|
|
||||||
|
private final String alt;
|
||||||
|
private final String image_url;
|
||||||
|
private String caption;
|
||||||
|
|
||||||
|
public Image(@NonNull String alt, @NonNull String imageURL) {
|
||||||
|
this.alt = alt;
|
||||||
|
image_url = imageURL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NonNull Image withCaption(@CheckForNull String caption) {
|
||||||
|
this.caption = caption;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see <a href="https://developer.github.com/v3/checks/runs/#actions-object">documentation</a>
|
||||||
|
*/
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
|
public static final class Action {
|
||||||
|
|
||||||
|
private final String label;
|
||||||
|
private final String description;
|
||||||
|
private final String identifier;
|
||||||
|
|
||||||
|
public Action(@NonNull String label, @NonNull String description, @NonNull String identifier) {
|
||||||
|
this.label = label;
|
||||||
|
this.description = description;
|
||||||
|
this.identifier = identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
44
src/main/java/org/kohsuke/github/GHCheckRunsIterable.java
Normal file
44
src/main/java/org/kohsuke/github/GHCheckRunsIterable.java
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterable for check-runs listing.
|
||||||
|
*/
|
||||||
|
class GHCheckRunsIterable extends PagedIterable<GHCheckRun> {
|
||||||
|
private GitHub root;
|
||||||
|
private final GitHubRequest request;
|
||||||
|
|
||||||
|
private GHCheckRunsPage result;
|
||||||
|
|
||||||
|
public GHCheckRunsIterable(GitHub root, GitHubRequest request) {
|
||||||
|
this.root = root;
|
||||||
|
this.request = request;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public PagedIterator<GHCheckRun> _iterator(int pageSize) {
|
||||||
|
return new PagedIterator<>(
|
||||||
|
adapt(GitHubPageIterator.create(root.getClient(), GHCheckRunsPage.class, request, pageSize)),
|
||||||
|
null);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Iterator<GHCheckRun[]> adapt(final Iterator<GHCheckRunsPage> base) {
|
||||||
|
return new Iterator<GHCheckRun[]>() {
|
||||||
|
public boolean hasNext() {
|
||||||
|
return base.hasNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHCheckRun[] next() {
|
||||||
|
GHCheckRunsPage v = base.next();
|
||||||
|
if (result == null) {
|
||||||
|
result = v;
|
||||||
|
}
|
||||||
|
return v.getCheckRuns(root);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
20
src/main/java/org/kohsuke/github/GHCheckRunsPage.java
Normal file
20
src/main/java/org/kohsuke/github/GHCheckRunsPage.java
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the one page of check-runs result when listing check-runs.
|
||||||
|
*/
|
||||||
|
class GHCheckRunsPage {
|
||||||
|
private int total_count;
|
||||||
|
private GHCheckRun[] check_runs;
|
||||||
|
|
||||||
|
public int getTotalCount() {
|
||||||
|
return total_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
GHCheckRun[] getCheckRuns(GitHub root) {
|
||||||
|
for (GHCheckRun check_run : check_runs) {
|
||||||
|
check_run.wrap(root);
|
||||||
|
}
|
||||||
|
return check_runs;
|
||||||
|
}
|
||||||
|
}
|
||||||
260
src/main/java/org/kohsuke/github/GHCheckSuite.java
Normal file
260
src/main/java/org/kohsuke/github/GHCheckSuite.java
Normal file
@@ -0,0 +1,260 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a check suite.
|
||||||
|
*
|
||||||
|
* @see <a href="https://developer.github.com/v3/checks/suites/">documentation</a>
|
||||||
|
*/
|
||||||
|
@SuppressFBWarnings(value = { "UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD", "URF_UNREAD_FIELD" },
|
||||||
|
justification = "JSON API")
|
||||||
|
public class GHCheckSuite extends GHObject {
|
||||||
|
|
||||||
|
@JsonProperty("repository")
|
||||||
|
GHRepository owner;
|
||||||
|
GitHub root;
|
||||||
|
|
||||||
|
private String nodeId;
|
||||||
|
private String headBranch;
|
||||||
|
private String headSha;
|
||||||
|
private String status;
|
||||||
|
private String conclusion;
|
||||||
|
private String before;
|
||||||
|
private String after;
|
||||||
|
private int latestCheckRunsCount;
|
||||||
|
private URL checkRunsUrl;
|
||||||
|
private HeadCommit headCommit;
|
||||||
|
private GHApp app;
|
||||||
|
private GHPullRequest[] pullRequests;
|
||||||
|
|
||||||
|
GHCheckSuite wrap(GHRepository owner) {
|
||||||
|
this.owner = owner;
|
||||||
|
this.wrap(owner.root);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
GHCheckSuite wrap(GitHub root) {
|
||||||
|
this.root = root;
|
||||||
|
if (owner != null) {
|
||||||
|
owner.wrap(root);
|
||||||
|
if (pullRequests != null && pullRequests.length != 0) {
|
||||||
|
for (GHPullRequest singlePull : pullRequests) {
|
||||||
|
singlePull.wrap(owner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (app != null) {
|
||||||
|
app.wrapUp(root);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
GHPullRequest[] wrap() {
|
||||||
|
return pullRequests;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the global node id to access most objects in GitHub.
|
||||||
|
*
|
||||||
|
* @see <a href="https://developer.github.com/v4/guides/using-global-node-ids/">documentation</a>
|
||||||
|
* @return global node id
|
||||||
|
*/
|
||||||
|
public String getNodeId() {
|
||||||
|
return nodeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The head branch name the changes are on.
|
||||||
|
*
|
||||||
|
* @return head branch name
|
||||||
|
*/
|
||||||
|
public String getHeadBranch() {
|
||||||
|
return headBranch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the HEAD SHA.
|
||||||
|
*
|
||||||
|
* @return sha for the HEAD commit
|
||||||
|
*/
|
||||||
|
public String getHeadSha() {
|
||||||
|
return headSha;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets status of the check suite. It can be one of request, in_progress, or completed.
|
||||||
|
*
|
||||||
|
* @return status of the check suite
|
||||||
|
*/
|
||||||
|
public String getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets conclusion of a completed check suite. It can be one of success, failure, neutral, cancelled, time_out,
|
||||||
|
* action_required, or stale. The check suite will report the highest priority check run conclusion in the check
|
||||||
|
* suite's conclusion.
|
||||||
|
*
|
||||||
|
* @return conclusion of the check suite
|
||||||
|
*/
|
||||||
|
public String getConclusion() {
|
||||||
|
return conclusion;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The SHA of the most recent commit on ref before the push.
|
||||||
|
*
|
||||||
|
* @return sha of a commit
|
||||||
|
*/
|
||||||
|
public String getBefore() {
|
||||||
|
return before;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The SHA of the most recent commit on ref after the push.
|
||||||
|
*
|
||||||
|
* @return sha of a commit
|
||||||
|
*/
|
||||||
|
public String getAfter() {
|
||||||
|
return after;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The quantity of check runs that had run as part of the latest push.
|
||||||
|
*
|
||||||
|
* @return sha of the most recent commit
|
||||||
|
*/
|
||||||
|
public int getLatestCheckRunsCount() {
|
||||||
|
return latestCheckRunsCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The url used to list all the check runs belonged to this suite.
|
||||||
|
*
|
||||||
|
* @return url containing all check runs
|
||||||
|
*/
|
||||||
|
public URL getCheckRunsUrl() {
|
||||||
|
return checkRunsUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The commit of current head.
|
||||||
|
*
|
||||||
|
* @return head commit
|
||||||
|
*/
|
||||||
|
public HeadCommit getHeadCommit() {
|
||||||
|
return headCommit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the GitHub app this check suite belongs to, included in response.
|
||||||
|
*
|
||||||
|
* @return GitHub App
|
||||||
|
*/
|
||||||
|
public GHApp getApp() {
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the pull requests participated in this check suite.
|
||||||
|
*
|
||||||
|
* Note this field is only populated for events. When getting a {@link GHCheckSuite} outside of an event, this is
|
||||||
|
* always empty.
|
||||||
|
*
|
||||||
|
* @return the list of {@link GHPullRequest}s for this check suite. Only populated for events.
|
||||||
|
* @throws IOException
|
||||||
|
* the io exception
|
||||||
|
*/
|
||||||
|
public List<GHPullRequest> getPullRequests() throws IOException {
|
||||||
|
if (pullRequests != null && pullRequests.length != 0) {
|
||||||
|
for (GHPullRequest singlePull : pullRequests) {
|
||||||
|
// Only refresh if we haven't do so before
|
||||||
|
singlePull.refresh(singlePull.getTitle());
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableList(Arrays.asList(pullRequests));
|
||||||
|
}
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check suite doesn't have a HTML URL.
|
||||||
|
*
|
||||||
|
* @return null
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public URL getHtmlUrl() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class HeadCommit {
|
||||||
|
private String id;
|
||||||
|
private String treeId;
|
||||||
|
private String message;
|
||||||
|
private String timestamp;
|
||||||
|
private GitUser author;
|
||||||
|
private GitUser committer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets id of the commit, used by {@link GHCheckSuite} when a {@link GHEvent#CHECK_SUITE} comes
|
||||||
|
*
|
||||||
|
* @return id of the commit
|
||||||
|
*/
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets id of the tree.
|
||||||
|
*
|
||||||
|
* @return id of the tree
|
||||||
|
*/
|
||||||
|
public String getTreeId() {
|
||||||
|
return treeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets message.
|
||||||
|
*
|
||||||
|
* @return commit message.
|
||||||
|
*/
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets timestamp of the commit.
|
||||||
|
*
|
||||||
|
* @return timestamp of the commit
|
||||||
|
*/
|
||||||
|
public Date getTimestamp() {
|
||||||
|
return GitHubClient.parseDate(timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets author.
|
||||||
|
*
|
||||||
|
* @return the author
|
||||||
|
*/
|
||||||
|
public GitUser getAuthor() {
|
||||||
|
return author;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets committer.
|
||||||
|
*
|
||||||
|
* @return the committer
|
||||||
|
*/
|
||||||
|
public GitUser getCommitter() {
|
||||||
|
return committer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,6 +11,8 @@ import java.util.Collections;
|
|||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.kohsuke.github.Previews.GROOT;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A commit in a repository.
|
* A commit in a repository.
|
||||||
*
|
*
|
||||||
@@ -39,6 +41,8 @@ public class GHCommit {
|
|||||||
|
|
||||||
private int comment_count;
|
private int comment_count;
|
||||||
|
|
||||||
|
private GHVerification verification;
|
||||||
|
|
||||||
static class Tree {
|
static class Tree {
|
||||||
String sha;
|
String sha;
|
||||||
}
|
}
|
||||||
@@ -61,7 +65,7 @@ public class GHCommit {
|
|||||||
* @return the authored date
|
* @return the authored date
|
||||||
*/
|
*/
|
||||||
public Date getAuthoredDate() {
|
public Date getAuthoredDate() {
|
||||||
return GitHub.parseDate(author.date);
|
return GitHubClient.parseDate(author.date);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -80,7 +84,7 @@ public class GHCommit {
|
|||||||
* @return the commit date
|
* @return the commit date
|
||||||
*/
|
*/
|
||||||
public Date getCommitDate() {
|
public Date getCommitDate() {
|
||||||
return GitHub.parseDate(committer.date);
|
return GitHubClient.parseDate(committer.date);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -100,6 +104,15 @@ public class GHCommit {
|
|||||||
public int getCommentCount() {
|
public int getCommentCount() {
|
||||||
return comment_count;
|
return comment_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets Verification Status.
|
||||||
|
*
|
||||||
|
* @return the Verification status
|
||||||
|
*/
|
||||||
|
public GHVerification getVerification() {
|
||||||
|
return verification;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -201,7 +214,7 @@ public class GHCommit {
|
|||||||
* resolves to the actual content of the file.
|
* resolves to the actual content of the file.
|
||||||
*/
|
*/
|
||||||
public URL getRawUrl() {
|
public URL getRawUrl() {
|
||||||
return GitHub.parseURL(raw_url);
|
return GitHubClient.parseURL(raw_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -212,7 +225,7 @@ public class GHCommit {
|
|||||||
* that resolves to the HTML page that describes this file.
|
* that resolves to the HTML page that describes this file.
|
||||||
*/
|
*/
|
||||||
public URL getBlobUrl() {
|
public URL getBlobUrl() {
|
||||||
return GitHub.parseURL(blob_url);
|
return GitHubClient.parseURL(blob_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -326,7 +339,7 @@ public class GHCommit {
|
|||||||
* "https://github.com/kohsuke/sandbox-ant/commit/8ae38db0ea5837313ab5f39d43a6f73de3bd9000"
|
* "https://github.com/kohsuke/sandbox-ant/commit/8ae38db0ea5837313ab5f39d43a6f73de3bd9000"
|
||||||
*/
|
*/
|
||||||
public URL getHtmlUrl() {
|
public URL getHtmlUrl() {
|
||||||
return GitHub.parseURL(html_url);
|
return GitHubClient.parseURL(html_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -435,6 +448,39 @@ public class GHCommit {
|
|||||||
return owner.root.getUser(author.login);
|
return owner.root.getUser(author.login);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a list of pull requests which contain this commit.
|
||||||
|
*
|
||||||
|
* @return {@link PagedIterable} with the pull requests which contain this commit
|
||||||
|
*/
|
||||||
|
@Preview
|
||||||
|
@Deprecated
|
||||||
|
public PagedIterable<GHPullRequest> listPullRequests() {
|
||||||
|
return owner.root.createRequest()
|
||||||
|
.withPreview(GROOT)
|
||||||
|
.withUrlPath(String.format("/repos/%s/%s/commits/%s/pulls", owner.getOwnerName(), owner.getName(), sha))
|
||||||
|
.toIterable(GHPullRequest[].class, item -> item.wrapUp(owner));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a list of branches where this commit is the head commit.
|
||||||
|
*
|
||||||
|
* @return {@link PagedIterable} with the branches where the commit is the head commit
|
||||||
|
* @throws IOException
|
||||||
|
* the io exception
|
||||||
|
*/
|
||||||
|
@Preview
|
||||||
|
@Deprecated
|
||||||
|
public PagedIterable<GHBranch> listBranchesWhereHead() throws IOException {
|
||||||
|
return owner.root.createRequest()
|
||||||
|
.withPreview(GROOT)
|
||||||
|
.withUrlPath(String.format("/repos/%s/%s/commits/%s/branches-where-head",
|
||||||
|
owner.getOwnerName(),
|
||||||
|
owner.getName(),
|
||||||
|
sha))
|
||||||
|
.toIterable(GHBranch[].class, item -> item.wrap(owner));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List comments paged iterable.
|
* List comments paged iterable.
|
||||||
*
|
*
|
||||||
@@ -512,6 +558,19 @@ public class GHCommit {
|
|||||||
return owner.getLastCommitStatus(sha);
|
return owner.getLastCommitStatus(sha);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets check-runs for given sha.
|
||||||
|
*
|
||||||
|
* @return check runs for given sha.
|
||||||
|
* @throws IOException
|
||||||
|
* on error
|
||||||
|
*/
|
||||||
|
@Preview
|
||||||
|
@Deprecated
|
||||||
|
public PagedIterable<GHCheckRun> getCheckRuns() throws IOException {
|
||||||
|
return owner.getCheckRuns(sha);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Some of the fields are not always filled in when this object is retrieved as a part of another API call.
|
* Some of the fields are not always filled in when this object is retrieved as a part of another API call.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -89,6 +89,19 @@ public class GHCommitBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures the PGP signature of this commit.
|
||||||
|
*
|
||||||
|
* @param signature
|
||||||
|
* the signature calculated from the commit
|
||||||
|
*
|
||||||
|
* @return the gh commit builder
|
||||||
|
*/
|
||||||
|
public GHCommitBuilder withSignature(String signature) {
|
||||||
|
req.with("signature", signature);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configures the committer of this commit.
|
* Configures the committer of this commit.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ public class GHCommitComment extends GHObject implements Reactable {
|
|||||||
* show this commit comment in a browser.
|
* show this commit comment in a browser.
|
||||||
*/
|
*/
|
||||||
public URL getHtmlUrl() {
|
public URL getHtmlUrl() {
|
||||||
return GitHub.parseURL(html_url);
|
return GitHubClient.parseURL(html_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -153,7 +153,7 @@ public class GHCommitComment extends GHObject implements Reactable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String getApiTail() {
|
private String getApiTail() {
|
||||||
return String.format("/repos/%s/%s/comments/%s", owner.getOwnerName(), owner.getName(), id);
|
return String.format("/repos/%s/%s/comments/%s", owner.getOwnerName(), owner.getName(), getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
GHCommitComment wrap(GHRepository owner) {
|
GHCommitComment wrap(GHRepository owner) {
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ public class GHCommitQueryBuilder {
|
|||||||
* @return the gh commit query builder
|
* @return the gh commit query builder
|
||||||
*/
|
*/
|
||||||
public GHCommitQueryBuilder since(Date dt) {
|
public GHCommitQueryBuilder since(Date dt) {
|
||||||
req.with("since", GitHub.printDate(dt));
|
req.with("since", GitHubClient.printDate(dt));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,7 +106,7 @@ public class GHCommitQueryBuilder {
|
|||||||
* @return the gh commit query builder
|
* @return the gh commit query builder
|
||||||
*/
|
*/
|
||||||
public GHCommitQueryBuilder until(Date dt) {
|
public GHCommitQueryBuilder until(Date dt) {
|
||||||
req.with("until", GitHub.printDate(dt));
|
req.with("until", GitHubClient.printDate(dt));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -259,7 +259,7 @@ public class GHCommitSearchBuilder extends GHSearchBuilder<GHCommit> {
|
|||||||
if (StringUtils.isBlank(commitUrl)) {
|
if (StringUtils.isBlank(commitUrl)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
int indexOfUsername = (GitHub.GITHUB_URL + "/repos/").length();
|
int indexOfUsername = (GitHubClient.GITHUB_URL + "/repos/").length();
|
||||||
String[] tokens = commitUrl.substring(indexOfUsername).split("/", 3);
|
String[] tokens = commitUrl.substring(indexOfUsername).split("/", 3);
|
||||||
return tokens[0] + '/' + tokens[1];
|
return tokens[0] + '/' + tokens[1];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ public class GHCompare {
|
|||||||
* @return the url
|
* @return the url
|
||||||
*/
|
*/
|
||||||
public URL getUrl() {
|
public URL getUrl() {
|
||||||
return GitHub.parseURL(url);
|
return GitHubClient.parseURL(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -36,7 +36,7 @@ public class GHCompare {
|
|||||||
* @return the html url
|
* @return the html url
|
||||||
*/
|
*/
|
||||||
public URL getHtmlUrl() {
|
public URL getHtmlUrl() {
|
||||||
return GitHub.parseURL(html_url);
|
return GitHubClient.parseURL(html_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -45,7 +45,7 @@ public class GHCompare {
|
|||||||
* @return the permalink url
|
* @return the permalink url
|
||||||
*/
|
*/
|
||||||
public URL getPermalinkUrl() {
|
public URL getPermalinkUrl() {
|
||||||
return GitHub.parseURL(permalink_url);
|
return GitHubClient.parseURL(permalink_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -54,7 +54,7 @@ public class GHCompare {
|
|||||||
* @return the diff url
|
* @return the diff url
|
||||||
*/
|
*/
|
||||||
public URL getDiffUrl() {
|
public URL getDiffUrl() {
|
||||||
return GitHub.parseURL(diff_url);
|
return GitHubClient.parseURL(diff_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -63,7 +63,7 @@ public class GHCompare {
|
|||||||
* @return the patch url
|
* @return the patch url
|
||||||
*/
|
*/
|
||||||
public URL getPatchUrl() {
|
public URL getPatchUrl() {
|
||||||
return GitHub.parseURL(patch_url);
|
return GitHubClient.parseURL(patch_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ public class GHContent implements Refreshable {
|
|||||||
private String sha;
|
private String sha;
|
||||||
private String name;
|
private String name;
|
||||||
private String path;
|
private String path;
|
||||||
|
private String target;
|
||||||
private String content;
|
private String content;
|
||||||
private String url; // this is the API url
|
private String url; // this is the API url
|
||||||
private String git_url; // this is the Blob url
|
private String git_url; // this is the Blob url
|
||||||
@@ -99,6 +100,15 @@ public class GHContent implements Refreshable {
|
|||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets target of a symlink. This will only be set if {@code "symlink".equals(getType())}
|
||||||
|
*
|
||||||
|
* @return the target
|
||||||
|
*/
|
||||||
|
public String getTarget() {
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the decoded content that is stored at this location.
|
* Retrieve the decoded content that is stored at this location.
|
||||||
*
|
*
|
||||||
@@ -388,22 +398,6 @@ public class GHContent implements Refreshable {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrap gh content [ ].
|
|
||||||
*
|
|
||||||
* @param contents
|
|
||||||
* the contents
|
|
||||||
* @param repository
|
|
||||||
* the repository
|
|
||||||
* @return the gh content [ ]
|
|
||||||
*/
|
|
||||||
public static GHContent[] wrap(GHContent[] contents, GHRepository repository) {
|
|
||||||
for (GHContent unwrappedContent : contents) {
|
|
||||||
unwrappedContent.wrap(repository);
|
|
||||||
}
|
|
||||||
return contents;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fully populate the data by retrieving missing data.
|
* Fully populate the data by retrieving missing data.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package org.kohsuke.github;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
|
||||||
|
import static org.kohsuke.github.Previews.BAPTISE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a repository
|
* Creates a repository
|
||||||
*
|
*
|
||||||
@@ -11,7 +13,7 @@ import java.net.URL;
|
|||||||
public class GHCreateRepositoryBuilder {
|
public class GHCreateRepositoryBuilder {
|
||||||
private final GitHub root;
|
private final GitHub root;
|
||||||
protected final Requester builder;
|
protected final Requester builder;
|
||||||
private final String apiUrlTail;
|
private String apiUrlTail;
|
||||||
|
|
||||||
GHCreateRepositoryBuilder(GitHub root, String apiUrlTail, String name) {
|
GHCreateRepositoryBuilder(GitHub root, String apiUrlTail, String name) {
|
||||||
this.root = root;
|
this.root = root;
|
||||||
@@ -79,6 +81,18 @@ public class GHCreateRepositoryBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables projects
|
||||||
|
*
|
||||||
|
* @param enabled
|
||||||
|
* true if enabled
|
||||||
|
* @return a builder to continue with building
|
||||||
|
*/
|
||||||
|
public GHCreateRepositoryBuilder projects(boolean enabled) {
|
||||||
|
this.builder.with("has_projects", enabled);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enables wiki
|
* Enables wiki
|
||||||
*
|
*
|
||||||
@@ -188,6 +202,51 @@ public class GHCreateRepositoryBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies whether the repository is a template.
|
||||||
|
*
|
||||||
|
* @param enabled
|
||||||
|
* true if enabled
|
||||||
|
* @return a builder to continue with building
|
||||||
|
*/
|
||||||
|
@Preview
|
||||||
|
@Deprecated
|
||||||
|
public GHCreateRepositoryBuilder templateRepository(boolean enabled) {
|
||||||
|
this.builder.withPreview(BAPTISE);
|
||||||
|
this.builder.with("is_template", enabled);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the ownership of the repository.
|
||||||
|
*
|
||||||
|
* @param owner
|
||||||
|
* organization or personage
|
||||||
|
* @return a builder to continue with building
|
||||||
|
*/
|
||||||
|
public GHCreateRepositoryBuilder owner(String owner) {
|
||||||
|
this.builder.with("owner", owner);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create repository from template repository.
|
||||||
|
*
|
||||||
|
* @param templateOwner
|
||||||
|
* template repository owner
|
||||||
|
* @param templateRepo
|
||||||
|
* template repository
|
||||||
|
* @return a builder to continue with building
|
||||||
|
* @see <a href="https://developer.github.com/v3/previews/">GitHub API Previews</a>
|
||||||
|
*/
|
||||||
|
@Preview
|
||||||
|
@Deprecated
|
||||||
|
public GHCreateRepositoryBuilder fromTemplateRepository(String templateOwner, String templateRepo) {
|
||||||
|
this.builder.withPreview(BAPTISE);
|
||||||
|
this.apiUrlTail = "/repos/" + templateOwner + "/" + templateRepo + "/generate";
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a repository with all the parameters.
|
* Creates a repository with all the parameters.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package org.kohsuke.github;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a deployment
|
* Represents a deployment
|
||||||
@@ -38,7 +39,7 @@ public class GHDeployment extends GHObject {
|
|||||||
* @return the statuses url
|
* @return the statuses url
|
||||||
*/
|
*/
|
||||||
public URL getStatusesUrl() {
|
public URL getStatusesUrl() {
|
||||||
return GitHub.parseURL(statuses_url);
|
return GitHubClient.parseURL(statuses_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -47,7 +48,7 @@ public class GHDeployment extends GHObject {
|
|||||||
* @return the repository url
|
* @return the repository url
|
||||||
*/
|
*/
|
||||||
public URL getRepositoryUrl() {
|
public URL getRepositoryUrl() {
|
||||||
return GitHub.parseURL(repository_url);
|
return GitHubClient.parseURL(repository_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -60,7 +61,8 @@ public class GHDeployment extends GHObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets payload.
|
* Gets payload. <b>NOTE:</b> only use this method if you can guarantee the payload will be a simple string,
|
||||||
|
* otherwise use {@link #getPayloadObject()}.
|
||||||
*
|
*
|
||||||
* @return the payload
|
* @return the payload
|
||||||
*/
|
*/
|
||||||
@@ -68,6 +70,25 @@ public class GHDeployment extends GHObject {
|
|||||||
return (String) payload;
|
return (String) payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets payload. <b>NOTE:</b> only use this method if you can guarantee the payload will be a JSON object (Map),
|
||||||
|
* otherwise use {@link #getPayloadObject()}.
|
||||||
|
*
|
||||||
|
* @return the payload
|
||||||
|
*/
|
||||||
|
public Map<String, Object> getPayloadMap() {
|
||||||
|
return (Map<String, Object>) payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets payload without assuming its type. It could be a String or a Map.
|
||||||
|
*
|
||||||
|
* @return the payload
|
||||||
|
*/
|
||||||
|
public Object getPayloadObject() {
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets environment.
|
* Gets environment.
|
||||||
*
|
*
|
||||||
@@ -122,7 +143,7 @@ public class GHDeployment extends GHObject {
|
|||||||
* @return the gh deployment status builder
|
* @return the gh deployment status builder
|
||||||
*/
|
*/
|
||||||
public GHDeploymentStatusBuilder createStatus(GHDeploymentState state) {
|
public GHDeploymentStatusBuilder createStatus(GHDeploymentState state) {
|
||||||
return new GHDeploymentStatusBuilder(owner, id, state);
|
return new GHDeploymentStatusBuilder(owner, getId(), state);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ public class GHDeploymentStatus extends GHObject {
|
|||||||
* @return the target url
|
* @return the target url
|
||||||
*/
|
*/
|
||||||
public URL getTargetUrl() {
|
public URL getTargetUrl() {
|
||||||
return GitHub.parseURL(target_url);
|
return GitHubClient.parseURL(target_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -46,7 +46,7 @@ public class GHDeploymentStatus extends GHObject {
|
|||||||
* @return the deployment url
|
* @return the deployment url
|
||||||
*/
|
*/
|
||||||
public URL getDeploymentUrl() {
|
public URL getDeploymentUrl() {
|
||||||
return GitHub.parseURL(deployment_url);
|
return GitHubClient.parseURL(deployment_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -55,7 +55,7 @@ public class GHDeploymentStatus extends GHObject {
|
|||||||
* @return the repository url
|
* @return the repository url
|
||||||
*/
|
*/
|
||||||
public URL getRepositoryUrl() {
|
public URL getRepositoryUrl() {
|
||||||
return GitHub.parseURL(repository_url);
|
return GitHubClient.parseURL(repository_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ public class GHDeploymentStatusBuilder {
|
|||||||
* the state
|
* the state
|
||||||
* @deprecated Use {@link GHDeployment#createStatus(GHDeploymentState)}
|
* @deprecated Use {@link GHDeployment#createStatus(GHDeploymentState)}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public GHDeploymentStatusBuilder(GHRepository repo, int deploymentId, GHDeploymentState state) {
|
public GHDeploymentStatusBuilder(GHRepository repo, int deploymentId, GHDeploymentState state) {
|
||||||
this(repo, (long) deploymentId, state);
|
this(repo, (long) deploymentId, state);
|
||||||
}
|
}
|
||||||
|
|||||||
232
src/main/java/org/kohsuke/github/GHDiscussion.java
Normal file
232
src/main/java/org/kohsuke/github/GHDiscussion.java
Normal file
@@ -0,0 +1,232 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JacksonInject;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import javax.annotation.CheckForNull;
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A discussion in GitHub Team.
|
||||||
|
*
|
||||||
|
* @author Charles Moulliard
|
||||||
|
* @see <a href="https://developer.github.com/v3/teams/discussions">GitHub Team Discussions</a>
|
||||||
|
*/
|
||||||
|
public class GHDiscussion extends GHObject {
|
||||||
|
|
||||||
|
@JacksonInject
|
||||||
|
private GitHub root;
|
||||||
|
private GHTeam team;
|
||||||
|
private long number;
|
||||||
|
private String body, title, htmlUrl;
|
||||||
|
|
||||||
|
@JsonProperty(value = "private")
|
||||||
|
private boolean isPrivate;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public URL getHtmlUrl() throws IOException {
|
||||||
|
return GitHubClient.parseURL(htmlUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
GHDiscussion wrapUp(GHTeam team) {
|
||||||
|
this.team = team;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the team to which this discussion belongs.
|
||||||
|
*
|
||||||
|
* @return the team for this discussion
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public GHTeam getTeam() {
|
||||||
|
return team;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the title of the discussion.
|
||||||
|
*
|
||||||
|
* @return the title
|
||||||
|
*/
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The description of this discussion.
|
||||||
|
*
|
||||||
|
* @return the body
|
||||||
|
*/
|
||||||
|
public String getBody() {
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of this discussion.
|
||||||
|
*
|
||||||
|
* @return the number
|
||||||
|
*/
|
||||||
|
public long getNumber() {
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The id number of this discussion. GitHub discussions have "number" instead of "id". This is provided for
|
||||||
|
* convenience.
|
||||||
|
*
|
||||||
|
* @return the id number for this discussion
|
||||||
|
* @see #getNumber()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public long getId() {
|
||||||
|
return getNumber();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the discussion is private to the team.
|
||||||
|
*
|
||||||
|
* @return {@code true} if discussion is private.
|
||||||
|
*/
|
||||||
|
public boolean isPrivate() {
|
||||||
|
return isPrivate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Begins the creation of a new instance.
|
||||||
|
*
|
||||||
|
* Consumer must call {@link GHDiscussion.Creator#done()} to commit changes.
|
||||||
|
*
|
||||||
|
* @param team
|
||||||
|
* the team in which the discussion will be created.
|
||||||
|
* @return a {@link GHLabel.Creator}
|
||||||
|
* @throws IOException
|
||||||
|
* the io exception
|
||||||
|
*/
|
||||||
|
static GHDiscussion.Creator create(GHTeam team) throws IOException {
|
||||||
|
return new GHDiscussion.Creator(team);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GHDiscussion read(GHTeam team, long discussionNumber) throws IOException {
|
||||||
|
return team.root.createRequest()
|
||||||
|
.setRawUrlPath(getRawUrlPath(team, discussionNumber))
|
||||||
|
.fetch(GHDiscussion.class)
|
||||||
|
.wrapUp(team);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PagedIterable<GHDiscussion> readAll(GHTeam team) throws IOException {
|
||||||
|
return team.root.createRequest()
|
||||||
|
.setRawUrlPath(getRawUrlPath(team, null))
|
||||||
|
.toIterable(GHDiscussion[].class, item -> item.wrapUp(team));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Begins a batch update
|
||||||
|
*
|
||||||
|
* Consumer must call {@link GHDiscussion.Updater#done()} to commit changes.
|
||||||
|
*
|
||||||
|
* @return a {@link GHDiscussion.Updater}
|
||||||
|
*/
|
||||||
|
@Preview
|
||||||
|
@Deprecated
|
||||||
|
public GHDiscussion.Updater update() {
|
||||||
|
return new GHDiscussion.Updater(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Begins a single property update.
|
||||||
|
*
|
||||||
|
* @return a {@link GHDiscussion.Setter}
|
||||||
|
*/
|
||||||
|
@Preview
|
||||||
|
@Deprecated
|
||||||
|
public GHDiscussion.Setter set() {
|
||||||
|
return new GHDiscussion.Setter(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the discussion
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
* the io exception
|
||||||
|
*/
|
||||||
|
public void delete() throws IOException {
|
||||||
|
team.root.createRequest().method("DELETE").setRawUrlPath(getRawUrlPath(team, number)).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getRawUrlPath(@Nonnull GHTeam team, @CheckForNull Long discussionNumber) {
|
||||||
|
return team.getUrl().toString() + "/discussions" + (discussionNumber == null ? "" : "/" + discussionNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link GHLabelBuilder} that updates a single property per request
|
||||||
|
*
|
||||||
|
* {@link #done()} is called automatically after the property is set.
|
||||||
|
*/
|
||||||
|
public static class Setter extends GHDiscussionBuilder<GHDiscussion> {
|
||||||
|
private Setter(@Nonnull GHDiscussion base) {
|
||||||
|
super(GHDiscussion.class, base.team, base);
|
||||||
|
requester.method("PATCH").setRawUrlPath(base.getUrl().toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link GHLabelBuilder} that allows multiple properties to be updated per request.
|
||||||
|
*
|
||||||
|
* Consumer must call {@link #done()} to commit changes.
|
||||||
|
*/
|
||||||
|
public static class Updater extends GHDiscussionBuilder<Updater> {
|
||||||
|
private Updater(@Nonnull GHDiscussion base) {
|
||||||
|
super(GHDiscussion.Updater.class, base.team, base);
|
||||||
|
requester.method("PATCH").setRawUrlPath(base.getUrl().toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link GHLabelBuilder} that creates a new {@link GHLabel}
|
||||||
|
*
|
||||||
|
* Consumer must call {@link #done()} to create the new instance.
|
||||||
|
*/
|
||||||
|
public static class Creator extends GHDiscussionBuilder<Creator> {
|
||||||
|
|
||||||
|
private Creator(@Nonnull GHTeam team) {
|
||||||
|
super(GHDiscussion.Creator.class, team, null);
|
||||||
|
requester.method("POST").setRawUrlPath(getRawUrlPath(team, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether this discussion is private to this team.
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* privacy of this discussion
|
||||||
|
* @return either a continuing builder or an updated {@link GHDiscussion}
|
||||||
|
* @throws IOException
|
||||||
|
* if there is an I/O Exception
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public Creator private_(boolean value) throws IOException {
|
||||||
|
return with("private", value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
GHDiscussion that = (GHDiscussion) o;
|
||||||
|
return number == that.number && Objects.equals(getUrl(), that.getUrl()) && Objects.equals(team, that.team)
|
||||||
|
&& Objects.equals(body, that.body) && Objects.equals(title, that.title);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(team, number, body, title);
|
||||||
|
}
|
||||||
|
}
|
||||||
80
src/main/java/org/kohsuke/github/GHDiscussionBuilder.java
Normal file
80
src/main/java/org/kohsuke/github/GHDiscussionBuilder.java
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.annotation.CheckForNull;
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for creating or updating a discussion.
|
||||||
|
*
|
||||||
|
* @param <S>
|
||||||
|
* Intermediate return type for this builder returned by calls to {@link #with(String, Object)}. If {@link S}
|
||||||
|
* the same as {@link GHLabel}, this builder will commit changes after each call to
|
||||||
|
* {@link #with(String, Object)}.
|
||||||
|
*/
|
||||||
|
class GHDiscussionBuilder<S> extends AbstractBuilder<GHDiscussion, S> {
|
||||||
|
|
||||||
|
private final GHTeam team;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param intermediateReturnType
|
||||||
|
* Intermediate return type for this builder returned by calls to {@link #with(String, Object)}. If
|
||||||
|
* {@link S} the same as {@link GHDiscussion}, this builder will commit changes after each call to
|
||||||
|
* {@link #with(String, Object)}.
|
||||||
|
* @param team
|
||||||
|
* the GitHub team. Updates will be sent to the root of this team.
|
||||||
|
* @param baseInstance
|
||||||
|
* instance on which to base this builder. If {@code null} a new instance will be created.
|
||||||
|
*/
|
||||||
|
protected GHDiscussionBuilder(@Nonnull Class<S> intermediateReturnType,
|
||||||
|
@Nonnull GHTeam team,
|
||||||
|
@CheckForNull GHDiscussion baseInstance) {
|
||||||
|
super(GHDiscussion.class, intermediateReturnType, team.root, baseInstance);
|
||||||
|
|
||||||
|
this.team = team;
|
||||||
|
|
||||||
|
if (baseInstance != null) {
|
||||||
|
requester.with("title", baseInstance.getTitle());
|
||||||
|
requester.with("body", baseInstance.getBody());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Title for this discussion.
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* title of discussion
|
||||||
|
* @return either a continuing builder or an updated {@link GHDiscussion}
|
||||||
|
* @throws IOException
|
||||||
|
* if there is an I/O Exception
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public S title(String value) throws IOException {
|
||||||
|
return with("title", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Body content for this discussion.
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* body of discussion*
|
||||||
|
* @return either a continuing builder or an updated {@link GHDiscussion}
|
||||||
|
* @throws IOException
|
||||||
|
* if there is an I/O Exception
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public S body(String value) throws IOException {
|
||||||
|
return with("body", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public GHDiscussion done() throws IOException {
|
||||||
|
return super.done().wrapUp(team);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -50,6 +50,7 @@ public enum GHEvent {
|
|||||||
PULL_REQUEST_REVIEW,
|
PULL_REQUEST_REVIEW,
|
||||||
PULL_REQUEST_REVIEW_COMMENT,
|
PULL_REQUEST_REVIEW_COMMENT,
|
||||||
PUSH,
|
PUSH,
|
||||||
|
REGISTRY_PACKAGE,
|
||||||
RELEASE,
|
RELEASE,
|
||||||
REPOSITORY_DISPATCH, // only valid for org hooks
|
REPOSITORY_DISPATCH, // only valid for org hooks
|
||||||
REPOSITORY,
|
REPOSITORY,
|
||||||
@@ -61,6 +62,8 @@ public enum GHEvent {
|
|||||||
TEAM,
|
TEAM,
|
||||||
TEAM_ADD,
|
TEAM_ADD,
|
||||||
WATCH,
|
WATCH,
|
||||||
|
WORKFLOW_DISPATCH,
|
||||||
|
WORKFLOW_RUN,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Special event type that means "every possible event"
|
* Special event type that means "every possible event"
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ public class GHEventInfo {
|
|||||||
* @return the created at
|
* @return the created at
|
||||||
*/
|
*/
|
||||||
public Date getCreatedAt() {
|
public Date getCreatedAt() {
|
||||||
return GitHub.parseDate(created_at);
|
return GitHubClient.parseDate(created_at);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -144,7 +144,7 @@ public class GHEventInfo {
|
|||||||
* if payload cannot be parsed
|
* if payload cannot be parsed
|
||||||
*/
|
*/
|
||||||
public <T extends GHEventPayload> T getPayload(Class<T> type) throws IOException {
|
public <T extends GHEventPayload> T getPayload(Class<T> type) throws IOException {
|
||||||
T v = GitHub.MAPPER.readValue(payload.traverse(), type);
|
T v = GitHubClient.getMappingObjectReader(root).readValue(payload.traverse(), type);
|
||||||
v.wrapUp(root);
|
v.wrapUp(root);
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,11 +1,11 @@
|
|||||||
package org.kohsuke.github;
|
package org.kohsuke.github;
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.net.HttpURLConnection;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.annotation.CheckForNull;
|
import javax.annotation.CheckForNull;
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request/responce contains useful metadata. Custom exception allows store info for next diagnostics.
|
* Request/responce contains useful metadata. Custom exception allows store info for next diagnostics.
|
||||||
@@ -24,11 +24,24 @@ public class GHFileNotFoundException extends FileNotFoundException {
|
|||||||
/**
|
/**
|
||||||
* Instantiates a new Gh file not found exception.
|
* Instantiates a new Gh file not found exception.
|
||||||
*
|
*
|
||||||
* @param s
|
* @param message
|
||||||
* the s
|
* the message
|
||||||
*/
|
*/
|
||||||
public GHFileNotFoundException(String s) {
|
public GHFileNotFoundException(String message) {
|
||||||
super(s);
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates a new Gh file not found exception.
|
||||||
|
*
|
||||||
|
* @param message
|
||||||
|
* the message
|
||||||
|
* @param cause
|
||||||
|
* the cause
|
||||||
|
*/
|
||||||
|
public GHFileNotFoundException(String message, Throwable cause) {
|
||||||
|
super(message);
|
||||||
|
this.initCause(cause);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -41,8 +54,8 @@ public class GHFileNotFoundException extends FileNotFoundException {
|
|||||||
return responseHeaderFields;
|
return responseHeaderFields;
|
||||||
}
|
}
|
||||||
|
|
||||||
GHFileNotFoundException withResponseHeaderFields(HttpURLConnection urlConnection) {
|
GHFileNotFoundException withResponseHeaderFields(@Nonnull Map<String, List<String>> headerFields) {
|
||||||
this.responseHeaderFields = urlConnection.getHeaderFields();
|
this.responseHeaderFields = headerFields;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
package org.kohsuke.github;
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JacksonInject;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
@@ -20,8 +21,9 @@ import java.util.Map.Entry;
|
|||||||
* @see <a href="https://developer.github.com/v3/gists/">documentation</a>
|
* @see <a href="https://developer.github.com/v3/gists/">documentation</a>
|
||||||
*/
|
*/
|
||||||
public class GHGist extends GHObject {
|
public class GHGist extends GHObject {
|
||||||
/* package almost final */ GHUser owner;
|
|
||||||
/* package almost final */ GitHub root;
|
final GHUser owner;
|
||||||
|
final GitHub root;
|
||||||
|
|
||||||
private String forks_url, commits_url, id, git_pull_url, git_push_url, html_url;
|
private String forks_url, commits_url, id, git_pull_url, git_push_url, html_url;
|
||||||
|
|
||||||
@@ -34,7 +36,43 @@ public class GHGist extends GHObject {
|
|||||||
|
|
||||||
private String comments_url;
|
private String comments_url;
|
||||||
|
|
||||||
private Map<String, GHGistFile> files = new HashMap<String, GHGistFile>();
|
private final Map<String, GHGistFile> files;
|
||||||
|
|
||||||
|
@JsonCreator
|
||||||
|
private GHGist(@JacksonInject GitHub root,
|
||||||
|
@JsonProperty("owner") GHUser owner,
|
||||||
|
@JsonProperty("files") Map<String, GHGistFile> files) {
|
||||||
|
this.root = root;
|
||||||
|
for (Entry<String, GHGistFile> e : files.entrySet()) {
|
||||||
|
e.getValue().fileName = e.getKey();
|
||||||
|
}
|
||||||
|
this.files = Collections.unmodifiableMap(files);
|
||||||
|
this.owner = root.getUser(owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unlike most other GitHub objects, the id for Gists can be non-numeric, such as "aa5a315d61ae9438b18d". If the id
|
||||||
|
* is numeric, this method will get it. If id is not numeric, this will throw a runtime
|
||||||
|
* {@link NumberFormatException}.
|
||||||
|
*
|
||||||
|
* @return id of the Gist.
|
||||||
|
* @deprecated Use {@link #getGistId()} instead.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
@Override
|
||||||
|
public long getId() {
|
||||||
|
return Long.parseLong(getGistId());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the id for this Gist. Unlike most other GitHub objects, the id for Gists can be non-numeric, such as
|
||||||
|
* "aa5a315d61ae9438b18d". This should be used instead of {@link #getId()}.
|
||||||
|
*
|
||||||
|
* @return id of this Gist
|
||||||
|
*/
|
||||||
|
public String getGistId() {
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets owner.
|
* Gets owner.
|
||||||
@@ -44,7 +82,7 @@ public class GHGist extends GHObject {
|
|||||||
* the io exception
|
* the io exception
|
||||||
*/
|
*/
|
||||||
public GHUser getOwner() throws IOException {
|
public GHUser getOwner() throws IOException {
|
||||||
return root.intern(owner);
|
return owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -83,8 +121,13 @@ public class GHGist extends GHObject {
|
|||||||
return git_push_url;
|
return git_push_url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the html url.
|
||||||
|
*
|
||||||
|
* @return the github html url
|
||||||
|
*/
|
||||||
public URL getHtmlUrl() {
|
public URL getHtmlUrl() {
|
||||||
return GitHub.parseURL(html_url);
|
return GitHubClient.parseURL(html_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -140,31 +183,7 @@ public class GHGist extends GHObject {
|
|||||||
* @return the files
|
* @return the files
|
||||||
*/
|
*/
|
||||||
public Map<String, GHGistFile> getFiles() {
|
public Map<String, GHGistFile> getFiles() {
|
||||||
return Collections.unmodifiableMap(files);
|
return files;
|
||||||
}
|
|
||||||
|
|
||||||
GHGist wrapUp(GHUser owner) {
|
|
||||||
this.owner = owner;
|
|
||||||
this.root = owner.root;
|
|
||||||
wrapUp();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used when caller obtains {@link GHGist} without knowing its owner. A partially constructed owner object is
|
|
||||||
* interned.
|
|
||||||
*/
|
|
||||||
GHGist wrapUp(GitHub root) {
|
|
||||||
this.owner = root.getUser(owner);
|
|
||||||
this.root = root;
|
|
||||||
wrapUp();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void wrapUp() {
|
|
||||||
for (Entry<String, GHGistFile> e : files.entrySet()) {
|
|
||||||
e.getValue().fileName = e.getKey();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String getApiTailUrl(String tail) {
|
String getApiTailUrl(String tail) {
|
||||||
@@ -214,7 +233,7 @@ public class GHGist extends GHObject {
|
|||||||
* the io exception
|
* the io exception
|
||||||
*/
|
*/
|
||||||
public GHGist fork() throws IOException {
|
public GHGist fork() throws IOException {
|
||||||
return root.createRequest().method("POST").withUrlPath(getApiTailUrl("forks")).fetch(GHGist.class).wrapUp(root);
|
return root.createRequest().method("POST").withUrlPath(getApiTailUrl("forks")).fetch(GHGist.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -223,9 +242,7 @@ public class GHGist extends GHObject {
|
|||||||
* @return the paged iterable
|
* @return the paged iterable
|
||||||
*/
|
*/
|
||||||
public PagedIterable<GHGist> listForks() {
|
public PagedIterable<GHGist> listForks() {
|
||||||
return root.createRequest()
|
return root.createRequest().withUrlPath(getApiTailUrl("forks")).toIterable(GHGist[].class, null);
|
||||||
.withUrlPath(getApiTailUrl("forks"))
|
|
||||||
.toIterable(GHGist[].class, item -> item.wrapUp(root));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -264,10 +281,4 @@ public class GHGist extends GHObject {
|
|||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return id.hashCode();
|
return id.hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
GHGist wrap(GHUser owner) {
|
|
||||||
this.owner = owner;
|
|
||||||
this.root = owner.root;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import java.io.IOException;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builder pattern for creating a new Gist.
|
* Builder pattern for creating a new Gist.
|
||||||
*
|
*
|
||||||
@@ -59,7 +61,7 @@ public class GHGistBuilder {
|
|||||||
* the content
|
* the content
|
||||||
* @return Adds a new file.
|
* @return Adds a new file.
|
||||||
*/
|
*/
|
||||||
public GHGistBuilder file(String fileName, String content) {
|
public GHGistBuilder file(@Nonnull String fileName, @Nonnull String content) {
|
||||||
files.put(fileName, Collections.singletonMap("content", content));
|
files.put(fileName, Collections.singletonMap("content", content));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -73,6 +75,6 @@ public class GHGistBuilder {
|
|||||||
*/
|
*/
|
||||||
public GHGist create() throws IOException {
|
public GHGist create() throws IOException {
|
||||||
req.with("files", files);
|
req.with("files", files);
|
||||||
return req.withUrlPath("/gists").fetch(GHGist.class).wrapUp(root);
|
return req.withUrlPath("/gists").fetch(GHGist.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
package org.kohsuke.github;
|
package org.kohsuke.github;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collections;
|
import java.util.HashMap;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builder pattern for updating a Gist.
|
* Builder pattern for updating a Gist.
|
||||||
@@ -12,7 +15,7 @@ import java.util.LinkedHashMap;
|
|||||||
public class GHGistUpdater {
|
public class GHGistUpdater {
|
||||||
private final GHGist base;
|
private final GHGist base;
|
||||||
private final Requester builder;
|
private final Requester builder;
|
||||||
LinkedHashMap<String, Object> files;
|
LinkedHashMap<String, Map<String, String>> files;
|
||||||
|
|
||||||
GHGistUpdater(GHGist base) {
|
GHGistUpdater(GHGist base) {
|
||||||
this.base = base;
|
this.base = base;
|
||||||
@@ -32,16 +35,15 @@ public class GHGistUpdater {
|
|||||||
* @throws IOException
|
* @throws IOException
|
||||||
* the io exception
|
* the io exception
|
||||||
*/
|
*/
|
||||||
public GHGistUpdater addFile(String fileName, String content) throws IOException {
|
public GHGistUpdater addFile(@Nonnull String fileName, @Nonnull String content) throws IOException {
|
||||||
updateFile(fileName, content);
|
updateFile(fileName, content);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// // This method does not work.
|
public GHGistUpdater deleteFile(@Nonnull String fileName) throws IOException {
|
||||||
// public GHGistUpdater deleteFile(String fileName) throws IOException {
|
files.put(fileName, null);
|
||||||
// files.put(fileName, Collections.singletonMap("filename", null));
|
return this;
|
||||||
// return this;
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rename file gh gist updater.
|
* Rename file gh gist updater.
|
||||||
@@ -54,8 +56,9 @@ public class GHGistUpdater {
|
|||||||
* @throws IOException
|
* @throws IOException
|
||||||
* the io exception
|
* the io exception
|
||||||
*/
|
*/
|
||||||
public GHGistUpdater renameFile(String fileName, String newFileName) throws IOException {
|
public GHGistUpdater renameFile(@Nonnull String fileName, @Nonnull String newFileName) throws IOException {
|
||||||
files.put(fileName, Collections.singletonMap("filename", newFileName));
|
Map<String, String> file = files.computeIfAbsent(fileName, d -> new HashMap<>());
|
||||||
|
file.put("filename", newFileName);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,8 +73,31 @@ public class GHGistUpdater {
|
|||||||
* @throws IOException
|
* @throws IOException
|
||||||
* the io exception
|
* the io exception
|
||||||
*/
|
*/
|
||||||
public GHGistUpdater updateFile(String fileName, String content) throws IOException {
|
public GHGistUpdater updateFile(@Nonnull String fileName, @Nonnull String content) throws IOException {
|
||||||
files.put(fileName, Collections.singletonMap("content", content));
|
Map<String, String> file = files.computeIfAbsent(fileName, d -> new HashMap<>());
|
||||||
|
file.put("content", content);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update file name and content
|
||||||
|
*
|
||||||
|
* @param fileName
|
||||||
|
* the file name
|
||||||
|
* @param newFileName
|
||||||
|
* the new file name
|
||||||
|
* @param content
|
||||||
|
* the content
|
||||||
|
* @return the gh gist updater
|
||||||
|
* @throws IOException
|
||||||
|
* the io exception
|
||||||
|
*/
|
||||||
|
public GHGistUpdater updateFile(@Nonnull String fileName, @Nonnull String newFileName, @Nonnull String content)
|
||||||
|
throws IOException {
|
||||||
|
Map<String, String> file = files.computeIfAbsent(fileName, d -> new HashMap<>());
|
||||||
|
file.put("content", content);
|
||||||
|
file.put("filename", newFileName);
|
||||||
|
files.put(fileName, file);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,6 +122,6 @@ public class GHGistUpdater {
|
|||||||
*/
|
*/
|
||||||
public GHGist update() throws IOException {
|
public GHGist update() throws IOException {
|
||||||
builder.with("files", files);
|
builder.with("files", files);
|
||||||
return builder.method("PATCH").withUrlPath(base.getApiTailUrl("")).fetch(GHGist.class).wrap(base.owner);
|
return builder.method("PATCH").withUrlPath(base.getApiTailUrl("")).fetch(GHGist.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
package org.kohsuke.github;
|
package org.kohsuke.github;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.HttpURLConnection;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.annotation.CheckForNull;
|
import javax.annotation.CheckForNull;
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request/responce contains useful metadata. Custom exception allows store info for next diagnostics.
|
* Request/responce contains useful metadata. Custom exception allows store info for next diagnostics.
|
||||||
@@ -31,6 +31,20 @@ public class GHIOException extends IOException {
|
|||||||
super(message);
|
super(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a {@code GHIOException} with the specified detail message and cause.
|
||||||
|
*
|
||||||
|
* @param message
|
||||||
|
* The detail message (which is saved for later retrieval by the {@link #getMessage()} method)
|
||||||
|
*
|
||||||
|
* @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.)
|
||||||
|
*/
|
||||||
|
public GHIOException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets response header fields.
|
* Gets response header fields.
|
||||||
*
|
*
|
||||||
@@ -41,8 +55,8 @@ public class GHIOException extends IOException {
|
|||||||
return responseHeaderFields;
|
return responseHeaderFields;
|
||||||
}
|
}
|
||||||
|
|
||||||
GHIOException withResponseHeaderFields(HttpURLConnection urlConnection) {
|
GHIOException withResponseHeaderFields(@Nonnull Map<String, List<String>> headerFields) {
|
||||||
this.responseHeaderFields = urlConnection.getHeaderFields();
|
this.responseHeaderFields = headerFields;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,6 +51,6 @@ public class GHInvitation extends GHObject {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public URL getHtmlUrl() {
|
public URL getHtmlUrl() {
|
||||||
return GitHub.parseURL(html_url);
|
return GitHubClient.parseURL(html_url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ package org.kohsuke.github;
|
|||||||
|
|
||||||
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
|
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
|
||||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
@@ -36,6 +37,7 @@ import java.util.Collections;
|
|||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import static org.kohsuke.github.Previews.SQUIRREL_GIRL;
|
import static org.kohsuke.github.Previews.SQUIRREL_GIRL;
|
||||||
|
|
||||||
@@ -63,8 +65,7 @@ public class GHIssue extends GHObject implements Reactable {
|
|||||||
protected int comments;
|
protected int comments;
|
||||||
@SkipFromToString
|
@SkipFromToString
|
||||||
protected String body;
|
protected String body;
|
||||||
// for backward compatibility with < 1.63, this collection needs to hold instances of Label, not GHLabel
|
protected List<GHLabel> labels;
|
||||||
protected List<Label> labels;
|
|
||||||
protected GHUser user;
|
protected GHUser user;
|
||||||
protected String title, html_url;
|
protected String title, html_url;
|
||||||
protected GHIssue.PullRequest pull_request;
|
protected GHIssue.PullRequest pull_request;
|
||||||
@@ -72,14 +73,6 @@ public class GHIssue extends GHObject implements Reactable {
|
|||||||
protected GHUser closed_by;
|
protected GHUser closed_by;
|
||||||
protected boolean locked;
|
protected boolean locked;
|
||||||
|
|
||||||
/**
|
|
||||||
* The type Label.
|
|
||||||
*
|
|
||||||
* @deprecated use {@link GHLabel}
|
|
||||||
*/
|
|
||||||
public static class Label extends GHLabel {
|
|
||||||
}
|
|
||||||
|
|
||||||
GHIssue wrap(GHRepository owner) {
|
GHIssue wrap(GHRepository owner) {
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
if (milestone != null)
|
if (milestone != null)
|
||||||
@@ -100,12 +93,6 @@ public class GHIssue extends GHObject implements Reactable {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GHIssue[] wrap(GHIssue[] issues, GHRepository owner) {
|
|
||||||
for (GHIssue i : issues)
|
|
||||||
i.wrap(owner);
|
|
||||||
return issues;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Repository to which the issue belongs.
|
* Repository to which the issue belongs.
|
||||||
*
|
*
|
||||||
@@ -137,7 +124,7 @@ public class GHIssue extends GHObject implements Reactable {
|
|||||||
* The HTML page of this issue, like https://github.com/jenkinsci/jenkins/issues/100
|
* The HTML page of this issue, like https://github.com/jenkinsci/jenkins/issues/100
|
||||||
*/
|
*/
|
||||||
public URL getHtmlUrl() {
|
public URL getHtmlUrl() {
|
||||||
return GitHub.parseURL(html_url);
|
return GitHubClient.parseURL(html_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -171,14 +158,12 @@ public class GHIssue extends GHObject implements Reactable {
|
|||||||
* Gets labels.
|
* Gets labels.
|
||||||
*
|
*
|
||||||
* @return the labels
|
* @return the labels
|
||||||
* @throws IOException
|
|
||||||
* the io exception
|
|
||||||
*/
|
*/
|
||||||
public Collection<GHLabel> getLabels() throws IOException {
|
public Collection<GHLabel> getLabels() {
|
||||||
if (labels == null) {
|
if (labels == null) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
return Collections.<GHLabel>unmodifiableList(labels);
|
return Collections.unmodifiableList(labels);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -187,16 +172,18 @@ public class GHIssue extends GHObject implements Reactable {
|
|||||||
* @return the closed at
|
* @return the closed at
|
||||||
*/
|
*/
|
||||||
public Date getClosedAt() {
|
public Date getClosedAt() {
|
||||||
return GitHub.parseDate(closed_at);
|
return GitHubClient.parseDate(closed_at);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets api url.
|
* Gets api url.
|
||||||
*
|
*
|
||||||
* @return the api url
|
* @return API URL of this object.
|
||||||
|
* @deprecated use {@link #getUrl()}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public URL getApiURL() {
|
public URL getApiURL() {
|
||||||
return GitHub.parseURL(url);
|
return getUrl();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -242,8 +229,15 @@ public class GHIssue extends GHObject implements Reactable {
|
|||||||
root.createRequest().with(key, value).method("PATCH").withUrlPath(getApiRoute()).send();
|
root.createRequest().with(key, value).method("PATCH").withUrlPath(getApiRoute()).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identical to edit(), but allows null for the value.
|
||||||
|
*/
|
||||||
|
private void editNullable(String key, Object value) throws IOException {
|
||||||
|
root.createRequest().withNullable(key, value).method("PATCH").withUrlPath(getApiRoute()).send();
|
||||||
|
}
|
||||||
|
|
||||||
private void editIssue(String key, Object value) throws IOException {
|
private void editIssue(String key, Object value) throws IOException {
|
||||||
root.createRequest().with(key, value).method("PATCH").withUrlPath(getIssuesApiRoute()).send();
|
root.createRequest().withNullable(key, value).method("PATCH").withUrlPath(getIssuesApiRoute()).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -291,15 +285,19 @@ public class GHIssue extends GHObject implements Reactable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets milestone.
|
* Sets the milestone for this issue.
|
||||||
*
|
*
|
||||||
* @param milestone
|
* @param milestone
|
||||||
* the milestone
|
* The milestone to assign this issue to. Use null to remove the milestone for this issue.
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
* the io exception
|
* The io exception
|
||||||
*/
|
*/
|
||||||
public void setMilestone(GHMilestone milestone) throws IOException {
|
public void setMilestone(GHMilestone milestone) throws IOException {
|
||||||
edit("milestone", milestone.getNumber());
|
if (milestone == null) {
|
||||||
|
editIssue("milestone", null);
|
||||||
|
} else {
|
||||||
|
editIssue("milestone", milestone.getNumber());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -434,7 +432,7 @@ public class GHIssue extends GHObject implements Reactable {
|
|||||||
* @see #listComments() #listComments()
|
* @see #listComments() #listComments()
|
||||||
*/
|
*/
|
||||||
public List<GHIssueComment> getComments() throws IOException {
|
public List<GHIssueComment> getComments() throws IOException {
|
||||||
return listComments().asList();
|
return listComments().toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -453,7 +451,7 @@ public class GHIssue extends GHObject implements Reactable {
|
|||||||
@Preview
|
@Preview
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public GHReaction createReaction(ReactionContent content) throws IOException {
|
public GHReaction createReaction(ReactionContent content) throws IOException {
|
||||||
return owner.root.createRequest()
|
return root.createRequest()
|
||||||
.method("POST")
|
.method("POST")
|
||||||
.withPreview(SQUIRREL_GIRL)
|
.withPreview(SQUIRREL_GIRL)
|
||||||
.with("content", content.getContent())
|
.with("content", content.getContent())
|
||||||
@@ -465,10 +463,10 @@ public class GHIssue extends GHObject implements Reactable {
|
|||||||
@Preview
|
@Preview
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public PagedIterable<GHReaction> listReactions() {
|
public PagedIterable<GHReaction> listReactions() {
|
||||||
return owner.root.createRequest()
|
return root.createRequest()
|
||||||
.withPreview(SQUIRREL_GIRL)
|
.withPreview(SQUIRREL_GIRL)
|
||||||
.withUrlPath(getApiRoute() + "/reactions")
|
.withUrlPath(getApiRoute() + "/reactions")
|
||||||
.toIterable(GHReaction[].class, item -> item.wrap(owner.root));
|
.toIterable(GHReaction[].class, item -> item.wrap(root));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -571,6 +569,11 @@ public class GHIssue extends GHObject implements Reactable {
|
|||||||
* @return the issues api route
|
* @return the issues api route
|
||||||
*/
|
*/
|
||||||
protected String getIssuesApiRoute() {
|
protected String getIssuesApiRoute() {
|
||||||
|
if (owner == null) {
|
||||||
|
// Issues returned from search to do not have an owner. Attempt to use url.
|
||||||
|
final URL url = Objects.requireNonNull(getUrl(), "Missing instance URL!");
|
||||||
|
return StringUtils.prependIfMissing(url.toString().replace(root.getApiUrl(), ""), "/");
|
||||||
|
}
|
||||||
return "/repos/" + owner.getOwnerName() + "/" + owner.getName() + "/issues/" + number;
|
return "/repos/" + owner.getOwnerName() + "/" + owner.getName() + "/issues/" + number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -677,7 +680,7 @@ public class GHIssue extends GHObject implements Reactable {
|
|||||||
* @return the diff url
|
* @return the diff url
|
||||||
*/
|
*/
|
||||||
public URL getDiffUrl() {
|
public URL getDiffUrl() {
|
||||||
return GitHub.parseURL(diff_url);
|
return GitHubClient.parseURL(diff_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -686,7 +689,7 @@ public class GHIssue extends GHObject implements Reactable {
|
|||||||
* @return the patch url
|
* @return the patch url
|
||||||
*/
|
*/
|
||||||
public URL getPatchUrl() {
|
public URL getPatchUrl() {
|
||||||
return GitHub.parseURL(patch_url);
|
return GitHubClient.parseURL(patch_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -695,7 +698,7 @@ public class GHIssue extends GHObject implements Reactable {
|
|||||||
* @return the url
|
* @return the url
|
||||||
*/
|
*/
|
||||||
public URL getUrl() {
|
public URL getUrl() {
|
||||||
return GitHub.parseURL(html_url);
|
return GitHubClient.parseURL(html_url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ public class GHIssueComment extends GHObject implements Reactable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public URL getHtmlUrl() {
|
public URL getHtmlUrl() {
|
||||||
return GitHub.parseURL(html_url);
|
return GitHubClient.parseURL(html_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -149,6 +149,6 @@ public class GHIssueComment extends GHObject implements Reactable {
|
|||||||
|
|
||||||
private String getApiRoute() {
|
private String getApiRoute() {
|
||||||
return "/repos/" + owner.getRepository().getOwnerName() + "/" + owner.getRepository().getName()
|
return "/repos/" + owner.getRepository().getOwnerName() + "/" + owner.getRepository().getName()
|
||||||
+ "/issues/comments/" + id;
|
+ "/issues/comments/" + getId();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import java.util.Date;
|
|||||||
/**
|
/**
|
||||||
* The type GHIssueEvent.
|
* The type GHIssueEvent.
|
||||||
*
|
*
|
||||||
|
* @see <a href="https://developer.github.com/v3/issues/events/">Github documentation for issue events</a>
|
||||||
|
*
|
||||||
* @author Martin van Zijl
|
* @author Martin van Zijl
|
||||||
*/
|
*/
|
||||||
public class GHIssueEvent {
|
public class GHIssueEvent {
|
||||||
@@ -18,6 +20,9 @@ public class GHIssueEvent {
|
|||||||
private String commit_id;
|
private String commit_id;
|
||||||
private String commit_url;
|
private String commit_url;
|
||||||
private String created_at;
|
private String created_at;
|
||||||
|
private GHMilestone milestone;
|
||||||
|
private GHLabel label;
|
||||||
|
private GHUser assignee;
|
||||||
|
|
||||||
private GHIssue issue;
|
private GHIssue issue;
|
||||||
|
|
||||||
@@ -90,7 +95,7 @@ public class GHIssueEvent {
|
|||||||
* @return the created at
|
* @return the created at
|
||||||
*/
|
*/
|
||||||
public Date getCreatedAt() {
|
public Date getCreatedAt() {
|
||||||
return GitHub.parseDate(created_at);
|
return GitHubClient.parseDate(created_at);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -111,6 +116,36 @@ public class GHIssueEvent {
|
|||||||
return issue;
|
return issue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the {@link GHMilestone} that this issue was added to or removed from. Only present for events "milestoned"
|
||||||
|
* and "demilestoned", <code>null</code> otherwise.
|
||||||
|
*
|
||||||
|
* @return the milestone
|
||||||
|
*/
|
||||||
|
public GHMilestone getMilestone() {
|
||||||
|
return milestone;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the {@link GHLabel} that was added to or removed from the issue. Only present for events "labeled" and
|
||||||
|
* "unlabeled", <code>null</code> otherwise.
|
||||||
|
*
|
||||||
|
* @return the label
|
||||||
|
*/
|
||||||
|
public GHLabel getLabel() {
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the {@link GHUser} that was assigned or unassigned from the issue. Only present for events "assigned" and
|
||||||
|
* "unassigned", <code>null</code> otherwise.
|
||||||
|
*
|
||||||
|
* @return the user
|
||||||
|
*/
|
||||||
|
public GHUser getAssignee() {
|
||||||
|
return assignee;
|
||||||
|
}
|
||||||
|
|
||||||
GHIssueEvent wrapUp(GitHub root) {
|
GHIssueEvent wrapUp(GitHub root) {
|
||||||
this.root = root;
|
this.root = root;
|
||||||
return this;
|
return this;
|
||||||
|
|||||||
@@ -1,27 +1,56 @@
|
|||||||
package org.kohsuke.github;
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JacksonInject;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import javax.annotation.CheckForNull;
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The type GHLabel.
|
* The type GHLabel.
|
||||||
*
|
*
|
||||||
* @author Kohsuke Kawaguchi
|
* @author Kohsuke Kawaguchi
|
||||||
|
* @see <a href="https://developer.github.com/v3/issues/labels/">Labels</a>
|
||||||
* @see GHIssue#getLabels() GHIssue#getLabels()
|
* @see GHIssue#getLabels() GHIssue#getLabels()
|
||||||
* @see GHRepository#listLabels() GHRepository#listLabels()
|
* @see GHRepository#listLabels() GHRepository#listLabels()
|
||||||
*/
|
*/
|
||||||
public class GHLabel {
|
public class GHLabel {
|
||||||
private String url, name, color, description;
|
|
||||||
private GHRepository repo;
|
@Nonnull
|
||||||
|
private String url, name, color;
|
||||||
|
|
||||||
|
@CheckForNull
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
private final GitHub root;
|
||||||
|
|
||||||
|
@JsonCreator
|
||||||
|
private GHLabel(@JacksonInject @Nonnull GitHub root) {
|
||||||
|
this.root = root;
|
||||||
|
url = "";
|
||||||
|
name = "";
|
||||||
|
color = "";
|
||||||
|
description = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
GitHub getApiRoot() {
|
||||||
|
return Objects.requireNonNull(root);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets url.
|
* Gets url.
|
||||||
*
|
*
|
||||||
* @return the url
|
* @return the url
|
||||||
*/
|
*/
|
||||||
|
@Nonnull
|
||||||
public String getUrl() {
|
public String getUrl() {
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
@@ -31,6 +60,7 @@ public class GHLabel {
|
|||||||
*
|
*
|
||||||
* @return the name
|
* @return the name
|
||||||
*/
|
*/
|
||||||
|
@Nonnull
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
@@ -40,6 +70,7 @@ public class GHLabel {
|
|||||||
*
|
*
|
||||||
* @return the color
|
* @return the color
|
||||||
*/
|
*/
|
||||||
|
@Nonnull
|
||||||
public String getColor() {
|
public String getColor() {
|
||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
@@ -49,25 +80,11 @@ public class GHLabel {
|
|||||||
*
|
*
|
||||||
* @return the description
|
* @return the description
|
||||||
*/
|
*/
|
||||||
|
@CheckForNull
|
||||||
public String getDescription() {
|
public String getDescription() {
|
||||||
return description;
|
return description;
|
||||||
}
|
}
|
||||||
|
|
||||||
GHLabel wrapUp(GHRepository repo) {
|
|
||||||
this.repo = repo;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete.
|
|
||||||
*
|
|
||||||
* @throws IOException
|
|
||||||
* the io exception
|
|
||||||
*/
|
|
||||||
public void delete() throws IOException {
|
|
||||||
repo.root.createRequest().method("DELETE").setRawUrlPath(url).send();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets color.
|
* Sets color.
|
||||||
*
|
*
|
||||||
@@ -75,15 +92,11 @@ public class GHLabel {
|
|||||||
* 6-letter hex color code, like "f29513"
|
* 6-letter hex color code, like "f29513"
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
* the io exception
|
* the io exception
|
||||||
|
* @deprecated use {@link #set()} or {@link #update()} instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void setColor(String newColor) throws IOException {
|
public void setColor(String newColor) throws IOException {
|
||||||
repo.root.createRequest()
|
set().color(newColor);
|
||||||
.method("PATCH")
|
|
||||||
.with("name", name)
|
|
||||||
.with("color", newColor)
|
|
||||||
.with("description", description)
|
|
||||||
.setRawUrlPath(url)
|
|
||||||
.send();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -93,25 +106,106 @@ public class GHLabel {
|
|||||||
* Description of label
|
* Description of label
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
* the io exception
|
* the io exception
|
||||||
|
* @deprecated use {@link #set()} or {@link #update()} instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void setDescription(String newDescription) throws IOException {
|
public void setDescription(String newDescription) throws IOException {
|
||||||
repo.root.createRequest()
|
set().description(newDescription);
|
||||||
.method("PATCH")
|
|
||||||
.with("name", name)
|
|
||||||
.with("color", color)
|
|
||||||
.with("description", newDescription)
|
|
||||||
.setRawUrlPath(url)
|
|
||||||
.send();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Collection<String> toNames(Collection<GHLabel> labels) {
|
static Collection<String> toNames(Collection<GHLabel> labels) {
|
||||||
List<String> r = new ArrayList<String>();
|
List<String> r = new ArrayList<>();
|
||||||
for (GHLabel l : labels) {
|
for (GHLabel l : labels) {
|
||||||
r.add(l.getName());
|
r.add(l.getName());
|
||||||
}
|
}
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Begins the creation of a new instance.
|
||||||
|
*
|
||||||
|
* Consumer must call {@link Creator#done()} to commit changes.
|
||||||
|
*
|
||||||
|
* @param repository
|
||||||
|
* the repository in which the label will be created.
|
||||||
|
* @return a {@link Creator}
|
||||||
|
* @throws IOException
|
||||||
|
* the io exception
|
||||||
|
*/
|
||||||
|
@Preview
|
||||||
|
@Deprecated
|
||||||
|
static Creator create(GHRepository repository) throws IOException {
|
||||||
|
return new Creator(repository);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads a label from a repository.
|
||||||
|
*
|
||||||
|
* @param repository
|
||||||
|
* the repository to read from
|
||||||
|
* @param name
|
||||||
|
* the name of the label
|
||||||
|
* @return a label
|
||||||
|
* @throws IOException
|
||||||
|
* the io exception
|
||||||
|
*/
|
||||||
|
static GHLabel read(@Nonnull GHRepository repository, @Nonnull String name) throws IOException {
|
||||||
|
return repository.root.createRequest()
|
||||||
|
.withUrlPath(repository.getApiTailUrl("labels"), name)
|
||||||
|
.fetch(GHLabel.class);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads all labels from a repository.
|
||||||
|
*
|
||||||
|
* @param repository
|
||||||
|
* the repository to read from
|
||||||
|
* @return iterable of all labels
|
||||||
|
* @throws IOException
|
||||||
|
* the io exception
|
||||||
|
*/
|
||||||
|
static PagedIterable<GHLabel> readAll(@Nonnull final GHRepository repository) throws IOException {
|
||||||
|
return repository.root.createRequest()
|
||||||
|
.withUrlPath(repository.getApiTailUrl("labels"))
|
||||||
|
.toIterable(GHLabel[].class, null);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Begins a batch update
|
||||||
|
*
|
||||||
|
* Consumer must call {@link Updater#done()} to commit changes.
|
||||||
|
*
|
||||||
|
* @return a {@link Updater}
|
||||||
|
*/
|
||||||
|
@Preview
|
||||||
|
@Deprecated
|
||||||
|
public Updater update() {
|
||||||
|
return new Updater(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Begins a single property update.
|
||||||
|
*
|
||||||
|
* @return a {@link Setter}
|
||||||
|
*/
|
||||||
|
@Preview
|
||||||
|
@Deprecated
|
||||||
|
public Setter set() {
|
||||||
|
return new Setter(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete this label from the repository.
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
* the io exception
|
||||||
|
*/
|
||||||
|
public void delete() throws IOException {
|
||||||
|
root.createRequest().method("DELETE").setRawUrlPath(getUrl()).send();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(final Object o) {
|
public boolean equals(final Object o) {
|
||||||
if (this == o)
|
if (this == o)
|
||||||
@@ -120,11 +214,54 @@ public class GHLabel {
|
|||||||
return false;
|
return false;
|
||||||
final GHLabel ghLabel = (GHLabel) o;
|
final GHLabel ghLabel = (GHLabel) o;
|
||||||
return Objects.equals(url, ghLabel.url) && Objects.equals(name, ghLabel.name)
|
return Objects.equals(url, ghLabel.url) && Objects.equals(name, ghLabel.name)
|
||||||
&& Objects.equals(color, ghLabel.color) && Objects.equals(repo, ghLabel.repo);
|
&& Objects.equals(color, ghLabel.color) && Objects.equals(description, ghLabel.description);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(url, name, color, repo);
|
return Objects.hash(url, name, color, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link GHLabelBuilder} that updates a single property per request
|
||||||
|
*
|
||||||
|
* {@link #done()} is called automatically after the property is set.
|
||||||
|
*/
|
||||||
|
@Preview
|
||||||
|
@Deprecated
|
||||||
|
public static class Setter extends GHLabelBuilder<GHLabel> {
|
||||||
|
private Setter(@Nonnull GHLabel base) {
|
||||||
|
super(GHLabel.class, base.getApiRoot(), base);
|
||||||
|
requester.method("PATCH").setRawUrlPath(base.getUrl());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link GHLabelBuilder} that allows multiple properties to be updated per request.
|
||||||
|
*
|
||||||
|
* Consumer must call {@link #done()} to commit changes.
|
||||||
|
*/
|
||||||
|
@Preview
|
||||||
|
@Deprecated
|
||||||
|
public static class Updater extends GHLabelBuilder<Updater> {
|
||||||
|
private Updater(@Nonnull GHLabel base) {
|
||||||
|
super(Updater.class, base.getApiRoot(), base);
|
||||||
|
requester.method("PATCH").setRawUrlPath(base.getUrl());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link GHLabelBuilder} that creates a new {@link GHLabel}
|
||||||
|
*
|
||||||
|
* Consumer must call {@link #done()} to create the new instance.
|
||||||
|
*/
|
||||||
|
@Preview
|
||||||
|
@Deprecated
|
||||||
|
public static class Creator extends GHLabelBuilder<Creator> {
|
||||||
|
private Creator(@Nonnull GHRepository repository) {
|
||||||
|
super(Creator.class, repository.root, null);
|
||||||
|
requester.method("POST").withUrlPath(repository.getApiTailUrl("labels"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
60
src/main/java/org/kohsuke/github/GHLabelBuilder.java
Normal file
60
src/main/java/org/kohsuke/github/GHLabelBuilder.java
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.annotation.CheckForNull;
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param <S>
|
||||||
|
* Intermediate return type for this builder returned by calls to {@link #with(String, Object)}. If {@link S}
|
||||||
|
* the same as {@link GHLabel}, this builder will commit changes after each call to
|
||||||
|
* {@link #with(String, Object)}.
|
||||||
|
*/
|
||||||
|
class GHLabelBuilder<S> extends AbstractBuilder<GHLabel, S> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param intermediateReturnType
|
||||||
|
* Intermediate return type for this builder returned by calls to {@link #with(String, Object)}. If
|
||||||
|
* {@link S} the same as {@link GHLabel}, this builder will commit changes after each call to
|
||||||
|
* {@link #with(String, Object)}.
|
||||||
|
* @param root
|
||||||
|
* the GitHub instance to which updates will be sent
|
||||||
|
* @param baseInstance
|
||||||
|
* instance on which to base this builder. If {@code null} a new instance will be created.
|
||||||
|
*/
|
||||||
|
protected GHLabelBuilder(@Nonnull Class<S> intermediateReturnType,
|
||||||
|
@Nonnull GitHub root,
|
||||||
|
@CheckForNull GHLabel baseInstance) {
|
||||||
|
super(GHLabel.class, intermediateReturnType, root, baseInstance);
|
||||||
|
|
||||||
|
if (baseInstance != null) {
|
||||||
|
requester.with("name", baseInstance.getName());
|
||||||
|
requester.with("color", baseInstance.getColor());
|
||||||
|
requester.with("description", baseInstance.getDescription());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Preview
|
||||||
|
@Deprecated
|
||||||
|
public S name(String value) throws IOException {
|
||||||
|
return with("name", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Preview
|
||||||
|
@Deprecated
|
||||||
|
public S color(String value) throws IOException {
|
||||||
|
return with("color", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Preview
|
||||||
|
@Deprecated
|
||||||
|
public S description(String value) throws IOException {
|
||||||
|
return with("description", value);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,13 +24,13 @@
|
|||||||
|
|
||||||
package org.kohsuke.github;
|
package org.kohsuke.github;
|
||||||
|
|
||||||
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
|
|
||||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The GitHub Preview API's license information
|
* The GitHub Preview API's license information
|
||||||
@@ -78,14 +78,6 @@ public class GHLicense extends GHObject {
|
|||||||
return name;
|
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
|
* Featured licenses are bold in the new repository drop-down
|
||||||
*
|
*
|
||||||
@@ -100,7 +92,7 @@ public class GHLicense extends GHObject {
|
|||||||
|
|
||||||
public URL getHtmlUrl() throws IOException {
|
public URL getHtmlUrl() throws IOException {
|
||||||
populate();
|
populate();
|
||||||
return GitHub.parseURL(html_url);
|
return GitHubClient.parseURL(html_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -199,7 +191,14 @@ public class GHLicense extends GHObject {
|
|||||||
if (description != null)
|
if (description != null)
|
||||||
return; // already populated
|
return; // already populated
|
||||||
|
|
||||||
root.createRequest().withUrlPath(url).fetchInto(this);
|
if (root == null || root.isOffline()) {
|
||||||
|
return; // cannot populate, will have to live with what we have
|
||||||
|
}
|
||||||
|
|
||||||
|
URL url = getUrl();
|
||||||
|
if (url != null) {
|
||||||
|
root.createRequest().setRawUrlPath(url.toString()).fetchInto(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -210,12 +209,12 @@ public class GHLicense extends GHObject {
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
GHLicense that = (GHLicense) o;
|
GHLicense that = (GHLicense) o;
|
||||||
return this.url.equals(that.url);
|
return Objects.equals(getUrl(), that.getUrl());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return url.hashCode();
|
return Objects.hashCode(getUrl());
|
||||||
}
|
}
|
||||||
|
|
||||||
GHLicense wrap(GitHub root) {
|
GHLicense wrap(GitHub root) {
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ public class GHMarketplaceAccount {
|
|||||||
* @return the url
|
* @return the url
|
||||||
*/
|
*/
|
||||||
public URL getUrl() {
|
public URL getUrl() {
|
||||||
return GitHub.parseURL(url);
|
return GitHubClient.parseURL(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ public class GHMarketplacePendingChange {
|
|||||||
* @return the effective date
|
* @return the effective date
|
||||||
*/
|
*/
|
||||||
public Date getEffectiveDate() {
|
public Date getEffectiveDate() {
|
||||||
return GitHub.parseDate(effectiveDate);
|
return GitHubClient.parseDate(effectiveDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ public class GHMarketplacePlan {
|
|||||||
* @return the url
|
* @return the url
|
||||||
*/
|
*/
|
||||||
public URL getUrl() {
|
public URL getUrl() {
|
||||||
return GitHub.parseURL(url);
|
return GitHubClient.parseURL(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ public class GHMarketplacePurchase {
|
|||||||
* @return the next billing date
|
* @return the next billing date
|
||||||
*/
|
*/
|
||||||
public Date getNextBillingDate() {
|
public Date getNextBillingDate() {
|
||||||
return GitHub.parseDate(nextBillingDate);
|
return GitHubClient.parseDate(nextBillingDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -70,7 +70,7 @@ public class GHMarketplacePurchase {
|
|||||||
* @return the free trial ends on
|
* @return the free trial ends on
|
||||||
*/
|
*/
|
||||||
public Date getFreeTrialEndsOn() {
|
public Date getFreeTrialEndsOn() {
|
||||||
return GitHub.parseDate(freeTrialEndsOn);
|
return GitHubClient.parseDate(freeTrialEndsOn);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -88,7 +88,7 @@ public class GHMarketplacePurchase {
|
|||||||
* @return the updated at
|
* @return the updated at
|
||||||
*/
|
*/
|
||||||
public Date getUpdatedAt() {
|
public Date getUpdatedAt() {
|
||||||
return GitHub.parseDate(updatedAt);
|
return GitHubClient.parseDate(updatedAt);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ public class GHMarketplaceUserPurchase {
|
|||||||
* @return the next billing date
|
* @return the next billing date
|
||||||
*/
|
*/
|
||||||
public Date getNextBillingDate() {
|
public Date getNextBillingDate() {
|
||||||
return GitHub.parseDate(nextBillingDate);
|
return GitHubClient.parseDate(nextBillingDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -72,7 +72,7 @@ public class GHMarketplaceUserPurchase {
|
|||||||
* @return the free trial ends on
|
* @return the free trial ends on
|
||||||
*/
|
*/
|
||||||
public Date getFreeTrialEndsOn() {
|
public Date getFreeTrialEndsOn() {
|
||||||
return GitHub.parseDate(freeTrialEndsOn);
|
return GitHubClient.parseDate(freeTrialEndsOn);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -90,7 +90,7 @@ public class GHMarketplaceUserPurchase {
|
|||||||
* @return the updated at
|
* @return the updated at
|
||||||
*/
|
*/
|
||||||
public Date getUpdatedAt() {
|
public Date getUpdatedAt() {
|
||||||
return GitHub.parseDate(updatedAt);
|
return GitHubClient.parseDate(updatedAt);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ public class GHMembership /* extends GHObject --- but it doesn't have id, create
|
|||||||
* @return the url
|
* @return the url
|
||||||
*/
|
*/
|
||||||
public URL getUrl() {
|
public URL getUrl() {
|
||||||
return GitHub.parseURL(url);
|
return GitHubClient.parseURL(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -84,11 +84,6 @@ public class GHMembership /* extends GHObject --- but it doesn't have id, create
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void wrap(GHMembership[] page, GitHub root) {
|
|
||||||
for (GHMembership m : page)
|
|
||||||
m.wrap(root);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Role of a user in an organization.
|
* Role of a user in an organization.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ public class GHMilestone extends GHObject {
|
|||||||
public Date getDueOn() {
|
public Date getDueOn() {
|
||||||
if (due_on == null)
|
if (due_on == null)
|
||||||
return null;
|
return null;
|
||||||
return GitHub.parseDate(due_on);
|
return GitHubClient.parseDate(due_on);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -67,7 +67,7 @@ public class GHMilestone extends GHObject {
|
|||||||
* the io exception
|
* the io exception
|
||||||
*/
|
*/
|
||||||
public Date getClosedAt() throws IOException {
|
public Date getClosedAt() throws IOException {
|
||||||
return GitHub.parseDate(closed_at);
|
return GitHubClient.parseDate(closed_at);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -116,7 +116,7 @@ public class GHMilestone extends GHObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public URL getHtmlUrl() {
|
public URL getHtmlUrl() {
|
||||||
return GitHub.parseURL(html_url);
|
return GitHubClient.parseURL(html_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -195,7 +195,7 @@ public class GHMilestone extends GHObject {
|
|||||||
* the io exception
|
* the io exception
|
||||||
*/
|
*/
|
||||||
public void setDueOn(Date dueOn) throws IOException {
|
public void setDueOn(Date dueOn) throws IOException {
|
||||||
edit("due_on", GitHub.printDate(dueOn));
|
edit("due_on", GitHubClient.printDate(dueOn));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package org.kohsuke.github;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -71,8 +70,7 @@ public class GHMyself extends GHUser {
|
|||||||
* the io exception
|
* the io exception
|
||||||
*/
|
*/
|
||||||
public List<GHEmail> getEmails2() throws IOException {
|
public List<GHEmail> getEmails2() throws IOException {
|
||||||
GHEmail[] addresses = root.createRequest().withUrlPath("/user/emails").fetchArray(GHEmail[].class);
|
return root.createRequest().withUrlPath("/user/emails").toIterable(GHEmail[].class, null).toList();
|
||||||
return Collections.unmodifiableList(Arrays.asList(addresses));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -86,8 +84,7 @@ public class GHMyself extends GHUser {
|
|||||||
* the io exception
|
* the io exception
|
||||||
*/
|
*/
|
||||||
public List<GHKey> getPublicKeys() throws IOException {
|
public List<GHKey> getPublicKeys() throws IOException {
|
||||||
return Collections.unmodifiableList(
|
return root.createRequest().withUrlPath("/user/keys").toIterable(GHKey[].class, null).toList();
|
||||||
Arrays.asList(root.createRequest().withUrlPath("/user/keys").fetchArray(GHKey[].class)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -101,8 +98,10 @@ public class GHMyself extends GHUser {
|
|||||||
* the io exception
|
* the io exception
|
||||||
*/
|
*/
|
||||||
public List<GHVerifiedKey> getPublicVerifiedKeys() throws IOException {
|
public List<GHVerifiedKey> getPublicVerifiedKeys() throws IOException {
|
||||||
return Collections.unmodifiableList(Arrays.asList(
|
return root.createRequest()
|
||||||
root.createRequest().withUrlPath("/users/" + getLogin() + "/keys").fetchArray(GHVerifiedKey[].class)));
|
.withUrlPath("/users/" + getLogin() + "/keys")
|
||||||
|
.toIterable(GHVerifiedKey[].class, null)
|
||||||
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -115,7 +114,10 @@ public class GHMyself extends GHUser {
|
|||||||
public GHPersonSet<GHOrganization> getAllOrganizations() throws IOException {
|
public GHPersonSet<GHOrganization> getAllOrganizations() throws IOException {
|
||||||
GHPersonSet<GHOrganization> orgs = new GHPersonSet<GHOrganization>();
|
GHPersonSet<GHOrganization> orgs = new GHPersonSet<GHOrganization>();
|
||||||
Set<String> names = new HashSet<String>();
|
Set<String> names = new HashSet<String>();
|
||||||
for (GHOrganization o : root.createRequest().withUrlPath("/user/orgs").fetchArray(GHOrganization[].class)) {
|
for (GHOrganization o : root.createRequest()
|
||||||
|
.withUrlPath("/user/orgs")
|
||||||
|
.toIterable(GHOrganization[].class, null)
|
||||||
|
.toArray()) {
|
||||||
if (names.add(o.getLogin())) // in case of rumoured duplicates in the data
|
if (names.add(o.getLogin())) // in case of rumoured duplicates in the data
|
||||||
orgs.add(root.getOrganization(o.getLogin()));
|
orgs.add(root.getOrganization(o.getLogin()));
|
||||||
}
|
}
|
||||||
@@ -189,6 +191,7 @@ public class GHMyself extends GHUser {
|
|||||||
* @return the paged iterable
|
* @return the paged iterable
|
||||||
* @deprecated Use {@link #listRepositories()}
|
* @deprecated Use {@link #listRepositories()}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public PagedIterable<GHRepository> listAllRepositories() {
|
public PagedIterable<GHRepository> listAllRepositories() {
|
||||||
return listRepositories();
|
return listRepositories();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ public class GHNotificationStream implements Iterable<GHThread> {
|
|||||||
* @return the gh notification stream
|
* @return the gh notification stream
|
||||||
*/
|
*/
|
||||||
public GHNotificationStream since(Date dt) {
|
public GHNotificationStream since(Date dt) {
|
||||||
since = GitHub.printDate(dt);
|
since = GitHubClient.printDate(dt);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,7 +180,11 @@ public class GHNotificationStream implements Iterable<GHThread> {
|
|||||||
|
|
||||||
req.setHeader("If-Modified-Since", lastModified);
|
req.setHeader("If-Modified-Since", lastModified);
|
||||||
|
|
||||||
threads = req.withUrlPath(apiUrl).fetchArray(GHThread[].class);
|
Requester requester = req.withUrlPath(apiUrl);
|
||||||
|
GitHubResponse<GHThread[]> response = ((GitHubPageContentsIterable<GHThread>) requester
|
||||||
|
.toIterable(GHThread[].class, null)).toResponse();
|
||||||
|
threads = response.body();
|
||||||
|
|
||||||
if (threads == null) {
|
if (threads == null) {
|
||||||
threads = EMPTY_ARRAY; // if unmodified, we get empty array
|
threads = EMPTY_ARRAY; // if unmodified, we get empty array
|
||||||
} else {
|
} else {
|
||||||
@@ -189,27 +193,21 @@ public class GHNotificationStream implements Iterable<GHThread> {
|
|||||||
}
|
}
|
||||||
idx = threads.length - 1;
|
idx = threads.length - 1;
|
||||||
|
|
||||||
nextCheckTime = calcNextCheckTime();
|
nextCheckTime = calcNextCheckTime(response);
|
||||||
lastModified = req.getResponseHeader("Last-Modified");
|
lastModified = response.headerField("Last-Modified");
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException | InterruptedException e) {
|
||||||
throw new RuntimeException(e);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private long calcNextCheckTime() {
|
private long calcNextCheckTime(GitHubResponse<GHThread[]> response) {
|
||||||
String v = req.getResponseHeader("X-Poll-Interval");
|
String v = response.headerField("X-Poll-Interval");
|
||||||
if (v == null)
|
if (v == null)
|
||||||
v = "60";
|
v = "60";
|
||||||
long seconds = Integer.parseInt(v);
|
long seconds = Integer.parseInt(v);
|
||||||
return System.currentTimeMillis() + seconds * 1000;
|
return System.currentTimeMillis() + seconds * 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void remove() {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,7 +232,7 @@ public class GHNotificationStream implements Iterable<GHThread> {
|
|||||||
public void markAsRead(long timestamp) throws IOException {
|
public void markAsRead(long timestamp) throws IOException {
|
||||||
final Requester req = root.createRequest();
|
final Requester req = root.createRequest();
|
||||||
if (timestamp >= 0)
|
if (timestamp >= 0)
|
||||||
req.with("last_read_at", GitHub.printDate(new Date(timestamp)));
|
req.with("last_read_at", GitHubClient.printDate(new Date(timestamp)));
|
||||||
req.withUrlPath(apiUrl).fetchHttpStatusCode();
|
req.withUrlPath(apiUrl).fetchHttpStatusCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.kohsuke.github;
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JacksonInject;
|
||||||
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
|
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
|
||||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||||
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
|
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
|
||||||
@@ -23,16 +24,31 @@ public abstract class GHObject {
|
|||||||
/**
|
/**
|
||||||
* Capture response HTTP headers on the state object.
|
* Capture response HTTP headers on the state object.
|
||||||
*/
|
*/
|
||||||
protected Map<String, List<String>> responseHeaderFields;
|
protected transient Map<String, List<String>> responseHeaderFields;
|
||||||
|
|
||||||
protected String url;
|
private String url;
|
||||||
protected long id;
|
|
||||||
protected String created_at;
|
private long id;
|
||||||
protected String updated_at;
|
private String nodeId;
|
||||||
|
private String createdAt;
|
||||||
|
private String updatedAt;
|
||||||
|
|
||||||
GHObject() {
|
GHObject() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by Jackson
|
||||||
|
*
|
||||||
|
* @param responseInfo
|
||||||
|
* the {@link GitHubResponse.ResponseInfo} to get headers from.
|
||||||
|
*/
|
||||||
|
@JacksonInject
|
||||||
|
protected void setResponseHeaderFields(@CheckForNull GitHubResponse.ResponseInfo responseInfo) {
|
||||||
|
if (responseInfo != null) {
|
||||||
|
responseHeaderFields = responseInfo.headers();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the HTTP response headers given along with the state of this object.
|
* Returns the HTTP response headers given along with the state of this object.
|
||||||
*
|
*
|
||||||
@@ -60,12 +76,12 @@ public abstract class GHObject {
|
|||||||
*/
|
*/
|
||||||
@WithBridgeMethods(value = String.class, adapterMethod = "createdAtStr")
|
@WithBridgeMethods(value = String.class, adapterMethod = "createdAtStr")
|
||||||
public Date getCreatedAt() throws IOException {
|
public Date getCreatedAt() throws IOException {
|
||||||
return GitHub.parseDate(created_at);
|
return GitHubClient.parseDate(createdAt);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD", justification = "Bridge method of getCreatedAt")
|
@SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD", justification = "Bridge method of getCreatedAt")
|
||||||
private Object createdAtStr(Date id, Class type) {
|
private Object createdAtStr(Date id, Class type) {
|
||||||
return created_at;
|
return createdAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -75,7 +91,7 @@ public abstract class GHObject {
|
|||||||
*/
|
*/
|
||||||
@WithBridgeMethods(value = String.class, adapterMethod = "urlToString")
|
@WithBridgeMethods(value = String.class, adapterMethod = "urlToString")
|
||||||
public URL getUrl() {
|
public URL getUrl() {
|
||||||
return GitHub.parseURL(url);
|
return GitHubClient.parseURL(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -96,7 +112,18 @@ public abstract class GHObject {
|
|||||||
* on error
|
* on error
|
||||||
*/
|
*/
|
||||||
public Date getUpdatedAt() throws IOException {
|
public Date getUpdatedAt() throws IOException {
|
||||||
return GitHub.parseDate(updated_at);
|
return GitHubClient.parseDate(updatedAt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Global node_id from Github object.
|
||||||
|
*
|
||||||
|
* @see <a href="https://developer.github.com/v4/guides/using-global-node-ids/">Using Global Node IDs</a>
|
||||||
|
*
|
||||||
|
* @return Global Node ID.
|
||||||
|
*/
|
||||||
|
public String getNodeId() {
|
||||||
|
return nodeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -22,6 +22,6 @@ class GHOrgHook extends GHHook {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
String getApiRoute() {
|
String getApiRoute() {
|
||||||
return String.format("/orgs/%s/hooks/%d", organization.getLogin(), id);
|
return String.format("/orgs/%s/hooks/%d", organization.getLogin(), getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ public class GHOrganization extends GHPerson {
|
|||||||
* the io exception
|
* the io exception
|
||||||
* @deprecated Use {@link #createRepository(String)} that uses a builder pattern to let you control every aspect.
|
* @deprecated Use {@link #createRepository(String)} that uses a builder pattern to let you control every aspect.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public GHRepository createRepository(String name,
|
public GHRepository createRepository(String name,
|
||||||
String description,
|
String description,
|
||||||
String homepage,
|
String homepage,
|
||||||
@@ -69,6 +70,7 @@ public class GHOrganization extends GHPerson {
|
|||||||
* the io exception
|
* the io exception
|
||||||
* @deprecated Use {@link #createRepository(String)} that uses a builder pattern to let you control every aspect.
|
* @deprecated Use {@link #createRepository(String)} that uses a builder pattern to let you control every aspect.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public GHRepository createRepository(String name,
|
public GHRepository createRepository(String name,
|
||||||
String description,
|
String description,
|
||||||
String homepage,
|
String homepage,
|
||||||
@@ -126,6 +128,40 @@ public class GHOrganization extends GHPerson {
|
|||||||
.toIterable(GHTeam[].class, item -> item.wrapUp(this));
|
.toIterable(GHTeam[].class, item -> item.wrapUp(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a single team by ID.
|
||||||
|
*
|
||||||
|
* @param teamId
|
||||||
|
* id of the team that we want to query for
|
||||||
|
* @return the team
|
||||||
|
* @throws IOException
|
||||||
|
* the io exception
|
||||||
|
*
|
||||||
|
* @deprecated Use {@link GHOrganization#getTeam(long)}
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public GHTeam getTeam(int teamId) throws IOException {
|
||||||
|
return getTeam((long) teamId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a single team by ID.
|
||||||
|
*
|
||||||
|
* @param teamId
|
||||||
|
* id of the team that we want to query for
|
||||||
|
* @return the team
|
||||||
|
* @throws IOException
|
||||||
|
* the io exception
|
||||||
|
*
|
||||||
|
* @see <a href= "https://developer.github.com/v3/teams/#get-team-by-name">documentation</a>
|
||||||
|
*/
|
||||||
|
public GHTeam getTeam(long teamId) throws IOException {
|
||||||
|
return root.createRequest()
|
||||||
|
.withUrlPath(String.format("/organizations/%d/team/%d", getId(), teamId))
|
||||||
|
.fetch(GHTeam.class)
|
||||||
|
.wrapUp(this);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds a team that has the given name in its {@link GHTeam#getName()}
|
* Finds a team that has the given name in its {@link GHTeam#getName()}
|
||||||
*
|
*
|
||||||
@@ -145,19 +181,19 @@ public class GHOrganization extends GHPerson {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds a team that has the given slug in its {@link GHTeam#getSlug()}
|
* Finds a team that has the given slug in its {@link GHTeam#getSlug()}
|
||||||
*
|
*
|
||||||
* @param slug
|
* @param slug
|
||||||
* the slug
|
* the slug
|
||||||
* @return the team by slug
|
* @return the team by slug
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
* the io exception
|
* the io exception
|
||||||
|
* @see <a href= "https://developer.github.com/v3/teams/#get-team-by-name">documentation</a>
|
||||||
*/
|
*/
|
||||||
public GHTeam getTeamBySlug(String slug) throws IOException {
|
public GHTeam getTeamBySlug(String slug) throws IOException {
|
||||||
for (GHTeam t : listTeams()) {
|
return root.createRequest()
|
||||||
if (t.getSlug().equals(slug))
|
.withUrlPath(String.format("/orgs/%s/teams/%s", login, slug))
|
||||||
return t;
|
.fetch(GHTeam.class)
|
||||||
}
|
.wrapUp(this);
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -255,7 +291,7 @@ public class GHOrganization extends GHPerson {
|
|||||||
* @deprecated use {@link #listMembers()}
|
* @deprecated use {@link #listMembers()}
|
||||||
*/
|
*/
|
||||||
public List<GHUser> getMembers() throws IOException {
|
public List<GHUser> getMembers() throws IOException {
|
||||||
return listMembers().asList();
|
return listMembers().toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -281,7 +317,7 @@ public class GHOrganization extends GHPerson {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private PagedIterable<GHUser> listMembers(String suffix) throws IOException {
|
private PagedIterable<GHUser> listMembers(String suffix) throws IOException {
|
||||||
return listMembers(suffix, null);
|
return listMembers(suffix, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -294,13 +330,28 @@ public class GHOrganization extends GHPerson {
|
|||||||
* the io exception
|
* the io exception
|
||||||
*/
|
*/
|
||||||
public PagedIterable<GHUser> listMembersWithFilter(String filter) throws IOException {
|
public PagedIterable<GHUser> listMembersWithFilter(String filter) throws IOException {
|
||||||
return listMembers("members", filter);
|
return listMembers("members", filter, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private PagedIterable<GHUser> listMembers(final String suffix, final String filter) throws IOException {
|
/**
|
||||||
String filterParams = (filter == null) ? "" : ("?filter=" + filter);
|
* List members with specified role paged iterable.
|
||||||
|
*
|
||||||
|
* @param role
|
||||||
|
* the role
|
||||||
|
* @return the paged iterable
|
||||||
|
* @throws IOException
|
||||||
|
* the io exception
|
||||||
|
*/
|
||||||
|
public PagedIterable<GHUser> listMembersWithRole(String role) throws IOException {
|
||||||
|
return listMembers("members", null, role);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PagedIterable<GHUser> listMembers(final String suffix, final String filter, String role)
|
||||||
|
throws IOException {
|
||||||
return root.createRequest()
|
return root.createRequest()
|
||||||
.withUrlPath(String.format("/orgs/%s/%s%s", login, suffix, filterParams))
|
.withUrlPath(String.format("/orgs/%s/%s", login, suffix))
|
||||||
|
.with("filter", filter)
|
||||||
|
.with("role", role)
|
||||||
.toIterable(GHUser[].class, item -> item.wrapUp(root));
|
.toIterable(GHUser[].class, item -> item.wrapUp(root));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -370,7 +421,7 @@ public class GHOrganization extends GHPerson {
|
|||||||
* The enum Permission.
|
* The enum Permission.
|
||||||
*/
|
*/
|
||||||
public enum Permission {
|
public enum Permission {
|
||||||
ADMIN, PUSH, PULL
|
ADMIN, MAINTAIN, PUSH, TRIAGE, PULL
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2,13 +2,14 @@ package org.kohsuke.github;
|
|||||||
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -20,13 +21,16 @@ public abstract class GHPerson extends GHObject {
|
|||||||
/* package almost final */ GitHub root;
|
/* package almost final */ GitHub root;
|
||||||
|
|
||||||
// core data fields that exist even for "small" user data (such as the user info in pull request)
|
// core data fields that exist even for "small" user data (such as the user info in pull request)
|
||||||
protected String login, avatar_url, gravatar_id;
|
protected String login, avatar_url;
|
||||||
|
|
||||||
// other fields (that only show up in full data)
|
// other fields (that only show up in full data)
|
||||||
protected String location, blog, email, name, company, type;
|
protected String location, blog, email, bio, name, company, type, twitter_username;
|
||||||
protected String html_url;
|
protected String html_url;
|
||||||
protected int followers, following, public_repos, public_gists;
|
protected int followers, following, public_repos, public_gists;
|
||||||
protected boolean site_admin;
|
protected boolean site_admin, hireable;
|
||||||
|
|
||||||
|
// other fields (that only show up in full data) that require privileged scope
|
||||||
|
protected Integer total_private_repos;
|
||||||
|
|
||||||
GHPerson wrapUp(GitHub root) {
|
GHPerson wrapUp(GitHub root) {
|
||||||
this.root = root;
|
this.root = root;
|
||||||
@@ -42,13 +46,16 @@ public abstract class GHPerson extends GHObject {
|
|||||||
* the io exception
|
* the io exception
|
||||||
*/
|
*/
|
||||||
protected synchronized void populate() throws IOException {
|
protected synchronized void populate() throws IOException {
|
||||||
if (created_at != null) {
|
if (super.getCreatedAt() != null) {
|
||||||
return; // already populated
|
return; // already populated
|
||||||
}
|
}
|
||||||
if (root == null || root.isOffline()) {
|
if (root == null || root.isOffline()) {
|
||||||
return; // cannot populate, will have to live with what we have
|
return; // cannot populate, will have to live with what we have
|
||||||
}
|
}
|
||||||
root.createRequest().withUrlPath(url).fetchInto(this);
|
URL url = getUrl();
|
||||||
|
if (url != null) {
|
||||||
|
root.createRequest().setRawUrlPath(url.toString()).fetchInto(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -112,29 +119,27 @@ public abstract class GHPerson extends GHObject {
|
|||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public synchronized Iterable<List<GHRepository>> iterateRepositories(final int pageSize) {
|
public synchronized Iterable<List<GHRepository>> iterateRepositories(final int pageSize) {
|
||||||
return new Iterable<List<GHRepository>>() {
|
return () -> {
|
||||||
public Iterator<List<GHRepository>> iterator() {
|
final PagedIterator<GHRepository> pager;
|
||||||
final Iterator<GHRepository[]> pager = root.createRequest()
|
try {
|
||||||
.withUrlPath("users", login, "repos")
|
GitHubPageIterator<GHRepository[]> iterator = GitHubPageIterator.create(root.getClient(),
|
||||||
.asIterator(GHRepository[].class, pageSize);
|
GHRepository[].class,
|
||||||
|
root.createRequest().withUrlPath("users", login, "repos").build(),
|
||||||
return new Iterator<List<GHRepository>>() {
|
pageSize);
|
||||||
public boolean hasNext() {
|
pager = new PagedIterator<>(iterator, item -> item.wrap(root));
|
||||||
return pager.hasNext();
|
} catch (MalformedURLException e) {
|
||||||
}
|
throw new GHException("Unable to build GitHub API URL", e);
|
||||||
|
|
||||||
public List<GHRepository> next() {
|
|
||||||
GHRepository[] batch = pager.next();
|
|
||||||
for (GHRepository r : batch)
|
|
||||||
r.root = root;
|
|
||||||
return Arrays.asList(batch);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void remove() {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return new Iterator<List<GHRepository>>() {
|
||||||
|
public boolean hasNext() {
|
||||||
|
return pager.hasNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<GHRepository> next() {
|
||||||
|
return pager.nextPage();
|
||||||
|
}
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,10 +154,7 @@ public abstract class GHPerson extends GHObject {
|
|||||||
*/
|
*/
|
||||||
public GHRepository getRepository(String name) throws IOException {
|
public GHRepository getRepository(String name) throws IOException {
|
||||||
try {
|
try {
|
||||||
return root.createRequest()
|
return GHRepository.read(root, login, name);
|
||||||
.withUrlPath("/repos/" + login + '/' + name)
|
|
||||||
.fetch(GHRepository.class)
|
|
||||||
.wrap(root);
|
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -173,22 +175,18 @@ public abstract class GHPerson extends GHObject {
|
|||||||
* @return the gravatar id
|
* @return the gravatar id
|
||||||
* @deprecated No longer available in the v3 API.
|
* @deprecated No longer available in the v3 API.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public String getGravatarId() {
|
public String getGravatarId() {
|
||||||
return gravatar_id;
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a string like 'https://secure.gravatar.com/avatar/0cb9832a01c22c083390f3c5dcb64105' that indicates the
|
* Returns a string of the avatar image URL.
|
||||||
* avatar image URL.
|
|
||||||
*
|
*
|
||||||
* @return the avatar url
|
* @return the avatar url
|
||||||
*/
|
*/
|
||||||
public String getAvatarUrl() {
|
public String getAvatarUrl() {
|
||||||
if (avatar_url != null)
|
return avatar_url;
|
||||||
return avatar_url;
|
|
||||||
if (gravatar_id != null)
|
|
||||||
return "https://secure.gravatar.com/avatar/" + gravatar_id;
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -236,6 +234,18 @@ public abstract class GHPerson extends GHObject {
|
|||||||
return location;
|
return location;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the Twitter Username of this user, like "GitHub"
|
||||||
|
*
|
||||||
|
* @return the Twitter username
|
||||||
|
* @throws IOException
|
||||||
|
* the io exception
|
||||||
|
*/
|
||||||
|
public String getTwitterUsername() throws IOException {
|
||||||
|
populate();
|
||||||
|
return twitter_username;
|
||||||
|
}
|
||||||
|
|
||||||
public Date getCreatedAt() throws IOException {
|
public Date getCreatedAt() throws IOException {
|
||||||
populate();
|
populate();
|
||||||
return super.getCreatedAt();
|
return super.getCreatedAt();
|
||||||
@@ -260,7 +270,7 @@ public abstract class GHPerson extends GHObject {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public URL getHtmlUrl() {
|
public URL getHtmlUrl() {
|
||||||
return GitHub.parseURL(html_url);
|
return GitHubClient.parseURL(html_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -346,4 +356,16 @@ public abstract class GHPerson extends GHObject {
|
|||||||
populate();
|
populate();
|
||||||
return site_admin;
|
return site_admin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets total private repo count.
|
||||||
|
*
|
||||||
|
* @return the total private repo count
|
||||||
|
* @throws IOException
|
||||||
|
* the io exception
|
||||||
|
*/
|
||||||
|
public Optional<Integer> getTotalPrivateRepoCount() throws IOException {
|
||||||
|
populate();
|
||||||
|
return Optional.ofNullable(total_private_repos);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ public class GHProject extends GHObject {
|
|||||||
|
|
||||||
private String owner_url;
|
private String owner_url;
|
||||||
private String html_url;
|
private String html_url;
|
||||||
private String node_id;
|
|
||||||
private String name;
|
private String name;
|
||||||
private String body;
|
private String body;
|
||||||
private int number;
|
private int number;
|
||||||
@@ -51,7 +50,7 @@ public class GHProject extends GHObject {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public URL getHtmlUrl() throws IOException {
|
public URL getHtmlUrl() throws IOException {
|
||||||
return GitHub.parseURL(html_url);
|
return GitHubClient.parseURL(html_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -81,10 +80,8 @@ public class GHProject extends GHObject {
|
|||||||
} else if (owner_url.contains("/users/")) {
|
} else if (owner_url.contains("/users/")) {
|
||||||
owner = root.createRequest().withUrlPath(getOwnerUrl().getPath()).fetch(GHUser.class).wrapUp(root);
|
owner = root.createRequest().withUrlPath(getOwnerUrl().getPath()).fetch(GHUser.class).wrapUp(root);
|
||||||
} else if (owner_url.contains("/repos/")) {
|
} else if (owner_url.contains("/repos/")) {
|
||||||
owner = root.createRequest()
|
String[] pathElements = getOwnerUrl().getPath().split("/");
|
||||||
.withUrlPath(getOwnerUrl().getPath())
|
owner = GHRepository.read(root, pathElements[1], pathElements[2]);
|
||||||
.fetch(GHRepository.class)
|
|
||||||
.wrap(root);
|
|
||||||
}
|
}
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
return null;
|
return null;
|
||||||
@@ -99,16 +96,18 @@ public class GHProject extends GHObject {
|
|||||||
* @return the owner url
|
* @return the owner url
|
||||||
*/
|
*/
|
||||||
public URL getOwnerUrl() {
|
public URL getOwnerUrl() {
|
||||||
return GitHub.parseURL(owner_url);
|
return GitHubClient.parseURL(owner_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets node id.
|
* Gets node id.
|
||||||
*
|
*
|
||||||
|
* @deprecated Use {@link GHObject#getNodeId()}
|
||||||
* @return the node id
|
* @return the node id
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public String getNode_id() {
|
public String getNode_id() {
|
||||||
return node_id;
|
return getNodeId();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -191,7 +190,7 @@ public class GHProject extends GHObject {
|
|||||||
* @return the api route
|
* @return the api route
|
||||||
*/
|
*/
|
||||||
protected String getApiRoute() {
|
protected String getApiRoute() {
|
||||||
return "/projects/" + id;
|
return "/projects/" + getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -290,7 +289,7 @@ public class GHProject extends GHObject {
|
|||||||
final GHProject project = this;
|
final GHProject project = this;
|
||||||
return root.createRequest()
|
return root.createRequest()
|
||||||
.withPreview(INERTIA)
|
.withPreview(INERTIA)
|
||||||
.withUrlPath(String.format("/projects/%d/columns", id))
|
.withUrlPath(String.format("/projects/%d/columns", getId()))
|
||||||
.toIterable(GHProjectColumn[].class, item -> item.wrap(project));
|
.toIterable(GHProjectColumn[].class, item -> item.wrap(project));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -308,7 +307,7 @@ public class GHProject extends GHObject {
|
|||||||
.method("POST")
|
.method("POST")
|
||||||
.withPreview(INERTIA)
|
.withPreview(INERTIA)
|
||||||
.with("name", name)
|
.with("name", name)
|
||||||
.withUrlPath(String.format("/projects/%d/columns", id))
|
.withUrlPath(String.format("/projects/%d/columns", getId()))
|
||||||
.fetch(GHProjectColumn.class)
|
.fetch(GHProjectColumn.class)
|
||||||
.wrap(this);
|
.wrap(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ public class GHProjectCard extends GHObject {
|
|||||||
* @return the content url
|
* @return the content url
|
||||||
*/
|
*/
|
||||||
public URL getContentUrl() {
|
public URL getContentUrl() {
|
||||||
return GitHub.parseURL(content_url);
|
return GitHubClient.parseURL(content_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -158,7 +158,7 @@ public class GHProjectCard extends GHObject {
|
|||||||
* @return the project url
|
* @return the project url
|
||||||
*/
|
*/
|
||||||
public URL getProjectUrl() {
|
public URL getProjectUrl() {
|
||||||
return GitHub.parseURL(project_url);
|
return GitHubClient.parseURL(project_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -167,7 +167,7 @@ public class GHProjectCard extends GHObject {
|
|||||||
* @return the column url
|
* @return the column url
|
||||||
*/
|
*/
|
||||||
public URL getColumnUrl() {
|
public URL getColumnUrl() {
|
||||||
return GitHub.parseURL(column_url);
|
return GitHubClient.parseURL(column_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -213,7 +213,7 @@ public class GHProjectCard extends GHObject {
|
|||||||
* @return the api route
|
* @return the api route
|
||||||
*/
|
*/
|
||||||
protected String getApiRoute() {
|
protected String getApiRoute() {
|
||||||
return String.format("/projects/columns/cards/%d", id);
|
return String.format("/projects/columns/cards/%d", getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ public class GHProjectColumn extends GHObject {
|
|||||||
* @return the project url
|
* @return the project url
|
||||||
*/
|
*/
|
||||||
public URL getProjectUrl() {
|
public URL getProjectUrl() {
|
||||||
return GitHub.parseURL(project_url);
|
return GitHubClient.parseURL(project_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -115,7 +115,7 @@ public class GHProjectColumn extends GHObject {
|
|||||||
* @return the api route
|
* @return the api route
|
||||||
*/
|
*/
|
||||||
protected String getApiRoute() {
|
protected String getApiRoute() {
|
||||||
return String.format("/projects/columns/%d", id);
|
return String.format("/projects/columns/%d", getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -139,7 +139,7 @@ public class GHProjectColumn extends GHObject {
|
|||||||
final GHProjectColumn column = this;
|
final GHProjectColumn column = this;
|
||||||
return root.createRequest()
|
return root.createRequest()
|
||||||
.withPreview(INERTIA)
|
.withPreview(INERTIA)
|
||||||
.withUrlPath(String.format("/projects/columns/%d/cards", id))
|
.withUrlPath(String.format("/projects/columns/%d/cards", getId()))
|
||||||
.toIterable(GHProjectCard[].class, item -> item.wrap(column));
|
.toIterable(GHProjectCard[].class, item -> item.wrap(column));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,7 +157,7 @@ public class GHProjectColumn extends GHObject {
|
|||||||
.method("POST")
|
.method("POST")
|
||||||
.withPreview(INERTIA)
|
.withPreview(INERTIA)
|
||||||
.with("note", note)
|
.with("note", note)
|
||||||
.withUrlPath(String.format("/projects/columns/%d/cards", id))
|
.withUrlPath(String.format("/projects/columns/%d/cards", getId()))
|
||||||
.fetch(GHProjectCard.class)
|
.fetch(GHProjectCard.class)
|
||||||
.wrap(this);
|
.wrap(this);
|
||||||
}
|
}
|
||||||
@@ -177,7 +177,7 @@ public class GHProjectColumn extends GHObject {
|
|||||||
.withPreview(INERTIA)
|
.withPreview(INERTIA)
|
||||||
.with("content_type", issue instanceof GHPullRequest ? "PullRequest" : "Issue")
|
.with("content_type", issue instanceof GHPullRequest ? "PullRequest" : "Issue")
|
||||||
.with("content_id", issue.getId())
|
.with("content_id", issue.getId())
|
||||||
.withUrlPath(String.format("/projects/columns/%d/cards", id))
|
.withUrlPath(String.format("/projects/columns/%d/cards", getId()))
|
||||||
.fetch(GHProjectCard.class)
|
.fetch(GHProjectCard.class)
|
||||||
.wrap(this);
|
.wrap(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,17 +23,20 @@
|
|||||||
*/
|
*/
|
||||||
package org.kohsuke.github;
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import javax.annotation.CheckForNull;
|
import javax.annotation.CheckForNull;
|
||||||
|
|
||||||
|
import static org.kohsuke.github.Previews.LYDIAN;
|
||||||
import static org.kohsuke.github.Previews.SHADOW_CAT;
|
import static org.kohsuke.github.Previews.SHADOW_CAT;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -69,13 +72,6 @@ public class GHPullRequest extends GHIssue implements Refreshable {
|
|||||||
private GHUser[] requested_reviewers;
|
private GHUser[] requested_reviewers;
|
||||||
private GHTeam[] requested_teams;
|
private GHTeam[] requested_teams;
|
||||||
|
|
||||||
/**
|
|
||||||
* GitHub doesn't return some properties of {@link GHIssue} when requesting the GET on the 'pulls' API route as
|
|
||||||
* opposed to 'issues' API route. This flag remembers whether we made the GET call on the 'issues' route on this
|
|
||||||
* object to fill in those missing details
|
|
||||||
*/
|
|
||||||
private transient boolean fetchedIssueDetails;
|
|
||||||
|
|
||||||
GHPullRequest wrapUp(GHRepository owner) {
|
GHPullRequest wrapUp(GHRepository owner) {
|
||||||
this.wrap(owner);
|
this.wrap(owner);
|
||||||
return wrapUp(owner.root);
|
return wrapUp(owner.root);
|
||||||
@@ -99,6 +95,12 @@ public class GHPullRequest extends GHIssue implements Refreshable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getApiRoute() {
|
protected String getApiRoute() {
|
||||||
|
if (owner == null) {
|
||||||
|
// Issues returned from search to do not have an owner. Attempt to use url.
|
||||||
|
final URL url = Objects.requireNonNull(getUrl(), "Missing instance URL!");
|
||||||
|
return StringUtils.prependIfMissing(url.toString().replace(root.getApiUrl(), ""), "/");
|
||||||
|
|
||||||
|
}
|
||||||
return "/repos/" + owner.getOwnerName() + "/" + owner.getName() + "/pulls/" + number;
|
return "/repos/" + owner.getOwnerName() + "/" + owner.getName() + "/pulls/" + number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,7 +110,7 @@ public class GHPullRequest extends GHIssue implements Refreshable {
|
|||||||
* @return the patch url
|
* @return the patch url
|
||||||
*/
|
*/
|
||||||
public URL getPatchUrl() {
|
public URL getPatchUrl() {
|
||||||
return GitHub.parseURL(patch_url);
|
return GitHubClient.parseURL(patch_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -117,7 +119,7 @@ public class GHPullRequest extends GHIssue implements Refreshable {
|
|||||||
* @return the issue url
|
* @return the issue url
|
||||||
*/
|
*/
|
||||||
public URL getIssueUrl() {
|
public URL getIssueUrl() {
|
||||||
return GitHub.parseURL(issue_url);
|
return GitHubClient.parseURL(issue_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -156,7 +158,7 @@ public class GHPullRequest extends GHIssue implements Refreshable {
|
|||||||
* @return the diff url
|
* @return the diff url
|
||||||
*/
|
*/
|
||||||
public URL getDiffUrl() {
|
public URL getDiffUrl() {
|
||||||
return GitHub.parseURL(diff_url);
|
return GitHubClient.parseURL(diff_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -165,13 +167,7 @@ public class GHPullRequest extends GHIssue implements Refreshable {
|
|||||||
* @return the merged at
|
* @return the merged at
|
||||||
*/
|
*/
|
||||||
public Date getMergedAt() {
|
public Date getMergedAt() {
|
||||||
return GitHub.parseDate(merged_at);
|
return GitHubClient.parseDate(merged_at);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<GHLabel> getLabels() throws IOException {
|
|
||||||
fetchIssue();
|
|
||||||
return super.getLabels();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -381,10 +377,14 @@ public class GHPullRequest extends GHIssue implements Refreshable {
|
|||||||
* Repopulates this object.
|
* Repopulates this object.
|
||||||
*/
|
*/
|
||||||
public void refresh() throws IOException {
|
public void refresh() throws IOException {
|
||||||
if (root.isOffline()) {
|
if (root == null || root.isOffline()) {
|
||||||
return; // cannot populate, will have to live with what we have
|
return; // cannot populate, will have to live with what we have
|
||||||
}
|
}
|
||||||
root.createRequest().withPreview(SHADOW_CAT).withUrlPath(url).fetchInto(this).wrapUp(owner);
|
|
||||||
|
URL url = getUrl();
|
||||||
|
if (url != null) {
|
||||||
|
root.createRequest().withPreview(SHADOW_CAT).setRawUrlPath(url.toString()).fetchInto(this).wrapUp(owner);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -447,6 +447,7 @@ public class GHPullRequest extends GHIssue implements Refreshable {
|
|||||||
* the io exception
|
* the io exception
|
||||||
* @deprecated Use {@link #createReview()}
|
* @deprecated Use {@link #createReview()}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public GHPullRequestReview createReview(String body,
|
public GHPullRequestReview createReview(String body,
|
||||||
@CheckForNull GHPullRequestReviewState event,
|
@CheckForNull GHPullRequestReviewState event,
|
||||||
GHPullRequestReviewComment... comments) throws IOException {
|
GHPullRequestReviewComment... comments) throws IOException {
|
||||||
@@ -467,6 +468,7 @@ public class GHPullRequest extends GHIssue implements Refreshable {
|
|||||||
* the io exception
|
* the io exception
|
||||||
* @deprecated Use {@link #createReview()}
|
* @deprecated Use {@link #createReview()}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public GHPullRequestReview createReview(String body,
|
public GHPullRequestReview createReview(String body,
|
||||||
@CheckForNull GHPullRequestReviewState event,
|
@CheckForNull GHPullRequestReviewState event,
|
||||||
List<GHPullRequestReviewComment> comments) throws IOException {
|
List<GHPullRequestReviewComment> comments) throws IOException {
|
||||||
@@ -550,6 +552,41 @@ public class GHPullRequest extends GHIssue implements Refreshable {
|
|||||||
.send();
|
.send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the base branch on the pull request
|
||||||
|
*
|
||||||
|
* @param newBaseBranch
|
||||||
|
* the name of the new base branch
|
||||||
|
* @throws IOException
|
||||||
|
* the io exception
|
||||||
|
* @return the updated pull request
|
||||||
|
*/
|
||||||
|
public GHPullRequest setBaseBranch(String newBaseBranch) throws IOException {
|
||||||
|
return root.createRequest()
|
||||||
|
.method("PATCH")
|
||||||
|
.with("base", newBaseBranch)
|
||||||
|
.withUrlPath(getApiRoute())
|
||||||
|
.fetch(GHPullRequest.class)
|
||||||
|
.wrapUp(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the branch. The same as pressing the button in the web GUI.
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
* the io exception
|
||||||
|
*/
|
||||||
|
@Preview
|
||||||
|
@Deprecated
|
||||||
|
public void updateBranch() throws IOException {
|
||||||
|
root.createRequest()
|
||||||
|
.withPreview(LYDIAN)
|
||||||
|
.method("PUT")
|
||||||
|
.with("expected_head_sha", head.getSha())
|
||||||
|
.withUrlPath(getApiRoute() + "/update-branch")
|
||||||
|
.send();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Merge this pull request.
|
* Merge this pull request.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -611,10 +648,4 @@ public class GHPullRequest extends GHIssue implements Refreshable {
|
|||||||
MERGE, SQUASH, REBASE
|
MERGE, SQUASH, REBASE
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fetchIssue() throws IOException {
|
|
||||||
if (!fetchedIssueDetails) {
|
|
||||||
root.createRequest().withUrlPath(getIssuesApiRoute()).fetchInto(this);
|
|
||||||
fetchedIssueDetails = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
86
src/main/java/org/kohsuke/github/GHPullRequestChanges.java
Normal file
86
src/main/java/org/kohsuke/github/GHPullRequestChanges.java
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper to define changed fields on pull_request action="edited"
|
||||||
|
*
|
||||||
|
* @see GHEventPayload.PullRequest
|
||||||
|
*/
|
||||||
|
@SuppressFBWarnings("UWF_UNWRITTEN_FIELD")
|
||||||
|
public class GHPullRequestChanges {
|
||||||
|
|
||||||
|
private GHCommitPointer base;
|
||||||
|
private GHFrom title;
|
||||||
|
private GHFrom body;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Old target branch for pull request.
|
||||||
|
*
|
||||||
|
* @return old target branch info (or null if not changed)
|
||||||
|
*/
|
||||||
|
public GHCommitPointer getBase() {
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Old pull request title.
|
||||||
|
*
|
||||||
|
* @return old pull request title (or null if not changed)
|
||||||
|
*/
|
||||||
|
public GHFrom getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Old pull request body.
|
||||||
|
*
|
||||||
|
* @return old pull request body (or null if not changed)
|
||||||
|
*/
|
||||||
|
public GHFrom getBody() {
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see org.kohsuke.github.GHCommitPointer
|
||||||
|
*/
|
||||||
|
public static class GHCommitPointer {
|
||||||
|
private GHFrom ref;
|
||||||
|
private GHFrom sha;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Named ref to the commit. This (from value) appears to be a "short ref" that doesn't include "refs/heads/"
|
||||||
|
* portion.
|
||||||
|
*
|
||||||
|
* @return the ref
|
||||||
|
*/
|
||||||
|
public GHFrom getRef() {
|
||||||
|
return ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SHA1 of the commit.
|
||||||
|
*
|
||||||
|
* @return sha
|
||||||
|
*/
|
||||||
|
public GHFrom getSha() {
|
||||||
|
return sha;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper for changed values.
|
||||||
|
*/
|
||||||
|
public static class GHFrom {
|
||||||
|
private String from;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Previous value that was changed.
|
||||||
|
*
|
||||||
|
* @return previous value
|
||||||
|
*/
|
||||||
|
public String getFrom() {
|
||||||
|
return from;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -75,7 +75,7 @@ public class GHPullRequestCommitDetail {
|
|||||||
* @return the url
|
* @return the url
|
||||||
*/
|
*/
|
||||||
public URL getUrl() {
|
public URL getUrl() {
|
||||||
return GitHub.parseURL(url);
|
return GitHubClient.parseURL(url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,7 +125,7 @@ public class GHPullRequestCommitDetail {
|
|||||||
* @return the url
|
* @return the url
|
||||||
*/
|
*/
|
||||||
public URL getUrl() {
|
public URL getUrl() {
|
||||||
return GitHub.parseURL(url);
|
return GitHubClient.parseURL(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -161,7 +161,7 @@ public class GHPullRequestCommitDetail {
|
|||||||
* @return the url
|
* @return the url
|
||||||
*/
|
*/
|
||||||
public URL getUrl() {
|
public URL getUrl() {
|
||||||
return GitHub.parseURL(url);
|
return GitHubClient.parseURL(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -170,7 +170,7 @@ public class GHPullRequestCommitDetail {
|
|||||||
* @return the html url
|
* @return the html url
|
||||||
*/
|
*/
|
||||||
public URL getHtml_url() {
|
public URL getHtml_url() {
|
||||||
return GitHub.parseURL(html_url);
|
return GitHubClient.parseURL(html_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -214,7 +214,7 @@ public class GHPullRequestCommitDetail {
|
|||||||
* @return the api url
|
* @return the api url
|
||||||
*/
|
*/
|
||||||
public URL getApiUrl() {
|
public URL getApiUrl() {
|
||||||
return GitHub.parseURL(url);
|
return GitHubClient.parseURL(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -223,7 +223,7 @@ public class GHPullRequestCommitDetail {
|
|||||||
* @return the url
|
* @return the url
|
||||||
*/
|
*/
|
||||||
public URL getUrl() {
|
public URL getUrl() {
|
||||||
return GitHub.parseURL(html_url);
|
return GitHubClient.parseURL(html_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -232,7 +232,7 @@ public class GHPullRequestCommitDetail {
|
|||||||
* @return the comments url
|
* @return the comments url
|
||||||
*/
|
*/
|
||||||
public URL getCommentsUrl() {
|
public URL getCommentsUrl() {
|
||||||
return GitHub.parseURL(comments_url);
|
return GitHubClient.parseURL(comments_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ public class GHPullRequestFileDetail {
|
|||||||
* @return the blob url
|
* @return the blob url
|
||||||
*/
|
*/
|
||||||
public URL getBlobUrl() {
|
public URL getBlobUrl() {
|
||||||
return GitHub.parseURL(blob_url);
|
return GitHubClient.parseURL(blob_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -114,7 +114,7 @@ public class GHPullRequestFileDetail {
|
|||||||
* @return the raw url
|
* @return the raw url
|
||||||
*/
|
*/
|
||||||
public URL getRawUrl() {
|
public URL getRawUrl() {
|
||||||
return GitHub.parseURL(raw_url);
|
return GitHubClient.parseURL(raw_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -123,7 +123,7 @@ public class GHPullRequestFileDetail {
|
|||||||
* @return the contents url
|
* @return the contents url
|
||||||
*/
|
*/
|
||||||
public URL getContentsUrl() {
|
public URL getContentsUrl() {
|
||||||
return GitHub.parseURL(contents_url);
|
return GitHubClient.parseURL(contents_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ public class GHPullRequestReview extends GHObject {
|
|||||||
private String commit_id;
|
private String commit_id;
|
||||||
private GHPullRequestReviewState state;
|
private GHPullRequestReviewState state;
|
||||||
private String submitted_at;
|
private String submitted_at;
|
||||||
|
private String html_url;
|
||||||
|
|
||||||
GHPullRequestReview wrapUp(GHPullRequest owner) {
|
GHPullRequestReview wrapUp(GHPullRequest owner) {
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
@@ -102,7 +103,7 @@ public class GHPullRequestReview extends GHObject {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public URL getHtmlUrl() {
|
public URL getHtmlUrl() {
|
||||||
return null;
|
return GitHubClient.parseURL(html_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -111,7 +112,7 @@ public class GHPullRequestReview extends GHObject {
|
|||||||
* @return the api route
|
* @return the api route
|
||||||
*/
|
*/
|
||||||
protected String getApiRoute() {
|
protected String getApiRoute() {
|
||||||
return owner.getApiRoute() + "/reviews/" + id;
|
return owner.getApiRoute() + "/reviews/" + getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -122,7 +123,7 @@ public class GHPullRequestReview extends GHObject {
|
|||||||
* the io exception
|
* the io exception
|
||||||
*/
|
*/
|
||||||
public Date getSubmittedAt() throws IOException {
|
public Date getSubmittedAt() throws IOException {
|
||||||
return GitHub.parseDate(submitted_at);
|
return GitHubClient.parseDate(submitted_at);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -145,6 +146,7 @@ public class GHPullRequestReview extends GHObject {
|
|||||||
* @deprecated Former preview method that changed when it got public. Left here for backward compatibility. Use
|
* @deprecated Former preview method that changed when it got public. Left here for backward compatibility. Use
|
||||||
* {@link #submit(String, GHPullRequestReviewEvent)}
|
* {@link #submit(String, GHPullRequestReviewEvent)}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void submit(String body, GHPullRequestReviewState state) throws IOException {
|
public void submit(String body, GHPullRequestReviewState state) throws IOException {
|
||||||
submit(body, state.toEvent());
|
submit(body, state.toEvent());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ public class GHPullRequestReviewComment extends GHObject implements Reactable {
|
|||||||
private String body;
|
private String body;
|
||||||
private GHUser user;
|
private GHUser user;
|
||||||
private String path;
|
private String path;
|
||||||
|
private String html_url;
|
||||||
private int position = -1;
|
private int position = -1;
|
||||||
private int original_position = -1;
|
private int original_position = -1;
|
||||||
private long in_reply_to_id = -1L;
|
private long in_reply_to_id = -1L;
|
||||||
@@ -60,6 +61,7 @@ public class GHPullRequestReviewComment extends GHObject implements Reactable {
|
|||||||
* @return the gh pull request review comment
|
* @return the gh pull request review comment
|
||||||
* @deprecated You should be using {@link GHPullRequestReviewBuilder#comment(String, String, int)}
|
* @deprecated You should be using {@link GHPullRequestReviewBuilder#comment(String, String, int)}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public static GHPullRequestReviewComment draft(String body, String path, int position) {
|
public static GHPullRequestReviewComment draft(String body, String path, int position) {
|
||||||
GHPullRequestReviewComment result = new GHPullRequestReviewComment();
|
GHPullRequestReviewComment result = new GHPullRequestReviewComment();
|
||||||
result.body = body;
|
result.body = body;
|
||||||
@@ -142,7 +144,7 @@ public class GHPullRequestReviewComment extends GHObject implements Reactable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public URL getHtmlUrl() {
|
public URL getHtmlUrl() {
|
||||||
return null;
|
return GitHubClient.parseURL(html_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -151,7 +153,7 @@ public class GHPullRequestReviewComment extends GHObject implements Reactable {
|
|||||||
* @return the api route
|
* @return the api route
|
||||||
*/
|
*/
|
||||||
protected String getApiRoute() {
|
protected String getApiRoute() {
|
||||||
return "/repos/" + owner.getRepository().getFullName() + "/pulls/comments/" + id;
|
return "/repos/" + owner.getRepository().getFullName() + "/pulls/comments/" + getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
package org.kohsuke.github;
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JacksonInject;
|
||||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.time.format.DateTimeParseException;
|
import java.time.format.DateTimeParseException;
|
||||||
@@ -28,7 +30,7 @@ public class GHRateLimit {
|
|||||||
/**
|
/**
|
||||||
* Remaining calls that can be made.
|
* Remaining calls that can be made.
|
||||||
*
|
*
|
||||||
* @deprecated This value should never have been made public. Use {@link #getRemaining()}
|
* @deprecated This field should never have been made public. Use {@link #getRemaining()}
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public int remaining;
|
public int remaining;
|
||||||
@@ -36,7 +38,7 @@ public class GHRateLimit {
|
|||||||
/**
|
/**
|
||||||
* Allotted API call per hour.
|
* Allotted API call per hour.
|
||||||
*
|
*
|
||||||
* @deprecated This value should never have been made public. Use {@link #getLimit()}
|
* @deprecated This field should never have been made public. Use {@link #getLimit()}
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public int limit;
|
public int limit;
|
||||||
@@ -47,7 +49,7 @@ public class GHRateLimit {
|
|||||||
* date. To use this field in any meaningful way, it must be converted to a long using {@link Date#getTime()}
|
* date. To use this field in any meaningful way, it must be converted to a long using {@link Date#getTime()}
|
||||||
* multiplied by 1000.
|
* multiplied by 1000.
|
||||||
*
|
*
|
||||||
* @deprecated This value should never have been made public. Use {@link #getResetDate()}
|
* @deprecated This field should never have been made public. Use {@link #getResetDate()}
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public Date reset;
|
public Date reset;
|
||||||
@@ -64,17 +66,58 @@ public class GHRateLimit {
|
|||||||
@Nonnull
|
@Nonnull
|
||||||
private final Record integrationManifest;
|
private final Record integrationManifest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default GHRateLimit provided to new {@link GitHubClient}s.
|
||||||
|
*
|
||||||
|
* Contains all expired records that will cause {@link GitHubClient#rateLimit(RateLimitTarget)} to refresh with new
|
||||||
|
* data when called.
|
||||||
|
*
|
||||||
|
* Private, but made internal for testing.
|
||||||
|
*/
|
||||||
@Nonnull
|
@Nonnull
|
||||||
static GHRateLimit Unknown() {
|
static final GHRateLimit DEFAULT = new GHRateLimit(UnknownLimitRecord.DEFAULT,
|
||||||
return new GHRateLimit(new UnknownLimitRecord(),
|
UnknownLimitRecord.DEFAULT,
|
||||||
new UnknownLimitRecord(),
|
UnknownLimitRecord.DEFAULT,
|
||||||
new UnknownLimitRecord(),
|
UnknownLimitRecord.DEFAULT);
|
||||||
new UnknownLimitRecord());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link GHRateLimit} from a single record for the specified endpoint with place holders for other
|
||||||
|
* records.
|
||||||
|
*
|
||||||
|
* This is used to create {@link GHRateLimit} instances that can merged with other instances.
|
||||||
|
*
|
||||||
|
* @param record
|
||||||
|
* the rate limit record. Can be a regular {@link Record} constructed from header information or an
|
||||||
|
* {@link UnknownLimitRecord} placeholder.
|
||||||
|
* @param rateLimitTarget
|
||||||
|
* which rate limit record to fill
|
||||||
|
* @return a new {@link GHRateLimit} instance containing the supplied record
|
||||||
|
*/
|
||||||
@Nonnull
|
@Nonnull
|
||||||
static GHRateLimit fromHeaderRecord(Record header) {
|
static GHRateLimit fromRecord(@Nonnull Record record, @Nonnull RateLimitTarget rateLimitTarget) {
|
||||||
return new GHRateLimit(header, new UnknownLimitRecord(), new UnknownLimitRecord(), new UnknownLimitRecord());
|
if (rateLimitTarget == RateLimitTarget.CORE || rateLimitTarget == RateLimitTarget.NONE) {
|
||||||
|
return new GHRateLimit(record,
|
||||||
|
UnknownLimitRecord.DEFAULT,
|
||||||
|
UnknownLimitRecord.DEFAULT,
|
||||||
|
UnknownLimitRecord.DEFAULT);
|
||||||
|
} else if (rateLimitTarget == RateLimitTarget.SEARCH) {
|
||||||
|
return new GHRateLimit(UnknownLimitRecord.DEFAULT,
|
||||||
|
record,
|
||||||
|
UnknownLimitRecord.DEFAULT,
|
||||||
|
UnknownLimitRecord.DEFAULT);
|
||||||
|
} else if (rateLimitTarget == RateLimitTarget.GRAPHQL) {
|
||||||
|
return new GHRateLimit(UnknownLimitRecord.DEFAULT,
|
||||||
|
UnknownLimitRecord.DEFAULT,
|
||||||
|
record,
|
||||||
|
UnknownLimitRecord.DEFAULT);
|
||||||
|
} else if (rateLimitTarget == RateLimitTarget.INTEGRATION_MANIFEST) {
|
||||||
|
return new GHRateLimit(UnknownLimitRecord.DEFAULT,
|
||||||
|
UnknownLimitRecord.DEFAULT,
|
||||||
|
UnknownLimitRecord.DEFAULT,
|
||||||
|
record);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Unknown rate limit target: " + rateLimitTarget.toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonCreator
|
@JsonCreator
|
||||||
@@ -141,7 +184,7 @@ public class GHRateLimit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the rate limit reset date for this instance has passed.
|
* Whether the reset date for the Core API rate limit has passed.
|
||||||
*
|
*
|
||||||
* @return true if the rate limit reset date has passed. Otherwise false.
|
* @return true if the rate limit reset date has passed. Otherwise false.
|
||||||
* @since 1.100
|
* @since 1.100
|
||||||
@@ -151,7 +194,7 @@ public class GHRateLimit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The core object provides your rate limit status for all non-search-related resources in the REST API.
|
* The core object provides the rate limit status for all non-search-related resources in the REST API.
|
||||||
*
|
*
|
||||||
* @return a rate limit record
|
* @return a rate limit record
|
||||||
* @since 1.100
|
* @since 1.100
|
||||||
@@ -162,42 +205,43 @@ public class GHRateLimit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The search object provides your rate limit status for the Search API. TODO: integrate with header limit updating.
|
* The search record provides the rate limit status for the Search API.
|
||||||
* Issue #605.
|
|
||||||
*
|
*
|
||||||
* @return a rate limit record
|
* @return a rate limit record
|
||||||
|
* @since 1.115
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
@Nonnull
|
||||||
Record getSearch() {
|
public Record getSearch() {
|
||||||
return search;
|
return search;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The graphql object provides your rate limit status for the GraphQL API. TODO: integrate with header limit
|
* The graphql record provides the rate limit status for the GraphQL API.
|
||||||
* updating. Issue #605.
|
|
||||||
*
|
*
|
||||||
* @return a rate limit record
|
* @return a rate limit record
|
||||||
|
* @since 1.115
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
@Nonnull
|
||||||
Record getGraphQL() {
|
public Record getGraphQL() {
|
||||||
return graphql;
|
return graphql;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The integration_manifest object provides your rate limit status for the GitHub App Manifest code conversion
|
* The integration manifest record provides the rate limit status for the GitHub App Manifest code conversion
|
||||||
* endpoint. TODO: integrate with header limit updating. Issue #605.
|
* endpoint.
|
||||||
*
|
*
|
||||||
* @return a rate limit record
|
* @return a rate limit record
|
||||||
|
* @since 1.115
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
@Nonnull
|
||||||
Record getIntegrationManifest() {
|
public Record getIntegrationManifest() {
|
||||||
return integrationManifest;
|
return integrationManifest;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "GHRateLimit {" + "core " + getCore().toString() + "search " + getSearch().toString() + "graphql "
|
return "GHRateLimit {" + "core " + getCore().toString() + ", search " + getSearch().toString() + ", graphql "
|
||||||
+ getGraphQL().toString() + "integrationManifest " + getIntegrationManifest().toString() + '}';
|
+ getGraphQL().toString() + ", integrationManifest " + getIntegrationManifest().toString() + "}";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -219,23 +263,112 @@ public class GHRateLimit {
|
|||||||
return Objects.hash(getCore(), getSearch(), getGraphQL(), getIntegrationManifest());
|
return Objects.hash(getCore(), getSearch(), getGraphQL(), getIntegrationManifest());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merge a {@link GHRateLimit} with another one to create a new {@link GHRateLimit} keeping the latest
|
||||||
|
* {@link Record}s from each.
|
||||||
|
*
|
||||||
|
* @param newLimit
|
||||||
|
* {@link GHRateLimit} with potentially updated {@link Record}s.
|
||||||
|
* @return a merged {@link GHRateLimit} with the latest {@link Record}s from these two instances. If the merged
|
||||||
|
* instance is equal to the current instance, the current instance is returned.
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
GHRateLimit getMergedRateLimit(@Nonnull GHRateLimit newLimit) {
|
||||||
|
|
||||||
|
GHRateLimit merged = new GHRateLimit(getCore().currentOrUpdated(newLimit.getCore()),
|
||||||
|
getSearch().currentOrUpdated(newLimit.getSearch()),
|
||||||
|
getGraphQL().currentOrUpdated(newLimit.getGraphQL()),
|
||||||
|
getIntegrationManifest().currentOrUpdated(newLimit.getIntegrationManifest()));
|
||||||
|
|
||||||
|
if (merged.equals(this)) {
|
||||||
|
merged = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
return merged;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the specified {@link Record}.
|
||||||
|
*
|
||||||
|
* {@link RateLimitTarget#NONE} will return {@link UnknownLimitRecord#DEFAULT} to prevent any clients from
|
||||||
|
* accidentally waiting on that record to reset before continuing.
|
||||||
|
*
|
||||||
|
* @param rateLimitTarget
|
||||||
|
* the target rate limit record
|
||||||
|
* @return the target {@link Record} from this instance.
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
Record getRecord(@Nonnull RateLimitTarget rateLimitTarget) {
|
||||||
|
if (rateLimitTarget == RateLimitTarget.CORE) {
|
||||||
|
return getCore();
|
||||||
|
} else if (rateLimitTarget == RateLimitTarget.SEARCH) {
|
||||||
|
return getSearch();
|
||||||
|
} else if (rateLimitTarget == RateLimitTarget.GRAPHQL) {
|
||||||
|
return getGraphQL();
|
||||||
|
} else if (rateLimitTarget == RateLimitTarget.INTEGRATION_MANIFEST) {
|
||||||
|
return getIntegrationManifest();
|
||||||
|
} else if (rateLimitTarget == RateLimitTarget.NONE) {
|
||||||
|
return UnknownLimitRecord.DEFAULT;
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Unknown rate limit target: " + rateLimitTarget.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A limit record used as a placeholder when the the actual limit is not known.
|
* A limit record used as a placeholder when the the actual limit is not known.
|
||||||
* <p>
|
|
||||||
* Has a large limit and long duration so that it will doesn't expire too often.
|
|
||||||
*
|
*
|
||||||
* @since 1.100
|
* @since 1.100
|
||||||
*/
|
*/
|
||||||
public static class UnknownLimitRecord extends Record {
|
public static class UnknownLimitRecord extends Record {
|
||||||
|
|
||||||
// One hour
|
private static final long defaultUnknownLimitResetSeconds = Duration.ofSeconds(30).getSeconds();
|
||||||
private static final long unknownLimitResetSeconds = 60L * 60L;
|
|
||||||
|
/**
|
||||||
|
* The number of seconds until a {@link UnknownLimitRecord} will expire.
|
||||||
|
*
|
||||||
|
* This is set to a somewhat short duration, rather than a long one. This avoids
|
||||||
|
* {@link {@link GitHubClient#rateLimit(RateLimitTarget)}} requesting rate limit updates continuously, but also
|
||||||
|
* avoids holding on to stale unknown records indefinitely.
|
||||||
|
*
|
||||||
|
* When merging {@link GHRateLimit} instances, {@link UnknownLimitRecord}s will be superseded by incoming
|
||||||
|
* regular {@link Record}s.
|
||||||
|
*
|
||||||
|
* @see GHRateLimit#getMergedRateLimit(GHRateLimit)
|
||||||
|
*/
|
||||||
|
static long unknownLimitResetSeconds = defaultUnknownLimitResetSeconds;
|
||||||
|
|
||||||
static final int unknownLimit = 1000000;
|
static final int unknownLimit = 1000000;
|
||||||
static final int unknownRemaining = 999999;
|
static final int unknownRemaining = 999999;
|
||||||
|
|
||||||
private UnknownLimitRecord() {
|
// The default UnknownLimitRecord is an expired record.
|
||||||
super(unknownLimit, unknownRemaining, System.currentTimeMillis() / 1000L + unknownLimitResetSeconds);
|
private static final UnknownLimitRecord DEFAULT = new UnknownLimitRecord(Long.MIN_VALUE);
|
||||||
|
|
||||||
|
// The starting current UnknownLimitRecord is an expired record.
|
||||||
|
private static UnknownLimitRecord current = DEFAULT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new unknown record that resets at the specified time.
|
||||||
|
*
|
||||||
|
* @param resetEpochSeconds
|
||||||
|
* the epoch second time when this record will expire.
|
||||||
|
*/
|
||||||
|
private UnknownLimitRecord(long resetEpochSeconds) {
|
||||||
|
super(unknownLimit, unknownRemaining, resetEpochSeconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
static synchronized Record current() {
|
||||||
|
if (current.isExpired()) {
|
||||||
|
current = new UnknownLimitRecord(System.currentTimeMillis() / 1000L + unknownLimitResetSeconds);
|
||||||
|
}
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the current UnknownLimitRecord. For use during testing only.
|
||||||
|
*/
|
||||||
|
static synchronized void reset() {
|
||||||
|
current = DEFAULT;
|
||||||
|
unknownLimitResetSeconds = defaultUnknownLimitResetSeconds;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,7 +384,7 @@ public class GHRateLimit {
|
|||||||
private final int remaining;
|
private final int remaining;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allotted API call per hour.
|
* Allotted API call per time period.
|
||||||
*/
|
*/
|
||||||
private final int limit;
|
private final int limit;
|
||||||
|
|
||||||
@@ -266,11 +399,14 @@ public class GHRateLimit {
|
|||||||
private final long createdAtEpochSeconds = System.currentTimeMillis() / 1000;
|
private final long createdAtEpochSeconds = System.currentTimeMillis() / 1000;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The calculated time at which the rate limit will reset. Recalculated if {@link #recalculateResetDate} is
|
* The date at which the rate limit will reset, adjusted to local machine time if the local machine's clock not
|
||||||
* called.
|
* synchronized with to the same clock as the GitHub server.
|
||||||
|
*
|
||||||
|
* @see #calculateResetDate(String)
|
||||||
|
* @see #getResetDate()
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
@Nonnull
|
||||||
private Date resetDate;
|
private final Date resetDate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instantiates a new Record.
|
* Instantiates a new Record.
|
||||||
@@ -282,7 +418,6 @@ public class GHRateLimit {
|
|||||||
* @param resetEpochSeconds
|
* @param resetEpochSeconds
|
||||||
* the reset epoch seconds
|
* the reset epoch seconds
|
||||||
*/
|
*/
|
||||||
@JsonCreator
|
|
||||||
public Record(@JsonProperty(value = "limit", required = true) int limit,
|
public Record(@JsonProperty(value = "limit", required = true) int limit,
|
||||||
@JsonProperty(value = "remaining", required = true) int remaining,
|
@JsonProperty(value = "remaining", required = true) int remaining,
|
||||||
@JsonProperty(value = "reset", required = true) long resetEpochSeconds) {
|
@JsonProperty(value = "reset", required = true) long resetEpochSeconds) {
|
||||||
@@ -290,7 +425,7 @@ public class GHRateLimit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instantiates a new Record.
|
* Instantiates a new Record. Called by Jackson data binding or during header parsing.
|
||||||
*
|
*
|
||||||
* @param limit
|
* @param limit
|
||||||
* the limit
|
* the limit
|
||||||
@@ -298,26 +433,99 @@ public class GHRateLimit {
|
|||||||
* the remaining
|
* the remaining
|
||||||
* @param resetEpochSeconds
|
* @param resetEpochSeconds
|
||||||
* the reset epoch seconds
|
* the reset epoch seconds
|
||||||
* @param updatedAt
|
* @param responseInfo
|
||||||
* the updated at
|
* the response info
|
||||||
*/
|
*/
|
||||||
@SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD", justification = "Deprecated")
|
@JsonCreator
|
||||||
public Record(int limit, int remaining, long resetEpochSeconds, @CheckForNull String updatedAt) {
|
Record(@JsonProperty(value = "limit", required = true) int limit,
|
||||||
|
@JsonProperty(value = "remaining", required = true) int remaining,
|
||||||
|
@JsonProperty(value = "reset", required = true) long resetEpochSeconds,
|
||||||
|
@JacksonInject @CheckForNull GitHubResponse.ResponseInfo responseInfo) {
|
||||||
this.limit = limit;
|
this.limit = limit;
|
||||||
this.remaining = remaining;
|
this.remaining = remaining;
|
||||||
this.resetEpochSeconds = resetEpochSeconds;
|
this.resetEpochSeconds = resetEpochSeconds;
|
||||||
this.resetDate = recalculateResetDate(updatedAt);
|
String updatedAt = null;
|
||||||
|
if (responseInfo != null) {
|
||||||
|
updatedAt = responseInfo.headerField("Date");
|
||||||
|
}
|
||||||
|
this.resetDate = calculateResetDate(updatedAt);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recalculates the reset date using the server response date to calculate a time duration and then add that to
|
* Determine if the current {@link Record} is outdated compared to another. Rate Limit dates are only accurate
|
||||||
* the local created time for this record.
|
* to the second, so we look at other information in the record as well.
|
||||||
|
*
|
||||||
|
* {@link Record}s with earlier {@link #getResetEpochSeconds()} are replaced by those with later.
|
||||||
|
* {@link Record}s with the same {@link #getResetEpochSeconds()} are replaced by those with less remaining
|
||||||
|
* count.
|
||||||
|
*
|
||||||
|
* {@link UnknownLimitRecord}s compare with each other like regular {@link Record}s.
|
||||||
|
*
|
||||||
|
* {@link Record}s are replaced by {@link UnknownLimitRecord}s only when the current {@link Record} is expired
|
||||||
|
* and the {@link UnknownLimitRecord} is not. Otherwise Regular {@link Record}s are not replaced by
|
||||||
|
* {@link UnknownLimitRecord}s.
|
||||||
|
*
|
||||||
|
* Expiration is only considered after other checks, meaning expired records may sometimes be replaced by other
|
||||||
|
* expired records.
|
||||||
|
*
|
||||||
|
* @param other
|
||||||
|
* the other {@link Record}
|
||||||
|
* @return the {@link Record} that is most current
|
||||||
|
*/
|
||||||
|
Record currentOrUpdated(@Nonnull Record other) {
|
||||||
|
// This set of checks avoids most calls to isExpired()
|
||||||
|
// Depends on UnknownLimitRecord.current() to prevent continuous updating of GHRateLimit rateLimit()
|
||||||
|
if (getResetEpochSeconds() > other.getResetEpochSeconds()
|
||||||
|
|| (getResetEpochSeconds() == other.getResetEpochSeconds()
|
||||||
|
&& getRemaining() <= other.getRemaining())) {
|
||||||
|
// If the current record has a later reset
|
||||||
|
// or the current record has the same reset and fewer or same requests remaining
|
||||||
|
// Then it is most recent
|
||||||
|
return this;
|
||||||
|
} else if (!(other instanceof UnknownLimitRecord)) {
|
||||||
|
// If the above is not the case that means other has a later reset
|
||||||
|
// or the same resent and fewer requests remaining.
|
||||||
|
// If the other record is not an unknown record, the the other is more recent
|
||||||
|
return other;
|
||||||
|
} else if (this.isExpired() && !other.isExpired()) {
|
||||||
|
// The other is an unknown record.
|
||||||
|
// If the current record has expired and the other hasn't, return the other.
|
||||||
|
return other;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If none of the above, the current record is most valid.
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recalculates the {@link #resetDate} relative to the local machine clock.
|
||||||
|
* <p>
|
||||||
|
* {@link RateLimitChecker}s and {@link RateLimitHandler}s use {@link #getResetDate()} to make decisions about
|
||||||
|
* how long to wait for until for the rate limit to reset. That means that {@link #getResetDate()} needs to be
|
||||||
|
* calculated based on the local machine clock.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* When we say that the clock on two machines is "synchronized", we mean that the UTC time returned from
|
||||||
|
* {@link System#currentTimeMillis()} on each machine is basically the same. For the purposes of rate limits an
|
||||||
|
* differences of up to a second can be ignored.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* When the clock on the local machine is synchronized to the same time as the clock on the GitHub server (via a
|
||||||
|
* time service for example), the {@link #resetDate} generated directly from {@link #resetEpochSeconds} will be
|
||||||
|
* accurate for the local machine as well.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* When the clock on the local machine is not synchronized with the server, the {@link #resetDate} must be
|
||||||
|
* recalculated relative to the local machine clock. This is done by taking the number of seconds between the
|
||||||
|
* response "Date" header and {@link #resetEpochSeconds} and then adding that to this record's
|
||||||
|
* {@link #createdAtEpochSeconds}.
|
||||||
*
|
*
|
||||||
* @param updatedAt
|
* @param updatedAt
|
||||||
* a string date in RFC 1123
|
* a string date in RFC 1123
|
||||||
* @return reset date based on the passed date
|
* @return reset date based on the passed date
|
||||||
*/
|
*/
|
||||||
Date recalculateResetDate(@CheckForNull String updatedAt) {
|
@Nonnull
|
||||||
|
private Date calculateResetDate(@CheckForNull String updatedAt) {
|
||||||
long updatedAtEpochSeconds = createdAtEpochSeconds;
|
long updatedAtEpochSeconds = createdAtEpochSeconds;
|
||||||
if (!StringUtils.isBlank(updatedAt)) {
|
if (!StringUtils.isBlank(updatedAt)) {
|
||||||
try {
|
try {
|
||||||
@@ -334,7 +542,7 @@ public class GHRateLimit {
|
|||||||
// This may seem odd but it results in an accurate or slightly pessimistic reset date
|
// This may seem odd but it results in an accurate or slightly pessimistic reset date
|
||||||
// based on system time rather than assuming the system time synchronized with the server
|
// based on system time rather than assuming the system time synchronized with the server
|
||||||
long calculatedSecondsUntilReset = resetEpochSeconds - updatedAtEpochSeconds;
|
long calculatedSecondsUntilReset = resetEpochSeconds - updatedAtEpochSeconds;
|
||||||
return resetDate = new Date((createdAtEpochSeconds + calculatedSecondsUntilReset) * 1000);
|
return new Date((createdAtEpochSeconds + calculatedSecondsUntilReset) * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -358,7 +566,12 @@ public class GHRateLimit {
|
|||||||
/**
|
/**
|
||||||
* Gets the time in epoch seconds when the rate limit will reset.
|
* Gets the time in epoch seconds when the rate limit will reset.
|
||||||
*
|
*
|
||||||
* @return a long
|
* This is the raw value returned by the server. This value is not adjusted if local machine time is not
|
||||||
|
* synchronized with server time. If attempting to check when the rate limit will reset, use
|
||||||
|
* {@link #getResetDate()} or implement a {@link RateLimitChecker} instead.
|
||||||
|
*
|
||||||
|
* @return a long representing the time in epoch seconds when the rate limit will reset
|
||||||
|
* @see #getResetDate()
|
||||||
*/
|
*/
|
||||||
public long getResetEpochSeconds() {
|
public long getResetEpochSeconds() {
|
||||||
return resetEpochSeconds;
|
return resetEpochSeconds;
|
||||||
@@ -367,6 +580,8 @@ public class GHRateLimit {
|
|||||||
/**
|
/**
|
||||||
* Whether the rate limit reset date indicated by this instance is expired
|
* Whether the rate limit reset date indicated by this instance is expired
|
||||||
*
|
*
|
||||||
|
* If attempting to wait for the rate limit to reset, consider implementing a {@link RateLimitChecker} instead.
|
||||||
|
*
|
||||||
* @return true if the rate limit reset date has passed. Otherwise false.
|
* @return true if the rate limit reset date has passed. Otherwise false.
|
||||||
*/
|
*/
|
||||||
public boolean isExpired() {
|
public boolean isExpired() {
|
||||||
@@ -374,7 +589,10 @@ public class GHRateLimit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the date at which the rate limit will reset.
|
* The date at which the rate limit will reset, adjusted to local machine time if the local machine's clock not
|
||||||
|
* synchronized with to the same clock as the GitHub server.
|
||||||
|
*
|
||||||
|
* If attempting to wait for the rate limit to reset, consider implementing a {@link RateLimitChecker} instead.
|
||||||
*
|
*
|
||||||
* @return the calculated date at which the rate limit has or will reset.
|
* @return the calculated date at which the rate limit has or will reset.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -58,6 +58,6 @@ public class GHReaction extends GHObject {
|
|||||||
* the io exception
|
* the io exception
|
||||||
*/
|
*/
|
||||||
public void delete() throws IOException {
|
public void delete() throws IOException {
|
||||||
root.createRequest().method("DELETE").withPreview(SQUIRREL_GIRL).withUrlPath("/reactions/" + id).send();
|
root.createRequest().method("DELETE").withPreview(SQUIRREL_GIRL).withUrlPath("/reactions/" + getId()).send();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.kohsuke.github;
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -31,7 +32,7 @@ public class GHRef {
|
|||||||
* @return the url
|
* @return the url
|
||||||
*/
|
*/
|
||||||
public URL getUrl() {
|
public URL getUrl() {
|
||||||
return GitHub.parseURL(url);
|
return GitHubClient.parseURL(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -90,11 +91,76 @@ public class GHRef {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GHRef[] wrap(GHRef[] in, GitHub root) {
|
/**
|
||||||
for (GHRef r : in) {
|
* Retrive a ref of the given type for the current GitHub repository.
|
||||||
r.wrap(root);
|
*
|
||||||
|
* @param repository
|
||||||
|
* the repository to read from
|
||||||
|
* @param refName
|
||||||
|
* eg: heads/branch
|
||||||
|
* @return refs matching the request type
|
||||||
|
* @throws IOException
|
||||||
|
* on failure communicating with GitHub, potentially due to an invalid ref type being requested
|
||||||
|
*/
|
||||||
|
static GHRef read(GHRepository repository, String refName) throws IOException {
|
||||||
|
// Also accept e.g. "refs/heads/branch" for consistency with createRef().
|
||||||
|
if (refName.startsWith("refs/")) {
|
||||||
|
refName = refName.replaceFirst("refs/", "");
|
||||||
}
|
}
|
||||||
return in;
|
|
||||||
|
// We would expect this to use `git/ref/%s` but some versions of GHE seem to not support it
|
||||||
|
// Instead use `git/refs/%s` and check the result actually matches the ref
|
||||||
|
GHRef result = null;
|
||||||
|
try {
|
||||||
|
result = repository.root.createRequest()
|
||||||
|
.withUrlPath(repository.getApiTailUrl(String.format("git/refs/%s", refName)))
|
||||||
|
.fetch(GHRef.class)
|
||||||
|
.wrap(repository.root);
|
||||||
|
} catch (IOException e) {
|
||||||
|
// If the parse exception is due to the above returning an array instead of a single ref
|
||||||
|
// that means the individual ref did not exist. Handled by result check below.
|
||||||
|
// Otherwise, rethrow.
|
||||||
|
if (!(e.getCause() instanceof JsonMappingException)) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that the ref returned is the one requested
|
||||||
|
// Used .endsWith(refName) instead of .equals("refs/" + refName) to workaround a GitBucket
|
||||||
|
// issue where the "ref" field omits the "refs/" prefix. "endsWith()" is functionally
|
||||||
|
// the same for this scenario - the server refs matching is prefix-based, so
|
||||||
|
// a ref that ends with the correct string will always be the correct one.
|
||||||
|
if (result == null || !result.getRef().endsWith(refName)) {
|
||||||
|
throw new GHFileNotFoundException(String.format("git/refs/%s", refName)
|
||||||
|
+ " {\"message\":\"Not Found\",\"documentation_url\":\"https://developer.github.com/v3/git/refs/#get-a-reference\"}");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves all refs of the given type for the current GitHub repository.
|
||||||
|
*
|
||||||
|
* @param repository
|
||||||
|
* the repository to read from
|
||||||
|
* @param refType
|
||||||
|
* the type of reg to search for e.g. <code>tags</code> or <code>commits</code>
|
||||||
|
* @return paged iterable of all refs of the specified type
|
||||||
|
* @throws IOException
|
||||||
|
* on failure communicating with GitHub, potentially due to an invalid ref type being requested
|
||||||
|
*/
|
||||||
|
static PagedIterable<GHRef> readMatching(GHRepository repository, String refType) throws IOException {
|
||||||
|
if (refType.startsWith("refs/")) {
|
||||||
|
refType = refType.replaceFirst("refs/", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
String url = repository.getApiTailUrl(String.format("git/refs/%s", refType));
|
||||||
|
// if no types, do not end with slash just to be safe.
|
||||||
|
if (refType.equals("")) {
|
||||||
|
url = url.substring(0, url.length() - 1);
|
||||||
|
}
|
||||||
|
return repository.root.createRequest()
|
||||||
|
.withUrlPath(url)
|
||||||
|
.toIterable(GHRef[].class, item -> item.wrap(repository.root));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -131,7 +197,7 @@ public class GHRef {
|
|||||||
* @return the url
|
* @return the url
|
||||||
*/
|
*/
|
||||||
public URL getUrl() {
|
public URL getUrl() {
|
||||||
return GitHub.parseURL(url);
|
return GitHubClient.parseURL(url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -16,6 +15,7 @@ import static java.lang.String.*;
|
|||||||
* Release in a github repository.
|
* Release in a github repository.
|
||||||
*
|
*
|
||||||
* @see GHRepository#getReleases() GHRepository#getReleases()
|
* @see GHRepository#getReleases() GHRepository#getReleases()
|
||||||
|
* @see GHRepository#listReleases() () GHRepository#listReleases()
|
||||||
* @see GHRepository#createRelease(String) GHRepository#createRelease(String)
|
* @see GHRepository#createRelease(String) GHRepository#createRelease(String)
|
||||||
*/
|
*/
|
||||||
public class GHRelease extends GHObject {
|
public class GHRelease extends GHObject {
|
||||||
@@ -24,6 +24,7 @@ public class GHRelease extends GHObject {
|
|||||||
|
|
||||||
private String html_url;
|
private String html_url;
|
||||||
private String assets_url;
|
private String assets_url;
|
||||||
|
private List<GHAsset> assets;
|
||||||
private String upload_url;
|
private String upload_url;
|
||||||
private String tag_name;
|
private String tag_name;
|
||||||
private String target_commitish;
|
private String target_commitish;
|
||||||
@@ -72,12 +73,13 @@ public class GHRelease extends GHObject {
|
|||||||
* the io exception
|
* the io exception
|
||||||
* @deprecated Use {@link #update()}
|
* @deprecated Use {@link #update()}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public GHRelease setDraft(boolean draft) throws IOException {
|
public GHRelease setDraft(boolean draft) throws IOException {
|
||||||
return update().draft(draft).update();
|
return update().draft(draft).update();
|
||||||
}
|
}
|
||||||
|
|
||||||
public URL getHtmlUrl() {
|
public URL getHtmlUrl() {
|
||||||
return GitHub.parseURL(html_url);
|
return GitHubClient.parseURL(html_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -249,17 +251,45 @@ public class GHRelease extends GHObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets assets.
|
* Get the cached assets.
|
||||||
|
*
|
||||||
|
* @return the assets
|
||||||
|
*
|
||||||
|
* @deprecated This should be the default behavior of {@link #getAssets()} in a future release. This method is
|
||||||
|
* introduced in addition to enable a transition to using cached asset information while keeping the
|
||||||
|
* existing logic in place for backwards compatibility.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
@Preview
|
||||||
|
public List<GHAsset> assets() {
|
||||||
|
return assets;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Re-fetch the assets of this release.
|
||||||
|
*
|
||||||
|
* @return the assets
|
||||||
|
* @throws IOException
|
||||||
|
* the io exception
|
||||||
|
* @deprecated The behavior of this method will change in a future release. It will then provide cached assets as
|
||||||
|
* provided by {@link #assets()}. Use {@link #listAssets()} instead to fetch up-to-date information of
|
||||||
|
* assets.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public List<GHAsset> getAssets() throws IOException {
|
||||||
|
return listAssets().toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Re-fetch the assets of this release.
|
||||||
*
|
*
|
||||||
* @return the assets
|
* @return the assets
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
* the io exception
|
* the io exception
|
||||||
*/
|
*/
|
||||||
public List<GHAsset> getAssets() throws IOException {
|
public PagedIterable<GHAsset> listAssets() throws IOException {
|
||||||
Requester builder = owner.root.createRequest();
|
Requester builder = owner.root.createRequest();
|
||||||
|
return builder.withUrlPath(getApiTailUrl("assets")).toIterable(GHAsset[].class, item -> item.wrap(this));
|
||||||
GHAsset[] assets = builder.withUrlPath(getApiTailUrl("assets")).fetchArray(GHAsset[].class);
|
|
||||||
return Arrays.asList(GHAsset.wrap(assets, this));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -269,7 +299,7 @@ public class GHRelease extends GHObject {
|
|||||||
* the io exception
|
* the io exception
|
||||||
*/
|
*/
|
||||||
public void delete() throws IOException {
|
public void delete() throws IOException {
|
||||||
root.createRequest().method("DELETE").withUrlPath(owner.getApiTailUrl("releases/" + id)).send();
|
root.createRequest().method("DELETE").withUrlPath(owner.getApiTailUrl("releases/" + getId())).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -282,6 +312,6 @@ public class GHRelease extends GHObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String getApiTailUrl(String end) {
|
private String getApiTailUrl(String end) {
|
||||||
return owner.getApiTailUrl(format("releases/%s/%s", id, end));
|
return owner.getApiTailUrl(format("releases/%s/%s", getId(), end));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ public class GHReleaseUpdater {
|
|||||||
*/
|
*/
|
||||||
public GHRelease update() throws IOException {
|
public GHRelease update() throws IOException {
|
||||||
return builder.method("PATCH")
|
return builder.method("PATCH")
|
||||||
.withUrlPath(base.owner.getApiTailUrl("releases/" + base.id))
|
.withUrlPath(base.owner.getApiTailUrl("releases/" + base.getId()))
|
||||||
.fetch(GHRelease.class)
|
.fetch(GHRelease.class)
|
||||||
.wrap(base.owner);
|
.wrap(base.owner);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,6 @@ class GHRepoHook extends GHHook {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
String getApiRoute() {
|
String getApiRoute() {
|
||||||
return String.format("/repos/%s/%s/hooks/%d", repository.getOwnerName(), repository.getName(), id);
|
return String.format("/repos/%s/%s/hooks/%d", repository.getOwnerName(), repository.getName(), getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,10 @@
|
|||||||
package org.kohsuke.github;
|
package org.kohsuke.github;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.fasterxml.jackson.core.JsonParseException;
|
||||||
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
|
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
|
||||||
|
import edu.umd.cs.findbugs.annotations.CheckForNull;
|
||||||
|
import edu.umd.cs.findbugs.annotations.NonNull;
|
||||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
@@ -37,15 +40,16 @@ import java.io.Reader;
|
|||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.AbstractSet;
|
import java.util.AbstractSet;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
import java.util.WeakHashMap;
|
import java.util.WeakHashMap;
|
||||||
@@ -62,10 +66,12 @@ import static org.kohsuke.github.Previews.*;
|
|||||||
@SuppressFBWarnings(value = { "UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD" },
|
@SuppressFBWarnings(value = { "UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD" },
|
||||||
justification = "JSON API")
|
justification = "JSON API")
|
||||||
public class GHRepository extends GHObject {
|
public class GHRepository extends GHObject {
|
||||||
/* package almost final */ GitHub root;
|
/* package almost final */ transient GitHub root;
|
||||||
|
|
||||||
|
private String nodeId, description, homepage, name, full_name;
|
||||||
|
|
||||||
private String description, homepage, name, full_name;
|
|
||||||
private String html_url; // this is the UI
|
private String html_url; // this is the UI
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The license information makes use of the preview API.
|
* The license information makes use of the preview API.
|
||||||
*
|
*
|
||||||
@@ -74,20 +80,30 @@ public class GHRepository extends GHObject {
|
|||||||
private GHLicense license;
|
private GHLicense license;
|
||||||
|
|
||||||
private String git_url, ssh_url, clone_url, svn_url, mirror_url;
|
private String git_url, ssh_url, clone_url, svn_url, mirror_url;
|
||||||
|
|
||||||
private GHUser owner; // not fully populated. beware.
|
private GHUser owner; // not fully populated. beware.
|
||||||
private boolean has_issues, has_wiki, fork, has_downloads, has_pages, archived;
|
|
||||||
|
private boolean has_issues, has_wiki, fork, has_downloads, has_pages, archived, has_projects;
|
||||||
|
|
||||||
private boolean allow_squash_merge;
|
private boolean allow_squash_merge;
|
||||||
|
|
||||||
private boolean allow_merge_commit;
|
private boolean allow_merge_commit;
|
||||||
|
|
||||||
private boolean allow_rebase_merge;
|
private boolean allow_rebase_merge;
|
||||||
|
|
||||||
|
private boolean delete_branch_on_merge;
|
||||||
|
|
||||||
@JsonProperty("private")
|
@JsonProperty("private")
|
||||||
private boolean _private;
|
private boolean _private;
|
||||||
|
|
||||||
private int forks_count, stargazers_count, watchers_count, size, open_issues_count, subscribers_count;
|
private int forks_count, stargazers_count, watchers_count, size, open_issues_count, subscribers_count;
|
||||||
|
|
||||||
private String pushed_at;
|
private String pushed_at;
|
||||||
|
|
||||||
private Map<Integer, GHMilestone> milestones = new WeakHashMap<Integer, GHMilestone>();
|
private Map<Integer, GHMilestone> milestones = new WeakHashMap<Integer, GHMilestone>();
|
||||||
|
|
||||||
private String default_branch, language;
|
private String default_branch, language;
|
||||||
|
|
||||||
private Map<String, GHCommit> commits = new WeakHashMap<String, GHCommit>();
|
private Map<String, GHCommit> commits = new WeakHashMap<String, GHCommit>();
|
||||||
|
|
||||||
@SkipFromToString
|
@SkipFromToString
|
||||||
@@ -95,6 +111,12 @@ public class GHRepository extends GHObject {
|
|||||||
|
|
||||||
private GHRepository source, parent;
|
private GHRepository source, parent;
|
||||||
|
|
||||||
|
private Boolean isTemplate;
|
||||||
|
|
||||||
|
static GHRepository read(GitHub root, String owner, String name) throws IOException {
|
||||||
|
return root.createRequest().withUrlPath("/repos/" + owner + '/' + name).fetch(GHRepository.class).wrap(root);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create deployment gh deployment builder.
|
* Create deployment gh deployment builder.
|
||||||
*
|
*
|
||||||
@@ -116,6 +138,7 @@ public class GHRepository extends GHObject {
|
|||||||
* the io exception
|
* the io exception
|
||||||
* @deprecated Use {@code getDeployment(id).listStatuses()}
|
* @deprecated Use {@code getDeployment(id).listStatuses()}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public PagedIterable<GHDeploymentStatus> getDeploymentStatuses(final int id) throws IOException {
|
public PagedIterable<GHDeploymentStatus> getDeploymentStatuses(final int id) throws IOException {
|
||||||
return getDeployment(id).listStatuses();
|
return getDeployment(id).listStatuses();
|
||||||
}
|
}
|
||||||
@@ -171,6 +194,7 @@ public class GHRepository extends GHObject {
|
|||||||
* the io exception
|
* the io exception
|
||||||
* @deprecated Use {@code getDeployment(deploymentId).createStatus(ghDeploymentState)}
|
* @deprecated Use {@code getDeployment(deploymentId).createStatus(ghDeploymentState)}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public GHDeploymentStatusBuilder createDeployStatus(int deploymentId, GHDeploymentState ghDeploymentState)
|
public GHDeploymentStatusBuilder createDeployStatus(int deploymentId, GHDeploymentState ghDeploymentState)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
return getDeployment(deploymentId).createStatus(ghDeploymentState);
|
return getDeployment(deploymentId).createStatus(ghDeploymentState);
|
||||||
@@ -180,6 +204,15 @@ public class GHRepository extends GHObject {
|
|||||||
boolean pull, push, admin;
|
boolean pull, push, admin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets node id
|
||||||
|
*
|
||||||
|
* @return the node id
|
||||||
|
*/
|
||||||
|
public String getNodeId() {
|
||||||
|
return nodeId;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets description.
|
* Gets description.
|
||||||
*
|
*
|
||||||
@@ -222,6 +255,7 @@ public class GHRepository extends GHObject {
|
|||||||
* @return the string
|
* @return the string
|
||||||
* @deprecated Typo of {@link #getHttpTransportUrl()}
|
* @deprecated Typo of {@link #getHttpTransportUrl()}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public String gitHttpTransportUrl() {
|
public String gitHttpTransportUrl() {
|
||||||
return clone_url;
|
return clone_url;
|
||||||
}
|
}
|
||||||
@@ -255,7 +289,7 @@ public class GHRepository extends GHObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public URL getHtmlUrl() {
|
public URL getHtmlUrl() {
|
||||||
return GitHub.parseURL(html_url);
|
return GitHubClient.parseURL(html_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -358,7 +392,7 @@ public class GHRepository extends GHObject {
|
|||||||
* the io exception
|
* the io exception
|
||||||
*/
|
*/
|
||||||
public List<GHIssue> getIssues(GHIssueState state) throws IOException {
|
public List<GHIssue> getIssues(GHIssueState state) throws IOException {
|
||||||
return listIssues(state).asList();
|
return listIssues(state).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -376,8 +410,9 @@ public class GHRepository extends GHObject {
|
|||||||
Requester requester = root.createRequest()
|
Requester requester = root.createRequest()
|
||||||
.with("state", state)
|
.with("state", state)
|
||||||
.with("milestone", milestone == null ? "none" : "" + milestone.getNumber());
|
.with("milestone", milestone == null ? "none" : "" + milestone.getNumber());
|
||||||
return Arrays
|
return requester.withUrlPath(getApiTailUrl("issues"))
|
||||||
.asList(GHIssue.wrap(requester.withUrlPath(getApiTailUrl("issues")).fetchArray(GHIssue[].class), this));
|
.toIterable(GHIssue[].class, item -> item.wrap(this))
|
||||||
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -436,7 +471,7 @@ public class GHRepository extends GHObject {
|
|||||||
* @deprecated use {@link #listReleases()}
|
* @deprecated use {@link #listReleases()}
|
||||||
*/
|
*/
|
||||||
public List<GHRelease> getReleases() throws IOException {
|
public List<GHRelease> getReleases() throws IOException {
|
||||||
return listReleases().asList();
|
return listReleases().toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -551,6 +586,15 @@ public class GHRepository extends GHObject {
|
|||||||
return has_issues;
|
return has_issues;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Has projects boolean.
|
||||||
|
*
|
||||||
|
* @return the boolean
|
||||||
|
*/
|
||||||
|
public boolean hasProjects() {
|
||||||
|
return has_projects;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Has wiki boolean.
|
* Has wiki boolean.
|
||||||
*
|
*
|
||||||
@@ -605,13 +649,34 @@ public class GHRepository extends GHObject {
|
|||||||
return allow_rebase_merge;
|
return allow_rebase_merge;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Automatically deleting head branches when pull requests are merged
|
||||||
|
*
|
||||||
|
* @return the boolean
|
||||||
|
*/
|
||||||
|
public boolean isDeleteBranchOnMerge() {
|
||||||
|
return delete_branch_on_merge;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of all forks of this repository. This not only counts direct forks, but also forks of forks,
|
||||||
|
* and so on.
|
||||||
|
*
|
||||||
|
* @return the forks
|
||||||
|
* @deprecated use {@link #getForksCount()} instead
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public int getForks() {
|
||||||
|
return getForksCount();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the number of all forks of this repository. This not only counts direct forks, but also forks of forks,
|
* Returns the number of all forks of this repository. This not only counts direct forks, but also forks of forks,
|
||||||
* and so on.
|
* and so on.
|
||||||
*
|
*
|
||||||
* @return the forks
|
* @return the forks
|
||||||
*/
|
*/
|
||||||
public int getForks() {
|
public int getForksCount() {
|
||||||
return forks_count;
|
return forks_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -633,6 +698,28 @@ public class GHRepository extends GHObject {
|
|||||||
return _private;
|
return _private;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is template boolean.
|
||||||
|
*
|
||||||
|
* @return the boolean
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
@Preview
|
||||||
|
public boolean isTemplate() {
|
||||||
|
// isTemplate is still in preview, we do not want to retrieve it unless needed.
|
||||||
|
if (isTemplate == null) {
|
||||||
|
try {
|
||||||
|
populate();
|
||||||
|
} catch (IOException e) {
|
||||||
|
// Convert this to a runtime exception to avoid messy method signature
|
||||||
|
throw new GHException("Could not populate the template setting of the repository", e);
|
||||||
|
}
|
||||||
|
// if this somehow is not populated, set it to false;
|
||||||
|
isTemplate = Boolean.TRUE.equals(isTemplate);
|
||||||
|
}
|
||||||
|
return isTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Has downloads boolean.
|
* Has downloads boolean.
|
||||||
*
|
*
|
||||||
@@ -655,8 +742,19 @@ public class GHRepository extends GHObject {
|
|||||||
* Gets watchers.
|
* Gets watchers.
|
||||||
*
|
*
|
||||||
* @return the watchers
|
* @return the watchers
|
||||||
|
* @deprecated use {@link #getWatchersCount()} instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public int getWatchers() {
|
public int getWatchers() {
|
||||||
|
return getWatchersCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the count of watchers.
|
||||||
|
*
|
||||||
|
* @return the watchers
|
||||||
|
*/
|
||||||
|
public int getWatchersCount() {
|
||||||
return watchers_count;
|
return watchers_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -669,16 +767,6 @@ public class GHRepository extends GHObject {
|
|||||||
return open_issues_count;
|
return open_issues_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets network count.
|
|
||||||
*
|
|
||||||
* @return the network count
|
|
||||||
* @deprecated This no longer exists in the official API documentation. Use {@link #getForks()}
|
|
||||||
*/
|
|
||||||
public int getNetworkCount() {
|
|
||||||
return forks_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets subscribers count.
|
* Gets subscribers count.
|
||||||
*
|
*
|
||||||
@@ -694,7 +782,7 @@ public class GHRepository extends GHObject {
|
|||||||
* @return null if the repository was never pushed at.
|
* @return null if the repository was never pushed at.
|
||||||
*/
|
*/
|
||||||
public Date getPushedAt() {
|
public Date getPushedAt() {
|
||||||
return GitHub.parseDate(pushed_at);
|
return GitHubClient.parseDate(pushed_at);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -712,6 +800,7 @@ public class GHRepository extends GHObject {
|
|||||||
* @return the master branch
|
* @return the master branch
|
||||||
* @deprecated Renamed to {@link #getDefaultBranch()}
|
* @deprecated Renamed to {@link #getDefaultBranch()}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public String getMasterBranch() {
|
public String getMasterBranch() {
|
||||||
return default_branch;
|
return default_branch;
|
||||||
}
|
}
|
||||||
@@ -734,7 +823,7 @@ public class GHRepository extends GHObject {
|
|||||||
*/
|
*/
|
||||||
@WithBridgeMethods(Set.class)
|
@WithBridgeMethods(Set.class)
|
||||||
public GHPersonSet<GHUser> getCollaborators() throws IOException {
|
public GHPersonSet<GHUser> getCollaborators() throws IOException {
|
||||||
return new GHPersonSet<GHUser>(listCollaborators().asList());
|
return new GHPersonSet<GHUser>(listCollaborators().toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -784,11 +873,14 @@ public class GHRepository extends GHObject {
|
|||||||
* the io exception
|
* the io exception
|
||||||
*/
|
*/
|
||||||
public Set<String> getCollaboratorNames() throws IOException {
|
public Set<String> getCollaboratorNames() throws IOException {
|
||||||
Set<String> r = new HashSet<String>();
|
Set<String> r = new HashSet<>();
|
||||||
for (GHUser u : GHUser.wrap(
|
// no initializer - we just want to the logins
|
||||||
root.createRequest().withUrlPath(getApiTailUrl("collaborators")).fetchArray(GHUser[].class),
|
PagedIterable<GHUser> users = root.createRequest()
|
||||||
root))
|
.withUrlPath(getApiTailUrl("collaborators"))
|
||||||
|
.toIterable(GHUser[].class, null);
|
||||||
|
for (GHUser u : users.toArray()) {
|
||||||
r.add(u.login);
|
r.add(u.login);
|
||||||
|
}
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -813,7 +905,7 @@ public class GHRepository extends GHObject {
|
|||||||
* Obtain permission for a given user in this repository.
|
* Obtain permission for a given user in this repository.
|
||||||
*
|
*
|
||||||
* @param u
|
* @param u
|
||||||
* the u
|
* the user
|
||||||
* @return the permission
|
* @return the permission
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
* the io exception
|
* the io exception
|
||||||
@@ -830,9 +922,25 @@ public class GHRepository extends GHObject {
|
|||||||
* the io exception
|
* the io exception
|
||||||
*/
|
*/
|
||||||
public Set<GHTeam> getTeams() throws IOException {
|
public Set<GHTeam> getTeams() throws IOException {
|
||||||
return Collections.unmodifiableSet(new HashSet<GHTeam>(Arrays.asList(
|
GHOrganization org = root.getOrganization(getOwnerName());
|
||||||
GHTeam.wrapUp(root.createRequest().withUrlPath(getApiTailUrl("teams")).fetchArray(GHTeam[].class),
|
return root.createRequest()
|
||||||
root.getOrganization(getOwnerName())))));
|
.withUrlPath(getApiTailUrl("teams"))
|
||||||
|
.toIterable(GHTeam[].class, item -> item.wrapUp(org))
|
||||||
|
.toSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add collaborators.
|
||||||
|
*
|
||||||
|
* @param users
|
||||||
|
* the users
|
||||||
|
* @param permission
|
||||||
|
* the permission level
|
||||||
|
* @throws IOException
|
||||||
|
* the io exception
|
||||||
|
*/
|
||||||
|
public void addCollaborators(GHOrganization.Permission permission, GHUser... users) throws IOException {
|
||||||
|
addCollaborators(asList(users), permission);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -856,7 +964,21 @@ public class GHRepository extends GHObject {
|
|||||||
* the io exception
|
* the io exception
|
||||||
*/
|
*/
|
||||||
public void addCollaborators(Collection<GHUser> users) throws IOException {
|
public void addCollaborators(Collection<GHUser> users) throws IOException {
|
||||||
modifyCollaborators(users, "PUT");
|
modifyCollaborators(users, "PUT", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add collaborators.
|
||||||
|
*
|
||||||
|
* @param users
|
||||||
|
* the users
|
||||||
|
* @param permission
|
||||||
|
* the permission level
|
||||||
|
* @throws IOException
|
||||||
|
* the io exception
|
||||||
|
*/
|
||||||
|
public void addCollaborators(Collection<GHUser> users, GHOrganization.Permission permission) throws IOException {
|
||||||
|
modifyCollaborators(users, "PUT", permission);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -880,12 +1002,20 @@ public class GHRepository extends GHObject {
|
|||||||
* the io exception
|
* the io exception
|
||||||
*/
|
*/
|
||||||
public void removeCollaborators(Collection<GHUser> users) throws IOException {
|
public void removeCollaborators(Collection<GHUser> users) throws IOException {
|
||||||
modifyCollaborators(users, "DELETE");
|
modifyCollaborators(users, "DELETE", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void modifyCollaborators(Collection<GHUser> users, String method) throws IOException {
|
private void modifyCollaborators(@NonNull Collection<GHUser> users,
|
||||||
for (GHUser user : users) {
|
@NonNull String method,
|
||||||
root.createRequest().method(method).withUrlPath(getApiTailUrl("collaborators/" + user.getLogin())).send();
|
@CheckForNull GHOrganization.Permission permission) throws IOException {
|
||||||
|
Requester requester = root.createRequest().method(method);
|
||||||
|
if (permission != null) {
|
||||||
|
requester = requester.with("permission", permission).inBody();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure that the users collection doesn't have any duplicates
|
||||||
|
for (GHUser user : new LinkedHashSet<GHUser>(users)) {
|
||||||
|
requester.withUrlPath(getApiTailUrl("collaborators/" + user.getLogin())).send();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -911,8 +1041,9 @@ public class GHRepository extends GHObject {
|
|||||||
|
|
||||||
private void edit(String key, String value) throws IOException {
|
private void edit(String key, String value) throws IOException {
|
||||||
Requester requester = root.createRequest();
|
Requester requester = root.createRequest();
|
||||||
if (!key.equals("name"))
|
if (!key.equals("name")) {
|
||||||
requester.with("name", name); // even when we don't change the name, we need to send it in
|
requester.with("name", name); // even when we don't change the name, we need to send it in
|
||||||
|
}
|
||||||
requester.with(key, value).method("PATCH").withUrlPath(getApiTailUrl("")).send();
|
requester.with(key, value).method("PATCH").withUrlPath(getApiTailUrl("")).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -928,6 +1059,18 @@ public class GHRepository extends GHObject {
|
|||||||
edit("has_issues", String.valueOf(v));
|
edit("has_issues", String.valueOf(v));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables or disables projects for this repository.
|
||||||
|
*
|
||||||
|
* @param v
|
||||||
|
* the v
|
||||||
|
* @throws IOException
|
||||||
|
* the io exception
|
||||||
|
*/
|
||||||
|
public void enableProjects(boolean v) throws IOException {
|
||||||
|
edit("has_projects", String.valueOf(v));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enables or disables Wiki for this repository.
|
* Enables or disables Wiki for this repository.
|
||||||
*
|
*
|
||||||
@@ -1048,6 +1191,18 @@ public class GHRepository extends GHObject {
|
|||||||
edit("allow_rebase_merge", Boolean.toString(value));
|
edit("allow_rebase_merge", Boolean.toString(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* After pull requests are merged, you can have head branches deleted automatically.
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* the value
|
||||||
|
* @throws IOException
|
||||||
|
* the io exception
|
||||||
|
*/
|
||||||
|
public void deleteBranchOnMerge(boolean value) throws IOException {
|
||||||
|
edit("delete_branch_on_merge", Boolean.toString(value));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes this repository.
|
* Deletes this repository.
|
||||||
*
|
*
|
||||||
@@ -1133,8 +1288,9 @@ public class GHRepository extends GHObject {
|
|||||||
// this API is asynchronous. we need to wait for a bit
|
// this API is asynchronous. we need to wait for a bit
|
||||||
for (int i = 0; i < 10; i++) {
|
for (int i = 0; i < 10; i++) {
|
||||||
GHRepository r = root.getMyself().getRepository(name);
|
GHRepository r = root.getMyself().getRepository(name);
|
||||||
if (r != null)
|
if (r != null) {
|
||||||
return r;
|
return r;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
Thread.sleep(3000);
|
Thread.sleep(3000);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
@@ -1163,8 +1319,9 @@ public class GHRepository extends GHObject {
|
|||||||
// this API is asynchronous. we need to wait for a bit
|
// this API is asynchronous. we need to wait for a bit
|
||||||
for (int i = 0; i < 10; i++) {
|
for (int i = 0; i < 10; i++) {
|
||||||
GHRepository r = org.getRepository(name);
|
GHRepository r = org.getRepository(name);
|
||||||
if (r != null)
|
if (r != null) {
|
||||||
return r;
|
return r;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
Thread.sleep(3000);
|
Thread.sleep(3000);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
@@ -1202,7 +1359,7 @@ public class GHRepository extends GHObject {
|
|||||||
* @see #listPullRequests(GHIssueState) #listPullRequests(GHIssueState)
|
* @see #listPullRequests(GHIssueState) #listPullRequests(GHIssueState)
|
||||||
*/
|
*/
|
||||||
public List<GHPullRequest> getPullRequests(GHIssueState state) throws IOException {
|
public List<GHPullRequest> getPullRequests(GHIssueState state) throws IOException {
|
||||||
return queryPullRequests().state(state).list().asList();
|
return queryPullRequests().state(state).list().toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1213,6 +1370,7 @@ public class GHRepository extends GHObject {
|
|||||||
* @return the paged iterable
|
* @return the paged iterable
|
||||||
* @deprecated Use {@link #queryPullRequests()}
|
* @deprecated Use {@link #queryPullRequests()}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public PagedIterable<GHPullRequest> listPullRequests(GHIssueState state) {
|
public PagedIterable<GHPullRequest> listPullRequests(GHIssueState state) {
|
||||||
return queryPullRequests().state(state).list();
|
return queryPullRequests().state(state).list();
|
||||||
}
|
}
|
||||||
@@ -1413,9 +1571,7 @@ public class GHRepository extends GHObject {
|
|||||||
* on failure communicating with GitHub
|
* on failure communicating with GitHub
|
||||||
*/
|
*/
|
||||||
public GHRef[] getRefs() throws IOException {
|
public GHRef[] getRefs() throws IOException {
|
||||||
return GHRef.wrap(root.createRequest()
|
return listRefs().toArray();
|
||||||
.withUrlPath(String.format("/repos/%s/%s/git/refs", getOwnerName(), name))
|
|
||||||
.fetchArray(GHRef[].class), root);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1426,8 +1582,7 @@ public class GHRepository extends GHObject {
|
|||||||
* on failure communicating with GitHub, potentially due to an invalid ref type being requested
|
* on failure communicating with GitHub, potentially due to an invalid ref type being requested
|
||||||
*/
|
*/
|
||||||
public PagedIterable<GHRef> listRefs() throws IOException {
|
public PagedIterable<GHRef> listRefs() throws IOException {
|
||||||
final String url = String.format("/repos/%s/%s/git/refs", getOwnerName(), name);
|
return listRefs("");
|
||||||
return root.createRequest().withUrlPath(url).toIterable(GHRef[].class, item -> item.wrap(root));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1440,9 +1595,7 @@ public class GHRepository extends GHObject {
|
|||||||
* on failure communicating with GitHub, potentially due to an invalid ref type being requested
|
* on failure communicating with GitHub, potentially due to an invalid ref type being requested
|
||||||
*/
|
*/
|
||||||
public GHRef[] getRefs(String refType) throws IOException {
|
public GHRef[] getRefs(String refType) throws IOException {
|
||||||
return GHRef.wrap(root.createRequest()
|
return listRefs(refType).toArray();
|
||||||
.withUrlPath(String.format("/repos/%s/%s/git/refs/%s", getOwnerName(), name, refType))
|
|
||||||
.fetchArray(GHRef[].class), root);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1455,8 +1608,7 @@ public class GHRepository extends GHObject {
|
|||||||
* on failure communicating with GitHub, potentially due to an invalid ref type being requested
|
* on failure communicating with GitHub, potentially due to an invalid ref type being requested
|
||||||
*/
|
*/
|
||||||
public PagedIterable<GHRef> listRefs(String refType) throws IOException {
|
public PagedIterable<GHRef> listRefs(String refType) throws IOException {
|
||||||
final String url = String.format("/repos/%s/%s/git/refs/%s", getOwnerName(), name, refType);
|
return GHRef.readMatching(this, refType);
|
||||||
return root.createRequest().withUrlPath(url).toIterable(GHRef[].class, item -> item.wrap(root));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1469,10 +1621,7 @@ public class GHRepository extends GHObject {
|
|||||||
* on failure communicating with GitHub, potentially due to an invalid ref type being requested
|
* on failure communicating with GitHub, potentially due to an invalid ref type being requested
|
||||||
*/
|
*/
|
||||||
public GHRef getRef(String refName) throws IOException {
|
public GHRef getRef(String refName) throws IOException {
|
||||||
return root.createRequest()
|
return GHRef.read(this, refName);
|
||||||
.withUrlPath(getApiTailUrl(String.format("git/refs/%s", refName)))
|
|
||||||
.fetch(GHRef.class)
|
|
||||||
.wrap(root);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1639,6 +1788,20 @@ public class GHRepository extends GHObject {
|
|||||||
.toIterable(GHCommitComment[].class, item -> item.wrap(this));
|
.toIterable(GHCommitComment[].class, item -> item.wrap(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists all comments on a specific commit.
|
||||||
|
*
|
||||||
|
* @param commitSha
|
||||||
|
* the hash of the commit
|
||||||
|
*
|
||||||
|
* @return the paged iterable
|
||||||
|
*/
|
||||||
|
public PagedIterable<GHCommitComment> listCommitComments(String commitSha) {
|
||||||
|
return root.createRequest()
|
||||||
|
.withUrlPath(String.format("/repos/%s/%s/commits/%s/comments", getOwnerName(), name, commitSha))
|
||||||
|
.toIterable(GHCommitComment[].class, item -> item.wrap(this));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the basic license details for the repository.
|
* Gets the basic license details for the repository.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -1676,7 +1839,7 @@ public class GHRepository extends GHObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* /** Lists all the commit statues attached to the given commit, newer ones first.
|
* /** Lists all the commit statuses attached to the given commit, newer ones first.
|
||||||
*
|
*
|
||||||
* @param sha1
|
* @param sha1
|
||||||
* the sha 1
|
* the sha 1
|
||||||
@@ -1700,10 +1863,31 @@ public class GHRepository extends GHObject {
|
|||||||
* the io exception
|
* the io exception
|
||||||
*/
|
*/
|
||||||
public GHCommitStatus getLastCommitStatus(String sha1) throws IOException {
|
public GHCommitStatus getLastCommitStatus(String sha1) throws IOException {
|
||||||
List<GHCommitStatus> v = listCommitStatuses(sha1).asList();
|
List<GHCommitStatus> v = listCommitStatuses(sha1).toList();
|
||||||
return v.isEmpty() ? null : v.get(0);
|
return v.isEmpty() ? null : v.get(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets check runs for given ref.
|
||||||
|
*
|
||||||
|
* @param ref
|
||||||
|
* ref
|
||||||
|
* @return check runs for given ref
|
||||||
|
* @throws IOException
|
||||||
|
* the io exception
|
||||||
|
* @see <a href="https://developer.github.com/v3/checks/runs/#list-check-runs-for-a-specific-ref">List check runs
|
||||||
|
* for a specific ref</a>
|
||||||
|
*/
|
||||||
|
@Preview
|
||||||
|
@Deprecated
|
||||||
|
public PagedIterable<GHCheckRun> getCheckRuns(String ref) throws IOException {
|
||||||
|
GitHubRequest request = root.createRequest()
|
||||||
|
.withUrlPath(String.format("/repos/%s/%s/commits/%s/check-runs", getOwnerName(), name, ref))
|
||||||
|
.withPreview(ANTIOPE)
|
||||||
|
.build();
|
||||||
|
return new GHCheckRunsIterable(root, request);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a commit status
|
* Creates a commit status
|
||||||
*
|
*
|
||||||
@@ -1759,6 +1943,34 @@ public class GHRepository extends GHObject {
|
|||||||
return createCommitStatus(sha1, state, targetUrl, description, null);
|
return createCommitStatus(sha1, state, targetUrl, description, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a check run for a commit.
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* an identifier for the run
|
||||||
|
* @param headSHA
|
||||||
|
* the commit hash
|
||||||
|
* @return a builder which you should customize, then call {@link GHCheckRunBuilder#create}
|
||||||
|
*/
|
||||||
|
@Preview
|
||||||
|
@Deprecated
|
||||||
|
public @NonNull GHCheckRunBuilder createCheckRun(@NonNull String name, @NonNull String headSHA) {
|
||||||
|
return new GHCheckRunBuilder(this, name, headSHA);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates an existing check run.
|
||||||
|
*
|
||||||
|
* @param checkId
|
||||||
|
* the existing checkId
|
||||||
|
* @return a builder which you should customize, then call {@link GHCheckRunBuilder#create}
|
||||||
|
*/
|
||||||
|
@Preview
|
||||||
|
@Deprecated
|
||||||
|
public @NonNull GHCheckRunBuilder updateCheckRun(long checkId) {
|
||||||
|
return new GHCheckRunBuilder(this, checkId);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lists repository events.
|
* Lists repository events.
|
||||||
*
|
*
|
||||||
@@ -1782,9 +1994,7 @@ public class GHRepository extends GHObject {
|
|||||||
* the io exception
|
* the io exception
|
||||||
*/
|
*/
|
||||||
public PagedIterable<GHLabel> listLabels() throws IOException {
|
public PagedIterable<GHLabel> listLabels() throws IOException {
|
||||||
return root.createRequest()
|
return GHLabel.readAll(this);
|
||||||
.withUrlPath(getApiTailUrl("labels"))
|
|
||||||
.toIterable(GHLabel[].class, item -> item.wrapUp(this));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1797,7 +2007,7 @@ public class GHRepository extends GHObject {
|
|||||||
* the io exception
|
* the io exception
|
||||||
*/
|
*/
|
||||||
public GHLabel getLabel(String name) throws IOException {
|
public GHLabel getLabel(String name) throws IOException {
|
||||||
return root.createRequest().withUrlPath(getApiTailUrl("labels/" + name)).fetch(GHLabel.class).wrapUp(this);
|
return GHLabel.read(this, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1812,7 +2022,7 @@ public class GHRepository extends GHObject {
|
|||||||
* the io exception
|
* the io exception
|
||||||
*/
|
*/
|
||||||
public GHLabel createLabel(String name, String color) throws IOException {
|
public GHLabel createLabel(String name, String color) throws IOException {
|
||||||
return createLabel(name, color, "");
|
return GHLabel.create(this).name(name).color(color).description("").done();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1829,14 +2039,7 @@ public class GHRepository extends GHObject {
|
|||||||
* the io exception
|
* the io exception
|
||||||
*/
|
*/
|
||||||
public GHLabel createLabel(String name, String color, String description) throws IOException {
|
public GHLabel createLabel(String name, String color, String description) throws IOException {
|
||||||
return root.createRequest()
|
return GHLabel.create(this).name(name).color(color).description(description).done();
|
||||||
.method("POST")
|
|
||||||
.with("name", name)
|
|
||||||
.with("color", color)
|
|
||||||
.with("description", description)
|
|
||||||
.withUrlPath(getApiTailUrl("labels"))
|
|
||||||
.fetch(GHLabel.class)
|
|
||||||
.wrapUp(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1939,14 +2142,6 @@ public class GHRepository extends GHObject {
|
|||||||
return createWebHook(url, null);
|
return createWebHook(url, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// this is no different from getPullRequests(OPEN)
|
|
||||||
// /**
|
|
||||||
// * Retrieves all the pull requests.
|
|
||||||
// */
|
|
||||||
// public List<GHPullRequest> getPullRequests() throws IOException {
|
|
||||||
// return root.retrieveWithAuth("/pulls/"+owner+'/'+name,JsonPullRequests.class).wrap(root);
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a set that represents the post-commit hook URLs. The returned set is live, and changes made to them are
|
* Returns a set that represents the post-commit hook URLs. The returned set is live, and changes made to them are
|
||||||
* reflected to GitHub.
|
* reflected to GitHub.
|
||||||
@@ -1956,6 +2151,7 @@ public class GHRepository extends GHObject {
|
|||||||
*/
|
*/
|
||||||
@SuppressFBWarnings(value = "DMI_COLLECTION_OF_URLS",
|
@SuppressFBWarnings(value = "DMI_COLLECTION_OF_URLS",
|
||||||
justification = "It causes a performance degradation, but we have already exposed it to the API")
|
justification = "It causes a performance degradation, but we have already exposed it to the API")
|
||||||
|
@Deprecated
|
||||||
public Set<URL> getPostCommitHooks() {
|
public Set<URL> getPostCommitHooks() {
|
||||||
return postCommitHooks;
|
return postCommitHooks;
|
||||||
}
|
}
|
||||||
@@ -1969,7 +2165,7 @@ public class GHRepository extends GHObject {
|
|||||||
private final Set<URL> postCommitHooks = new AbstractSet<URL>() {
|
private final Set<URL> postCommitHooks = new AbstractSet<URL>() {
|
||||||
private List<URL> getPostCommitHooks() {
|
private List<URL> getPostCommitHooks() {
|
||||||
try {
|
try {
|
||||||
List<URL> r = new ArrayList<URL>();
|
List<URL> r = new ArrayList<>();
|
||||||
for (GHHook h : getHooks()) {
|
for (GHHook h : getHooks()) {
|
||||||
if (h.getName().equals("web")) {
|
if (h.getName().equals("web")) {
|
||||||
r.add(new URL(h.getConfig().get("url")));
|
r.add(new URL(h.getConfig().get("url")));
|
||||||
@@ -2020,9 +2216,15 @@ public class GHRepository extends GHObject {
|
|||||||
|
|
||||||
GHRepository wrap(GitHub root) {
|
GHRepository wrap(GitHub root) {
|
||||||
this.root = root;
|
this.root = root;
|
||||||
if (root.isOffline()) {
|
if (root.isOffline() && owner != null) {
|
||||||
owner.wrapUp(root);
|
owner.wrapUp(root);
|
||||||
}
|
}
|
||||||
|
if (source != null) {
|
||||||
|
source.wrap(root);
|
||||||
|
}
|
||||||
|
if (parent != null) {
|
||||||
|
parent.wrap(root);
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2035,8 +2237,10 @@ public class GHRepository extends GHObject {
|
|||||||
*/
|
*/
|
||||||
public Map<String, GHBranch> getBranches() throws IOException {
|
public Map<String, GHBranch> getBranches() throws IOException {
|
||||||
Map<String, GHBranch> r = new TreeMap<String, GHBranch>();
|
Map<String, GHBranch> r = new TreeMap<String, GHBranch>();
|
||||||
for (GHBranch p : root.createRequest().withUrlPath(getApiTailUrl("branches")).fetchArray(GHBranch[].class)) {
|
for (GHBranch p : root.createRequest()
|
||||||
p.wrap(this);
|
.withUrlPath(getApiTailUrl("branches"))
|
||||||
|
.toIterable(GHBranch[].class, item -> item.wrap(this))
|
||||||
|
.toArray()) {
|
||||||
r.put(p.getName(), p);
|
r.put(p.getName(), p);
|
||||||
}
|
}
|
||||||
return r;
|
return r;
|
||||||
@@ -2167,11 +2371,10 @@ public class GHRepository extends GHObject {
|
|||||||
}
|
}
|
||||||
String target = getApiTailUrl("contents/" + path);
|
String target = getApiTailUrl("contents/" + path);
|
||||||
|
|
||||||
GHContent[] files = requester.with("ref", ref).withUrlPath(target).fetchArray(GHContent[].class);
|
return requester.with("ref", ref)
|
||||||
|
.withUrlPath(target)
|
||||||
GHContent.wrap(files, this);
|
.toIterable(GHContent[].class, item -> item.wrap(this))
|
||||||
|
.toList();
|
||||||
return Arrays.asList(files);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2325,11 +2528,10 @@ public class GHRepository extends GHObject {
|
|||||||
* the io exception
|
* the io exception
|
||||||
*/
|
*/
|
||||||
public List<GHDeployKey> getDeployKeys() throws IOException {
|
public List<GHDeployKey> getDeployKeys() throws IOException {
|
||||||
List<GHDeployKey> list = new ArrayList<GHDeployKey>(
|
return root.createRequest()
|
||||||
Arrays.asList(root.createRequest().withUrlPath(getApiTailUrl("keys")).fetchArray(GHDeployKey[].class)));
|
.withUrlPath(getApiTailUrl("keys"))
|
||||||
for (GHDeployKey h : list)
|
.toIterable(GHDeployKey[].class, item -> item.wrap(this))
|
||||||
h.wrap(this);
|
.toList();
|
||||||
return list;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2342,10 +2544,13 @@ public class GHRepository extends GHObject {
|
|||||||
* @see #getParent() #getParent()
|
* @see #getParent() #getParent()
|
||||||
*/
|
*/
|
||||||
public GHRepository getSource() throws IOException {
|
public GHRepository getSource() throws IOException {
|
||||||
if (source == null)
|
if (fork && source == null) {
|
||||||
|
populate();
|
||||||
|
}
|
||||||
|
if (source == null) {
|
||||||
return null;
|
return null;
|
||||||
if (source.root == null)
|
}
|
||||||
source = root.getRepository(source.getFullName());
|
|
||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2360,10 +2565,13 @@ public class GHRepository extends GHObject {
|
|||||||
* @see #getSource() #getSource()
|
* @see #getSource() #getSource()
|
||||||
*/
|
*/
|
||||||
public GHRepository getParent() throws IOException {
|
public GHRepository getParent() throws IOException {
|
||||||
if (parent == null)
|
if (fork && parent == null) {
|
||||||
|
populate();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parent == null) {
|
||||||
return null;
|
return null;
|
||||||
if (parent.root == null)
|
}
|
||||||
parent = root.getRepository(parent.getFullName());
|
|
||||||
return parent;
|
return parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2582,8 +2790,9 @@ public class GHRepository extends GHObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String getApiTailUrl(String tail) {
|
String getApiTailUrl(String tail) {
|
||||||
if (tail.length() > 0 && !tail.startsWith("/"))
|
if (tail.length() > 0 && !tail.startsWith("/")) {
|
||||||
tail = '/' + tail;
|
tail = '/' + tail;
|
||||||
|
}
|
||||||
return "/repos/" + getOwnerName() + "/" + name + tail;
|
return "/repos/" + getOwnerName() + "/" + name + tail;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2682,4 +2891,32 @@ public class GHRepository extends GHObject {
|
|||||||
.fetch(GHTagObject.class)
|
.fetch(GHTagObject.class)
|
||||||
.wrap(this);
|
.wrap(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Populate this object.
|
||||||
|
*
|
||||||
|
* @throws java.io.IOException
|
||||||
|
* The IO exception
|
||||||
|
*/
|
||||||
|
void populate() throws IOException {
|
||||||
|
if (root.isOffline()) {
|
||||||
|
return; // can't populate if the root is offline
|
||||||
|
}
|
||||||
|
|
||||||
|
final URL url = Objects.requireNonNull(getUrl(), "Missing instance URL!");
|
||||||
|
|
||||||
|
try {
|
||||||
|
// IMPORTANT: the url for repository records is does not reliably point to the API url.
|
||||||
|
// There is bug in Push event payloads that returns the wrong url.
|
||||||
|
// All other occurrences of "url" take the form "https://api.github.com/...".
|
||||||
|
// For Push event repository records, they take the form "https://github.com/{fullName}".
|
||||||
|
root.createRequest().withPreview(BAPTISE).setRawUrlPath(url.toString()).fetchInto(this).wrap(root);
|
||||||
|
} catch (HttpException e) {
|
||||||
|
if (e.getCause() instanceof JsonParseException) {
|
||||||
|
root.createRequest().withPreview(BAPTISE).withUrlPath("/repos/" + full_name).fetchInto(this).wrap(root);
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
package org.kohsuke.github;
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||||
import com.fasterxml.jackson.databind.exc.MismatchedInputException;
|
import com.fasterxml.jackson.databind.exc.MismatchedInputException;
|
||||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
@@ -315,21 +316,12 @@ public class GHRepositoryStatistics {
|
|||||||
* the io exception
|
* the io exception
|
||||||
*/
|
*/
|
||||||
public List<CodeFrequency> getCodeFrequency() throws IOException {
|
public List<CodeFrequency> getCodeFrequency() throws IOException {
|
||||||
// Map to arrays first, since there are no field names in the
|
|
||||||
// returned JSON.
|
|
||||||
try {
|
try {
|
||||||
Integer[][] list = root.createRequest()
|
CodeFrequency[] list = root.createRequest()
|
||||||
.withUrlPath(getApiTailUrl("code_frequency"))
|
.withUrlPath(getApiTailUrl("code_frequency"))
|
||||||
.fetch(Integer[][].class);
|
.fetch(CodeFrequency[].class);
|
||||||
|
|
||||||
// Convert to proper objects.
|
return Arrays.asList(list);
|
||||||
List<CodeFrequency> returnList = new ArrayList<>();
|
|
||||||
for (Integer[] item : list) {
|
|
||||||
CodeFrequency cf = new CodeFrequency(Arrays.asList(item));
|
|
||||||
returnList.add(cf);
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnList;
|
|
||||||
} catch (MismatchedInputException e) {
|
} catch (MismatchedInputException e) {
|
||||||
// This sometimes happens when retrieving code frequency statistics
|
// This sometimes happens when retrieving code frequency statistics
|
||||||
// for a repository for the first time. It is probably still being
|
// for a repository for the first time. It is probably still being
|
||||||
@@ -342,10 +334,12 @@ public class GHRepositoryStatistics {
|
|||||||
* The type CodeFrequency.
|
* The type CodeFrequency.
|
||||||
*/
|
*/
|
||||||
public static class CodeFrequency {
|
public static class CodeFrequency {
|
||||||
private int week;
|
|
||||||
private int additions;
|
|
||||||
private int deletions;
|
|
||||||
|
|
||||||
|
private final int week;
|
||||||
|
private final int additions;
|
||||||
|
private final int deletions;
|
||||||
|
|
||||||
|
@JsonCreator(mode = JsonCreator.Mode.DELEGATING)
|
||||||
private CodeFrequency(List<Integer> item) {
|
private CodeFrequency(List<Integer> item) {
|
||||||
week = item.get(0);
|
week = item.get(0);
|
||||||
additions = item.get(1);
|
additions = item.get(1);
|
||||||
@@ -428,7 +422,7 @@ public class GHRepositoryStatistics {
|
|||||||
* @return The list of commit counts for everyone combined, for the last 52 weeks.
|
* @return The list of commit counts for everyone combined, for the last 52 weeks.
|
||||||
*/
|
*/
|
||||||
public List<Integer> getAllCommits() {
|
public List<Integer> getAllCommits() {
|
||||||
return all;
|
return Collections.unmodifiableList(all);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -437,7 +431,7 @@ public class GHRepositoryStatistics {
|
|||||||
* @return The list of commit counts for the owner, for the last 52 weeks.
|
* @return The list of commit counts for the owner, for the last 52 weeks.
|
||||||
*/
|
*/
|
||||||
public List<Integer> getOwnerCommits() {
|
public List<Integer> getOwnerCommits() {
|
||||||
return owner;
|
return Collections.unmodifiableList(owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
Participation wrapUp(GitHub root) {
|
Participation wrapUp(GitHub root) {
|
||||||
@@ -455,28 +449,22 @@ public class GHRepositoryStatistics {
|
|||||||
* the io exception
|
* the io exception
|
||||||
*/
|
*/
|
||||||
public List<PunchCardItem> getPunchCard() throws IOException {
|
public List<PunchCardItem> getPunchCard() throws IOException {
|
||||||
// Map to ArrayLists first, since there are no field names in the
|
PunchCardItem[] list = root.createRequest()
|
||||||
// returned JSON.
|
.withUrlPath(getApiTailUrl("punch_card"))
|
||||||
Integer[][] list = root.createRequest().withUrlPath(getApiTailUrl("punch_card")).fetch(Integer[][].class);
|
.fetch(PunchCardItem[].class);
|
||||||
|
return Arrays.asList(list);
|
||||||
// Convert to proper objects.
|
|
||||||
ArrayList<PunchCardItem> returnList = new ArrayList<>();
|
|
||||||
for (Integer[] item : list) {
|
|
||||||
PunchCardItem pci = new PunchCardItem(Arrays.asList(item));
|
|
||||||
returnList.add(pci);
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnList;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The type PunchCardItem.
|
* The type PunchCardItem.
|
||||||
*/
|
*/
|
||||||
public static class PunchCardItem {
|
public static class PunchCardItem {
|
||||||
private int dayOfWeek;
|
|
||||||
private int hourOfDay;
|
|
||||||
private int numberOfCommits;
|
|
||||||
|
|
||||||
|
private final int dayOfWeek;
|
||||||
|
private final int hourOfDay;
|
||||||
|
private final int numberOfCommits;
|
||||||
|
|
||||||
|
@JsonCreator(mode = JsonCreator.Mode.DELEGATING)
|
||||||
private PunchCardItem(List<Integer> item) {
|
private PunchCardItem(List<Integer> item) {
|
||||||
dayOfWeek = item.get(0);
|
dayOfWeek = item.get(0);
|
||||||
hourOfDay = item.get(1);
|
hourOfDay = item.get(1);
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ public abstract class GHRepositoryTraffic implements TrafficInfo {
|
|||||||
* @return the timestamp
|
* @return the timestamp
|
||||||
*/
|
*/
|
||||||
public Date getTimestamp() {
|
public Date getTimestamp() {
|
||||||
return GitHub.parseDate(timestamp);
|
return GitHubClient.parseDate(timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getCount() {
|
public int getCount() {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package org.kohsuke.github;
|
|||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import java.net.MalformedURLException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -23,6 +24,8 @@ public abstract class GHSearchBuilder<T> extends GHQueryBuilder<T> {
|
|||||||
GHSearchBuilder(GitHub root, Class<? extends SearchResult<T>> receiverType) {
|
GHSearchBuilder(GitHub root, Class<? extends SearchResult<T>> receiverType) {
|
||||||
super(root);
|
super(root);
|
||||||
this.receiverType = receiverType;
|
this.receiverType = receiverType;
|
||||||
|
req.withUrlPath(getApiUrl());
|
||||||
|
req.rateLimit(RateLimitTarget.SEARCH);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -42,16 +45,13 @@ public abstract class GHSearchBuilder<T> extends GHQueryBuilder<T> {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public PagedSearchIterable<T> list() {
|
public PagedSearchIterable<T> list() {
|
||||||
return new PagedSearchIterable<T>(root) {
|
|
||||||
public PagedIterator<T> _iterator(int pageSize) {
|
req.set("q", StringUtils.join(terms, " "));
|
||||||
req.set("q", StringUtils.join(terms, " "));
|
try {
|
||||||
return new PagedIterator<T>(adapt(req.withUrlPath(getApiUrl()).asIterator(receiverType, pageSize))) {
|
return new PagedSearchIterable<>(root, req.build(), receiverType);
|
||||||
protected void wrapUp(T[] page) {
|
} catch (MalformedURLException e) {
|
||||||
// SearchResult.getItems() should do it
|
throw new GHException("", e);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ public class GHStargazer {
|
|||||||
* @return the date the stargazer was added
|
* @return the date the stargazer was added
|
||||||
*/
|
*/
|
||||||
public Date getStarredAt() {
|
public Date getStarredAt() {
|
||||||
return GitHub.parseDate(starred_at);
|
return GitHubClient.parseDate(starred_at);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ public class GHSubscription {
|
|||||||
* @return the created at
|
* @return the created at
|
||||||
*/
|
*/
|
||||||
public Date getCreatedAt() {
|
public Date getCreatedAt() {
|
||||||
return GitHub.parseDate(created_at);
|
return GitHubClient.parseDate(created_at);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ public class GHTagObject {
|
|||||||
private String message;
|
private String message;
|
||||||
private GitUser tagger;
|
private GitUser tagger;
|
||||||
private GHRef.GHObject object;
|
private GHRef.GHObject object;
|
||||||
|
private GHVerification verification;
|
||||||
|
|
||||||
GHTagObject wrap(GHRepository owner) {
|
GHTagObject wrap(GHRepository owner) {
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
@@ -97,4 +98,13 @@ public class GHTagObject {
|
|||||||
public GHRef.GHObject getObject() {
|
public GHRef.GHObject getObject() {
|
||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets Verification Status.
|
||||||
|
*
|
||||||
|
* @return the Verification status
|
||||||
|
*/
|
||||||
|
public GHVerification getVerification() {
|
||||||
|
return verification;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,27 @@
|
|||||||
package org.kohsuke.github;
|
package org.kohsuke.github;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collections;
|
import java.net.URL;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A team in GitHub organization.
|
* A team in GitHub organization.
|
||||||
*
|
*
|
||||||
* @author Kohsuke Kawaguchi
|
* @author Kohsuke Kawaguchi
|
||||||
*/
|
*/
|
||||||
public class GHTeam implements Refreshable {
|
public class GHTeam extends GHObject implements Refreshable {
|
||||||
|
private String html_url;
|
||||||
private String name;
|
private String name;
|
||||||
private String permission;
|
private String permission;
|
||||||
private String slug;
|
private String slug;
|
||||||
private String description;
|
private String description;
|
||||||
private Privacy privacy;
|
private Privacy privacy;
|
||||||
|
|
||||||
private int id;
|
|
||||||
private GHOrganization organization; // populated by GET /user/teams where Teams+Orgs are returned together
|
private GHOrganization organization; // populated by GET /user/teams where Teams+Orgs are returned together
|
||||||
|
|
||||||
protected /* final */ GitHub root;
|
protected /* final */ GitHub root;
|
||||||
@@ -54,13 +57,6 @@ public class GHTeam implements Refreshable {
|
|||||||
return wrapUp(organization);
|
return wrapUp(organization);
|
||||||
}
|
}
|
||||||
|
|
||||||
static GHTeam[] wrapUp(GHTeam[] teams, GHOrganization owner) {
|
|
||||||
for (GHTeam t : teams) {
|
|
||||||
t.wrapUp(owner);
|
|
||||||
}
|
|
||||||
return teams;
|
|
||||||
}
|
|
||||||
|
|
||||||
static GHTeam[] wrapUp(GHTeam[] teams, GHPullRequest owner) {
|
static GHTeam[] wrapUp(GHTeam[] teams, GHPullRequest owner) {
|
||||||
for (GHTeam t : teams) {
|
for (GHTeam t : teams) {
|
||||||
t.root = owner.root;
|
t.root = owner.root;
|
||||||
@@ -138,12 +134,49 @@ public class GHTeam implements Refreshable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets id.
|
* Retrieves the discussions.
|
||||||
*
|
*
|
||||||
* @return the id
|
* @return the paged iterable
|
||||||
|
* @throws IOException
|
||||||
|
* the io exception
|
||||||
*/
|
*/
|
||||||
public int getId() {
|
@Nonnull
|
||||||
return id;
|
public PagedIterable<GHDiscussion> listDiscussions() throws IOException {
|
||||||
|
return GHDiscussion.readAll(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List members with specified role paged iterable.
|
||||||
|
*
|
||||||
|
* @param role
|
||||||
|
* the role
|
||||||
|
* @return the paged iterable
|
||||||
|
* @throws IOException
|
||||||
|
* the io exception
|
||||||
|
*/
|
||||||
|
public PagedIterable<GHUser> listMembers(String role) throws IOException {
|
||||||
|
return root.createRequest()
|
||||||
|
.withUrlPath(api("/members"))
|
||||||
|
.with("role", role)
|
||||||
|
.toIterable(GHUser[].class, item -> item.wrapUp(root));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a single discussion by ID.
|
||||||
|
*
|
||||||
|
* @param discussionNumber
|
||||||
|
* id of the discussion that we want to query for
|
||||||
|
* @return the discussion
|
||||||
|
* @throws java.io.FileNotFoundException
|
||||||
|
* if the discussion does not exist
|
||||||
|
* @throws IOException
|
||||||
|
* the io exception
|
||||||
|
*
|
||||||
|
* @see <a href= "https://developer.github.com/v3/teams/discussions/#get-a-discussion">documentation</a>
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public GHDiscussion getDiscussion(long discussionNumber) throws IOException {
|
||||||
|
return GHDiscussion.read(this, discussionNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -154,7 +187,20 @@ public class GHTeam implements Refreshable {
|
|||||||
* the io exception
|
* the io exception
|
||||||
*/
|
*/
|
||||||
public PagedIterable<GHUser> listMembers() throws IOException {
|
public PagedIterable<GHUser> listMembers() throws IOException {
|
||||||
return root.createRequest().withUrlPath(api("/members")).toIterable(GHUser[].class, item -> item.wrapUp(root));
|
return listMembers("all");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the teams that are children of this team.
|
||||||
|
*
|
||||||
|
* @return the paged iterable
|
||||||
|
* @throws IOException
|
||||||
|
* the io exception
|
||||||
|
*/
|
||||||
|
public PagedIterable<GHTeam> listChildTeams() throws IOException {
|
||||||
|
return root.createRequest()
|
||||||
|
.withUrlPath(api("/teams"))
|
||||||
|
.toIterable(GHTeam[].class, item -> item.wrapUp(this.organization));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -165,7 +211,7 @@ public class GHTeam implements Refreshable {
|
|||||||
* the io exception
|
* the io exception
|
||||||
*/
|
*/
|
||||||
public Set<GHUser> getMembers() throws IOException {
|
public Set<GHUser> getMembers() throws IOException {
|
||||||
return Collections.unmodifiableSet(listMembers().asSet());
|
return listMembers().toSet();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -177,7 +223,7 @@ public class GHTeam implements Refreshable {
|
|||||||
*/
|
*/
|
||||||
public boolean hasMember(GHUser user) {
|
public boolean hasMember(GHUser user) {
|
||||||
try {
|
try {
|
||||||
root.createRequest().withUrlPath("/teams/" + id + "/members/" + user.getLogin()).send();
|
root.createRequest().withUrlPath("/teams/" + getId() + "/members/" + user.getLogin()).send();
|
||||||
return true;
|
return true;
|
||||||
} catch (IOException ignore) {
|
} catch (IOException ignore) {
|
||||||
return false;
|
return false;
|
||||||
@@ -310,7 +356,22 @@ public class GHTeam implements Refreshable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String api(String tail) {
|
private String api(String tail) {
|
||||||
return "/teams/" + id + tail;
|
return "/teams/" + getId() + tail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Begins the creation of a new instance.
|
||||||
|
*
|
||||||
|
* Consumer must call {@link GHDiscussion.Creator#done()} to commit changes.
|
||||||
|
*
|
||||||
|
* @param title
|
||||||
|
* title of the discussion to be created
|
||||||
|
* @return a {@link GHDiscussion.Creator}
|
||||||
|
* @throws IOException
|
||||||
|
* the io exception
|
||||||
|
*/
|
||||||
|
public GHDiscussion.Creator createDiscussion(String title) throws IOException {
|
||||||
|
return GHDiscussion.create(this).title(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -329,4 +390,28 @@ public class GHTeam implements Refreshable {
|
|||||||
public void refresh() throws IOException {
|
public void refresh() throws IOException {
|
||||||
root.createRequest().withUrlPath(api("")).fetchInto(this).wrapUp(root);
|
root.createRequest().withUrlPath(api("")).fetchInto(this).wrapUp(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public URL getHtmlUrl() {
|
||||||
|
return GitHubClient.parseURL(html_url);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
GHTeam ghTeam = (GHTeam) o;
|
||||||
|
return Objects.equals(name, ghTeam.name) && Objects.equals(getUrl(), ghTeam.getUrl())
|
||||||
|
&& Objects.equals(permission, ghTeam.permission) && Objects.equals(slug, ghTeam.slug)
|
||||||
|
&& Objects.equals(description, ghTeam.description) && privacy == ghTeam.privacy;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(name, getUrl(), permission, slug, description, privacy);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ public class GHTeamBuilder {
|
|||||||
* parentTeamId of team
|
* parentTeamId of team
|
||||||
* @return a builder to continue with building
|
* @return a builder to continue with building
|
||||||
*/
|
*/
|
||||||
public GHTeamBuilder parentTeamId(int parentTeamId) {
|
public GHTeamBuilder parentTeamId(long parentTeamId) {
|
||||||
this.builder.with("parent_team_id", parentTeamId);
|
this.builder.with("parent_team_id", parentTeamId);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ public class GHThread extends GHObject {
|
|||||||
* @return the last read at
|
* @return the last read at
|
||||||
*/
|
*/
|
||||||
public Date getLastReadAt() {
|
public Date getLastReadAt() {
|
||||||
return GitHub.parseDate(last_read_at);
|
return GitHubClient.parseDate(last_read_at);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ public class GHTree {
|
|||||||
* @return the url
|
* @return the url
|
||||||
*/
|
*/
|
||||||
public URL getUrl() {
|
public URL getUrl() {
|
||||||
return GitHub.parseURL(url);
|
return GitHubClient.parseURL(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
GHTree wrap(GHRepository repo) {
|
GHTree wrap(GHRepository repo) {
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ public class GHTreeEntry {
|
|||||||
* @return the url
|
* @return the url
|
||||||
*/
|
*/
|
||||||
public URL getUrl() {
|
public URL getUrl() {
|
||||||
return GitHub.parseURL(url);
|
return GitHubClient.parseURL(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -43,8 +43,7 @@ public class GHUser extends GHPerson {
|
|||||||
* the io exception
|
* the io exception
|
||||||
*/
|
*/
|
||||||
public List<GHKey> getKeys() throws IOException {
|
public List<GHKey> getKeys() throws IOException {
|
||||||
return Collections.unmodifiableList(
|
return root.createRequest().withUrlPath(getApiTailUrl("keys")).toIterable(GHKey[].class, null).toList();
|
||||||
Arrays.asList(root.createRequest().withUrlPath(getApiTailUrl("keys")).fetchArray(GHKey[].class)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -76,7 +75,7 @@ public class GHUser extends GHPerson {
|
|||||||
*/
|
*/
|
||||||
@WithBridgeMethods(Set.class)
|
@WithBridgeMethods(Set.class)
|
||||||
public GHPersonSet<GHUser> getFollows() throws IOException {
|
public GHPersonSet<GHUser> getFollows() throws IOException {
|
||||||
return new GHPersonSet<GHUser>(listFollows().asList());
|
return new GHPersonSet<GHUser>(listFollows().toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -97,7 +96,7 @@ public class GHUser extends GHPerson {
|
|||||||
*/
|
*/
|
||||||
@WithBridgeMethods(Set.class)
|
@WithBridgeMethods(Set.class)
|
||||||
public GHPersonSet<GHUser> getFollowers() throws IOException {
|
public GHPersonSet<GHUser> getFollowers() throws IOException {
|
||||||
return new GHPersonSet<GHUser>(listFollowers().asList());
|
return new GHPersonSet<GHUser>(listFollowers().toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -174,6 +173,19 @@ public class GHUser extends GHPerson {
|
|||||||
return org.hasPublicMember(this);
|
return org.hasPublicMember(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if this user is marked as hireable, false otherwise
|
||||||
|
*
|
||||||
|
* @return if the user is marked as hireable
|
||||||
|
*/
|
||||||
|
public boolean isHireable() {
|
||||||
|
return hireable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBio() {
|
||||||
|
return bio;
|
||||||
|
}
|
||||||
|
|
||||||
static GHUser[] wrap(GHUser[] users, GitHub root) {
|
static GHUser[] wrap(GHUser[] users, GitHub root) {
|
||||||
for (GHUser f : users)
|
for (GHUser f : users)
|
||||||
f.root = root;
|
f.root = root;
|
||||||
@@ -193,7 +205,8 @@ public class GHUser extends GHPerson {
|
|||||||
Set<String> names = new HashSet<String>();
|
Set<String> names = new HashSet<String>();
|
||||||
for (GHOrganization o : root.createRequest()
|
for (GHOrganization o : root.createRequest()
|
||||||
.withUrlPath("/users/" + login + "/orgs")
|
.withUrlPath("/users/" + login + "/orgs")
|
||||||
.fetchArray(GHOrganization[].class)) {
|
.toIterable(GHOrganization[].class, null)
|
||||||
|
.toArray()) {
|
||||||
if (names.add(o.getLogin())) // I've seen some duplicates in the data
|
if (names.add(o.getLogin())) // I've seen some duplicates in the data
|
||||||
orgs.add(root.getOrganization(o.getLogin()));
|
orgs.add(root.getOrganization(o.getLogin()));
|
||||||
}
|
}
|
||||||
@@ -219,7 +232,7 @@ public class GHUser extends GHPerson {
|
|||||||
public PagedIterable<GHGist> listGists() throws IOException {
|
public PagedIterable<GHGist> listGists() throws IOException {
|
||||||
return root.createRequest()
|
return root.createRequest()
|
||||||
.withUrlPath(String.format("/users/%s/gists", login))
|
.withUrlPath(String.format("/users/%s/gists", login))
|
||||||
.toIterable(GHGist[].class, item -> item.wrapUp(this));
|
.toIterable(GHGist[].class, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
82
src/main/java/org/kohsuke/github/GHVerification.java
Normal file
82
src/main/java/org/kohsuke/github/GHVerification.java
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The commit/tag can be signed by user. This object holds the verification status. Whether the Commit/Tag is signed or
|
||||||
|
* not.
|
||||||
|
*
|
||||||
|
* @see <a href="https://developer.github.com/v3/git/tags/#signature-verification-object">tags signature
|
||||||
|
* verificatiion</a>
|
||||||
|
* @see <a href="https://developer.github.com/v3/git/commits/#signature-verification-object">commits signature
|
||||||
|
* verificatiion</a>
|
||||||
|
*
|
||||||
|
* @author Sourabh Sarvotham Parkala
|
||||||
|
*/
|
||||||
|
@SuppressFBWarnings(value = { "UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD" },
|
||||||
|
justification = "JSON API")
|
||||||
|
public class GHVerification {
|
||||||
|
private String signature, payload;
|
||||||
|
private boolean verified;
|
||||||
|
private Reason reason;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether GitHub considers the signature in this commit to be verified.
|
||||||
|
*
|
||||||
|
* @return true if the signature is valid else returns false.
|
||||||
|
*/
|
||||||
|
public boolean isVerified() {
|
||||||
|
return verified;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets reason for verification value.
|
||||||
|
*
|
||||||
|
* @return return reason of type {@link Reason}, such as "valid" or "unsigned". The possible values can be found in
|
||||||
|
* {@link Reason}}
|
||||||
|
*/
|
||||||
|
public Reason getReason() {
|
||||||
|
return reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets signature used for the verification.
|
||||||
|
*
|
||||||
|
* @return null if not signed else encoded signature.
|
||||||
|
*/
|
||||||
|
public String getSignature() {
|
||||||
|
return signature;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the payload that was signed.
|
||||||
|
*
|
||||||
|
* @return null if not signed else encoded signature.
|
||||||
|
*/
|
||||||
|
public String getPayload() {
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The possible values for reason in verification object from github.
|
||||||
|
*
|
||||||
|
* @see <a href="https://developer.github.com/v3/repos/commits/#signature-verification-object">List of possible
|
||||||
|
* reason values</a>
|
||||||
|
* @author Sourabh Sarvotham Parkala
|
||||||
|
*/
|
||||||
|
public enum Reason {
|
||||||
|
EXPIRED_KEY,
|
||||||
|
NOT_SIGNING_KEY,
|
||||||
|
GPGVERIFY_ERROR,
|
||||||
|
GPGVERIFY_UNAVAILABLE,
|
||||||
|
UNSIGNED,
|
||||||
|
UNKNOWN_SIGNATURE_TYPE,
|
||||||
|
NO_USER,
|
||||||
|
UNVERIFIED_EMAIL,
|
||||||
|
BAD_EMAIL,
|
||||||
|
UNKNOWN_KEY,
|
||||||
|
MALFORMED_SIGNATURE,
|
||||||
|
INVALID,
|
||||||
|
VALID
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,23 +23,12 @@
|
|||||||
*/
|
*/
|
||||||
package org.kohsuke.github;
|
package org.kohsuke.github;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
import com.fasterxml.jackson.databind.ObjectReader;
|
||||||
import com.fasterxml.jackson.databind.MapperFeature;
|
import com.fasterxml.jackson.databind.ObjectWriter;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
|
|
||||||
import com.fasterxml.jackson.databind.introspect.VisibilityChecker.Std;
|
|
||||||
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
|
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
|
||||||
import org.apache.commons.io.IOUtils;
|
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.net.HttpURLConnection;
|
|
||||||
import java.net.MalformedURLException;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.text.ParseException;
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.Base64;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
import java.util.concurrent.ConcurrentMap;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
@@ -48,10 +37,6 @@ import java.util.logging.Logger;
|
|||||||
import javax.annotation.CheckForNull;
|
import javax.annotation.CheckForNull;
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.ANY;
|
|
||||||
import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.NONE;
|
|
||||||
import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
|
|
||||||
import static java.util.logging.Level.FINE;
|
|
||||||
import static org.kohsuke.github.Previews.INERTIA;
|
import static org.kohsuke.github.Previews.INERTIA;
|
||||||
import static org.kohsuke.github.Previews.MACHINE_MAN;
|
import static org.kohsuke.github.Previews.MACHINE_MAN;
|
||||||
|
|
||||||
@@ -67,27 +52,15 @@ import static org.kohsuke.github.Previews.MACHINE_MAN;
|
|||||||
* @author Kohsuke Kawaguchi
|
* @author Kohsuke Kawaguchi
|
||||||
*/
|
*/
|
||||||
public class GitHub {
|
public class GitHub {
|
||||||
final String login;
|
|
||||||
|
|
||||||
/**
|
@Nonnull
|
||||||
* Value of the authorization header to be sent with the request.
|
private final GitHubClient client;
|
||||||
*/
|
|
||||||
final String encodedAuthorization;
|
@CheckForNull
|
||||||
|
private GHMyself myself;
|
||||||
|
|
||||||
private final ConcurrentMap<String, GHUser> users;
|
private final ConcurrentMap<String, GHUser> users;
|
||||||
private final ConcurrentMap<String, GHOrganization> orgs;
|
private final ConcurrentMap<String, GHOrganization> orgs;
|
||||||
// Cache of myself object.
|
|
||||||
private GHMyself myself;
|
|
||||||
private final String apiUrl;
|
|
||||||
|
|
||||||
final RateLimitHandler rateLimitHandler;
|
|
||||||
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.
|
* Creates a client API root object.
|
||||||
@@ -137,36 +110,20 @@ public class GitHub {
|
|||||||
String password,
|
String password,
|
||||||
HttpConnector connector,
|
HttpConnector connector,
|
||||||
RateLimitHandler rateLimitHandler,
|
RateLimitHandler rateLimitHandler,
|
||||||
AbuseLimitHandler abuseLimitHandler) throws IOException {
|
AbuseLimitHandler abuseLimitHandler,
|
||||||
if (apiUrl.endsWith("/"))
|
GitHubRateLimitChecker rateLimitChecker) throws IOException {
|
||||||
apiUrl = apiUrl.substring(0, apiUrl.length() - 1); // normalize
|
this.client = new GitHubHttpUrlConnectionClient(apiUrl,
|
||||||
this.apiUrl = apiUrl;
|
login,
|
||||||
if (null != connector)
|
oauthAccessToken,
|
||||||
this.connector = connector;
|
jwtToken,
|
||||||
|
password,
|
||||||
if (oauthAccessToken != null) {
|
connector,
|
||||||
encodedAuthorization = "token " + oauthAccessToken;
|
rateLimitHandler,
|
||||||
} else {
|
abuseLimitHandler,
|
||||||
if (jwtToken != null) {
|
rateLimitChecker,
|
||||||
encodedAuthorization = "Bearer " + jwtToken;
|
(myself) -> setMyself(myself));
|
||||||
} else if (password != null) {
|
users = new ConcurrentHashMap<>();
|
||||||
String authorization = (login + ':' + password);
|
orgs = new ConcurrentHashMap<>();
|
||||||
String charsetName = StandardCharsets.UTF_8.name();
|
|
||||||
encodedAuthorization = "Basic "
|
|
||||||
+ Base64.getEncoder().encodeToString(authorization.getBytes(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 && jwtToken == null)
|
|
||||||
login = getMyself().getLogin();
|
|
||||||
this.login = login;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -184,7 +141,10 @@ public class GitHub {
|
|||||||
* Version that connects to GitHub Enterprise.
|
* Version that connects to GitHub Enterprise.
|
||||||
*
|
*
|
||||||
* @param apiUrl
|
* @param apiUrl
|
||||||
* the api url
|
* The URL of GitHub (or GitHub Enterprise) API endpoint, such as "https://api.github.com" or
|
||||||
|
* "http://ghe.acme.com/api/v3". Note that GitHub Enterprise has <code>/api/v3</code> in the URL. For
|
||||||
|
* historical reasons, this parameter still accepts the bare domain name, but that's considered
|
||||||
|
* deprecated.
|
||||||
* @param oauthAccessToken
|
* @param oauthAccessToken
|
||||||
* the oauth access token
|
* the oauth access token
|
||||||
* @return the git hub
|
* @return the git hub
|
||||||
@@ -264,8 +224,7 @@ public class GitHub {
|
|||||||
* @return the git hub
|
* @return the git hub
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
* the io exception
|
* the io exception
|
||||||
* @deprecated Either OAuth token or password is sufficient, so there's no point in passing both. Use
|
* @deprecated Use {@link #connectUsingOAuth(String)}.
|
||||||
* {@link #connectUsingPassword(String, String)} or {@link #connectUsingOAuth(String)}.
|
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public static GitHub connect(String login, String oauthAccessToken, String password) throws IOException {
|
public static GitHub connect(String login, String oauthAccessToken, String password) throws IOException {
|
||||||
@@ -282,7 +241,12 @@ public class GitHub {
|
|||||||
* @return the git hub
|
* @return the git hub
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
* the io exception
|
* the io exception
|
||||||
|
* @deprecated Use {@link #connectUsingOAuth(String)} instead.
|
||||||
|
* @see <a href=
|
||||||
|
* "https://developer.github.com/changes/2020-02-14-deprecating-password-auth/#changes-to-make">Deprecating
|
||||||
|
* password authentication and OAuth authorizations API</a>
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public static GitHub connectUsingPassword(String login, String password) throws IOException {
|
public static GitHub connectUsingPassword(String login, String password) throws IOException {
|
||||||
return new GitHubBuilder().withPassword(login, password).build();
|
return new GitHubBuilder().withPassword(login, password).build();
|
||||||
}
|
}
|
||||||
@@ -366,7 +330,7 @@ public class GitHub {
|
|||||||
* @return {@code true} if operations that require authentication will fail.
|
* @return {@code true} if operations that require authentication will fail.
|
||||||
*/
|
*/
|
||||||
public boolean isAnonymous() {
|
public boolean isAnonymous() {
|
||||||
return login == null && encodedAuthorization == null;
|
return client.isAnonymous();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -375,7 +339,7 @@ public class GitHub {
|
|||||||
* @return {@code true} if this is an always offline "connection".
|
* @return {@code true} if this is an always offline "connection".
|
||||||
*/
|
*/
|
||||||
public boolean isOffline() {
|
public boolean isOffline() {
|
||||||
return connector == HttpConnector.OFFLINE;
|
return client.isOffline();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -384,7 +348,19 @@ public class GitHub {
|
|||||||
* @return the connector
|
* @return the connector
|
||||||
*/
|
*/
|
||||||
public HttpConnector getConnector() {
|
public HttpConnector getConnector() {
|
||||||
return connector;
|
return client.getConnector();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the custom connector used to make requests to GitHub.
|
||||||
|
*
|
||||||
|
* @param connector
|
||||||
|
* the connector
|
||||||
|
* @deprecated HttpConnector should not be changed. If you find yourself needing to do this, file an issue.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public void setConnector(HttpConnector connector) {
|
||||||
|
client.setConnector(connector);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -393,107 +369,26 @@ public class GitHub {
|
|||||||
* @return the api url
|
* @return the api url
|
||||||
*/
|
*/
|
||||||
public String getApiUrl() {
|
public String getApiUrl() {
|
||||||
return apiUrl;
|
return client.getApiUrl();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the custom connector used to make requests to GitHub.
|
* Gets the current full rate limit information from the server.
|
||||||
*
|
*
|
||||||
* @param connector
|
* For some versions of GitHub Enterprise, the {@code /rate_limit} endpoint returns a {@code 404 Not Found}. In that
|
||||||
* the connector
|
* case, the most recent {@link GHRateLimit} information will be returned, including rate limit information returned
|
||||||
*/
|
* in the response header for this request in if was present.
|
||||||
public void setConnector(HttpConnector connector) {
|
*
|
||||||
this.connector = connector;
|
* For most use cases it would be better to implement a {@link RateLimitChecker} and add it via
|
||||||
}
|
* {@link GitHubBuilder#withRateLimitChecker(RateLimitChecker)}.
|
||||||
|
|
||||||
void requireCredential() {
|
|
||||||
if (isAnonymous())
|
|
||||||
throw new IllegalStateException(
|
|
||||||
"This operation requires a credential but none is given to the GitHub constructor");
|
|
||||||
}
|
|
||||||
|
|
||||||
URL getApiURL(String tailApiUrl) throws IOException {
|
|
||||||
if (tailApiUrl.startsWith("/")) {
|
|
||||||
if ("github.com".equals(apiUrl)) {// backward compatibility
|
|
||||||
return new URL(GITHUB_URL + tailApiUrl);
|
|
||||||
} else {
|
|
||||||
return new URL(apiUrl + tailApiUrl);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return new URL(tailApiUrl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Requester createRequest() {
|
|
||||||
return new Requester(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the current rate limit.
|
|
||||||
*
|
*
|
||||||
* @return the rate limit
|
* @return the rate limit
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
* the io exception
|
* the io exception
|
||||||
*/
|
*/
|
||||||
|
@Nonnull
|
||||||
public GHRateLimit getRateLimit() throws IOException {
|
public GHRateLimit getRateLimit() throws IOException {
|
||||||
GHRateLimit rateLimit;
|
return client.getRateLimit();
|
||||||
try {
|
|
||||||
rateLimit = createRequest().withUrlPath("/rate_limit").fetch(JsonRateLimit.class).resources;
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
// GitHub Enterprise doesn't have the rate limit
|
|
||||||
// return a default rate limit that
|
|
||||||
rateLimit = GHRateLimit.Unknown();
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.rateLimit = rateLimit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the Rate Limit with the latest info from response header. Due to multi-threading requests might complete
|
|
||||||
* out of order, we want to pick the one with the most recent info from the server.
|
|
||||||
*
|
|
||||||
* @param observed
|
|
||||||
* {@link GHRateLimit.Record} constructed from the response header information
|
|
||||||
*/
|
|
||||||
void updateCoreRateLimit(@Nonnull GHRateLimit.Record observed) {
|
|
||||||
synchronized (headerRateLimitLock) {
|
|
||||||
if (headerRateLimit == null || shouldReplace(observed, headerRateLimit.getCore())) {
|
|
||||||
headerRateLimit = GHRateLimit.fromHeaderRecord(observed);
|
|
||||||
LOGGER.log(FINE, "Rate limit now: {0}", headerRateLimit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the Rate Limit with the latest info from response header. Due to multi-threading requests might complete
|
|
||||||
* out of order, we want to pick the one with the most recent info from the server. Header date is only accurate to
|
|
||||||
* the second, so we look at the information in the record itself.
|
|
||||||
*
|
|
||||||
* {@link GHRateLimit.UnknownLimitRecord}s are always replaced by regular {@link GHRateLimit.Record}s. Regular
|
|
||||||
* {@link GHRateLimit.Record}s are never replaced by {@link GHRateLimit.UnknownLimitRecord}s. Candidates with
|
|
||||||
* resetEpochSeconds later than current record are more recent. Candidates with the same reset and a lower remaining
|
|
||||||
* count are more recent. Candidates with an earlier reset are older.
|
|
||||||
*
|
|
||||||
* @param candidate
|
|
||||||
* {@link GHRateLimit.Record} constructed from the response header information
|
|
||||||
* @param current
|
|
||||||
* the current {@link GHRateLimit.Record} record
|
|
||||||
*/
|
|
||||||
static boolean shouldReplace(@Nonnull GHRateLimit.Record candidate, @Nonnull GHRateLimit.Record current) {
|
|
||||||
if (candidate instanceof GHRateLimit.UnknownLimitRecord
|
|
||||||
&& !(current instanceof GHRateLimit.UnknownLimitRecord)) {
|
|
||||||
// Unknown candidate never replaces a regular record
|
|
||||||
return false;
|
|
||||||
} else if (current instanceof GHRateLimit.UnknownLimitRecord
|
|
||||||
&& !(candidate instanceof GHRateLimit.UnknownLimitRecord)) {
|
|
||||||
// Any real record should replace an unknown Record.
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
// records of the same type compare to each other as normal.
|
|
||||||
return current.getResetEpochSeconds() < candidate.getResetEpochSeconds()
|
|
||||||
|| (current.getResetEpochSeconds() == candidate.getResetEpochSeconds()
|
|
||||||
&& current.getRemaining() > candidate.getRemaining());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -501,12 +396,13 @@ public class GitHub {
|
|||||||
* GitHub Enterprise) or if no requests have been made.
|
* GitHub Enterprise) or if no requests have been made.
|
||||||
*
|
*
|
||||||
* @return the most recently observed rate limit data or {@code null}.
|
* @return the most recently observed rate limit data or {@code null}.
|
||||||
|
* @deprecated implement a {@link RateLimitChecker} and add it via
|
||||||
|
* {@link GitHubBuilder#withRateLimitChecker(RateLimitChecker)}.
|
||||||
*/
|
*/
|
||||||
@CheckForNull
|
@Nonnull
|
||||||
|
@Deprecated
|
||||||
public GHRateLimit lastRateLimit() {
|
public GHRateLimit lastRateLimit() {
|
||||||
synchronized (headerRateLimitLock) {
|
return client.lastRateLimit();
|
||||||
return headerRateLimit;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -515,19 +411,13 @@ public class GitHub {
|
|||||||
* @return the current rate limit data.
|
* @return the current rate limit data.
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
* if we couldn't get the current rate limit data.
|
* if we couldn't get the current rate limit data.
|
||||||
|
* @deprecated implement a {@link RateLimitChecker} and add it via
|
||||||
|
* {@link GitHubBuilder#withRateLimitChecker(RateLimitChecker)}.
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
@Nonnull
|
||||||
|
@Deprecated
|
||||||
public GHRateLimit rateLimit() throws IOException {
|
public GHRateLimit rateLimit() throws IOException {
|
||||||
synchronized (headerRateLimitLock) {
|
return client.rateLimit(RateLimitTarget.CORE);
|
||||||
if (headerRateLimit != null && !headerRateLimit.isExpired()) {
|
|
||||||
return headerRateLimit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
GHRateLimit rateLimit = this.rateLimit;
|
|
||||||
if (rateLimit == null || rateLimit.isExpired()) {
|
|
||||||
rateLimit = getRateLimit();
|
|
||||||
}
|
|
||||||
return rateLimit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -537,18 +427,22 @@ public class GitHub {
|
|||||||
* @throws IOException
|
* @throws IOException
|
||||||
* the io exception
|
* the io exception
|
||||||
*/
|
*/
|
||||||
@WithBridgeMethods(GHUser.class)
|
@WithBridgeMethods(value = GHUser.class)
|
||||||
public GHMyself getMyself() throws IOException {
|
public GHMyself getMyself() throws IOException {
|
||||||
requireCredential();
|
client.requireCredential();
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (this.myself != null)
|
if (this.myself == null) {
|
||||||
return myself;
|
GHMyself u = createRequest().withUrlPath("/user").fetch(GHMyself.class);
|
||||||
|
setMyself(u);
|
||||||
|
}
|
||||||
|
return myself;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
GHMyself u = createRequest().withUrlPath("/user").fetch(GHMyself.class);
|
private void setMyself(GHMyself myself) {
|
||||||
|
synchronized (this) {
|
||||||
u.root = this;
|
myself.wrapUp(this);
|
||||||
this.myself = u;
|
this.myself = myself;
|
||||||
return u;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -638,7 +532,7 @@ public class GitHub {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the repository object from 'user/reponame' string that GitHub calls as "repository name"
|
* Gets the repository object from 'owner/repo' string that GitHub calls as "repository name"
|
||||||
*
|
*
|
||||||
* @param name
|
* @param name
|
||||||
* the name
|
* the name
|
||||||
@@ -649,9 +543,10 @@ public class GitHub {
|
|||||||
*/
|
*/
|
||||||
public GHRepository getRepository(String name) throws IOException {
|
public GHRepository getRepository(String name) throws IOException {
|
||||||
String[] tokens = name.split("/");
|
String[] tokens = name.split("/");
|
||||||
return createRequest().withUrlPath("/repos/" + tokens[0] + '/' + tokens[1])
|
if (tokens.length < 2) {
|
||||||
.fetch(GHRepository.class)
|
throw new IllegalArgumentException("Repository name must be in format owner/repo");
|
||||||
.wrap(this);
|
}
|
||||||
|
return GHRepository.read(this, tokens[0], tokens[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -730,12 +625,9 @@ public class GitHub {
|
|||||||
* the io exception
|
* the io exception
|
||||||
*/
|
*/
|
||||||
public List<GHInvitation> getMyInvitations() throws IOException {
|
public List<GHInvitation> getMyInvitations() throws IOException {
|
||||||
GHInvitation[] invitations = createRequest().withUrlPath("/user/repository_invitations")
|
return createRequest().withUrlPath("/user/repository_invitations")
|
||||||
.fetchArray(GHInvitation[].class);
|
.toIterable(GHInvitation[].class, item -> item.wrapUp(this))
|
||||||
for (GHInvitation i : invitations) {
|
.toList();
|
||||||
i.wrapUp(this);
|
|
||||||
}
|
|
||||||
return Arrays.asList(invitations);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -749,11 +641,13 @@ public class GitHub {
|
|||||||
* the io exception
|
* the io exception
|
||||||
*/
|
*/
|
||||||
public Map<String, GHOrganization> getMyOrganizations() throws IOException {
|
public Map<String, GHOrganization> getMyOrganizations() throws IOException {
|
||||||
GHOrganization[] orgs = createRequest().withUrlPath("/user/orgs").fetchArray(GHOrganization[].class);
|
GHOrganization[] orgs = createRequest().withUrlPath("/user/orgs")
|
||||||
Map<String, GHOrganization> r = new HashMap<String, GHOrganization>();
|
.toIterable(GHOrganization[].class, item -> item.wrapUp(this))
|
||||||
|
.toArray();
|
||||||
|
Map<String, GHOrganization> r = new HashMap<>();
|
||||||
for (GHOrganization o : orgs) {
|
for (GHOrganization o : orgs) {
|
||||||
// don't put 'o' into orgs because they are shallow
|
// don't put 'o' into orgs because they are shallow
|
||||||
r.put(o.getLogin(), o.wrapUp(this));
|
r.put(o.getLogin(), o);
|
||||||
}
|
}
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
@@ -803,11 +697,12 @@ public class GitHub {
|
|||||||
*/
|
*/
|
||||||
public Map<String, GHOrganization> getUserPublicOrganizations(String login) throws IOException {
|
public Map<String, GHOrganization> getUserPublicOrganizations(String login) throws IOException {
|
||||||
GHOrganization[] orgs = createRequest().withUrlPath("/users/" + login + "/orgs")
|
GHOrganization[] orgs = createRequest().withUrlPath("/users/" + login + "/orgs")
|
||||||
.fetchArray(GHOrganization[].class);
|
.toIterable(GHOrganization[].class, item -> item.wrapUp(this))
|
||||||
Map<String, GHOrganization> r = new HashMap<String, GHOrganization>();
|
.toArray();
|
||||||
|
Map<String, GHOrganization> r = new HashMap<>();
|
||||||
for (GHOrganization o : orgs) {
|
for (GHOrganization o : orgs) {
|
||||||
// don't put 'o' into orgs because they are shallow
|
// don't put 'o' into orgs cache because they are shallow records
|
||||||
r.put(o.getLogin(), o.wrapUp(this));
|
r.put(o.getLogin(), o);
|
||||||
}
|
}
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
@@ -823,13 +718,14 @@ public class GitHub {
|
|||||||
* the io exception
|
* the io exception
|
||||||
*/
|
*/
|
||||||
public Map<String, Set<GHTeam>> getMyTeams() throws IOException {
|
public Map<String, Set<GHTeam>> getMyTeams() throws IOException {
|
||||||
Map<String, Set<GHTeam>> allMyTeams = new HashMap<String, Set<GHTeam>>();
|
Map<String, Set<GHTeam>> allMyTeams = new HashMap<>();
|
||||||
for (GHTeam team : createRequest().withUrlPath("/user/teams").fetchArray(GHTeam[].class)) {
|
for (GHTeam team : createRequest().withUrlPath("/user/teams")
|
||||||
team.wrapUp(this);
|
.toIterable(GHTeam[].class, item -> item.wrapUp(this))
|
||||||
|
.toArray()) {
|
||||||
String orgLogin = team.getOrganization().getLogin();
|
String orgLogin = team.getOrganization().getLogin();
|
||||||
Set<GHTeam> teamsPerOrg = allMyTeams.get(orgLogin);
|
Set<GHTeam> teamsPerOrg = allMyTeams.get(orgLogin);
|
||||||
if (teamsPerOrg == null) {
|
if (teamsPerOrg == null) {
|
||||||
teamsPerOrg = new HashSet<GHTeam>();
|
teamsPerOrg = new HashSet<>();
|
||||||
}
|
}
|
||||||
teamsPerOrg.add(team);
|
teamsPerOrg.add(team);
|
||||||
allMyTeams.put(orgLogin, teamsPerOrg);
|
allMyTeams.put(orgLogin, teamsPerOrg);
|
||||||
@@ -845,7 +741,11 @@ public class GitHub {
|
|||||||
* @return the team
|
* @return the team
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
* the io exception
|
* the io exception
|
||||||
|
*
|
||||||
|
* @deprecated Use {@link GHOrganization#getTeam(long)}
|
||||||
|
* @see <a href= "https://developer.github.com/v3/teams/#get-team-legacy">deprecation notice</a>
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public GHTeam getTeam(int id) throws IOException {
|
public GHTeam getTeam(int id) throws IOException {
|
||||||
return createRequest().withUrlPath("/teams/" + id).fetch(GHTeam.class).wrapUp(this);
|
return createRequest().withUrlPath("/teams/" + id).fetch(GHTeam.class).wrapUp(this);
|
||||||
}
|
}
|
||||||
@@ -858,10 +758,9 @@ public class GitHub {
|
|||||||
* the io exception
|
* the io exception
|
||||||
*/
|
*/
|
||||||
public List<GHEventInfo> getEvents() throws IOException {
|
public List<GHEventInfo> getEvents() throws IOException {
|
||||||
GHEventInfo[] events = createRequest().withUrlPath("/events").fetchArray(GHEventInfo[].class);
|
return createRequest().withUrlPath("/events")
|
||||||
for (GHEventInfo e : events)
|
.toIterable(GHEventInfo[].class, item -> item.wrapUp(this))
|
||||||
e.wrapUp(this);
|
.toList();
|
||||||
return Arrays.asList(events);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -874,7 +773,7 @@ public class GitHub {
|
|||||||
* the io exception
|
* the io exception
|
||||||
*/
|
*/
|
||||||
public GHGist getGist(String id) throws IOException {
|
public GHGist getGist(String id) throws IOException {
|
||||||
return createRequest().withUrlPath("/gists/" + id).fetch(GHGist.class).wrapUp(this);
|
return createRequest().withUrlPath("/gists/" + id).fetch(GHGist.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -903,7 +802,7 @@ public class GitHub {
|
|||||||
* the io exception
|
* the io exception
|
||||||
*/
|
*/
|
||||||
public <T extends GHEventPayload> T parseEventPayload(Reader r, Class<T> type) throws IOException {
|
public <T extends GHEventPayload> T parseEventPayload(Reader r, Class<T> type) throws IOException {
|
||||||
T t = MAPPER.readValue(r, type);
|
T t = GitHubClient.getMappingObjectReader(this).forType(type).readValue(r);
|
||||||
t.wrapUp(this);
|
t.wrapUp(this);
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
@@ -1126,16 +1025,7 @@ public class GitHub {
|
|||||||
* @return the boolean
|
* @return the boolean
|
||||||
*/
|
*/
|
||||||
public boolean isCredentialValid() {
|
public boolean isCredentialValid() {
|
||||||
try {
|
return client.isCredentialValid();
|
||||||
createRequest().withUrlPath("/user").fetch(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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1151,20 +1041,6 @@ public class GitHub {
|
|||||||
return createRequest().withUrlPath("/meta").fetch(GHMeta.class);
|
return createRequest().withUrlPath("/meta").fetch(GHMeta.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets project.
|
* Gets project.
|
||||||
*
|
*
|
||||||
@@ -1210,18 +1086,6 @@ public class GitHub {
|
|||||||
.wrap(this);
|
.wrap(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
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.
|
* Tests the connection.
|
||||||
*
|
*
|
||||||
@@ -1236,62 +1100,7 @@ public class GitHub {
|
|||||||
* the io exception
|
* the io exception
|
||||||
*/
|
*/
|
||||||
public void checkApiUrlValidity() throws IOException {
|
public void checkApiUrlValidity() throws IOException {
|
||||||
try {
|
client.checkApiUrlValidity();
|
||||||
createRequest().withUrlPath("/").fetch(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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if a GitHub Enterprise server is configured in private mode.
|
|
||||||
*
|
|
||||||
* In private mode response looks like:
|
|
||||||
*
|
|
||||||
* <pre>
|
|
||||||
* $ 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
|
|
||||||
* </pre>
|
|
||||||
*
|
|
||||||
* @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("/"));
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1398,49 +1207,55 @@ public class GitHub {
|
|||||||
"UTF-8");
|
"UTF-8");
|
||||||
}
|
}
|
||||||
|
|
||||||
static URL parseURL(String s) {
|
/**
|
||||||
try {
|
* Do not use this method. This method will be removed and should never have been needed in the first place.
|
||||||
return s == null ? null : new URL(s);
|
*
|
||||||
} catch (MalformedURLException e) {
|
* @return an {@link ObjectWriter} instance that can be further configured.
|
||||||
throw new IllegalStateException("Invalid URL: " + s);
|
* @deprecated DO NOT USE THIS METHOD. Provided for backward compatibility with projects that did their own jackson
|
||||||
}
|
* mapping of this project's data objects, such as Jenkins Blue Ocean.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
@Nonnull
|
||||||
|
public static ObjectWriter getMappingObjectWriter() {
|
||||||
|
return GitHubClient.getMappingObjectWriter();
|
||||||
}
|
}
|
||||||
|
|
||||||
static Date parseDate(String timestamp) {
|
/**
|
||||||
if (timestamp == null)
|
* Do not use this method. This method will be removed and should never have been needed in the first place.
|
||||||
return null;
|
*
|
||||||
for (String f : TIME_FORMATS) {
|
* @return an {@link ObjectReader} instance that can be further configured.
|
||||||
try {
|
* @deprecated DO NOT USE THIS METHOD. Provided for backward compatibility with projects that did their own jackson
|
||||||
SimpleDateFormat df = new SimpleDateFormat(f);
|
* mapping of this project's data objects, such as Jenkins Blue Ocean.
|
||||||
df.setTimeZone(TimeZone.getTimeZone("GMT"));
|
*/
|
||||||
return df.parse(timestamp);
|
@Deprecated
|
||||||
} catch (ParseException e) {
|
@Nonnull
|
||||||
// try next
|
public static ObjectReader getMappingObjectReader() {
|
||||||
}
|
return GitHubClient.getMappingObjectReader(GitHub.offline());
|
||||||
}
|
|
||||||
throw new IllegalStateException("Unable to parse the timestamp: " + timestamp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static String printDate(Date dt) {
|
@Nonnull
|
||||||
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
|
GitHubClient getClient() {
|
||||||
df.setTimeZone(TimeZone.getTimeZone("GMT"));
|
return client;
|
||||||
return df.format(dt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static final ObjectMapper MAPPER = new ObjectMapper();
|
@Nonnull
|
||||||
|
Requester createRequest() {
|
||||||
private static final String[] TIME_FORMATS = { "yyyy/MM/dd HH:mm:ss ZZZZ", "yyyy-MM-dd'T'HH:mm:ss'Z'",
|
return new Requester(client).injectMappingValue(this);
|
||||||
"yyyy-MM-dd'T'HH:mm:ss.S'Z'" // GitHub App endpoints return a different date format
|
|
||||||
};
|
|
||||||
|
|
||||||
static {
|
|
||||||
MAPPER.setVisibility(new Std(NONE, NONE, NONE, NONE, ANY));
|
|
||||||
MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
|
||||||
MAPPER.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS, true);
|
|
||||||
MAPPER.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static final String GITHUB_URL = "https://api.github.com";
|
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 final Logger LOGGER = Logger.getLogger(GitHub.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(GitHub.class.getName());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,11 +9,12 @@ import java.io.FileNotFoundException;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
import java.net.Proxy;
|
import java.net.Proxy;
|
||||||
import java.net.URL;
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configures connection details and produces {@link GitHub}.
|
* Configures connection details and produces {@link GitHub}.
|
||||||
*
|
*
|
||||||
@@ -22,7 +23,7 @@ import java.util.Properties;
|
|||||||
public class GitHubBuilder implements Cloneable {
|
public class GitHubBuilder implements Cloneable {
|
||||||
|
|
||||||
// default scoped so unit tests can read them.
|
// default scoped so unit tests can read them.
|
||||||
/* private */ String endpoint = GitHub.GITHUB_URL;
|
/* private */ String endpoint = GitHubClient.GITHUB_URL;
|
||||||
/* private */ String user;
|
/* private */ String user;
|
||||||
/* private */ String password;
|
/* private */ String password;
|
||||||
/* private */ String oauthToken;
|
/* private */ String oauthToken;
|
||||||
@@ -32,6 +33,7 @@ public class GitHubBuilder implements Cloneable {
|
|||||||
|
|
||||||
private RateLimitHandler rateLimitHandler = RateLimitHandler.WAIT;
|
private RateLimitHandler rateLimitHandler = RateLimitHandler.WAIT;
|
||||||
private AbuseLimitHandler abuseLimitHandler = AbuseLimitHandler.WAIT;
|
private AbuseLimitHandler abuseLimitHandler = AbuseLimitHandler.WAIT;
|
||||||
|
private GitHubRateLimitChecker rateLimitChecker = new GitHubRateLimitChecker();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instantiates a new Git hub builder.
|
* Instantiates a new Git hub builder.
|
||||||
@@ -90,6 +92,7 @@ public class GitHubBuilder implements Cloneable {
|
|||||||
* @deprecated Use {@link #fromEnvironment()} to pick up standard set of environment variables, so that different
|
* @deprecated Use {@link #fromEnvironment()} to pick up standard set of environment variables, so that different
|
||||||
* clients of this library will all recognize one consistent set of coordinates.
|
* clients of this library will all recognize one consistent set of coordinates.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public static GitHubBuilder fromEnvironment(String loginVariableName,
|
public static GitHubBuilder fromEnvironment(String loginVariableName,
|
||||||
String passwordVariableName,
|
String passwordVariableName,
|
||||||
String oauthVariableName) throws IOException {
|
String oauthVariableName) throws IOException {
|
||||||
@@ -119,6 +122,7 @@ public class GitHubBuilder implements Cloneable {
|
|||||||
* @deprecated Use {@link #fromEnvironment()} to pick up standard set of environment variables, so that different
|
* @deprecated Use {@link #fromEnvironment()} to pick up standard set of environment variables, so that different
|
||||||
* clients of this library will all recognize one consistent set of coordinates.
|
* clients of this library will all recognize one consistent set of coordinates.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public static GitHubBuilder fromEnvironment(String loginVariableName,
|
public static GitHubBuilder fromEnvironment(String loginVariableName,
|
||||||
String passwordVariableName,
|
String passwordVariableName,
|
||||||
String oauthVariableName,
|
String oauthVariableName,
|
||||||
@@ -214,7 +218,7 @@ public class GitHubBuilder implements Cloneable {
|
|||||||
self.withOAuthToken(props.getProperty("oauth"), props.getProperty("login"));
|
self.withOAuthToken(props.getProperty("oauth"), props.getProperty("login"));
|
||||||
self.withJwtToken(props.getProperty("jwt"));
|
self.withJwtToken(props.getProperty("jwt"));
|
||||||
self.withPassword(props.getProperty("login"), props.getProperty("password"));
|
self.withPassword(props.getProperty("login"), props.getProperty("password"));
|
||||||
self.withEndpoint(props.getProperty("endpoint", GitHub.GITHUB_URL));
|
self.withEndpoint(props.getProperty("endpoint", GitHubClient.GITHUB_URL));
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -311,11 +315,26 @@ public class GitHubBuilder implements Cloneable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* With rate limit handler git hub builder.
|
* Adds a {@link RateLimitHandler} to this {@link GitHubBuilder}.
|
||||||
|
* <p>
|
||||||
|
* GitHub allots a certain number of requests to each user or application per period of time (usually per hour). The
|
||||||
|
* number of requests remaining is returned in the response header and can also be requested using
|
||||||
|
* {@link GitHub#getRateLimit()}. This requests per interval is referred to as the "rate limit".
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* When the remaining number of requests reaches zero, the next request will return an error. If this happens,
|
||||||
|
* {@link RateLimitHandler#onError(IOException, HttpURLConnection)} will be called.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* NOTE: GitHub treats clients that exceed their rate limit very harshly. If possible, clients should avoid
|
||||||
|
* exceeding their rate limit. Consider adding a {@link RateLimitChecker} to automatically check the rate limit for
|
||||||
|
* each request and wait if needed.
|
||||||
|
* </p>
|
||||||
*
|
*
|
||||||
* @param handler
|
* @param handler
|
||||||
* the handler
|
* the handler
|
||||||
* @return the git hub builder
|
* @return the git hub builder
|
||||||
|
* @see #withRateLimitChecker(RateLimitChecker)
|
||||||
*/
|
*/
|
||||||
public GitHubBuilder withRateLimitHandler(RateLimitHandler handler) {
|
public GitHubBuilder withRateLimitHandler(RateLimitHandler handler) {
|
||||||
this.rateLimitHandler = handler;
|
this.rateLimitHandler = handler;
|
||||||
@@ -323,7 +342,12 @@ public class GitHubBuilder implements Cloneable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* With abuse limit handler git hub builder.
|
* Adds a {@link AbuseLimitHandler} to this {@link GitHubBuilder}.
|
||||||
|
* <p>
|
||||||
|
* When a client sends too many requests in a short time span, GitHub may return an error and set a header telling
|
||||||
|
* the client to not make any more request for some period of time. If this happens,
|
||||||
|
* {@link AbuseLimitHandler#onError(IOException, HttpURLConnection)} will be called.
|
||||||
|
* </p>
|
||||||
*
|
*
|
||||||
* @param handler
|
* @param handler
|
||||||
* the handler
|
* the handler
|
||||||
@@ -334,6 +358,48 @@ public class GitHubBuilder implements Cloneable {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a {@link RateLimitChecker} for the Core API for this {@link GitHubBuilder}.
|
||||||
|
*
|
||||||
|
* @param coreRateLimitChecker
|
||||||
|
* the {@link RateLimitChecker} for core GitHub API requests
|
||||||
|
* @return the git hub builder
|
||||||
|
* @see #withRateLimitChecker(RateLimitChecker, RateLimitTarget)
|
||||||
|
*/
|
||||||
|
public GitHubBuilder withRateLimitChecker(@Nonnull RateLimitChecker coreRateLimitChecker) {
|
||||||
|
return withRateLimitChecker(coreRateLimitChecker, RateLimitTarget.CORE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a {@link RateLimitChecker} to this {@link GitHubBuilder}.
|
||||||
|
* <p>
|
||||||
|
* GitHub allots a certain number of requests to each user or application per period of time (usually per hour). The
|
||||||
|
* number of requests remaining is returned in the response header and can also be requested using
|
||||||
|
* {@link GitHub#getRateLimit()}. This requests per interval is referred to as the "rate limit".
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* GitHub prefers that clients stop before exceeding their rate limit rather than stopping after they exceed it. The
|
||||||
|
* {@link RateLimitChecker} is called before each request to check the rate limit and wait if the checker criteria
|
||||||
|
* are met.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* Checking your rate limit using {@link GitHub#getRateLimit()} does not effect your rate limit, but each
|
||||||
|
* {@link GitHub} instance will attempt to cache and reuse the last seen rate limit rather than making a new
|
||||||
|
* request.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param rateLimitChecker
|
||||||
|
* the {@link RateLimitChecker} for requests
|
||||||
|
* @param rateLimitTarget
|
||||||
|
* the {@link RateLimitTarget} specifying which rate limit record to check
|
||||||
|
* @return the git hub builder
|
||||||
|
*/
|
||||||
|
public GitHubBuilder withRateLimitChecker(@Nonnull RateLimitChecker rateLimitChecker,
|
||||||
|
@Nonnull RateLimitTarget rateLimitTarget) {
|
||||||
|
this.rateLimitChecker = this.rateLimitChecker.with(rateLimitChecker, rateLimitTarget);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configures {@linkplain #withConnector(HttpConnector) connector} that uses HTTP library in JRE but use a specific
|
* Configures {@linkplain #withConnector(HttpConnector) connector} that uses HTTP library in JRE but use a specific
|
||||||
* proxy, instead of the system default one.
|
* proxy, instead of the system default one.
|
||||||
@@ -343,15 +409,11 @@ public class GitHubBuilder implements Cloneable {
|
|||||||
* @return the git hub builder
|
* @return the git hub builder
|
||||||
*/
|
*/
|
||||||
public GitHubBuilder withProxy(final Proxy p) {
|
public GitHubBuilder withProxy(final Proxy p) {
|
||||||
return withConnector(new ImpatientHttpConnector(new HttpConnector() {
|
return withConnector(new ImpatientHttpConnector(url -> (HttpURLConnection) url.openConnection(p)));
|
||||||
public HttpURLConnection connect(URL url) throws IOException {
|
|
||||||
return (HttpURLConnection) url.openConnection(p);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build git hub.
|
* Builds a {@link GitHub} instance.
|
||||||
*
|
*
|
||||||
* @return the git hub
|
* @return the git hub
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
@@ -365,7 +427,8 @@ public class GitHubBuilder implements Cloneable {
|
|||||||
password,
|
password,
|
||||||
connector,
|
connector,
|
||||||
rateLimitHandler,
|
rateLimitHandler,
|
||||||
abuseLimitHandler);
|
abuseLimitHandler,
|
||||||
|
rateLimitChecker);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
727
src/main/java/org/kohsuke/github/GitHubClient.java
Normal file
727
src/main/java/org/kohsuke/github/GitHubClient.java
Normal file
@@ -0,0 +1,727 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||||
|
import com.fasterxml.jackson.databind.InjectableValues;
|
||||||
|
import com.fasterxml.jackson.databind.MapperFeature;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectReader;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectWriter;
|
||||||
|
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
|
||||||
|
import com.fasterxml.jackson.databind.introspect.VisibilityChecker;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InterruptedIOException;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.SocketException;
|
||||||
|
import java.net.SocketTimeoutException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.annotation.CheckForNull;
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.net.ssl.SSLHandshakeException;
|
||||||
|
|
||||||
|
import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.ANY;
|
||||||
|
import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.NONE;
|
||||||
|
import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
|
||||||
|
import static java.util.logging.Level.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A GitHub API Client
|
||||||
|
* <p>
|
||||||
|
* A GitHubClient can be used to send requests and retrieve their responses. GitHubClient is thread-safe and can be used
|
||||||
|
* to send multiple requests. GitHubClient also track some GitHub API information such as {@link GHRateLimit}.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
abstract class GitHubClient {
|
||||||
|
|
||||||
|
static final int CONNECTION_ERROR_RETRIES = 2;
|
||||||
|
/**
|
||||||
|
* If timeout issues let's retry after milliseconds.
|
||||||
|
*/
|
||||||
|
static final int retryTimeoutMillis = 100;
|
||||||
|
/* private */ final String login;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value of the authorization header to be sent with the request.
|
||||||
|
*/
|
||||||
|
/* private */ final String encodedAuthorization;
|
||||||
|
|
||||||
|
// Cache of myself object.
|
||||||
|
private final String apiUrl;
|
||||||
|
|
||||||
|
protected final RateLimitHandler rateLimitHandler;
|
||||||
|
protected final AbuseLimitHandler abuseLimitHandler;
|
||||||
|
private final GitHubRateLimitChecker rateLimitChecker;
|
||||||
|
|
||||||
|
private HttpConnector connector;
|
||||||
|
|
||||||
|
private final Object rateLimitLock = new Object();
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
private GHRateLimit rateLimit = GHRateLimit.DEFAULT;
|
||||||
|
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(GitHubClient.class.getName());
|
||||||
|
|
||||||
|
private static final ObjectMapper MAPPER = new ObjectMapper();
|
||||||
|
static final String GITHUB_URL = "https://api.github.com";
|
||||||
|
|
||||||
|
private static final DateTimeFormatter DATE_TIME_PARSER_SLASHES = DateTimeFormatter
|
||||||
|
.ofPattern("yyyy/MM/dd HH:mm:ss Z");
|
||||||
|
|
||||||
|
static {
|
||||||
|
MAPPER.setVisibility(new VisibilityChecker.Std(NONE, NONE, NONE, NONE, ANY));
|
||||||
|
MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||||
|
MAPPER.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS, true);
|
||||||
|
MAPPER.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
|
||||||
|
}
|
||||||
|
|
||||||
|
GitHubClient(String apiUrl,
|
||||||
|
String login,
|
||||||
|
String oauthAccessToken,
|
||||||
|
String jwtToken,
|
||||||
|
String password,
|
||||||
|
HttpConnector connector,
|
||||||
|
RateLimitHandler rateLimitHandler,
|
||||||
|
AbuseLimitHandler abuseLimitHandler,
|
||||||
|
GitHubRateLimitChecker rateLimitChecker,
|
||||||
|
Consumer<GHMyself> myselfConsumer) throws IOException {
|
||||||
|
|
||||||
|
if (apiUrl.endsWith("/")) {
|
||||||
|
apiUrl = apiUrl.substring(0, apiUrl.length() - 1); // normalize
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null == connector) {
|
||||||
|
connector = HttpConnector.DEFAULT;
|
||||||
|
}
|
||||||
|
this.apiUrl = apiUrl;
|
||||||
|
this.connector = connector;
|
||||||
|
|
||||||
|
if (oauthAccessToken != null) {
|
||||||
|
encodedAuthorization = "token " + oauthAccessToken;
|
||||||
|
} else {
|
||||||
|
if (jwtToken != null) {
|
||||||
|
encodedAuthorization = "Bearer " + jwtToken;
|
||||||
|
} else if (password != null) {
|
||||||
|
String authorization = (login + ':' + password);
|
||||||
|
String charsetName = StandardCharsets.UTF_8.name();
|
||||||
|
encodedAuthorization = "Basic "
|
||||||
|
+ Base64.getEncoder().encodeToString(authorization.getBytes(charsetName));
|
||||||
|
} else {// anonymous access
|
||||||
|
encodedAuthorization = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.rateLimitHandler = rateLimitHandler;
|
||||||
|
this.abuseLimitHandler = abuseLimitHandler;
|
||||||
|
this.rateLimitChecker = rateLimitChecker;
|
||||||
|
|
||||||
|
if (login == null && encodedAuthorization != null && jwtToken == null) {
|
||||||
|
GHMyself myself = fetch(GHMyself.class, "/user");
|
||||||
|
login = myself.getLogin();
|
||||||
|
if (myselfConsumer != null) {
|
||||||
|
myselfConsumer.accept(myself);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.login = login;
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> T fetch(Class<T> type, String urlPath) throws IOException {
|
||||||
|
GitHubRequest request = GitHubRequest.newBuilder().withApiUrl(getApiUrl()).withUrlPath(urlPath).build();
|
||||||
|
return this.sendRequest(request, (responseInfo) -> GitHubResponse.parseBody(responseInfo, type)).body();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that the credential for this client is valid.
|
||||||
|
*
|
||||||
|
* @return the boolean
|
||||||
|
*/
|
||||||
|
public boolean isCredentialValid() {
|
||||||
|
try {
|
||||||
|
// If 404, ratelimit returns a default value.
|
||||||
|
// This works as credential test because invalid credentials returns 401, not 404
|
||||||
|
getRateLimit();
|
||||||
|
return true;
|
||||||
|
} catch (IOException e) {
|
||||||
|
if (LOGGER.isLoggable(FINE))
|
||||||
|
LOGGER.log(FINE,
|
||||||
|
"Exception validating credentials on " + getApiUrl() + " with login '" + login + "' " + e,
|
||||||
|
e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this an always offline "connection".
|
||||||
|
*
|
||||||
|
* @return {@code true} if this is an always offline "connection".
|
||||||
|
*/
|
||||||
|
public boolean isOffline() {
|
||||||
|
return getConnector() == HttpConnector.OFFLINE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets connector.
|
||||||
|
*
|
||||||
|
* @return the connector
|
||||||
|
*/
|
||||||
|
public HttpConnector getConnector() {
|
||||||
|
return connector;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the custom connector used to make requests to GitHub.
|
||||||
|
*
|
||||||
|
* @param connector
|
||||||
|
* the connector
|
||||||
|
* @deprecated HttpConnector should not be changed.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public void setConnector(HttpConnector connector) {
|
||||||
|
LOGGER.warning("Connector should not be changed. Please file an issue describing your use case.");
|
||||||
|
this.connector = connector;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this an anonymous connection
|
||||||
|
*
|
||||||
|
* @return {@code true} if operations that require authentication will fail.
|
||||||
|
*/
|
||||||
|
public boolean isAnonymous() {
|
||||||
|
return login == null && encodedAuthorization == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the current full rate limit information from the server.
|
||||||
|
*
|
||||||
|
* For some versions of GitHub Enterprise, the {@code /rate_limit} endpoint returns a {@code 404 Not Found}. In that
|
||||||
|
* case, the most recent {@link GHRateLimit} information will be returned, including rate limit information returned
|
||||||
|
* in the response header for this request in if was present.
|
||||||
|
*
|
||||||
|
* For most use cases it would be better to implement a {@link RateLimitChecker} and add it via
|
||||||
|
* {@link GitHubBuilder#withRateLimitChecker(RateLimitChecker)}.
|
||||||
|
*
|
||||||
|
* @return the rate limit
|
||||||
|
* @throws IOException
|
||||||
|
* the io exception
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public GHRateLimit getRateLimit() throws IOException {
|
||||||
|
return getRateLimit(RateLimitTarget.NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
GHRateLimit getRateLimit(@Nonnull RateLimitTarget rateLimitTarget) throws IOException {
|
||||||
|
GHRateLimit result;
|
||||||
|
try {
|
||||||
|
GitHubRequest request = GitHubRequest.newBuilder()
|
||||||
|
.rateLimit(RateLimitTarget.NONE)
|
||||||
|
.withApiUrl(getApiUrl())
|
||||||
|
.withUrlPath("/rate_limit")
|
||||||
|
.build();
|
||||||
|
result = this
|
||||||
|
.sendRequest(request, (responseInfo) -> GitHubResponse.parseBody(responseInfo, JsonRateLimit.class))
|
||||||
|
.body().resources;
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
// For some versions of GitHub Enterprise, the rate_limit endpoint returns a 404.
|
||||||
|
LOGGER.log(FINE, "/rate_limit returned 404 Not Found.");
|
||||||
|
|
||||||
|
// However some newer versions of GHE include rate limit header information
|
||||||
|
// If the header info is missing and the endpoint returns 404, fill the rate limit
|
||||||
|
// with unknown
|
||||||
|
result = GHRateLimit.fromRecord(GHRateLimit.UnknownLimitRecord.current(), rateLimitTarget);
|
||||||
|
}
|
||||||
|
return updateRateLimit(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the most recently observed rate limit data.
|
||||||
|
*
|
||||||
|
* Generally, instead of calling this you should implement a {@link RateLimitChecker} or call
|
||||||
|
*
|
||||||
|
* @return the most recently observed rate limit data. This may include expired or
|
||||||
|
* {@link GHRateLimit.UnknownLimitRecord} entries.
|
||||||
|
* @deprecated implement a {@link RateLimitChecker} and add it via
|
||||||
|
* {@link GitHubBuilder#withRateLimitChecker(RateLimitChecker)}.
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
@Deprecated
|
||||||
|
GHRateLimit lastRateLimit() {
|
||||||
|
synchronized (rateLimitLock) {
|
||||||
|
return rateLimit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the current rate limit for an endpoint while trying not to actually make any remote requests unless
|
||||||
|
* absolutely necessary.
|
||||||
|
*
|
||||||
|
* If the {@link GHRateLimit.Record} for {@code urlPath} is not expired, it is returned. If the
|
||||||
|
* {@link GHRateLimit.Record} for {@code urlPath} is expired, {@link #getRateLimit()} will be called to get the
|
||||||
|
* current rate limit.
|
||||||
|
*
|
||||||
|
* @param rateLimitTarget
|
||||||
|
* the endpoint to get the rate limit for.
|
||||||
|
*
|
||||||
|
* @return the current rate limit data. {@link GHRateLimit.Record}s in this instance may be expired when returned.
|
||||||
|
* @throws IOException
|
||||||
|
* if there was an error getting current rate limit data.
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
GHRateLimit rateLimit(@Nonnull RateLimitTarget rateLimitTarget) throws IOException {
|
||||||
|
synchronized (rateLimitLock) {
|
||||||
|
if (rateLimit.getRecord(rateLimitTarget).isExpired()) {
|
||||||
|
getRateLimit(rateLimitTarget);
|
||||||
|
}
|
||||||
|
return rateLimit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the Rate Limit with the latest info from response header.
|
||||||
|
*
|
||||||
|
* Due to multi-threading, requests might complete out of order. This method calls
|
||||||
|
* {@link GHRateLimit#getMergedRateLimit(GHRateLimit)} to ensure the most current records are used.
|
||||||
|
*
|
||||||
|
* @param observed
|
||||||
|
* {@link GHRateLimit.Record} constructed from the response header information
|
||||||
|
*/
|
||||||
|
private GHRateLimit updateRateLimit(@Nonnull GHRateLimit observed) {
|
||||||
|
synchronized (rateLimitLock) {
|
||||||
|
observed = rateLimit.getMergedRateLimit(observed);
|
||||||
|
|
||||||
|
if (rateLimit != observed) {
|
||||||
|
rateLimit = observed;
|
||||||
|
LOGGER.log(FINE, "Rate limit now: {0}", rateLimit);
|
||||||
|
}
|
||||||
|
return rateLimit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
* the io exception
|
||||||
|
*/
|
||||||
|
public void checkApiUrlValidity() throws IOException {
|
||||||
|
try {
|
||||||
|
fetch(GHApiInfo.class, "/").check(getApiUrl());
|
||||||
|
} catch (IOException e) {
|
||||||
|
if (isPrivateModeEnabled()) {
|
||||||
|
throw (IOException) new IOException(
|
||||||
|
"GitHub Enterprise server (" + getApiUrl() + ") with private mode enabled").initCause(e);
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getApiUrl() {
|
||||||
|
return apiUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a {@link GitHubRequest}, sends the {@link GitHubRequest} to the server, and uses the
|
||||||
|
* {@link GitHubResponse.BodyHandler} to parse the response info and response body data into an instance of
|
||||||
|
* {@link T}.
|
||||||
|
*
|
||||||
|
* @param builder
|
||||||
|
* used to build the request that will be sent to the server.
|
||||||
|
* @param handler
|
||||||
|
* parse the response info and body data into a instance of {@link T}. If null, no parsing occurs and
|
||||||
|
* {@link GitHubResponse#body()} will return null.
|
||||||
|
* @param <T>
|
||||||
|
* the type of the parse body data.
|
||||||
|
* @return a {@link GitHubResponse} containing the parsed body data as a {@link T}. Parsed instance may be null.
|
||||||
|
* @throws IOException
|
||||||
|
* if an I/O Exception occurs
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public <T> GitHubResponse<T> sendRequest(@Nonnull GitHubRequest.Builder<?> builder,
|
||||||
|
@CheckForNull GitHubResponse.BodyHandler<T> handler) throws IOException {
|
||||||
|
return sendRequest(builder.build(), handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends the {@link GitHubRequest} to the server, and uses the {@link GitHubResponse.BodyHandler} to parse the
|
||||||
|
* response info and response body data into an instance of {@link T}.
|
||||||
|
*
|
||||||
|
* @param request
|
||||||
|
* the request that will be sent to the server.
|
||||||
|
* @param handler
|
||||||
|
* parse the response info and body data into a instance of {@link T}. If null, no parsing occurs and
|
||||||
|
* {@link GitHubResponse#body()} will return null.
|
||||||
|
* @param <T>
|
||||||
|
* the type of the parse body data.
|
||||||
|
* @return a {@link GitHubResponse} containing the parsed body data as a {@link T}. Parsed instance may be null.
|
||||||
|
* @throws IOException
|
||||||
|
* if an I/O Exception occurs
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public <T> GitHubResponse<T> sendRequest(GitHubRequest request, @CheckForNull GitHubResponse.BodyHandler<T> handler)
|
||||||
|
throws IOException {
|
||||||
|
int retries = CONNECTION_ERROR_RETRIES;
|
||||||
|
|
||||||
|
do {
|
||||||
|
// if we fail to create a connection we do not retry and we do not wrap
|
||||||
|
|
||||||
|
GitHubResponse.ResponseInfo responseInfo = null;
|
||||||
|
try {
|
||||||
|
try {
|
||||||
|
if (LOGGER.isLoggable(FINE)) {
|
||||||
|
LOGGER.log(FINE,
|
||||||
|
"GitHub API request [" + (login == null ? "anonymous" : login) + "]: "
|
||||||
|
+ request.method() + " " + request.url().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
rateLimitChecker.checkRateLimit(this, request);
|
||||||
|
|
||||||
|
responseInfo = getResponseInfo(request);
|
||||||
|
noteRateLimit(responseInfo);
|
||||||
|
detectOTPRequired(responseInfo);
|
||||||
|
|
||||||
|
if (isInvalidCached404Response(responseInfo)) {
|
||||||
|
// Setting "Cache-Control" to "no-cache" stops the cache from supplying
|
||||||
|
// "If-Modified-Since" or "If-None-Match" values.
|
||||||
|
// This makes GitHub give us current data (not incorrectly cached data)
|
||||||
|
request = request.toBuilder().setHeader("Cache-Control", "no-cache").build();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!(isRateLimitResponse(responseInfo) || isAbuseLimitResponse(responseInfo))) {
|
||||||
|
return createResponse(responseInfo, handler);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
// For transient errors, retry
|
||||||
|
if (retryConnectionError(e, request.url(), retries)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw interpretApiError(e, request, responseInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleLimitingErrors(responseInfo);
|
||||||
|
} finally {
|
||||||
|
IOUtils.closeQuietly(responseInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
} while (--retries >= 0);
|
||||||
|
|
||||||
|
throw new GHIOException("Ran out of retries for URL: " + request.url().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
protected abstract GitHubResponse.ResponseInfo getResponseInfo(GitHubRequest request) throws IOException;
|
||||||
|
|
||||||
|
protected abstract void handleLimitingErrors(@Nonnull GitHubResponse.ResponseInfo responseInfo) throws IOException;
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
private static <T> GitHubResponse<T> createResponse(@Nonnull GitHubResponse.ResponseInfo responseInfo,
|
||||||
|
@CheckForNull GitHubResponse.BodyHandler<T> handler) throws IOException {
|
||||||
|
T body = null;
|
||||||
|
if (responseInfo.statusCode() == HttpURLConnection.HTTP_NOT_MODIFIED) {
|
||||||
|
// special case handling for 304 unmodified, as the content will be ""
|
||||||
|
} else if (responseInfo.statusCode() == HttpURLConnection.HTTP_ACCEPTED) {
|
||||||
|
|
||||||
|
// Response code 202 means data is being generated.
|
||||||
|
// This happens in specific cases:
|
||||||
|
// statistics - See https://developer.github.com/v3/repos/statistics/#a-word-about-caching
|
||||||
|
// fork creation - See https://developer.github.com/v3/repos/forks/#create-a-fork
|
||||||
|
|
||||||
|
if (responseInfo.url().toString().endsWith("/forks")) {
|
||||||
|
LOGGER.log(INFO, "The fork is being created. Please try again in 5 seconds.");
|
||||||
|
} else if (responseInfo.url().toString().endsWith("/statistics")) {
|
||||||
|
LOGGER.log(INFO, "The statistics are being generated. Please try again in 5 seconds.");
|
||||||
|
} else {
|
||||||
|
LOGGER.log(INFO,
|
||||||
|
"Received 202 from " + responseInfo.url().toString() + " . Please try again in 5 seconds.");
|
||||||
|
}
|
||||||
|
// Maybe throw an exception instead?
|
||||||
|
} else if (handler != null) {
|
||||||
|
body = handler.apply(responseInfo);
|
||||||
|
}
|
||||||
|
return new GitHubResponse<>(responseInfo, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle API error by either throwing it or by returning normally to retry.
|
||||||
|
*/
|
||||||
|
private static IOException interpretApiError(IOException e,
|
||||||
|
@Nonnull GitHubRequest request,
|
||||||
|
@CheckForNull GitHubResponse.ResponseInfo responseInfo) throws IOException {
|
||||||
|
// If we're already throwing a GHIOException, pass through
|
||||||
|
if (e instanceof GHIOException) {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
int statusCode = -1;
|
||||||
|
String message = null;
|
||||||
|
Map<String, List<String>> headers = new HashMap<>();
|
||||||
|
String errorMessage = null;
|
||||||
|
|
||||||
|
if (responseInfo != null) {
|
||||||
|
statusCode = responseInfo.statusCode();
|
||||||
|
message = responseInfo.headerField("Status");
|
||||||
|
headers = responseInfo.headers();
|
||||||
|
errorMessage = responseInfo.errorMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errorMessage != null) {
|
||||||
|
if (e instanceof FileNotFoundException) {
|
||||||
|
// pass through 404 Not Found to allow the caller to handle it intelligently
|
||||||
|
e = new GHFileNotFoundException(e.getMessage() + " " + errorMessage, e)
|
||||||
|
.withResponseHeaderFields(headers);
|
||||||
|
} else if (statusCode >= 0) {
|
||||||
|
e = new HttpException(errorMessage, statusCode, message, request.url().toString(), e);
|
||||||
|
} else {
|
||||||
|
e = new GHIOException(errorMessage).withResponseHeaderFields(headers);
|
||||||
|
}
|
||||||
|
} else if (!(e instanceof FileNotFoundException)) {
|
||||||
|
e = new HttpException(statusCode, message, request.url().toString(), e);
|
||||||
|
}
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static boolean isRateLimitResponse(@Nonnull GitHubResponse.ResponseInfo responseInfo) {
|
||||||
|
return responseInfo.statusCode() == HttpURLConnection.HTTP_FORBIDDEN
|
||||||
|
&& "0".equals(responseInfo.headerField("X-RateLimit-Remaining"));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static boolean isAbuseLimitResponse(@Nonnull GitHubResponse.ResponseInfo responseInfo) {
|
||||||
|
return responseInfo.statusCode() == HttpURLConnection.HTTP_FORBIDDEN
|
||||||
|
&& responseInfo.headerField("Retry-After") != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean retryConnectionError(IOException e, URL url, int retries) throws IOException {
|
||||||
|
// There are a range of connection errors where we want to wait a moment and just automatically retry
|
||||||
|
boolean connectionError = e instanceof SocketException || e instanceof SocketTimeoutException
|
||||||
|
|| e instanceof SSLHandshakeException;
|
||||||
|
if (connectionError && retries > 0) {
|
||||||
|
LOGGER.log(INFO,
|
||||||
|
e.getMessage() + " while connecting to " + url + ". Sleeping " + GitHubClient.retryTimeoutMillis
|
||||||
|
+ " milliseconds before retrying... ; will try " + retries + " more time(s)");
|
||||||
|
try {
|
||||||
|
Thread.sleep(GitHubClient.retryTimeoutMillis);
|
||||||
|
} catch (InterruptedException ie) {
|
||||||
|
throw (IOException) new InterruptedIOException().initCause(e);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isInvalidCached404Response(GitHubResponse.ResponseInfo responseInfo) {
|
||||||
|
// WORKAROUND FOR ISSUE #669:
|
||||||
|
// When the Requester detects a 404 response with an ETag (only happpens when the server's 304
|
||||||
|
// is bogus and would cause cache corruption), try the query again with new request header
|
||||||
|
// that forces the server to not return 304 and return new data instead.
|
||||||
|
//
|
||||||
|
// This solution is transparent to users of this library and automatically handles a
|
||||||
|
// situation that was cause insidious and hard to debug bad responses in caching
|
||||||
|
// scenarios. If GitHub ever fixes their issue and/or begins providing accurate ETags to
|
||||||
|
// their 404 responses, this will result in at worst two requests being made for each 404
|
||||||
|
// responses. However, only the second request will count against rate limit.
|
||||||
|
if (responseInfo.statusCode() == 404 && Objects.equals(responseInfo.request().method(), "GET")
|
||||||
|
&& responseInfo.headerField("ETag") != null
|
||||||
|
&& !Objects.equals(responseInfo.request().headers().get("Cache-Control"), "no-cache")) {
|
||||||
|
LOGGER.log(FINE,
|
||||||
|
"Encountered GitHub invalid cached 404 from " + responseInfo.url()
|
||||||
|
+ ". Retrying with \"Cache-Control\"=\"no-cache\"...");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void noteRateLimit(@Nonnull GitHubResponse.ResponseInfo responseInfo) {
|
||||||
|
try {
|
||||||
|
String limitString = Objects.requireNonNull(responseInfo.headerField("X-RateLimit-Limit"),
|
||||||
|
"Missing X-RateLimit-Limit");
|
||||||
|
String remainingString = Objects.requireNonNull(responseInfo.headerField("X-RateLimit-Remaining"),
|
||||||
|
"Missing X-RateLimit-Remaining");
|
||||||
|
String resetString = Objects.requireNonNull(responseInfo.headerField("X-RateLimit-Reset"),
|
||||||
|
"Missing X-RateLimit-Reset");
|
||||||
|
int limit, remaining;
|
||||||
|
long reset;
|
||||||
|
limit = Integer.parseInt(limitString);
|
||||||
|
remaining = Integer.parseInt(remainingString);
|
||||||
|
reset = Long.parseLong(resetString);
|
||||||
|
GHRateLimit.Record observed = new GHRateLimit.Record(limit, remaining, reset, responseInfo);
|
||||||
|
updateRateLimit(GHRateLimit.fromRecord(observed, responseInfo.request().rateLimitTarget()));
|
||||||
|
} catch (NumberFormatException | NullPointerException e) {
|
||||||
|
if (LOGGER.isLoggable(FINEST)) {
|
||||||
|
LOGGER.log(FINEST, "Missing or malformed X-RateLimit header: ", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void detectOTPRequired(@Nonnull GitHubResponse.ResponseInfo responseInfo) throws GHIOException {
|
||||||
|
// 401 Unauthorized == bad creds or OTP request
|
||||||
|
if (responseInfo.statusCode() == HTTP_UNAUTHORIZED) {
|
||||||
|
// In the case of a user with 2fa enabled, a header with X-GitHub-OTP
|
||||||
|
// will be returned indicating the user needs to respond with an otp
|
||||||
|
if (responseInfo.headerField("X-GitHub-OTP") != null) {
|
||||||
|
throw new GHOTPRequiredException().withResponseHeaderFields(responseInfo.headers());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void requireCredential() {
|
||||||
|
if (isAnonymous())
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"This operation requires a credential but none is given to the GitHub constructor");
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a GitHub Enterprise server is configured in private mode.
|
||||||
|
*
|
||||||
|
* In private mode response looks like:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* $ 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
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @return {@code true} if private mode is enabled. If it tries to use this method with GitHub, returns {@code
|
||||||
|
* false}.
|
||||||
|
*/
|
||||||
|
private boolean isPrivateModeEnabled() {
|
||||||
|
try {
|
||||||
|
GitHubResponse<?> response = sendRequest(GitHubRequest.newBuilder().withApiUrl(getApiUrl()), null);
|
||||||
|
return response.statusCode() == HTTP_UNAUTHORIZED && response.headerField("X-GitHub-Media-Type") != null;
|
||||||
|
} catch (IOException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static URL parseURL(String s) {
|
||||||
|
try {
|
||||||
|
return s == null ? null : new URL(s);
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
throw new IllegalStateException("Invalid URL: " + s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Date parseDate(String timestamp) {
|
||||||
|
if (timestamp == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return Date.from(parseInstant(timestamp));
|
||||||
|
}
|
||||||
|
|
||||||
|
static Instant parseInstant(String timestamp) {
|
||||||
|
if (timestamp == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (timestamp.charAt(4) == '/') {
|
||||||
|
// Unsure where this is used, but retained for compatibility.
|
||||||
|
return Instant.from(DATE_TIME_PARSER_SLASHES.parse(timestamp));
|
||||||
|
} else {
|
||||||
|
return Instant.from(DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(timestamp));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static String printDate(Date dt) {
|
||||||
|
return DateTimeFormatter.ISO_INSTANT.format(Instant.ofEpochMilli(dt.getTime()).truncatedTo(ChronoUnit.SECONDS));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an {@link ObjectWriter}.
|
||||||
|
*
|
||||||
|
* @return an {@link ObjectWriter} instance that can be further configured.
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
static ObjectWriter getMappingObjectWriter() {
|
||||||
|
return MAPPER.writer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper for {@link #getMappingObjectReader(GitHubResponse.ResponseInfo)}
|
||||||
|
*
|
||||||
|
* @param root
|
||||||
|
* the root GitHub object for this reader
|
||||||
|
*
|
||||||
|
* @return an {@link ObjectReader} instance that can be further configured.
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
static ObjectReader getMappingObjectReader(@Nonnull GitHub root) {
|
||||||
|
ObjectReader reader = getMappingObjectReader((GitHubResponse.ResponseInfo) null);
|
||||||
|
((InjectableValues.Std) reader.getInjectableValues()).addValue(GitHub.class, root);
|
||||||
|
return reader;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an {@link ObjectReader}.
|
||||||
|
*
|
||||||
|
* Members of {@link InjectableValues} must be present even if {@code null}, otherwise classes expecting those
|
||||||
|
* values will fail to read. This differs from regular JSONProperties which provide defaults instead of failing.
|
||||||
|
*
|
||||||
|
* Having one spot to create readers and having it take all injectable values is not a great long term solution but
|
||||||
|
* it is sufficient for this first cut.
|
||||||
|
*
|
||||||
|
* @param responseInfo
|
||||||
|
* the {@link GitHubResponse.ResponseInfo} to inject for this reader.
|
||||||
|
*
|
||||||
|
* @return an {@link ObjectReader} instance that can be further configured.
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
static ObjectReader getMappingObjectReader(@CheckForNull GitHubResponse.ResponseInfo responseInfo) {
|
||||||
|
Map<String, Object> injected = new HashMap<>();
|
||||||
|
|
||||||
|
// Required or many things break
|
||||||
|
injected.put(GitHubResponse.ResponseInfo.class.getName(), null);
|
||||||
|
injected.put(GitHub.class.getName(), null);
|
||||||
|
|
||||||
|
if (responseInfo != null) {
|
||||||
|
injected.put(GitHubResponse.ResponseInfo.class.getName(), responseInfo);
|
||||||
|
injected.putAll(responseInfo.request().injectedMappingValues());
|
||||||
|
}
|
||||||
|
return MAPPER.reader(new InjectableValues.Std(injected));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,244 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.ProtocolException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
|
import static java.util.logging.Level.*;
|
||||||
|
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A GitHub API Client for HttpUrlConnection
|
||||||
|
* <p>
|
||||||
|
* A GitHubClient can be used to send requests and retrieve their responses. GitHubClient is thread-safe and can be used
|
||||||
|
* to send multiple requests. GitHubClient also track some GitHub API information such as {@link GHRateLimit}.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* GitHubHttpUrlConnectionClient gets a new {@link HttpURLConnection} for each call to send.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
class GitHubHttpUrlConnectionClient extends GitHubClient {
|
||||||
|
|
||||||
|
GitHubHttpUrlConnectionClient(String apiUrl,
|
||||||
|
String login,
|
||||||
|
String oauthAccessToken,
|
||||||
|
String jwtToken,
|
||||||
|
String password,
|
||||||
|
HttpConnector connector,
|
||||||
|
RateLimitHandler rateLimitHandler,
|
||||||
|
AbuseLimitHandler abuseLimitHandler,
|
||||||
|
GitHubRateLimitChecker rateLimitChecker,
|
||||||
|
Consumer<GHMyself> myselfConsumer) throws IOException {
|
||||||
|
super(apiUrl,
|
||||||
|
login,
|
||||||
|
oauthAccessToken,
|
||||||
|
jwtToken,
|
||||||
|
password,
|
||||||
|
connector,
|
||||||
|
rateLimitHandler,
|
||||||
|
abuseLimitHandler,
|
||||||
|
rateLimitChecker,
|
||||||
|
myselfConsumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
protected GitHubResponse.ResponseInfo getResponseInfo(GitHubRequest request) throws IOException {
|
||||||
|
HttpURLConnection connection;
|
||||||
|
try {
|
||||||
|
connection = HttpURLConnectionResponseInfo.setupConnection(this, request);
|
||||||
|
} catch (IOException e) {
|
||||||
|
// An error in here should be wrapped to bypass http exception wrapping.
|
||||||
|
throw new GHIOException(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// HttpUrlConnection is nuts. This call opens the connection and gets a response.
|
||||||
|
// Putting this on it's own line for ease of debugging if needed.
|
||||||
|
int statusCode = connection.getResponseCode();
|
||||||
|
Map<String, List<String>> headers = connection.getHeaderFields();
|
||||||
|
|
||||||
|
return new HttpURLConnectionResponseInfo(request, statusCode, headers, connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void handleLimitingErrors(@Nonnull GitHubResponse.ResponseInfo responseInfo) throws IOException {
|
||||||
|
if (isRateLimitResponse(responseInfo)) {
|
||||||
|
GHIOException e = new HttpException("Rate limit violation",
|
||||||
|
responseInfo.statusCode(),
|
||||||
|
responseInfo.headerField("Status"),
|
||||||
|
responseInfo.url().toString()).withResponseHeaderFields(responseInfo.headers());
|
||||||
|
rateLimitHandler.onError(e, ((HttpURLConnectionResponseInfo) responseInfo).connection);
|
||||||
|
} else if (isAbuseLimitResponse(responseInfo)) {
|
||||||
|
GHIOException e = new HttpException("Abuse limit violation",
|
||||||
|
responseInfo.statusCode(),
|
||||||
|
responseInfo.headerField("Status"),
|
||||||
|
responseInfo.url().toString()).withResponseHeaderFields(responseInfo.headers());
|
||||||
|
abuseLimitHandler.onError(e, ((HttpURLConnectionResponseInfo) responseInfo).connection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initial response information supplied to a {@link GitHubResponse.BodyHandler} when a response is initially
|
||||||
|
* received and before the body is processed.
|
||||||
|
*
|
||||||
|
* Implementation specific to {@link HttpURLConnection}.
|
||||||
|
*/
|
||||||
|
static class HttpURLConnectionResponseInfo extends GitHubResponse.ResponseInfo {
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
private final HttpURLConnection connection;
|
||||||
|
|
||||||
|
HttpURLConnectionResponseInfo(@Nonnull GitHubRequest request,
|
||||||
|
int statusCode,
|
||||||
|
@Nonnull Map<String, List<String>> headers,
|
||||||
|
@Nonnull HttpURLConnection connection) {
|
||||||
|
super(request, statusCode, headers);
|
||||||
|
this.connection = connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
static HttpURLConnection setupConnection(@Nonnull GitHubClient client, @Nonnull GitHubRequest request)
|
||||||
|
throws IOException {
|
||||||
|
HttpURLConnection connection = client.getConnector().connect(request.url());
|
||||||
|
|
||||||
|
// if the authentication is needed but no credential is given, try it anyway (so that some calls
|
||||||
|
// that do work with anonymous access in the reduced form should still work.)
|
||||||
|
if (client.encodedAuthorization != null)
|
||||||
|
connection.setRequestProperty("Authorization", client.encodedAuthorization);
|
||||||
|
|
||||||
|
setRequestMethod(request.method(), connection);
|
||||||
|
buildRequest(request, connection);
|
||||||
|
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set up the request parameters or POST payload.
|
||||||
|
*/
|
||||||
|
private static void buildRequest(GitHubRequest request, HttpURLConnection connection) throws IOException {
|
||||||
|
for (Map.Entry<String, String> e : request.headers().entrySet()) {
|
||||||
|
String v = e.getValue();
|
||||||
|
if (v != null)
|
||||||
|
connection.setRequestProperty(e.getKey(), v);
|
||||||
|
}
|
||||||
|
connection.setRequestProperty("Accept-Encoding", "gzip");
|
||||||
|
|
||||||
|
if (request.inBody()) {
|
||||||
|
connection.setDoOutput(true);
|
||||||
|
|
||||||
|
try (InputStream body = request.body()) {
|
||||||
|
if (body != null) {
|
||||||
|
connection.setRequestProperty("Content-type",
|
||||||
|
defaultString(request.contentType(), "application/x-www-form-urlencoded"));
|
||||||
|
byte[] bytes = new byte[32768];
|
||||||
|
int read;
|
||||||
|
while ((read = body.read(bytes)) != -1) {
|
||||||
|
connection.getOutputStream().write(bytes, 0, read);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
connection.setRequestProperty("Content-type",
|
||||||
|
defaultString(request.contentType(), "application/json"));
|
||||||
|
Map<String, Object> json = new HashMap<>();
|
||||||
|
for (GitHubRequest.Entry e : request.args()) {
|
||||||
|
json.put(e.key, e.value);
|
||||||
|
}
|
||||||
|
getMappingObjectWriter().writeValue(connection.getOutputStream(), json);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void setRequestMethod(String method, HttpURLConnection connection) throws IOException {
|
||||||
|
try {
|
||||||
|
connection.setRequestMethod(method);
|
||||||
|
} catch (ProtocolException e) {
|
||||||
|
// JDK only allows one of the fixed set of verbs. Try to override that
|
||||||
|
try {
|
||||||
|
Field $method = HttpURLConnection.class.getDeclaredField("method");
|
||||||
|
$method.setAccessible(true);
|
||||||
|
$method.set(connection, method);
|
||||||
|
} 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 = connection.getClass().getDeclaredField("delegate");
|
||||||
|
$delegate.setAccessible(true);
|
||||||
|
Object delegate = $delegate.get(connection);
|
||||||
|
if (delegate instanceof HttpURLConnection) {
|
||||||
|
HttpURLConnection nested = (HttpURLConnection) delegate;
|
||||||
|
setRequestMethod(method, nested);
|
||||||
|
}
|
||||||
|
} catch (NoSuchFieldException x) {
|
||||||
|
// no problem
|
||||||
|
} catch (IllegalAccessException x) {
|
||||||
|
throw (IOException) new IOException("Failed to set the custom verb").initCause(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!connection.getRequestMethod().equals(method))
|
||||||
|
throw new IllegalStateException("Failed to set the request method to " + method);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
InputStream bodyStream() throws IOException {
|
||||||
|
return wrapStream(connection.getInputStream());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
String errorMessage() {
|
||||||
|
String result = null;
|
||||||
|
InputStream stream = null;
|
||||||
|
try {
|
||||||
|
stream = connection.getErrorStream();
|
||||||
|
if (stream != null) {
|
||||||
|
result = IOUtils.toString(wrapStream(stream), StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOGGER.log(FINER, "Ignored exception get error message", e);
|
||||||
|
} finally {
|
||||||
|
IOUtils.closeQuietly(stream);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the "Content-Encoding" header.
|
||||||
|
*
|
||||||
|
* @param stream
|
||||||
|
* the stream to possibly wrap
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private InputStream wrapStream(InputStream stream) throws IOException {
|
||||||
|
String encoding = headerField("Content-Encoding");
|
||||||
|
if (encoding == null || stream == null)
|
||||||
|
return stream;
|
||||||
|
if (encoding.equals("gzip"))
|
||||||
|
return new GZIPInputStream(stream);
|
||||||
|
|
||||||
|
throw new UnsupportedOperationException("Unexpected Content-Encoding: " + encoding);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(GitHubClient.class.getName());
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
IOUtils.closeQuietly(connection.getInputStream());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link PagedIterable} implementation that take a {@link Consumer} that initializes all the items on each page as they
|
||||||
|
* are retrieved.
|
||||||
|
*
|
||||||
|
* {@link GitHubPageContentsIterable} is immutable and thread-safe, but the iterator returned from {@link #iterator()}
|
||||||
|
* is not. Any one instance of iterator should only be called from a single thread.
|
||||||
|
*
|
||||||
|
* @param <T>
|
||||||
|
* the type of items on each page
|
||||||
|
*/
|
||||||
|
class GitHubPageContentsIterable<T> extends PagedIterable<T> {
|
||||||
|
|
||||||
|
private final GitHubClient client;
|
||||||
|
private final GitHubRequest request;
|
||||||
|
private final Class<T[]> receiverType;
|
||||||
|
private final Consumer<T> itemInitializer;
|
||||||
|
|
||||||
|
GitHubPageContentsIterable(GitHubClient client,
|
||||||
|
GitHubRequest request,
|
||||||
|
Class<T[]> receiverType,
|
||||||
|
Consumer<T> itemInitializer) {
|
||||||
|
this.client = client;
|
||||||
|
this.request = request;
|
||||||
|
this.receiverType = receiverType;
|
||||||
|
this.itemInitializer = itemInitializer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Nonnull
|
||||||
|
public PagedIterator<T> _iterator(int pageSize) {
|
||||||
|
final GitHubPageIterator<T[]> iterator = GitHubPageIterator.create(client, receiverType, request, pageSize);
|
||||||
|
return new GitHubPageContentsIterator(iterator, itemInitializer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Eagerly walk {@link Iterable} and return the result in a {@link GitHubResponse} containing an array of {@link T}
|
||||||
|
* items.
|
||||||
|
*
|
||||||
|
* @return the last response with an array containing all the results from all pages.
|
||||||
|
* @throws IOException
|
||||||
|
* if an I/O exception occurs.
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
GitHubResponse<T[]> toResponse() throws IOException {
|
||||||
|
GitHubPageContentsIterator iterator = (GitHubPageContentsIterator) iterator();
|
||||||
|
T[] items = toArray(iterator);
|
||||||
|
GitHubResponse<T[]> lastResponse = iterator.lastResponse();
|
||||||
|
return new GitHubResponse<>(lastResponse, items);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is not thread-safe. Any one instance should only be called from a single thread.
|
||||||
|
*/
|
||||||
|
private class GitHubPageContentsIterator extends PagedIterator<T> {
|
||||||
|
|
||||||
|
public GitHubPageContentsIterator(GitHubPageIterator<T[]> iterator, Consumer<T> itemInitializer) {
|
||||||
|
super(iterator, itemInitializer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the {@link GitHubResponse} for the last page received.
|
||||||
|
*
|
||||||
|
* @return the {@link GitHubResponse} for the last page received.
|
||||||
|
*/
|
||||||
|
private GitHubResponse<T[]> lastResponse() {
|
||||||
|
return ((GitHubPageIterator<T[]>) base).finalResponse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user