When using batch mode with the github
API, we can rapidly reach the rate
limit allowed by Github.
One solution consists in using a wallet of tokens so we can rely on more than one token and switch them when one become exhausted.
One functionality of the wallet is to check the validity of a token against
the API. check_token
will return false if a token is invalid or exhausted.
var wallet = new GithubWallet
assert not wallet.check_token("this is a bad token")
The wallet can also be used to store tokens and check all of them.
wallet = new GithubWallet
wallet.add "some token"
wallet.add "some other token"
or
wallet = new GithubWallet(["token 1", "token 2"])
The show_status
method can be used to display a summary of the validity of
each token in the wallet.
wallet.show_status
Will display something like this:
Wallet (2 tokens):
* [OK] token 1
* [KO] token 2
Using the wallet you can cycle through tokens and obtain a new Github API client instance with a fresh rate limit.
wallet = new GithubWallet(["token 1", "token 2"])
var api = wallet.api
The wallet will automatically cycle through the registered tokens to find one that works.
If no valid token is found after all of them was tried, the wallet returns a client based on the last tried one.
Serializable::inspect
to show more useful information
curl :: native_curl
Binding of C libCurl which allow us to interact with network.serialization :: serialization_core
Abstract services to serialize Nit objects to different formatsdeserialize_json
and JsonDeserializer
serialize_to_json
and JsonSerializer
core :: union_find
union–find algorithm using an efficient disjoint-set data structure
# Github OAuth tokens management
#
# When using batch mode with the `github` API, we can rapidly reach the rate
# limit allowed by Github.
#
# One solution consists in using a wallet of tokens so we can rely on more than
# one token and switch them when one become exhausted.
#
# ## Using the Github wallet to check tokens
#
# One functionality of the wallet is to check the validity of a token against
# the API. `check_token` will return false if a token is invalid or exhausted.
#
# ~~~
# var wallet = new GithubWallet
# assert not wallet.check_token("this is a bad token")
# ~~~
#
# ## Storing tokens
#
# The wallet can also be used to store tokens and check all of them.
#
# ~~~
# wallet = new GithubWallet
# wallet.add "some token"
# wallet.add "some other token"
# ~~~
#
# or
#
# ~~~
# wallet = new GithubWallet(["token 1", "token 2"])
# ~~~
#
# The `show_status` method can be used to display a summary of the validity of
# each token in the wallet.
#
# ~~~
# wallet.show_status
# ~~~
#
# Will display something like this:
#
# ~~~raw
# Wallet (2 tokens):
# * [OK] token 1
# * [KO] token 2
# ~~~
#
# ## Using the wallet to obtain a Github API client
#
# Using the wallet you can cycle through tokens and obtain a new Github API client
# instance with a fresh rate limit.
#
# ~~~
# wallet = new GithubWallet(["token 1", "token 2"])
# var api = wallet.api
# ~~~
#
# The wallet will automatically cycle through the registered tokens to find one
# that works.
#
# If no valid token is found after all of them was tried, the wallet returns a
# client based on the last tried one.
module wallet
import github
import logger
# Github OAuth tokens wallet
class GithubWallet
# Github API tokens
var tokens = new Array[String] is optional
# Logger used to display info about tokens state
var logger = new Logger is optional, writable
# Add a new token in the wallet
fun add(token: String) do tokens.add token
# Get an instance of GithubAPI based on the next available token.
#
# If no token is found, return an api based on the last exhausted token.
fun api: GithubAPI do
var token
if tokens.is_empty then
logger.warn "No tokens, using `get_github_oauth`"
token = get_github_oauth
else
token = get_next_token
var tried = 0
while not check_token(token) do
if tried >= tokens.length - 1 then
logger.warn "Exhausted all tokens, using {token}"
break
end
tried += 1
token = get_next_token
end
end
var api = new GithubAPI(token)
api.enable_cache = true
return api
end
# The current index in the `tokens` array
private var current_index = 0
# The current token in the `tokens` array based on `current_index`
fun current_token: String do return tokens[current_index]
# Get the next token in token `array` based on `current_token`.
#
# If the end of the list is reached, start again from the begining.
fun get_next_token: String do
if tokens.is_empty then
return get_github_oauth
end
var token = current_token
if current_index < tokens.length - 1 then
current_index += 1
else
current_index = 0
end
return token
end
# Check if a token is valid
fun check_token(token: String): Bool do
logger.debug "Try token {token}"
var api = new GithubAPI(token)
api.get_auth_user
return not api.was_error
end
# Show wallet status in console
fun show_status(no_color: nullable Bool) do
no_color = no_color or else false
if tokens.is_empty then
print "Wallet is empty"
return
end
print "Wallet ({tokens.length} tokens):"
for token in tokens do
var status
if check_token(token) then
status = if no_color then "OK" else "OK".green
else
status = if no_color then "KO" else "KO".red
end
print " * [{status}] {token}"
end
end
end
lib/github/wallet.nit:17,1--173,3