mirror of
https://github.com/jlengrand/github-api.git
synced 2026-03-11 08:21:23 +00:00
Compare commits
616 Commits
github-api
...
github-api
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e9368fb04e | ||
|
|
e2a1630cf4 | ||
|
|
4f15b7c9fa | ||
|
|
63f500ad7f | ||
|
|
a9fb4546e1 | ||
|
|
cabbbf7f02 | ||
|
|
59324b0082 | ||
|
|
6a356c82a5 | ||
|
|
70f0f5714a | ||
|
|
07b527a0f2 | ||
|
|
80aa75aab1 | ||
|
|
0cf4211aa5 | ||
|
|
1de02a5099 | ||
|
|
bb1cecb95b | ||
|
|
d82397a173 | ||
|
|
856cf5e568 | ||
|
|
9f5a6ee549 | ||
|
|
a2f0837d14 | ||
|
|
c7f6889534 | ||
|
|
0415326d09 | ||
|
|
16a0f8ece0 | ||
|
|
5d1ef296b3 | ||
|
|
16dbcde90b | ||
|
|
50cbf25c72 | ||
|
|
7b87de2b4c | ||
|
|
1ce54a7925 | ||
|
|
1bfe7dd99b | ||
|
|
27e855ddbd | ||
|
|
3d1bed0f8f | ||
|
|
5c9ea9b63a | ||
|
|
7c034f5670 | ||
|
|
3b9f5a417a | ||
|
|
cde501af8d | ||
|
|
3c5592c1c8 | ||
|
|
2508e022bb | ||
|
|
01fcbc24e8 | ||
|
|
204e639679 | ||
|
|
3d301ec730 | ||
|
|
9ab6d57019 | ||
|
|
37c473130f | ||
|
|
5f95987a48 | ||
|
|
d530b34073 | ||
|
|
35dec7a5ec | ||
|
|
baedad8124 | ||
|
|
007378c3a6 | ||
|
|
beae9fd6ec | ||
|
|
b7507076c6 | ||
|
|
6b5ade3ca0 | ||
|
|
715192d26c | ||
|
|
ce140460af | ||
|
|
d30b0403ce | ||
|
|
255c993548 | ||
|
|
557ae4165c | ||
|
|
a31395ed80 | ||
|
|
397886d289 | ||
|
|
7307bec2ae | ||
|
|
0cd5147e1a | ||
|
|
36d5b092d7 | ||
|
|
f9014dbab3 | ||
|
|
755d5f77ea | ||
|
|
906d9af7b7 | ||
|
|
14dcb37ee1 | ||
|
|
c1c2a27358 | ||
|
|
5ab9657f9c | ||
|
|
7a78f9f5aa | ||
|
|
ac8c65f062 | ||
|
|
1954a9f3f8 | ||
|
|
3b764f9c90 | ||
|
|
10f55cc549 | ||
|
|
cd8d955646 | ||
|
|
bba07c9080 | ||
|
|
dba84a33b9 | ||
|
|
ae49166aa2 | ||
|
|
e09185fd0e | ||
|
|
56379bb3b9 | ||
|
|
027e4b4f25 | ||
|
|
ba951cb6e3 | ||
|
|
ae85cf4b6c | ||
|
|
dbc79f8c42 | ||
|
|
54c3070607 | ||
|
|
013eaa30b6 | ||
|
|
751043bf81 | ||
|
|
14f7198a07 | ||
|
|
94af819ae5 | ||
|
|
dbcc9afbc7 | ||
|
|
8556033ae6 | ||
|
|
650493f863 | ||
|
|
d80ad77871 | ||
|
|
f4b129b9f1 | ||
|
|
c0a05e0650 | ||
|
|
33d95d3e3a | ||
|
|
f573f83fb9 | ||
|
|
e94c36b7e6 | ||
|
|
c879e9e34d | ||
|
|
ac39b564a8 | ||
|
|
d91388aba4 | ||
|
|
733d78abdd | ||
|
|
d5809e375c | ||
|
|
2440a676bd | ||
|
|
03ac6c72e7 | ||
|
|
1bbbcabae0 | ||
|
|
9149b6b998 | ||
|
|
2603b5a402 | ||
|
|
dbddf5b9eb | ||
|
|
841f77bac2 | ||
|
|
83ffe75baa | ||
|
|
8cb7094803 | ||
|
|
ed8cd0ad19 | ||
|
|
90d8e65a3b | ||
|
|
b24fcb18af | ||
|
|
c2f2d0f8af | ||
|
|
e33bdd7e62 | ||
|
|
402adc3559 | ||
|
|
0f45d03c51 | ||
|
|
0397d7ab53 | ||
|
|
79f86b82e4 | ||
|
|
261a7a34e3 | ||
|
|
723bb89e10 | ||
|
|
832e4f3c37 | ||
|
|
75a4081549 | ||
|
|
f9291f9fd1 | ||
|
|
c3b4ee9321 | ||
|
|
f86896943d | ||
|
|
c33f05e8ca | ||
|
|
52727ded03 | ||
|
|
f7d132758e | ||
|
|
bb17ca9a53 | ||
|
|
aab21c5b17 | ||
|
|
acbafee02a | ||
|
|
c6d2b1a222 | ||
|
|
b037f75fb0 | ||
|
|
a1e79d3050 | ||
|
|
354969d5fa | ||
|
|
a371892409 | ||
|
|
b0789a7ce7 | ||
|
|
08be8eb4f8 | ||
|
|
bafddf4baf | ||
|
|
75b9184a00 | ||
|
|
5dc83cf2bf | ||
|
|
092e9062c8 | ||
|
|
512c921a81 | ||
|
|
defcd6fe26 | ||
|
|
025b6cbfb7 | ||
|
|
b0687dbeb5 | ||
|
|
e0b109cba6 | ||
|
|
adaa8ece89 | ||
|
|
6516b20e16 | ||
|
|
839cb03690 | ||
|
|
2bcd99b14f | ||
|
|
e3ebf6e8a1 | ||
|
|
76d28314b0 | ||
|
|
ec450b8fd8 | ||
|
|
79c5b2edd5 | ||
|
|
2d45ac51ef | ||
|
|
505bb8f06d | ||
|
|
f8408bd29f | ||
|
|
5f2c84a913 | ||
|
|
3011c99e3f | ||
|
|
ebc97f42ad | ||
|
|
4660c6d363 | ||
|
|
e239ef50ba | ||
|
|
8b4312a880 | ||
|
|
90daf8087e | ||
|
|
dd21bcb34c | ||
|
|
c4113f1ac7 | ||
|
|
efa48acd1d | ||
|
|
15e4d07a6d | ||
|
|
cb2248809c | ||
|
|
eb9551d81b | ||
|
|
87d1256a1b | ||
|
|
dd6179cf25 | ||
|
|
23c56ff887 | ||
|
|
c4eefa6917 | ||
|
|
202cff58f2 | ||
|
|
025806f0fd | ||
|
|
b0c54ef0f1 | ||
|
|
4d7681b1a4 | ||
|
|
340fb3f624 | ||
|
|
a83aad22ca | ||
|
|
5a418dcce6 | ||
|
|
ec5392708f | ||
|
|
901db92b11 | ||
|
|
01b8b10344 | ||
|
|
698d642ec8 | ||
|
|
90d1047fb2 | ||
|
|
b0d1eac477 | ||
|
|
cce02aec3d | ||
|
|
492ff58aa8 | ||
|
|
dd3e73996b | ||
|
|
931ed7adac | ||
|
|
861fd55d06 | ||
|
|
9a4eee4e7d | ||
|
|
ed76cdbddf | ||
|
|
9d91549803 | ||
|
|
a5425a3c71 | ||
|
|
f4b105b10f | ||
|
|
e4de09c55b | ||
|
|
d77be9d382 | ||
|
|
626909addb | ||
|
|
9b750bedef | ||
|
|
b976e0ef4e | ||
|
|
fd434292ad | ||
|
|
7b4d3a869b | ||
|
|
eeebb1b59f | ||
|
|
63136f64b7 | ||
|
|
1d2fbf2d92 | ||
|
|
1e52dded14 | ||
|
|
cfc7005275 | ||
|
|
f23afcd5aa | ||
|
|
4e88a0c91b | ||
|
|
d070f9deb0 | ||
|
|
b736e20a74 | ||
|
|
aad20d0a03 | ||
|
|
7ff97348d9 | ||
|
|
68dda3a46d | ||
|
|
2cd44f8c33 | ||
|
|
9775954aff | ||
|
|
1a071b0b54 | ||
|
|
8dcea59c74 | ||
|
|
f482f77871 | ||
|
|
b058c39ee1 | ||
|
|
b926b6c67f | ||
|
|
3fb8e5f799 | ||
|
|
277ccb5188 | ||
|
|
9ebc9c0867 | ||
|
|
f1f96713a4 | ||
|
|
fc3b6d2c2e | ||
|
|
d0d0716b3b | ||
|
|
73119afeff | ||
|
|
8939179be8 | ||
|
|
adba2e68db | ||
|
|
0ef8b471a3 | ||
|
|
205950fc5f | ||
|
|
8835b2c745 | ||
|
|
74fda40764 | ||
|
|
687a36937e | ||
|
|
2c7b8bd6e8 | ||
|
|
e9417f5fa1 | ||
|
|
5e08b34c43 | ||
|
|
7b436ffb3b | ||
|
|
1ee2ec3728 | ||
|
|
ed28768146 | ||
|
|
f931835176 | ||
|
|
0cf9bc2814 | ||
|
|
8b428f2c93 | ||
|
|
10238dbcd3 | ||
|
|
6229e0928d | ||
|
|
5c7b259fe9 | ||
|
|
cc84c867c0 | ||
|
|
5bf252e12d | ||
|
|
75512ff66a | ||
|
|
6f4832476a | ||
|
|
86b0d27299 | ||
|
|
5a8845f7f6 | ||
|
|
709e47f32f | ||
|
|
77590b4eb3 | ||
|
|
72fc313135 | ||
|
|
39b32cee2e | ||
|
|
bdcee7c052 | ||
|
|
4093e53b5b | ||
|
|
a4c1c8de24 | ||
|
|
7ed234c875 | ||
|
|
0359160ac6 | ||
|
|
2478dad9b5 | ||
|
|
690292352b | ||
|
|
271d18cddc | ||
|
|
e1465639e7 | ||
|
|
ce7ca59339 | ||
|
|
76610b25d7 | ||
|
|
dfce0bda7c | ||
|
|
41c0dd9727 | ||
|
|
232c0389d3 | ||
|
|
d95c8a4ab0 | ||
|
|
374fdb37e1 | ||
|
|
f78530636e | ||
|
|
0bf81f4fb9 | ||
|
|
dcc3b7f36b | ||
|
|
d6722266f5 | ||
|
|
11566891dc | ||
|
|
9aaf69cc9a | ||
|
|
a716a59489 | ||
|
|
bad0d1bbcf | ||
|
|
aa43e265b7 | ||
|
|
67280951ff | ||
|
|
c3a9f6f9f5 | ||
|
|
e631e46dd1 | ||
|
|
15163ffde0 | ||
|
|
b898284821 | ||
|
|
3bb7eb2e03 | ||
|
|
11fcb9d456 | ||
|
|
29f826448a | ||
|
|
a8cf4a7120 | ||
|
|
60dce94a47 | ||
|
|
c965b9cc24 | ||
|
|
762a32eb6d | ||
|
|
541dac1aee | ||
|
|
e2e2329301 | ||
|
|
9afad71b0f | ||
|
|
7bbe0f7e8a | ||
|
|
d90adfa98e | ||
|
|
1dbcc4b776 | ||
|
|
18696fca2d | ||
|
|
c40100b6da | ||
|
|
6396818740 | ||
|
|
6df5a0d47b | ||
|
|
15c18c5547 | ||
|
|
4b6981c2e7 | ||
|
|
1b4025300c | ||
|
|
983c871bff | ||
|
|
609f532f8c | ||
|
|
c6fc03c73a | ||
|
|
b6e48cc4f9 | ||
|
|
50f43cc178 | ||
|
|
f421067a0d | ||
|
|
edd9a2d5b6 | ||
|
|
41e0329f55 | ||
|
|
21ea916e0d | ||
|
|
3b6ca3020e | ||
|
|
3737845b78 | ||
|
|
a30c78cd12 | ||
|
|
f8fba41a30 | ||
|
|
052902fb49 | ||
|
|
58143c26bc | ||
|
|
5d83894056 | ||
|
|
e35667525f | ||
|
|
2f318152d8 | ||
|
|
bc518a9ae8 | ||
|
|
425ae2d536 | ||
|
|
a6cacd4aba | ||
|
|
5a7083537c | ||
|
|
e15f7a59fd | ||
|
|
a2fa526aa0 | ||
|
|
7e959d6a87 | ||
|
|
5121fe1cbf | ||
|
|
3c3d4fc151 | ||
|
|
a7f75c9a6c | ||
|
|
e7262b8fbe | ||
|
|
af3099c526 | ||
|
|
2b9d47cea8 | ||
|
|
5db90d3fc4 | ||
|
|
372d5ff758 | ||
|
|
ebf39eaea1 | ||
|
|
556786f2e1 | ||
|
|
0f64994537 | ||
|
|
ac64c2022b | ||
|
|
9802132b6f | ||
|
|
4d6c5c14f1 | ||
|
|
d228a5fb93 | ||
|
|
7b46ef10c8 | ||
|
|
8eb9fba051 | ||
|
|
7b58182683 | ||
|
|
95fbf9274b | ||
|
|
09557dfa0f | ||
|
|
4029fcc1ca | ||
|
|
d6627b1e34 | ||
|
|
86d75fd767 | ||
|
|
e2220bb3b3 | ||
|
|
1a9b8bd1da | ||
|
|
9eda2d3f77 | ||
|
|
e6d59df705 | ||
|
|
db845850b2 | ||
|
|
d516597659 | ||
|
|
3af5a8145a | ||
|
|
31bebd4a7a | ||
|
|
ccb87258b0 | ||
|
|
2ab71e88bd | ||
|
|
78bd7585bb | ||
|
|
92cc81d9b8 | ||
|
|
97a1c741de | ||
|
|
077d693959 | ||
|
|
94831fc10a | ||
|
|
1f298a88b0 | ||
|
|
7e49946bed | ||
|
|
1e81ab1017 | ||
|
|
311180d12e | ||
|
|
0efb206881 | ||
|
|
0b92fa5615 | ||
|
|
a58a5b56b2 | ||
|
|
b9764c0a44 | ||
|
|
590f7ba511 | ||
|
|
449909b0e8 | ||
|
|
40780525f8 | ||
|
|
0e3707d1c3 | ||
|
|
8ddbef093b | ||
|
|
4ccdfccdf0 | ||
|
|
3c8aa0c630 | ||
|
|
ba519f996a | ||
|
|
62d9b92e6e | ||
|
|
d2c909584d | ||
|
|
219916eb14 | ||
|
|
0a37ac901f | ||
|
|
4586baea27 | ||
|
|
d83961e85b | ||
|
|
7e35716ff0 | ||
|
|
d0cf1ac605 | ||
|
|
9a19d07ab8 | ||
|
|
922be0b164 | ||
|
|
d0b8e2e37e | ||
|
|
e3d6e08b6a | ||
|
|
16a6623095 | ||
|
|
7bf31ca149 | ||
|
|
030f2360ca | ||
|
|
08eafc4214 | ||
|
|
134ece9385 | ||
|
|
3e4b06e959 | ||
|
|
3e3c6f70ba | ||
|
|
3097378a10 | ||
|
|
53f7a0f78a | ||
|
|
45a6841772 | ||
|
|
80b93a6e33 | ||
|
|
6aaab641be | ||
|
|
4999490c06 | ||
|
|
c7c1cd8bb3 | ||
|
|
8a1f116305 | ||
|
|
f8fe1dda6d | ||
|
|
bf44232d5c | ||
|
|
8c8ba47ef9 | ||
|
|
b3580002d0 | ||
|
|
1249199b22 | ||
|
|
f39a47f354 | ||
|
|
f58f15925f | ||
|
|
a071f8bb86 | ||
|
|
d33609ee57 | ||
|
|
f2c08bc1e8 | ||
|
|
688d8ed7bc | ||
|
|
2ddb4018b8 | ||
|
|
3ec25683f7 | ||
|
|
25d426f483 | ||
|
|
9d91ebc47a | ||
|
|
c784ab6632 | ||
|
|
ed5e3f13a1 | ||
|
|
3dcd82745e | ||
|
|
c564dc5f29 | ||
|
|
d64453b661 | ||
|
|
834ed7db0e | ||
|
|
db1dde533f | ||
|
|
9c66d9465e | ||
|
|
8c193d004e | ||
|
|
7cbd7f999a | ||
|
|
4340c8cb00 | ||
|
|
6f6583772d | ||
|
|
c62bc8d5da | ||
|
|
f68bfcb3e2 | ||
|
|
a37ade13cd | ||
|
|
1e0129f2d8 | ||
|
|
2e1d4381b0 | ||
|
|
a409b4f49c | ||
|
|
926776204b | ||
|
|
bba490127d | ||
|
|
80672c43c0 | ||
|
|
926e0ea0e5 | ||
|
|
6da169cd56 | ||
|
|
aaf10aa118 | ||
|
|
145bdacf13 | ||
|
|
51309efd46 | ||
|
|
60fe143b07 | ||
|
|
3f921b69f6 | ||
|
|
d2ac81d7ba | ||
|
|
2e5a9479a8 | ||
|
|
ec664ab6a2 | ||
|
|
8818bf8cf0 | ||
|
|
cc53e0c8ff | ||
|
|
8353499d18 | ||
|
|
d2c5994616 | ||
|
|
500b9097f7 | ||
|
|
7993266e6e | ||
|
|
74090103a9 | ||
|
|
a18cde2340 | ||
|
|
ee98e97876 | ||
|
|
ff63bafd72 | ||
|
|
3f8c8be77a | ||
|
|
769cdc73e7 | ||
|
|
cef8f74c00 | ||
|
|
9f5ab709de | ||
|
|
c6fdae3b3c | ||
|
|
320cf0fea2 | ||
|
|
0261f1262f | ||
|
|
5278ae3662 | ||
|
|
2941c44de2 | ||
|
|
627222602e | ||
|
|
e13b1ffc48 | ||
|
|
10bafce217 | ||
|
|
4817969495 | ||
|
|
2feda55eb7 | ||
|
|
a74cfd453a | ||
|
|
a360f65133 | ||
|
|
6a903d65a7 | ||
|
|
d4b3166036 | ||
|
|
21a54e2333 | ||
|
|
37c58bffba | ||
|
|
4a42b7277c | ||
|
|
071aea397b | ||
|
|
9c02f918c1 | ||
|
|
15c5c9615e | ||
|
|
0b74692d62 | ||
|
|
8f24f55eb7 | ||
|
|
cd5ab1ae9b | ||
|
|
d29ea97948 | ||
|
|
9930e7bb93 | ||
|
|
b1d37e4848 | ||
|
|
893152cdf3 | ||
|
|
efbf98751d | ||
|
|
caac294e79 | ||
|
|
d145ad8f47 | ||
|
|
628034ae8c | ||
|
|
fee02a3d16 | ||
|
|
ce5ae13bf6 | ||
|
|
26b6a94e32 | ||
|
|
be6ef475ba | ||
|
|
55e218ac37 | ||
|
|
7d1f636cdd | ||
|
|
a4b8b9b09b | ||
|
|
4e8e28d27c | ||
|
|
4b52414435 | ||
|
|
811b96bcbe | ||
|
|
f3207855ca | ||
|
|
9f3c5b93e3 | ||
|
|
a56357f0c1 | ||
|
|
f8d14d2e56 | ||
|
|
b0c30759c8 | ||
|
|
54037e6e10 | ||
|
|
fc260d4a37 | ||
|
|
387d4e5bc9 | ||
|
|
4ffd46b391 | ||
|
|
3b49370c06 | ||
|
|
925d26e54c | ||
|
|
1283006b53 | ||
|
|
83a718c9db | ||
|
|
2abf03ccb7 | ||
|
|
a59810a487 | ||
|
|
9cc9e06ad1 | ||
|
|
0108a0c146 | ||
|
|
4188758d84 | ||
|
|
2144100f81 | ||
|
|
8a61e04be6 | ||
|
|
eaeb083891 | ||
|
|
25e117b412 | ||
|
|
6b7ceed1a5 | ||
|
|
0533fc98a2 | ||
|
|
edde3ab2fb | ||
|
|
ec20e518be | ||
|
|
930a582afa | ||
|
|
7536eeea54 | ||
|
|
d940d43907 | ||
|
|
3ad3dd9493 | ||
|
|
553df7ac85 | ||
|
|
9648602252 | ||
|
|
63d0d13330 | ||
|
|
ff9b538a49 | ||
|
|
6632da34c0 | ||
|
|
178c9ff4d0 | ||
|
|
bfefeae5c1 | ||
|
|
ebf953cbc4 | ||
|
|
19ec3321ae | ||
|
|
d82af9f1a0 | ||
|
|
4712d2c8ac | ||
|
|
a2df4217fd | ||
|
|
096c96550b | ||
|
|
2fb3f31930 | ||
|
|
f52b4a2e11 | ||
|
|
bbc78ffec6 | ||
|
|
3606f412b3 | ||
|
|
6e0e94094b | ||
|
|
976960f495 | ||
|
|
d2f2f3b2d3 | ||
|
|
2da7c45840 | ||
|
|
fb078de627 | ||
|
|
da46b7fddb | ||
|
|
c4e0729b7d | ||
|
|
eee9f0ace5 | ||
|
|
d0692458a3 | ||
|
|
c96e6c7c92 | ||
|
|
4956278f17 | ||
|
|
5858a86624 | ||
|
|
c87d178a6a | ||
|
|
6fc872b1fd | ||
|
|
589c5783a0 | ||
|
|
40165628d6 | ||
|
|
435be77249 | ||
|
|
b932ba856d | ||
|
|
094514f617 | ||
|
|
fab96879d0 | ||
|
|
367a5f0c57 | ||
|
|
0d2ecfbc67 | ||
|
|
5410ba3b1d | ||
|
|
716bfd4611 | ||
|
|
3830a58493 | ||
|
|
31d5cf6129 | ||
|
|
8c78d20e6e | ||
|
|
abe78cf0bb | ||
|
|
eeebfd5f04 | ||
|
|
5e3d3dd023 | ||
|
|
60175ebfad | ||
|
|
c6fafe453f | ||
|
|
838ecd0dd8 | ||
|
|
beec605e2c | ||
|
|
da1405a060 | ||
|
|
e38eeae533 | ||
|
|
8442e7e326 | ||
|
|
e6c82e2003 | ||
|
|
c4de972c53 | ||
|
|
d2adbaec89 | ||
|
|
72736588c9 | ||
|
|
46e726363a | ||
|
|
f7e5292b8c | ||
|
|
f67954fa3e | ||
|
|
ba6c6a17ae | ||
|
|
c21d61a70d | ||
|
|
df857d3a84 | ||
|
|
e9564f101b | ||
|
|
e8a2a69649 | ||
|
|
ba416b1294 | ||
|
|
3024b598ad | ||
|
|
5ac7a34a13 | ||
|
|
b63181d9b5 | ||
|
|
a3119e2cc7 |
22
LICENSE.txt
Normal file
22
LICENSE.txt
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
Copyright (c) 2011- Kohsuke Kawaguchi and other contributors
|
||||||
|
|
||||||
|
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.
|
||||||
112
pom.xml
112
pom.xml
@@ -3,11 +3,11 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.kohsuke</groupId>
|
<groupId>org.kohsuke</groupId>
|
||||||
<artifactId>pom</artifactId>
|
<artifactId>pom</artifactId>
|
||||||
<version>4</version>
|
<version>14</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>github-api</artifactId>
|
<artifactId>github-api</artifactId>
|
||||||
<version>1.37</version>
|
<version>1.77</version>
|
||||||
<name>GitHub API for Java</name>
|
<name>GitHub API for Java</name>
|
||||||
<url>http://github-api.kohsuke.org/</url>
|
<url>http://github-api.kohsuke.org/</url>
|
||||||
<description>GitHub API for Java</description>
|
<description>GitHub API for Java</description>
|
||||||
@@ -16,6 +16,7 @@
|
|||||||
<connection>scm:git:git@github.com/kohsuke/${project.artifactId}.git</connection>
|
<connection>scm:git:git@github.com/kohsuke/${project.artifactId}.git</connection>
|
||||||
<developerConnection>scm:git:ssh://git@github.com/kohsuke/${project.artifactId}.git</developerConnection>
|
<developerConnection>scm:git:ssh://git@github.com/kohsuke/${project.artifactId}.git</developerConnection>
|
||||||
<url>http://${project.artifactId}.kohsuke.org/</url>
|
<url>http://${project.artifactId}.kohsuke.org/</url>
|
||||||
|
<tag>github-api-1.77</tag>
|
||||||
</scm>
|
</scm>
|
||||||
|
|
||||||
<distributionManagement>
|
<distributionManagement>
|
||||||
@@ -27,14 +28,37 @@
|
|||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<findbugs-maven-plugin.version>3.0.2</findbugs-maven-plugin.version>
|
||||||
|
<findbugs-maven-plugin.failOnError>true</findbugs-maven-plugin.failOnError>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.codehaus.mojo</groupId>
|
||||||
|
<artifactId>animal-sniffer-maven-plugin</artifactId>
|
||||||
|
<version>1.15</version>
|
||||||
|
<configuration>
|
||||||
|
<signature>
|
||||||
|
<groupId>org.codehaus.mojo.signature</groupId>
|
||||||
|
<artifactId>java15</artifactId>
|
||||||
|
<version>1.0</version>
|
||||||
|
</signature>
|
||||||
|
</configuration>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>ensure-java-1.5-class-library</id>
|
||||||
|
<phase>test</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>check</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>com.infradna.tool</groupId>
|
<groupId>com.infradna.tool</groupId>
|
||||||
<artifactId>bridge-method-injector</artifactId>
|
<artifactId>bridge-method-injector</artifactId>
|
||||||
<version>1.2</version>
|
<version>1.14</version>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<goals>
|
<goals>
|
||||||
@@ -43,6 +67,24 @@
|
|||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.codehaus.mojo</groupId>
|
||||||
|
<artifactId>findbugs-maven-plugin</artifactId>
|
||||||
|
<version>${findbugs-maven-plugin.version}</version>
|
||||||
|
<configuration>
|
||||||
|
<xmlOutput>true</xmlOutput>
|
||||||
|
<failOnError>${findbugs-maven-plugin.failOnError}</failOnError>
|
||||||
|
</configuration>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>run-findbugs</id>
|
||||||
|
<phase>verify</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>check</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
@@ -60,13 +102,13 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>junit</groupId>
|
<groupId>junit</groupId>
|
||||||
<artifactId>junit</artifactId>
|
<artifactId>junit</artifactId>
|
||||||
<version>3.8.1</version>
|
<version>4.11</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.codehaus.jackson</groupId>
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
<artifactId>jackson-mapper-asl</artifactId>
|
<artifactId>jackson-databind</artifactId>
|
||||||
<version>1.9.9</version>
|
<version>2.2.3</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>commons-io</groupId>
|
<groupId>commons-io</groupId>
|
||||||
@@ -76,8 +118,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.infradna.tool</groupId>
|
<groupId>com.infradna.tool</groupId>
|
||||||
<artifactId>bridge-method-annotation</artifactId>
|
<artifactId>bridge-method-annotation</artifactId>
|
||||||
<version>1.4</version>
|
<version>1.14</version>
|
||||||
<optional>true</optional>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.kohsuke.stapler</groupId>
|
<groupId>org.kohsuke.stapler</groupId>
|
||||||
@@ -85,14 +126,49 @@
|
|||||||
<version>1.1</version>
|
<version>1.1</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jgit</groupId>
|
||||||
|
<artifactId>org.eclipse.jgit</artifactId>
|
||||||
|
<version>3.1.0.201310021548-r</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.squareup.okhttp</groupId>
|
||||||
|
<artifactId>okhttp-urlconnection</artifactId>
|
||||||
|
<version>2.7.5</version>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.kohsuke</groupId>
|
||||||
|
<artifactId>wordnet-random-name</artifactId>
|
||||||
|
<version>1.2</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mockito</groupId>
|
||||||
|
<artifactId>mockito-all</artifactId>
|
||||||
|
<version>1.9.5</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.code.findbugs</groupId>
|
||||||
|
<artifactId>annotations</artifactId>
|
||||||
|
<version>3.0.0</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<repositories>
|
<repositories>
|
||||||
<repository>
|
<repository>
|
||||||
<id>repo.jenkins-ci.org</id>
|
<id>repo.jenkins-ci.org</id>
|
||||||
<url>http://repo.jenkins-ci.org/public/</url>
|
<url>http://repo.jenkins-ci.org/public/</url>
|
||||||
</repository>
|
</repository>
|
||||||
</repositories>
|
</repositories>
|
||||||
|
<pluginRepositories>
|
||||||
|
<pluginRepository>
|
||||||
|
<id>repo.jenkins-ci.org</id>
|
||||||
|
<url>http://repo.jenkins-ci.org/public/</url>
|
||||||
|
</pluginRepository>
|
||||||
|
</pluginRepositories>
|
||||||
|
|
||||||
<reporting>
|
<reporting>
|
||||||
<plugins>
|
<plugins>
|
||||||
@@ -102,4 +178,20 @@
|
|||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</reporting>
|
</reporting>
|
||||||
|
|
||||||
|
<licenses>
|
||||||
|
<license>
|
||||||
|
<name>The MIT license</name>
|
||||||
|
<url>http://www.opensource.org/licenses/mit-license.php</url>
|
||||||
|
<distribution>repo</distribution>
|
||||||
|
</license>
|
||||||
|
</licenses>
|
||||||
|
|
||||||
|
<mailingLists>
|
||||||
|
<mailingList>
|
||||||
|
<name>User List</name>
|
||||||
|
<post>github-api@googlegroups.com</post>
|
||||||
|
<archive>https://groups.google.com/forum/#!forum/github-api</archive>
|
||||||
|
</mailingList>
|
||||||
|
</mailingLists>
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
63
src/main/java/org/kohsuke/github/AbuseLimitHandler.java
Normal file
63
src/main/java/org/kohsuke/github/AbuseLimitHandler.java
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InterruptedIOException;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pluggable strategy to determine what to do when the API abuse limit is hit.
|
||||||
|
*
|
||||||
|
* @author Kohsuke Kawaguchi
|
||||||
|
* @see GitHubBuilder#withAbuseLimitHandler(AbuseLimitHandler)
|
||||||
|
* @see <a href="https://developer.github.com/v3/#abuse-rate-limits">documentation</a>
|
||||||
|
* @see RateLimitHandler
|
||||||
|
*/
|
||||||
|
public abstract class AbuseLimitHandler {
|
||||||
|
/**
|
||||||
|
* Called when the library encounters HTTP error indicating that the API abuse limit is reached.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Any exception thrown from this method will cause the request to fail, and the caller of github-api
|
||||||
|
* will receive an exception. If this method returns normally, another request will be attempted.
|
||||||
|
* For that to make sense, the implementation needs to wait for some time.
|
||||||
|
*
|
||||||
|
* @see <a href="https://developer.github.com/v3/#abuse-rate-limits">API documentation from GitHub</a>
|
||||||
|
* @param e
|
||||||
|
* Exception from Java I/O layer. If you decide to fail the processing, you can throw
|
||||||
|
* this exception (or wrap this exception into another exception and throw it.)
|
||||||
|
* @param uc
|
||||||
|
* Connection that resulted in an error. Useful for accessing other response headers.
|
||||||
|
*/
|
||||||
|
public abstract void onError(IOException e, HttpURLConnection uc) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait until the API abuse "wait time" is passed.
|
||||||
|
*/
|
||||||
|
public static final AbuseLimitHandler WAIT = new AbuseLimitHandler() {
|
||||||
|
@Override
|
||||||
|
public void onError(IOException e, HttpURLConnection uc) throws IOException {
|
||||||
|
try {
|
||||||
|
Thread.sleep(parseWaitTime(uc));
|
||||||
|
} catch (InterruptedException _) {
|
||||||
|
throw (InterruptedIOException)new InterruptedIOException().initCause(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private long parseWaitTime(HttpURLConnection uc) {
|
||||||
|
String v = uc.getHeaderField("Retry-After");
|
||||||
|
if (v==null) return 60 * 1000; // can't tell, return 1 min
|
||||||
|
|
||||||
|
return Math.max(1000, Long.parseLong(v)*1000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fail immediately.
|
||||||
|
*/
|
||||||
|
public static final AbuseLimitHandler FAIL = new AbuseLimitHandler() {
|
||||||
|
@Override
|
||||||
|
public void onError(IOException e, HttpURLConnection uc) throws IOException {
|
||||||
|
throw (IOException)new IOException("Abust limit reached").initCause(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
21
src/main/java/org/kohsuke/github/BranchProtection.java
Normal file
21
src/main/java/org/kohsuke/github/BranchProtection.java
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Kohsuke Kawaguchi
|
||||||
|
* @see GHBranch#disableProtection()
|
||||||
|
*/
|
||||||
|
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD", "URF_UNREAD_FIELD"}, justification = "JSON API")
|
||||||
|
class BranchProtection {
|
||||||
|
boolean enabled;
|
||||||
|
RequiredStatusChecks requiredStatusChecks;
|
||||||
|
|
||||||
|
static class RequiredStatusChecks {
|
||||||
|
EnforcementLevel enforcement_level;
|
||||||
|
List<String> contexts = new ArrayList<String>();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,9 +23,13 @@
|
|||||||
*/
|
*/
|
||||||
package org.kohsuke.github;
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Kohsuke Kawaguchi
|
* @author Kohsuke Kawaguchi
|
||||||
*/
|
*/
|
||||||
|
@SuppressFBWarnings(value = "UUF_UNUSED_PUBLIC_OR_PROTECTED_FIELD",
|
||||||
|
justification = "Being constructed by JSON deserialization")
|
||||||
class DeleteToken {
|
class DeleteToken {
|
||||||
public String delete_token;
|
public String delete_token;
|
||||||
}
|
}
|
||||||
|
|||||||
14
src/main/java/org/kohsuke/github/EnforcementLevel.java
Normal file
14
src/main/java/org/kohsuke/github/EnforcementLevel.java
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Kohsuke Kawaguchi
|
||||||
|
*/
|
||||||
|
public enum EnforcementLevel {
|
||||||
|
OFF, NON_ADMINS, EVERYONE;
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return name().toLowerCase(Locale.ENGLISH);
|
||||||
|
}
|
||||||
|
}
|
||||||
101
src/main/java/org/kohsuke/github/GHAsset.java
Normal file
101
src/main/java/org/kohsuke/github/GHAsset.java
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asset in a release.
|
||||||
|
*
|
||||||
|
* @see GHRelease#getAssets()
|
||||||
|
*/
|
||||||
|
public class GHAsset extends GHObject {
|
||||||
|
GitHub root;
|
||||||
|
GHRepository owner;
|
||||||
|
private String name;
|
||||||
|
private String label;
|
||||||
|
private String state;
|
||||||
|
private String content_type;
|
||||||
|
private long size;
|
||||||
|
private long download_count;
|
||||||
|
private String browser_download_url;
|
||||||
|
|
||||||
|
public String getContentType() {
|
||||||
|
return content_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContentType(String contentType) throws IOException {
|
||||||
|
edit("content_type", contentType);
|
||||||
|
this.content_type = contentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getDownloadCount() {
|
||||||
|
return download_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLabel() {
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLabel(String label) throws IOException {
|
||||||
|
edit("label", label);
|
||||||
|
this.label = label;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHRepository getOwner() {
|
||||||
|
return owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GitHub getRoot() {
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getSize() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getState() {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated This object has no HTML URL.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public URL getHtmlUrl() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBrowserDownloadUrl() {
|
||||||
|
return browser_download_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void edit(String key, Object value) throws IOException {
|
||||||
|
new Requester(root)._with(key, value).method("PATCH").to(getApiRoute());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void delete() throws IOException {
|
||||||
|
new Requester(root).method("DELETE").to(getApiRoute());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String getApiRoute() {
|
||||||
|
return "/repos/" + owner.getOwnerName() + "/" + owner.getName() + "/releases/assets/" + id;
|
||||||
|
}
|
||||||
|
|
||||||
|
GHAsset wrap(GHRelease release) {
|
||||||
|
this.owner = release.getOwner();
|
||||||
|
this.root = owner.root;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GHAsset[] wrap(GHAsset[] assets, GHRelease release) {
|
||||||
|
for (GHAsset aTo : assets) {
|
||||||
|
aTo.wrap(release);
|
||||||
|
}
|
||||||
|
return assets;
|
||||||
|
}
|
||||||
|
}
|
||||||
96
src/main/java/org/kohsuke/github/GHAuthorization.java
Normal file
96
src/main/java/org/kohsuke/github/GHAuthorization.java
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generated OAuth token
|
||||||
|
*
|
||||||
|
* @author janinko
|
||||||
|
* @see GitHub#createToken(Collection, String, String)
|
||||||
|
* @see <a href="http://developer.github.com/v3/oauth/#create-a-new-authorization">API documentation</a>
|
||||||
|
*/
|
||||||
|
public class GHAuthorization extends GHObject {
|
||||||
|
public static final String USER = "user";
|
||||||
|
public static final String USER_EMAIL = "user:email";
|
||||||
|
public static final String USER_FOLLOW = "user:follow";
|
||||||
|
public static final String PUBLIC_REPO = "public_repo";
|
||||||
|
public static final String REPO = "repo";
|
||||||
|
public static final String REPO_STATUS = "repo:status";
|
||||||
|
public static final String DELETE_REPO = "delete_repo";
|
||||||
|
public static final String NOTIFICATIONS = "notifications";
|
||||||
|
public static final String GIST = "gist";
|
||||||
|
public static final String READ_HOOK = "read:repo_hook";
|
||||||
|
public static final String WRITE_HOOK = "write:repo_hook";
|
||||||
|
public static final String AMIN_HOOK = "admin:repo_hook";
|
||||||
|
public static final String READ_ORG = "read:org";
|
||||||
|
public static final String WRITE_ORG = "write:org";
|
||||||
|
public static final String ADMIN_ORG = "admin:org";
|
||||||
|
public static final String READ_KEY = "read:public_key";
|
||||||
|
public static final String WRITE_KEY = "write:public_key";
|
||||||
|
public static final String ADMIN_KEY = "admin:public_key";
|
||||||
|
|
||||||
|
private GitHub root;
|
||||||
|
private List<String> scopes;
|
||||||
|
private String token;
|
||||||
|
private App app;
|
||||||
|
private String note;
|
||||||
|
private String note_url;
|
||||||
|
|
||||||
|
public GitHub getRoot() {
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getScopes() {
|
||||||
|
return scopes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getToken() {
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public URL getAppUrl() {
|
||||||
|
return GitHub.parseURL(app.url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAppName() {
|
||||||
|
return app.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressFBWarnings(value = "NM_CONFUSING",
|
||||||
|
justification = "It's a part of the library API, cannot be changed")
|
||||||
|
public URL getApiURL() {
|
||||||
|
return GitHub.parseURL(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated This object has no HTML URL.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public URL getHtmlUrl() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNote() {
|
||||||
|
return note;
|
||||||
|
}
|
||||||
|
|
||||||
|
public URL getNoteUrl() {
|
||||||
|
return GitHub.parseURL(note_url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*package*/ GHAuthorization wrap(GitHub root) {
|
||||||
|
this.root = root;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD"},
|
||||||
|
justification = "JSON API")
|
||||||
|
private static class App {
|
||||||
|
private String url;
|
||||||
|
private String name;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,21 @@
|
|||||||
package org.kohsuke.github;
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||||
|
import org.kohsuke.github.BranchProtection.RequiredStatusChecks;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import static org.kohsuke.github.Previews.LOKI;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A branch in a repository.
|
* A branch in a repository.
|
||||||
*
|
*
|
||||||
* @author Yusuke Kokubo
|
* @author Yusuke Kokubo
|
||||||
*/
|
*/
|
||||||
|
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
|
||||||
|
"NP_UNWRITTEN_FIELD", "URF_UNREAD_FIELD"}, justification = "JSON API")
|
||||||
public class GHBranch {
|
public class GHBranch {
|
||||||
private GitHub root;
|
private GitHub root;
|
||||||
private GHRepository owner;
|
private GHRepository owner;
|
||||||
@@ -13,7 +24,10 @@ public class GHBranch {
|
|||||||
private Commit commit;
|
private Commit commit;
|
||||||
|
|
||||||
public static class Commit {
|
public static class Commit {
|
||||||
String sha,url;
|
String sha;
|
||||||
|
|
||||||
|
@SuppressFBWarnings(value = "UUF_UNUSED_FIELD", justification = "We don't provide it in API now")
|
||||||
|
String url;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GitHub getRoot() {
|
public GitHub getRoot() {
|
||||||
@@ -38,9 +52,48 @@ public class GHBranch {
|
|||||||
return commit.sha;
|
return commit.sha;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disables branch protection and allows anyone with push access to push changes.
|
||||||
|
*/
|
||||||
|
@Preview @Deprecated
|
||||||
|
public void disableProtection() throws IOException {
|
||||||
|
BranchProtection bp = new BranchProtection();
|
||||||
|
bp.enabled = false;
|
||||||
|
setProtection(bp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables branch protection to control what commit statuses are required to push.
|
||||||
|
*
|
||||||
|
* @see GHCommitStatus#getContext()
|
||||||
|
*/
|
||||||
|
@Preview @Deprecated
|
||||||
|
public void enableProtection(EnforcementLevel level, Collection<String> contexts) throws IOException {
|
||||||
|
BranchProtection bp = new BranchProtection();
|
||||||
|
bp.enabled = true;
|
||||||
|
bp.requiredStatusChecks = new RequiredStatusChecks();
|
||||||
|
bp.requiredStatusChecks.enforcement_level = level;
|
||||||
|
bp.requiredStatusChecks.contexts.addAll(contexts);
|
||||||
|
setProtection(bp);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview @Deprecated
|
||||||
|
public void enableProtection(EnforcementLevel level, String... contexts) throws IOException {
|
||||||
|
enableProtection(level, Arrays.asList(contexts));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setProtection(BranchProtection bp) throws IOException {
|
||||||
|
new Requester(root).method("PATCH").withPreview(LOKI)._with("protection",bp).to(getApiRoute());
|
||||||
|
}
|
||||||
|
|
||||||
|
String getApiRoute() {
|
||||||
|
return owner.getApiTailUrl("/branches/"+name);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Branch:" + name + " in " + owner.getUrl();
|
final String url = owner != null ? owner.getUrl().toString() : "unknown";
|
||||||
|
return "Branch:" + name + " in " + url;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*package*/ GHBranch wrap(GHRepository repo) {
|
/*package*/ GHBranch wrap(GHRepository repo) {
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
package org.kohsuke.github;
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
|
||||||
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.AbstractList;
|
import java.util.AbstractList;
|
||||||
@@ -14,9 +17,54 @@ import java.util.List;
|
|||||||
* @see GHRepository#getCommit(String)
|
* @see GHRepository#getCommit(String)
|
||||||
* @see GHCommitComment#getCommit()
|
* @see GHCommitComment#getCommit()
|
||||||
*/
|
*/
|
||||||
|
@SuppressFBWarnings(value = {"NP_UNWRITTEN_FIELD", "UWF_UNWRITTEN_FIELD"},
|
||||||
|
justification = "JSON API")
|
||||||
public class GHCommit {
|
public class GHCommit {
|
||||||
private GHRepository owner;
|
private GHRepository owner;
|
||||||
|
|
||||||
|
private ShortInfo commit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Short summary of this commit.
|
||||||
|
*/
|
||||||
|
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
|
||||||
|
"NP_UNWRITTEN_FIELD", "UWF_UNWRITTEN_FIELD"}, justification = "JSON API")
|
||||||
|
public static class ShortInfo {
|
||||||
|
private GHAuthor author;
|
||||||
|
private GHAuthor committer;
|
||||||
|
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
private int comment_count;
|
||||||
|
|
||||||
|
@WithBridgeMethods(value = GHAuthor.class, castRequired = true)
|
||||||
|
public GitUser getAuthor() {
|
||||||
|
return author;
|
||||||
|
}
|
||||||
|
|
||||||
|
@WithBridgeMethods(value = GHAuthor.class, castRequired = true)
|
||||||
|
public GitUser getCommitter() {
|
||||||
|
return committer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Commit message.
|
||||||
|
*/
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCommentCount() {
|
||||||
|
return comment_count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use {@link GitUser} instead.
|
||||||
|
*/
|
||||||
|
public static class GHAuthor extends GitUser {
|
||||||
|
}
|
||||||
|
|
||||||
public static class Stats {
|
public static class Stats {
|
||||||
int total,additions,deletions;
|
int total,additions,deletions;
|
||||||
}
|
}
|
||||||
@@ -24,10 +72,13 @@ public class GHCommit {
|
|||||||
/**
|
/**
|
||||||
* A file that was modified.
|
* A file that was modified.
|
||||||
*/
|
*/
|
||||||
|
@SuppressFBWarnings(value = "UWF_UNWRITTEN_FIELD",
|
||||||
|
justification = "It's being initilized by JSON deserialization")
|
||||||
public static class File {
|
public static class File {
|
||||||
String status;
|
String status;
|
||||||
int changes,additions,deletions;
|
int changes,additions,deletions;
|
||||||
String raw_url, blob_url, filename, sha, patch;
|
String raw_url, blob_url, sha, patch;
|
||||||
|
String filename, previous_filename;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Number of lines added + removed.
|
* Number of lines added + removed.
|
||||||
@@ -51,19 +102,28 @@ public class GHCommit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* "modified", "added", or "deleted"
|
* "modified", "added", or "removed"
|
||||||
*/
|
*/
|
||||||
public String getStatus() {
|
public String getStatus() {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Just the base name and the extension without any directory name.
|
* Full path in the repository.
|
||||||
*/
|
*/
|
||||||
|
@SuppressFBWarnings(value = "NM_CONFUSING",
|
||||||
|
justification = "It's a part of the library's API and cannot be renamed")
|
||||||
public String getFileName() {
|
public String getFileName() {
|
||||||
return filename;
|
return filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Previous path, in case file has moved.
|
||||||
|
*/
|
||||||
|
public String getPreviousFilename() {
|
||||||
|
return previous_filename;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The actual change.
|
* The actual change.
|
||||||
*/
|
*/
|
||||||
@@ -96,21 +156,34 @@ public class GHCommit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class Parent {
|
public static class Parent {
|
||||||
String url,sha;
|
@SuppressFBWarnings(value = "UUF_UNUSED_FIELD", justification = "We don't provide it in API now")
|
||||||
|
String url;
|
||||||
|
String sha;
|
||||||
}
|
}
|
||||||
|
|
||||||
static class User {
|
static class User {
|
||||||
// TODO: what if someone who doesn't have an account on GitHub makes a commit?
|
// TODO: what if someone who doesn't have an account on GitHub makes a commit?
|
||||||
String url,avatar_url,login,gravatar_id;
|
@SuppressFBWarnings(value = "UUF_UNUSED_FIELD", justification = "We don't provide it in API now")
|
||||||
|
String url,avatar_url,gravatar_id;
|
||||||
|
@SuppressFBWarnings(value = "UUF_UNUSED_FIELD", justification = "We don't provide it in API now")
|
||||||
int id;
|
int id;
|
||||||
|
|
||||||
|
String login;
|
||||||
}
|
}
|
||||||
|
|
||||||
String url,sha;
|
String url,html_url,sha;
|
||||||
List<File> files;
|
List<File> files;
|
||||||
Stats stats;
|
Stats stats;
|
||||||
List<Parent> parents;
|
List<Parent> parents;
|
||||||
User author,committer;
|
User author,committer;
|
||||||
|
|
||||||
|
|
||||||
|
public ShortInfo getCommitShortInfo() throws IOException {
|
||||||
|
if (commit==null)
|
||||||
|
populate();
|
||||||
|
return commit;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The repository that contains the commit.
|
* The repository that contains the commit.
|
||||||
*/
|
*/
|
||||||
@@ -121,24 +194,34 @@ public class GHCommit {
|
|||||||
/**
|
/**
|
||||||
* Number of lines added + removed.
|
* Number of lines added + removed.
|
||||||
*/
|
*/
|
||||||
public int getLinesChanged() {
|
public int getLinesChanged() throws IOException {
|
||||||
|
populate();
|
||||||
return stats.total;
|
return stats.total;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Number of lines added.
|
* Number of lines added.
|
||||||
*/
|
*/
|
||||||
public int getLinesAdded() {
|
public int getLinesAdded() throws IOException {
|
||||||
|
populate();
|
||||||
return stats.additions;
|
return stats.additions;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Number of lines removed.
|
* Number of lines removed.
|
||||||
*/
|
*/
|
||||||
public int getLinesDeleted() {
|
public int getLinesDeleted() throws IOException {
|
||||||
|
populate();
|
||||||
return stats.deletions;
|
return stats.deletions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* URL of this commit like "https://github.com/kohsuke/sandbox-ant/commit/8ae38db0ea5837313ab5f39d43a6f73de3bd9000"
|
||||||
|
*/
|
||||||
|
public URL getHtmlUrl() {
|
||||||
|
return GitHub.parseURL(html_url);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [0-9a-f]{40} SHA1 checksum.
|
* [0-9a-f]{40} SHA1 checksum.
|
||||||
*/
|
*/
|
||||||
@@ -152,7 +235,8 @@ public class GHCommit {
|
|||||||
* @return
|
* @return
|
||||||
* Can be empty but never null.
|
* Can be empty but never null.
|
||||||
*/
|
*/
|
||||||
public List<File> getFiles() {
|
public List<File> getFiles() throws IOException {
|
||||||
|
populate();
|
||||||
return files!=null ? Collections.unmodifiableList(files) : Collections.<File>emptyList();
|
return files!=null ? Collections.unmodifiableList(files) : Collections.<File>emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,8 +286,8 @@ public class GHCommit {
|
|||||||
*/
|
*/
|
||||||
public PagedIterable<GHCommitComment> listComments() {
|
public PagedIterable<GHCommitComment> listComments() {
|
||||||
return new PagedIterable<GHCommitComment>() {
|
return new PagedIterable<GHCommitComment>() {
|
||||||
public PagedIterator<GHCommitComment> iterator() {
|
public PagedIterator<GHCommitComment> _iterator(int pageSize) {
|
||||||
return new PagedIterator<GHCommitComment>(owner.root.retrieve().asIterator(String.format("/repos/%s/%s/commits/%s/comments", owner.getOwnerName(), owner.getName(), sha), GHCommitComment[].class)) {
|
return new PagedIterator<GHCommitComment>(owner.root.retrieve().asIterator(String.format("/repos/%s/%s/commits/%s/comments", owner.getOwnerName(), owner.getName(), sha), GHCommitComment[].class, pageSize)) {
|
||||||
@Override
|
@Override
|
||||||
protected void wrapUp(GHCommitComment[] page) {
|
protected void wrapUp(GHCommitComment[] page) {
|
||||||
for (GHCommitComment c : page)
|
for (GHCommitComment c : page)
|
||||||
@@ -230,7 +314,7 @@ public class GHCommit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public GHCommitComment createComment(String body) throws IOException {
|
public GHCommitComment createComment(String body) throws IOException {
|
||||||
return createComment(body,null,null,null);
|
return createComment(body, null, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -247,6 +331,14 @@ public class GHCommit {
|
|||||||
return owner.getLastCommitStatus(sha);
|
return owner.getLastCommitStatus(sha);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Some of the fields are not always filled in when this object is retrieved as a part of another API call.
|
||||||
|
*/
|
||||||
|
void populate() throws IOException {
|
||||||
|
if (files==null && stats==null)
|
||||||
|
owner.root.retrieve().to(owner.getApiTailUrl("commits/" + sha), this);
|
||||||
|
}
|
||||||
|
|
||||||
GHCommit wrapUp(GHRepository owner) {
|
GHCommit wrapUp(GHRepository owner) {
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
return this;
|
return this;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.kohsuke.github;
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
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.Date;
|
import java.util.Date;
|
||||||
@@ -12,34 +13,30 @@ import java.util.Date;
|
|||||||
* @see GHCommit#listComments()
|
* @see GHCommit#listComments()
|
||||||
* @see GHCommit#createComment(String, String, Integer, Integer)
|
* @see GHCommit#createComment(String, String, Integer, Integer)
|
||||||
*/
|
*/
|
||||||
public class GHCommitComment {
|
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
|
||||||
|
"NP_UNWRITTEN_FIELD"}, justification = "JSON API")
|
||||||
|
public class GHCommitComment extends GHObject {
|
||||||
private GHRepository owner;
|
private GHRepository owner;
|
||||||
|
|
||||||
String updated_at, created_at;
|
String body, html_url, commit_id;
|
||||||
String body, url, html_url, commit_id;
|
|
||||||
Integer line;
|
Integer line;
|
||||||
int id;
|
|
||||||
String path;
|
String path;
|
||||||
User user;
|
User user;
|
||||||
|
|
||||||
static class User {
|
static class User {
|
||||||
// TODO: what if someone who doesn't have an account on GitHub makes a commit?
|
// TODO: what if someone who doesn't have an account on GitHub makes a commit?
|
||||||
String url,avatar_url,login,gravatar_id;
|
@SuppressFBWarnings(value = "UUF_UNUSED_FIELD", justification = "We don't provide it in API now")
|
||||||
|
String url,avatar_url,gravatar_id;
|
||||||
|
@SuppressFBWarnings(value = "UUF_UNUSED_FIELD", justification = "We don't provide it in API now")
|
||||||
int id;
|
int id;
|
||||||
|
|
||||||
|
String login;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GHRepository getOwner() {
|
public GHRepository getOwner() {
|
||||||
return owner;
|
return owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Date getCreatedAt() {
|
|
||||||
return GitHub.parseDate(created_at);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Date getUpdatedAt() {
|
|
||||||
return GitHub.parseDate(updated_at);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* URL like 'https://github.com/kohsuke/sandbox-ant/commit/8ae38db0ea5837313ab5f39d43a6f73de3bd9000#commitcomment-1252827' to
|
* URL like 'https://github.com/kohsuke/sandbox-ant/commit/8ae38db0ea5837313ab5f39d43a6f73de3bd9000#commitcomment-1252827' to
|
||||||
* show this commit comment in a browser.
|
* show this commit comment in a browser.
|
||||||
@@ -75,10 +72,6 @@ public class GHCommitComment {
|
|||||||
return line!=null ? line : -1;
|
return line!=null ? line : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the user who put this comment.
|
* Gets the user who put this comment.
|
||||||
*/
|
*/
|
||||||
@@ -97,7 +90,7 @@ public class GHCommitComment {
|
|||||||
* Updates the body of the commit message.
|
* Updates the body of the commit message.
|
||||||
*/
|
*/
|
||||||
public void update(String body) throws IOException {
|
public void update(String body) throws IOException {
|
||||||
GHCommitComment r = new Requester(owner.root)
|
new Requester(owner.root)
|
||||||
.with("body", body)
|
.with("body", body)
|
||||||
.method("PATCH").to(getApiTail(), GHCommitComment.class);
|
.method("PATCH").to(getApiTail(), GHCommitComment.class);
|
||||||
this.body = body;
|
this.body = body;
|
||||||
|
|||||||
@@ -23,6 +23,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.kohsuke.github;
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Identifies a commit in {@link GHPullRequest}.
|
* Identifies a commit in {@link GHPullRequest}.
|
||||||
*
|
*
|
||||||
@@ -31,11 +33,11 @@ package org.kohsuke.github;
|
|||||||
public class GHCommitPointer {
|
public class GHCommitPointer {
|
||||||
private String ref, sha, label;
|
private String ref, sha, label;
|
||||||
private GHUser user;
|
private GHUser user;
|
||||||
private GHRepository repository/*V2*/,repo/*V3*/;
|
private GHRepository repo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This points to the user who owns
|
* This points to the user who owns
|
||||||
* the {@link #repository}.
|
* the {@link #getRepository()}.
|
||||||
*/
|
*/
|
||||||
public GHUser getUser() {
|
public GHUser getUser() {
|
||||||
return user;
|
return user;
|
||||||
@@ -45,7 +47,7 @@ public class GHCommitPointer {
|
|||||||
* The repository that contains the commit.
|
* The repository that contains the commit.
|
||||||
*/
|
*/
|
||||||
public GHRepository getRepository() {
|
public GHRepository getRepository() {
|
||||||
return repo!=null ? repo : repository;
|
return repo;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -69,9 +71,15 @@ public class GHCommitPointer {
|
|||||||
return label;
|
return label;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains the commit that this pointer is referring to.
|
||||||
|
*/
|
||||||
|
public GHCommit getCommit() throws IOException {
|
||||||
|
return getRepository().getCommit(getSha());
|
||||||
|
}
|
||||||
|
|
||||||
void wrapUp(GitHub root) {
|
void wrapUp(GitHub root) {
|
||||||
if (user!=null) user.root = root;
|
if (user!=null) user.root = root;
|
||||||
if (repo!=null) repo.wrap(root);
|
if (repo!=null) repo.wrap(root);
|
||||||
if (repository!=null) repository.wrap(root);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
105
src/main/java/org/kohsuke/github/GHCommitQueryBuilder.java
Normal file
105
src/main/java/org/kohsuke/github/GHCommitQueryBuilder.java
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds up query for listing commits.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Call various methods that set the filter criteria, then {@link #list()} method to actually list up the commit.
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* GHRepository r = ...;
|
||||||
|
* for (GHCommit c : r.queryCommits().since(x).until(y).author("kohsuke")) {
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @author Kohsuke Kawaguchi
|
||||||
|
* @see GHRepository#queryCommits()
|
||||||
|
*/
|
||||||
|
public class GHCommitQueryBuilder {
|
||||||
|
private final Requester req;
|
||||||
|
private final GHRepository repo;
|
||||||
|
|
||||||
|
/*package*/ GHCommitQueryBuilder(GHRepository repo) {
|
||||||
|
this.repo = repo;
|
||||||
|
this.req = repo.root.retrieve(); // requester to build up
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GItHub login or email address by which to filter by commit author.
|
||||||
|
*/
|
||||||
|
public GHCommitQueryBuilder author(String author) {
|
||||||
|
req.with("author",author);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only commits containing this file path will be returned.
|
||||||
|
*/
|
||||||
|
public GHCommitQueryBuilder path(String path) {
|
||||||
|
req.with("path",path);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the SHA1 commit / tag / branch / etc to start listing commits from.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public GHCommitQueryBuilder from(String ref) {
|
||||||
|
req.with("sha",ref);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHCommitQueryBuilder pageSize(int pageSize) {
|
||||||
|
req.with("per_page",pageSize);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only commits after this date will be returned
|
||||||
|
*/
|
||||||
|
public GHCommitQueryBuilder since(Date dt) {
|
||||||
|
req.with("since",GitHub.printDate(dt));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only commits after this date will be returned
|
||||||
|
*/
|
||||||
|
public GHCommitQueryBuilder since(long timestamp) {
|
||||||
|
return since(new Date(timestamp));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only commits before this date will be returned
|
||||||
|
*/
|
||||||
|
public GHCommitQueryBuilder until(Date dt) {
|
||||||
|
req.with("until",GitHub.printDate(dt));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only commits before this date will be returned
|
||||||
|
*/
|
||||||
|
public GHCommitQueryBuilder until(long timestamp) {
|
||||||
|
return until(new Date(timestamp));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists up the commits with the criteria built so far.
|
||||||
|
*/
|
||||||
|
public PagedIterable<GHCommit> list() {
|
||||||
|
return new PagedIterable<GHCommit>() {
|
||||||
|
public PagedIterator<GHCommit> _iterator(int pageSize) {
|
||||||
|
return new PagedIterator<GHCommit>(req.asIterator(repo.getApiTailUrl("commits"), GHCommit[].class, pageSize)) {
|
||||||
|
protected void wrapUp(GHCommit[] page) {
|
||||||
|
for (GHCommit c : page)
|
||||||
|
c.wrapUp(repo);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,21 +1,19 @@
|
|||||||
package org.kohsuke.github;
|
package org.kohsuke.github;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.net.URL;
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a status of a commit.
|
* Represents a status of a commit.
|
||||||
*
|
*
|
||||||
* @author Kohsuke Kawaguchi
|
* @author Kohsuke Kawaguchi
|
||||||
* @see GHRepository#getCommitStatus(String)
|
* @see GHRepository#getLastCommitStatus(String)
|
||||||
* @see GHCommit#getStatus()
|
* @see GHCommit#getLastStatus()
|
||||||
|
* @see GHRepository#createCommitStatus(String, GHCommitState, String, String)
|
||||||
*/
|
*/
|
||||||
public class GHCommitStatus {
|
public class GHCommitStatus extends GHObject {
|
||||||
String created_at, updated_at;
|
|
||||||
String state;
|
String state;
|
||||||
String target_url,description;
|
String target_url,description;
|
||||||
int id;
|
String context;
|
||||||
String url;
|
|
||||||
GHUser creator;
|
GHUser creator;
|
||||||
|
|
||||||
private GitHub root;
|
private GitHub root;
|
||||||
@@ -26,14 +24,6 @@ public class GHCommitStatus {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Date getCreatedAt() {
|
|
||||||
return GitHub.parseDate(created_at);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Date getUpdatedAt() {
|
|
||||||
return GitHub.parseDate(updated_at);
|
|
||||||
}
|
|
||||||
|
|
||||||
public GHCommitState getState() {
|
public GHCommitState getState() {
|
||||||
for (GHCommitState s : GHCommitState.values()) {
|
for (GHCommitState s : GHCommitState.values()) {
|
||||||
if (s.name().equalsIgnoreCase(state))
|
if (s.name().equalsIgnoreCase(state))
|
||||||
@@ -55,18 +45,19 @@ public class GHCommitStatus {
|
|||||||
return description;
|
return description;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* API URL of this commit status.
|
|
||||||
*/
|
|
||||||
public String getUrl() {
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
|
|
||||||
public GHUser getCreator() {
|
public GHUser getCreator() {
|
||||||
return creator;
|
return creator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getContext() {
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated This object has no HTML URL.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public URL getHtmlUrl() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
package org.kohsuke.github;
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
|
||||||
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||||
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The model user for comparing 2 commits in the GitHub API.
|
* The model user for comparing 2 commits in the GitHub API.
|
||||||
@@ -63,10 +65,25 @@ public class GHCompare {
|
|||||||
return merge_base_commit;
|
return merge_base_commit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an array of commits.
|
||||||
|
* @return A copy of the array being stored in the class.
|
||||||
|
*/
|
||||||
public Commit[] getCommits() {
|
public Commit[] getCommits() {
|
||||||
return commits;
|
Commit[] newValue = new Commit[commits.length];
|
||||||
|
System.arraycopy(commits, 0, newValue, 0, commits.length);
|
||||||
|
return newValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an array of commits.
|
||||||
|
* @return A copy of the array being stored in the class.
|
||||||
|
*/
|
||||||
|
public GHCommit.File[] getFiles() {
|
||||||
|
GHCommit.File[] newValue = new GHCommit.File[files.length];
|
||||||
|
System.arraycopy(files, 0, newValue, 0, files.length);
|
||||||
|
return newValue;
|
||||||
|
}
|
||||||
|
|
||||||
public GHCompare wrap(GHRepository owner) {
|
public GHCompare wrap(GHRepository owner) {
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
@@ -82,6 +99,8 @@ public class GHCompare {
|
|||||||
* Compare commits had a child commit element with additional details we want to capture.
|
* Compare commits had a child commit element with additional details we want to capture.
|
||||||
* This extenstion of GHCommit provides that.
|
* This extenstion of GHCommit provides that.
|
||||||
*/
|
*/
|
||||||
|
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD"},
|
||||||
|
justification = "JSON API")
|
||||||
public static class Commit extends GHCommit {
|
public static class Commit extends GHCommit {
|
||||||
|
|
||||||
private InnerCommit commit;
|
private InnerCommit commit;
|
||||||
@@ -109,11 +128,13 @@ public class GHCompare {
|
|||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
public User getAuthor() {
|
@WithBridgeMethods(value=User.class,castRequired=true)
|
||||||
|
public GitUser getAuthor() {
|
||||||
return author;
|
return author;
|
||||||
}
|
}
|
||||||
|
|
||||||
public User getCommitter() {
|
@WithBridgeMethods(value=User.class,castRequired=true)
|
||||||
|
public GitUser getCommitter() {
|
||||||
return committer;
|
return committer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,23 +155,13 @@ public class GHCompare {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class User {
|
/**
|
||||||
private String name, email, date;
|
* @deprecated use {@link GitUser} instead.
|
||||||
|
*/
|
||||||
public String getName() {
|
public static class User extends GitUser {
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getEmail() {
|
|
||||||
return email;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Date getDate() {
|
|
||||||
return GitHub.parseDate(date);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static enum Status {
|
public static enum Status {
|
||||||
behind, ahead, identical;
|
behind, ahead, identical, diverged
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
248
src/main/java/org/kohsuke/github/GHContent.java
Normal file
248
src/main/java/org/kohsuke/github/GHContent.java
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||||
|
import org.apache.commons.codec.binary.Base64;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import javax.xml.bind.DatatypeConverter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Content of a repository.
|
||||||
|
*
|
||||||
|
* @author Alexandre COLLIGNON
|
||||||
|
* @see GHRepository#getFileContent(String)
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({"UnusedDeclaration"})
|
||||||
|
public class GHContent {
|
||||||
|
/*
|
||||||
|
In normal use of this class, repository field is set via wrap(),
|
||||||
|
but in the code search API, there's a nested 'repository' field that gets populated from JSON.
|
||||||
|
*/
|
||||||
|
private GHRepository repository;
|
||||||
|
|
||||||
|
private GitHub root;
|
||||||
|
|
||||||
|
private String type;
|
||||||
|
private String encoding;
|
||||||
|
private long size;
|
||||||
|
private String sha;
|
||||||
|
private String name;
|
||||||
|
private String path;
|
||||||
|
private String content;
|
||||||
|
private String url; // this is the API url
|
||||||
|
private String git_url; // this is the Blob url
|
||||||
|
private String html_url; // this is the UI
|
||||||
|
private String download_url;
|
||||||
|
|
||||||
|
public GHRepository getOwner() {
|
||||||
|
return repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEncoding() {
|
||||||
|
return encoding;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getSize() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSha() {
|
||||||
|
return sha;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPath() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the decoded content that is stored at this location.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Due to the nature of GitHub's API, you're not guaranteed that
|
||||||
|
* the content will already be populated, so this may trigger
|
||||||
|
* network activity, and can throw an IOException.
|
||||||
|
*
|
||||||
|
* @deprecated
|
||||||
|
* Use {@link #read()}
|
||||||
|
*/
|
||||||
|
@SuppressFBWarnings("DM_DEFAULT_ENCODING")
|
||||||
|
public String getContent() throws IOException {
|
||||||
|
return new String(Base64.decodeBase64(getEncodedContent()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the base64-encoded content that is stored at this location.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Due to the nature of GitHub's API, you're not guaranteed that
|
||||||
|
* the content will already be populated, so this may trigger
|
||||||
|
* network activity, and can throw an IOException.
|
||||||
|
*
|
||||||
|
* @deprecated
|
||||||
|
* Use {@link #read()}
|
||||||
|
*/
|
||||||
|
public String getEncodedContent() throws IOException {
|
||||||
|
if (content!=null)
|
||||||
|
return content;
|
||||||
|
else
|
||||||
|
return Base64.encodeBase64String(IOUtils.toByteArray(read()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGitUrl() {
|
||||||
|
return git_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHtmlUrl() {
|
||||||
|
return html_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the actual content stored here.
|
||||||
|
*/
|
||||||
|
public InputStream read() throws IOException {
|
||||||
|
// if the download link is encoded with a token on the query string, the default behavior of POST will fail
|
||||||
|
return new Requester(root).method("GET").asStream(getDownloadUrl());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* URL to retrieve the raw content of the file. Null if this is a directory.
|
||||||
|
*/
|
||||||
|
public String getDownloadUrl() throws IOException {
|
||||||
|
populate();
|
||||||
|
return download_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFile() {
|
||||||
|
return "file".equals(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDirectory() {
|
||||||
|
return "dir".equals(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fully populate the data by retrieving missing data.
|
||||||
|
*
|
||||||
|
* Depending on the original API call where this object is created, it may not contain everything.
|
||||||
|
*/
|
||||||
|
protected synchronized void populate() throws IOException {
|
||||||
|
if (download_url!=null) return; // already populated
|
||||||
|
root.retrieve().to(url, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List immediate children of this directory.
|
||||||
|
*/
|
||||||
|
public PagedIterable<GHContent> listDirectoryContent() throws IOException {
|
||||||
|
if (!isDirectory())
|
||||||
|
throw new IllegalStateException(path+" is not a directory");
|
||||||
|
|
||||||
|
return new PagedIterable<GHContent>() {
|
||||||
|
public PagedIterator<GHContent> _iterator(int pageSize) {
|
||||||
|
return new PagedIterator<GHContent>(root.retrieve().asIterator(url, GHContent[].class, pageSize)) {
|
||||||
|
@Override
|
||||||
|
protected void wrapUp(GHContent[] page) {
|
||||||
|
GHContent.wrap(page, repository);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressFBWarnings("DM_DEFAULT_ENCODING")
|
||||||
|
public GHContentUpdateResponse update(String newContent, String commitMessage) throws IOException {
|
||||||
|
return update(newContent.getBytes(), commitMessage, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressFBWarnings("DM_DEFAULT_ENCODING")
|
||||||
|
public GHContentUpdateResponse update(String newContent, String commitMessage, String branch) throws IOException {
|
||||||
|
return update(newContent.getBytes(), commitMessage, branch);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHContentUpdateResponse update(byte[] newContentBytes, String commitMessage) throws IOException {
|
||||||
|
return update(newContentBytes, commitMessage, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHContentUpdateResponse update(byte[] newContentBytes, String commitMessage, String branch) throws IOException {
|
||||||
|
String encodedContent = Base64.encodeBase64String(newContentBytes);
|
||||||
|
|
||||||
|
Requester requester = new Requester(root)
|
||||||
|
.with("path", path)
|
||||||
|
.with("message", commitMessage)
|
||||||
|
.with("sha", sha)
|
||||||
|
.with("content", encodedContent)
|
||||||
|
.method("PUT");
|
||||||
|
|
||||||
|
if (branch != null) {
|
||||||
|
requester.with("branch", branch);
|
||||||
|
}
|
||||||
|
|
||||||
|
GHContentUpdateResponse response = requester.to(getApiRoute(), GHContentUpdateResponse.class);
|
||||||
|
|
||||||
|
response.getContent().wrap(repository);
|
||||||
|
response.getCommit().wrapUp(repository);
|
||||||
|
|
||||||
|
this.content = encodedContent;
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHContentUpdateResponse delete(String message) throws IOException {
|
||||||
|
return delete(message, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHContentUpdateResponse delete(String commitMessage, String branch) throws IOException {
|
||||||
|
Requester requester = new Requester(root)
|
||||||
|
.with("path", path)
|
||||||
|
.with("message", commitMessage)
|
||||||
|
.with("sha", sha)
|
||||||
|
.method("DELETE");
|
||||||
|
|
||||||
|
if (branch != null) {
|
||||||
|
requester.with("branch", branch);
|
||||||
|
}
|
||||||
|
|
||||||
|
GHContentUpdateResponse response = requester.to(getApiRoute(), GHContentUpdateResponse.class);
|
||||||
|
|
||||||
|
response.getCommit().wrapUp(repository);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getApiRoute() {
|
||||||
|
return "/repos/" + repository.getOwnerName() + "/" + repository.getName() + "/contents/" + path;
|
||||||
|
}
|
||||||
|
|
||||||
|
GHContent wrap(GHRepository owner) {
|
||||||
|
this.repository = owner;
|
||||||
|
this.root = owner.root;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
GHContent wrap(GitHub root) {
|
||||||
|
this.root = root;
|
||||||
|
if (repository!=null)
|
||||||
|
repository.wrap(root);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static GHContent[] wrap(GHContent[] contents, GHRepository repository) {
|
||||||
|
for (GHContent unwrappedContent : contents) {
|
||||||
|
unwrappedContent.wrap(repository);
|
||||||
|
}
|
||||||
|
return contents;
|
||||||
|
}
|
||||||
|
}
|
||||||
74
src/main/java/org/kohsuke/github/GHContentSearchBuilder.java
Normal file
74
src/main/java/org/kohsuke/github/GHContentSearchBuilder.java
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search code for {@link GHContent}.
|
||||||
|
*
|
||||||
|
* @author Kohsuke Kawaguchi
|
||||||
|
* @see GitHub#searchContent()
|
||||||
|
*/
|
||||||
|
public class GHContentSearchBuilder extends GHSearchBuilder<GHContent> {
|
||||||
|
/*package*/ GHContentSearchBuilder(GitHub root) {
|
||||||
|
super(root,ContentSearchResult.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search terms.
|
||||||
|
*/
|
||||||
|
public GHContentSearchBuilder q(String term) {
|
||||||
|
super.q(term);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHContentSearchBuilder in(String v) {
|
||||||
|
return q("in:"+v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHContentSearchBuilder language(String v) {
|
||||||
|
return q("language:"+v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHContentSearchBuilder fork(String v) {
|
||||||
|
return q("fork:"+v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHContentSearchBuilder size(String v) {
|
||||||
|
return q("size:"+v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHContentSearchBuilder path(String v) {
|
||||||
|
return q("path:"+v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHContentSearchBuilder filename(String v) {
|
||||||
|
return q("filename:"+v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHContentSearchBuilder extension(String v) {
|
||||||
|
return q("extension:"+v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHContentSearchBuilder user(String v) {
|
||||||
|
return q("user:"+v);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public GHContentSearchBuilder repo(String v) {
|
||||||
|
return q("repo:"+v);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ContentSearchResult extends SearchResult<GHContent> {
|
||||||
|
private GHContent[] items;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
/*package*/ GHContent[] getItems(GitHub root) {
|
||||||
|
for (GHContent item : items)
|
||||||
|
item.wrap(root);
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getApiUrl() {
|
||||||
|
return "/search/code";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The response that is returned when updating
|
||||||
|
* repository content.
|
||||||
|
**/
|
||||||
|
public class GHContentUpdateResponse {
|
||||||
|
private GHContent content;
|
||||||
|
private GHCommit commit;
|
||||||
|
|
||||||
|
public GHContent getContent() {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHCommit getCommit() {
|
||||||
|
return commit;
|
||||||
|
}
|
||||||
|
}
|
||||||
21
src/main/java/org/kohsuke/github/GHContentWithLicense.java
Normal file
21
src/main/java/org/kohsuke/github/GHContentWithLicense.java
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link GHContent} with license information.
|
||||||
|
*
|
||||||
|
* @author Kohsuke Kawaguchi
|
||||||
|
* @see <a href="https://developer.github.com/v3/licenses/#get-a-repositorys-license">documentation</a>
|
||||||
|
* @see GHRepository#getLicense()
|
||||||
|
*/
|
||||||
|
@Preview @Deprecated
|
||||||
|
class GHContentWithLicense extends GHContent {
|
||||||
|
GHLicense license;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
GHContentWithLicense wrap(GHRepository owner) {
|
||||||
|
super.wrap(owner);
|
||||||
|
if (license!=null)
|
||||||
|
license.wrap(owner.root);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
114
src/main/java/org/kohsuke/github/GHCreateRepositoryBuilder.java
Normal file
114
src/main/java/org/kohsuke/github/GHCreateRepositoryBuilder.java
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a repository
|
||||||
|
*
|
||||||
|
* @author Kohsuke Kawaguchi
|
||||||
|
*/
|
||||||
|
public class GHCreateRepositoryBuilder {
|
||||||
|
private final GitHub root;
|
||||||
|
protected final Requester builder;
|
||||||
|
private final String apiUrlTail;
|
||||||
|
|
||||||
|
/*package*/ GHCreateRepositoryBuilder(GitHub root, String apiUrlTail, String name) {
|
||||||
|
this.root = root;
|
||||||
|
this.apiUrlTail = apiUrlTail;
|
||||||
|
this.builder = new Requester(root);
|
||||||
|
this.builder.with("name",name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHCreateRepositoryBuilder description(String description) {
|
||||||
|
this.builder.with("description",description);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHCreateRepositoryBuilder homepage(URL homepage) {
|
||||||
|
return homepage(homepage.toExternalForm());
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHCreateRepositoryBuilder homepage(String homepage) {
|
||||||
|
this.builder.with("homepage",homepage);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a private repository
|
||||||
|
*/
|
||||||
|
public GHCreateRepositoryBuilder private_(boolean b) {
|
||||||
|
this.builder.with("private",b);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables issue tracker
|
||||||
|
*/
|
||||||
|
public GHCreateRepositoryBuilder issues(boolean b) {
|
||||||
|
this.builder.with("has_issues",b);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables wiki
|
||||||
|
*/
|
||||||
|
public GHCreateRepositoryBuilder wiki(boolean b) {
|
||||||
|
this.builder.with("has_wiki",b);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables downloads
|
||||||
|
*/
|
||||||
|
public GHCreateRepositoryBuilder downloads(boolean b) {
|
||||||
|
this.builder.with("has_downloads",b);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If true, create an initial commit with empty README.
|
||||||
|
*/
|
||||||
|
public GHCreateRepositoryBuilder autoInit(boolean b) {
|
||||||
|
this.builder.with("auto_init",b);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a default .gitignore
|
||||||
|
*
|
||||||
|
* See https://developer.github.com/v3/repos/#create
|
||||||
|
*/
|
||||||
|
public GHCreateRepositoryBuilder gitignoreTemplate(String language) {
|
||||||
|
this.builder.with("gitignore_template",language);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Desired license template to apply
|
||||||
|
*
|
||||||
|
* See https://developer.github.com/v3/repos/#create
|
||||||
|
*/
|
||||||
|
public GHCreateRepositoryBuilder licenseTemplate(String license) {
|
||||||
|
this.builder.with("license_template",license);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The team that gets granted access to this repository. Only valid for creating a repository in
|
||||||
|
* an organization.
|
||||||
|
*/
|
||||||
|
public GHCreateRepositoryBuilder team(GHTeam team) {
|
||||||
|
if (team!=null)
|
||||||
|
this.builder.with("team_id",team.getId());
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a repository with all the parameters.
|
||||||
|
*/
|
||||||
|
public GHRepository create() throws IOException {
|
||||||
|
return builder.method("POST").to(apiUrlTail, GHRepository.class).wrap(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
46
src/main/java/org/kohsuke/github/GHDeployKey.java
Normal file
46
src/main/java/org/kohsuke/github/GHDeployKey.java
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.builder.ToStringBuilder;
|
||||||
|
|
||||||
|
public class GHDeployKey {
|
||||||
|
|
||||||
|
protected String url, key, title;
|
||||||
|
protected boolean verified;
|
||||||
|
protected int id;
|
||||||
|
private GHRepository owner;
|
||||||
|
|
||||||
|
public int getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isVerified() {
|
||||||
|
return verified;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHDeployKey wrap(GHRepository repo) {
|
||||||
|
this.owner = repo;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return new ToStringBuilder(this).append("title",title).append("id",id).append("key",key).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void delete() throws IOException {
|
||||||
|
new Requester(owner.root).method("DELETE").to(String.format("/repos/%s/%s/keys/%d", owner.getOwnerName(), owner.getName(), id));
|
||||||
|
}
|
||||||
|
}
|
||||||
61
src/main/java/org/kohsuke/github/GHDeployment.java
Normal file
61
src/main/java/org/kohsuke/github/GHDeployment.java
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
public class GHDeployment extends GHObject {
|
||||||
|
private GHRepository owner;
|
||||||
|
private GitHub root;
|
||||||
|
protected String sha;
|
||||||
|
protected String ref;
|
||||||
|
protected String task;
|
||||||
|
protected Object payload;
|
||||||
|
protected String environment;
|
||||||
|
protected String description;
|
||||||
|
protected String statuses_url;
|
||||||
|
protected String repository_url;
|
||||||
|
protected GHUser creator;
|
||||||
|
|
||||||
|
|
||||||
|
GHDeployment wrap(GHRepository owner) {
|
||||||
|
this.owner = owner;
|
||||||
|
this.root = owner.root;
|
||||||
|
if(creator != null) creator.wrapUp(root);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public URL getStatusesUrl() {
|
||||||
|
return GitHub.parseURL(statuses_url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public URL getRepositoryUrl() {
|
||||||
|
return GitHub.parseURL(repository_url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTask() {
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
public String getPayload() {
|
||||||
|
return (String) payload;
|
||||||
|
}
|
||||||
|
public String getEnvironment() {
|
||||||
|
return environment;
|
||||||
|
}
|
||||||
|
public GHUser getCreator() {
|
||||||
|
return creator;
|
||||||
|
}
|
||||||
|
public String getRef() {
|
||||||
|
return ref;
|
||||||
|
}
|
||||||
|
public String getSha(){
|
||||||
|
return sha;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated This object has no HTML URL.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public URL getHtmlUrl() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
55
src/main/java/org/kohsuke/github/GHDeploymentBuilder.java
Normal file
55
src/main/java/org/kohsuke/github/GHDeploymentBuilder.java
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
//Based on https://developer.github.com/v3/repos/deployments/#create-a-deployment
|
||||||
|
public class GHDeploymentBuilder {
|
||||||
|
private final GHRepository repo;
|
||||||
|
private final Requester builder;
|
||||||
|
|
||||||
|
public GHDeploymentBuilder(GHRepository repo) {
|
||||||
|
this.repo = repo;
|
||||||
|
this.builder = new Requester(repo.root);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHDeploymentBuilder(GHRepository repo, String ref) {
|
||||||
|
this(repo);
|
||||||
|
ref(ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHDeploymentBuilder ref(String branch) {
|
||||||
|
builder.with("ref",branch);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public GHDeploymentBuilder task(String task) {
|
||||||
|
builder.with("task",task);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public GHDeploymentBuilder autoMerge(boolean autoMerge) {
|
||||||
|
builder.with("auto_merge",autoMerge);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHDeploymentBuilder requiredContexts(List<String> requiredContexts) {
|
||||||
|
builder.with("required_contexts",requiredContexts);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public GHDeploymentBuilder payload(String payload) {
|
||||||
|
builder.with("payload",payload);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHDeploymentBuilder environment(String environment) {
|
||||||
|
builder.with("environment",environment);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public GHDeploymentBuilder description(String description) {
|
||||||
|
builder.with("description",description);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHDeployment create() throws IOException {
|
||||||
|
return builder.to(repo.getApiTailUrl("deployments"),GHDeployment.class).wrap(repo);
|
||||||
|
}
|
||||||
|
}
|
||||||
8
src/main/java/org/kohsuke/github/GHDeploymentState.java
Normal file
8
src/main/java/org/kohsuke/github/GHDeploymentState.java
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the state of deployment
|
||||||
|
*/
|
||||||
|
public enum GHDeploymentState {
|
||||||
|
PENDING, SUCCESS, ERROR, FAILURE
|
||||||
|
}
|
||||||
44
src/main/java/org/kohsuke/github/GHDeploymentStatus.java
Normal file
44
src/main/java/org/kohsuke/github/GHDeploymentStatus.java
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public class GHDeploymentStatus extends GHObject {
|
||||||
|
private GHRepository owner;
|
||||||
|
private GitHub root;
|
||||||
|
protected GHUser creator;
|
||||||
|
protected String state;
|
||||||
|
protected String description;
|
||||||
|
protected String target_url;
|
||||||
|
protected String deployment_url;
|
||||||
|
protected String repository_url;
|
||||||
|
public GHDeploymentStatus wrap(GHRepository owner) {
|
||||||
|
this.owner = owner;
|
||||||
|
this.root = owner.root;
|
||||||
|
if(creator != null) creator.wrapUp(root);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public URL getTargetUrl() {
|
||||||
|
return GitHub.parseURL(target_url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public URL getDeploymentUrl() {
|
||||||
|
return GitHub.parseURL(deployment_url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public URL getRepositoryUrl() {
|
||||||
|
return GitHub.parseURL(repository_url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHDeploymentState getState() {
|
||||||
|
return GHDeploymentState.valueOf(state.toUpperCase(Locale.ENGLISH));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated This object has no HTML URL.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public URL getHtmlUrl() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public class GHDeploymentStatusBuilder {
|
||||||
|
private final Requester builder;
|
||||||
|
private GHRepository repo;
|
||||||
|
private int deploymentId;
|
||||||
|
|
||||||
|
public GHDeploymentStatusBuilder(GHRepository repo, int deploymentId, GHDeploymentState state) {
|
||||||
|
this.repo = repo;
|
||||||
|
this.deploymentId = deploymentId;
|
||||||
|
this.builder = new Requester(repo.root);
|
||||||
|
this.builder.with("state",state);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHDeploymentStatusBuilder description(String description) {
|
||||||
|
this.builder.with("description",description);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHDeploymentStatusBuilder targetUrl(String targetUrl) {
|
||||||
|
this.builder.with("target_url",targetUrl);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHDeploymentStatus create() throws IOException {
|
||||||
|
return builder.to(repo.getApiTailUrl("deployments")+"/"+deploymentId+"/statuses",GHDeploymentStatus.class).wrap(repo);
|
||||||
|
}
|
||||||
|
}
|
||||||
10
src/main/java/org/kohsuke/github/GHDirection.java
Normal file
10
src/main/java/org/kohsuke/github/GHDirection.java
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort direction
|
||||||
|
*
|
||||||
|
* @author Kohsuke Kawaguchi
|
||||||
|
*/
|
||||||
|
public enum GHDirection {
|
||||||
|
ASC, DESC
|
||||||
|
}
|
||||||
73
src/main/java/org/kohsuke/github/GHEmail.java
Normal file
73
src/main/java/org/kohsuke/github/GHEmail.java
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an email of GitHub.
|
||||||
|
*
|
||||||
|
* @author Kelly Campbell
|
||||||
|
*/
|
||||||
|
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
|
||||||
|
"NP_UNWRITTEN_FIELD", "NP_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD"}, justification = "JSON API")
|
||||||
|
public class GHEmail {
|
||||||
|
|
||||||
|
protected String email;
|
||||||
|
protected boolean primary;
|
||||||
|
protected boolean verified;
|
||||||
|
|
||||||
|
public String getEmail() {
|
||||||
|
return email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPrimary() {
|
||||||
|
return primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isVerified() {
|
||||||
|
return verified;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Email:"+email;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return email.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof GHEmail) {
|
||||||
|
GHEmail that = (GHEmail) obj;
|
||||||
|
return this.email.equals(that.email);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +1,20 @@
|
|||||||
package org.kohsuke.github;
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hook event type.
|
* Hook event type.
|
||||||
*
|
*
|
||||||
* See http://developer.github.com/v3/events/types/
|
|
||||||
*
|
|
||||||
* @author Kohsuke Kawaguchi
|
* @author Kohsuke Kawaguchi
|
||||||
|
* @see GHEventInfo
|
||||||
|
* @see <a href="https://developer.github.com/v3/activity/events/types/">Event type reference</a>
|
||||||
*/
|
*/
|
||||||
public enum GHEvent {
|
public enum GHEvent {
|
||||||
COMMIT_COMMENT,
|
COMMIT_COMMENT,
|
||||||
CREATE,
|
CREATE,
|
||||||
DELETE,
|
DELETE,
|
||||||
|
DEPLOYMENT,
|
||||||
|
DEPLOYMENT_STATUS,
|
||||||
DOWNLOAD,
|
DOWNLOAD,
|
||||||
FOLLOW,
|
FOLLOW,
|
||||||
FORK,
|
FORK,
|
||||||
@@ -20,9 +24,28 @@ public enum GHEvent {
|
|||||||
ISSUE_COMMENT,
|
ISSUE_COMMENT,
|
||||||
ISSUES,
|
ISSUES,
|
||||||
MEMBER,
|
MEMBER,
|
||||||
|
PAGE_BUILD,
|
||||||
PUBLIC,
|
PUBLIC,
|
||||||
PULL_REQUEST,
|
PULL_REQUEST,
|
||||||
|
PULL_REQUEST_REVIEW_COMMENT,
|
||||||
PUSH,
|
PUSH,
|
||||||
|
RELEASE,
|
||||||
|
REPOSITORY, // only valid for org hooks
|
||||||
|
STATUS,
|
||||||
TEAM_ADD,
|
TEAM_ADD,
|
||||||
WATCH
|
WATCH,
|
||||||
|
PING,
|
||||||
|
/**
|
||||||
|
* Special event type that means "every possible event"
|
||||||
|
*/
|
||||||
|
ALL;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns GitHub's internal representation of this event.
|
||||||
|
*/
|
||||||
|
String symbol() {
|
||||||
|
if (this==ALL) return "*";
|
||||||
|
return name().toLowerCase(Locale.ENGLISH);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,24 @@
|
|||||||
package org.kohsuke.github;
|
package org.kohsuke.github;
|
||||||
|
|
||||||
import org.codehaus.jackson.node.ObjectNode;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents an event.
|
* Represents an event.
|
||||||
*
|
*
|
||||||
* @author Kohsuke Kawaguchi
|
* @author Kohsuke Kawaguchi
|
||||||
*/
|
*/
|
||||||
|
@SuppressFBWarnings(value = "UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", justification = "JSON API")
|
||||||
public class GHEventInfo {
|
public class GHEventInfo {
|
||||||
private GitHub root;
|
private GitHub root;
|
||||||
|
|
||||||
// we don't want to expose Jackson dependency to the user. This needs databinding
|
// we don't want to expose Jackson dependency to the user. This needs databinding
|
||||||
private ObjectNode payload;
|
private ObjectNode payload;
|
||||||
|
|
||||||
|
private long id;
|
||||||
private String created_at;
|
private String created_at;
|
||||||
private String type;
|
private String type;
|
||||||
|
|
||||||
@@ -27,8 +30,12 @@ public class GHEventInfo {
|
|||||||
/**
|
/**
|
||||||
* Inside the event JSON model, GitHub uses a slightly different format.
|
* Inside the event JSON model, GitHub uses a slightly different format.
|
||||||
*/
|
*/
|
||||||
|
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
|
||||||
|
"UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR" }, justification = "JSON API")
|
||||||
public static class GHEventRepository {
|
public static class GHEventRepository {
|
||||||
|
@SuppressFBWarnings(value = "UUF_UNUSED_FIELD", justification = "We don't provide it in API now")
|
||||||
private int id;
|
private int id;
|
||||||
|
@SuppressFBWarnings(value = "UUF_UNUSED_FIELD", justification = "We don't provide it in API now")
|
||||||
private String url; // repository API URL
|
private String url; // repository API URL
|
||||||
private String name; // owner/repo
|
private String name; // owner/repo
|
||||||
}
|
}
|
||||||
@@ -48,6 +55,10 @@ public class GHEventInfo {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
public Date getCreatedAt() {
|
public Date getCreatedAt() {
|
||||||
return GitHub.parseDate(created_at);
|
return GitHub.parseDate(created_at);
|
||||||
}
|
}
|
||||||
@@ -55,14 +66,27 @@ public class GHEventInfo {
|
|||||||
/**
|
/**
|
||||||
* Repository where the change was made.
|
* Repository where the change was made.
|
||||||
*/
|
*/
|
||||||
|
@SuppressFBWarnings(value = {"UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR" },
|
||||||
|
justification = "The field comes from JSON deserialization")
|
||||||
public GHRepository getRepository() throws IOException {
|
public GHRepository getRepository() throws IOException {
|
||||||
return root.getRepository(repo.name);
|
return root.getRepository(repo.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressFBWarnings(value = {"UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR" },
|
||||||
|
justification = "The field comes from JSON deserialization")
|
||||||
public GHUser getActor() throws IOException {
|
public GHUser getActor() throws IOException {
|
||||||
return root.getUser(actor.getLogin());
|
return root.getUser(actor.getLogin());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Quick way to just get the actor of the login.
|
||||||
|
*/
|
||||||
|
public String getActorLogin() throws IOException {
|
||||||
|
return actor.getLogin();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressFBWarnings(value = {"UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR" },
|
||||||
|
justification = "The field comes from JSON deserialization")
|
||||||
public GHOrganization getOrganization() throws IOException {
|
public GHOrganization getOrganization() throws IOException {
|
||||||
return (org==null || org.getLogin()==null) ? null : root.getOrganization(org.getLogin());
|
return (org==null || org.getLogin()==null) ? null : root.getOrganization(org.getLogin());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package org.kohsuke.github;
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base type for types used in databinding of the event payload.
|
* Base type for types used in databinding of the event payload.
|
||||||
@@ -8,6 +10,7 @@ import java.io.Reader;
|
|||||||
* @see GitHub#parseEventPayload(Reader, Class)
|
* @see GitHub#parseEventPayload(Reader, Class)
|
||||||
* @see GHEventInfo#getPayload(Class)
|
* @see GHEventInfo#getPayload(Class)
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("UnusedDeclaration")
|
||||||
public abstract class GHEventPayload {
|
public abstract class GHEventPayload {
|
||||||
protected GitHub root;
|
protected GitHub root;
|
||||||
|
|
||||||
@@ -18,10 +21,18 @@ public abstract class GHEventPayload {
|
|||||||
this.root = root;
|
this.root = root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A pull request status has changed.
|
||||||
|
*
|
||||||
|
* @see <a href="http://developer.github.com/v3/activity/events/types/#pullrequestevent">authoritative source</a>
|
||||||
|
*/
|
||||||
|
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
|
||||||
|
"NP_UNWRITTEN_FIELD"}, justification = "JSON API")
|
||||||
public static class PullRequest extends GHEventPayload {
|
public static class PullRequest extends GHEventPayload {
|
||||||
private String action;
|
private String action;
|
||||||
private int number;
|
private int number;
|
||||||
GHPullRequest pull_request;
|
private GHPullRequest pull_request;
|
||||||
|
private GHRepository repository;
|
||||||
|
|
||||||
public String getAction() {
|
public String getAction() {
|
||||||
return action;
|
return action;
|
||||||
@@ -36,11 +47,172 @@ public abstract class GHEventPayload {
|
|||||||
return pull_request;
|
return pull_request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public GHRepository getRepository() {
|
||||||
|
return repository;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void wrapUp(GitHub root) {
|
void wrapUp(GitHub root) {
|
||||||
super.wrapUp(root);
|
super.wrapUp(root);
|
||||||
pull_request.wrapUp(root);
|
if (pull_request==null)
|
||||||
|
throw new IllegalStateException("Expected pull_request payload, but got something else. Maybe we've got another type of event?");
|
||||||
|
if (repository!=null) {
|
||||||
|
repository.wrap(root);
|
||||||
|
pull_request.wrap(repository);
|
||||||
|
} else {
|
||||||
|
pull_request.wrapUp(root);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A comment was added to an issue
|
||||||
|
*
|
||||||
|
* @see <a href="http://developer.github.com/v3/activity/events/types/#issuecommentevent">authoritative source</a>
|
||||||
|
*/
|
||||||
|
@SuppressFBWarnings(value = {"UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR", "NP_UNWRITTEN_FIELD" },
|
||||||
|
justification = "Constructed by JSON deserialization")
|
||||||
|
public static class IssueComment extends GHEventPayload {
|
||||||
|
private String action;
|
||||||
|
private GHIssueComment comment;
|
||||||
|
private GHIssue issue;
|
||||||
|
private GHRepository repository;
|
||||||
|
|
||||||
|
@SuppressFBWarnings(value = "UWF_UNWRITTEN_FIELD", justification = "Comes from JSON deserialization")
|
||||||
|
public String getAction() {
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHIssueComment getComment() {
|
||||||
|
return comment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setComment(GHIssueComment comment) {
|
||||||
|
this.comment = comment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHIssue getIssue() {
|
||||||
|
return issue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIssue(GHIssue issue) {
|
||||||
|
this.issue = issue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHRepository getRepository() {
|
||||||
|
return repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRepository(GHRepository repository) {
|
||||||
|
this.repository = repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void wrapUp(GitHub root) {
|
||||||
|
super.wrapUp(root);
|
||||||
|
if (repository != null) {
|
||||||
|
repository.wrap(root);
|
||||||
|
issue.wrap(repository);
|
||||||
|
} else {
|
||||||
|
issue.wrap(root);
|
||||||
|
}
|
||||||
|
comment.wrapUp(issue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A commit was pushed.
|
||||||
|
*
|
||||||
|
* @see <a href="http://developer.github.com/v3/activity/events/types/#pushevent">authoritative source</a>
|
||||||
|
*/
|
||||||
|
public static class Push extends GHEventPayload {
|
||||||
|
private String head, before;
|
||||||
|
private String ref;
|
||||||
|
private int size;
|
||||||
|
private List<PushCommit> commits;
|
||||||
|
private GHRepository repository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The SHA of the HEAD commit on the repository
|
||||||
|
*/
|
||||||
|
public String getHead() {
|
||||||
|
return head;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is undocumented, but it looks like this captures the commit that the ref was pointing to
|
||||||
|
* before the push.
|
||||||
|
*/
|
||||||
|
public String getBefore() {
|
||||||
|
return before;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The full Git ref that was pushed. Example: “refs/heads/master”
|
||||||
|
*/
|
||||||
|
public String getRef() {
|
||||||
|
return ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of commits in the push.
|
||||||
|
* Is this always the same as {@code getCommits().size()}?
|
||||||
|
*/
|
||||||
|
public int getSize() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of pushed commits.
|
||||||
|
*/
|
||||||
|
public List<PushCommit> getCommits() {
|
||||||
|
return commits;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHRepository getRepository() {
|
||||||
|
return repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void wrapUp(GitHub root) {
|
||||||
|
super.wrapUp(root);
|
||||||
|
if (repository!=null)
|
||||||
|
repository.wrap(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Commit in a push
|
||||||
|
*/
|
||||||
|
public static class PushCommit {
|
||||||
|
private GitUser author;
|
||||||
|
private String url, sha, message;
|
||||||
|
private boolean distinct;
|
||||||
|
|
||||||
|
public GitUser getAuthor() {
|
||||||
|
return author;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Points to the commit API resource.
|
||||||
|
*/
|
||||||
|
public String getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSha() {
|
||||||
|
return sha;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this commit is distinct from any that have been pushed before.
|
||||||
|
*/
|
||||||
|
public boolean isDistinct() {
|
||||||
|
return distinct;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
179
src/main/java/org/kohsuke/github/GHGist.java
Normal file
179
src/main/java/org/kohsuke/github/GHGist.java
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gist
|
||||||
|
*
|
||||||
|
* @author Kohsuke Kawaguchi
|
||||||
|
* @see GHUser#listGists()
|
||||||
|
* @see GitHub#getGist(String)
|
||||||
|
* @see GitHub#createGist()
|
||||||
|
* @see <a href="https://developer.github.com/v3/gists/">documentation</a>
|
||||||
|
*/
|
||||||
|
public class GHGist extends GHObject {
|
||||||
|
/*package almost final*/ GHUser owner;
|
||||||
|
/*package almost final*/ GitHub root;
|
||||||
|
|
||||||
|
private String forks_url, commits_url, id, git_pull_url, git_push_url, html_url;
|
||||||
|
|
||||||
|
@JsonProperty("public")
|
||||||
|
private boolean _public;
|
||||||
|
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
private int comments;
|
||||||
|
|
||||||
|
private String comments_url;
|
||||||
|
|
||||||
|
private Map<String,GHGistFile> files = new HashMap<String, GHGistFile>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User that owns this Gist.
|
||||||
|
*/
|
||||||
|
public GHUser getOwner() {
|
||||||
|
return owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getForksUrl() {
|
||||||
|
return forks_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCommitsUrl() {
|
||||||
|
return commits_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* URL like https://gist.github.com/gists/12345.git
|
||||||
|
*/
|
||||||
|
public String getGitPullUrl() {
|
||||||
|
return git_pull_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGitPushUrl() {
|
||||||
|
return git_push_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public URL getHtmlUrl() {
|
||||||
|
return GitHub.parseURL(html_url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPublic() {
|
||||||
|
return _public;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCommentCount() {
|
||||||
|
return comments;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* API URL of listing comments.
|
||||||
|
*/
|
||||||
|
public String getCommentsUrl() {
|
||||||
|
return comments_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHGistFile getFile(String name) {
|
||||||
|
return files.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String,GHGistFile> getFiles() {
|
||||||
|
return Collections.unmodifiableMap(files);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*package*/ 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.
|
||||||
|
*/
|
||||||
|
/*package*/ GHGist wrapUp(GitHub root) throws IOException {
|
||||||
|
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) {
|
||||||
|
return "/gists/" + id + '/' + tail;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void star() throws IOException {
|
||||||
|
new Requester(root).method("PUT").to(getApiTailUrl("star"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unstar() throws IOException {
|
||||||
|
new Requester(root).method("DELETE").to(getApiTailUrl("star"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isStarred() throws IOException {
|
||||||
|
return root.retrieve().asHttpStatusCode(getApiTailUrl("star"))/100==2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forks this gist into your own.
|
||||||
|
*/
|
||||||
|
public GHGist fork() throws IOException {
|
||||||
|
return new Requester(root).to(getApiTailUrl("forks"),GHGist.class).wrapUp(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PagedIterable<GHGist> listForks() {
|
||||||
|
return new PagedIterable<GHGist>() {
|
||||||
|
public PagedIterator<GHGist> _iterator(int pageSize) {
|
||||||
|
return new PagedIterator<GHGist>(root.retrieve().asIterator(getApiTailUrl("forks"), GHGist[].class, pageSize)) {
|
||||||
|
@Override
|
||||||
|
protected void wrapUp(GHGist[] page) {
|
||||||
|
try {
|
||||||
|
for (GHGist c : page)
|
||||||
|
c.wrapUp(root);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new Error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes this gist.
|
||||||
|
*/
|
||||||
|
public void delete() throws IOException {
|
||||||
|
new Requester(root).method("DELETE").to("/gists/" + id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
GHGist ghGist = (GHGist) o;
|
||||||
|
return id.equals(ghGist.id);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return id.hashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
48
src/main/java/org/kohsuke/github/GHGistBuilder.java
Normal file
48
src/main/java/org/kohsuke/github/GHGistBuilder.java
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builder pattern for creating a new Gist.
|
||||||
|
*
|
||||||
|
* @author Kohsuke Kawaguchi
|
||||||
|
* @see GitHub#createGist()
|
||||||
|
*/
|
||||||
|
public class GHGistBuilder {
|
||||||
|
private final GitHub root;
|
||||||
|
private final Requester req;
|
||||||
|
private final LinkedHashMap<String,Object> files = new LinkedHashMap<String, Object>();
|
||||||
|
|
||||||
|
public GHGistBuilder(GitHub root) {
|
||||||
|
this.root = root;
|
||||||
|
req = new Requester(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHGistBuilder description(String desc) {
|
||||||
|
req.with("description",desc);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHGistBuilder public_(boolean v) {
|
||||||
|
req.with("public",v);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new file.
|
||||||
|
*/
|
||||||
|
public GHGistBuilder file(String fileName, String content) {
|
||||||
|
files.put(fileName, Collections.singletonMap("content", content));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Gist based on the parameters specified thus far.
|
||||||
|
*/
|
||||||
|
public GHGist create() throws IOException {
|
||||||
|
req._with("files",files);
|
||||||
|
return req.to("/gists",GHGist.class).wrapUp(root);
|
||||||
|
}
|
||||||
|
}
|
||||||
60
src/main/java/org/kohsuke/github/GHGistFile.java
Normal file
60
src/main/java/org/kohsuke/github/GHGistFile.java
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A file inside {@link GHGist}
|
||||||
|
*
|
||||||
|
* @author Kohsuke Kawaguchi
|
||||||
|
* @see GHGist#getFile(String)
|
||||||
|
* @see GHGist#getFiles()
|
||||||
|
*/
|
||||||
|
public class GHGistFile {
|
||||||
|
/*package almost final*/ String fileName;
|
||||||
|
|
||||||
|
private int size;
|
||||||
|
private String raw_url, type, language, content;
|
||||||
|
private boolean truncated;
|
||||||
|
|
||||||
|
|
||||||
|
public String getFileName() {
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File size in bytes.
|
||||||
|
*/
|
||||||
|
public int getSize() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* URL that serves this file as-is.
|
||||||
|
*/
|
||||||
|
public String getRawUrl() {
|
||||||
|
return raw_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Content type of this Gist, such as "text/plain"
|
||||||
|
*/
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLanguage() {
|
||||||
|
return language;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Content of this file.
|
||||||
|
*/
|
||||||
|
public String getContent() {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (?) indicates if {@link #getContent()} contains a truncated content.
|
||||||
|
*/
|
||||||
|
public boolean isTruncated() {
|
||||||
|
return truncated;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
package org.kohsuke.github;
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -10,22 +12,13 @@ import java.util.Map;
|
|||||||
/**
|
/**
|
||||||
* @author Kohsuke Kawaguchi
|
* @author Kohsuke Kawaguchi
|
||||||
*/
|
*/
|
||||||
public final class GHHook {
|
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
|
||||||
/**
|
"NP_UNWRITTEN_FIELD"}, justification = "JSON API")
|
||||||
* Repository that the hook belongs to.
|
public abstract class GHHook extends GHObject {
|
||||||
*/
|
String name;
|
||||||
/*package*/ transient GHRepository repository;
|
|
||||||
|
|
||||||
String created_at, updated_at, name;
|
|
||||||
List<String> events;
|
List<String> events;
|
||||||
boolean active;
|
boolean active;
|
||||||
Map<String,String> config;
|
Map<String,String> config;
|
||||||
int id;
|
|
||||||
|
|
||||||
/*package*/ GHHook wrap(GHRepository owner) {
|
|
||||||
this.repository = owner;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
@@ -33,8 +26,10 @@ public final class GHHook {
|
|||||||
|
|
||||||
public EnumSet<GHEvent> getEvents() {
|
public EnumSet<GHEvent> getEvents() {
|
||||||
EnumSet<GHEvent> s = EnumSet.noneOf(GHEvent.class);
|
EnumSet<GHEvent> s = EnumSet.noneOf(GHEvent.class);
|
||||||
for (String e : events)
|
for (String e : events) {
|
||||||
Enum.valueOf(GHEvent.class,e.toUpperCase(Locale.ENGLISH));
|
if (e.equals("*")) s.add(GHEvent.ALL);
|
||||||
|
else s.add(Enum.valueOf(GHEvent.class, e.toUpperCase(Locale.ENGLISH)));
|
||||||
|
}
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,14 +41,22 @@ public final class GHHook {
|
|||||||
return Collections.unmodifiableMap(config);
|
return Collections.unmodifiableMap(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes this hook.
|
* Deletes this hook.
|
||||||
*/
|
*/
|
||||||
public void delete() throws IOException {
|
public void delete() throws IOException {
|
||||||
new Requester(repository.root).method("DELETE").to(String.format("/repos/%s/%s/hooks/%d", repository.getOwnerName(), repository.getName(), id));
|
new Requester(getRoot()).method("DELETE").to(getApiRoute());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated This object has no HTML URL.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public URL getHtmlUrl() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract GitHub getRoot();
|
||||||
|
|
||||||
|
abstract String getApiRoute();
|
||||||
}
|
}
|
||||||
|
|||||||
131
src/main/java/org/kohsuke/github/GHHooks.java
Normal file
131
src/main/java/org/kohsuke/github/GHHooks.java
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class for creating and retrieving webhooks; removes duplication between GHOrganization and GHRepository
|
||||||
|
* functionality
|
||||||
|
*/
|
||||||
|
class GHHooks {
|
||||||
|
static abstract class Context {
|
||||||
|
private final GitHub root;
|
||||||
|
|
||||||
|
private Context(GitHub root) {
|
||||||
|
this.root = root;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<GHHook> getHooks() throws IOException {
|
||||||
|
|
||||||
|
GHHook [] hookArray = root.retrieve().to(collection(),collectionClass()); // jdk/eclipse bug requires this to be on separate line
|
||||||
|
List<GHHook> list = new ArrayList<GHHook>(Arrays.asList(hookArray));
|
||||||
|
for (GHHook h : list)
|
||||||
|
wrap(h);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHHook getHook(int id) throws IOException {
|
||||||
|
GHHook hook = root.retrieve().to(collection() + "/" + id, clazz());
|
||||||
|
return wrap(hook);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHHook createHook(String name, Map<String, String> config, Collection<GHEvent> events, boolean active) throws IOException {
|
||||||
|
List<String> ea = null;
|
||||||
|
if (events!=null) {
|
||||||
|
ea = new ArrayList<String>();
|
||||||
|
for (GHEvent e : events)
|
||||||
|
ea.add(e.symbol());
|
||||||
|
}
|
||||||
|
|
||||||
|
GHHook hook = new Requester(root)
|
||||||
|
.with("name", name)
|
||||||
|
.with("active", active)
|
||||||
|
._with("config", config)
|
||||||
|
._with("events", ea)
|
||||||
|
.to(collection(), clazz());
|
||||||
|
|
||||||
|
return wrap(hook);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract String collection();
|
||||||
|
|
||||||
|
abstract Class<? extends GHHook[]> collectionClass();
|
||||||
|
|
||||||
|
abstract Class<? extends GHHook> clazz();
|
||||||
|
|
||||||
|
abstract GHHook wrap(GHHook hook);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class RepoContext extends Context {
|
||||||
|
private final GHRepository repository;
|
||||||
|
private final GHUser owner;
|
||||||
|
|
||||||
|
private RepoContext(GHRepository repository, GHUser owner) {
|
||||||
|
super(repository.root);
|
||||||
|
this.repository = repository;
|
||||||
|
this.owner = owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String collection() {
|
||||||
|
return String.format("/repos/%s/%s/hooks", owner.getLogin(), repository.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Class<? extends GHHook[]> collectionClass() {
|
||||||
|
return GHRepoHook[].class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Class<? extends GHHook> clazz() {
|
||||||
|
return GHRepoHook.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
GHHook wrap(GHHook hook) {
|
||||||
|
return ((GHRepoHook)hook).wrap(repository);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class OrgContext extends Context {
|
||||||
|
private final GHOrganization organization;
|
||||||
|
|
||||||
|
private OrgContext(GHOrganization organization) {
|
||||||
|
super(organization.root);
|
||||||
|
this.organization = organization;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String collection() {
|
||||||
|
return String.format("/orgs/%s/hooks", organization.getLogin());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Class<? extends GHHook[]> collectionClass() {
|
||||||
|
return GHOrgHook[].class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Class<? extends GHHook> clazz() {
|
||||||
|
return GHOrgHook.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
GHHook wrap(GHHook hook) {
|
||||||
|
return ((GHOrgHook)hook).wrap(organization);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Context repoContext(GHRepository repository, GHUser owner) {
|
||||||
|
return new RepoContext(repository, owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Context orgContext(GHOrganization organization) {
|
||||||
|
return new OrgContext(organization);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,6 +24,9 @@
|
|||||||
|
|
||||||
package org.kohsuke.github;
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
|
||||||
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@@ -37,34 +40,47 @@ import java.util.Locale;
|
|||||||
*
|
*
|
||||||
* @author Eric Maupin
|
* @author Eric Maupin
|
||||||
* @author Kohsuke Kawaguchi
|
* @author Kohsuke Kawaguchi
|
||||||
|
* @see GHRepository#getIssue(int)
|
||||||
|
* @see GitHub#searchIssues()
|
||||||
|
* @see GHIssueSearchBuilder
|
||||||
*/
|
*/
|
||||||
public class GHIssue {
|
public class GHIssue extends GHObject {
|
||||||
GitHub root;
|
GitHub root;
|
||||||
GHRepository owner;
|
GHRepository owner;
|
||||||
|
|
||||||
// API v3
|
// API v3
|
||||||
protected GHUser assignee;
|
protected GHUser assignee;
|
||||||
protected String state;
|
protected String state;
|
||||||
protected int number;
|
protected int number;
|
||||||
protected String closed_at;
|
protected String closed_at;
|
||||||
protected int comments;
|
protected int comments;
|
||||||
|
@SkipFromToString
|
||||||
protected String body;
|
protected String body;
|
||||||
protected List<String> labels;
|
// for backward compatibility with < 1.63, this collection needs to hold instances of Label, not GHLabel
|
||||||
|
protected List<Label> labels;
|
||||||
protected GHUser user;
|
protected GHUser user;
|
||||||
protected String title, created_at, html_url;
|
protected String title, html_url;
|
||||||
protected GHIssue.PullRequest pull_request;
|
protected GHIssue.PullRequest pull_request;
|
||||||
protected GHMilestone milestone;
|
protected GHMilestone milestone;
|
||||||
protected String url, updated_at;
|
|
||||||
protected int id;
|
|
||||||
protected GHUser closed_by;
|
protected GHUser closed_by;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use {@link GHLabel}
|
||||||
|
*/
|
||||||
|
public static class Label extends GHLabel {
|
||||||
|
}
|
||||||
|
|
||||||
/*package*/ GHIssue wrap(GHRepository owner) {
|
/*package*/ GHIssue wrap(GHRepository owner) {
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
this.root = owner.root;
|
if(milestone != null) milestone.wrap(owner);
|
||||||
if(milestone != null) milestone.wrap(owner);
|
return wrap(owner.root);
|
||||||
if(assignee != null) assignee.wrapUp(root);
|
}
|
||||||
if(user != null) user.wrapUp(root);
|
|
||||||
if(closed_by != null) closed_by.wrapUp(root);
|
/*package*/ GHIssue wrap(GitHub root) {
|
||||||
|
this.root = root;
|
||||||
|
if(assignee != null) assignee.wrapUp(root);
|
||||||
|
if(user != null) user.wrapUp(root);
|
||||||
|
if(closed_by != null) closed_by.wrapUp(root);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,7 +115,7 @@ public class GHIssue {
|
|||||||
* The HTML page of this issue,
|
* The HTML page of this issue,
|
||||||
* like https://github.com/jenkinsci/jenkins/issues/100
|
* like https://github.com/jenkinsci/jenkins/issues/100
|
||||||
*/
|
*/
|
||||||
public URL getUrl() {
|
public URL getHtmlUrl() {
|
||||||
return GitHub.parseURL(html_url);
|
return GitHub.parseURL(html_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,40 +127,41 @@ public class GHIssue {
|
|||||||
return Enum.valueOf(GHIssueState.class, state.toUpperCase(Locale.ENGLISH));
|
return Enum.valueOf(GHIssueState.class, state.toUpperCase(Locale.ENGLISH));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Collection<String> getLabels() {
|
public Collection<GHLabel> getLabels() throws IOException {
|
||||||
if(labels == null){
|
if(labels == null){
|
||||||
return Collections.EMPTY_LIST;
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
return Collections.unmodifiableList(labels);
|
return Collections.<GHLabel>unmodifiableList(labels);
|
||||||
}
|
|
||||||
|
|
||||||
public Date getCreatedAt() {
|
|
||||||
return GitHub.parseDate(created_at);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Date getUpdatedAt() {
|
|
||||||
return GitHub.parseDate(updated_at);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Date getClosedAt() {
|
public Date getClosedAt() {
|
||||||
return GitHub.parseDate(closed_at);
|
return GitHub.parseDate(closed_at);
|
||||||
}
|
}
|
||||||
|
|
||||||
public URL getApiURL(){
|
public URL getApiURL(){
|
||||||
return GitHub.parseURL(url);
|
return GitHub.parseURL(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the issue by adding a comment.
|
* Updates the issue by adding a comment.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* Newly posted comment.
|
||||||
*/
|
*/
|
||||||
public void comment(String message) throws IOException {
|
@WithBridgeMethods(void.class)
|
||||||
new Requester(root).with("body",message).to(getApiRoute() + "/comments");
|
public GHIssueComment comment(String message) throws IOException {
|
||||||
|
GHIssueComment r = new Requester(root).with("body",message).to(getIssuesApiRoute() + "/comments", GHIssueComment.class);
|
||||||
|
return r.wrapUp(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void edit(String key, Object value) throws IOException {
|
private void edit(String key, Object value) throws IOException {
|
||||||
new Requester(root)._with(key, value).method("PATCH").to(getApiRoute());
|
new Requester(root)._with(key, value).method("PATCH").to(getApiRoute());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void editIssue(String key, Object value) throws IOException {
|
||||||
|
new Requester(root)._with(key, value).method("PATCH").to(getIssuesApiRoute());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes this issue.
|
* Closes this issue.
|
||||||
*/
|
*/
|
||||||
@@ -168,29 +185,29 @@ public class GHIssue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void assignTo(GHUser user) throws IOException {
|
public void assignTo(GHUser user) throws IOException {
|
||||||
edit("assignee",user.getLogin());
|
editIssue("assignee", user.getLogin());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLabels(String... labels) throws IOException {
|
public void setLabels(String... labels) throws IOException {
|
||||||
edit("assignee",labels);
|
editIssue("labels",labels);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Obtains all the comments associated with this issue.
|
* Obtains all the comments associated with this issue.
|
||||||
*
|
*
|
||||||
* @see #listComments()
|
* @see #listComments()
|
||||||
*/
|
*/
|
||||||
public List<GHIssueComment> getComments() throws IOException {
|
public List<GHIssueComment> getComments() throws IOException {
|
||||||
return listComments().asList();
|
return listComments().asList();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Obtains all the comments associated with this issue.
|
* Obtains all the comments associated with this issue.
|
||||||
*/
|
*/
|
||||||
public PagedIterable<GHIssueComment> listComments() throws IOException {
|
public PagedIterable<GHIssueComment> listComments() throws IOException {
|
||||||
return new PagedIterable<GHIssueComment>() {
|
return new PagedIterable<GHIssueComment>() {
|
||||||
public PagedIterator<GHIssueComment> iterator() {
|
public PagedIterator<GHIssueComment> _iterator(int pageSize) {
|
||||||
return new PagedIterator<GHIssueComment>(root.retrieve().asIterator(getApiRoute() + "/comments", GHIssueComment[].class)) {
|
return new PagedIterator<GHIssueComment>(root.retrieve().asIterator(getIssuesApiRoute() + "/comments", GHIssueComment[].class, pageSize)) {
|
||||||
protected void wrapUp(GHIssueComment[] page) {
|
protected void wrapUp(GHIssueComment[] page) {
|
||||||
for (GHIssueComment c : page)
|
for (GHIssueComment c : page)
|
||||||
c.wrapUp(GHIssue.this);
|
c.wrapUp(GHIssue.this);
|
||||||
@@ -200,54 +217,75 @@ public class GHIssue {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getApiRoute() {
|
protected String getApiRoute() {
|
||||||
|
return getIssuesApiRoute();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getIssuesApiRoute() {
|
||||||
return "/repos/"+owner.getOwnerName()+"/"+owner.getName()+"/issues/"+number;
|
return "/repos/"+owner.getOwnerName()+"/"+owner.getName()+"/issues/"+number;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GHUser getAssignee() {
|
public GHUser getAssignee() {
|
||||||
return assignee;
|
return assignee;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User who submitted the issue.
|
* User who submitted the issue.
|
||||||
*/
|
*/
|
||||||
public GHUser getUser() {
|
public GHUser getUser() {
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GHUser getClosedBy() {
|
/**
|
||||||
if(!"closed".equals(state)) return null;
|
* Reports who has closed the issue.
|
||||||
if(closed_by != null) return closed_by;
|
*
|
||||||
|
* <p>
|
||||||
|
* Note that GitHub doesn't always seem to report this information
|
||||||
|
* even for an issue that's already closed. See
|
||||||
|
* https://github.com/kohsuke/github-api/issues/60.
|
||||||
|
*/
|
||||||
|
public GHUser getClosedBy() {
|
||||||
|
if(!"closed".equals(state)) return null;
|
||||||
|
if(closed_by != null) return closed_by;
|
||||||
|
|
||||||
//TODO closed_by = owner.getIssue(number).getClosed_by();
|
//TODO closed_by = owner.getIssue(number).getClosed_by();
|
||||||
return closed_by;
|
return closed_by;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getCommentsCount(){
|
public int getCommentsCount(){
|
||||||
return comments;
|
return comments;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PullRequest getPullRequest() {
|
/**
|
||||||
return pull_request;
|
* Returns non-null if this issue is a shadow of a pull request.
|
||||||
}
|
*/
|
||||||
|
public PullRequest getPullRequest() {
|
||||||
|
return pull_request;
|
||||||
|
}
|
||||||
|
|
||||||
public GHMilestone getMilestone() {
|
public boolean isPullRequest() {
|
||||||
return milestone;
|
return pull_request!=null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class PullRequest{
|
public GHMilestone getMilestone() {
|
||||||
private String diff_url, patch_url, html_url;
|
return milestone;
|
||||||
|
}
|
||||||
|
|
||||||
public URL getDiffUrl() {
|
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD"},
|
||||||
return GitHub.parseURL(diff_url);
|
justification = "JSON API")
|
||||||
}
|
public static class PullRequest{
|
||||||
|
private String diff_url, patch_url, html_url;
|
||||||
|
|
||||||
public URL getPatchUrl() {
|
public URL getDiffUrl() {
|
||||||
return GitHub.parseURL(patch_url);
|
return GitHub.parseURL(diff_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
public URL getUrl() {
|
public URL getPatchUrl() {
|
||||||
return GitHub.parseURL(html_url);
|
return GitHub.parseURL(patch_url);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public URL getUrl() {
|
||||||
|
return GitHub.parseURL(html_url);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -25,19 +25,16 @@ package org.kohsuke.github;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Comment to the issue
|
* Comment to the issue
|
||||||
*
|
*
|
||||||
* @author Kohsuke Kawaguchi
|
* @author Kohsuke Kawaguchi
|
||||||
*/
|
*/
|
||||||
public class GHIssueComment {
|
public class GHIssueComment extends GHObject {
|
||||||
GHIssue owner;
|
GHIssue owner;
|
||||||
|
|
||||||
private String body, gravatar_id, created_at, updated_at;
|
private String body, gravatar_id;
|
||||||
private URL url;
|
|
||||||
private int id;
|
|
||||||
private GHUser user;
|
private GHUser user;
|
||||||
|
|
||||||
/*package*/ GHIssueComment wrapUp(GHIssue owner) {
|
/*package*/ GHIssueComment wrapUp(GHIssue owner) {
|
||||||
@@ -59,22 +56,6 @@ public class GHIssueComment {
|
|||||||
return body;
|
return body;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Date getCreatedAt() {
|
|
||||||
return GitHub.parseDate(created_at);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Date getUpdatedAt() {
|
|
||||||
return GitHub.parseDate(updated_at);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public URL getUrl() {
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the ID of the user who posted this comment.
|
* Gets the ID of the user who posted this comment.
|
||||||
*/
|
*/
|
||||||
@@ -89,4 +70,31 @@ public class GHIssueComment {
|
|||||||
public GHUser getUser() throws IOException {
|
public GHUser getUser() throws IOException {
|
||||||
return owner.root.getUser(user.getLogin());
|
return owner.root.getUser(user.getLogin());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated This object has no HTML URL.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public URL getHtmlUrl() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the body of the issue comment.
|
||||||
|
*/
|
||||||
|
public void update(String body) throws IOException {
|
||||||
|
new Requester(owner.root).with("body", body).method("PATCH").to(getApiRoute(), GHIssueComment.class);
|
||||||
|
this.body = body;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes this issue comment.
|
||||||
|
*/
|
||||||
|
public void delete() throws IOException {
|
||||||
|
new Requester(owner.root).method("DELETE").to(getApiRoute());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getApiRoute() {
|
||||||
|
return "/repos/"+owner.getRepository().getOwnerName()+"/"+owner.getRepository().getName()+"/issues/comments/" + id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
66
src/main/java/org/kohsuke/github/GHIssueSearchBuilder.java
Normal file
66
src/main/java/org/kohsuke/github/GHIssueSearchBuilder.java
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search issues.
|
||||||
|
*
|
||||||
|
* @author Kohsuke Kawaguchi
|
||||||
|
* @see GitHub#searchIssues()
|
||||||
|
*/
|
||||||
|
public class GHIssueSearchBuilder extends GHSearchBuilder<GHIssue> {
|
||||||
|
/*package*/ GHIssueSearchBuilder(GitHub root) {
|
||||||
|
super(root,IssueSearchResult.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search terms.
|
||||||
|
*/
|
||||||
|
public GHIssueSearchBuilder q(String term) {
|
||||||
|
super.q(term);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHIssueSearchBuilder mentions(GHUser u) {
|
||||||
|
return mentions(u.getLogin());
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHIssueSearchBuilder mentions(String login) {
|
||||||
|
return q("mentions:"+login);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHIssueSearchBuilder isOpen() {
|
||||||
|
return q("is:open");
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHIssueSearchBuilder isClosed() {
|
||||||
|
return q("is:closed");
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHIssueSearchBuilder isMerged() {
|
||||||
|
return q("is:merged");
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHIssueSearchBuilder sort(Sort sort) {
|
||||||
|
req.with("sort",sort);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Sort { COMMENTS, CREATED, UPDATED }
|
||||||
|
|
||||||
|
private static class IssueSearchResult extends SearchResult<GHIssue> {
|
||||||
|
private GHIssue[] items;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
/*package*/ GHIssue[] getItems(GitHub root) {
|
||||||
|
for (GHIssue i : items)
|
||||||
|
i.wrap(root);
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getApiUrl() {
|
||||||
|
return "/search/issues";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,7 +24,11 @@
|
|||||||
|
|
||||||
package org.kohsuke.github;
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see GHPullRequestQueryBuilder#state(GHIssueState)
|
||||||
|
*/
|
||||||
public enum GHIssueState {
|
public enum GHIssueState {
|
||||||
OPEN,
|
OPEN,
|
||||||
CLOSED
|
CLOSED,
|
||||||
|
ALL
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.kohsuke.github;
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||||
import org.apache.commons.lang.builder.ToStringBuilder;
|
import org.apache.commons.lang.builder.ToStringBuilder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -7,12 +8,13 @@ import org.apache.commons.lang.builder.ToStringBuilder;
|
|||||||
*
|
*
|
||||||
* @author Kohsuke Kawaguchi
|
* @author Kohsuke Kawaguchi
|
||||||
*/
|
*/
|
||||||
|
@SuppressFBWarnings(value = "UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", justification = "JSON API")
|
||||||
public class GHKey {
|
public class GHKey {
|
||||||
/*package almost final*/ GitHub root;
|
/*package almost final*/ GitHub root;
|
||||||
|
|
||||||
private String url, key, title;
|
protected String url, key, title;
|
||||||
private boolean verified;
|
protected boolean verified;
|
||||||
private int id;
|
protected int id;
|
||||||
|
|
||||||
public int getId() {
|
public int getId() {
|
||||||
return id;
|
return id;
|
||||||
@@ -33,6 +35,10 @@ public class GHKey {
|
|||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public GitHub getRoot() {
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isVerified() {
|
public boolean isVerified() {
|
||||||
return verified;
|
return verified;
|
||||||
}
|
}
|
||||||
|
|||||||
37
src/main/java/org/kohsuke/github/GHLabel.java
Normal file
37
src/main/java/org/kohsuke/github/GHLabel.java
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Kohsuke Kawaguchi
|
||||||
|
* @see GHIssue#getLabels()
|
||||||
|
* @see GHRepository#listLabels()
|
||||||
|
*/
|
||||||
|
public class GHLabel {
|
||||||
|
private String url, name, color;
|
||||||
|
private GHRepository repo;
|
||||||
|
|
||||||
|
public String getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Color code without leading '#', such as 'f29513'
|
||||||
|
*/
|
||||||
|
public String getColor() {
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*package*/ GHLabel wrapUp(GHRepository repo) {
|
||||||
|
this.repo = repo;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void delete() throws IOException {
|
||||||
|
repo.root.retrieve().method("DELETE").to(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
168
src/main/java/org/kohsuke/github/GHLicense.java
Normal file
168
src/main/java/org/kohsuke/github/GHLicense.java
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2016, Duncan Dickinson
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
|
||||||
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.kohsuke.github.Previews.DRAX;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The GitHub Preview API's license information
|
||||||
|
* <p>
|
||||||
|
* WARNING: This uses a PREVIEW API - subject to change.
|
||||||
|
*
|
||||||
|
* @author Duncan Dickinson
|
||||||
|
* @see GitHub#getLicense(String)
|
||||||
|
* @see GHRepository#getLicense()
|
||||||
|
* @see <a href="https://developer.github.com/v3/licenses/">https://developer.github.com/v3/licenses/</a>
|
||||||
|
*/
|
||||||
|
@Preview @Deprecated
|
||||||
|
@SuppressWarnings({"UnusedDeclaration"})
|
||||||
|
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
|
||||||
|
"NP_UNWRITTEN_FIELD"}, justification = "JSON API")
|
||||||
|
public class GHLicense extends GHObject {
|
||||||
|
@SuppressFBWarnings("IS2_INCONSISTENT_SYNC") // root is set before the object is returned to the app
|
||||||
|
/*package almost final*/ GitHub root;
|
||||||
|
|
||||||
|
// these fields are always present, even in the short form
|
||||||
|
protected String key, name;
|
||||||
|
|
||||||
|
// the rest is only after populated
|
||||||
|
protected Boolean featured;
|
||||||
|
|
||||||
|
protected String html_url, description, category, implementation, body;
|
||||||
|
|
||||||
|
protected List<String> required = new ArrayList<String>();
|
||||||
|
protected List<String> permitted = new ArrayList<String>();
|
||||||
|
protected List<String> forbidden = new ArrayList<String>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return a mnemonic for the license
|
||||||
|
*/
|
||||||
|
public String getKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the license name
|
||||||
|
*/
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return API URL of this object.
|
||||||
|
*/
|
||||||
|
@WithBridgeMethods(value = String.class, adapterMethod = "urlToString")
|
||||||
|
public URL getUrl() {
|
||||||
|
return GitHub.parseURL(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Featured licenses are bold in the new repository drop-down
|
||||||
|
*
|
||||||
|
* @return True if the license is featured, false otherwise
|
||||||
|
*/
|
||||||
|
public Boolean isFeatured() throws IOException {
|
||||||
|
populate();
|
||||||
|
return featured;
|
||||||
|
}
|
||||||
|
|
||||||
|
public URL getHtmlUrl() throws IOException {
|
||||||
|
populate();
|
||||||
|
return GitHub.parseURL(html_url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() throws IOException {
|
||||||
|
populate();
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCategory() throws IOException {
|
||||||
|
populate();
|
||||||
|
return category;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getImplementation() throws IOException {
|
||||||
|
populate();
|
||||||
|
return implementation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getRequired() throws IOException {
|
||||||
|
populate();
|
||||||
|
return required;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getPermitted() throws IOException {
|
||||||
|
populate();
|
||||||
|
return permitted;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getForbidden() throws IOException {
|
||||||
|
populate();
|
||||||
|
return forbidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBody() throws IOException {
|
||||||
|
populate();
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fully populate the data by retrieving missing data.
|
||||||
|
*
|
||||||
|
* Depending on the original API call where this object is created, it may not contain everything.
|
||||||
|
*/
|
||||||
|
protected synchronized void populate() throws IOException {
|
||||||
|
if (description!=null) return; // already populated
|
||||||
|
|
||||||
|
root.retrieve().withPreview(DRAX).to(url, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (!(o instanceof GHLicense)) return false;
|
||||||
|
|
||||||
|
GHLicense that = (GHLicense) o;
|
||||||
|
return this.url.equals(that.url);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return url.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*package*/ GHLicense wrap(GitHub root) {
|
||||||
|
this.root = root;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
package org.kohsuke.github;
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
@@ -8,66 +10,92 @@ import java.util.Locale;
|
|||||||
* @author Yusuke Kokubo
|
* @author Yusuke Kokubo
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class GHMilestone {
|
public class GHMilestone extends GHObject {
|
||||||
GitHub root;
|
GitHub root;
|
||||||
GHRepository owner;
|
GHRepository owner;
|
||||||
|
|
||||||
GHUser creator;
|
GHUser creator;
|
||||||
private String state, due_on, title, url, created_at, description;
|
private String state, due_on, title, description, html_url;
|
||||||
private int closed_issues, open_issues, number;
|
private int closed_issues, open_issues, number;
|
||||||
|
protected String closed_at;
|
||||||
|
|
||||||
public GitHub getRoot() {
|
public GitHub getRoot() {
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GHRepository getOwner() {
|
public GHRepository getOwner() {
|
||||||
return owner;
|
return owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GHUser getCreator() {
|
public GHUser getCreator() {
|
||||||
return creator;
|
return creator;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Date getDueOn() {
|
public Date getDueOn() {
|
||||||
if (due_on == null) return null;
|
if (due_on == null) return null;
|
||||||
return GitHub.parseDate(due_on);
|
return GitHub.parseDate(due_on);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getTitle() {
|
/**
|
||||||
return title;
|
* When was this milestone closed?
|
||||||
}
|
*/
|
||||||
|
public Date getClosedAt() throws IOException {
|
||||||
|
return GitHub.parseDate(closed_at);
|
||||||
|
}
|
||||||
|
|
||||||
public String getUrl() {
|
public String getTitle() {
|
||||||
return url;
|
return title;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Date getCreatedAt() {
|
public String getDescription() {
|
||||||
return GitHub.parseDate(created_at);
|
return description;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getDescription() {
|
public int getClosedIssues() {
|
||||||
return description;
|
return closed_issues;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getClosedIssues() {
|
public int getOpenIssues() {
|
||||||
return closed_issues;
|
return open_issues;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getOpenIssues() {
|
public int getNumber() {
|
||||||
return open_issues;
|
return number;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getNumber() {
|
public URL getHtmlUrl() {
|
||||||
return number;
|
return GitHub.parseURL(html_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
public GHMilestoneState getState() {
|
public GHMilestoneState getState() {
|
||||||
return Enum.valueOf(GHMilestoneState.class, state.toUpperCase(Locale.ENGLISH));
|
return Enum.valueOf(GHMilestoneState.class, state.toUpperCase(Locale.ENGLISH));
|
||||||
}
|
}
|
||||||
|
|
||||||
public GHMilestone wrap(GHRepository repo) {
|
/**
|
||||||
this.owner = repo;
|
* Closes this milestone.
|
||||||
this.root = repo.root;
|
*/
|
||||||
return this;
|
public void close() throws IOException {
|
||||||
}
|
edit("state", "closed");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reopens this milestone.
|
||||||
|
*/
|
||||||
|
public void reopen() throws IOException {
|
||||||
|
edit("state", "open");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void edit(String key, Object value) throws IOException {
|
||||||
|
new Requester(root)._with(key, value).method("PATCH").to(getApiRoute());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getApiRoute() {
|
||||||
|
return "/repos/"+owner.getOwnerName()+"/"+owner.getName()+"/milestones/"+number;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHMilestone wrap(GHRepository repo) {
|
||||||
|
this.owner = repo;
|
||||||
|
this.root = repo.root;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,15 @@
|
|||||||
package org.kohsuke.github;
|
package org.kohsuke.github;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the account that's logging into GitHub.
|
* Represents the account that's logging into GitHub.
|
||||||
@@ -11,6 +17,46 @@ import java.util.List;
|
|||||||
* @author Kohsuke Kawaguchi
|
* @author Kohsuke Kawaguchi
|
||||||
*/
|
*/
|
||||||
public class GHMyself extends GHUser {
|
public class GHMyself extends GHUser {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type of repositories returned during listing.
|
||||||
|
*/
|
||||||
|
public enum RepositoryListFilter {
|
||||||
|
/**
|
||||||
|
* All public and private repositories that current user has access or collaborates to
|
||||||
|
*/
|
||||||
|
ALL,
|
||||||
|
/**
|
||||||
|
* Public and private repositories owned by current user
|
||||||
|
*/
|
||||||
|
OWNER,
|
||||||
|
/**
|
||||||
|
* Public repositories that current user has access or collaborates to
|
||||||
|
*/
|
||||||
|
PUBLIC,
|
||||||
|
/**
|
||||||
|
* Private repositories that current user has access or collaborates to
|
||||||
|
*/
|
||||||
|
PRIVATE,
|
||||||
|
/**
|
||||||
|
* Public and private repositories that current user is a member
|
||||||
|
*/
|
||||||
|
MEMBER;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
* Use {@link #getEmails2()}
|
||||||
|
*/
|
||||||
|
public List<String> getEmails() throws IOException {
|
||||||
|
List<GHEmail> src = getEmails2();
|
||||||
|
List<String> r = new ArrayList<String>(src.size());
|
||||||
|
for (GHEmail e : src) {
|
||||||
|
r.add(e.getEmail());
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the read-only list of e-mail addresses configured for you.
|
* Returns the read-only list of e-mail addresses configured for you.
|
||||||
*
|
*
|
||||||
@@ -21,14 +67,17 @@ public class GHMyself extends GHUser {
|
|||||||
* @return
|
* @return
|
||||||
* Always non-null.
|
* Always non-null.
|
||||||
*/
|
*/
|
||||||
public List<String> getEmails() throws IOException {
|
public List<GHEmail> getEmails2() throws IOException {
|
||||||
String[] addresses = root.retrieve().to("/user/emails", String[].class);
|
GHEmail[] addresses = root.retrieve().to("/user/emails", GHEmail[].class);
|
||||||
return Collections.unmodifiableList(Arrays.asList(addresses));
|
return Collections.unmodifiableList(Arrays.asList(addresses));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the read-only list of all the pulic keys of the current user.
|
* Returns the read-only list of all the pulic keys of the current user.
|
||||||
*
|
*
|
||||||
|
* NOTE: When using OAuth authenticaiton, the READ/WRITE User scope is
|
||||||
|
* required by the GitHub APIs, otherwise you will get a 404 NOT FOUND.
|
||||||
|
*
|
||||||
* @return
|
* @return
|
||||||
* Always non-null.
|
* Always non-null.
|
||||||
*/
|
*/
|
||||||
@@ -36,6 +85,99 @@ public class GHMyself extends GHUser {
|
|||||||
return Collections.unmodifiableList(Arrays.asList(root.retrieve().to("/user/keys", GHKey[].class)));
|
return Collections.unmodifiableList(Arrays.asList(root.retrieve().to("/user/keys", GHKey[].class)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the read-only list of all the public verified keys of the current user.
|
||||||
|
*
|
||||||
|
* Differently from the getPublicKeys() method, the retrieval of the user's
|
||||||
|
* verified public keys does not require any READ/WRITE OAuth Scope to the
|
||||||
|
* user's profile.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* Always non-null.
|
||||||
|
*/
|
||||||
|
public List<GHVerifiedKey> getPublicVerifiedKeys() throws IOException {
|
||||||
|
return Collections.unmodifiableList(Arrays.asList(root.retrieve().to(
|
||||||
|
"/users/" + getLogin() + "/keys", GHVerifiedKey[].class)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the organization that this user belongs to.
|
||||||
|
*/
|
||||||
|
public GHPersonSet<GHOrganization> getAllOrganizations() throws IOException {
|
||||||
|
GHPersonSet<GHOrganization> orgs = new GHPersonSet<GHOrganization>();
|
||||||
|
Set<String> names = new HashSet<String>();
|
||||||
|
for (GHOrganization o : root.retrieve().to("/user/orgs", GHOrganization[].class)) {
|
||||||
|
if (names.add(o.getLogin())) // in case of rumoured duplicates in the data
|
||||||
|
orgs.add(root.getOrganization(o.getLogin()));
|
||||||
|
}
|
||||||
|
return orgs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the all repositories this user owns (public and private).
|
||||||
|
*/
|
||||||
|
public synchronized Map<String,GHRepository> getAllRepositories() throws IOException {
|
||||||
|
Map<String,GHRepository> repositories = new TreeMap<String, GHRepository>();
|
||||||
|
for (GHRepository r : listAllRepositories()) {
|
||||||
|
repositories.put(r.getName(),r);
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableMap(repositories);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists up all repositories this user owns (public and private).
|
||||||
|
*
|
||||||
|
* Unlike {@link #getAllRepositories()}, this does not wait until all the repositories are returned.
|
||||||
|
* Repositories are returned by GitHub API with a 30 items per page.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public PagedIterable<GHRepository> listRepositories() {
|
||||||
|
return listRepositories(30);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List repositories that are accessible to the authenticated user (public and private) using the specified page size.
|
||||||
|
*
|
||||||
|
* This includes repositories owned by the authenticated user, repositories that belong to other users
|
||||||
|
* where the authenticated user is a collaborator, and other organizations' repositories that the authenticated
|
||||||
|
* user has access to through an organization membership.
|
||||||
|
*
|
||||||
|
* @param pageSize size for each page of items returned by GitHub. Maximum page size is 100.
|
||||||
|
*
|
||||||
|
* Unlike {@link #getRepositories()}, this does not wait until all the repositories are returned.
|
||||||
|
*/
|
||||||
|
public PagedIterable<GHRepository> listRepositories(final int pageSize) {
|
||||||
|
return listRepositories(pageSize, RepositoryListFilter.ALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List repositories of a certain type that are accessible by current authenticated user using the specified page size.
|
||||||
|
*
|
||||||
|
* @param pageSize size for each page of items returned by GitHub. Maximum page size is 100.
|
||||||
|
* @param repoType type of repository returned in the listing
|
||||||
|
*/
|
||||||
|
public PagedIterable<GHRepository> listRepositories(final int pageSize, final RepositoryListFilter repoType) {
|
||||||
|
return new PagedIterable<GHRepository>() {
|
||||||
|
public PagedIterator<GHRepository> _iterator(int pageSize) {
|
||||||
|
return new PagedIterator<GHRepository>(root.retrieve().with("type",repoType).asIterator("/user/repos", GHRepository[].class, pageSize)) {
|
||||||
|
@Override
|
||||||
|
protected void wrapUp(GHRepository[] page) {
|
||||||
|
for (GHRepository c : page)
|
||||||
|
c.wrap(root);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}.withPageSize(pageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
* Use {@link #listRepositories()}
|
||||||
|
*/
|
||||||
|
public PagedIterable<GHRepository> listAllRepositories() {
|
||||||
|
return listRepositories();
|
||||||
|
}
|
||||||
|
|
||||||
// public void addEmails(Collection<String> emails) throws IOException {
|
// public void addEmails(Collection<String> emails) throws IOException {
|
||||||
//// new Requester(root,ApiVersion.V3).withCredential().to("/user/emails");
|
//// new Requester(root,ApiVersion.V3).withCredential().to("/user/emails");
|
||||||
// root.retrieveWithAuth3()
|
// root.retrieveWithAuth3()
|
||||||
|
|||||||
208
src/main/java/org/kohsuke/github/GHNotificationStream.java
Normal file
208
src/main/java/org/kohsuke/github/GHNotificationStream.java
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listens to GitHub notification stream.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This class supports two modes of retrieving notifications that can
|
||||||
|
* be controlled via {@link #nonBlocking(boolean)}.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* In the blocking mode, which is the default, iterator will be infinite.
|
||||||
|
* The call to {@link Iterator#next()} will block until a new notification
|
||||||
|
* arrives. This is useful for application that runs perpetually and reacts
|
||||||
|
* to notifications.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* In the non-blocking mode, the iterator will only report the set of
|
||||||
|
* notifications initially retrieved from GitHub, then quit. This is useful
|
||||||
|
* for a batch application to process the current set of notifications.
|
||||||
|
*
|
||||||
|
* @author Kohsuke Kawaguchi
|
||||||
|
* @see GitHub#listNotifications()
|
||||||
|
* @see GHRepository#listNotifications()
|
||||||
|
*/
|
||||||
|
public class GHNotificationStream implements Iterable<GHThread> {
|
||||||
|
private final GitHub root;
|
||||||
|
|
||||||
|
private Boolean all, participating;
|
||||||
|
private String since;
|
||||||
|
private String apiUrl;
|
||||||
|
private boolean nonBlocking = false;
|
||||||
|
|
||||||
|
/*package*/ GHNotificationStream(GitHub root, String apiUrl) {
|
||||||
|
this.root = root;
|
||||||
|
this.apiUrl = apiUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should the stream include notifications that are already read?
|
||||||
|
*/
|
||||||
|
public GHNotificationStream read(boolean v) {
|
||||||
|
all = v;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should the stream be restricted to notifications in which the user
|
||||||
|
* is directly participating or mentioned?
|
||||||
|
*/
|
||||||
|
public GHNotificationStream participating(boolean v) {
|
||||||
|
participating = v;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHNotificationStream since(long timestamp) {
|
||||||
|
return since(new Date(timestamp));
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHNotificationStream since(Date dt) {
|
||||||
|
since = GitHub.printDate(dt);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If set to true, {@link #iterator()} will stop iterating instead of blocking and
|
||||||
|
* waiting for the updates to arrive.
|
||||||
|
*/
|
||||||
|
public GHNotificationStream nonBlocking(boolean v) {
|
||||||
|
this.nonBlocking = v;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an infinite blocking {@link Iterator} that returns
|
||||||
|
* {@link GHThread} as notifications arrive.
|
||||||
|
*/
|
||||||
|
public Iterator<GHThread> iterator() {
|
||||||
|
// capture the configuration setting here
|
||||||
|
final Requester req = new Requester(root).method("GET")
|
||||||
|
.with("all", all).with("participating", participating).with("since", since);
|
||||||
|
|
||||||
|
return new Iterator<GHThread>() {
|
||||||
|
/**
|
||||||
|
* Stuff we've fetched but haven't returned to the caller.
|
||||||
|
* Newer ones first.
|
||||||
|
*/
|
||||||
|
private GHThread[] threads = EMPTY_ARRAY;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Next element in {@link #threads} to return. This counts down.
|
||||||
|
*/
|
||||||
|
private int idx=-1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* threads whose updated_at is older than this should be ignored.
|
||||||
|
*/
|
||||||
|
private long lastUpdated = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Next request should have "If-Modified-Since" header with this value.
|
||||||
|
*/
|
||||||
|
private String lastModified;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When is the next polling allowed?
|
||||||
|
*/
|
||||||
|
private long nextCheckTime = -1;
|
||||||
|
|
||||||
|
private GHThread next;
|
||||||
|
|
||||||
|
public GHThread next() {
|
||||||
|
if (next==null) {
|
||||||
|
next = fetch();
|
||||||
|
if (next==null)
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
}
|
||||||
|
|
||||||
|
GHThread r = next;
|
||||||
|
next = null;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasNext() {
|
||||||
|
if (next==null)
|
||||||
|
next = fetch();
|
||||||
|
return next!=null;
|
||||||
|
}
|
||||||
|
|
||||||
|
GHThread fetch() {
|
||||||
|
try {
|
||||||
|
while (true) {// loop until we get new threads to return
|
||||||
|
|
||||||
|
// if we have fetched un-returned threads, use them first
|
||||||
|
while (idx>=0) {
|
||||||
|
GHThread n = threads[idx--];
|
||||||
|
long nt = n.getUpdatedAt().getTime();
|
||||||
|
if (nt >= lastUpdated) {
|
||||||
|
lastUpdated = nt;
|
||||||
|
return n.wrap(root);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nonBlocking && nextCheckTime>=0)
|
||||||
|
return null; // nothing more to report, and we aren't blocking
|
||||||
|
|
||||||
|
// observe the polling interval before making the call
|
||||||
|
while (true) {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
if (nextCheckTime < now) break;
|
||||||
|
long waitTime = Math.min(Math.max(nextCheckTime - now, 1000), 60 * 1000);
|
||||||
|
Thread.sleep(waitTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
req.setHeader("If-Modified-Since", lastModified);
|
||||||
|
|
||||||
|
threads = req.to(apiUrl, GHThread[].class);
|
||||||
|
if (threads==null) {
|
||||||
|
threads = EMPTY_ARRAY; // if unmodified, we get empty array
|
||||||
|
} else {
|
||||||
|
// we get a new batch, but we want to ignore the ones that we've seen
|
||||||
|
lastUpdated++;
|
||||||
|
}
|
||||||
|
idx = threads.length-1;
|
||||||
|
|
||||||
|
nextCheckTime = calcNextCheckTime();
|
||||||
|
lastModified = req.getResponseHeader("Last-Modified");
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private long calcNextCheckTime() {
|
||||||
|
String v = req.getResponseHeader("X-Poll-Interval");
|
||||||
|
if (v==null) v="60";
|
||||||
|
long seconds = Integer.parseInt(v);
|
||||||
|
return System.currentTimeMillis() + seconds*1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void markAsRead() throws IOException {
|
||||||
|
markAsRead(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks all the notifications as read.
|
||||||
|
*/
|
||||||
|
public void markAsRead(long timestamp) throws IOException {
|
||||||
|
final Requester req = new Requester(root).method("PUT");
|
||||||
|
if (timestamp>=0)
|
||||||
|
req.with("last_read_at", GitHub.printDate(new Date(timestamp)));
|
||||||
|
req.asHttpStatusCode(apiUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final GHThread[] EMPTY_ARRAY = new GHThread[0];
|
||||||
|
}
|
||||||
115
src/main/java/org/kohsuke/github/GHObject.java
Normal file
115
src/main/java/org/kohsuke/github/GHObject.java
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
|
||||||
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||||
|
import org.apache.commons.lang.builder.ReflectionToStringBuilder;
|
||||||
|
import org.apache.commons.lang.builder.ToStringBuilder;
|
||||||
|
import org.apache.commons.lang.builder.ToStringStyle;
|
||||||
|
import org.apache.commons.lang.reflect.FieldUtils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Most (all?) domain objects in GitHub seems to have these 4 properties.
|
||||||
|
*/
|
||||||
|
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
|
||||||
|
"NP_UNWRITTEN_FIELD"}, justification = "JSON API")
|
||||||
|
public abstract class GHObject {
|
||||||
|
protected String url;
|
||||||
|
protected int id;
|
||||||
|
protected String created_at;
|
||||||
|
protected String updated_at;
|
||||||
|
|
||||||
|
/*package*/ GHObject() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When was this resource created?
|
||||||
|
*/
|
||||||
|
@WithBridgeMethods(value=String.class, adapterMethod="createdAtStr")
|
||||||
|
public Date getCreatedAt() throws IOException {
|
||||||
|
return GitHub.parseDate(created_at);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD", justification = "Bridge method of getCreatedAt")
|
||||||
|
private Object createdAtStr(Date id, Class type) {
|
||||||
|
return created_at;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* API URL of this object.
|
||||||
|
*/
|
||||||
|
@WithBridgeMethods(value=String.class, adapterMethod="urlToString")
|
||||||
|
public URL getUrl() {
|
||||||
|
return GitHub.parseURL(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* URL of this object for humans, which renders some HTML.
|
||||||
|
*/
|
||||||
|
@WithBridgeMethods(value=String.class, adapterMethod="urlToString")
|
||||||
|
public abstract URL getHtmlUrl() throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When was this resource last updated?
|
||||||
|
*/
|
||||||
|
public Date getUpdatedAt() throws IOException {
|
||||||
|
return GitHub.parseDate(updated_at);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unique ID number of this resource.
|
||||||
|
*/
|
||||||
|
@WithBridgeMethods(value=String.class, adapterMethod="intToString")
|
||||||
|
public int getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD", justification = "Bridge method of getId")
|
||||||
|
private Object intToString(int id, Class type) {
|
||||||
|
return String.valueOf(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD", justification = "Bridge method of getHtmlUrl")
|
||||||
|
private Object urlToString(URL url, Class type) {
|
||||||
|
return url==null ? null : url.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* String representation to assist debugging and inspection. The output format of this string
|
||||||
|
* is not a committed part of the API and is subject to change.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return new ReflectionToStringBuilder(this, TOSTRING_STYLE, null, null, false, false) {
|
||||||
|
@Override
|
||||||
|
protected boolean accept(Field field) {
|
||||||
|
return super.accept(field) && !field.isAnnotationPresent(SkipFromToString.class);
|
||||||
|
}
|
||||||
|
}.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final ToStringStyle TOSTRING_STYLE = new ToStringStyle() {
|
||||||
|
{
|
||||||
|
this.setUseShortClassName(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void append(StringBuffer buffer, String fieldName, Object value, Boolean fullDetail) {
|
||||||
|
// skip unimportant properties. '_' is a heuristics as important properties tend to have short names
|
||||||
|
if (fieldName.contains("_"))
|
||||||
|
return;
|
||||||
|
// avoid recursing other GHObject
|
||||||
|
if (value instanceof GHObject)
|
||||||
|
return;
|
||||||
|
// likewise no point in showing root
|
||||||
|
if (value instanceof GitHub)
|
||||||
|
return;
|
||||||
|
|
||||||
|
super.append(buffer,fieldName,value,fullDetail);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
27
src/main/java/org/kohsuke/github/GHOrgHook.java
Normal file
27
src/main/java/org/kohsuke/github/GHOrgHook.java
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* © Copyright 2015 - SourceClear Inc
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
class GHOrgHook extends GHHook {
|
||||||
|
/**
|
||||||
|
* Organization that the hook belongs to.
|
||||||
|
*/
|
||||||
|
/*package*/ transient GHOrganization organization;
|
||||||
|
|
||||||
|
/*package*/ GHOrgHook wrap(GHOrganization owner) {
|
||||||
|
this.organization = owner;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
GitHub getRoot() {
|
||||||
|
return organization.root;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String getApiRoute() {
|
||||||
|
return String.format("/orgs/%s/hooks/%d", organization.getLogin(), id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
package org.kohsuke.github;
|
package org.kohsuke.github;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.AbstractList;
|
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.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
@@ -22,30 +23,87 @@ public class GHOrganization extends GHPerson {
|
|||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
* Newly created repository.
|
* Newly created repository.
|
||||||
|
* @deprecated
|
||||||
|
* Use {@link #createRepository(String)} that uses a builder pattern to let you control every aspect.
|
||||||
*/
|
*/
|
||||||
public GHRepository createRepository(String name, String description, String homepage, String team, boolean isPublic) throws IOException {
|
public GHRepository createRepository(String name, String description, String homepage, String team, boolean isPublic) throws IOException {
|
||||||
return createRepository(name,description,homepage,getTeams().get(team),isPublic);
|
GHTeam t = getTeams().get(team);
|
||||||
|
if (t==null)
|
||||||
|
throw new IllegalArgumentException("No such team: "+team);
|
||||||
|
return createRepository(name, description, homepage, t, isPublic);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
* Use {@link #createRepository(String)} that uses a builder pattern to let you control every aspect.
|
||||||
|
*/
|
||||||
public GHRepository createRepository(String name, String description, String homepage, GHTeam team, boolean isPublic) throws IOException {
|
public GHRepository createRepository(String name, String description, String homepage, GHTeam team, boolean isPublic) throws IOException {
|
||||||
// such API doesn't exist, so fall back to HTML scraping
|
if (team==null)
|
||||||
return new Requester(root)
|
throw new IllegalArgumentException("Invalid team");
|
||||||
.with("name", name).with("description", description).with("homepage", homepage)
|
return createRepository(name).description(description).homepage(homepage).private_(!isPublic).team(team).create();
|
||||||
.with("public", isPublic).with("team_id",team.getId()).to("/orgs/"+login+"/repos", GHRepository.class).wrap(root);
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts a builder that creates a new repository.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* You use the returned builder to set various properties, then call {@link GHCreateRepositoryBuilder#create()}
|
||||||
|
* to finally createa repository.
|
||||||
|
*/
|
||||||
|
public GHCreateRepositoryBuilder createRepository(String name) throws IOException {
|
||||||
|
return new GHCreateRepositoryBuilder(root,"/orgs/"+login+"/repos",name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Teams by their names.
|
* Teams by their names.
|
||||||
*/
|
*/
|
||||||
public Map<String,GHTeam> getTeams() throws IOException {
|
public Map<String,GHTeam> getTeams() throws IOException {
|
||||||
GHTeam[] teams = root.retrieve().to("/orgs/" + login + "/teams", GHTeam[].class);
|
|
||||||
Map<String,GHTeam> r = new TreeMap<String, GHTeam>();
|
Map<String,GHTeam> r = new TreeMap<String, GHTeam>();
|
||||||
for (GHTeam t : teams) {
|
for (GHTeam t : listTeams()) {
|
||||||
r.put(t.getName(),t.wrapUp(this));
|
r.put(t.getName(),t);
|
||||||
}
|
}
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List up all the teams.
|
||||||
|
*/
|
||||||
|
public PagedIterable<GHTeam> listTeams() throws IOException {
|
||||||
|
return new PagedIterable<GHTeam>() {
|
||||||
|
public PagedIterator<GHTeam> _iterator(int pageSize) {
|
||||||
|
return new PagedIterator<GHTeam>(root.retrieve().asIterator(String.format("/orgs/%s/teams", login), GHTeam[].class, pageSize)) {
|
||||||
|
@Override
|
||||||
|
protected void wrapUp(GHTeam[] page) {
|
||||||
|
for (GHTeam c : page)
|
||||||
|
c.wrapUp(GHOrganization.this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds a team that has the given name in its {@link GHTeam#getName()}
|
||||||
|
*/
|
||||||
|
public GHTeam getTeamByName(String name) throws IOException {
|
||||||
|
for (GHTeam t : listTeams()) {
|
||||||
|
if(t.getName().equals(name))
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds a team that has the given slug in its {@link GHTeam#getSlug()}
|
||||||
|
*/
|
||||||
|
public GHTeam getTeamBySlug(String slug) throws IOException {
|
||||||
|
for (GHTeam t : listTeams()) {
|
||||||
|
if(t.getSlug().equals(slug))
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if this organization has the specified user as a member.
|
* Checks if this organization has the specified user as a member.
|
||||||
*/
|
*/
|
||||||
@@ -58,6 +116,14 @@ public class GHOrganization extends GHPerson {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a member of the organisation - which will remove them from
|
||||||
|
* all teams, and remove their access to the organization’s repositories.
|
||||||
|
*/
|
||||||
|
public void remove(GHUser user) throws IOException {
|
||||||
|
root.retrieve().method("DELETE").to("/orgs/" + login + "/members/" + user.getLogin());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if this organization has the specified user as a public member.
|
* Checks if this organization has the specified user as a public member.
|
||||||
*/
|
*/
|
||||||
@@ -78,26 +144,44 @@ public class GHOrganization extends GHPerson {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All the members of this organization.
|
* @deprecated use {@link #listMembers()}
|
||||||
*/
|
*/
|
||||||
public List<GHUser> getMembers() throws IOException {
|
public List<GHUser> getMembers() throws IOException {
|
||||||
return new AbstractList<GHUser>() {
|
return listMembers().asList();
|
||||||
// these are shallow objects with only some limited values filled out
|
}
|
||||||
// TODO: it's better to allow objects to fill themselves in later when missing values are requested
|
|
||||||
final GHUser[] shallow = root.retrieve().to("/orgs/" + login + "/members", GHUser[].class);
|
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public GHUser get(int index) {
|
* All the members of this organization.
|
||||||
try {
|
*/
|
||||||
return root.getUser(shallow[index].getLogin());
|
public PagedIterable<GHUser> listMembers() throws IOException {
|
||||||
} catch (IOException e) {
|
return listMembers("members");
|
||||||
throw new RuntimeException(e);
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public int size() {
|
* All the public members of this organization.
|
||||||
return shallow.length;
|
*/
|
||||||
|
public PagedIterable<GHUser> listPublicMembers() throws IOException {
|
||||||
|
return listMembers("public_members");
|
||||||
|
}
|
||||||
|
|
||||||
|
private PagedIterable<GHUser> listMembers(String suffix) throws IOException {
|
||||||
|
return listMembers(suffix, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PagedIterable<GHUser> listMembersWithFilter(String filter) throws IOException {
|
||||||
|
return listMembers("members", filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PagedIterable<GHUser> listMembers(final String suffix, final String filter) throws IOException {
|
||||||
|
return new PagedIterable<GHUser>() {
|
||||||
|
public PagedIterator<GHUser> _iterator(int pageSize) {
|
||||||
|
String filterParams = (filter == null) ? "" : ("?filter=" + filter);
|
||||||
|
return new PagedIterator<GHUser>(root.retrieve().asIterator(String.format("/orgs/%s/%s%s", login, suffix, filterParams), GHUser[].class, pageSize)) {
|
||||||
|
@Override
|
||||||
|
protected void wrapUp(GHUser[] users) {
|
||||||
|
GHUser.wrap(users, root);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -115,7 +199,7 @@ public class GHOrganization extends GHPerson {
|
|||||||
* Creates a new team and assigns the repositories.
|
* Creates a new team and assigns the repositories.
|
||||||
*/
|
*/
|
||||||
public GHTeam createTeam(String name, Permission p, Collection<GHRepository> repositories) throws IOException {
|
public GHTeam createTeam(String name, Permission p, Collection<GHRepository> repositories) throws IOException {
|
||||||
Requester post = new Requester(root).with("name", name).with("permission", p.name().toLowerCase());
|
Requester post = new Requester(root).with("name", name).with("permission", p);
|
||||||
List<String> repo_names = new ArrayList<String>();
|
List<String> repo_names = new ArrayList<String>();
|
||||||
for (GHRepository r : repositories) {
|
for (GHRepository r : repositories) {
|
||||||
repo_names.add(r.getName());
|
repo_names.add(r.getName());
|
||||||
@@ -125,15 +209,19 @@ public class GHOrganization extends GHPerson {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public GHTeam createTeam(String name, Permission p, GHRepository... repositories) throws IOException {
|
public GHTeam createTeam(String name, Permission p, GHRepository... repositories) throws IOException {
|
||||||
return createTeam(name,p, Arrays.asList(repositories));
|
return createTeam(name, p, Arrays.asList(repositories));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List up repositories that has some open pull requests.
|
* List up repositories that has some open pull requests.
|
||||||
|
*
|
||||||
|
* This used to be an efficient method that didn't involve traversing every repository, but now
|
||||||
|
* it doesn't do any optimization.
|
||||||
*/
|
*/
|
||||||
public List<GHRepository> getRepositoriesWithOpenPullRequests() throws IOException {
|
public List<GHRepository> getRepositoriesWithOpenPullRequests() throws IOException {
|
||||||
List<GHRepository> r = new ArrayList<GHRepository>();
|
List<GHRepository> r = new ArrayList<GHRepository>();
|
||||||
for (GHRepository repository : root.retrieve().to("/orgs/" + login + "/repos", GHRepository[].class)) {
|
for (GHRepository repository : listRepositories()) {
|
||||||
|
repository.wrap(root);
|
||||||
List<GHPullRequest> pullRequests = repository.getPullRequests(GHIssueState.OPEN);
|
List<GHPullRequest> pullRequests = repository.getPullRequests(GHIssueState.OPEN);
|
||||||
if (pullRequests.size() > 0) {
|
if (pullRequests.size() > 0) {
|
||||||
r.add(repository);
|
r.add(repository);
|
||||||
@@ -152,4 +240,78 @@ public class GHOrganization extends GHPerson {
|
|||||||
}
|
}
|
||||||
return all;
|
return all;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists events performed by a user (this includes private events if the caller is authenticated.
|
||||||
|
*/
|
||||||
|
public PagedIterable<GHEventInfo> listEvents() throws IOException {
|
||||||
|
return new PagedIterable<GHEventInfo>() {
|
||||||
|
public PagedIterator<GHEventInfo> _iterator(int pageSize) {
|
||||||
|
return new PagedIterator<GHEventInfo>(root.retrieve().asIterator(String.format("/orgs/%s/events", login), GHEventInfo[].class, pageSize)) {
|
||||||
|
@Override
|
||||||
|
protected void wrapUp(GHEventInfo[] page) {
|
||||||
|
for (GHEventInfo c : page)
|
||||||
|
c.wrapUp(root);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists up all the repositories using the specified page size.
|
||||||
|
*
|
||||||
|
* @param pageSize size for each page of items returned by GitHub. Maximum page size is 100.
|
||||||
|
*
|
||||||
|
* Unlike {@link #getRepositories()}, this does not wait until all the repositories are returned.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public PagedIterable<GHRepository> listRepositories(final int pageSize) {
|
||||||
|
return new PagedIterable<GHRepository>() {
|
||||||
|
public PagedIterator<GHRepository> _iterator(int pageSize) {
|
||||||
|
return new PagedIterator<GHRepository>(root.retrieve().asIterator("/orgs/" + login + "/repos?per_page=" + pageSize, GHRepository[].class, pageSize)) {
|
||||||
|
@Override
|
||||||
|
protected void wrapUp(GHRepository[] page) {
|
||||||
|
for (GHRepository c : page)
|
||||||
|
c.wrap(root);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the currently configured hooks.
|
||||||
|
*/
|
||||||
|
public List<GHHook> getHooks() throws IOException {
|
||||||
|
return GHHooks.orgContext(this).getHooks();
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHHook getHook(int id) throws IOException {
|
||||||
|
return GHHooks.orgContext(this).getHook(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* See https://api.github.com/hooks for possible names and their configuration scheme.
|
||||||
|
* TODO: produce type-safe binding
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* Type of the hook to be created. See https://api.github.com/hooks for possible names.
|
||||||
|
* @param config
|
||||||
|
* The configuration hash.
|
||||||
|
* @param events
|
||||||
|
* Can be null. Types of events to hook into.
|
||||||
|
*/
|
||||||
|
public GHHook createHook(String name, Map<String,String> config, Collection<GHEvent> events, boolean active) throws IOException {
|
||||||
|
return GHHooks.orgContext(this).createHook(name, config, events, active);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHHook createWebHook(URL url, Collection<GHEvent> events) throws IOException {
|
||||||
|
return createHook("web", Collections.singletonMap("url", url.toExternalForm()),events,true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHHook createWebHook(URL url) throws IOException {
|
||||||
|
return createWebHook(url, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ package org.kohsuke.github;
|
|||||||
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
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;
|
||||||
@@ -14,15 +16,14 @@ import java.util.TreeMap;
|
|||||||
*
|
*
|
||||||
* @author Kohsuke Kawaguchi
|
* @author Kohsuke Kawaguchi
|
||||||
*/
|
*/
|
||||||
public abstract class GHPerson {
|
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, url, gravatar_id;
|
protected String login, avatar_url, gravatar_id;
|
||||||
protected int id;
|
|
||||||
|
|
||||||
// other fields (that only show up in full data)
|
// other fields (that only show up in full data)
|
||||||
protected String location,blog,email,name,created_at,company;
|
protected String location,blog,email,name,company;
|
||||||
protected String html_url;
|
protected String html_url;
|
||||||
protected int followers,following,public_repos,public_gists;
|
protected int followers,following,public_repos,public_gists;
|
||||||
|
|
||||||
@@ -36,24 +37,57 @@ public abstract class GHPerson {
|
|||||||
*
|
*
|
||||||
* Depending on the original API call where this object is created, it may not contain everything.
|
* Depending on the original API call where this object is created, it may not contain everything.
|
||||||
*/
|
*/
|
||||||
protected void populate() throws IOException {
|
protected synchronized void populate() throws IOException {
|
||||||
if (created_at!=null) return; // already populated
|
if (created_at!=null) return; // already populated
|
||||||
|
|
||||||
root.retrieve().to(url, this);
|
root.retrieve().to(url, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the repositories this user owns.
|
* Gets the public repositories this user owns.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* To list your own repositories, including private repositories,
|
||||||
|
* use {@link GHMyself#listRepositories()}
|
||||||
*/
|
*/
|
||||||
public synchronized Map<String,GHRepository> getRepositories() throws IOException {
|
public synchronized Map<String,GHRepository> getRepositories() throws IOException {
|
||||||
Map<String,GHRepository> repositories = new TreeMap<String, GHRepository>();
|
Map<String,GHRepository> repositories = new TreeMap<String, GHRepository>();
|
||||||
for (List<GHRepository> batch : iterateRepositories(100)) {
|
for (GHRepository r : listRepositories()) {
|
||||||
for (GHRepository r : batch)
|
repositories.put(r.getName(),r);
|
||||||
repositories.put(r.getName(),r);
|
|
||||||
}
|
}
|
||||||
return Collections.unmodifiableMap(repositories);
|
return Collections.unmodifiableMap(repositories);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists up all the repositories using a 30 items page size.
|
||||||
|
*
|
||||||
|
* Unlike {@link #getRepositories()}, this does not wait until all the repositories are returned.
|
||||||
|
*/
|
||||||
|
public PagedIterable<GHRepository> listRepositories() {
|
||||||
|
return listRepositories(30);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists up all the repositories using the specified page size.
|
||||||
|
*
|
||||||
|
* @param pageSize size for each page of items returned by GitHub. Maximum page size is 100.
|
||||||
|
*
|
||||||
|
* Unlike {@link #getRepositories()}, this does not wait until all the repositories are returned.
|
||||||
|
*/
|
||||||
|
public PagedIterable<GHRepository> listRepositories(final int pageSize) {
|
||||||
|
return new PagedIterable<GHRepository>() {
|
||||||
|
public PagedIterator<GHRepository> _iterator(int pageSize) {
|
||||||
|
return new PagedIterator<GHRepository>(root.retrieve().asIterator("/users/" + login + "/repos?per_page=" + pageSize, GHRepository[].class, pageSize)) {
|
||||||
|
@Override
|
||||||
|
protected void wrapUp(GHRepository[] page) {
|
||||||
|
for (GHRepository c : page)
|
||||||
|
c.wrap(root);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads repository list in a pagenated fashion.
|
* Loads repository list in a pagenated fashion.
|
||||||
*
|
*
|
||||||
@@ -63,11 +97,14 @@ public abstract class GHPerson {
|
|||||||
*
|
*
|
||||||
* Every {@link Iterator#next()} call results in I/O. Exceptions that occur during the processing is wrapped
|
* Every {@link Iterator#next()} call results in I/O. Exceptions that occur during the processing is wrapped
|
||||||
* into {@link Error}.
|
* into {@link Error}.
|
||||||
|
*
|
||||||
|
* @deprecated
|
||||||
|
* Use {@link #listRepositories()}
|
||||||
*/
|
*/
|
||||||
public synchronized Iterable<List<GHRepository>> iterateRepositories(final int pageSize) {
|
public synchronized Iterable<List<GHRepository>> iterateRepositories(final int pageSize) {
|
||||||
return new Iterable<List<GHRepository>>() {
|
return new Iterable<List<GHRepository>>() {
|
||||||
public Iterator<List<GHRepository>> iterator() {
|
public Iterator<List<GHRepository>> iterator() {
|
||||||
final Iterator<GHRepository[]> pager = root.retrieve().asIterator("/users/" + login + "/repos?per_page="+pageSize,GHRepository[].class);
|
final Iterator<GHRepository[]> pager = root.retrieve().asIterator("/users/" + login + "/repos?per_page="+pageSize,GHRepository[].class, pageSize);
|
||||||
|
|
||||||
return new Iterator<List<GHRepository>>() {
|
return new Iterator<List<GHRepository>>() {
|
||||||
public boolean hasNext() {
|
public boolean hasNext() {
|
||||||
@@ -102,6 +139,11 @@ public abstract class GHPerson {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists events for an organization or an user.
|
||||||
|
*/
|
||||||
|
public abstract PagedIterable<GHEventInfo> listEvents() throws IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gravatar ID of this user, like 0cb9832a01c22c083390f3c5dcb64105
|
* Gravatar ID of this user, like 0cb9832a01c22c083390f3c5dcb64105
|
||||||
*
|
*
|
||||||
@@ -155,9 +197,14 @@ public abstract class GHPerson {
|
|||||||
return location;
|
return location;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getCreatedAt() throws IOException {
|
public Date getCreatedAt() throws IOException {
|
||||||
populate();
|
populate();
|
||||||
return created_at;
|
return super.getCreatedAt();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getUpdatedAt() throws IOException {
|
||||||
|
populate();
|
||||||
|
return super.getUpdatedAt();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -168,6 +215,11 @@ public abstract class GHPerson {
|
|||||||
return blog;
|
return blog;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public URL getHtmlUrl() {
|
||||||
|
return GitHub.parseURL(html_url);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the e-mail address of the user.
|
* Gets the e-mail address of the user.
|
||||||
*/
|
*/
|
||||||
@@ -191,16 +243,8 @@ public abstract class GHPerson {
|
|||||||
return following;
|
return following;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* What appears to be a GitHub internal unique number that identifies this user.
|
|
||||||
*/
|
|
||||||
public int getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getFollowersCount() throws IOException {
|
public int getFollowersCount() throws IOException {
|
||||||
populate();
|
populate();
|
||||||
return followers;
|
return followers;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,9 @@ import java.util.HashSet;
|
|||||||
*
|
*
|
||||||
* @author Kohsuke Kawaguchi
|
* @author Kohsuke Kawaguchi
|
||||||
*/
|
*/
|
||||||
public final class GHPersonSet<T extends GHPerson> extends HashSet<T> {
|
public class GHPersonSet<T extends GHPerson> extends HashSet<T> {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
public GHPersonSet() {
|
public GHPersonSet() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,38 +32,52 @@ import java.util.Date;
|
|||||||
* A pull request.
|
* A pull request.
|
||||||
*
|
*
|
||||||
* @author Kohsuke Kawaguchi
|
* @author Kohsuke Kawaguchi
|
||||||
|
* @see GHRepository#getPullRequest(int)
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings({"UnusedDeclaration"})
|
@SuppressWarnings({"UnusedDeclaration"})
|
||||||
public class GHPullRequest extends GHIssue {
|
public class GHPullRequest extends GHIssue {
|
||||||
|
|
||||||
private String patch_url, diff_url, issue_url;
|
private String patch_url, diff_url, issue_url;
|
||||||
private GHCommitPointer base;
|
private GHCommitPointer base;
|
||||||
private String merged_at;
|
private String merged_at;
|
||||||
private GHCommitPointer head;
|
private GHCommitPointer head;
|
||||||
|
|
||||||
// details that are only available when obtained from ID
|
// details that are only available when obtained from ID
|
||||||
private GHUser merged_by;
|
private GHUser merged_by;
|
||||||
private int review_comments, additions;
|
private int review_comments, additions;
|
||||||
private boolean merged;
|
private boolean merged;
|
||||||
private Boolean mergeable;
|
private Boolean mergeable;
|
||||||
private int deletions;
|
private int deletions;
|
||||||
private String mergeable_state;
|
private String mergeable_state;
|
||||||
private int changed_files;
|
private int changed_files;
|
||||||
|
private String merge_commit_sha;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GitHub doesn't return some properties of {@link GHIssue} when requesting the GET on the 'pulls' API
|
||||||
|
* 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
GHPullRequest wrapUp(GitHub root) {
|
GHPullRequest wrapUp(GitHub root) {
|
||||||
if (owner!=null) owner.wrap(root);
|
if (owner != null) owner.wrap(root);
|
||||||
if (base!=null) base.wrapUp(root);
|
if (base != null) base.wrapUp(root);
|
||||||
if (head!=null) head.wrapUp(root);
|
if (head != null) head.wrapUp(root);
|
||||||
if (merged_by != null) merged_by.wrapUp(root);
|
if (merged_by != null) merged_by.wrapUp(root);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getApiRoute() {
|
||||||
|
return "/repos/"+owner.getOwnerName()+"/"+owner.getName()+"/pulls/"+number;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The URL of the patch file.
|
* The URL of the patch file.
|
||||||
* like https://github.com/jenkinsci/jenkins/pull/100.patch
|
* like https://github.com/jenkinsci/jenkins/pull/100.patch
|
||||||
@@ -72,7 +86,7 @@ public class GHPullRequest extends GHIssue {
|
|||||||
return GitHub.parseURL(patch_url);
|
return GitHub.parseURL(patch_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The URL of the patch file.
|
* The URL of the patch file.
|
||||||
* like https://github.com/jenkinsci/jenkins/pull/100.patch
|
* like https://github.com/jenkinsci/jenkins/pull/100.patch
|
||||||
*/
|
*/
|
||||||
@@ -95,8 +109,8 @@ public class GHPullRequest extends GHIssue {
|
|||||||
return head;
|
return head;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public Date getIssueUpdatedAt() {
|
public Date getIssueUpdatedAt() throws IOException {
|
||||||
return super.getUpdatedAt();
|
return super.getUpdatedAt();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,64 +126,73 @@ public class GHPullRequest extends GHIssue {
|
|||||||
return GitHub.parseDate(merged_at);
|
return GitHub.parseDate(merged_at);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<String> getLabels() {
|
public Collection<GHLabel> getLabels() throws IOException {
|
||||||
return super.getLabels();
|
fetchIssue();
|
||||||
}
|
return super.getLabels();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GHUser getClosedBy() {
|
public GHUser getClosedBy() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PullRequest getPullRequest() {
|
public PullRequest getPullRequest() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// details that are only available via get with ID
|
||||||
|
//
|
||||||
|
|
||||||
//
|
|
||||||
// details that are only available via get with ID
|
|
||||||
//
|
|
||||||
//
|
|
||||||
public GHUser getMergedBy() throws IOException {
|
public GHUser getMergedBy() throws IOException {
|
||||||
populate();
|
populate();
|
||||||
return merged_by;
|
return merged_by;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getReviewComments() throws IOException {
|
public int getReviewComments() throws IOException {
|
||||||
populate();
|
populate();
|
||||||
return review_comments;
|
return review_comments;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getAdditions() throws IOException {
|
public int getAdditions() throws IOException {
|
||||||
populate();
|
populate();
|
||||||
return additions;
|
return additions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isMerged() throws IOException {
|
public boolean isMerged() throws IOException {
|
||||||
populate();
|
populate();
|
||||||
return merged;
|
return merged;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Boolean getMergeable() throws IOException {
|
public Boolean getMergeable() throws IOException {
|
||||||
populate();
|
populate();
|
||||||
return mergeable;
|
return mergeable;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getDeletions() throws IOException {
|
public int getDeletions() throws IOException {
|
||||||
populate();
|
populate();
|
||||||
return deletions;
|
return deletions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getMergeableState() throws IOException {
|
public String getMergeableState() throws IOException {
|
||||||
populate();
|
populate();
|
||||||
return mergeable_state;
|
return mergeable_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getChangedFiles() throws IOException {
|
public int getChangedFiles() throws IOException {
|
||||||
populate();
|
populate();
|
||||||
return changed_files;
|
return changed_files;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See <a href="https://developer.github.com/changes/2013-04-25-deprecating-merge-commit-sha">GitHub blog post</a>
|
||||||
|
*/
|
||||||
|
public String getMergeCommitSha() throws IOException {
|
||||||
|
populate();
|
||||||
|
return merge_commit_sha;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fully populate the data by retrieving missing data.
|
* Fully populate the data by retrieving missing data.
|
||||||
@@ -179,6 +202,100 @@ public class GHPullRequest extends GHIssue {
|
|||||||
private void populate() throws IOException {
|
private void populate() throws IOException {
|
||||||
if (merged_by!=null) return; // already populated
|
if (merged_by!=null) return; // already populated
|
||||||
|
|
||||||
root.retrieve().to(url, this);
|
root.retrieve().to(url, this).wrapUp(owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves all the commits associated to this pull request.
|
||||||
|
*/
|
||||||
|
public PagedIterable<GHPullRequestFileDetail> listFiles() {
|
||||||
|
return new PagedIterable<GHPullRequestFileDetail>() {
|
||||||
|
public PagedIterator<GHPullRequestFileDetail> _iterator(int pageSize) {
|
||||||
|
return new PagedIterator<GHPullRequestFileDetail>(root.retrieve().asIterator(String.format("%s/files", getApiURL()),
|
||||||
|
GHPullRequestFileDetail[].class, pageSize)) {
|
||||||
|
@Override
|
||||||
|
protected void wrapUp(GHPullRequestFileDetail[] page) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains all the review comments associated with this pull request.
|
||||||
|
*/
|
||||||
|
public PagedIterable<GHPullRequestReviewComment> listReviewComments() throws IOException {
|
||||||
|
return new PagedIterable<GHPullRequestReviewComment>() {
|
||||||
|
public PagedIterator<GHPullRequestReviewComment> _iterator(int pageSize) {
|
||||||
|
return new PagedIterator<GHPullRequestReviewComment>(root.retrieve().asIterator(getApiRoute() + "/comments",
|
||||||
|
GHPullRequestReviewComment[].class, pageSize)) {
|
||||||
|
protected void wrapUp(GHPullRequestReviewComment[] page) {
|
||||||
|
for (GHPullRequestReviewComment c : page)
|
||||||
|
c.wrapUp(GHPullRequest.this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves all the commits associated to this pull request.
|
||||||
|
*/
|
||||||
|
public PagedIterable<GHPullRequestCommitDetail> listCommits() {
|
||||||
|
return new PagedIterable<GHPullRequestCommitDetail>() {
|
||||||
|
public PagedIterator<GHPullRequestCommitDetail> _iterator(int pageSize) {
|
||||||
|
return new PagedIterator<GHPullRequestCommitDetail>(root.retrieve().asIterator(
|
||||||
|
String.format("%s/commits", getApiURL()),
|
||||||
|
GHPullRequestCommitDetail[].class, pageSize)) {
|
||||||
|
@Override
|
||||||
|
protected void wrapUp(GHPullRequestCommitDetail[] page) {
|
||||||
|
for (GHPullRequestCommitDetail c : page)
|
||||||
|
c.wrapUp(GHPullRequest.this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHPullRequestReviewComment createReviewComment(String body, String sha, String path, int position) throws IOException {
|
||||||
|
return new Requester(root).method("POST")
|
||||||
|
.with("body", body)
|
||||||
|
.with("commit_id", sha)
|
||||||
|
.with("path", path)
|
||||||
|
.with("position", position)
|
||||||
|
.to(getApiRoute() + "/comments", GHPullRequestReviewComment.class).wrapUp(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merge this pull request.
|
||||||
|
*
|
||||||
|
* The equivalent of the big green "Merge pull request" button.
|
||||||
|
*
|
||||||
|
* @param msg
|
||||||
|
* Commit message. If null, the default one will be used.
|
||||||
|
*/
|
||||||
|
public void merge(String msg) throws IOException {
|
||||||
|
merge(msg,null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merge this pull request.
|
||||||
|
*
|
||||||
|
* The equivalent of the big green "Merge pull request" button.
|
||||||
|
*
|
||||||
|
* @param msg
|
||||||
|
* Commit message. If null, the default one will be used.
|
||||||
|
* @param sha
|
||||||
|
* SHA that pull request head must match to allow merge.
|
||||||
|
*/
|
||||||
|
public void merge(String msg, String sha) throws IOException {
|
||||||
|
new Requester(root).method("PUT").with("commit_message",msg).with("sha",sha).to(getApiRoute()+"/merge");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fetchIssue() throws IOException {
|
||||||
|
if (!fetchedIssueDetails) {
|
||||||
|
new Requester(root).to(getIssuesApiRoute(), this);
|
||||||
|
fetchedIssueDetails = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
150
src/main/java/org/kohsuke/github/GHPullRequestCommitDetail.java
Normal file
150
src/main/java/org/kohsuke/github/GHPullRequestCommitDetail.java
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013, Luca Milanesio
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
|
||||||
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Commit detail inside a {@link GHPullRequest}.
|
||||||
|
*
|
||||||
|
* @author Luca Milanesio
|
||||||
|
* @see GHPullRequest#listCommits()
|
||||||
|
*/
|
||||||
|
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
|
||||||
|
"NP_UNWRITTEN_FIELD", "URF_UNREAD_FIELD"}, justification = "JSON API")
|
||||||
|
public class GHPullRequestCommitDetail {
|
||||||
|
private GHPullRequest owner;
|
||||||
|
|
||||||
|
/*package*/ void wrapUp(GHPullRequest owner) {
|
||||||
|
this.owner = owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use {@link GitUser}
|
||||||
|
*/
|
||||||
|
public static class Authorship extends GitUser {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Tree {
|
||||||
|
String sha;
|
||||||
|
String url;
|
||||||
|
|
||||||
|
public String getSha() {
|
||||||
|
return sha;
|
||||||
|
}
|
||||||
|
|
||||||
|
public URL getUrl() {
|
||||||
|
return GitHub.parseURL(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Commit {
|
||||||
|
Authorship author;
|
||||||
|
Authorship committer;
|
||||||
|
String message;
|
||||||
|
Tree tree;
|
||||||
|
String url;
|
||||||
|
int comment_count;
|
||||||
|
|
||||||
|
@WithBridgeMethods(value = Authorship.class, castRequired = true)
|
||||||
|
public GitUser getAuthor() {
|
||||||
|
return author;
|
||||||
|
}
|
||||||
|
|
||||||
|
@WithBridgeMethods(value = Authorship.class, castRequired = true)
|
||||||
|
public GitUser getCommitter() {
|
||||||
|
return committer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public URL getUrl() {
|
||||||
|
return GitHub.parseURL(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getComment_count() {
|
||||||
|
return comment_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Tree getTree() {
|
||||||
|
return tree;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class CommitPointer {
|
||||||
|
String sha;
|
||||||
|
String url;
|
||||||
|
String html_url;
|
||||||
|
|
||||||
|
public URL getUrl() {
|
||||||
|
return GitHub.parseURL(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public URL getHtml_url() {
|
||||||
|
return GitHub.parseURL(html_url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSha() {
|
||||||
|
return sha;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String sha;
|
||||||
|
Commit commit;
|
||||||
|
String url;
|
||||||
|
String html_url;
|
||||||
|
String comments_url;
|
||||||
|
CommitPointer[] parents;
|
||||||
|
|
||||||
|
public String getSha() {
|
||||||
|
return sha;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Commit getCommit() {
|
||||||
|
return commit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public URL getApiUrl() {
|
||||||
|
return GitHub.parseURL(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public URL getUrl() {
|
||||||
|
return GitHub.parseURL(html_url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public URL getCommentsUrl() {
|
||||||
|
return GitHub.parseURL(comments_url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommitPointer[] getParents() {
|
||||||
|
CommitPointer[] newValue = new CommitPointer[parents.length];
|
||||||
|
System.arraycopy(parents, 0, newValue, 0, parents.length);
|
||||||
|
return newValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2015, Julien Henry
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File detail inside a {@link GHPullRequest}.
|
||||||
|
*
|
||||||
|
* @author Julien Henry
|
||||||
|
* @see GHPullRequest#listFiles()
|
||||||
|
*/
|
||||||
|
public class GHPullRequestFileDetail {
|
||||||
|
|
||||||
|
String sha;
|
||||||
|
String filename;
|
||||||
|
String status;
|
||||||
|
int additions;
|
||||||
|
int deletions;
|
||||||
|
int changes;
|
||||||
|
String blob_url;
|
||||||
|
String raw_url;
|
||||||
|
String contents_url;
|
||||||
|
String patch;
|
||||||
|
|
||||||
|
public String getSha() {
|
||||||
|
return sha;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFilename() {
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAdditions() {
|
||||||
|
return additions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDeletions() {
|
||||||
|
return deletions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getChanges() {
|
||||||
|
return changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public URL getBlobUrl() {
|
||||||
|
return GitHub.parseURL(blob_url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public URL getRawUrl() {
|
||||||
|
return GitHub.parseURL(raw_url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public URL getContentsUrl() {
|
||||||
|
return GitHub.parseURL(contents_url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPatch() {
|
||||||
|
return patch;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists up pull requests with some filtering and sorting.
|
||||||
|
*
|
||||||
|
* @author Kohsuke Kawaguchi
|
||||||
|
* @see GHRepository#queryPullRequests()
|
||||||
|
*/
|
||||||
|
public class GHPullRequestQueryBuilder extends GHQueryBuilder<GHPullRequest> {
|
||||||
|
private final GHRepository repo;
|
||||||
|
|
||||||
|
/*package*/ GHPullRequestQueryBuilder(GHRepository repo) {
|
||||||
|
super(repo.root);
|
||||||
|
this.repo = repo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHPullRequestQueryBuilder state(GHIssueState state) {
|
||||||
|
req.with("state",state);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHPullRequestQueryBuilder head(String head) {
|
||||||
|
req.with("head",head);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHPullRequestQueryBuilder base(String base) {
|
||||||
|
req.with("base",base);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHPullRequestQueryBuilder sort(Sort sort) {
|
||||||
|
req.with("sort",sort);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Sort { CREATED, UPDATED, POPULARITY, LONG_RUNNING }
|
||||||
|
|
||||||
|
public GHPullRequestQueryBuilder direction(GHDirection d) {
|
||||||
|
req.with("direction",d);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PagedIterable<GHPullRequest> list() {
|
||||||
|
return new PagedIterable<GHPullRequest>() {
|
||||||
|
public PagedIterator<GHPullRequest> _iterator(int pageSize) {
|
||||||
|
return new PagedIterator<GHPullRequest>(req.asIterator(repo.getApiTailUrl("pulls"), GHPullRequest[].class, pageSize)) {
|
||||||
|
@Override
|
||||||
|
protected void wrapUp(GHPullRequest[] page) {
|
||||||
|
for (GHPullRequest pr : page)
|
||||||
|
pr.wrapUp(repo);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
106
src/main/java/org/kohsuke/github/GHPullRequestReviewComment.java
Normal file
106
src/main/java/org/kohsuke/github/GHPullRequestReviewComment.java
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
* 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 java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Review comment to the pull request
|
||||||
|
*
|
||||||
|
* @author Julien Henry
|
||||||
|
* @see GHPullRequest#listReviewComments()
|
||||||
|
* @see GHPullRequest#createReviewComment(String, String, String, int)
|
||||||
|
*/
|
||||||
|
public class GHPullRequestReviewComment extends GHObject {
|
||||||
|
GHPullRequest owner;
|
||||||
|
|
||||||
|
private String body;
|
||||||
|
private GHUser user;
|
||||||
|
private String path;
|
||||||
|
private int position;
|
||||||
|
private int originalPosition;
|
||||||
|
|
||||||
|
/*package*/ GHPullRequestReviewComment wrapUp(GHPullRequest owner) {
|
||||||
|
this.owner = owner;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the pull request to which this review comment is associated.
|
||||||
|
*/
|
||||||
|
public GHPullRequest getParent() {
|
||||||
|
return owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The comment itself.
|
||||||
|
*/
|
||||||
|
public String getBody() {
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the user who posted this comment.
|
||||||
|
*/
|
||||||
|
public GHUser getUser() throws IOException {
|
||||||
|
return owner.root.getUser(user.getLogin());
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPath() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPosition() {
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOriginalPosition() {
|
||||||
|
return originalPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public URL getHtmlUrl() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getApiRoute() {
|
||||||
|
return "/repos/"+owner.getRepository().getFullName()+"/pulls/comments/"+id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the comment.
|
||||||
|
*/
|
||||||
|
public void update(String body) throws IOException {
|
||||||
|
new Requester(owner.root).method("PATCH").with("body", body).to(getApiRoute(),this);
|
||||||
|
this.body = body;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes this review comment.
|
||||||
|
*/
|
||||||
|
public void delete() throws IOException {
|
||||||
|
new Requester(owner.root).method("DELETE").to(getApiRoute());
|
||||||
|
}
|
||||||
|
}
|
||||||
21
src/main/java/org/kohsuke/github/GHQueryBuilder.java
Normal file
21
src/main/java/org/kohsuke/github/GHQueryBuilder.java
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to specify filters, sort order, etc for listing items in a collection.
|
||||||
|
*
|
||||||
|
* @author Kohsuke Kawaguchi
|
||||||
|
*/
|
||||||
|
public abstract class GHQueryBuilder<T> {
|
||||||
|
protected final GitHub root;
|
||||||
|
protected final Requester req;
|
||||||
|
|
||||||
|
/*package*/ GHQueryBuilder(GitHub root) {
|
||||||
|
this.root = root;
|
||||||
|
this.req = root.retrieve();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start listing items by using the settings built up on this object.
|
||||||
|
*/
|
||||||
|
public abstract PagedIterable<T> list();
|
||||||
|
}
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
package org.kohsuke.github;
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rate limit.
|
* Rate limit.
|
||||||
* @author Kohsuke Kawaguchi
|
* @author Kohsuke Kawaguchi
|
||||||
@@ -10,12 +13,30 @@ public class GHRateLimit {
|
|||||||
*/
|
*/
|
||||||
public int remaining;
|
public int remaining;
|
||||||
/**
|
/**
|
||||||
* Alotted API call per hour.
|
* Allotted API call per hour.
|
||||||
*/
|
*/
|
||||||
public int limit;
|
public int limit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The time at which the current rate limit window resets in UTC epoch seconds.
|
||||||
|
*/
|
||||||
|
public Date reset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Non-epoch date
|
||||||
|
*/
|
||||||
|
@SuppressFBWarnings(value = "UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR",
|
||||||
|
justification = "The value comes from JSON deserialization")
|
||||||
|
public Date getResetDate() {
|
||||||
|
return new Date(reset.getTime() * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return remaining+"/"+limit;
|
return "GHRateLimit{" +
|
||||||
|
"remaining=" + remaining +
|
||||||
|
", limit=" + limit +
|
||||||
|
", resetDate=" + getResetDate() +
|
||||||
|
'}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package org.kohsuke.github;
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||||
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -8,6 +10,7 @@ import java.net.URL;
|
|||||||
* @author Michael Clarke
|
* @author Michael Clarke
|
||||||
*/
|
*/
|
||||||
public class GHRef {
|
public class GHRef {
|
||||||
|
/*package almost final*/ GitHub root;
|
||||||
|
|
||||||
private String ref, url;
|
private String ref, url;
|
||||||
private GHObject object;
|
private GHObject object;
|
||||||
@@ -33,7 +36,50 @@ public class GHRef {
|
|||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates this ref to the specified commit.
|
||||||
|
*
|
||||||
|
* @param sha
|
||||||
|
* The SHA1 value to set this reference to
|
||||||
|
*/
|
||||||
|
public void updateTo(String sha) throws IOException {
|
||||||
|
updateTo(sha, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates this ref to the specified commit.
|
||||||
|
*
|
||||||
|
* @param sha
|
||||||
|
* The SHA1 value to set this reference to
|
||||||
|
* @param force
|
||||||
|
* Whether or not to force this ref update.
|
||||||
|
*/
|
||||||
|
public void updateTo(String sha, Boolean force) throws IOException {
|
||||||
|
new Requester(root)
|
||||||
|
.with("sha", sha).with("force", force).method("PATCH").to(url, GHRef.class).wrap(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes this ref from the repository using the GitHub API.
|
||||||
|
*/
|
||||||
|
public void delete() throws IOException {
|
||||||
|
new Requester(root).method("DELETE").to(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*package*/ GHRef wrap(GitHub root) {
|
||||||
|
this.root = root;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*package*/ static GHRef[] wrap(GHRef[] in, GitHub root) {
|
||||||
|
for (GHRef r : in) {
|
||||||
|
r.wrap(root);
|
||||||
|
}
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
|
||||||
|
"NP_UNWRITTEN_FIELD"}, justification = "JSON API")
|
||||||
public static class GHObject {
|
public static class GHObject {
|
||||||
private String type, sha, url;
|
private String type, sha, url;
|
||||||
|
|
||||||
|
|||||||
163
src/main/java/org/kohsuke/github/GHRelease.java
Normal file
163
src/main/java/org/kohsuke/github/GHRelease.java
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static java.lang.String.format;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Release in a github repository.
|
||||||
|
*
|
||||||
|
* @see GHRepository#getReleases()
|
||||||
|
* @see GHRepository#createRelease(String)
|
||||||
|
*/
|
||||||
|
public class GHRelease extends GHObject {
|
||||||
|
GitHub root;
|
||||||
|
GHRepository owner;
|
||||||
|
|
||||||
|
private String html_url;
|
||||||
|
private String assets_url;
|
||||||
|
private String upload_url;
|
||||||
|
private String tag_name;
|
||||||
|
private String target_commitish;
|
||||||
|
private String name;
|
||||||
|
private String body;
|
||||||
|
private boolean draft;
|
||||||
|
private boolean prerelease;
|
||||||
|
private Date published_at;
|
||||||
|
private String tarball_url;
|
||||||
|
private String zipball_url;
|
||||||
|
|
||||||
|
public String getAssetsUrl() {
|
||||||
|
return assets_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBody() {
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDraft() {
|
||||||
|
return draft;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHRelease setDraft(boolean draft) throws IOException {
|
||||||
|
edit("draft", draft);
|
||||||
|
this.draft = draft;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public URL getHtmlUrl() {
|
||||||
|
return GitHub.parseURL(html_url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHRepository getOwner() {
|
||||||
|
return owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOwner(GHRepository owner) {
|
||||||
|
this.owner = owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPrerelease() {
|
||||||
|
return prerelease;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getPublished_at() {
|
||||||
|
return new Date(published_at.getTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
public GitHub getRoot() {
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTagName() {
|
||||||
|
return tag_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTargetCommitish() {
|
||||||
|
return target_commitish;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUploadUrl() {
|
||||||
|
return upload_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getZipballUrl() {
|
||||||
|
return zipball_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTarballUrl() {
|
||||||
|
return tarball_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
GHRelease wrap(GHRepository owner) {
|
||||||
|
this.owner = owner;
|
||||||
|
this.root = owner.root;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GHRelease[] wrap(GHRelease[] releases, GHRepository owner) {
|
||||||
|
for (GHRelease release : releases) {
|
||||||
|
release.wrap(owner);
|
||||||
|
}
|
||||||
|
return releases;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Because github relies on SNI (http://en.wikipedia.org/wiki/Server_Name_Indication) this method will only work on
|
||||||
|
* Java 7 or greater. Options for fixing this for earlier JVMs can be found here
|
||||||
|
* http://stackoverflow.com/questions/12361090/server-name-indication-sni-on-java but involve more complicated
|
||||||
|
* handling of the HTTP requests to github's API.
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public GHAsset uploadAsset(File file, String contentType) throws IOException {
|
||||||
|
Requester builder = new Requester(owner.root);
|
||||||
|
|
||||||
|
String url = format("https://uploads.github.com%s/releases/%d/assets?name=%s",
|
||||||
|
owner.getApiTailUrl(""), getId(), file.getName());
|
||||||
|
return builder.contentType(contentType)
|
||||||
|
.with(new FileInputStream(file))
|
||||||
|
.to(url, GHAsset.class).wrap(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<GHAsset> getAssets() throws IOException {
|
||||||
|
Requester builder = new Requester(owner.root);
|
||||||
|
|
||||||
|
GHAsset[] assets = builder
|
||||||
|
.method("GET")
|
||||||
|
.to(getApiTailUrl("assets"), GHAsset[].class);
|
||||||
|
return Arrays.asList(GHAsset.wrap(assets, this));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes this release.
|
||||||
|
*/
|
||||||
|
public void delete() throws IOException {
|
||||||
|
new Requester(root).method("DELETE").to(owner.getApiTailUrl("releases/"+id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Edit this release.
|
||||||
|
*/
|
||||||
|
private void edit(String key, Object value) throws IOException {
|
||||||
|
new Requester(root)._with(key, value).method("PATCH").to(owner.getApiTailUrl("releases/"+id));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getApiTailUrl(String end) {
|
||||||
|
return owner.getApiTailUrl(format("releases/%s/%s",id,end));
|
||||||
|
}
|
||||||
|
}
|
||||||
80
src/main/java/org/kohsuke/github/GHReleaseBuilder.java
Normal file
80
src/main/java/org/kohsuke/github/GHReleaseBuilder.java
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builder pattern for creating a {@link GHRelease}
|
||||||
|
*
|
||||||
|
* @see GHRepository#createRelease(String)
|
||||||
|
*/
|
||||||
|
public class GHReleaseBuilder {
|
||||||
|
private final GHRepository repo;
|
||||||
|
private final Requester builder;
|
||||||
|
|
||||||
|
public GHReleaseBuilder(GHRepository ghRepository, String tag) {
|
||||||
|
this.repo = ghRepository;
|
||||||
|
this.builder = new Requester(repo.root);
|
||||||
|
builder.with("tag_name", tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param body The release notes body.
|
||||||
|
*/
|
||||||
|
public GHReleaseBuilder body(String body) {
|
||||||
|
if (body != null) {
|
||||||
|
builder.with("body", body);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the commitish value that determines where the Git tag is created from. Can be any branch or
|
||||||
|
* commit SHA.
|
||||||
|
*
|
||||||
|
* @param commitish Defaults to the repository’s default branch (usually "master"). Unused if the Git tag
|
||||||
|
* already exists.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public GHReleaseBuilder commitish(String commitish) {
|
||||||
|
if (commitish != null) {
|
||||||
|
builder.with("target_commitish", commitish);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional.
|
||||||
|
*
|
||||||
|
* @param draft {@code true} to create a draft (unpublished) release, {@code false} to create a published one.
|
||||||
|
* Default is {@code false}.
|
||||||
|
*/
|
||||||
|
public GHReleaseBuilder draft(boolean draft) {
|
||||||
|
builder.with("draft", draft);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param name the name of the release
|
||||||
|
*/
|
||||||
|
public GHReleaseBuilder name(String name) {
|
||||||
|
if (name != null) {
|
||||||
|
builder.with("name", name);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional
|
||||||
|
*
|
||||||
|
* @param prerelease {@code true} to identify the release as a prerelease. {@code false} to identify the release
|
||||||
|
* as a full release. Default is {@code false}.
|
||||||
|
*/
|
||||||
|
public GHReleaseBuilder prerelease(boolean prerelease) {
|
||||||
|
builder.with("prerelease", prerelease);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHRelease create() throws IOException {
|
||||||
|
return builder.to(repo.getApiTailUrl("releases"), GHRelease.class).wrap(repo);
|
||||||
|
}
|
||||||
|
}
|
||||||
23
src/main/java/org/kohsuke/github/GHRepoHook.java
Normal file
23
src/main/java/org/kohsuke/github/GHRepoHook.java
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
class GHRepoHook extends GHHook {
|
||||||
|
/**
|
||||||
|
* Repository that the hook belongs to.
|
||||||
|
*/
|
||||||
|
/*package*/ transient GHRepository repository;
|
||||||
|
|
||||||
|
/*package*/ GHRepoHook wrap(GHRepository owner) {
|
||||||
|
this.repository = owner;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
GitHub getRoot() {
|
||||||
|
return repository.root;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String getApiRoute() {
|
||||||
|
return String.format("/repos/%s/%s/hooks/%d", repository.getOwnerName(), repository.getName(), id);
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,82 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search repositories.
|
||||||
|
*
|
||||||
|
* @author Kohsuke Kawaguchi
|
||||||
|
* @see GitHub#searchRepositories()
|
||||||
|
*/
|
||||||
|
public class GHRepositorySearchBuilder extends GHSearchBuilder<GHRepository> {
|
||||||
|
/*package*/ GHRepositorySearchBuilder(GitHub root) {
|
||||||
|
super(root,RepositorySearchResult.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search terms.
|
||||||
|
*/
|
||||||
|
public GHRepositorySearchBuilder q(String term) {
|
||||||
|
super.q(term);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHRepositorySearchBuilder in(String v) {
|
||||||
|
return q("in:"+v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHRepositorySearchBuilder size(String v) {
|
||||||
|
return q("size:"+v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHRepositorySearchBuilder forks(String v) {
|
||||||
|
return q("forks:"+v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHRepositorySearchBuilder created(String v) {
|
||||||
|
return q("created:"+v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHRepositorySearchBuilder pushed(String v) {
|
||||||
|
return q("pushed:"+v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHRepositorySearchBuilder user(String v) {
|
||||||
|
return q("user:"+v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHRepositorySearchBuilder repo(String v) {
|
||||||
|
return q("repo:"+v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHRepositorySearchBuilder language(String v) {
|
||||||
|
return q("language:"+v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHRepositorySearchBuilder stars(String v) {
|
||||||
|
return q("stars:"+v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHRepositorySearchBuilder sort(Sort sort) {
|
||||||
|
req.with("sort",sort);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Sort { STARS, FORKS, UPDATED }
|
||||||
|
|
||||||
|
private static class RepositorySearchResult extends SearchResult<GHRepository> {
|
||||||
|
private GHRepository[] items;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
/*package*/ GHRepository[] getItems(GitHub root) {
|
||||||
|
for (GHRepository item : items)
|
||||||
|
item.wrap(root);
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getApiUrl() {
|
||||||
|
return "/search/repositories";
|
||||||
|
}
|
||||||
|
}
|
||||||
52
src/main/java/org/kohsuke/github/GHSearchBuilder.java
Normal file
52
src/main/java/org/kohsuke/github/GHSearchBuilder.java
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for various search builders.
|
||||||
|
*
|
||||||
|
* @author Kohsuke Kawaguchi
|
||||||
|
*/
|
||||||
|
public abstract class GHSearchBuilder<T> extends GHQueryBuilder<T> {
|
||||||
|
protected final List<String> terms = new ArrayList<String>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data transfer object that receives the result of search.
|
||||||
|
*/
|
||||||
|
private final Class<? extends SearchResult<T>> receiverType;
|
||||||
|
|
||||||
|
/*package*/ GHSearchBuilder(GitHub root, Class<? extends SearchResult<T>> receiverType) {
|
||||||
|
super(root);
|
||||||
|
this.receiverType = receiverType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search terms.
|
||||||
|
*/
|
||||||
|
public GHQueryBuilder<T> q(String term) {
|
||||||
|
terms.add(term);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs the search.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public PagedSearchIterable<T> list() {
|
||||||
|
return new PagedSearchIterable<T>(root) {
|
||||||
|
public PagedIterator<T> _iterator(int pageSize) {
|
||||||
|
req.set("q", StringUtils.join(terms, " "));
|
||||||
|
return new PagedIterator<T>(adapt(req.asIterator(getApiUrl(), receiverType, pageSize))) {
|
||||||
|
protected void wrapUp(T[] page) {
|
||||||
|
// SearchResult.getItems() should do it
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract String getApiUrl();
|
||||||
|
}
|
||||||
51
src/main/java/org/kohsuke/github/GHStargazer.java
Normal file
51
src/main/java/org/kohsuke/github/GHStargazer.java
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A stargazer at a repository on GitHub.
|
||||||
|
*
|
||||||
|
* @author noctarius
|
||||||
|
*/
|
||||||
|
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD"}, justification = "JSON API")
|
||||||
|
public class GHStargazer {
|
||||||
|
|
||||||
|
private GHRepository repository;
|
||||||
|
private String starred_at;
|
||||||
|
private GHUser user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the repository that is stargazed
|
||||||
|
*
|
||||||
|
* @return the starred repository
|
||||||
|
*/
|
||||||
|
public GHRepository getRepository() {
|
||||||
|
return repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the date when the repository was starred, however old stars before
|
||||||
|
* August 2012, will all show the date the API was changed to support starred_at.
|
||||||
|
*
|
||||||
|
* @return the date the stargazer was added
|
||||||
|
*/
|
||||||
|
public Date getStarredAt() {
|
||||||
|
return GitHub.parseDate(starred_at);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the user that starred the repository
|
||||||
|
*
|
||||||
|
* @return the stargazer user
|
||||||
|
*/
|
||||||
|
public GHUser getUser() {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
void wrapUp(GHRepository repository) {
|
||||||
|
this.repository = repository;
|
||||||
|
user.wrapUp(repository.root);
|
||||||
|
}
|
||||||
|
}
|
||||||
64
src/main/java/org/kohsuke/github/GHSubscription.java
Normal file
64
src/main/java/org/kohsuke/github/GHSubscription.java
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents your subscribing to a repository / conversation thread..
|
||||||
|
*
|
||||||
|
* @author Kohsuke Kawaguchi
|
||||||
|
* @see GHRepository#getSubscription()
|
||||||
|
* @see GHThread#getSubscription()
|
||||||
|
*/
|
||||||
|
public class GHSubscription {
|
||||||
|
private String created_at, url, repository_url, reason;
|
||||||
|
private boolean subscribed, ignored;
|
||||||
|
|
||||||
|
private GitHub root;
|
||||||
|
private GHRepository repo;
|
||||||
|
|
||||||
|
public Date getCreatedAt() {
|
||||||
|
return GitHub.parseDate(created_at);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRepositoryUrl() {
|
||||||
|
return repository_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getReason() {
|
||||||
|
return reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSubscribed() {
|
||||||
|
return subscribed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isIgnored() {
|
||||||
|
return ignored;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHRepository getRepository() {
|
||||||
|
return repo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes this subscription.
|
||||||
|
*/
|
||||||
|
public void delete() throws IOException {
|
||||||
|
new Requester(root).method("DELETE").to(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
GHSubscription wrapUp(GHRepository repo) {
|
||||||
|
this.repo = repo;
|
||||||
|
return wrapUp(repo.root);
|
||||||
|
}
|
||||||
|
|
||||||
|
GHSubscription wrapUp(GitHub root) {
|
||||||
|
this.root = root;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
42
src/main/java/org/kohsuke/github/GHTag.java
Normal file
42
src/main/java/org/kohsuke/github/GHTag.java
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a tag in {@link GHRepository}
|
||||||
|
*
|
||||||
|
* @see GHRepository#listTags()
|
||||||
|
*/
|
||||||
|
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
|
||||||
|
"NP_UNWRITTEN_FIELD"}, justification = "JSON API")
|
||||||
|
public class GHTag {
|
||||||
|
private GHRepository owner;
|
||||||
|
private GitHub root;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
private GHCommit commit;
|
||||||
|
|
||||||
|
/*package*/ GHTag wrap(GHRepository owner) {
|
||||||
|
this.owner = owner;
|
||||||
|
this.root = owner.root;
|
||||||
|
if (commit!=null)
|
||||||
|
commit.wrapUp(owner);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHRepository getOwner() {
|
||||||
|
return owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GitHub getRoot() {
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHCommit getCommit() {
|
||||||
|
return commit;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
package org.kohsuke.github;
|
package org.kohsuke.github;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
@@ -13,8 +12,9 @@ import java.util.TreeMap;
|
|||||||
* @author Kohsuke Kawaguchi
|
* @author Kohsuke Kawaguchi
|
||||||
*/
|
*/
|
||||||
public class GHTeam {
|
public class GHTeam {
|
||||||
private String name,permission;
|
private String name,permission,slug;
|
||||||
private int id;
|
private int id;
|
||||||
|
private GHOrganization organization; // populated by GET /user/teams where Teams+Orgs are returned together
|
||||||
|
|
||||||
protected /*final*/ GHOrganization org;
|
protected /*final*/ GHOrganization org;
|
||||||
|
|
||||||
@@ -23,6 +23,11 @@ public class GHTeam {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*package*/ GHTeam wrapUp(GitHub root) { // auto-wrapUp when organization is known from GET /user/teams
|
||||||
|
this.organization.wrapUp(root);
|
||||||
|
return wrapUp(organization);
|
||||||
|
}
|
||||||
|
|
||||||
/*package*/ static GHTeam[] wrapUp(GHTeam[] teams, GHOrganization owner) {
|
/*package*/ static GHTeam[] wrapUp(GHTeam[] teams, GHOrganization owner) {
|
||||||
for (GHTeam t : teams) {
|
for (GHTeam t : teams) {
|
||||||
t.wrapUp(owner);
|
t.wrapUp(owner);
|
||||||
@@ -38,6 +43,10 @@ public class GHTeam {
|
|||||||
return permission;
|
return permission;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getSlug() {
|
||||||
|
return slug;
|
||||||
|
}
|
||||||
|
|
||||||
public int getId() {
|
public int getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
@@ -45,24 +54,66 @@ public class GHTeam {
|
|||||||
/**
|
/**
|
||||||
* Retrieves the current members.
|
* Retrieves the current members.
|
||||||
*/
|
*/
|
||||||
|
public PagedIterable<GHUser> listMembers() throws IOException {
|
||||||
|
return new PagedIterable<GHUser>() {
|
||||||
|
public PagedIterator<GHUser> _iterator(int pageSize) {
|
||||||
|
return new PagedIterator<GHUser>(org.root.retrieve().asIterator(api("/members"), GHUser[].class, pageSize)) {
|
||||||
|
@Override
|
||||||
|
protected void wrapUp(GHUser[] page) {
|
||||||
|
GHUser.wrap(page, org.root);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public Set<GHUser> getMembers() throws IOException {
|
public Set<GHUser> getMembers() throws IOException {
|
||||||
return new HashSet<GHUser>(Arrays.asList(GHUser.wrap(org.root.retrieve().to(api("/members"), GHUser[].class), org.root)));
|
return Collections.unmodifiableSet(listMembers().asSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if this team has the specified user as a member.
|
||||||
|
*/
|
||||||
|
public boolean hasMember(GHUser user) {
|
||||||
|
try {
|
||||||
|
org.root.retrieve().to("/teams/" + id + "/members/" + user.getLogin());
|
||||||
|
return true;
|
||||||
|
} catch (IOException ignore) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String,GHRepository> getRepositories() throws IOException {
|
public Map<String,GHRepository> getRepositories() throws IOException {
|
||||||
GHRepository[] repos = org.root.retrieve().to(api("/repos"), GHRepository[].class);
|
|
||||||
Map<String,GHRepository> m = new TreeMap<String, GHRepository>();
|
Map<String,GHRepository> m = new TreeMap<String, GHRepository>();
|
||||||
for (GHRepository r : repos) {
|
for (GHRepository r : listRepositories()) {
|
||||||
m.put(r.getName(),r.wrap(org.root));
|
m.put(r.getName(), r);
|
||||||
}
|
}
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PagedIterable<GHRepository> listRepositories() {
|
||||||
|
return new PagedIterable<GHRepository>() {
|
||||||
|
public PagedIterator<GHRepository> _iterator(int pageSize) {
|
||||||
|
return new PagedIterator<GHRepository>(org.root.retrieve().asIterator(api("/repos"), GHRepository[].class, pageSize)) {
|
||||||
|
@Override
|
||||||
|
protected void wrapUp(GHRepository[] page) {
|
||||||
|
for (GHRepository r : page)
|
||||||
|
r.wrap(org.root);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a member to the team.
|
* Adds a member to the team.
|
||||||
|
*
|
||||||
|
* The user will be invited to the organization if required.
|
||||||
|
*
|
||||||
|
* @since 1.59
|
||||||
*/
|
*/
|
||||||
public void add(GHUser u) throws IOException {
|
public void add(GHUser u) throws IOException {
|
||||||
org.root.retrieve().method("PUT").to(api("/members/" + u.getLogin()), null);
|
org.root.retrieve().method("PUT").to(api("/memberships/" + u.getLogin()), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -73,14 +124,31 @@ public class GHTeam {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void add(GHRepository r) throws IOException {
|
public void add(GHRepository r) throws IOException {
|
||||||
org.root.retrieve().method("PUT").to(api("/repos/" + r.getOwnerName() + '/' + r.getName()), null);
|
add(r,null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(GHRepository r, GHOrganization.Permission permission) throws IOException {
|
||||||
|
org.root.retrieve().method("PUT")
|
||||||
|
.with("permission",permission)
|
||||||
|
.to(api("/repos/" + r.getOwnerName() + '/' + r.getName()), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void remove(GHRepository r) throws IOException {
|
public void remove(GHRepository r) throws IOException {
|
||||||
org.root.retrieve().method("DELETE").to(api("/repos/" + r.getOwnerName() + '/' + r.getName()), null);
|
org.root.retrieve().method("DELETE").to(api("/repos/" + r.getOwnerName() + '/' + r.getName()), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes this team.
|
||||||
|
*/
|
||||||
|
public void delete() throws IOException {
|
||||||
|
org.root.retrieve().method("DELETE").to(api(""));
|
||||||
|
}
|
||||||
|
|
||||||
private String api(String tail) {
|
private String api(String tail) {
|
||||||
return "/teams/"+id+tail;
|
return "/teams/"+id+tail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public GHOrganization getOrganization() {
|
||||||
|
return org;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
149
src/main/java/org/kohsuke/github/GHThread.java
Normal file
149
src/main/java/org/kohsuke/github/GHThread.java
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A conversation in the notification API.
|
||||||
|
*
|
||||||
|
* @see <a href="https://developer.github.com/v3/activity/notifications/">documentation</a>
|
||||||
|
* @see GHNotificationStream
|
||||||
|
* @author Kohsuke Kawaguchi
|
||||||
|
*/
|
||||||
|
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
|
||||||
|
"NP_UNWRITTEN_FIELD"}, justification = "JSON API")
|
||||||
|
public class GHThread extends GHObject {
|
||||||
|
private GitHub root;
|
||||||
|
private GHRepository repository;
|
||||||
|
private Subject subject;
|
||||||
|
private String reason;
|
||||||
|
private boolean unread;
|
||||||
|
private String last_read_at;
|
||||||
|
private String url,subscription_url;
|
||||||
|
|
||||||
|
static class Subject {
|
||||||
|
String title;
|
||||||
|
String url;
|
||||||
|
String latest_comment_url;
|
||||||
|
String type;
|
||||||
|
}
|
||||||
|
|
||||||
|
private GHThread() {// no external construction allowed
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns null if the entire thread has never been read.
|
||||||
|
*/
|
||||||
|
public Date getLastReadAt() {
|
||||||
|
return GitHub.parseDate(last_read_at);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated This object has no HTML URL.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public URL getHtmlUrl() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getReason() {
|
||||||
|
return reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHRepository getRepository() {
|
||||||
|
return repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: how to expose the subject?
|
||||||
|
|
||||||
|
public boolean isRead() {
|
||||||
|
return !unread;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTitle() {
|
||||||
|
return subject.title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return subject.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLastCommentUrl() {
|
||||||
|
return subject.latest_comment_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this thread is about an issue, return that issue.
|
||||||
|
*
|
||||||
|
* @return null if this thread is not about an issue.
|
||||||
|
*/
|
||||||
|
public GHIssue getBoundIssue() throws IOException {
|
||||||
|
if (!"Issue".equals(subject.type) && "PullRequest".equals(subject.type))
|
||||||
|
return null;
|
||||||
|
return repository.getIssue(
|
||||||
|
Integer.parseInt(subject.url.substring(subject.url.lastIndexOf('/') + 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this thread is about a pull request, return that pull request.
|
||||||
|
*
|
||||||
|
* @return null if this thread is not about a pull request.
|
||||||
|
*/
|
||||||
|
public GHPullRequest getBoundPullRequest() throws IOException {
|
||||||
|
if (!"PullRequest".equals(subject.type))
|
||||||
|
return null;
|
||||||
|
return repository.getPullRequest(
|
||||||
|
Integer.parseInt(subject.url.substring(subject.url.lastIndexOf('/') + 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this thread is about a commit, return that commit.
|
||||||
|
*
|
||||||
|
* @return null if this thread is not about a commit.
|
||||||
|
*/
|
||||||
|
public GHCommit getBoundCommit() throws IOException {
|
||||||
|
if (!"Commit".equals(subject.type))
|
||||||
|
return null;
|
||||||
|
return repository.getCommit(subject.url.substring(subject.url.lastIndexOf('/') + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*package*/ GHThread wrap(GitHub root) {
|
||||||
|
this.root = root;
|
||||||
|
if (this.repository!=null)
|
||||||
|
this.repository.wrap(root);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks this thread as read.
|
||||||
|
*/
|
||||||
|
public void markAsRead() throws IOException {
|
||||||
|
new Requester(root).method("PATCH").to(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subscribes to this conversation to get notifications.
|
||||||
|
*/
|
||||||
|
public GHSubscription subscribe(boolean subscribed, boolean ignored) throws IOException {
|
||||||
|
return new Requester(root)
|
||||||
|
.with("subscribed", subscribed)
|
||||||
|
.with("ignored", ignored)
|
||||||
|
.method("PUT").to(subscription_url, GHSubscription.class).wrapUp(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current subscription for this thread.
|
||||||
|
*
|
||||||
|
* @return null if no subscription exists.
|
||||||
|
*/
|
||||||
|
public GHSubscription getSubscription() throws IOException {
|
||||||
|
try {
|
||||||
|
return new Requester(root).to(subscription_url, GHSubscription.class).wrapUp(root);
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
58
src/main/java/org/kohsuke/github/GHTree.java
Normal file
58
src/main/java/org/kohsuke/github/GHTree.java
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides information for Git Trees
|
||||||
|
* https://developer.github.com/v3/git/trees/
|
||||||
|
*
|
||||||
|
* @author Daniel Teixeira - https://github.com/ddtxra
|
||||||
|
* @see GHRepository#getTree(String)
|
||||||
|
*/
|
||||||
|
public class GHTree {
|
||||||
|
/* package almost final */GitHub root;
|
||||||
|
|
||||||
|
private boolean truncated;
|
||||||
|
private String sha, url;
|
||||||
|
private GHTreeEntry[] tree;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The SHA for this trees
|
||||||
|
*/
|
||||||
|
public String getSha() {
|
||||||
|
return sha;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an array of entries of the trees
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public List<GHTreeEntry> getTree() {
|
||||||
|
return Collections.unmodifiableList(Arrays.asList(tree));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the number of items in the tree array exceeded the GitHub maximum limit.
|
||||||
|
* @return true true if the number of items in the tree array exceeded the GitHub maximum limit otherwise false.
|
||||||
|
*/
|
||||||
|
public boolean isTruncated() {
|
||||||
|
return truncated;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The API URL of this tag, such as
|
||||||
|
* "url": "https://api.github.com/repos/octocat/Hello-World/trees/fc6274d15fa3ae2ab983129fb037999f264ba9a7",
|
||||||
|
*/
|
||||||
|
public URL getUrl() {
|
||||||
|
return GitHub.parseURL(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* package */GHTree wrap(GitHub root) {
|
||||||
|
this.root = root;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
71
src/main/java/org/kohsuke/github/GHTreeEntry.java
Normal file
71
src/main/java/org/kohsuke/github/GHTreeEntry.java
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides information for Git Trees
|
||||||
|
* https://developer.github.com/v3/git/trees/
|
||||||
|
*
|
||||||
|
* @author Daniel Teixeira - https://github.com/ddtxra
|
||||||
|
* @see GHTree
|
||||||
|
*/
|
||||||
|
public class GHTreeEntry {
|
||||||
|
private String path, mode, type, sha, url;
|
||||||
|
private long size;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the path such as
|
||||||
|
* "subdir/file.txt"
|
||||||
|
*
|
||||||
|
* @return the path
|
||||||
|
*/
|
||||||
|
public String getPath() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get mode such as
|
||||||
|
* 100644
|
||||||
|
*
|
||||||
|
* @return the mode
|
||||||
|
*/
|
||||||
|
public String getMode() {
|
||||||
|
return mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the size of the file, such as
|
||||||
|
* 132
|
||||||
|
* @return The size of the path or 0 if it is a directory
|
||||||
|
*/
|
||||||
|
public long getSize() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the type such as:
|
||||||
|
* "blob"
|
||||||
|
*
|
||||||
|
* @return The type
|
||||||
|
*/
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SHA1 of this object.
|
||||||
|
*/
|
||||||
|
public String getSha() {
|
||||||
|
return sha;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* API URL to this Git data, such as
|
||||||
|
* https://api.github.com/repos/jenkinsci
|
||||||
|
* /jenkins/git/commits/b72322675eb0114363a9a86e9ad5a170d1d07ac0
|
||||||
|
*/
|
||||||
|
public URL getUrl() {
|
||||||
|
return GitHub.parseURL(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,7 +26,6 @@ package org.kohsuke.github;
|
|||||||
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
|
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
@@ -56,8 +55,14 @@ public class GHUser extends GHPerson {
|
|||||||
*/
|
*/
|
||||||
@WithBridgeMethods(Set.class)
|
@WithBridgeMethods(Set.class)
|
||||||
public GHPersonSet<GHUser> getFollows() throws IOException {
|
public GHPersonSet<GHUser> getFollows() throws IOException {
|
||||||
GHUser[] followers = root.retrieve().to("/users/" + login + "/following", GHUser[].class);
|
return new GHPersonSet<GHUser>(listFollows().asList());
|
||||||
return new GHPersonSet<GHUser>(Arrays.asList(wrap(followers,root)));
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists the users that this user is following
|
||||||
|
*/
|
||||||
|
public PagedIterable<GHUser> listFollows() {
|
||||||
|
return listUser("following");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -65,8 +70,55 @@ public class GHUser extends GHPerson {
|
|||||||
*/
|
*/
|
||||||
@WithBridgeMethods(Set.class)
|
@WithBridgeMethods(Set.class)
|
||||||
public GHPersonSet<GHUser> getFollowers() throws IOException {
|
public GHPersonSet<GHUser> getFollowers() throws IOException {
|
||||||
GHUser[] followers = root.retrieve().to("/users/" + login + "/followers", GHUser[].class);
|
return new GHPersonSet<GHUser>(listFollowers().asList());
|
||||||
return new GHPersonSet<GHUser>(Arrays.asList(wrap(followers,root)));
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists the users who are following this user.
|
||||||
|
*/
|
||||||
|
public PagedIterable<GHUser> listFollowers() {
|
||||||
|
return listUser("followers");
|
||||||
|
}
|
||||||
|
|
||||||
|
private PagedIterable<GHUser> listUser(final String suffix) {
|
||||||
|
return new PagedIterable<GHUser>() {
|
||||||
|
public PagedIterator<GHUser> _iterator(int pageSize) {
|
||||||
|
return new PagedIterator<GHUser>(root.retrieve().asIterator(getApiTailUrl(suffix), GHUser[].class, pageSize)) {
|
||||||
|
protected void wrapUp(GHUser[] page) {
|
||||||
|
GHUser.wrap(page,root);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists all the subscribed (aka watched) repositories.
|
||||||
|
*
|
||||||
|
* https://developer.github.com/v3/activity/watching/
|
||||||
|
*/
|
||||||
|
public PagedIterable<GHRepository> listSubscriptions() {
|
||||||
|
return listRepositories("subscriptions");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists all the repositories that this user has starred.
|
||||||
|
*/
|
||||||
|
public PagedIterable<GHRepository> listStarredRepositories() {
|
||||||
|
return listRepositories("starred");
|
||||||
|
}
|
||||||
|
|
||||||
|
private PagedIterable<GHRepository> listRepositories(final String suffix) {
|
||||||
|
return new PagedIterable<GHRepository>() {
|
||||||
|
public PagedIterator<GHRepository> _iterator(int pageSize) {
|
||||||
|
return new PagedIterator<GHRepository>(root.retrieve().asIterator(getApiTailUrl(suffix), GHRepository[].class, pageSize)) {
|
||||||
|
protected void wrapUp(GHRepository[] page) {
|
||||||
|
for (GHRepository c : page)
|
||||||
|
c.wrap(root);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -76,6 +128,13 @@ public class GHUser extends GHPerson {
|
|||||||
return org.hasMember(this);
|
return org.hasMember(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if this user belongs to the specified team.
|
||||||
|
*/
|
||||||
|
public boolean isMemberOf(GHTeam team) {
|
||||||
|
return team.hasMember(this);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if this user belongs to the specified organization as a public member.
|
* Returns true if this user belongs to the specified organization as a public member.
|
||||||
*/
|
*/
|
||||||
@@ -103,9 +162,38 @@ public class GHUser extends GHPerson {
|
|||||||
return orgs;
|
return orgs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public String toString() {
|
* Lists events performed by a user (this includes private events if the caller is authenticated.
|
||||||
return "User:"+login;
|
*/
|
||||||
|
public PagedIterable<GHEventInfo> listEvents() throws IOException {
|
||||||
|
return new PagedIterable<GHEventInfo>() {
|
||||||
|
public PagedIterator<GHEventInfo> _iterator(int pageSize) {
|
||||||
|
return new PagedIterator<GHEventInfo>(root.retrieve().asIterator(String.format("/users/%s/events", login), GHEventInfo[].class, pageSize)) {
|
||||||
|
@Override
|
||||||
|
protected void wrapUp(GHEventInfo[] page) {
|
||||||
|
for (GHEventInfo c : page)
|
||||||
|
c.wrapUp(root);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists Gists created by this user.
|
||||||
|
*/
|
||||||
|
public PagedIterable<GHGist> listGists() throws IOException {
|
||||||
|
return new PagedIterable<GHGist>() {
|
||||||
|
public PagedIterator<GHGist> _iterator(int pageSize) {
|
||||||
|
return new PagedIterator<GHGist>(root.retrieve().asIterator(String.format("/users/%s/gists", login), GHGist[].class, pageSize)) {
|
||||||
|
@Override
|
||||||
|
protected void wrapUp(GHGist[] page) {
|
||||||
|
for (GHGist c : page)
|
||||||
|
c.wrapUp(GHUser.this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -121,4 +209,9 @@ public class GHUser extends GHPerson {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String getApiTailUrl(String tail) {
|
||||||
|
if (tail.length()>0 && !tail.startsWith("/")) tail='/'+tail;
|
||||||
|
return "/users/" + login + tail;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
72
src/main/java/org/kohsuke/github/GHUserSearchBuilder.java
Normal file
72
src/main/java/org/kohsuke/github/GHUserSearchBuilder.java
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search users.
|
||||||
|
*
|
||||||
|
* @author Kohsuke Kawaguchi
|
||||||
|
* @see GitHub#searchUsers()
|
||||||
|
*/
|
||||||
|
public class GHUserSearchBuilder extends GHSearchBuilder<GHUser> {
|
||||||
|
/*package*/ GHUserSearchBuilder(GitHub root) {
|
||||||
|
super(root,UserSearchResult.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search terms.
|
||||||
|
*/
|
||||||
|
public GHUserSearchBuilder q(String term) {
|
||||||
|
super.q(term);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHUserSearchBuilder type(String v) {
|
||||||
|
return q("type:"+v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHUserSearchBuilder in(String v) {
|
||||||
|
return q("in:"+v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHUserSearchBuilder repos(String v) {
|
||||||
|
return q("repos:"+v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHUserSearchBuilder location(String v) {
|
||||||
|
return q("location:"+v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHUserSearchBuilder language(String v) {
|
||||||
|
return q("language:"+v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHUserSearchBuilder created(String v) {
|
||||||
|
return q("created:"+v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHUserSearchBuilder followers(String v) {
|
||||||
|
return q("followers:"+v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHUserSearchBuilder sort(Sort sort) {
|
||||||
|
req.with("sort",sort);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Sort { FOLLOWERS, REPOSITORIES, JOINED }
|
||||||
|
|
||||||
|
private static class UserSearchResult extends SearchResult<GHUser> {
|
||||||
|
private GHUser[] items;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
/*package*/ GHUser[] getItems(GitHub root) {
|
||||||
|
return GHUser.wrap(items,root);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getApiUrl() {
|
||||||
|
return "/search/users";
|
||||||
|
}
|
||||||
|
}
|
||||||
13
src/main/java/org/kohsuke/github/GHVerifiedKey.java
Normal file
13
src/main/java/org/kohsuke/github/GHVerifiedKey.java
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
public class GHVerifiedKey extends GHKey {
|
||||||
|
|
||||||
|
public GHVerifiedKey() {
|
||||||
|
this.verified = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getTitle() {
|
||||||
|
return (title == null ? "key-" + id : title);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,102 +23,138 @@
|
|||||||
*/
|
*/
|
||||||
package org.kohsuke.github;
|
package org.kohsuke.github;
|
||||||
|
|
||||||
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
|
import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.ANY;
|
||||||
import org.apache.commons.codec.binary.Base64;
|
import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.NONE;
|
||||||
import org.apache.commons.io.IOUtils;
|
import static java.util.logging.Level.FINE;
|
||||||
import org.codehaus.jackson.map.DeserializationConfig.Feature;
|
import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
|
||||||
import org.codehaus.jackson.map.ObjectMapper;
|
import static org.kohsuke.github.Previews.DRAX;
|
||||||
import org.codehaus.jackson.map.introspect.VisibilityChecker.Std;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Hashtable;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Set;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
|
||||||
import static org.codehaus.jackson.annotate.JsonAutoDetect.Visibility.*;
|
import org.apache.commons.codec.Charsets;
|
||||||
|
import org.apache.commons.codec.binary.Base64;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.introspect.VisibilityChecker.Std;
|
||||||
|
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
|
||||||
|
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Root of the GitHub API.
|
* Root of the GitHub API.
|
||||||
*
|
*
|
||||||
|
* <h2>Thread safety</h2>
|
||||||
|
* <p>
|
||||||
|
* This library aims to be safe for use by multiple threads concurrently, although
|
||||||
|
* the library itself makes no attempt to control/serialize potentially conflicting
|
||||||
|
* operations to GitHub, such as updating & deleting a repository at the same time.
|
||||||
|
*
|
||||||
* @author Kohsuke Kawaguchi
|
* @author Kohsuke Kawaguchi
|
||||||
*/
|
*/
|
||||||
public class GitHub {
|
public class GitHub {
|
||||||
/*package*/ final String login;
|
/*package*/ final String login;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value of the authorization header to be sent with the request.
|
||||||
|
*/
|
||||||
/*package*/ final String encodedAuthorization;
|
/*package*/ final String encodedAuthorization;
|
||||||
/*package*/ final String apiToken;
|
|
||||||
|
|
||||||
private final Map<String,GHUser> users = new HashMap<String, GHUser>();
|
private final Map<String,GHUser> users = new Hashtable<String, GHUser>();
|
||||||
private final Map<String,GHOrganization> orgs = new HashMap<String, GHOrganization>();
|
private final Map<String,GHOrganization> orgs = new Hashtable<String, GHOrganization>();
|
||||||
/*package*/ String oauthAccessToken;
|
|
||||||
|
|
||||||
private final String apiUrl;
|
private final String apiUrl;
|
||||||
|
|
||||||
private GitHub(String login, String apiToken, String password) {
|
/*package*/ final RateLimitHandler rateLimitHandler;
|
||||||
this (GITHUB_URL, login, apiToken, password);
|
/*package*/ final AbuseLimitHandler abuseLimitHandler;
|
||||||
}
|
|
||||||
|
private HttpConnector connector = HttpConnector.DEFAULT;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Creates a client API root object.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Several different combinations of the login/oauthAccessToken/password parameters are allowed
|
||||||
|
* to represent different ways of authentication.
|
||||||
|
*
|
||||||
|
* <dl>
|
||||||
|
* <dt>Loging anonymously
|
||||||
|
* <dd>Leave all three parameters null and you will be making HTTP requests without any authentication.
|
||||||
|
*
|
||||||
|
* <dt>Log in with password
|
||||||
|
* <dd>Specify the login and password, then leave oauthAccessToken null.
|
||||||
|
* This will use the HTTP BASIC auth with the GitHub API.
|
||||||
|
*
|
||||||
|
* <dt>Log in with OAuth token
|
||||||
|
* <dd>Specify oauthAccessToken, and optionally specify the login. Leave password null.
|
||||||
|
* This will send OAuth token to the GitHub API. If the login parameter is null,
|
||||||
|
* The constructor makes an API call to figure out the user name that owns the token.
|
||||||
|
* </dl>
|
||||||
*
|
*
|
||||||
* @param apiUrl
|
* @param apiUrl
|
||||||
* The URL of GitHub (or GitHub enterprise) API endpoint, such as "https://api.github.com" or
|
* The URL of GitHub (or GitHub enterprise) API endpoint, such as "https://api.github.com" or
|
||||||
* "http://ghe.acme.com/api/v3". Note that GitHub Enterprise has <tt>/api/v3</tt> in the URL.
|
* "http://ghe.acme.com/api/v3". Note that GitHub Enterprise has <tt>/api/v3</tt> in the URL.
|
||||||
* For historical reasons, this parameter still accepts the bare domain name, but that's considered deprecated.
|
* For historical reasons, this parameter still accepts the bare domain name, but that's considered deprecated.
|
||||||
* Password is also considered deprecated as it is no longer required for api usage.
|
* Password is also considered deprecated as it is no longer required for api usage.
|
||||||
|
* @param login
|
||||||
|
* The use ID on GitHub that you are logging in as. Can be omitted if the OAuth token is
|
||||||
|
* provided or if logging in anonymously. Specifying this would save one API call.
|
||||||
|
* @param oauthAccessToken
|
||||||
|
* Secret OAuth token.
|
||||||
|
* @param password
|
||||||
|
* User's password. Always used in conjunction with the {@code login} parameter
|
||||||
|
* @param connector
|
||||||
|
* HttpConnector to use. Pass null to use default connector.
|
||||||
*/
|
*/
|
||||||
private GitHub(String apiUrl, String login, String apiToken, String password) {
|
/* package */ GitHub(String apiUrl, String login, String oauthAccessToken, String password, HttpConnector connector, RateLimitHandler rateLimitHandler, AbuseLimitHandler abuseLimitHandler) throws IOException {
|
||||||
if (apiUrl.endsWith("/")) apiUrl = apiUrl.substring(0, apiUrl.length()-1); // normalize
|
if (apiUrl.endsWith("/")) apiUrl = apiUrl.substring(0, apiUrl.length()-1); // normalize
|
||||||
this.apiUrl = apiUrl;
|
this.apiUrl = apiUrl;
|
||||||
this.login = login;
|
if (null != connector) this.connector = connector;
|
||||||
this.apiToken = apiToken;
|
|
||||||
|
|
||||||
if (apiToken!=null || password!=null) {
|
if (oauthAccessToken!=null) {
|
||||||
String authorization = password==null ? (login + "/token" + ":" + apiToken) : (login + ':'+password);
|
encodedAuthorization = "token "+oauthAccessToken;
|
||||||
encodedAuthorization = new String(Base64.encodeBase64(authorization.getBytes()));
|
} else {
|
||||||
} else
|
if (password!=null) {
|
||||||
encodedAuthorization = null;
|
String authorization = (login + ':' + password);
|
||||||
}
|
String charsetName = Charsets.UTF_8.name();
|
||||||
|
encodedAuthorization = "Basic "+new String(Base64.encodeBase64(authorization.getBytes(charsetName)), charsetName);
|
||||||
|
} else {// anonymous access
|
||||||
|
encodedAuthorization = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private GitHub (String apiUrl, String oauthAccessToken) throws IOException {
|
this.rateLimitHandler = rateLimitHandler;
|
||||||
if (apiUrl.endsWith("/")) apiUrl = apiUrl.substring(0, apiUrl.length()-1); // normalize
|
this.abuseLimitHandler = abuseLimitHandler;
|
||||||
this.apiUrl = apiUrl;
|
|
||||||
this.encodedAuthorization = null;
|
|
||||||
|
|
||||||
this.oauthAccessToken = oauthAccessToken;
|
if (login==null && encodedAuthorization!=null)
|
||||||
this.apiToken = oauthAccessToken;
|
login = getMyself().getLogin();
|
||||||
|
this.login = login;
|
||||||
this.login = getMyself().getLogin();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Obtains the credential from "~/.github"
|
* Obtains the credential from "~/.github" or from the System Environment Properties.
|
||||||
*/
|
*/
|
||||||
public static GitHub connect() throws IOException {
|
public static GitHub connect() throws IOException {
|
||||||
Properties props = new Properties();
|
return GitHubBuilder.fromCredentials().build();
|
||||||
File homeDir = new File(System.getProperty("user.home"));
|
|
||||||
FileInputStream in = new FileInputStream(new File(homeDir, ".github"));
|
|
||||||
try {
|
|
||||||
props.load(in);
|
|
||||||
} finally {
|
|
||||||
IOUtils.closeQuietly(in);
|
|
||||||
}
|
|
||||||
String oauth = props.getProperty("oauth");
|
|
||||||
if (oauth!=null)
|
|
||||||
return new GitHub(GITHUB_URL,oauth);
|
|
||||||
else
|
|
||||||
return new GitHub(props.getProperty("login"),props.getProperty("token"),props.getProperty("password"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -129,45 +165,81 @@ public class GitHub {
|
|||||||
* "http://ghe.acme.com/api/v3". Note that GitHub Enterprise has <tt>/api/v3</tt> in the URL.
|
* "http://ghe.acme.com/api/v3". Note that GitHub Enterprise has <tt>/api/v3</tt> in the URL.
|
||||||
* For historical reasons, this parameter still accepts the bare domain name, but that's considered deprecated.
|
* For historical reasons, this parameter still accepts the bare domain name, but that's considered deprecated.
|
||||||
*/
|
*/
|
||||||
public static GitHub connectToEnterprise(String apiUrl, String login, String apiToken) {
|
public static GitHub connectToEnterprise(String apiUrl, String oauthAccessToken) throws IOException {
|
||||||
return new GitHub(apiUrl,login,apiToken,null);
|
return new GitHubBuilder().withEndpoint(apiUrl).withOAuthToken(oauthAccessToken).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GitHub connect(String login, String apiToken){
|
public static GitHub connectToEnterprise(String apiUrl, String login, String password) throws IOException {
|
||||||
return new GitHub(login,apiToken,null);
|
return new GitHubBuilder().withEndpoint(apiUrl).withPassword(login, password).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GitHub connect(String login, String apiToken, String password){
|
public static GitHub connect(String login, String oauthAccessToken) throws IOException {
|
||||||
return new GitHub(login,apiToken,password);
|
return new GitHubBuilder().withOAuthToken(oauthAccessToken, login).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GitHub connectUsingOAuth (String accessToken) throws IOException {
|
/**
|
||||||
return connectUsingOAuth("github.com", accessToken);
|
* @deprecated
|
||||||
|
* Either OAuth token or password is sufficient, so there's no point in passing both.
|
||||||
|
* Use {@link #connectUsingPassword(String, String)} or {@link #connectUsingOAuth(String)}.
|
||||||
|
*/
|
||||||
|
public static GitHub connect(String login, String oauthAccessToken, String password) throws IOException {
|
||||||
|
return new GitHubBuilder().withOAuthToken(oauthAccessToken, login).withPassword(login, password).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GitHub connectUsingOAuth (String githubServer, String accessToken) throws IOException {
|
public static GitHub connectUsingPassword(String login, String password) throws IOException {
|
||||||
return new GitHub(githubServer, accessToken);
|
return new GitHubBuilder().withPassword(login, password).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GitHub connectUsingOAuth(String oauthAccessToken) throws IOException {
|
||||||
|
return new GitHubBuilder().withOAuthToken(oauthAccessToken).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GitHub connectUsingOAuth(String githubServer, String oauthAccessToken) throws IOException {
|
||||||
|
return new GitHubBuilder().withEndpoint(githubServer).withOAuthToken(oauthAccessToken).build();
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Connects to GitHub anonymously.
|
* Connects to GitHub anonymously.
|
||||||
*
|
*
|
||||||
* All operations that requires authentication will fail.
|
* All operations that requires authentication will fail.
|
||||||
*/
|
*/
|
||||||
public static GitHub connectAnonymously() {
|
public static GitHub connectAnonymously() throws IOException {
|
||||||
return new GitHub(null,null,null);
|
return new GitHubBuilder().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connects to GitHub Enterprise anonymously.
|
||||||
|
*
|
||||||
|
* All operations that requires authentication will fail.
|
||||||
|
*/
|
||||||
|
public static GitHub connectToEnterpriseAnonymously(String apiUrl) throws IOException {
|
||||||
|
return new GitHubBuilder().withEndpoint(apiUrl).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this an anonymous connection
|
||||||
|
* @return {@code true} if operations that require authentication will fail.
|
||||||
|
*/
|
||||||
|
public boolean isAnonymous() {
|
||||||
|
return login==null && encodedAuthorization==null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpConnector getConnector() {
|
||||||
|
return connector;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the custom connector used to make requests to GitHub.
|
||||||
|
*/
|
||||||
|
public void setConnector(HttpConnector connector) {
|
||||||
|
this.connector = connector;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*package*/ void requireCredential() {
|
/*package*/ void requireCredential() {
|
||||||
if ((login==null || encodedAuthorization==null) && oauthAccessToken == null)
|
if (isAnonymous())
|
||||||
throw new IllegalStateException("This operation requires a credential but none is given to the GitHub constructor");
|
throw new IllegalStateException("This operation requires a credential but none is given to the GitHub constructor");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*package*/ URL getApiURL(String tailApiUrl) throws IOException {
|
/*package*/ URL getApiURL(String tailApiUrl) throws IOException {
|
||||||
if (oauthAccessToken != null) {
|
|
||||||
// append the access token
|
|
||||||
tailApiUrl = tailApiUrl + (tailApiUrl.indexOf('?')>=0 ?'&':'?') + "access_token=" + oauthAccessToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tailApiUrl.startsWith("/")) {
|
if (tailApiUrl.startsWith("/")) {
|
||||||
if ("github.com".equals(apiUrl)) {// backward compatibility
|
if ("github.com".equals(apiUrl)) {// backward compatibility
|
||||||
return new URL(GITHUB_URL + tailApiUrl);
|
return new URL(GITHUB_URL + tailApiUrl);
|
||||||
@@ -187,15 +259,26 @@ public class GitHub {
|
|||||||
* Gets the current rate limit.
|
* Gets the current rate limit.
|
||||||
*/
|
*/
|
||||||
public GHRateLimit getRateLimit() throws IOException {
|
public GHRateLimit getRateLimit() throws IOException {
|
||||||
return retrieve().to("/rate_limit", JsonRateLimit.class).rate;
|
try {
|
||||||
|
return retrieve().to("/rate_limit", JsonRateLimit.class).rate;
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
// GitHub Enterprise doesn't have the rate limit, so in that case
|
||||||
|
// return some big number that's not too big.
|
||||||
|
// see issue #78
|
||||||
|
GHRateLimit r = new GHRateLimit();
|
||||||
|
r.limit = r.remaining = 1000000;
|
||||||
|
long hours = 1000L * 60 * 60;
|
||||||
|
r.reset = new Date(System.currentTimeMillis() + 1 * hours );
|
||||||
|
return r;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the {@link GHUser} that represents yourself.
|
* Gets the {@link GHUser} that represents yourself.
|
||||||
*/
|
*/
|
||||||
@WithBridgeMethods(GHUser.class)
|
@WithBridgeMethods(GHUser.class)
|
||||||
public GHMyself getMyself() throws IOException {
|
public GHMyself getMyself() throws IOException {
|
||||||
requireCredential();
|
requireCredential();
|
||||||
|
|
||||||
GHMyself u = retrieve().to("/user", GHMyself.class);
|
GHMyself u = retrieve().to("/user", GHMyself.class);
|
||||||
|
|
||||||
@@ -203,20 +286,29 @@ public class GitHub {
|
|||||||
users.put(u.getLogin(), u);
|
users.put(u.getLogin(), u);
|
||||||
|
|
||||||
return u;
|
return u;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Obtains the object that represents the named user.
|
* Obtains the object that represents the named user.
|
||||||
*/
|
*/
|
||||||
public GHUser getUser(String login) throws IOException {
|
public GHUser getUser(String login) throws IOException {
|
||||||
GHUser u = users.get(login);
|
GHUser u = users.get(login);
|
||||||
if (u == null) {
|
if (u == null) {
|
||||||
u = retrieve().to("/users/" + login, GHUser.class);
|
u = retrieve().to("/users/" + login, GHUser.class);
|
||||||
u.root = this;
|
u.root = this;
|
||||||
users.put(u.getLogin(), u);
|
users.put(u.getLogin(), u);
|
||||||
}
|
}
|
||||||
return u;
|
return u;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* clears all cached data in order for external changes (modifications and del
|
||||||
|
*/
|
||||||
|
public void refreshCache() {
|
||||||
|
users.clear();
|
||||||
|
orgs.clear();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interns the given {@link GHUser}.
|
* Interns the given {@link GHUser}.
|
||||||
@@ -225,7 +317,7 @@ public class GitHub {
|
|||||||
GHUser u = users.get(orig.getLogin());
|
GHUser u = users.get(orig.getLogin());
|
||||||
if (u==null) {
|
if (u==null) {
|
||||||
orig.root = this;
|
orig.root = this;
|
||||||
users.put(login,orig);
|
users.put(orig.getLogin(),orig);
|
||||||
return orig;
|
return orig;
|
||||||
}
|
}
|
||||||
return u;
|
return u;
|
||||||
@@ -249,6 +341,46 @@ public class GitHub {
|
|||||||
String[] tokens = name.split("/");
|
String[] tokens = name.split("/");
|
||||||
return retrieve().to("/repos/" + tokens[0] + '/' + tokens[1], GHRepository.class).wrap(this);
|
return retrieve().to("/repos/" + tokens[0] + '/' + tokens[1], GHRepository.class).wrap(this);
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Returns a list of popular open source licenses
|
||||||
|
*
|
||||||
|
* WARNING: This uses a PREVIEW API.
|
||||||
|
*
|
||||||
|
* @see <a href="https://developer.github.com/v3/licenses/">GitHub API - Licenses</a>
|
||||||
|
*
|
||||||
|
* @return a list of popular open source licenses
|
||||||
|
*/
|
||||||
|
@Preview @Deprecated
|
||||||
|
public PagedIterable<GHLicense> listLicenses() throws IOException {
|
||||||
|
return new PagedIterable<GHLicense>() {
|
||||||
|
public PagedIterator<GHLicense> _iterator(int pageSize) {
|
||||||
|
return new PagedIterator<GHLicense>(retrieve().withPreview(DRAX).asIterator("/licenses", GHLicense[].class, pageSize)) {
|
||||||
|
@Override
|
||||||
|
protected void wrapUp(GHLicense[] page) {
|
||||||
|
for (GHLicense c : page)
|
||||||
|
c.wrap(GitHub.this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the full details for a license
|
||||||
|
*
|
||||||
|
* WARNING: This uses a PREVIEW API.
|
||||||
|
*
|
||||||
|
* @param key The license key provided from the API
|
||||||
|
* @return The license details
|
||||||
|
* @throws IOException
|
||||||
|
* @see GHLicense#getKey()
|
||||||
|
*/
|
||||||
|
@Preview @Deprecated
|
||||||
|
public GHLicense getLicense(String key) throws IOException {
|
||||||
|
return retrieve().withPreview(DRAX).to("/licenses/" + key, GHLicense.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method returns a shallowly populated organizations.
|
* This method returns a shallowly populated organizations.
|
||||||
@@ -266,17 +398,49 @@ public class GitHub {
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets complete map of organizations/teams that current user belongs to.
|
||||||
|
*
|
||||||
|
* Leverages the new GitHub API /user/teams made available recently to
|
||||||
|
* get in a single call the complete set of organizations, teams and permissions
|
||||||
|
* in a single call.
|
||||||
|
*/
|
||||||
|
public Map<String, Set<GHTeam>> getMyTeams() throws IOException {
|
||||||
|
Map<String, Set<GHTeam>> allMyTeams = new HashMap<String, Set<GHTeam>>();
|
||||||
|
for (GHTeam team : retrieve().to("/user/teams", GHTeam[].class)) {
|
||||||
|
team.wrapUp(this);
|
||||||
|
String orgLogin = team.getOrganization().getLogin();
|
||||||
|
Set<GHTeam> teamsPerOrg = allMyTeams.get(orgLogin);
|
||||||
|
if (teamsPerOrg == null) {
|
||||||
|
teamsPerOrg = new HashSet<GHTeam>();
|
||||||
|
}
|
||||||
|
teamsPerOrg.add(team);
|
||||||
|
allMyTeams.put(orgLogin, teamsPerOrg);
|
||||||
|
}
|
||||||
|
return allMyTeams;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Public events visible to you. Equivalent of what's displayed on https://github.com/
|
* Public events visible to you. Equivalent of what's displayed on https://github.com/
|
||||||
*/
|
*/
|
||||||
public List<GHEventInfo> getEvents() throws IOException {
|
public List<GHEventInfo> getEvents() throws IOException {
|
||||||
// TODO: pagination
|
|
||||||
GHEventInfo[] events = retrieve().to("/events", GHEventInfo[].class);
|
GHEventInfo[] events = retrieve().to("/events", GHEventInfo[].class);
|
||||||
for (GHEventInfo e : events)
|
for (GHEventInfo e : events)
|
||||||
e.wrapUp(this);
|
e.wrapUp(this);
|
||||||
return Arrays.asList(events);
|
return Arrays.asList(events);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a sigle gist by ID.
|
||||||
|
*/
|
||||||
|
public GHGist getGist(String id) throws IOException {
|
||||||
|
return retrieve().to("/gists/"+id,GHGist.class).wrapUp(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GHGistBuilder createGist() {
|
||||||
|
return new GHGistBuilder(this);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the GitHub event object.
|
* Parses the GitHub event object.
|
||||||
*
|
*
|
||||||
@@ -295,12 +459,42 @@ public class GitHub {
|
|||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
* Newly created repository.
|
* Newly created repository.
|
||||||
|
* @deprecated
|
||||||
|
* Use {@link #createRepository(String)} that uses a builder pattern to let you control every aspect.
|
||||||
*/
|
*/
|
||||||
public GHRepository createRepository(String name, String description, String homepage, boolean isPublic) throws IOException {
|
public GHRepository createRepository(String name, String description, String homepage, boolean isPublic) throws IOException {
|
||||||
|
return createRepository(name).description(description).homepage(homepage).private_(!isPublic).create();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts a builder that creates a new repository.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* You use the returned builder to set various properties, then call {@link GHCreateRepositoryBuilder#create()}
|
||||||
|
* to finally createa repository.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* To create a repository in an organization, see
|
||||||
|
* {@link GHOrganization#createRepository(String, String, String, GHTeam, boolean)}
|
||||||
|
*/
|
||||||
|
public GHCreateRepositoryBuilder createRepository(String name) {
|
||||||
|
return new GHCreateRepositoryBuilder(this,"/user/repos",name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new authorization.
|
||||||
|
*
|
||||||
|
* The token created can be then used for {@link GitHub#connectUsingOAuth(String)} in the future.
|
||||||
|
*
|
||||||
|
* @see <a href="http://developer.github.com/v3/oauth/#create-a-new-authorization">Documentation</a>
|
||||||
|
*/
|
||||||
|
public GHAuthorization createToken(Collection<String> scope, String note, String noteUrl) throws IOException{
|
||||||
Requester requester = new Requester(this)
|
Requester requester = new Requester(this)
|
||||||
.with("name", name).with("description", description).with("homepage", homepage)
|
.with("scopes", scope)
|
||||||
.with("public", isPublic ? 1 : 0);
|
.with("note", note)
|
||||||
return requester.method("POST").to("/user/repos", GHRepository.class).wrap(this);
|
.with("note_url", noteUrl);
|
||||||
|
|
||||||
|
return requester.method("POST").to("/authorizations", GHAuthorization.class).wrap(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -311,10 +505,163 @@ public class GitHub {
|
|||||||
retrieve().to("/user", GHUser.class);
|
retrieve().to("/user", GHUser.class);
|
||||||
return true;
|
return true;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
if (LOGGER.isLoggable(FINE))
|
||||||
|
LOGGER.log(FINE, "Exception validating credentials on " + this.apiUrl + " with login '" + this.login + "' " + e, e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class GHApiInfo {
|
||||||
|
private String rate_limit_url;
|
||||||
|
|
||||||
|
void check(String apiUrl) throws IOException {
|
||||||
|
if (rate_limit_url==null)
|
||||||
|
throw new IOException(apiUrl+" doesn't look like GitHub API URL");
|
||||||
|
|
||||||
|
// make sure that the URL is legitimate
|
||||||
|
new URL(rate_limit_url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the connection.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Verify that the API URL and credentials are valid to access this GitHub.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This method returns normally if the endpoint is reachable and verified to be GitHub API URL.
|
||||||
|
* Otherwise this method throws {@link IOException} to indicate the problem.
|
||||||
|
*/
|
||||||
|
public void checkApiUrlValidity() throws IOException {
|
||||||
|
try {
|
||||||
|
retrieve().to("/", GHApiInfo.class).check(apiUrl);
|
||||||
|
} catch (IOException e) {
|
||||||
|
if (isPrivateModeEnabled()) {
|
||||||
|
throw (IOException)new IOException("GitHub Enterprise server (" + apiUrl + ") with private mode enabled").initCause(e);
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures if a GitHub Enterprise server is configured in private mode.
|
||||||
|
*
|
||||||
|
* @return {@code true} if private mode is enabled. If it tries to use this method with GitHub, returns {@code
|
||||||
|
* false}.
|
||||||
|
*/
|
||||||
|
private boolean isPrivateModeEnabled() {
|
||||||
|
try {
|
||||||
|
HttpURLConnection uc = getConnector().connect(getApiURL("/"));
|
||||||
|
/*
|
||||||
|
$ curl -i https://github.mycompany.com/api/v3/
|
||||||
|
HTTP/1.1 401 Unauthorized
|
||||||
|
Server: GitHub.com
|
||||||
|
Date: Sat, 05 Mar 2016 19:45:01 GMT
|
||||||
|
Content-Type: application/json; charset=utf-8
|
||||||
|
Content-Length: 130
|
||||||
|
Status: 401 Unauthorized
|
||||||
|
X-GitHub-Media-Type: github.v3
|
||||||
|
X-XSS-Protection: 1; mode=block
|
||||||
|
X-Frame-Options: deny
|
||||||
|
Content-Security-Policy: default-src 'none'
|
||||||
|
Access-Control-Allow-Credentials: true
|
||||||
|
Access-Control-Expose-Headers: ETag, Link, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval
|
||||||
|
Access-Control-Allow-Origin: *
|
||||||
|
X-GitHub-Request-Id: dbc70361-b11d-4131-9a7f-674b8edd0411
|
||||||
|
Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
|
||||||
|
X-Content-Type-Options: nosniff
|
||||||
|
*/
|
||||||
|
return uc.getResponseCode() == HTTP_UNAUTHORIZED
|
||||||
|
&& uc.getHeaderField("X-GitHub-Media-Type") != null;
|
||||||
|
} catch (IOException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search issues.
|
||||||
|
*/
|
||||||
|
public GHIssueSearchBuilder searchIssues() {
|
||||||
|
return new GHIssueSearchBuilder(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search users.
|
||||||
|
*/
|
||||||
|
public GHUserSearchBuilder searchUsers() {
|
||||||
|
return new GHUserSearchBuilder(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search repositories.
|
||||||
|
*/
|
||||||
|
public GHRepositorySearchBuilder searchRepositories() {
|
||||||
|
return new GHRepositorySearchBuilder(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search content.
|
||||||
|
*/
|
||||||
|
public GHContentSearchBuilder searchContent() {
|
||||||
|
return new GHContentSearchBuilder(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List all the notifications.
|
||||||
|
*/
|
||||||
|
public GHNotificationStream listNotifications() {
|
||||||
|
return new GHNotificationStream(this,"/notifications");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This provides a dump of every public repository, in the order that they were created.
|
||||||
|
* @see <a href="https://developer.github.com/v3/repos/#list-all-public-repositories">documentation</a>
|
||||||
|
*/
|
||||||
|
public PagedIterable<GHRepository> listAllPublicRepositories() {
|
||||||
|
return listAllPublicRepositories(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This provides a dump of every public repository, in the order that they were created.
|
||||||
|
*
|
||||||
|
* @param since
|
||||||
|
* The integer ID of the last Repository that you’ve seen. See {@link GHRepository#getId()}
|
||||||
|
* @see <a href="https://developer.github.com/v3/repos/#list-all-public-repositories">documentation</a>
|
||||||
|
*/
|
||||||
|
public PagedIterable<GHRepository> listAllPublicRepositories(final String since) {
|
||||||
|
return new PagedIterable<GHRepository>() {
|
||||||
|
public PagedIterator<GHRepository> _iterator(int pageSize) {
|
||||||
|
return new PagedIterator<GHRepository>(retrieve().with("since",since).asIterator("/repositories", GHRepository[].class, pageSize)) {
|
||||||
|
@Override
|
||||||
|
protected void wrapUp(GHRepository[] page) {
|
||||||
|
for (GHRepository c : page)
|
||||||
|
c.wrap(GitHub.this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render a Markdown document in raw mode.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* It takes a Markdown document as plaintext and renders it as plain Markdown
|
||||||
|
* without a repository context (just like a README.md file is rendered – this
|
||||||
|
* is the simplest way to preview a readme online).
|
||||||
|
*
|
||||||
|
* @see GHRepository#renderMarkdown(String, MarkdownMode)
|
||||||
|
*/
|
||||||
|
public Reader renderMarkdown(String text) throws IOException {
|
||||||
|
return new InputStreamReader(
|
||||||
|
new Requester(this)
|
||||||
|
.with(new ByteArrayInputStream(text.getBytes("UTF-8")))
|
||||||
|
.contentType("text/plain;charset=UTF-8")
|
||||||
|
.asStream("/markdown/raw"),
|
||||||
|
"UTF-8");
|
||||||
|
}
|
||||||
|
|
||||||
/*package*/ static URL parseURL(String s) {
|
/*package*/ static URL parseURL(String s) {
|
||||||
try {
|
try {
|
||||||
return s==null ? null : new URL(s);
|
return s==null ? null : new URL(s);
|
||||||
@@ -337,14 +684,20 @@ public class GitHub {
|
|||||||
throw new IllegalStateException("Unable to parse the timestamp: "+timestamp);
|
throw new IllegalStateException("Unable to parse the timestamp: "+timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*package*/ static String printDate(Date dt) {
|
||||||
|
return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").format(dt);
|
||||||
|
}
|
||||||
|
|
||||||
/*package*/ static final ObjectMapper MAPPER = new ObjectMapper();
|
/*package*/ static final ObjectMapper MAPPER = new ObjectMapper();
|
||||||
|
|
||||||
private static final String[] TIME_FORMATS = {"yyyy/MM/dd HH:mm:ss ZZZZ","yyyy-MM-dd'T'HH:mm:ss'Z'"};
|
private static final String[] TIME_FORMATS = {"yyyy/MM/dd HH:mm:ss ZZZZ","yyyy-MM-dd'T'HH:mm:ss'Z'"};
|
||||||
|
|
||||||
static {
|
static {
|
||||||
MAPPER.setVisibilityChecker(new Std(NONE, NONE, NONE, NONE, ANY));
|
MAPPER.setVisibilityChecker(new Std(NONE, NONE, NONE, NONE, ANY));
|
||||||
MAPPER.getDeserializationConfig().set(Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final String GITHUB_URL = "https://api.github.com";
|
/* package */ static final String GITHUB_URL = "https://api.github.com";
|
||||||
|
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(GitHub.class.getName());
|
||||||
}
|
}
|
||||||
|
|||||||
203
src/main/java/org/kohsuke/github/GitHubBuilder.java
Normal file
203
src/main/java/org/kohsuke/github/GitHubBuilder.java
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.kohsuke.github.extras.ImpatientHttpConnector;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.Proxy;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures connection details and produces {@link GitHub}.
|
||||||
|
*
|
||||||
|
* @since 1.59
|
||||||
|
*/
|
||||||
|
public class GitHubBuilder {
|
||||||
|
|
||||||
|
// default scoped so unit tests can read them.
|
||||||
|
/* private */ String endpoint = GitHub.GITHUB_URL;
|
||||||
|
/* private */ String user;
|
||||||
|
/* private */ String password;
|
||||||
|
/* private */ String oauthToken;
|
||||||
|
|
||||||
|
private HttpConnector connector;
|
||||||
|
|
||||||
|
private RateLimitHandler rateLimitHandler = RateLimitHandler.WAIT;
|
||||||
|
private AbuseLimitHandler abuseLimitHandler = AbuseLimitHandler.WAIT;
|
||||||
|
|
||||||
|
public GitHubBuilder() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* First check if the credentials are configured using the ~/.github properties file.
|
||||||
|
*
|
||||||
|
* If no user is specified it means there is no configuration present so check the environment instead.
|
||||||
|
*
|
||||||
|
* If there is still no user it means there are no credentials defined and throw an IOException.
|
||||||
|
*
|
||||||
|
* @return the configured Builder from credentials defined on the system or in the environment.
|
||||||
|
*
|
||||||
|
* @throws IOException If there are no credentials defined in the ~/.github properties file or the process environment.
|
||||||
|
*/
|
||||||
|
public static GitHubBuilder fromCredentials() throws IOException {
|
||||||
|
Exception cause = null;
|
||||||
|
GitHubBuilder builder;
|
||||||
|
|
||||||
|
try {
|
||||||
|
builder = fromPropertyFile();
|
||||||
|
|
||||||
|
if (builder.oauthToken != null || builder.user != null)
|
||||||
|
return builder;
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
// fall through
|
||||||
|
cause = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
builder = fromEnvironment();
|
||||||
|
|
||||||
|
if (builder.oauthToken != null || builder.user != null)
|
||||||
|
return builder;
|
||||||
|
else
|
||||||
|
throw (IOException)new IOException("Failed to resolve credentials from ~/.github or the environment.").initCause(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
public static GitHubBuilder fromEnvironment(String loginVariableName, String passwordVariableName, String oauthVariableName) throws IOException {
|
||||||
|
return fromEnvironment(loginVariableName, passwordVariableName, oauthVariableName, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void loadIfSet(String envName, Properties p, String propName) {
|
||||||
|
String v = System.getenv(envName);
|
||||||
|
if (v != null)
|
||||||
|
p.put(propName, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
public static GitHubBuilder fromEnvironment(String loginVariableName, String passwordVariableName, String oauthVariableName, String endpointVariableName) throws IOException {
|
||||||
|
Properties env = new Properties();
|
||||||
|
loadIfSet(loginVariableName,env,"login");
|
||||||
|
loadIfSet(passwordVariableName,env,"password");
|
||||||
|
loadIfSet(oauthVariableName,env,"oauth");
|
||||||
|
loadIfSet(endpointVariableName,env,"endpoint");
|
||||||
|
return fromProperties(env);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates {@link GitHubBuilder} by picking up coordinates from environment variables.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The following environment variables are recognized:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>GITHUB_LOGIN: username like 'kohsuke'
|
||||||
|
* <li>GITHUB_PASSWORD: raw password
|
||||||
|
* <li>GITHUB_OAUTH: OAuth token to login
|
||||||
|
* <li>GITHUB_ENDPOINT: URL of the API endpoint
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* See class javadoc for the relationship between these coordinates.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* For backward compatibility, the following environment variables are recognized but discouraged:
|
||||||
|
* login, password, oauth
|
||||||
|
*/
|
||||||
|
public static GitHubBuilder fromEnvironment() throws IOException {
|
||||||
|
Properties props = new Properties();
|
||||||
|
for (Entry<String, String> e : System.getenv().entrySet()) {
|
||||||
|
String name = e.getKey().toLowerCase(Locale.ENGLISH);
|
||||||
|
if (name.startsWith("github_")) name=name.substring(7);
|
||||||
|
props.put(name,e.getValue());
|
||||||
|
}
|
||||||
|
return fromProperties(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GitHubBuilder fromPropertyFile() throws IOException {
|
||||||
|
File homeDir = new File(System.getProperty("user.home"));
|
||||||
|
File propertyFile = new File(homeDir, ".github");
|
||||||
|
return fromPropertyFile(propertyFile.getPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GitHubBuilder fromPropertyFile(String propertyFileName) throws IOException {
|
||||||
|
Properties props = new Properties();
|
||||||
|
FileInputStream in = null;
|
||||||
|
try {
|
||||||
|
in = new FileInputStream(propertyFileName);
|
||||||
|
props.load(in);
|
||||||
|
} finally {
|
||||||
|
IOUtils.closeQuietly(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fromProperties(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GitHubBuilder fromProperties(Properties props) {
|
||||||
|
GitHubBuilder self = new GitHubBuilder();
|
||||||
|
self.withOAuthToken(props.getProperty("oauth"), props.getProperty("login"));
|
||||||
|
self.withPassword(props.getProperty("login"), props.getProperty("password"));
|
||||||
|
self.withEndpoint(props.getProperty("endpoint", GitHub.GITHUB_URL));
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GitHubBuilder withEndpoint(String endpoint) {
|
||||||
|
this.endpoint = endpoint;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public GitHubBuilder withPassword(String user, String password) {
|
||||||
|
this.user = user;
|
||||||
|
this.password = password;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public GitHubBuilder withOAuthToken(String oauthToken) {
|
||||||
|
return withOAuthToken(oauthToken, null);
|
||||||
|
}
|
||||||
|
public GitHubBuilder withOAuthToken(String oauthToken, String user) {
|
||||||
|
this.oauthToken = oauthToken;
|
||||||
|
this.user = user;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public GitHubBuilder withConnector(HttpConnector connector) {
|
||||||
|
this.connector = connector;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public GitHubBuilder withRateLimitHandler(RateLimitHandler handler) {
|
||||||
|
this.rateLimitHandler = handler;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public GitHubBuilder withAbuseLimitHandler(AbuseLimitHandler handler) {
|
||||||
|
this.abuseLimitHandler = handler;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures {@linkplain #withConnector(HttpConnector) connector}
|
||||||
|
* that uses HTTP library in JRE but use a specific proxy, instead of
|
||||||
|
* the system default one.
|
||||||
|
*/
|
||||||
|
public GitHubBuilder withProxy(final Proxy p) {
|
||||||
|
return withConnector(new ImpatientHttpConnector(new HttpConnector() {
|
||||||
|
public HttpURLConnection connect(URL url) throws IOException {
|
||||||
|
return (HttpURLConnection) url.openConnection(p);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
public GitHub build() throws IOException {
|
||||||
|
return new GitHub(endpoint, user, oauthToken, password, connector, rateLimitHandler, abuseLimitHandler);
|
||||||
|
}
|
||||||
|
}
|
||||||
41
src/main/java/org/kohsuke/github/GitUser.java
Normal file
41
src/main/java/org/kohsuke/github/GitUser.java
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a user in Git who authors/commits a commit.
|
||||||
|
*
|
||||||
|
* In contrast, {@link GHUser} is an user of GitHub. Because Git allows a person to
|
||||||
|
* use multiple e-mail addresses and names when creating a commit, there's generally
|
||||||
|
* no meaningful mapping between {@link GHUser} and {@link GitUser}.
|
||||||
|
*
|
||||||
|
* @author Kohsuke Kawaguchi
|
||||||
|
*/
|
||||||
|
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
|
||||||
|
"NP_UNWRITTEN_FIELD"}, justification = "JSON API")
|
||||||
|
public class GitUser {
|
||||||
|
private String name, email, date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Human readable name of the user, such as "Kohsuke Kawaguchi"
|
||||||
|
*/
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* E-mail address, such as "foo@example.com"
|
||||||
|
*/
|
||||||
|
public String getEmail() {
|
||||||
|
return email;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This field doesn't appear to be consistently available in all the situations where this class
|
||||||
|
* is used.
|
||||||
|
*/
|
||||||
|
public Date getDate() {
|
||||||
|
return GitHub.parseDate(date);
|
||||||
|
}
|
||||||
|
}
|
||||||
32
src/main/java/org/kohsuke/github/HttpConnector.java
Normal file
32
src/main/java/org/kohsuke/github/HttpConnector.java
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import org.kohsuke.github.extras.ImpatientHttpConnector;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pluggability for customizing HTTP request behaviors or using altogether different library.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* For example, you can implement this to st custom timeouts.
|
||||||
|
*
|
||||||
|
* @author Kohsuke Kawaguchi
|
||||||
|
*/
|
||||||
|
public interface HttpConnector {
|
||||||
|
/**
|
||||||
|
* Opens a connection to the given URL.
|
||||||
|
*/
|
||||||
|
HttpURLConnection connect(URL url) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default implementation that uses {@link URL#openConnection()}.
|
||||||
|
*/
|
||||||
|
HttpConnector DEFAULT = new ImpatientHttpConnector(new HttpConnector() {
|
||||||
|
public HttpURLConnection connect(URL url) throws IOException {
|
||||||
|
return (HttpURLConnection) url.openConnection();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
118
src/main/java/org/kohsuke/github/HttpException.java
Normal file
118
src/main/java/org/kohsuke/github/HttpException.java
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
import javax.annotation.CheckForNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link IOException} for http exceptions because {@link HttpURLConnection} throws un-discerned
|
||||||
|
* {@link IOException} and it can help to know the http response code to decide how to handle an
|
||||||
|
* http exceptions.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:cleclerc@cloudbees.com">Cyrille Le Clerc</a>
|
||||||
|
*/
|
||||||
|
public class HttpException extends IOException {
|
||||||
|
static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private final int responseCode;
|
||||||
|
private final String responseMessage;
|
||||||
|
private final String url;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param message The detail message (which is saved for later retrieval
|
||||||
|
* by the {@link #getMessage()} method)
|
||||||
|
* @param responseCode Http response code. {@code -1} if no code can be discerned.
|
||||||
|
* @param responseMessage Http response message
|
||||||
|
* @param url The url that was invoked
|
||||||
|
* @see HttpURLConnection#getResponseCode()
|
||||||
|
* @see HttpURLConnection#getResponseMessage()
|
||||||
|
*/
|
||||||
|
public HttpException(String message, int responseCode, String responseMessage, String url) {
|
||||||
|
super(message);
|
||||||
|
this.responseCode = responseCode;
|
||||||
|
this.responseMessage = responseMessage;
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param message The detail message (which is saved for later retrieval
|
||||||
|
* by the {@link #getMessage()} method)
|
||||||
|
* @param responseCode Http response code. {@code -1} if no code can be discerned.
|
||||||
|
* @param responseMessage Http response message
|
||||||
|
* @param url The url that was invoked
|
||||||
|
* @param cause The cause (which is saved for later retrieval by the
|
||||||
|
* {@link #getCause()} method). (A null value is permitted,
|
||||||
|
* and indicates that the cause is nonexistent or unknown.)
|
||||||
|
* @see HttpURLConnection#getResponseCode()
|
||||||
|
* @see HttpURLConnection#getResponseMessage()
|
||||||
|
*/
|
||||||
|
public HttpException(String message, int responseCode, String responseMessage, String url, Throwable cause) {
|
||||||
|
super(message);
|
||||||
|
initCause(cause);
|
||||||
|
this.responseCode = responseCode;
|
||||||
|
this.responseMessage = responseMessage;
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param responseCode Http response code. {@code -1} if no code can be discerned.
|
||||||
|
* @param responseMessage Http response message
|
||||||
|
* @param url The url that was invoked
|
||||||
|
* @param cause The cause (which is saved for later retrieval by the
|
||||||
|
* {@link #getCause()} method). (A null value is permitted,
|
||||||
|
* and indicates that the cause is nonexistent or unknown.)
|
||||||
|
* @see HttpURLConnection#getResponseCode()
|
||||||
|
* @see HttpURLConnection#getResponseMessage()
|
||||||
|
*/
|
||||||
|
public HttpException(int responseCode, String responseMessage, String url, Throwable cause) {
|
||||||
|
super("Server returned HTTP response code: " + responseCode + ", message: '" + responseMessage + "'" +
|
||||||
|
" for URL: " + url);
|
||||||
|
initCause(cause);
|
||||||
|
this.responseCode = responseCode;
|
||||||
|
this.responseMessage = responseMessage;
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param responseCode Http response code. {@code -1} if no code can be discerned.
|
||||||
|
* @param responseMessage Http response message
|
||||||
|
* @param url The url that was invoked
|
||||||
|
* @param cause The cause (which is saved for later retrieval by the
|
||||||
|
* {@link #getCause()} method). (A null value is permitted,
|
||||||
|
* and indicates that the cause is nonexistent or unknown.)
|
||||||
|
* @see HttpURLConnection#getResponseCode()
|
||||||
|
* @see HttpURLConnection#getResponseMessage()
|
||||||
|
*/
|
||||||
|
public HttpException(int responseCode, String responseMessage, @CheckForNull URL url, Throwable cause) {
|
||||||
|
this(responseCode, responseMessage, url == null ? null : url.toString(), cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Http response code of the request that cause the exception
|
||||||
|
*
|
||||||
|
* @return {@code -1} if no code can be discerned.
|
||||||
|
*/
|
||||||
|
public int getResponseCode() {
|
||||||
|
return responseCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Http response message of the request that cause the exception
|
||||||
|
*
|
||||||
|
* @return {@code null} if no response message can be discerned.
|
||||||
|
*/
|
||||||
|
public String getResponseMessage() {
|
||||||
|
return responseMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The http URL that caused the exception
|
||||||
|
*
|
||||||
|
* @return url
|
||||||
|
*/
|
||||||
|
public String getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
}
|
||||||
29
src/main/java/org/kohsuke/github/MarkdownMode.java
Normal file
29
src/main/java/org/kohsuke/github/MarkdownMode.java
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rendering mode of markdown.
|
||||||
|
*
|
||||||
|
* @author Kohsuke Kawaguchi
|
||||||
|
* @see GitHub#renderMarkdown(String)
|
||||||
|
* @see GHRepository#renderMarkdown(String, MarkdownMode)
|
||||||
|
*/
|
||||||
|
public enum MarkdownMode {
|
||||||
|
/**
|
||||||
|
* Render a document as plain Markdown, just like README files are rendered.
|
||||||
|
*/
|
||||||
|
MARKDOWN,
|
||||||
|
/**
|
||||||
|
* Render a document as user-content, e.g. like user comments or issues are rendered.
|
||||||
|
* In GFM mode, hard line breaks are always taken into account, and issue and user
|
||||||
|
* mentions are linked accordingly.
|
||||||
|
*
|
||||||
|
* @see GHRepository#renderMarkdown(String, MarkdownMode)
|
||||||
|
*/
|
||||||
|
GFM;
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return name().toLowerCase(Locale.ENGLISH);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
package org.kohsuke.github;
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link Iterable} that returns {@link PagedIterator}
|
* {@link Iterable} that returns {@link PagedIterator}
|
||||||
@@ -9,7 +12,27 @@ import java.util.List;
|
|||||||
* @author Kohsuke Kawaguchi
|
* @author Kohsuke Kawaguchi
|
||||||
*/
|
*/
|
||||||
public abstract class PagedIterable<T> implements Iterable<T> {
|
public abstract class PagedIterable<T> implements Iterable<T> {
|
||||||
public abstract PagedIterator<T> iterator();
|
/**
|
||||||
|
* Page size. 0 is default.
|
||||||
|
*/
|
||||||
|
private int size = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the pagination size.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* When set to non-zero, each API call will retrieve this many entries.
|
||||||
|
*/
|
||||||
|
public PagedIterable<T> withPageSize(int size) {
|
||||||
|
this.size = size;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final PagedIterator<T> iterator() {
|
||||||
|
return _iterator(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract PagedIterator<T> _iterator(int pageSize);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Eagerly walk {@link Iterable} and return the result in a list.
|
* Eagerly walk {@link Iterable} and return the result in a list.
|
||||||
@@ -21,4 +44,15 @@ public abstract class PagedIterable<T> implements Iterable<T> {
|
|||||||
}
|
}
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Eagerly walk {@link Iterable} and return the result in a set.
|
||||||
|
*/
|
||||||
|
public Set<T> asSet() {
|
||||||
|
LinkedHashSet<T> r = new LinkedHashSet<T>();
|
||||||
|
for(PagedIterator<T> i = iterator(); i.hasNext();) {
|
||||||
|
r.addAll(i.nextPage());
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
70
src/main/java/org/kohsuke/github/PagedSearchIterable.java
Normal file
70
src/main/java/org/kohsuke/github/PagedSearchIterable.java
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link PagedIterable} enhanced to report search result specific information.
|
||||||
|
*
|
||||||
|
* @author Kohsuke Kawaguchi
|
||||||
|
*/
|
||||||
|
@SuppressFBWarnings(value = {"UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD",
|
||||||
|
"UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR"}, justification = "Constructed by JSON API")
|
||||||
|
public abstract class PagedSearchIterable<T> extends PagedIterable<T> {
|
||||||
|
private final GitHub root;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* As soon as we have any result fetched, it's set here so that we can report the total count.
|
||||||
|
*/
|
||||||
|
private SearchResult<T> result;
|
||||||
|
|
||||||
|
/*package*/ PagedSearchIterable(GitHub root) {
|
||||||
|
this.root = root;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PagedSearchIterable<T> withPageSize(int size) {
|
||||||
|
return (PagedSearchIterable<T>)super.withPageSize(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the total number of hit, including the results that's not yet fetched.
|
||||||
|
*/
|
||||||
|
public int getTotalCount() {
|
||||||
|
populate();
|
||||||
|
return result.total_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isIncomplete() {
|
||||||
|
populate();
|
||||||
|
return result.incomplete_results;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void populate() {
|
||||||
|
if (result==null)
|
||||||
|
iterator().hasNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapts {@link Iterator}.
|
||||||
|
*/
|
||||||
|
protected Iterator<T[]> adapt(final Iterator<? extends SearchResult<T>> base) {
|
||||||
|
return new Iterator<T[]>() {
|
||||||
|
public boolean hasNext() {
|
||||||
|
return base.hasNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
public T[] next() {
|
||||||
|
SearchResult<T> v = base.next();
|
||||||
|
if (result==null) result = v;
|
||||||
|
return v.getItems(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
18
src/main/java/org/kohsuke/github/Preview.java
Normal file
18
src/main/java/org/kohsuke/github/Preview.java
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that the method/class/etc marked maps to GitHub API in the preview period.
|
||||||
|
*
|
||||||
|
* These APIs are subject to change and not a part of the backward compatibility commitment.
|
||||||
|
* Always used in conjunction with 'deprecated' to raise awareness to clients.
|
||||||
|
*
|
||||||
|
* @author Kohsuke Kawaguchi
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
public @interface Preview {
|
||||||
|
}
|
||||||
9
src/main/java/org/kohsuke/github/Previews.java
Normal file
9
src/main/java/org/kohsuke/github/Previews.java
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Kohsuke Kawaguchi
|
||||||
|
*/
|
||||||
|
/*package*/ class Previews {
|
||||||
|
static final String LOKI = "application/vnd.github.loki-preview+json";
|
||||||
|
static final String DRAX = "application/vnd.github.drax-preview+json";
|
||||||
|
}
|
||||||
62
src/main/java/org/kohsuke/github/RateLimitHandler.java
Normal file
62
src/main/java/org/kohsuke/github/RateLimitHandler.java
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InterruptedIOException;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pluggable strategy to determine what to do when the API rate limit is reached.
|
||||||
|
*
|
||||||
|
* @author Kohsuke Kawaguchi
|
||||||
|
* @see GitHubBuilder#withRateLimitHandler(RateLimitHandler)
|
||||||
|
* @see AbuseLimitHandler
|
||||||
|
*/
|
||||||
|
public abstract class RateLimitHandler {
|
||||||
|
/**
|
||||||
|
* Called when the library encounters HTTP error indicating that the API rate limit is reached.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Any exception thrown from this method will cause the request to fail, and the caller of github-api
|
||||||
|
* will receive an exception. If this method returns normally, another request will be attempted.
|
||||||
|
* For that to make sense, the implementation needs to wait for some time.
|
||||||
|
*
|
||||||
|
* @see <a href="https://developer.github.com/v3/#rate-limiting">API documentation from GitHub</a>
|
||||||
|
* @param e
|
||||||
|
* Exception from Java I/O layer. If you decide to fail the processing, you can throw
|
||||||
|
* this exception (or wrap this exception into another exception and throw it.)
|
||||||
|
* @param uc
|
||||||
|
* Connection that resulted in an error. Useful for accessing other response headers.
|
||||||
|
*/
|
||||||
|
public abstract void onError(IOException e, HttpURLConnection uc) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Block until the API rate limit is reset. Useful for long-running batch processing.
|
||||||
|
*/
|
||||||
|
public static final RateLimitHandler WAIT = new RateLimitHandler() {
|
||||||
|
@Override
|
||||||
|
public void onError(IOException e, HttpURLConnection uc) throws IOException {
|
||||||
|
try {
|
||||||
|
Thread.sleep(parseWaitTime(uc));
|
||||||
|
} catch (InterruptedException _) {
|
||||||
|
throw (InterruptedIOException)new InterruptedIOException().initCause(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private long parseWaitTime(HttpURLConnection uc) {
|
||||||
|
String v = uc.getHeaderField("X-RateLimit-Reset");
|
||||||
|
if (v==null) return 10000; // can't tell
|
||||||
|
|
||||||
|
return Math.max(10000, Long.parseLong(v)*1000 - System.currentTimeMillis());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fail immediately.
|
||||||
|
*/
|
||||||
|
public static final RateLimitHandler FAIL = new RateLimitHandler() {
|
||||||
|
@Override
|
||||||
|
public void onError(IOException e, HttpURLConnection uc) throws IOException {
|
||||||
|
throw (IOException)new IOException("API rate limit reached").initCause(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -23,28 +23,41 @@
|
|||||||
*/
|
*/
|
||||||
package org.kohsuke.github;
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||||
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.InterruptedIOException;
|
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.lang.reflect.Array;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.ProtocolException;
|
import java.net.ProtocolException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.net.URLEncoder;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
import java.util.zip.GZIPInputStream;
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
import javax.annotation.WillClose;
|
||||||
|
|
||||||
|
import static java.util.Arrays.asList;
|
||||||
|
import static java.util.logging.Level.FINE;
|
||||||
import static org.kohsuke.github.GitHub.*;
|
import static org.kohsuke.github.GitHub.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -55,11 +68,19 @@ import static org.kohsuke.github.GitHub.*;
|
|||||||
class Requester {
|
class Requester {
|
||||||
private final GitHub root;
|
private final GitHub root;
|
||||||
private final List<Entry> args = new ArrayList<Entry>();
|
private final List<Entry> args = new ArrayList<Entry>();
|
||||||
|
private final Map<String,String> headers = new LinkedHashMap<String, String>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request method.
|
* Request method.
|
||||||
*/
|
*/
|
||||||
private String method = "POST";
|
private String method = "POST";
|
||||||
|
private String contentType = "application/x-www-form-urlencoded";
|
||||||
|
private InputStream body;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current connection.
|
||||||
|
*/
|
||||||
|
private HttpURLConnection uc;
|
||||||
|
|
||||||
private static class Entry {
|
private static class Entry {
|
||||||
String key;
|
String key;
|
||||||
@@ -75,6 +96,24 @@ class Requester {
|
|||||||
this.root = root;
|
this.root = root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the request HTTP header.
|
||||||
|
*
|
||||||
|
* If a header of the same name is already set, this method overrides it.
|
||||||
|
*/
|
||||||
|
public void setHeader(String name, String value) {
|
||||||
|
headers.put(name,value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Requester withHeader(String name, String value) {
|
||||||
|
setHeader(name,value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*package*/ Requester withPreview(String name) {
|
||||||
|
return withHeader("Accept",name);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes a request with authentication credential.
|
* Makes a request with authentication credential.
|
||||||
*/
|
*/
|
||||||
@@ -91,13 +130,25 @@ class Requester {
|
|||||||
|
|
||||||
public Requester with(String key, Integer value) {
|
public Requester with(String key, Integer value) {
|
||||||
if (value!=null)
|
if (value!=null)
|
||||||
_with(key, value.intValue());
|
_with(key, value);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Requester with(String key, boolean value) {
|
public Requester with(String key, boolean value) {
|
||||||
return _with(key, value);
|
return _with(key, value);
|
||||||
}
|
}
|
||||||
|
public Requester with(String key, Boolean value) {
|
||||||
|
return _with(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Requester with(String key, Enum e) {
|
||||||
|
if (e==null) return _with(key, null);
|
||||||
|
|
||||||
|
// by convention Java constant names are upper cases, but github uses
|
||||||
|
// lower-case constants. GitHub also uses '-', which in Java we always
|
||||||
|
// replace by '_'
|
||||||
|
return with(key, e.toString().toLowerCase(Locale.ENGLISH).replace('_', '-'));
|
||||||
|
}
|
||||||
|
|
||||||
public Requester with(String key, String value) {
|
public Requester with(String key, String value) {
|
||||||
return _with(key, value);
|
return _with(key, value);
|
||||||
@@ -111,6 +162,11 @@ class Requester {
|
|||||||
return _with(key, value);
|
return _with(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Requester with(@WillClose/*later*/ InputStream body) {
|
||||||
|
this.body = body;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public Requester _with(String key, Object value) {
|
public Requester _with(String key, Object value) {
|
||||||
if (value!=null) {
|
if (value!=null) {
|
||||||
args.add(new Entry(key,value));
|
args.add(new Entry(key,value));
|
||||||
@@ -118,11 +174,29 @@ class Requester {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unlike {@link #with(String, String)}, overrides the existing value
|
||||||
|
*/
|
||||||
|
public Requester set(String key, Object value) {
|
||||||
|
for (Entry e : args) {
|
||||||
|
if (e.key.equals(key)) {
|
||||||
|
e.value = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _with(key,value);
|
||||||
|
}
|
||||||
|
|
||||||
public Requester method(String method) {
|
public Requester method(String method) {
|
||||||
this.method = method;
|
this.method = method;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Requester contentType(String contentType) {
|
||||||
|
this.contentType = contentType;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public void to(String tailApiUrl) throws IOException {
|
public void to(String tailApiUrl) throws IOException {
|
||||||
to(tailApiUrl,null);
|
to(tailApiUrl,null);
|
||||||
}
|
}
|
||||||
@@ -151,130 +225,257 @@ class Requester {
|
|||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public <T> T to(String tailApiUrl, Class<T> type, String method) throws IOException {
|
public <T> T to(String tailApiUrl, Class<T> type, String method) throws IOException {
|
||||||
return method(method).to(tailApiUrl,type);
|
return method(method).to(tailApiUrl, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressFBWarnings("SBSC_USE_STRINGBUFFER_CONCATENATION")
|
||||||
private <T> T _to(String tailApiUrl, Class<T> type, T instance) throws IOException {
|
private <T> T _to(String tailApiUrl, Class<T> type, T instance) throws IOException {
|
||||||
|
if (METHODS_WITHOUT_BODY.contains(method) && !args.isEmpty()) {
|
||||||
|
boolean questionMarkFound = tailApiUrl.indexOf('?') != -1;
|
||||||
|
tailApiUrl += questionMarkFound ? '&' : '?';
|
||||||
|
for (Iterator<Entry> it = args.listIterator(); it.hasNext();) {
|
||||||
|
Entry arg = it.next();
|
||||||
|
tailApiUrl += arg.key + '=' + URLEncoder.encode(arg.value.toString(),"UTF-8");
|
||||||
|
if (it.hasNext()) {
|
||||||
|
tailApiUrl += '&';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
while (true) {// loop while API rate limit is hit
|
while (true) {// loop while API rate limit is hit
|
||||||
HttpURLConnection uc = setupConnection(root.getApiURL(tailApiUrl));
|
setupConnection(root.getApiURL(tailApiUrl));
|
||||||
|
|
||||||
if (!method.equals("GET")) {
|
buildRequest();
|
||||||
uc.setDoOutput(true);
|
|
||||||
uc.setRequestProperty("Content-type","application/x-www-form-urlencoded");
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
T result = parse(type, instance);
|
||||||
|
if (type != null && type.isArray()) { // we might have to loop for pagination - done through recursion
|
||||||
|
final String links = uc.getHeaderField("link");
|
||||||
|
if (links != null && links.contains("rel=\"next\"")) {
|
||||||
|
Pattern nextLinkPattern = Pattern.compile(".*<(.*)>; rel=\"next\"");
|
||||||
|
Matcher nextLinkMatcher = nextLinkPattern.matcher(links);
|
||||||
|
if (nextLinkMatcher.find()) {
|
||||||
|
final String link = nextLinkMatcher.group(1);
|
||||||
|
T nextResult = _to(link, type, instance);
|
||||||
|
|
||||||
|
final int resultLength = Array.getLength(result);
|
||||||
|
final int nextResultLength = Array.getLength(nextResult);
|
||||||
|
T concatResult = (T) Array.newInstance(type.getComponentType(), resultLength + nextResultLength);
|
||||||
|
System.arraycopy(result, 0, concatResult, 0, resultLength);
|
||||||
|
System.arraycopy(nextResult, 0, concatResult, resultLength, nextResultLength);
|
||||||
|
result = concatResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} catch (IOException e) {
|
||||||
|
handleApiError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes a request and just obtains the HTTP status code.
|
||||||
|
*/
|
||||||
|
public int asHttpStatusCode(String tailApiUrl) throws IOException {
|
||||||
|
while (true) {// loop while API rate limit is hit
|
||||||
|
method("GET");
|
||||||
|
setupConnection(root.getApiURL(tailApiUrl));
|
||||||
|
|
||||||
|
buildRequest();
|
||||||
|
|
||||||
|
try {
|
||||||
|
return uc.getResponseCode();
|
||||||
|
} catch (IOException e) {
|
||||||
|
handleApiError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputStream asStream(String tailApiUrl) throws IOException {
|
||||||
|
while (true) {// loop while API rate limit is hit
|
||||||
|
setupConnection(root.getApiURL(tailApiUrl));
|
||||||
|
|
||||||
|
buildRequest();
|
||||||
|
|
||||||
|
try {
|
||||||
|
return wrapStream(uc.getInputStream());
|
||||||
|
} catch (IOException e) {
|
||||||
|
handleApiError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getResponseHeader(String header) {
|
||||||
|
return uc.getHeaderField(header);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set up the request parameters or POST payload.
|
||||||
|
*/
|
||||||
|
private void buildRequest() throws IOException {
|
||||||
|
if (isMethodWithBody()) {
|
||||||
|
uc.setDoOutput(true);
|
||||||
|
uc.setRequestProperty("Content-type", contentType);
|
||||||
|
|
||||||
|
if (body == null) {
|
||||||
Map json = new HashMap();
|
Map json = new HashMap();
|
||||||
for (Entry e : args) {
|
for (Entry e : args) {
|
||||||
json.put(e.key, e.value);
|
json.put(e.key, e.value);
|
||||||
}
|
}
|
||||||
MAPPER.writeValue(uc.getOutputStream(),json);
|
MAPPER.writeValue(uc.getOutputStream(), json);
|
||||||
}
|
} else {
|
||||||
|
try {
|
||||||
try {
|
byte[] bytes = new byte[32768];
|
||||||
return parse(uc,type,instance);
|
int read = 0;
|
||||||
} catch (IOException e) {
|
while ((read = body.read(bytes)) != -1) {
|
||||||
handleApiError(e,uc);
|
uc.getOutputStream().write(bytes, 0, read);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
body.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isMethodWithBody() {
|
||||||
|
return !METHODS_WITHOUT_BODY.contains(method);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads pagenated resources.
|
* Loads pagenated resources.
|
||||||
*
|
*
|
||||||
* Every iterator call reports a new batch.
|
* Every iterator call reports a new batch.
|
||||||
*/
|
*/
|
||||||
/*package*/ <T> Iterator<T> asIterator(final String tailApiUrl, final Class<T> type) {
|
/*package*/ <T> Iterator<T> asIterator(String tailApiUrl, Class<T> type, int pageSize) {
|
||||||
method("GET");
|
method("GET");
|
||||||
if (!args.isEmpty()) throw new IllegalStateException();
|
|
||||||
|
|
||||||
return new Iterator<T>() {
|
if (pageSize!=0)
|
||||||
/**
|
args.add(new Entry("per_page",pageSize));
|
||||||
* The next batch to be returned from {@link #next()}.
|
|
||||||
*/
|
|
||||||
T next;
|
|
||||||
/**
|
|
||||||
* URL of the next resource to be retrieved, or null if no more data is available.
|
|
||||||
*/
|
|
||||||
URL url;
|
|
||||||
|
|
||||||
{
|
StringBuilder s = new StringBuilder(tailApiUrl);
|
||||||
try {
|
if (!args.isEmpty()) {
|
||||||
url = root.getApiURL(tailApiUrl);
|
boolean first = true;
|
||||||
} catch (IOException e) {
|
try {
|
||||||
throw new Error(e);
|
for (Entry a : args) {
|
||||||
|
s.append(first ? '?' : '&');
|
||||||
|
first = false;
|
||||||
|
s.append(URLEncoder.encode(a.key, "UTF-8"));
|
||||||
|
s.append('=');
|
||||||
|
s.append(URLEncoder.encode(a.value.toString(), "UTF-8"));
|
||||||
}
|
}
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new AssertionError(e); // UTF-8 is mandatory
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public boolean hasNext() {
|
try {
|
||||||
fetch();
|
return new PagingIterator<T>(type, root.getApiURL(s.toString()));
|
||||||
return next!=null;
|
} catch (IOException e) {
|
||||||
}
|
throw new Error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public T next() {
|
class PagingIterator<T> implements Iterator<T> {
|
||||||
fetch();
|
|
||||||
T r = next;
|
|
||||||
if (r==null) throw new NoSuchElementException();
|
|
||||||
next = null;
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void remove() {
|
private final Class<T> type;
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void fetch() {
|
/**
|
||||||
if (next!=null) return; // already fetched
|
* The next batch to be returned from {@link #next()}.
|
||||||
if (url==null) return; // no more data to fetch
|
*/
|
||||||
|
private T next;
|
||||||
|
|
||||||
try {
|
/**
|
||||||
while (true) {// loop while API rate limit is hit
|
* URL of the next resource to be retrieved, or null if no more data is available.
|
||||||
HttpURLConnection uc = setupConnection(url);
|
*/
|
||||||
try {
|
private URL url;
|
||||||
next = parse(uc,type,null);
|
|
||||||
assert next!=null;
|
|
||||||
findNextURL(uc);
|
|
||||||
return;
|
|
||||||
} catch (IOException e) {
|
|
||||||
handleApiError(e,uc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new Error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
PagingIterator(Class<T> type, URL url) {
|
||||||
* Locate the next page from the pagination "Link" tag.
|
this.url = url;
|
||||||
*/
|
this.type = type;
|
||||||
private void findNextURL(HttpURLConnection uc) throws MalformedURLException {
|
}
|
||||||
url = null; // start defensively
|
|
||||||
String link = uc.getHeaderField("Link");
|
|
||||||
if (link==null) return;
|
|
||||||
|
|
||||||
for (String token : link.split(", ")) {
|
public boolean hasNext() {
|
||||||
if (token.endsWith("rel=\"next\"")) {
|
fetch();
|
||||||
// found the next page. This should look something like
|
return next!=null;
|
||||||
// <https://api.github.com/repos?page=3&per_page=100>; rel="next"
|
}
|
||||||
int idx = token.indexOf('>');
|
|
||||||
url = new URL(token.substring(1,idx));
|
public T next() {
|
||||||
|
fetch();
|
||||||
|
T r = next;
|
||||||
|
if (r==null) throw new NoSuchElementException();
|
||||||
|
next = null;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fetch() {
|
||||||
|
if (next!=null) return; // already fetched
|
||||||
|
if (url==null) return; // no more data to fetch
|
||||||
|
|
||||||
|
try {
|
||||||
|
while (true) {// loop while API rate limit is hit
|
||||||
|
setupConnection(url);
|
||||||
|
try {
|
||||||
|
next = parse(type,null);
|
||||||
|
assert next!=null;
|
||||||
|
findNextURL();
|
||||||
return;
|
return;
|
||||||
|
} catch (IOException e) {
|
||||||
|
handleApiError(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
// no more "next" link. we are done.
|
throw new Error(e);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locate the next page from the pagination "Link" tag.
|
||||||
|
*/
|
||||||
|
private void findNextURL() throws MalformedURLException {
|
||||||
|
url = null; // start defensively
|
||||||
|
String link = uc.getHeaderField("Link");
|
||||||
|
if (link==null) return;
|
||||||
|
|
||||||
|
for (String token : link.split(", ")) {
|
||||||
|
if (token.endsWith("rel=\"next\"")) {
|
||||||
|
// found the next page. This should look something like
|
||||||
|
// <https://api.github.com/repos?page=3&per_page=100>; rel="next"
|
||||||
|
int idx = token.indexOf('>');
|
||||||
|
url = new URL(token.substring(1,idx));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// no more "next" link. we are done.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private HttpURLConnection setupConnection(URL url) throws IOException {
|
private void setupConnection(URL url) throws IOException {
|
||||||
HttpURLConnection uc = (HttpURLConnection) url.openConnection();
|
uc = root.getConnector().connect(url);
|
||||||
|
|
||||||
// if the authentication is needed but no credential is given, try it anyway (so that some calls
|
// 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.)
|
// that do work with anonymous access in the reduced form should still work.)
|
||||||
// if OAuth token is present, it'll be set in the URL, so need to set the Authorization header
|
if (root.encodedAuthorization!=null)
|
||||||
if (root.encodedAuthorization!=null && root.oauthAccessToken == null)
|
uc.setRequestProperty("Authorization", root.encodedAuthorization);
|
||||||
uc.setRequestProperty("Authorization", "Basic " + root.encodedAuthorization);
|
|
||||||
|
|
||||||
|
for (Map.Entry<String, String> e : headers.entrySet()) {
|
||||||
|
String v = e.getValue();
|
||||||
|
if (v!=null)
|
||||||
|
uc.setRequestProperty(e.getKey(), v);
|
||||||
|
}
|
||||||
|
|
||||||
|
setRequestMethod(uc);
|
||||||
|
uc.setRequestProperty("Accept-Encoding", "gzip");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setRequestMethod(HttpURLConnection uc) throws IOException {
|
||||||
try {
|
try {
|
||||||
uc.setRequestMethod(method);
|
uc.setRequestMethod(method);
|
||||||
} catch (ProtocolException e) {
|
} catch (ProtocolException e) {
|
||||||
@@ -286,21 +487,57 @@ class Requester {
|
|||||||
} catch (Exception x) {
|
} catch (Exception x) {
|
||||||
throw (IOException)new IOException("Failed to set the custom verb").initCause(x);
|
throw (IOException)new IOException("Failed to set the custom verb").initCause(x);
|
||||||
}
|
}
|
||||||
|
// sun.net.www.protocol.https.DelegatingHttpsURLConnection delegates to another HttpURLConnection
|
||||||
|
try {
|
||||||
|
Field $delegate = uc.getClass().getDeclaredField("delegate");
|
||||||
|
$delegate.setAccessible(true);
|
||||||
|
Object delegate = $delegate.get(uc);
|
||||||
|
if (delegate instanceof HttpURLConnection) {
|
||||||
|
HttpURLConnection nested = (HttpURLConnection) delegate;
|
||||||
|
setRequestMethod(nested);
|
||||||
|
}
|
||||||
|
} catch (NoSuchFieldException x) {
|
||||||
|
// no problem
|
||||||
|
} catch (IllegalAccessException x) {
|
||||||
|
throw (IOException)new IOException("Failed to set the custom verb").initCause(x);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
uc.setRequestProperty("Accept-Encoding", "gzip");
|
if (!uc.getRequestMethod().equals(method))
|
||||||
return uc;
|
throw new IllegalStateException("Failed to set the request method to "+method);
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> T parse(HttpURLConnection uc, Class<T> type, T instance) throws IOException {
|
private <T> T parse(Class<T> type, T instance) throws IOException {
|
||||||
InputStreamReader r = null;
|
InputStreamReader r = null;
|
||||||
|
int responseCode = -1;
|
||||||
|
String responseMessage = null;
|
||||||
try {
|
try {
|
||||||
r = new InputStreamReader(wrapStream(uc, uc.getInputStream()), "UTF-8");
|
responseCode = uc.getResponseCode();
|
||||||
|
responseMessage = uc.getResponseMessage();
|
||||||
|
if (responseCode == 304) {
|
||||||
|
return null; // special case handling for 304 unmodified, as the content will be ""
|
||||||
|
}
|
||||||
|
if (responseCode == 204 && type!=null && type.isArray()) {
|
||||||
|
// no content
|
||||||
|
return type.cast(Array.newInstance(type.getComponentType(),0));
|
||||||
|
}
|
||||||
|
|
||||||
|
r = new InputStreamReader(wrapStream(uc.getInputStream()), "UTF-8");
|
||||||
String data = IOUtils.toString(r);
|
String data = IOUtils.toString(r);
|
||||||
if (type!=null)
|
if (type!=null)
|
||||||
return MAPPER.readValue(data,type);
|
try {
|
||||||
|
return MAPPER.readValue(data,type);
|
||||||
|
} catch (JsonMappingException e) {
|
||||||
|
throw (IOException)new IOException("Failed to deserialize " +data).initCause(e);
|
||||||
|
}
|
||||||
if (instance!=null)
|
if (instance!=null)
|
||||||
return MAPPER.readerForUpdating(instance).<T>readValue(data);
|
return MAPPER.readerForUpdating(instance).<T>readValue(data);
|
||||||
return null;
|
return null;
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
// java.net.URLConnection handles 404 exception has FileNotFoundException, don't wrap exception in HttpException
|
||||||
|
// to preserve backward compatibility
|
||||||
|
throw e;
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new HttpException(responseCode, responseMessage, uc.getURL(), e);
|
||||||
} finally {
|
} finally {
|
||||||
IOUtils.closeQuietly(r);
|
IOUtils.closeQuietly(r);
|
||||||
}
|
}
|
||||||
@@ -309,7 +546,7 @@ class Requester {
|
|||||||
/**
|
/**
|
||||||
* Handles the "Content-Encoding" header.
|
* Handles the "Content-Encoding" header.
|
||||||
*/
|
*/
|
||||||
private InputStream wrapStream(HttpURLConnection uc, InputStream in) throws IOException {
|
private InputStream wrapStream(InputStream in) throws IOException {
|
||||||
String encoding = uc.getContentEncoding();
|
String encoding = uc.getContentEncoding();
|
||||||
if (encoding==null || in==null) return in;
|
if (encoding==null || in==null) return in;
|
||||||
if (encoding.equals("gzip")) return new GZIPInputStream(in);
|
if (encoding.equals("gzip")) return new GZIPInputStream(in);
|
||||||
@@ -318,31 +555,50 @@ class Requester {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the error is because of the API limit, wait 10 sec and return normally.
|
* Handle API error by either throwing it or by returning normally to retry.
|
||||||
* Otherwise throw an exception reporting an error.
|
|
||||||
*/
|
*/
|
||||||
/*package*/ void handleApiError(IOException e, HttpURLConnection uc) throws IOException {
|
/*package*/ void handleApiError(IOException e) throws IOException {
|
||||||
|
int responseCode;
|
||||||
|
try {
|
||||||
|
responseCode = uc.getResponseCode();
|
||||||
|
} catch (IOException e2) {
|
||||||
|
// likely to be a network exception (e.g. SSLHandshakeException),
|
||||||
|
// uc.getResponseCode() and any other getter on the response will cause an exception
|
||||||
|
if (LOGGER.isLoggable(FINE))
|
||||||
|
LOGGER.log(FINE, "Silently ignore exception retrieving response code for '" + uc.getURL() + "'" +
|
||||||
|
" handling exception " + e, e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
if (responseCode == HttpURLConnection.HTTP_UNAUTHORIZED) // 401 / Unauthorized == bad creds
|
||||||
|
throw e;
|
||||||
|
|
||||||
if ("0".equals(uc.getHeaderField("X-RateLimit-Remaining"))) {
|
if ("0".equals(uc.getHeaderField("X-RateLimit-Remaining"))) {
|
||||||
// API limit reached. wait 10 secs and return normally
|
root.rateLimitHandler.onError(e,uc);
|
||||||
try {
|
return;
|
||||||
Thread.sleep(10000);
|
|
||||||
return;
|
|
||||||
} catch (InterruptedException _) {
|
|
||||||
throw (InterruptedIOException)new InterruptedIOException().initCause(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e instanceof FileNotFoundException)
|
// Retry-After is not documented but apparently that field exists
|
||||||
throw e; // pass through 404 Not Found to allow the caller to handle it intelligently
|
if (responseCode == HttpURLConnection.HTTP_FORBIDDEN &&
|
||||||
|
uc.getHeaderField("Retry-After") != null) {
|
||||||
|
this.root.abuseLimitHandler.onError(e,uc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
InputStream es = wrapStream(uc, uc.getErrorStream());
|
InputStream es = wrapStream(uc.getErrorStream());
|
||||||
try {
|
try {
|
||||||
if (es!=null)
|
if (es!=null) {
|
||||||
throw (IOException)new IOException(IOUtils.toString(es,"UTF-8")).initCause(e);
|
if (e instanceof FileNotFoundException) {
|
||||||
else
|
// pass through 404 Not Found to allow the caller to handle it intelligently
|
||||||
|
throw (IOException) new FileNotFoundException(IOUtils.toString(es, "UTF-8")).initCause(e);
|
||||||
|
} else
|
||||||
|
throw (IOException) new IOException(IOUtils.toString(es, "UTF-8")).initCause(e);
|
||||||
|
} else
|
||||||
throw e;
|
throw e;
|
||||||
} finally {
|
} finally {
|
||||||
IOUtils.closeQuietly(es);
|
IOUtils.closeQuietly(es);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final List<String> METHODS_WITHOUT_BODY = asList("GET", "DELETE");
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(Requester.class.getName());
|
||||||
}
|
}
|
||||||
|
|||||||
21
src/main/java/org/kohsuke/github/SearchResult.java
Normal file
21
src/main/java/org/kohsuke/github/SearchResult.java
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the result of a search
|
||||||
|
*
|
||||||
|
* @author Kohsuke Kawaguchi
|
||||||
|
*/
|
||||||
|
abstract class SearchResult<T> {
|
||||||
|
@SuppressFBWarnings(value = "UWF_UNWRITTEN_FIELD", justification = "Field comes from JSON deserialization")
|
||||||
|
int total_count;
|
||||||
|
|
||||||
|
@SuppressFBWarnings(value = "UWF_UNWRITTEN_FIELD", justification = "Field comes from JSON deserialization")
|
||||||
|
boolean incomplete_results;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps up the retrieved object and return them. Only called once.
|
||||||
|
*/
|
||||||
|
/*package*/ abstract T[] getItems(GitHub root);
|
||||||
|
}
|
||||||
16
src/main/java/org/kohsuke/github/SkipFromToString.java
Normal file
16
src/main/java/org/kohsuke/github/SkipFromToString.java
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ignores this field for {@link GHObject#toString()}
|
||||||
|
*
|
||||||
|
* @author Kohsuke Kawaguchi
|
||||||
|
*/
|
||||||
|
@Target(ElementType.FIELD)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@interface SkipFromToString {
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package org.kohsuke.github.extras;
|
||||||
|
|
||||||
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||||
|
import org.kohsuke.github.HttpConnector;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link HttpConnector} wrapper that sets timeout
|
||||||
|
*
|
||||||
|
* @author Kohsuke Kawaguchi
|
||||||
|
*/
|
||||||
|
public class ImpatientHttpConnector implements HttpConnector {
|
||||||
|
private final HttpConnector base;
|
||||||
|
private final int readTimeout, connectTimeout;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param connectTimeout
|
||||||
|
* HTTP connection timeout in milliseconds
|
||||||
|
* @param readTimeout
|
||||||
|
* HTTP read timeout in milliseconds
|
||||||
|
*/
|
||||||
|
public ImpatientHttpConnector(HttpConnector base, int connectTimeout, int readTimeout) {
|
||||||
|
this.base = base;
|
||||||
|
this.connectTimeout = connectTimeout;
|
||||||
|
this.readTimeout = readTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImpatientHttpConnector(HttpConnector base, int timeout) {
|
||||||
|
this(base,timeout,timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImpatientHttpConnector(HttpConnector base) {
|
||||||
|
this(base,CONNECT_TIMEOUT,READ_TIMEOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpURLConnection connect(URL url) throws IOException {
|
||||||
|
HttpURLConnection con = base.connect(url);
|
||||||
|
con.setConnectTimeout(connectTimeout);
|
||||||
|
con.setReadTimeout(readTimeout);
|
||||||
|
return con;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default connection timeout in milliseconds
|
||||||
|
*/
|
||||||
|
@SuppressFBWarnings("MS_SHOULD_BE_FINAL")
|
||||||
|
public static int CONNECT_TIMEOUT = (int) TimeUnit.SECONDS.toMillis(10);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default read timeout in milliseconds
|
||||||
|
*/
|
||||||
|
@SuppressFBWarnings("MS_SHOULD_BE_FINAL")
|
||||||
|
public static int READ_TIMEOUT = (int) TimeUnit.SECONDS.toMillis(10);
|
||||||
|
}
|
||||||
32
src/main/java/org/kohsuke/github/extras/OkHttpConnector.java
Normal file
32
src/main/java/org/kohsuke/github/extras/OkHttpConnector.java
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package org.kohsuke.github.extras;
|
||||||
|
|
||||||
|
import com.squareup.okhttp.OkHttpClient;
|
||||||
|
import com.squareup.okhttp.OkUrlFactory;
|
||||||
|
import org.kohsuke.github.HttpConnector;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link HttpConnector} for {@link OkHttpClient}.
|
||||||
|
*
|
||||||
|
* Unlike {@link #DEFAULT}, OkHttp does response caching.
|
||||||
|
* Making a conditional request against GitHubAPI and receiving a 304
|
||||||
|
* response does not count against the rate limit.
|
||||||
|
* See http://developer.github.com/v3/#conditional-requests
|
||||||
|
*
|
||||||
|
* @author Roberto Tyley
|
||||||
|
* @author Kohsuke Kawaguchi
|
||||||
|
*/
|
||||||
|
public class OkHttpConnector implements HttpConnector {
|
||||||
|
private final OkUrlFactory urlFactory;
|
||||||
|
|
||||||
|
public OkHttpConnector(OkUrlFactory urlFactory) {
|
||||||
|
this.urlFactory = urlFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpURLConnection connect(URL url) throws IOException {
|
||||||
|
return urlFactory.open(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,8 +7,9 @@ operations that act on them as defined as methods (such as `GHUser.follow()`), a
|
|||||||
are used in favor of using string handle (such as `GHUser.isMemberOf(GHOrganization)` instead of
|
are used in favor of using string handle (such as `GHUser.isMemberOf(GHOrganization)` instead of
|
||||||
`GHUser.isMemberOf(String)`)
|
`GHUser.isMemberOf(String)`)
|
||||||
|
|
||||||
There are some corners of the GitHub API that's not yet implemented, but
|
The library supports both github.com and GitHub Enterprise.
|
||||||
the library is implemented with the right abstractions and libraries to make it very easy to improve the coverage.
|
|
||||||
|
Most of the GitHub APIs are covered, although there are some corners that are still not yet implemented.
|
||||||
|
|
||||||
Sample Usage
|
Sample Usage
|
||||||
-----
|
-----
|
||||||
@@ -34,3 +35,17 @@ Alternatively, you can have just the OAuth token in this file:
|
|||||||
|
|
||||||
oauth=4d98173f7c075527cb64878561d1fe70
|
oauth=4d98173f7c075527cb64878561d1fe70
|
||||||
|
|
||||||
|
OkHttp
|
||||||
|
----
|
||||||
|
This library comes with a pluggable connector to use different HTTP client implementations
|
||||||
|
through `HttpConnector`. In particular, this means you can use [OkHttp](http://square.github.io/okhttp/),
|
||||||
|
so we can make use of it's HTTP response cache.
|
||||||
|
Making a conditional request against the GitHub API and receiving a 304 response
|
||||||
|
[does not count against the rate limit](http://developer.github.com/v3/#conditional-requests).
|
||||||
|
|
||||||
|
The following code shows an example of how to set up persistent cache on the disk:
|
||||||
|
|
||||||
|
Cache cache = new Cache(cacheDirectory, 10 * 1024 * 1024); // 10MB cache
|
||||||
|
GitHub gitHub = GitHubBuilder.fromCredentials()
|
||||||
|
.withConnector(new OkHttpConnector(new OkUrlFactory(new OkHttpClient().setCache(cache))))
|
||||||
|
.build();
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
<item name="Introduction" href="/index.html"/>
|
<item name="Introduction" href="/index.html"/>
|
||||||
<item name="Download" href="http://mvnrepository.com/artifact/${project.groupId}/${project.artifactId}"/>
|
<item name="Download" href="http://mvnrepository.com/artifact/${project.groupId}/${project.artifactId}"/>
|
||||||
<item name="Source code" href="https://github.com/kohsuke/${project.artifactId}"/>
|
<item name="Source code" href="https://github.com/kohsuke/${project.artifactId}"/>
|
||||||
|
<item name="Mailing List" href="https://groups.google.com/forum/#!forum/github-api"/>
|
||||||
</menu>
|
</menu>
|
||||||
|
|
||||||
<menu name="References">
|
<menu name="References">
|
||||||
|
|||||||
25
src/test/java/Foo.java
Normal file
25
src/test/java/Foo.java
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import org.kohsuke.github.GHRepository;
|
||||||
|
import org.kohsuke.github.GHRepository.Contributor;
|
||||||
|
import org.kohsuke.github.GHUser;
|
||||||
|
import org.kohsuke.github.GitHub;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Kohsuke Kawaguchi
|
||||||
|
*/
|
||||||
|
public class Foo {
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
GitHub gh = GitHub.connect();
|
||||||
|
for (Contributor c : gh.getRepository("kohsuke/yo").listContributors()) {
|
||||||
|
System.out.println(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void testRateLimit() throws Exception {
|
||||||
|
GitHub g = GitHub.connectAnonymously();
|
||||||
|
for (GHUser u : g.getOrganization("jenkinsci").listMembers()) {
|
||||||
|
u.getFollowersCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,354 +0,0 @@
|
|||||||
package org.kohsuke;
|
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
|
||||||
import org.kohsuke.github.GHCommit;
|
|
||||||
import org.kohsuke.github.GHCommit.File;
|
|
||||||
import org.kohsuke.github.GHCommitComment;
|
|
||||||
import org.kohsuke.github.GHCommitStatus;
|
|
||||||
import org.kohsuke.github.GHEvent;
|
|
||||||
import org.kohsuke.github.GHEventInfo;
|
|
||||||
import org.kohsuke.github.GHEventPayload;
|
|
||||||
import org.kohsuke.github.GHHook;
|
|
||||||
import org.kohsuke.github.GHBranch;
|
|
||||||
import org.kohsuke.github.GHIssue;
|
|
||||||
import org.kohsuke.github.GHIssueComment;
|
|
||||||
import org.kohsuke.github.GHIssueState;
|
|
||||||
import org.kohsuke.github.GHKey;
|
|
||||||
import org.kohsuke.github.GHMilestone;
|
|
||||||
import org.kohsuke.github.GHMyself;
|
|
||||||
import org.kohsuke.github.GHOrganization;
|
|
||||||
import org.kohsuke.github.GHOrganization.Permission;
|
|
||||||
import org.kohsuke.github.GHPullRequest;
|
|
||||||
import org.kohsuke.github.GHRepository;
|
|
||||||
import org.kohsuke.github.GHTeam;
|
|
||||||
import org.kohsuke.github.GHUser;
|
|
||||||
import org.kohsuke.github.GitHub;
|
|
||||||
import org.kohsuke.github.PagedIterable;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unit test for simple App.
|
|
||||||
*/
|
|
||||||
public class AppTest extends TestCase {
|
|
||||||
|
|
||||||
private GitHub gitHub;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setUp() throws Exception {
|
|
||||||
super.setUp();
|
|
||||||
gitHub = GitHub.connect();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testRepoCRUD() throws Exception {
|
|
||||||
GHRepository r = gitHub.createRepository("github-api-test", "a test repository", "http://github-api.kohsuke.org/", true);
|
|
||||||
r.enableIssueTracker(false);
|
|
||||||
r.enableDownloads(false);
|
|
||||||
r.enableWiki(false);
|
|
||||||
r.renameTo("github-api-test2");
|
|
||||||
gitHub.getMyself().getRepository("github-api-test2").delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testCredentialValid() throws IOException {
|
|
||||||
assertTrue(gitHub.isCredentialValid());
|
|
||||||
assertFalse(GitHub.connect("totally", "bogus").isCredentialValid());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testIssueWithNoComment() throws IOException {
|
|
||||||
GHRepository repository = gitHub.getRepository("kohsuke/test");
|
|
||||||
List<GHIssueComment> v = repository.getIssue(4).getComments();
|
|
||||||
System.out.println(v);
|
|
||||||
assertTrue(v.isEmpty());
|
|
||||||
|
|
||||||
v = repository.getIssue(3).getComments();
|
|
||||||
System.out.println(v);
|
|
||||||
assertTrue(v.size() == 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testCreateIssue() throws IOException {
|
|
||||||
GHUser u = gitHub.getUser("kohsuke");
|
|
||||||
GHRepository r = u.getRepository("test");
|
|
||||||
GHMilestone someMilestone = r.listMilestones(GHIssueState.CLOSED).iterator().next();
|
|
||||||
GHIssue o = r.createIssue("testing").body("this is body").assignee(u).label("bug").label("question").milestone(someMilestone).create();
|
|
||||||
System.out.println(o.getUrl());
|
|
||||||
o.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testRateLimit() throws IOException {
|
|
||||||
System.out.println(gitHub.getRateLimit());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testMyOrganizations() throws IOException {
|
|
||||||
Map<String, GHOrganization> org = gitHub.getMyOrganizations();
|
|
||||||
assertFalse(org.keySet().contains(null));
|
|
||||||
System.out.println(org);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testFetchPullRequest() throws Exception {
|
|
||||||
GHRepository r = gitHub.getOrganization("jenkinsci").getRepository("jenkins");
|
|
||||||
assertEquals("master",r.getMasterBranch());
|
|
||||||
r.getPullRequest(1);
|
|
||||||
r.getPullRequests(GHIssueState.OPEN);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testFetchPullRequestAsList() throws Exception {
|
|
||||||
GHRepository r = gitHub.getOrganization("symfony").getRepository("symfony-docs");
|
|
||||||
assertEquals("master", r.getMasterBranch());
|
|
||||||
PagedIterable<GHPullRequest> i = r.listPullRequests(GHIssueState.CLOSED);
|
|
||||||
List<GHPullRequest> prs = i.asList();
|
|
||||||
assertNotNull(prs);
|
|
||||||
assertTrue(prs.size() > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testRepoPermissions() throws Exception {
|
|
||||||
GHRepository r = gitHub.getOrganization("jenkinsci").getRepository("jenkins");
|
|
||||||
assertTrue(r.hasPullAccess());
|
|
||||||
|
|
||||||
r = gitHub.getOrganization("github").getRepository("tire");
|
|
||||||
assertFalse(r.hasAdminAccess());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testGetMyself() throws Exception {
|
|
||||||
GHMyself me = gitHub.getMyself();
|
|
||||||
System.out.println(me);
|
|
||||||
GHUser u = gitHub.getUser("kohsuke2");
|
|
||||||
System.out.println(u);
|
|
||||||
for (List<GHRepository> lst : me.iterateRepositories(100)) {
|
|
||||||
for (GHRepository r : lst) {
|
|
||||||
System.out.println(r.getPushedAt());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testPublicKeys() throws Exception {
|
|
||||||
List<GHKey> keys = gitHub.getMyself().getPublicKeys();
|
|
||||||
System.out.println(keys);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void tryOrgFork() throws Exception {
|
|
||||||
gitHub.getUser("kohsuke").getRepository("rubywm").forkTo(gitHub.getOrganization("jenkinsci"));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void tryGetTeamsForRepo() throws Exception {
|
|
||||||
Set<GHTeam> o = gitHub.getOrganization("jenkinsci").getRepository("rubywm").getTeams();
|
|
||||||
System.out.println(o);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testMembership() throws Exception {
|
|
||||||
Set<String> members = gitHub.getOrganization("jenkinsci").getRepository("violations-plugin").getCollaboratorNames();
|
|
||||||
System.out.println(members.contains("kohsuke"));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testMemberOrgs() throws Exception {
|
|
||||||
Set<GHOrganization> o = gitHub.getUser("kohsuke").getOrganizations();
|
|
||||||
System.out.println(o);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testCommit() throws Exception {
|
|
||||||
GHCommit commit = gitHub.getUser("jenkinsci").getRepository("jenkins").getCommit("08c1c9970af4d609ae754fbe803e06186e3206f7");
|
|
||||||
System.out.println(commit);
|
|
||||||
assertEquals(1, commit.getParents().size());
|
|
||||||
assertEquals(1,commit.getFiles().size());
|
|
||||||
|
|
||||||
File f = commit.getFiles().get(0);
|
|
||||||
assertEquals(48,f.getLinesChanged());
|
|
||||||
assertEquals("modified",f.getStatus());
|
|
||||||
assertEquals("changelog.html", f.getFileName());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testListCommits() throws Exception {
|
|
||||||
List<String> sha1 = new ArrayList<String>();
|
|
||||||
for (GHCommit c : gitHub.getUser("kohsuke").getRepository("empty-commit").listCommits()) {
|
|
||||||
System.out.println(c.getSHA1());
|
|
||||||
sha1.add(c.getSHA1());
|
|
||||||
}
|
|
||||||
assertEquals("fdfad6be4db6f96faea1f153fb447b479a7a9cb7",sha1.get(0));
|
|
||||||
assertEquals(1,sha1.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testBranches() throws Exception {
|
|
||||||
Map<String,GHBranch> b =
|
|
||||||
gitHub.getUser("jenkinsci").getRepository("jenkins").getBranches();
|
|
||||||
System.out.println(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testCommitComment() throws Exception {
|
|
||||||
GHRepository r = gitHub.getUser("jenkinsci").getRepository("jenkins");
|
|
||||||
PagedIterable<GHCommitComment> comments = r.listCommitComments();
|
|
||||||
List<GHCommitComment> batch = comments.iterator().nextPage();
|
|
||||||
for (GHCommitComment comment : batch) {
|
|
||||||
System.out.println(comment.getBody());
|
|
||||||
assertSame(comment.getOwner(), r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testCreateCommitComment() throws Exception {
|
|
||||||
GHCommit commit = gitHub.getUser("kohsuke").getRepository("sandbox-ant").getCommit("8ae38db0ea5837313ab5f39d43a6f73de3bd9000");
|
|
||||||
GHCommitComment c = commit.createComment("[testing](http://kohsuse.org/)");
|
|
||||||
System.out.println(c);
|
|
||||||
c.update("updated text");
|
|
||||||
System.out.println(c);
|
|
||||||
c.delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void tryHook() throws Exception {
|
|
||||||
GHRepository r = gitHub.getMyself().getRepository("test2");
|
|
||||||
GHHook hook = r.createWebHook(new URL("http://www.google.com/"));
|
|
||||||
System.out.println(hook);
|
|
||||||
|
|
||||||
for (GHHook h : r.getHooks())
|
|
||||||
h.delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testEventApi() throws Exception {
|
|
||||||
for (GHEventInfo ev : gitHub.getEvents()) {
|
|
||||||
System.out.println(ev);
|
|
||||||
if (ev.getType()==GHEvent.PULL_REQUEST) {
|
|
||||||
GHEventPayload.PullRequest pr = ev.getPayload(GHEventPayload.PullRequest.class);
|
|
||||||
System.out.println(pr.getNumber());
|
|
||||||
System.out.println(pr.getPullRequest());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testApp() throws IOException {
|
|
||||||
System.out.println(gitHub.getMyself().getEmails());
|
|
||||||
|
|
||||||
// GHRepository r = gitHub.getOrganization("jenkinsci").createRepository("kktest4", "Kohsuke's test", "http://kohsuke.org/", "Everyone", true);
|
|
||||||
// r.fork();
|
|
||||||
|
|
||||||
// tryDisablingIssueTrackers(gitHub);
|
|
||||||
|
|
||||||
// tryDisablingWiki(gitHub);
|
|
||||||
|
|
||||||
// GHPullRequest i = gitHub.getOrganization("jenkinsci").getRepository("sandbox").getPullRequest(1);
|
|
||||||
// for (GHIssueComment c : i.getComments())
|
|
||||||
// System.out.println(c);
|
|
||||||
// System.out.println(i);
|
|
||||||
|
|
||||||
// gitHub.getMyself().getRepository("perforce-plugin").setEmailServiceHook("kk@kohsuke.org");
|
|
||||||
|
|
||||||
// tryRenaming(gitHub);
|
|
||||||
// tryOrgFork(gitHub);
|
|
||||||
|
|
||||||
// testOrganization(gitHub);
|
|
||||||
// testPostCommitHook(gitHub);
|
|
||||||
|
|
||||||
// tryTeamCreation(gitHub);
|
|
||||||
|
|
||||||
// t.add(gitHub.getMyself());
|
|
||||||
// System.out.println(t.getMembers());
|
|
||||||
// t.remove(gitHub.getMyself());
|
|
||||||
// System.out.println(t.getMembers());
|
|
||||||
|
|
||||||
// GHRepository r = gitHub.getOrganization("HudsonLabs").createRepository("auto-test", "some description", "http://kohsuke.org/", "Plugin Developers", true);
|
|
||||||
|
|
||||||
// r.
|
|
||||||
// GitHub hub = GitHub.connectAnonymously();
|
|
||||||
//// hub.createRepository("test","test repository",null,true);
|
|
||||||
//// hub.getUser("kohsuke").getRepository("test").delete();
|
|
||||||
//
|
|
||||||
// System.out.println(hub.getUser("kohsuke").getRepository("hudson").getCollaborators());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void tryDisablingIssueTrackers(GitHub gitHub) throws IOException {
|
|
||||||
for (GHRepository r : gitHub.getOrganization("jenkinsci").getRepositories().values()) {
|
|
||||||
if (r.hasIssues()) {
|
|
||||||
if (r.getOpenIssueCount()==0) {
|
|
||||||
System.out.println("DISABLED "+r.getName());
|
|
||||||
r.enableIssueTracker(false);
|
|
||||||
} else {
|
|
||||||
System.out.println("UNTOUCHED "+r.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void tryDisablingWiki(GitHub gitHub) throws IOException {
|
|
||||||
for (GHRepository r : gitHub.getOrganization("jenkinsci").getRepositories().values()) {
|
|
||||||
if (r.hasWiki()) {
|
|
||||||
System.out.println("DISABLED "+r.getName());
|
|
||||||
r.enableWiki(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void tryUpdatingIssueTracker(GitHub gitHub) throws IOException {
|
|
||||||
GHRepository r = gitHub.getOrganization("jenkinsci").getRepository("lib-task-reactor");
|
|
||||||
System.out.println(r.hasIssues());
|
|
||||||
System.out.println(r.getOpenIssueCount());
|
|
||||||
r.enableIssueTracker(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void tryRenaming(GitHub gitHub) throws IOException {
|
|
||||||
gitHub.getUser("kohsuke").getRepository("test").renameTo("test2");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void tryTeamCreation(GitHub gitHub) throws IOException {
|
|
||||||
GHOrganization o = gitHub.getOrganization("HudsonLabs");
|
|
||||||
GHTeam t = o.createTeam("auto team", Permission.PUSH);
|
|
||||||
t.add(o.getRepository("auto-test"));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void testPostCommitHook(GitHub gitHub) throws IOException {
|
|
||||||
GHRepository r = gitHub.getMyself().getRepository("foo");
|
|
||||||
Set<URL> hooks = r.getPostCommitHooks();
|
|
||||||
hooks.add(new URL("http://kohsuke.org/test"));
|
|
||||||
System.out.println(hooks);
|
|
||||||
hooks.remove(new URL("http://kohsuke.org/test"));
|
|
||||||
System.out.println(hooks);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testOrgRepositories() throws IOException {
|
|
||||||
GHOrganization j = gitHub.getOrganization("jenkinsci");
|
|
||||||
long start = System.currentTimeMillis();
|
|
||||||
Map<String, GHRepository> repos = j.getRepositories();
|
|
||||||
long end = System.currentTimeMillis();
|
|
||||||
System.out.printf("%d repositories in %dms\n",repos.size(),end-start);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testOrganization() throws IOException {
|
|
||||||
GHOrganization j = gitHub.getOrganization("jenkinsci");
|
|
||||||
GHTeam t = j.getTeams().get("Core Developers");
|
|
||||||
|
|
||||||
assertNotNull(j.getRepository("jenkins"));
|
|
||||||
|
|
||||||
// t.add(labs.getRepository("xyz"));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testCommitStatus() throws Exception {
|
|
||||||
GHRepository r = gitHub.getUser("kohsuke").getRepository("test");
|
|
||||||
GHCommitStatus state;
|
|
||||||
// state = r.createCommitStatus("edacdd76b06c5f3f0697a22ca75803169f25f296", GHCommitState.FAILURE, "http://jenkins-ci.org/", "oops!");
|
|
||||||
|
|
||||||
List<GHCommitStatus> lst = r.listCommitStatuses("edacdd76b06c5f3f0697a22ca75803169f25f296").asList();
|
|
||||||
state = lst.get(0);
|
|
||||||
System.out.println(state);
|
|
||||||
assertEquals("oops!",state.getDescription());
|
|
||||||
assertEquals("http://jenkins-ci.org/",state.getTargetUrl());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testPullRequestPopulate() throws Exception {
|
|
||||||
GHRepository r = gitHub.getUser("kohsuke").getRepository("github-api");
|
|
||||||
GHPullRequest p = r.getPullRequest(17);
|
|
||||||
GHUser u = p.getUser();
|
|
||||||
assertNotNull(u.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testCheckMembership() throws Exception {
|
|
||||||
GHOrganization j = gitHub.getOrganization("jenkinsci");
|
|
||||||
GHUser kohsuke = gitHub.getUser("kohsuke");
|
|
||||||
GHUser a = gitHub.getUser("a");
|
|
||||||
|
|
||||||
assertTrue(j.hasMember(kohsuke));
|
|
||||||
assertFalse(j.hasMember(a));
|
|
||||||
|
|
||||||
assertTrue(j.hasPublicMember(kohsuke));
|
|
||||||
assertFalse(j.hasPublicMember(a));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.kohsuke.randname.RandomNameGenerator;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Kohsuke Kawaguchi
|
||||||
|
*/
|
||||||
|
public abstract class AbstractGitHubApiTestBase extends Assert {
|
||||||
|
|
||||||
|
protected GitHub gitHub;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
File f = new File(System.getProperty("user.home"), ".github.kohsuke2");
|
||||||
|
if (f.exists()) {
|
||||||
|
// use the non-standard credential preferentially, so that developers of this library do not have
|
||||||
|
// to clutter their event stream.
|
||||||
|
gitHub = GitHubBuilder.fromPropertyFile(f.getPath()).withRateLimitHandler(RateLimitHandler.FAIL).build();
|
||||||
|
} else {
|
||||||
|
gitHub = GitHubBuilder.fromCredentials().withRateLimitHandler(RateLimitHandler.FAIL).build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static final RandomNameGenerator rnd = new RandomNameGenerator();
|
||||||
|
}
|
||||||
877
src/test/java/org/kohsuke/github/AppTest.java
Executable file
877
src/test/java/org/kohsuke/github/AppTest.java
Executable file
@@ -0,0 +1,877 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import com.google.common.base.Predicate;
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.junit.Assume;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.kohsuke.github.GHCommit.File;
|
||||||
|
import org.kohsuke.github.GHOrganization.Permission;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit test for simple App.
|
||||||
|
*/
|
||||||
|
public class AppTest extends AbstractGitHubApiTestBase {
|
||||||
|
private String getTestRepositoryName() throws IOException {
|
||||||
|
return getUser().getLogin() + "/github-api-test";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRepoCRUD() throws Exception {
|
||||||
|
String targetName = "github-api-test-rename2";
|
||||||
|
|
||||||
|
deleteRepository("github-api-test-rename");
|
||||||
|
deleteRepository(targetName);
|
||||||
|
GHRepository r = gitHub.createRepository("github-api-test-rename", "a test repository", "http://github-api.kohsuke.org/", true);
|
||||||
|
r.enableIssueTracker(false);
|
||||||
|
r.enableDownloads(false);
|
||||||
|
r.enableWiki(false);
|
||||||
|
r.renameTo(targetName);
|
||||||
|
getUser().getRepository(targetName).delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRepositoryWithAutoInitializationCRUD() throws Exception {
|
||||||
|
String name = "github-api-test-autoinit";
|
||||||
|
deleteRepository(name);
|
||||||
|
GHRepository r = gitHub.createRepository(name)
|
||||||
|
.description("a test repository for auto init")
|
||||||
|
.homepage("http://github-api.kohsuke.org/")
|
||||||
|
.autoInit(true).create();
|
||||||
|
r.enableIssueTracker(false);
|
||||||
|
r.enableDownloads(false);
|
||||||
|
r.enableWiki(false);
|
||||||
|
Thread.sleep(3000);
|
||||||
|
assertNotNull(r.getReadme());
|
||||||
|
getUser().getRepository(name).delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteRepository(final String name) throws IOException {
|
||||||
|
GHRepository repository = getUser().getRepository(name);
|
||||||
|
if(repository != null) {
|
||||||
|
repository.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCredentialValid() throws IOException {
|
||||||
|
assertTrue(gitHub.isCredentialValid());
|
||||||
|
GitHub connect = GitHub.connect("totally", "bogus");
|
||||||
|
assertFalse(connect.isCredentialValid());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIssueWithNoComment() throws IOException {
|
||||||
|
GHRepository repository = gitHub.getRepository("kohsuke/test");
|
||||||
|
List<GHIssueComment> v = repository.getIssue(4).getComments();
|
||||||
|
System.out.println(v);
|
||||||
|
assertTrue(v.isEmpty());
|
||||||
|
|
||||||
|
v = repository.getIssue(3).getComments();
|
||||||
|
System.out.println(v);
|
||||||
|
assertTrue(v.size() == 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateIssue() throws IOException {
|
||||||
|
GHUser u = getUser();
|
||||||
|
GHRepository repository = getTestRepository();
|
||||||
|
GHMilestone milestone = repository.createMilestone(System.currentTimeMillis() + "", "Test Milestone");
|
||||||
|
GHIssue o = repository.createIssue("testing")
|
||||||
|
.body("this is body")
|
||||||
|
.assignee(u)
|
||||||
|
.label("bug")
|
||||||
|
.label("question")
|
||||||
|
.milestone(milestone)
|
||||||
|
.create();
|
||||||
|
assertNotNull(o);
|
||||||
|
o.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateDeployment() throws IOException {
|
||||||
|
GHRepository repository = getTestRepository();
|
||||||
|
GHDeployment deployment = repository.createDeployment("master")
|
||||||
|
.payload("{\"user\":\"atmos\",\"room_id\":123456}")
|
||||||
|
.description("question")
|
||||||
|
.create();
|
||||||
|
assertNotNull(deployment.getCreator());
|
||||||
|
assertNotNull(deployment.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testListDeployments() throws IOException {
|
||||||
|
GHRepository repository = getTestRepository();
|
||||||
|
GHDeployment deployment = repository.createDeployment("master")
|
||||||
|
.payload("{\"user\":\"atmos\",\"room_id\":123456}")
|
||||||
|
.description("question")
|
||||||
|
.environment("unittest")
|
||||||
|
.create();
|
||||||
|
assertNotNull(deployment.getCreator());
|
||||||
|
assertNotNull(deployment.getId());
|
||||||
|
ArrayList<GHDeployment> deployments = Lists.newArrayList(repository.listDeployments(null, "master", null, "unittest"));
|
||||||
|
assertNotNull(deployments);
|
||||||
|
assertFalse(Iterables.isEmpty(deployments));
|
||||||
|
GHDeployment unitTestDeployment = deployments.get(0);
|
||||||
|
assertEquals("unittest",unitTestDeployment.getEnvironment());
|
||||||
|
assertEquals("master", unitTestDeployment.getRef());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetDeploymentStatuses() throws IOException {
|
||||||
|
GHRepository repository = getTestRepository();
|
||||||
|
GHDeployment deployment = repository.createDeployment("master")
|
||||||
|
.description("question")
|
||||||
|
.payload("{\"user\":\"atmos\",\"room_id\":123456}")
|
||||||
|
.create();
|
||||||
|
GHDeploymentStatus ghDeploymentStatus = repository.createDeployStatus(deployment.getId(), GHDeploymentState.SUCCESS)
|
||||||
|
.description("success")
|
||||||
|
.targetUrl("http://www.github.com").create();
|
||||||
|
Iterable<GHDeploymentStatus> deploymentStatuses = repository.getDeploymentStatuses(deployment.getId());
|
||||||
|
assertNotNull(deploymentStatuses);
|
||||||
|
assertEquals(1,Iterables.size(deploymentStatuses));
|
||||||
|
assertEquals(ghDeploymentStatus.getId(), Iterables.get(deploymentStatuses, 0).getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetIssues() throws Exception {
|
||||||
|
List<GHIssue> closedIssues = gitHub.getUser("kohsuke").getRepository("github-api").getIssues(GHIssueState.CLOSED);
|
||||||
|
// prior to using PagedIterable GHRepository.getIssues(GHIssueState) would only retrieve 30 issues
|
||||||
|
assertTrue(closedIssues.size() > 30);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private GHRepository getTestRepository() throws IOException {
|
||||||
|
GHRepository repository;
|
||||||
|
try {
|
||||||
|
repository = gitHub.getRepository(getTestRepositoryName());
|
||||||
|
} catch (IOException e) {
|
||||||
|
repository = gitHub.createRepository("github-api-test", "A test repository for testing" +
|
||||||
|
"the github-api project", "http://github-api.kohsuke.org/", true);
|
||||||
|
try {
|
||||||
|
Thread.sleep(2000);
|
||||||
|
} catch (InterruptedException e1) {
|
||||||
|
throw new RuntimeException(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
repository.enableIssueTracker(true);
|
||||||
|
repository.enableDownloads(true);
|
||||||
|
repository.enableWiki(true);
|
||||||
|
}
|
||||||
|
return repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
private GHUser getUser() {
|
||||||
|
try {
|
||||||
|
return gitHub.getMyself();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testListIssues() throws IOException {
|
||||||
|
GHUser u = getUser();
|
||||||
|
GHRepository repository = getTestRepository();
|
||||||
|
|
||||||
|
GHMilestone milestone = repository.createMilestone(System.currentTimeMillis() + "", "Test Milestone");
|
||||||
|
milestone.close();
|
||||||
|
GHIssue unhomed = null;
|
||||||
|
GHIssue homed = null;
|
||||||
|
try {
|
||||||
|
unhomed = repository.createIssue("testing").body("this is body")
|
||||||
|
.assignee(u)
|
||||||
|
.label("bug")
|
||||||
|
.label("question")
|
||||||
|
.create();
|
||||||
|
assertEquals(unhomed.getNumber(), repository.getIssues(GHIssueState.OPEN, null).get(0).getNumber());
|
||||||
|
homed = repository.createIssue("testing").body("this is body")
|
||||||
|
.assignee(u)
|
||||||
|
.label("bug")
|
||||||
|
.label("question")
|
||||||
|
.milestone(milestone)
|
||||||
|
.create();
|
||||||
|
assertEquals(homed.getNumber(), repository.getIssues(GHIssueState.OPEN, milestone).get(0).getNumber());
|
||||||
|
} finally {
|
||||||
|
if (unhomed != null) {
|
||||||
|
unhomed.close();
|
||||||
|
}
|
||||||
|
if (homed != null) {
|
||||||
|
homed.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRateLimit() throws IOException {
|
||||||
|
System.out.println(gitHub.getRateLimit());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMyOrganizations() throws IOException {
|
||||||
|
Map<String, GHOrganization> org = gitHub.getMyOrganizations();
|
||||||
|
assertFalse(org.keySet().contains(null));
|
||||||
|
System.out.println(org);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMyOrganizationsContainMyTeams() throws IOException {
|
||||||
|
Map<String, Set<GHTeam>> teams = gitHub.getMyTeams();
|
||||||
|
Map<String, GHOrganization> myOrganizations = gitHub.getMyOrganizations();
|
||||||
|
//GitHub no longer has default 'owners' team, so there may be organization memberships without a team
|
||||||
|
//https://help.github.com/articles/about-improved-organization-permissions/
|
||||||
|
assertTrue(myOrganizations.keySet().containsAll(teams.keySet()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMyTeamsShouldIncludeMyself() throws IOException {
|
||||||
|
Map<String, Set<GHTeam>> teams = gitHub.getMyTeams();
|
||||||
|
for (Entry<String, Set<GHTeam>> teamsPerOrg : teams.entrySet()) {
|
||||||
|
String organizationName = teamsPerOrg.getKey();
|
||||||
|
for (GHTeam team : teamsPerOrg.getValue()) {
|
||||||
|
String teamName = team.getName();
|
||||||
|
assertTrue("Team " + teamName + " in organization " + organizationName
|
||||||
|
+ " does not contain myself",
|
||||||
|
shouldBelongToTeam(organizationName, teamName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean shouldBelongToTeam(String organizationName, String teamName) throws IOException {
|
||||||
|
GHOrganization org = gitHub.getOrganization(organizationName);
|
||||||
|
assertNotNull(org);
|
||||||
|
GHTeam team = org.getTeamByName(teamName);
|
||||||
|
assertNotNull(team);
|
||||||
|
return team.hasMember(gitHub.getMyself());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFetchPullRequest() throws Exception {
|
||||||
|
GHRepository r = gitHub.getOrganization("jenkinsci").getRepository("jenkins");
|
||||||
|
assertEquals("master",r.getMasterBranch());
|
||||||
|
r.getPullRequest(1);
|
||||||
|
r.getPullRequests(GHIssueState.OPEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFetchPullRequestAsList() throws Exception {
|
||||||
|
GHRepository r = gitHub.getRepository("kohsuke/github-api");
|
||||||
|
assertEquals("master", r.getMasterBranch());
|
||||||
|
PagedIterable<GHPullRequest> i = r.listPullRequests(GHIssueState.CLOSED);
|
||||||
|
List<GHPullRequest> prs = i.asList();
|
||||||
|
assertNotNull(prs);
|
||||||
|
assertTrue(prs.size() > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRepoPermissions() throws Exception {
|
||||||
|
kohsuke();
|
||||||
|
GHRepository r = gitHub.getOrganization("jenkinsci").getRepository("jenkins");
|
||||||
|
assertTrue(r.hasPullAccess());
|
||||||
|
|
||||||
|
r = gitHub.getOrganization("github").getRepository("hub");
|
||||||
|
assertFalse(r.hasAdminAccess());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetMyself() throws Exception {
|
||||||
|
GHMyself me = gitHub.getMyself();
|
||||||
|
assertNotNull(me);
|
||||||
|
assertNotNull(gitHub.getUser("kohsuke2"));
|
||||||
|
PagedIterable<GHRepository> ghRepositories = me.listRepositories();
|
||||||
|
assertTrue(ghRepositories.iterator().hasNext());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPublicKeys() throws Exception {
|
||||||
|
List<GHKey> keys = gitHub.getMyself().getPublicKeys();
|
||||||
|
assertFalse(keys.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOrgFork() throws Exception {
|
||||||
|
kohsuke();
|
||||||
|
gitHub.getRepository("kohsuke/rubywm").forkTo(gitHub.getOrganization("github-api-test-org"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetTeamsForRepo() throws Exception {
|
||||||
|
kohsuke();
|
||||||
|
// 'Core Developers' and 'Owners'
|
||||||
|
assertEquals(2, gitHub.getOrganization("github-api-test-org").getRepository("testGetTeamsForRepo").getTeams().size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMembership() throws Exception {
|
||||||
|
Set<String> members = gitHub.getOrganization("jenkinsci").getRepository("violations-plugin").getCollaboratorNames();
|
||||||
|
System.out.println(members.contains("kohsuke"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMemberOrgs() throws Exception {
|
||||||
|
Set<GHOrganization> o = gitHub.getUser("kohsuke").getOrganizations();
|
||||||
|
System.out.println(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOrgTeams() throws Exception {
|
||||||
|
kohsuke();
|
||||||
|
int sz=0;
|
||||||
|
for (GHTeam t : gitHub.getOrganization("github-api-test-org").listTeams()) {
|
||||||
|
assertNotNull(t.getName());
|
||||||
|
sz++;
|
||||||
|
}
|
||||||
|
assertTrue(sz < 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOrgTeamByName() throws Exception {
|
||||||
|
kohsuke();
|
||||||
|
GHTeam e = gitHub.getOrganization("github-api-test-org").getTeamByName("Core Developers");
|
||||||
|
assertNotNull(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOrgTeamBySlug() throws Exception {
|
||||||
|
kohsuke();
|
||||||
|
GHTeam e = gitHub.getOrganization("github-api-test-org").getTeamBySlug("core-developers");
|
||||||
|
assertNotNull(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCommit() throws Exception {
|
||||||
|
GHCommit commit = gitHub.getUser("jenkinsci").getRepository("jenkins").getCommit("08c1c9970af4d609ae754fbe803e06186e3206f7");
|
||||||
|
System.out.println(commit);
|
||||||
|
assertEquals(1, commit.getParents().size());
|
||||||
|
assertEquals(1,commit.getFiles().size());
|
||||||
|
assertEquals("https://github.com/jenkinsci/jenkins/commit/08c1c9970af4d609ae754fbe803e06186e3206f7",
|
||||||
|
commit.getHtmlUrl().toString());
|
||||||
|
|
||||||
|
File f = commit.getFiles().get(0);
|
||||||
|
assertEquals(48,f.getLinesChanged());
|
||||||
|
assertEquals("modified",f.getStatus());
|
||||||
|
assertEquals("changelog.html", f.getFileName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testListCommits() throws Exception {
|
||||||
|
List<String> sha1 = new ArrayList<String>();
|
||||||
|
for (GHCommit c : gitHub.getUser("kohsuke").getRepository("empty-commit").listCommits()) {
|
||||||
|
System.out.println(c.getSHA1());
|
||||||
|
sha1.add(c.getSHA1());
|
||||||
|
}
|
||||||
|
assertEquals("fdfad6be4db6f96faea1f153fb447b479a7a9cb7", sha1.get(0));
|
||||||
|
assertEquals(1, sha1.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testQueryCommits() throws Exception {
|
||||||
|
List<String> sha1 = new ArrayList<String>();
|
||||||
|
for (GHCommit c : gitHub.getUser("jenkinsci").getRepository("jenkins").queryCommits()
|
||||||
|
.since(new Date(1199174400000L)).until(1201852800000L).path("pom.xml").list()) {
|
||||||
|
System.out.println(c.getSHA1());
|
||||||
|
sha1.add(c.getSHA1());
|
||||||
|
}
|
||||||
|
assertEquals("1cccddb22e305397151b2b7b87b4b47d74ca337b",sha1.get(0));
|
||||||
|
assertEquals(29, sha1.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBranches() throws Exception {
|
||||||
|
Map<String,GHBranch> b =
|
||||||
|
gitHub.getUser("jenkinsci").getRepository("jenkins").getBranches();
|
||||||
|
System.out.println(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCommitComment() throws Exception {
|
||||||
|
GHRepository r = gitHub.getUser("jenkinsci").getRepository("jenkins");
|
||||||
|
PagedIterable<GHCommitComment> comments = r.listCommitComments();
|
||||||
|
List<GHCommitComment> batch = comments.iterator().nextPage();
|
||||||
|
for (GHCommitComment comment : batch) {
|
||||||
|
System.out.println(comment.getBody());
|
||||||
|
assertSame(comment.getOwner(), r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateCommitComment() throws Exception {
|
||||||
|
GHCommit commit = gitHub.getUser("kohsuke").getRepository("sandbox-ant").getCommit("8ae38db0ea5837313ab5f39d43a6f73de3bd9000");
|
||||||
|
GHCommitComment c = commit.createComment("[testing](http://kohsuse.org/)");
|
||||||
|
System.out.println(c);
|
||||||
|
c.update("updated text");
|
||||||
|
System.out.println(c);
|
||||||
|
c.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void tryHook() throws Exception {
|
||||||
|
kohsuke();
|
||||||
|
GHRepository r = gitHub.getMyself().getRepository("test2");
|
||||||
|
GHHook hook = r.createWebHook(new URL("http://www.google.com/"));
|
||||||
|
System.out.println(hook);
|
||||||
|
|
||||||
|
for (GHHook h : r.getHooks())
|
||||||
|
h.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEventApi() throws Exception {
|
||||||
|
for (GHEventInfo ev : gitHub.getEvents()) {
|
||||||
|
System.out.println(ev);
|
||||||
|
if (ev.getType()==GHEvent.PULL_REQUEST) {
|
||||||
|
GHEventPayload.PullRequest pr = ev.getPayload(GHEventPayload.PullRequest.class);
|
||||||
|
System.out.println(pr.getNumber());
|
||||||
|
System.out.println(pr.getPullRequest());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testApp() throws IOException {
|
||||||
|
System.out.println(gitHub.getMyself().getEmails());
|
||||||
|
|
||||||
|
// GHRepository r = gitHub.getOrganization("jenkinsci").createRepository("kktest4", "Kohsuke's test", "http://kohsuke.org/", "Everyone", true);
|
||||||
|
// r.fork();
|
||||||
|
|
||||||
|
// tryDisablingIssueTrackers(gitHub);
|
||||||
|
|
||||||
|
// tryDisablingWiki(gitHub);
|
||||||
|
|
||||||
|
// GHPullRequest i = gitHub.getOrganization("jenkinsci").getRepository("sandbox").getPullRequest(1);
|
||||||
|
// for (GHIssueComment c : i.getComments())
|
||||||
|
// System.out.println(c);
|
||||||
|
// System.out.println(i);
|
||||||
|
|
||||||
|
// gitHub.getMyself().getRepository("perforce-plugin").setEmailServiceHook("kk@kohsuke.org");
|
||||||
|
|
||||||
|
// tryRenaming(gitHub);
|
||||||
|
// tryOrgFork(gitHub);
|
||||||
|
|
||||||
|
// testOrganization(gitHub);
|
||||||
|
// testPostCommitHook(gitHub);
|
||||||
|
|
||||||
|
// tryTeamCreation(gitHub);
|
||||||
|
|
||||||
|
// t.add(gitHub.getMyself());
|
||||||
|
// System.out.println(t.getMembers());
|
||||||
|
// t.remove(gitHub.getMyself());
|
||||||
|
// System.out.println(t.getMembers());
|
||||||
|
|
||||||
|
// GHRepository r = gitHub.getOrganization("HudsonLabs").createRepository("auto-test", "some description", "http://kohsuke.org/", "Plugin Developers", true);
|
||||||
|
|
||||||
|
// r.
|
||||||
|
// GitHub hub = GitHub.connectAnonymously();
|
||||||
|
//// hub.createRepository("test","test repository",null,true);
|
||||||
|
//// hub.getUser("kohsuke").getRepository("test").delete();
|
||||||
|
//
|
||||||
|
// System.out.println(hub.getUser("kohsuke").getRepository("hudson").getCollaborators());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tryDisablingIssueTrackers(GitHub gitHub) throws IOException {
|
||||||
|
for (GHRepository r : gitHub.getOrganization("jenkinsci").getRepositories().values()) {
|
||||||
|
if (r.hasIssues()) {
|
||||||
|
if (r.getOpenIssueCount()==0) {
|
||||||
|
System.out.println("DISABLED "+r.getName());
|
||||||
|
r.enableIssueTracker(false);
|
||||||
|
} else {
|
||||||
|
System.out.println("UNTOUCHED "+r.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tryDisablingWiki(GitHub gitHub) throws IOException {
|
||||||
|
for (GHRepository r : gitHub.getOrganization("jenkinsci").getRepositories().values()) {
|
||||||
|
if (r.hasWiki()) {
|
||||||
|
System.out.println("DISABLED "+r.getName());
|
||||||
|
r.enableWiki(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tryUpdatingIssueTracker(GitHub gitHub) throws IOException {
|
||||||
|
GHRepository r = gitHub.getOrganization("jenkinsci").getRepository("lib-task-reactor");
|
||||||
|
System.out.println(r.hasIssues());
|
||||||
|
System.out.println(r.getOpenIssueCount());
|
||||||
|
r.enableIssueTracker(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tryRenaming(GitHub gitHub) throws IOException {
|
||||||
|
gitHub.getUser("kohsuke").getRepository("test").renameTo("test2");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tryTeamCreation(GitHub gitHub) throws IOException {
|
||||||
|
GHOrganization o = gitHub.getOrganization("HudsonLabs");
|
||||||
|
GHTeam t = o.createTeam("auto team", Permission.PUSH);
|
||||||
|
t.add(o.getRepository("auto-test"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testPostCommitHook(GitHub gitHub) throws IOException {
|
||||||
|
GHRepository r = gitHub.getMyself().getRepository("foo");
|
||||||
|
Set<URL> hooks = r.getPostCommitHooks();
|
||||||
|
hooks.add(new URL("http://kohsuke.org/test"));
|
||||||
|
System.out.println(hooks);
|
||||||
|
hooks.remove(new URL("http://kohsuke.org/test"));
|
||||||
|
System.out.println(hooks);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOrgRepositories() throws IOException {
|
||||||
|
kohsuke();
|
||||||
|
GHOrganization j = gitHub.getOrganization("jenkinsci");
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
Map<String, GHRepository> repos = j.getRepositories();
|
||||||
|
long end = System.currentTimeMillis();
|
||||||
|
System.out.printf("%d repositories in %dms\n", repos.size(), end - start);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOrganization() throws IOException {
|
||||||
|
kohsuke();
|
||||||
|
GHOrganization j = gitHub.getOrganization("github-api-test-org");
|
||||||
|
GHTeam t = j.getTeams().get("Core Developers");
|
||||||
|
|
||||||
|
assertNotNull(j.getRepository("jenkins"));
|
||||||
|
|
||||||
|
// t.add(labs.getRepository("xyz"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCommitStatus() throws Exception {
|
||||||
|
GHRepository r = gitHub.getRepository("kohsuke/github-api");
|
||||||
|
|
||||||
|
GHCommitStatus state;
|
||||||
|
|
||||||
|
// state = r.createCommitStatus("ecbfdd7315ef2cf04b2be7f11a072ce0bd00c396", GHCommitState.FAILURE, "http://kohsuke.org/", "testing!");
|
||||||
|
|
||||||
|
List<GHCommitStatus> lst = r.listCommitStatuses("ecbfdd7315ef2cf04b2be7f11a072ce0bd00c396").asList();
|
||||||
|
state = lst.get(0);
|
||||||
|
System.out.println(state);
|
||||||
|
assertEquals("testing!",state.getDescription());
|
||||||
|
assertEquals("http://kohsuke.org/", state.getTargetUrl());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCommitShortInfo() throws Exception {
|
||||||
|
GHRepository r = gitHub.getRepository("kohsuke/github-api");
|
||||||
|
GHCommit commit = r.getCommit("86a2e245aa6d71d54923655066049d9e21a15f23");
|
||||||
|
assertEquals(commit.getCommitShortInfo().getAuthor().getName(), "Kohsuke Kawaguchi");
|
||||||
|
assertEquals(commit.getCommitShortInfo().getMessage(), "doc");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPullRequestPopulate() throws Exception {
|
||||||
|
GHRepository r = gitHub.getUser("kohsuke").getRepository("github-api");
|
||||||
|
GHPullRequest p = r.getPullRequest(17);
|
||||||
|
GHUser u = p.getUser();
|
||||||
|
assertNotNull(u.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCheckMembership() throws Exception {
|
||||||
|
kohsuke();
|
||||||
|
GHOrganization j = gitHub.getOrganization("jenkinsci");
|
||||||
|
GHUser kohsuke = gitHub.getUser("kohsuke");
|
||||||
|
GHUser b = gitHub.getUser("b");
|
||||||
|
|
||||||
|
assertTrue(j.hasMember(kohsuke));
|
||||||
|
assertFalse(j.hasMember(b));
|
||||||
|
|
||||||
|
assertTrue(j.hasPublicMember(kohsuke));
|
||||||
|
assertFalse(j.hasPublicMember(b));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateRelease() throws Exception {
|
||||||
|
kohsuke();
|
||||||
|
|
||||||
|
GHRepository r = gitHub.getRepository("kohsuke2/testCreateRelease");
|
||||||
|
|
||||||
|
String tagName = UUID.randomUUID().toString();
|
||||||
|
String releaseName = "release-" + tagName;
|
||||||
|
|
||||||
|
GHRelease rel = r.createRelease(tagName)
|
||||||
|
.name(releaseName)
|
||||||
|
.prerelease(false)
|
||||||
|
.create();
|
||||||
|
|
||||||
|
Thread.sleep(3000);
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
for (GHTag tag : r.listTags()) {
|
||||||
|
if (tagName.equals(tag.getName())) {
|
||||||
|
String ash = tag.getCommit().getSHA1();
|
||||||
|
GHRef ref = r.createRef("refs/heads/"+releaseName, ash);
|
||||||
|
assertEquals(ref.getRef(),"refs/heads/"+releaseName);
|
||||||
|
|
||||||
|
for (Map.Entry<String, GHBranch> entry : r.getBranches().entrySet()) {
|
||||||
|
System.out.println(entry.getKey() + "/" + entry.getValue());
|
||||||
|
if (releaseName.equals(entry.getValue().getName())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fail("branch not found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fail("release creation failed! tag not found");
|
||||||
|
} finally {
|
||||||
|
rel.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRef() throws IOException {
|
||||||
|
GHRef masterRef = gitHub.getRepository("jenkinsci/jenkins").getRef("heads/master");
|
||||||
|
assertEquals("https://api.github.com/repos/jenkinsci/jenkins/git/refs/heads/master", masterRef.getUrl().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void directoryListing() throws IOException {
|
||||||
|
List<GHContent> children = gitHub.getRepository("jenkinsci/jenkins").getDirectoryContent("core");
|
||||||
|
for (GHContent c : children) {
|
||||||
|
System.out.println(c.getName());
|
||||||
|
if (c.isDirectory()) {
|
||||||
|
for (GHContent d : c.listDirectoryContent()) {
|
||||||
|
System.out.println(" "+d.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddDeployKey() throws IOException {
|
||||||
|
GHRepository myRepository = getTestRepository();
|
||||||
|
final GHDeployKey newDeployKey = myRepository.addDeployKey("test", "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDUt0RAycC5cS42JKh6SecfFZBR1RrF+2hYMctz4mk74/arBE+wFb7fnSHGzdGKX2h5CFOWODifRCJVhB7hlVxodxe+QkQQYAEL/x1WVCJnGgTGQGOrhOMj95V3UE5pQKhsKD608C+u5tSofcWXLToP1/wZ7U4/AHjqYi08OLsWToHCax55TZkvdt2jo0hbIoYU+XI9Q8Uv4ONDN1oabiOdgeKi8+crvHAuvNleiBhWVBzFh8KdfzaH5uNdw7ihhFjEd1vzqACsjCINCjdMfzl6jD9ExuWuE92nZJnucls2cEoNC6k2aPmrZDg9hA32FXVpyseY+bDUWFU6LO2LG6PB kohsuke@atlas");
|
||||||
|
try {
|
||||||
|
assertNotNull(newDeployKey.getId());
|
||||||
|
|
||||||
|
GHDeployKey k = Iterables.find(myRepository.getDeployKeys(), new Predicate<GHDeployKey>() {
|
||||||
|
public boolean apply(GHDeployKey deployKey) {
|
||||||
|
return newDeployKey.getId() == deployKey.getId();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
assertNotNull(k);
|
||||||
|
} finally {
|
||||||
|
newDeployKey.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCommitStatusContext() throws IOException {
|
||||||
|
GHRepository myRepository = getTestRepository();
|
||||||
|
GHRef masterRef = myRepository.getRef("heads/master");
|
||||||
|
GHCommitStatus commitStatus = myRepository.createCommitStatus(masterRef.getObject().getSha(), GHCommitState.SUCCESS, "http://www.example.com", "test", "test/context");
|
||||||
|
assertEquals("test/context", commitStatus.getContext());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMemberPagenation() throws IOException {
|
||||||
|
Set<GHUser> all = new HashSet<GHUser>();
|
||||||
|
for (GHUser u : gitHub.getOrganization("github-api-test-org").getTeamByName("Core Developers").listMembers()) {
|
||||||
|
System.out.println(u.getLogin());
|
||||||
|
all.add(u);
|
||||||
|
}
|
||||||
|
assertFalse(all.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIssueSearch() throws IOException {
|
||||||
|
PagedSearchIterable<GHIssue> r = gitHub.searchIssues().mentions("kohsuke").isOpen().list();
|
||||||
|
for (GHIssue i : r) {
|
||||||
|
System.out.println(i.getTitle());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test // issue #99
|
||||||
|
public void testReadme() throws IOException {
|
||||||
|
GHContent readme = gitHub.getRepository("github-api-test-org/test-readme").getReadme();
|
||||||
|
assertEquals(readme.getName(),"README.md");
|
||||||
|
assertEquals(readme.getContent(),"This is a markdown readme.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTrees() throws IOException {
|
||||||
|
GHTree masterTree = gitHub.getRepository("kohsuke/github-api").getTree("master");
|
||||||
|
boolean foundReadme = false;
|
||||||
|
for(GHTreeEntry e : masterTree.getTree()){
|
||||||
|
if("readme".equalsIgnoreCase(e.getPath().replaceAll("\\.md", ""))){
|
||||||
|
foundReadme = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertTrue(foundReadme);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTreesRecursive() throws IOException {
|
||||||
|
GHTree masterTree = gitHub.getRepository("kohsuke/github-api").getTreeRecursive("master", 1);
|
||||||
|
boolean foundThisFile = false;
|
||||||
|
for(GHTreeEntry e : masterTree.getTree()){
|
||||||
|
if(e.getPath().endsWith(AppTest.class.getSimpleName() + ".java")){
|
||||||
|
foundThisFile = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertTrue(foundThisFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRepoLabel() throws IOException {
|
||||||
|
GHRepository r = gitHub.getRepository("github-api-test-org/test-labels");
|
||||||
|
List<GHLabel> lst = r.listLabels().asList();
|
||||||
|
for (GHLabel l : lst) {
|
||||||
|
System.out.println(l.getName());
|
||||||
|
}
|
||||||
|
assertTrue(lst.size() > 5);
|
||||||
|
GHLabel e = r.getLabel("enhancement");
|
||||||
|
assertEquals("enhancement",e.getName());
|
||||||
|
assertNotNull(e.getUrl());
|
||||||
|
assertTrue(Pattern.matches("[0-9a-fA-F]{6}",e.getColor()));
|
||||||
|
|
||||||
|
{// CRUD
|
||||||
|
GHLabel t = r.createLabel("test", "123456");
|
||||||
|
GHLabel t2 = r.getLabel("test");
|
||||||
|
assertEquals(t.getName(), t2.getName());
|
||||||
|
assertEquals(t.getColor(), "123456");
|
||||||
|
assertEquals(t.getColor(), t2.getColor());
|
||||||
|
assertEquals(t.getUrl(), t2.getUrl());
|
||||||
|
t.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSubscribers() throws IOException {
|
||||||
|
boolean kohsuke = false;
|
||||||
|
GHRepository mr = gitHub.getRepository("kohsuke/github-api");
|
||||||
|
for (GHUser u : mr.listSubscribers()) {
|
||||||
|
System.out.println(u.getLogin());
|
||||||
|
kohsuke |= u.getLogin().equals("kohsuke");
|
||||||
|
}
|
||||||
|
assertTrue(kohsuke);
|
||||||
|
System.out.println("---");
|
||||||
|
|
||||||
|
boolean githubApi = false;
|
||||||
|
for (GHRepository r : gitHub.getUser("kohsuke").listRepositories()) {
|
||||||
|
System.out.println(r.getName());
|
||||||
|
githubApi |= r.equals(mr);
|
||||||
|
}
|
||||||
|
assertTrue(githubApi);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testListAllRepositories() throws Exception {
|
||||||
|
Iterator<GHRepository> itr = gitHub.listAllPublicRepositories().iterator();
|
||||||
|
for (int i=0; i<30; i++) {
|
||||||
|
assertTrue(itr.hasNext());
|
||||||
|
GHRepository r = itr.next();
|
||||||
|
System.out.println(r.getFullName());
|
||||||
|
assertNotNull(r.getUrl());
|
||||||
|
assertNotEquals(0,r.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test // issue #162
|
||||||
|
public void testIssue162() throws Exception {
|
||||||
|
GHRepository r = gitHub.getRepository("kohsuke/github-api");
|
||||||
|
List<GHContent> contents = r.getDirectoryContent("", "gh-pages");
|
||||||
|
for (GHContent content : contents) {
|
||||||
|
if (content.isFile()) {
|
||||||
|
String content1 = content.getContent();
|
||||||
|
String content2 = r.getFileContent(content.getPath(), "gh-pages").getContent();
|
||||||
|
System.out.println(content.getPath());
|
||||||
|
assertEquals(content1, content2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void markDown() throws Exception {
|
||||||
|
assertEquals("<p><strong>Test日本語</strong></p>", IOUtils.toString(gitHub.renderMarkdown("**Test日本語**")).trim());
|
||||||
|
|
||||||
|
String actual = IOUtils.toString(gitHub.getRepository("kohsuke/github-api").renderMarkdown("@kohsuke to fix issue #1", MarkdownMode.GFM));
|
||||||
|
System.out.println(actual);
|
||||||
|
assertTrue(actual.contains("href=\"https://github.com/kohsuke\""));
|
||||||
|
assertTrue(actual.contains("href=\"https://github.com/kohsuke/github-api/pull/1\""));
|
||||||
|
assertTrue(actual.contains("class=\"user-mention\""));
|
||||||
|
assertTrue(actual.contains("class=\"issue-link "));
|
||||||
|
assertTrue(actual.contains("to fix issue"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void searchUsers() throws Exception {
|
||||||
|
PagedSearchIterable<GHUser> r = gitHub.searchUsers().q("tom").repos(">42").followers(">1000").list();
|
||||||
|
GHUser u = r.iterator().next();
|
||||||
|
System.out.println(u.getName());
|
||||||
|
assertNotNull(u.getId());
|
||||||
|
assertTrue(r.getTotalCount() > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void searchRepositories() throws Exception {
|
||||||
|
PagedSearchIterable<GHRepository> r = gitHub.searchRepositories().q("tetris").language("assembly").sort(GHRepositorySearchBuilder.Sort.STARS).list();
|
||||||
|
GHRepository u = r.iterator().next();
|
||||||
|
System.out.println(u.getName());
|
||||||
|
assertNotNull(u.getId());
|
||||||
|
assertEquals("Assembly", u.getLanguage());
|
||||||
|
assertTrue(r.getTotalCount() > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void searchContent() throws Exception {
|
||||||
|
PagedSearchIterable<GHContent> r = gitHub.searchContent().q("addClass").in("file").language("js").repo("jquery/jquery").list();
|
||||||
|
GHContent c = r.iterator().next();
|
||||||
|
System.out.println(c.getName());
|
||||||
|
assertNotNull(c.getDownloadUrl());
|
||||||
|
assertNotNull(c.getOwner());
|
||||||
|
assertEquals("jquery/jquery",c.getOwner().getFullName());
|
||||||
|
assertTrue(r.getTotalCount() > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void notifications() throws Exception {
|
||||||
|
boolean found=false;
|
||||||
|
for (GHThread t : gitHub.listNotifications().nonBlocking(true).read(true)) {
|
||||||
|
if (!found) {
|
||||||
|
found = true;
|
||||||
|
t.markAsRead(); // test this by calling it once on old nofication
|
||||||
|
}
|
||||||
|
assertNotNull(t.getTitle());
|
||||||
|
assertNotNull(t.getReason());
|
||||||
|
|
||||||
|
System.out.println(t.getTitle());
|
||||||
|
System.out.println(t.getLastReadAt());
|
||||||
|
System.out.println(t.getType());
|
||||||
|
System.out.println();
|
||||||
|
}
|
||||||
|
assertTrue(found);
|
||||||
|
gitHub.listNotifications().markAsRead();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Just basic code coverage to make sure toString() doesn't blow up
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void checkToString() throws Exception {
|
||||||
|
GHUser u = gitHub.getUser("rails");
|
||||||
|
System.out.println(u);
|
||||||
|
GHRepository r = u.getRepository("rails");
|
||||||
|
System.out.println(r);
|
||||||
|
System.out.println(r.getIssue(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void kohsuke() {
|
||||||
|
String login = getUser().getLogin();
|
||||||
|
Assume.assumeTrue(login.equals("kohsuke") || login.equals("kohsuke2"));
|
||||||
|
}
|
||||||
|
}
|
||||||
28
src/test/java/org/kohsuke/github/CommitTest.java
Normal file
28
src/test/java/org/kohsuke/github/CommitTest.java
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
import com.google.common.collect.Iterators;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Kohsuke Kawaguchi
|
||||||
|
*/
|
||||||
|
public class CommitTest extends AbstractGitHubApiTestBase {
|
||||||
|
@Test // issue 152
|
||||||
|
public void lastStatus() throws IOException {
|
||||||
|
GHTag t = gitHub.getRepository("stapler/stapler").listTags().iterator().next();
|
||||||
|
t.getCommit().getLastStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test // issue 230
|
||||||
|
public void listFiles() throws Exception {
|
||||||
|
GHRepository repo = gitHub.getRepository("stapler/stapler");
|
||||||
|
PagedIterable<GHCommit> commits = repo.queryCommits().path("pom.xml").list();
|
||||||
|
for (GHCommit commit : Iterables.limit(commits, 10)) {
|
||||||
|
GHCommit expected = repo.getCommit( commit.getSHA1() );
|
||||||
|
assertEquals(expected.getFiles().size(), commit.getFiles().size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integration test for {@link GHContent}.
|
||||||
|
*/
|
||||||
|
public class GHContentIntegrationTest extends AbstractGitHubApiTestBase {
|
||||||
|
|
||||||
|
private GHRepository repo;
|
||||||
|
private String createdFilename = rnd.next();
|
||||||
|
|
||||||
|
@Before
|
||||||
|
@Override
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
repo = gitHub.getRepository("github-api-test-org/GHContentIntegrationTest").fork();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBranchProtection() throws Exception {
|
||||||
|
GHBranch b = repo.getBranch("master");
|
||||||
|
b.enableProtection(EnforcementLevel.NON_ADMINS, "foo/bar");
|
||||||
|
b.disableProtection();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetFileContent() throws Exception {
|
||||||
|
GHContent content = repo.getFileContent("ghcontent-ro/a-file-with-content");
|
||||||
|
|
||||||
|
assertTrue(content.isFile());
|
||||||
|
assertEquals("thanks for reading me\n", content.getContent());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetEmptyFileContent() throws Exception {
|
||||||
|
GHContent content = repo.getFileContent("ghcontent-ro/an-empty-file");
|
||||||
|
|
||||||
|
assertTrue(content.isFile());
|
||||||
|
assertEquals("", content.getContent());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetDirectoryContent() throws Exception {
|
||||||
|
List<GHContent> entries = repo.getDirectoryContent("ghcontent-ro/a-dir-with-3-entries");
|
||||||
|
|
||||||
|
assertTrue(entries.size() == 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetDirectoryContentTrailingSlash() throws Exception {
|
||||||
|
//Used to truncate the ?ref=master, see gh-224 https://github.com/kohsuke/github-api/pull/224
|
||||||
|
List<GHContent> entries = repo.getDirectoryContent("ghcontent-ro/a-dir-with-3-entries/", "master");
|
||||||
|
|
||||||
|
assertTrue(entries.get(0).getUrl().endsWith("?ref=master"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCRUDContent() throws Exception {
|
||||||
|
GHContentUpdateResponse created = repo.createContent("this is an awesome file I created\n", "Creating a file for integration tests.", createdFilename);
|
||||||
|
GHContent createdContent = created.getContent();
|
||||||
|
|
||||||
|
assertNotNull(created.getCommit());
|
||||||
|
assertNotNull(created.getContent());
|
||||||
|
assertNotNull(createdContent.getContent());
|
||||||
|
assertEquals("this is an awesome file I created\n", createdContent.getContent());
|
||||||
|
|
||||||
|
GHContentUpdateResponse updatedContentResponse = createdContent.update("this is some new content\n", "Updated file for integration tests.");
|
||||||
|
GHContent updatedContent = updatedContentResponse.getContent();
|
||||||
|
|
||||||
|
assertNotNull(updatedContentResponse.getCommit());
|
||||||
|
assertNotNull(updatedContentResponse.getContent());
|
||||||
|
// due to what appears to be a cache propagation delay, this test is too flaky
|
||||||
|
// assertEquals("this is some new content\n", updatedContent.getContent());
|
||||||
|
|
||||||
|
GHContentUpdateResponse deleteResponse = updatedContent.delete("Enough of this foolishness!");
|
||||||
|
|
||||||
|
assertNotNull(deleteResponse.getCommit());
|
||||||
|
assertNull(deleteResponse.getContent());
|
||||||
|
}
|
||||||
|
}
|
||||||
193
src/test/java/org/kohsuke/github/GHLicenseTest.java
Normal file
193
src/test/java/org/kohsuke/github/GHLicenseTest.java
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2016, Duncan Dickinson
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.kohsuke.github;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Duncan Dickinson
|
||||||
|
*/
|
||||||
|
public class GHLicenseTest extends Assert {
|
||||||
|
private GitHub gitHub;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
gitHub = new GitHubBuilder()
|
||||||
|
.fromCredentials()
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic test to ensure that the list of licenses from {@link GitHub#listLicenses()} is returned
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void listLicenses() throws IOException {
|
||||||
|
Iterable<GHLicense> licenses = gitHub.listLicenses();
|
||||||
|
assertTrue(licenses.iterator().hasNext());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that {@link GitHub#listLicenses()} returns the MIT license
|
||||||
|
* in the expected manner.
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void listLicensesCheckIndividualLicense() throws IOException {
|
||||||
|
PagedIterable<GHLicense> licenses = gitHub.listLicenses();
|
||||||
|
for (GHLicense lic : licenses) {
|
||||||
|
if (lic.getKey().equals("mit")) {
|
||||||
|
assertTrue(lic.getUrl().equals(new URL("https://api.github.com/licenses/mit")));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fail("The MIT license was not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks that the request for an individual license using {@link GitHub#getLicense(String)}
|
||||||
|
* returns expected values (not all properties are checked)
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void getLicense() throws IOException {
|
||||||
|
String key = "mit";
|
||||||
|
GHLicense license = gitHub.getLicense(key);
|
||||||
|
assertNotNull(license);
|
||||||
|
assertTrue("The name is correct", license.getName().equals("MIT License"));
|
||||||
|
assertTrue("The HTML URL is correct", license.getHtmlUrl().equals(new URL("http://choosealicense.com/licenses/mit/")));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accesses the 'kohsuke/github-api' repo using {@link GitHub#getRepository(String)}
|
||||||
|
* and checks that the license is correct
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void checkRepositoryLicense() throws IOException {
|
||||||
|
GHRepository repo = gitHub.getRepository("kohsuke/github-api");
|
||||||
|
GHLicense license = repo.getLicense();
|
||||||
|
assertNotNull("The license is populated", license);
|
||||||
|
assertTrue("The key is correct", license.getKey().equals("mit"));
|
||||||
|
assertTrue("The name is correct", license.getName().equals("MIT License"));
|
||||||
|
assertTrue("The URL is correct", license.getUrl().equals(new URL("https://api.github.com/licenses/mit")));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accesses the 'atom/atom' repo using {@link GitHub#getRepository(String)}
|
||||||
|
* and checks that the license is correct
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void checkRepositoryLicenseAtom() throws IOException {
|
||||||
|
GHRepository repo = gitHub.getRepository("atom/atom");
|
||||||
|
GHLicense license = repo.getLicense();
|
||||||
|
assertNotNull("The license is populated", license);
|
||||||
|
assertTrue("The key is correct", license.getKey().equals("mit"));
|
||||||
|
assertTrue("The name is correct", license.getName().equals("MIT License"));
|
||||||
|
assertTrue("The URL is correct", license.getUrl().equals(new URL("https://api.github.com/licenses/mit")));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accesses the 'pomes/pomes' repo using {@link GitHub#getRepository(String)}
|
||||||
|
* and checks that the license is correct
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void checkRepositoryLicensePomes() throws IOException {
|
||||||
|
GHRepository repo = gitHub.getRepository("pomes/pomes");
|
||||||
|
GHLicense license = repo.getLicense();
|
||||||
|
assertNotNull("The license is populated", license);
|
||||||
|
assertTrue("The key is correct", license.getKey().equals("apache-2.0"));
|
||||||
|
assertTrue("The name is correct", license.getName().equals("Apache License 2.0"));
|
||||||
|
assertTrue("The URL is correct", license.getUrl().equals(new URL("https://api.github.com/licenses/apache-2.0")));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accesses the 'dedickinson/test-repo' repo using {@link GitHub#getRepository(String)}
|
||||||
|
* and checks that *no* license is returned as the repo doesn't have one
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void checkRepositoryWithoutLicense() throws IOException {
|
||||||
|
GHRepository repo = gitHub.getRepository("dedickinson/test-repo");
|
||||||
|
GHLicense license = repo.getLicense();
|
||||||
|
assertNull("There is no license", license);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accesses the 'kohsuke/github-api' repo using {@link GitHub#getRepository(String)}
|
||||||
|
* and then calls {@link GHRepository#getLicense()} and checks that certain
|
||||||
|
* properties are correct
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void checkRepositoryFullLicense() throws IOException {
|
||||||
|
GHRepository repo = gitHub.getRepository("kohsuke/github-api");
|
||||||
|
GHLicense license = repo.getLicense();
|
||||||
|
assertNotNull("The license is populated", license);
|
||||||
|
assertTrue("The key is correct", license.getKey().equals("mit"));
|
||||||
|
assertTrue("The name is correct", license.getName().equals("MIT License"));
|
||||||
|
assertTrue("The URL is correct", license.getUrl().equals(new URL("https://api.github.com/licenses/mit")));
|
||||||
|
assertTrue("The HTML URL is correct", license.getHtmlUrl().equals(new URL("http://choosealicense.com/licenses/mit/")));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accesses the 'pomes/pomes' repo using {@link GitHub#getRepository(String)}
|
||||||
|
* and then calls {@link GHRepository#getLicenseContent()} and checks that certain
|
||||||
|
* properties are correct
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void checkRepositoryLicenseContent() throws IOException {
|
||||||
|
GHRepository repo = gitHub.getRepository("pomes/pomes");
|
||||||
|
GHContent content = repo.getLicenseContent();
|
||||||
|
assertNotNull("The license content is populated", content);
|
||||||
|
assertTrue("The type is 'file'", content.getType().equals("file"));
|
||||||
|
assertTrue("The license file is 'LICENSE'", content.getName().equals("LICENSE"));
|
||||||
|
|
||||||
|
if (content.getEncoding().equals("base64")) {
|
||||||
|
String licenseText = new String(IOUtils.toByteArray(content.read()));
|
||||||
|
assertTrue("The license appears to be an Apache License", licenseText.contains("Apache License"));
|
||||||
|
} else {
|
||||||
|
fail("Expected the license to be Base64 encoded but instead it was " + content.getEncoding());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user