From fc42f3dffa364a360c76ff2785813d74f42016c7 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Tue, 11 Apr 2017 22:33:54 -0300 Subject: [PATCH] Add basic client for the GitHub API --- lib/github/client.rb | 21 +++++++++++++++++++ lib/github/collection.rb | 26 +++++++++++++++++++++++ lib/github/issues.rb | 20 ++++++++++++++++++ lib/github/labels.rb | 20 ++++++++++++++++++ lib/github/milestones.rb | 20 ++++++++++++++++++ lib/github/pull_requests.rb | 20 ++++++++++++++++++ lib/github/rate_limit.rb | 41 +++++++++++++++++++++++++++++++++++++ lib/github/releases.rb | 20 ++++++++++++++++++ lib/github/repositories.rb | 17 +++++++++++++++ lib/github/response.rb | 22 ++++++++++++++++++++ lib/github/user.rb | 23 +++++++++++++++++++++ 11 files changed, 250 insertions(+) create mode 100644 lib/github/client.rb create mode 100644 lib/github/collection.rb create mode 100644 lib/github/issues.rb create mode 100644 lib/github/labels.rb create mode 100644 lib/github/milestones.rb create mode 100644 lib/github/pull_requests.rb create mode 100644 lib/github/rate_limit.rb create mode 100644 lib/github/releases.rb create mode 100644 lib/github/repositories.rb create mode 100644 lib/github/response.rb create mode 100644 lib/github/user.rb diff --git a/lib/github/client.rb b/lib/github/client.rb new file mode 100644 index 00000000000..2511523bf6a --- /dev/null +++ b/lib/github/client.rb @@ -0,0 +1,21 @@ +module Github + class Client + attr_reader :connection + + def initialize(token) + @connection = Faraday.new(url: 'https://api.github.com') do |faraday| + faraday.adapter :net_http_persistent + faraday.response :json, content_type: /\bjson$/ + faraday.authorization 'token', token + faraday.response :logger + end + end + + def get(url, query = {}) + rate_limit = RateLimit.new(connection) + sleep rate_limit.reset_in if rate_limit.exceed? + + Github::Response.new(connection.get(url, query)) + end + end +end diff --git a/lib/github/collection.rb b/lib/github/collection.rb new file mode 100644 index 00000000000..bbca12a1c84 --- /dev/null +++ b/lib/github/collection.rb @@ -0,0 +1,26 @@ +module Github + class Collection + def initialize(url) + @url = url + end + + def fetch(query = {}) + return [] if @url.blank? + + Enumerator.new do |yielder| + loop do + response = client.get(@url, query) + response.body.each { |item| yielder << item } + raise StopIteration unless response.rels.key?(:next) + @url = response.rels[:next] + end + end.lazy + end + + private + + def client + @client ||= Github::Client.new + end + end +end diff --git a/lib/github/issues.rb b/lib/github/issues.rb new file mode 100644 index 00000000000..27843e1cdd8 --- /dev/null +++ b/lib/github/issues.rb @@ -0,0 +1,20 @@ +module Github + class Issues + attr_reader :owner, :repo + + def initialize(owner, repo) + @owner = owner + @repo = repo + end + + def fetch + Collection.new(issues_url).fetch(state: :all, sort: :created, direction: :asc, per_page: 10) + end + + private + + def issues_url + "/repos/#{owner}/#{repo}/issues" + end + end +end diff --git a/lib/github/labels.rb b/lib/github/labels.rb new file mode 100644 index 00000000000..0ea023888b3 --- /dev/null +++ b/lib/github/labels.rb @@ -0,0 +1,20 @@ +module Github + class Labels + attr_reader :owner, :repo + + def initialize(owner, repo) + @owner = owner + @repo = repo + end + + def fetch + Collection.new(labels_url).fetch + end + + private + + def labels_url + "/repos/#{owner}/#{repo}/labels" + end + end +end diff --git a/lib/github/milestones.rb b/lib/github/milestones.rb new file mode 100644 index 00000000000..d50278105db --- /dev/null +++ b/lib/github/milestones.rb @@ -0,0 +1,20 @@ +module Github + class Milestones + attr_reader :owner, :repo + + def initialize(owner, repo) + @owner = owner + @repo = repo + end + + def fetch + Collection.new(milestones_url).fetch + end + + private + + def milestones_url + "/repos/#{owner}/#{repo}/milestones" + end + end +end diff --git a/lib/github/pull_requests.rb b/lib/github/pull_requests.rb new file mode 100644 index 00000000000..af04c90d454 --- /dev/null +++ b/lib/github/pull_requests.rb @@ -0,0 +1,20 @@ +module Github + class PullRequests + attr_reader :owner, :repo + + def initialize(owner, repo) + @owner = owner + @repo = repo + end + + def fetch + Collection.new(pull_requests_url).fetch(state: :all, sort: :created, direction: :asc) + end + + private + + def pull_requests_url + "/repos/#{owner}/#{repo}/pulls" + end + end +end diff --git a/lib/github/rate_limit.rb b/lib/github/rate_limit.rb new file mode 100644 index 00000000000..30b000f8a9a --- /dev/null +++ b/lib/github/rate_limit.rb @@ -0,0 +1,41 @@ +module Github + class RateLimit + SAFE_REMAINING_REQUESTS = 100.freeze + SAFE_RESET_TIME = 500.freeze + + attr_reader :connection + + def initialize(connection) + @connection = connection + end + + def exceed? + return false unless enabled? + + remaining <= SAFE_REMAINING_REQUESTS + end + + def remaining + @remaining ||= response.body.dig('rate', 'remaining').to_i + end + + def reset_in + @reset ||= response.body.dig('rate', 'reset').to_i + end + + private + + def rate_limit_url + '/rate_limit' + end + + def response + @response ||= connection.get(rate_limit_url) + end + + # GitHub Rate Limit API returns 404 when the rate limit is disabled + def enabled? + response.status != 404 + end + end +end diff --git a/lib/github/releases.rb b/lib/github/releases.rb new file mode 100644 index 00000000000..026ef4e6853 --- /dev/null +++ b/lib/github/releases.rb @@ -0,0 +1,20 @@ +module Github + class Releases + attr_reader :owner, :repo + + def initialize(owner, repo) + @owner = owner + @repo = repo + end + + def fetch + Collection.new(releases_url).fetch + end + + private + + def releases_url + "/repos/#{owner}/#{repo}/releases" + end + end +end diff --git a/lib/github/repositories.rb b/lib/github/repositories.rb new file mode 100644 index 00000000000..42342471102 --- /dev/null +++ b/lib/github/repositories.rb @@ -0,0 +1,17 @@ +module Github + class Repositories + def initialize(username) + @username = username + end + + def fetch + Collection.new(repos_url).fetch + end + + private + + def repos_url + '/user/repos' + end + end +end diff --git a/lib/github/response.rb b/lib/github/response.rb new file mode 100644 index 00000000000..c34b69aa4ea --- /dev/null +++ b/lib/github/response.rb @@ -0,0 +1,22 @@ +module Github + class Response + attr_reader :raw, :headers, :body, :status + + def initialize(response) + @raw = response + @headers = response.headers + @body = response.body + @status = response.status + end + + def rels + links = headers['Link'].to_s.split(', ').map do |link| + href, name = link.match(/<(.*?)>; rel="(\w+)"/).captures + + [name.to_sym, href] + end + + Hash[*links.flatten] + end + end +end diff --git a/lib/github/user.rb b/lib/github/user.rb new file mode 100644 index 00000000000..19fe6230820 --- /dev/null +++ b/lib/github/user.rb @@ -0,0 +1,23 @@ +module Github + class User + attr_reader :username + + def initialize(username) + @username = username + end + + def get + client.get(user_url).body + end + + private + + def client + @client ||= Github::Client.new + end + + def user_url + "/users/#{username}" + end + end +end