lib/bundler/alive/client/git_hub_api.rb in bundler-alive-0.1.2 vs lib/bundler/alive/client/git_hub_api.rb in bundler-alive-0.1.3
- old
+ new
@@ -7,55 +7,155 @@
module Alive
module Client
#
# API Client for GitHub API
#
+ # @see https://docs.github.com/en/rest/search#search-repositories
+ #
module GitHubApi
+ # Environment variable name of GitHub Access Token
ACCESS_TOKEN_ENV_NAME = "BUNDLER_ALIVE_GITHUB_TOKEN"
+ # Separator of query condition
+ QUERY_CONDITION_SEPARATOR = " "
+
+ # Number of attempts to request after too many requests
+ RETRIES_ON_TOO_MANY_REQUESTS = 3
+
+ # Interval second when retrying request
+ RETRY_INTERVAL_SEC_ON_TOO_MANY_REQUESTS = 120
+
#
+ # Max number of conditional operator at once
+ #
+ # @see https://docs.github.com/en/rest/search#limitations-on-query-length
+ QUERY_MAX_OPERATORS_AT_ONCE = 6
+
+ private_constant :QUERY_MAX_OPERATORS_AT_ONCE
+
+ def self.extended(base)
+ base.instance_eval do
+ @rate_limit_exceeded = false
+ @retries_on_too_many_requests = 0
+ end
+ end
+
+ #
# Creates a GitHub client
#
# @return [Octokit::Client]
#
def create_client
Octokit::Client.new(access_token: ENV.fetch(ACCESS_TOKEN_ENV_NAME, nil))
end
#
- # Returns repository URL is archived?
+ # Query repository statuses
#
- # @param [SourceCodeRepositoryUrl] repository_url
+ # @param [Array<RepositoryUrl>] :urls
#
- # @raise [ArgumentError]
- # when repository_uri is not `SourceCodeRepositoryUrl`
+ # @return [StatusResult]
#
+ # rubocop:disable Metrics/MethodLength
+ def query(urls:)
+ collection = StatusCollection.new
+ name_with_archived = get_name_with_statuses(urls)
+ urls.each do |url|
+ yield if block_given?
+
+ gem_name = url.gem_name
+ alive = !name_with_archived[gem_name]
+ status = Status.new(name: gem_name, repository_url: url, alive: alive, checked_at: Time.now)
+ collection = collection.add(gem_name, status)
+ end
+
+ StatusResult.new(collection: collection, error_messages: @error_messages,
+ rate_limit_exceeded: @rate_limit_exceeded)
+ end
+ # rubocop:enable Metrics/MethodLength
+
+ private
+
+ #
+ # Search status of repositories
+ #
+ # @param [Array<RepositoryUrl>] urls
+ #
+ # @return [Hash<String, Boolean>]
+ # gem name with archived or not
+ #
+ # rubocop:disable Metrics/MethodLength
+ def get_name_with_statuses(urls)
+ raise ArgumentError unless urls.instance_of?(Array)
+
+ name_with_status = {}
+ urls.each_slice(QUERY_MAX_OPERATORS_AT_ONCE) do |sliced_urls|
+ q = search_query(sliced_urls)
+ repositories = search_repositories_with_retry(q)
+ next if repositories.nil?
+
+ repositories.each do |repository|
+ name = repository["name"]
+ name_with_status[name] = repository["archived"]
+ end
+ end
+ name_with_status
+ end
+ # rubocop:enable Metrics/MethodLength
+
+ #
+ # Search query of repositories
+ #
+ # @param [Array<RepositoryUrl>] urls
+ #
+ # @return [String]
+ #
+ def search_query(urls)
+ urls.map do |url|
+ "repo:#{slug(url.url)}"
+ end.join(QUERY_CONDITION_SEPARATOR)
+ end
+
+ #
+ # Search repositories
+ #
+ # @param [String] query
+ #
# @raise [Octokit::TooManyRequests]
# when too many requested to GitHub.com
- #
# @raise [SourceCodeClient::SearchRepositoryError]
# when Error without `Octokit::TooManyRequests`
#
- # @return [Boolean]
+ # @return [Array<Sawyer::Resource>|nil]
#
- # rubocop:disable Metrics/MethodLength
- def archived?(repository_url)
- unless repository_url.instance_of?(SourceCodeRepositoryUrl)
- raise ArgumentError, "UnSupported url: #{repository_url}"
+ def search_repositories(query)
+ result = @client.search_repositories(query)
+ result[:items]
+ rescue Octokit::TooManyRequests => e
+ raise e
+ rescue StandardError => e
+ @error_messages << e.message
+ []
+ end
+
+ def search_repositories_with_retry(query)
+ search_repositories(query)
+ rescue Octokit::TooManyRequests
+ if @retries_on_too_many_requests < RETRIES_ON_TOO_MANY_REQUESTS
+ @retries_on_too_many_requests += 1
+ sleep_with_message
+ retry
end
- query = "repo:#{slug(repository_url.url)}"
+ @rate_limit_exceeded = true
+ []
+ end
- begin
- result = @client.search_repositories(query)
- result[:items][0][:archived]
- rescue Octokit::TooManyRequests => e
- raise SourceCodeClient::RateLimitExceededError, e.message
- rescue StandardError => e
- raise SourceCodeClient::SearchRepositoryError, e.message
- end
+ def sleep_with_message
+ puts "Too many requested. Sleep #{RETRY_INTERVAL_SEC_ON_TOO_MANY_REQUESTS} sec."
+ sleep RETRY_INTERVAL_SEC_ON_TOO_MANY_REQUESTS
+ puts "Retry request (#{@retries_on_too_many_requests}/#{RETRIES_ON_TOO_MANY_REQUESTS})"
end
- # rubocop:enable Metrics/MethodLength
#
# Returns slug of repository URL
#
# @param [String] repository_url