1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 # Github OAuth tokens management
19 # When using batch mode with the `github` API, we can rapidly reach the rate
20 # limit allowed by Github.
22 # One solution consists in using a wallet of tokens so we can rely on more than
23 # one token and switch them when one become exhausted.
25 # ## Using the Github wallet to check tokens
27 # One functionality of the wallet is to check the validity of a token against
28 # the API. `check_token` will return false if a token is invalid or exhausted.
31 # var wallet = new GithubWallet
32 # assert not wallet.check_token("this is a bad token")
37 # The wallet can also be used to store tokens and check all of them.
40 # wallet = new GithubWallet
41 # wallet.add "some token"
42 # wallet.add "some other token"
48 # wallet = new GithubWallet.from_tokens(["token 1", "token 2"])
51 # The `show_status` method can be used to display a summary of the validity of
52 # each token in the wallet.
58 # Will display something like this:
66 # ## Using the wallet to obtain a Github API client
68 # Using the wallet you can cycle through tokens and obtain a new Github API client
69 # instance with a fresh rate limit.
72 # wallet = new GithubWallet.from_tokens(["token 1", "token 2"])
73 # var api = wallet.api
76 # The wallet will automatically cycle through the registered tokens to find one
79 # If no valid token is found after all of them was tried, the wallet returns a
80 # client based on the last tried one.
86 # Github OAuth tokens wallet
90 var tokens
= new Array[String]
92 # Init `self` from a collection of tokens
93 init from_tokens
(tokens
: Collection[String]) do self.tokens
.add_all tokens
95 # Do not use colors in console output
96 var no_colors
= false is writable
98 # Display debug information about the token processing
99 var verbose
= false is writable
101 # Add a new token in the wallet
102 fun add
(token
: String) do tokens
.add token
104 # Get an instance of GithubAPI based on the next available token.
106 # If no token is found, return an api based on the last exhausted token.
107 fun api
: GithubAPI do
109 if tokens
.is_empty
then
110 message
"No tokens, using `get_github_oauth`"
111 token
= get_github_oauth
113 token
= get_next_token
115 while not check_token
(token
) do
116 if tried
>= tokens
.length
- 1 then
117 message
"Exhausted all tokens, using {token}"
121 token
= get_next_token
124 var api
= new GithubAPI(token
)
125 api
.enable_cache
= true
129 # The current index in the `tokens` array
130 private var current_index
= 0
132 # The current token in the `tokens` array based on `current_index`
133 fun current_token
: String do return tokens
[current_index
]
135 # Get the next token in token `array` based on `current_token`.
137 # If the end of the list is reached, start again from the begining.
138 fun get_next_token
: String do
139 if tokens
.is_empty
then
140 return get_github_oauth
142 var token
= current_token
144 if current_index
< tokens
.length
- 1 then
152 # Check if a token is valid
153 fun check_token
(token
: String): Bool do
154 message
"Try token {token}"
155 var api
= new GithubAPI(token
)
156 api
.load_repo
("nitlang/nit")
157 return not api
.was_error
160 # Print a message depending on `verbose`
161 fun message
(message
: String) do if verbose
then print
"[Github Wallet] {message}"
163 # Show wallet status in console
165 if tokens
.is_empty
then
166 print
"Wallet is empty"
169 print
"Wallet ({tokens.length} tokens):"
170 for token
in tokens
do
172 if check_token
(token
) then
173 status
= if no_colors
then "OK" else "OK".green
175 status
= if no_colors
then "KO" else "KO".red
177 print
" * [{status}] {token}"