github_api2 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +770 -0
- data/LICENSE.txt +20 -0
- data/README.md +741 -0
- data/lib/github_api2/api/actions.rb +60 -0
- data/lib/github_api2/api/arguments.rb +253 -0
- data/lib/github_api2/api/config/property.rb +30 -0
- data/lib/github_api2/api/config/property_set.rb +120 -0
- data/lib/github_api2/api/config.rb +105 -0
- data/lib/github_api2/api/factory.rb +33 -0
- data/lib/github_api2/api.rb +398 -0
- data/lib/github_api2/authorization.rb +75 -0
- data/lib/github_api2/client/activity/events.rb +233 -0
- data/lib/github_api2/client/activity/feeds.rb +50 -0
- data/lib/github_api2/client/activity/notifications.rb +181 -0
- data/lib/github_api2/client/activity/starring.rb +130 -0
- data/lib/github_api2/client/activity/watching.rb +176 -0
- data/lib/github_api2/client/activity.rb +31 -0
- data/lib/github_api2/client/authorizations/app.rb +98 -0
- data/lib/github_api2/client/authorizations.rb +142 -0
- data/lib/github_api2/client/emojis.rb +19 -0
- data/lib/github_api2/client/gists/comments.rb +100 -0
- data/lib/github_api2/client/gists.rb +289 -0
- data/lib/github_api2/client/git_data/blobs.rb +51 -0
- data/lib/github_api2/client/git_data/commits.rb +101 -0
- data/lib/github_api2/client/git_data/references.rb +150 -0
- data/lib/github_api2/client/git_data/tags.rb +95 -0
- data/lib/github_api2/client/git_data/trees.rb +113 -0
- data/lib/github_api2/client/git_data.rb +31 -0
- data/lib/github_api2/client/gitignore.rb +57 -0
- data/lib/github_api2/client/issues/assignees.rb +77 -0
- data/lib/github_api2/client/issues/comments.rb +146 -0
- data/lib/github_api2/client/issues/events.rb +50 -0
- data/lib/github_api2/client/issues/labels.rb +189 -0
- data/lib/github_api2/client/issues/milestones.rb +146 -0
- data/lib/github_api2/client/issues.rb +248 -0
- data/lib/github_api2/client/markdown.rb +62 -0
- data/lib/github_api2/client/meta.rb +19 -0
- data/lib/github_api2/client/orgs/hooks.rb +182 -0
- data/lib/github_api2/client/orgs/members.rb +142 -0
- data/lib/github_api2/client/orgs/memberships.rb +131 -0
- data/lib/github_api2/client/orgs/projects.rb +57 -0
- data/lib/github_api2/client/orgs/teams.rb +407 -0
- data/lib/github_api2/client/orgs.rb +127 -0
- data/lib/github_api2/client/projects/cards.rb +158 -0
- data/lib/github_api2/client/projects/columns.rb +146 -0
- data/lib/github_api2/client/projects.rb +83 -0
- data/lib/github_api2/client/pull_requests/comments.rb +140 -0
- data/lib/github_api2/client/pull_requests/reviews.rb +158 -0
- data/lib/github_api2/client/pull_requests.rb +195 -0
- data/lib/github_api2/client/repos/branches/protections.rb +75 -0
- data/lib/github_api2/client/repos/branches.rb +48 -0
- data/lib/github_api2/client/repos/collaborators.rb +84 -0
- data/lib/github_api2/client/repos/comments.rb +125 -0
- data/lib/github_api2/client/repos/commits.rb +80 -0
- data/lib/github_api2/client/repos/contents.rb +263 -0
- data/lib/github_api2/client/repos/deployments.rb +138 -0
- data/lib/github_api2/client/repos/downloads.rb +62 -0
- data/lib/github_api2/client/repos/forks.rb +50 -0
- data/lib/github_api2/client/repos/hooks.rb +214 -0
- data/lib/github_api2/client/repos/invitations.rb +41 -0
- data/lib/github_api2/client/repos/keys.rb +104 -0
- data/lib/github_api2/client/repos/merging.rb +47 -0
- data/lib/github_api2/client/repos/pages.rb +48 -0
- data/lib/github_api2/client/repos/projects.rb +62 -0
- data/lib/github_api2/client/repos/pub_sub_hubbub.rb +133 -0
- data/lib/github_api2/client/repos/releases/assets.rb +136 -0
- data/lib/github_api2/client/repos/releases/tags.rb +24 -0
- data/lib/github_api2/client/repos/releases.rb +189 -0
- data/lib/github_api2/client/repos/statistics.rb +89 -0
- data/lib/github_api2/client/repos/statuses.rb +91 -0
- data/lib/github_api2/client/repos.rb +473 -0
- data/lib/github_api2/client/say.rb +25 -0
- data/lib/github_api2/client/scopes.rb +46 -0
- data/lib/github_api2/client/search/legacy.rb +111 -0
- data/lib/github_api2/client/search.rb +133 -0
- data/lib/github_api2/client/users/emails.rb +65 -0
- data/lib/github_api2/client/users/followers.rb +115 -0
- data/lib/github_api2/client/users/keys.rb +104 -0
- data/lib/github_api2/client/users.rb +117 -0
- data/lib/github_api2/client.rb +77 -0
- data/lib/github_api2/configuration.rb +70 -0
- data/lib/github_api2/connection.rb +82 -0
- data/lib/github_api2/constants.rb +61 -0
- data/lib/github_api2/core_ext/array.rb +25 -0
- data/lib/github_api2/core_ext/hash.rb +91 -0
- data/lib/github_api2/deprecation.rb +39 -0
- data/lib/github_api2/error/client_error.rb +89 -0
- data/lib/github_api2/error/service_error.rb +223 -0
- data/lib/github_api2/error.rb +32 -0
- data/lib/github_api2/ext/faraday.rb +40 -0
- data/lib/github_api2/mash.rb +7 -0
- data/lib/github_api2/middleware.rb +37 -0
- data/lib/github_api2/mime_type.rb +33 -0
- data/lib/github_api2/normalizer.rb +23 -0
- data/lib/github_api2/null_encoder.rb +25 -0
- data/lib/github_api2/page_iterator.rb +138 -0
- data/lib/github_api2/page_links.rb +63 -0
- data/lib/github_api2/paged_request.rb +42 -0
- data/lib/github_api2/pagination.rb +115 -0
- data/lib/github_api2/parameter_filter.rb +35 -0
- data/lib/github_api2/params_hash.rb +115 -0
- data/lib/github_api2/rate_limit.rb +25 -0
- data/lib/github_api2/request/basic_auth.rb +36 -0
- data/lib/github_api2/request/jsonize.rb +54 -0
- data/lib/github_api2/request/oauth2.rb +45 -0
- data/lib/github_api2/request/verbs.rb +63 -0
- data/lib/github_api2/request.rb +84 -0
- data/lib/github_api2/response/atom_parser.rb +22 -0
- data/lib/github_api2/response/follow_redirects.rb +140 -0
- data/lib/github_api2/response/header.rb +87 -0
- data/lib/github_api2/response/jsonize.rb +28 -0
- data/lib/github_api2/response/mashify.rb +24 -0
- data/lib/github_api2/response/raise_error.rb +22 -0
- data/lib/github_api2/response/xmlize.rb +28 -0
- data/lib/github_api2/response.rb +48 -0
- data/lib/github_api2/response_wrapper.rb +161 -0
- data/lib/github_api2/ssl_certs/cacerts.pem +2183 -0
- data/lib/github_api2/utils/url.rb +63 -0
- data/lib/github_api2/validations/format.rb +26 -0
- data/lib/github_api2/validations/presence.rb +32 -0
- data/lib/github_api2/validations/required.rb +21 -0
- data/lib/github_api2/validations/token.rb +41 -0
- data/lib/github_api2/validations.rb +22 -0
- data/lib/github_api2/version.rb +5 -0
- data/lib/github_api2.rb +92 -0
- metadata +363 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'faraday'
|
|
4
|
+
|
|
5
|
+
module Github
|
|
6
|
+
class Request::Jsonize < Faraday::Middleware
|
|
7
|
+
|
|
8
|
+
CONTENT_TYPE = 'Content-Type'.freeze
|
|
9
|
+
MIME_TYPE = 'application/json'.freeze
|
|
10
|
+
|
|
11
|
+
# dependency 'multi_json'
|
|
12
|
+
|
|
13
|
+
def call(env)
|
|
14
|
+
if request_with_body?(env)
|
|
15
|
+
env[:request_headers][CONTENT_TYPE] ||= MIME_TYPE
|
|
16
|
+
env[:body] = encode_body env[:body] unless env[:body].respond_to?(:to_str)
|
|
17
|
+
elsif safe_to_modify?(env)
|
|
18
|
+
# Ensure valid body for put and post requests
|
|
19
|
+
if [:put, :patch, :post].include?(env[:method])
|
|
20
|
+
env[:body] = encode_body({})
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
@app.call env
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def encode_body(value)
|
|
27
|
+
if MultiJson.respond_to?(:dump)
|
|
28
|
+
MultiJson.dump(value)
|
|
29
|
+
else
|
|
30
|
+
MultiJson.encode(value)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def request_with_body?(env)
|
|
35
|
+
has_body?(env) and safe_to_modify?(env)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def safe_to_modify?(env)
|
|
39
|
+
type = request_type(env)
|
|
40
|
+
type.empty? or type == MIME_TYPE
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Don't encode bodies in string form
|
|
44
|
+
def has_body?(env)
|
|
45
|
+
body = env[:body] and !(body.respond_to?(:to_str) and body.empty?)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def request_type(env)
|
|
49
|
+
type = env[:request_headers][CONTENT_TYPE].to_s
|
|
50
|
+
type = type.split(';', 2).first if type.index(';')
|
|
51
|
+
type
|
|
52
|
+
end
|
|
53
|
+
end # Request::Jsonize
|
|
54
|
+
end # Github
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'faraday'
|
|
4
|
+
require 'oauth2'
|
|
5
|
+
|
|
6
|
+
require_relative '../utils/url'
|
|
7
|
+
|
|
8
|
+
module Github
|
|
9
|
+
class Request
|
|
10
|
+
class OAuth2 < Faraday::Middleware
|
|
11
|
+
include Github::Utils::Url
|
|
12
|
+
|
|
13
|
+
ACCESS_TOKEN = 'access_token'.freeze
|
|
14
|
+
AUTH_HEADER = 'Authorization'.freeze
|
|
15
|
+
|
|
16
|
+
# dependency 'oauth2'
|
|
17
|
+
|
|
18
|
+
def call(env)
|
|
19
|
+
# Extract parameters from the query
|
|
20
|
+
params = { ACCESS_TOKEN => @token }.update query_params(env[:url])
|
|
21
|
+
|
|
22
|
+
if token = params[ACCESS_TOKEN] and !token.empty?
|
|
23
|
+
env[:url].query = build_query params
|
|
24
|
+
env[:request_headers].merge!(AUTH_HEADER => "token #{token}")
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
@app.call env
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def initialize(app, *args)
|
|
31
|
+
super app
|
|
32
|
+
@app = app
|
|
33
|
+
@token = args.shift
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def query_params(url)
|
|
37
|
+
if url.query.nil? or url.query.empty?
|
|
38
|
+
{}
|
|
39
|
+
else
|
|
40
|
+
parse_query url.query
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end # OAuth2
|
|
44
|
+
end # Request
|
|
45
|
+
end # Github
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require_relative '../request'
|
|
4
|
+
require_relative '../params_hash'
|
|
5
|
+
|
|
6
|
+
module Github
|
|
7
|
+
# A class responsible for dispatching http requests
|
|
8
|
+
class Request
|
|
9
|
+
|
|
10
|
+
# Defines HTTP verbs
|
|
11
|
+
module Verbs
|
|
12
|
+
# Make a head request
|
|
13
|
+
#
|
|
14
|
+
# @api public
|
|
15
|
+
def head_request(path, params = ParamsHash.empty)
|
|
16
|
+
Request.new(:head, path, self).call(current_options, params)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Make a get request
|
|
20
|
+
#
|
|
21
|
+
# @api public
|
|
22
|
+
def get_request(path, params = ParamsHash.empty)
|
|
23
|
+
request = Request.new(:get, path, self).call(current_options, params)
|
|
24
|
+
request.auto_paginate
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Make a patch request
|
|
28
|
+
#
|
|
29
|
+
# @api public
|
|
30
|
+
def patch_request(path, params = ParamsHash.empty)
|
|
31
|
+
Request.new(:patch, path, self).call(current_options, params)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Make a post request
|
|
35
|
+
#
|
|
36
|
+
# @api public
|
|
37
|
+
def post_request(path, params = ParamsHash.empty)
|
|
38
|
+
Request.new(:post, path, self).call(current_options, params)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Make a put request
|
|
42
|
+
#
|
|
43
|
+
# @api public
|
|
44
|
+
def put_request(path, params = ParamsHash.empty)
|
|
45
|
+
Request.new(:put, path, self).call(current_options, params)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Make a delete request
|
|
49
|
+
#
|
|
50
|
+
# @api public
|
|
51
|
+
def delete_request(path, params = ParamsHash.empty)
|
|
52
|
+
Request.new(:delete, path, self).call(current_options, params)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Make a options request
|
|
56
|
+
#
|
|
57
|
+
# @api public
|
|
58
|
+
def options_request(path, params = ParamsHash.empty)
|
|
59
|
+
Request.new(:options, path, self).call(current_options, params)
|
|
60
|
+
end
|
|
61
|
+
end # Verbs
|
|
62
|
+
end # Request
|
|
63
|
+
end # Github
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require_relative 'connection'
|
|
4
|
+
require_relative 'response_wrapper'
|
|
5
|
+
require_relative 'request/oauth2'
|
|
6
|
+
require_relative 'request/basic_auth'
|
|
7
|
+
require_relative 'request/jsonize'
|
|
8
|
+
|
|
9
|
+
module Github
|
|
10
|
+
# A class responsible for dispatching http requests
|
|
11
|
+
class Request
|
|
12
|
+
include Connection
|
|
13
|
+
|
|
14
|
+
HTTP_METHODS = [:get, :head, :post, :put, :delete, :patch]
|
|
15
|
+
|
|
16
|
+
METHODS_WITH_BODIES = [:post, :put, :patch]
|
|
17
|
+
|
|
18
|
+
# Return http verb
|
|
19
|
+
#
|
|
20
|
+
# @return [Symbol]
|
|
21
|
+
attr_reader :action
|
|
22
|
+
|
|
23
|
+
# Return url
|
|
24
|
+
#
|
|
25
|
+
# @return [String]
|
|
26
|
+
attr_accessor :path
|
|
27
|
+
|
|
28
|
+
# Return api this request is associated with
|
|
29
|
+
#
|
|
30
|
+
# @return [Github::API]
|
|
31
|
+
attr_reader :api
|
|
32
|
+
|
|
33
|
+
# Create a new Request
|
|
34
|
+
#
|
|
35
|
+
# @return [Github::Request]
|
|
36
|
+
#
|
|
37
|
+
# @api public
|
|
38
|
+
def initialize(action, path, api)
|
|
39
|
+
@action = action
|
|
40
|
+
@path = path
|
|
41
|
+
@api = api
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Performs a request
|
|
45
|
+
#
|
|
46
|
+
# @param current_options [Hash]
|
|
47
|
+
# @param [ParamsHash] params - ParamsHash to configure the request API
|
|
48
|
+
#
|
|
49
|
+
# @return [Github::ResponseWrapper]
|
|
50
|
+
#
|
|
51
|
+
# @api private
|
|
52
|
+
def call(current_options, params)
|
|
53
|
+
unless HTTP_METHODS.include?(action)
|
|
54
|
+
raise ArgumentError, "unknown http method: #{action}"
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
puts "EXECUTED: #{action} - #{path} with PARAMS: #{params.request_params}" if ENV['DEBUG']
|
|
58
|
+
|
|
59
|
+
request_options = params.options
|
|
60
|
+
connection_options = current_options.merge(request_options)
|
|
61
|
+
conn = connection(api, connection_options)
|
|
62
|
+
|
|
63
|
+
self.path = Utils::Url.normalize(self.path)
|
|
64
|
+
if conn.path_prefix != '/' && self.path.index(conn.path_prefix) != 0
|
|
65
|
+
self.path = (conn.path_prefix + self.path).gsub(/\/(\/)*/, '/')
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
response = conn.send(action) do |request|
|
|
69
|
+
case action.to_sym
|
|
70
|
+
when *(HTTP_METHODS - METHODS_WITH_BODIES)
|
|
71
|
+
request.body = params.data if params.key?('data')
|
|
72
|
+
if params.key?('encoder')
|
|
73
|
+
request.params.params_encoder(params.encoder)
|
|
74
|
+
end
|
|
75
|
+
request.url(self.path, params.request_params)
|
|
76
|
+
when *METHODS_WITH_BODIES
|
|
77
|
+
request.url(self.path, connection_options[:query] || {})
|
|
78
|
+
request.body = params.data unless params.empty?
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
ResponseWrapper.new(response, api)
|
|
82
|
+
end
|
|
83
|
+
end # Request
|
|
84
|
+
end # Github
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'faraday'
|
|
4
|
+
|
|
5
|
+
module Github
|
|
6
|
+
class Response::AtomParser < Response
|
|
7
|
+
define_parser do |body|
|
|
8
|
+
require 'rss'
|
|
9
|
+
RSS::Parser.parse(body)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def initialize(app, options = {})
|
|
13
|
+
super(app, options.merge(content_type: /(\batom|\brss)/))
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def on_complete(env)
|
|
17
|
+
if parse_body?(env)
|
|
18
|
+
process_body(env)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
require 'faraday'
|
|
2
|
+
require 'set'
|
|
3
|
+
|
|
4
|
+
# First saw on octokit, then copied from lostisland/faraday_middleware
|
|
5
|
+
# and adapted for this library.
|
|
6
|
+
#
|
|
7
|
+
# faraday_middleware/lib/faraday_middleware/response/follow_redirects.rb
|
|
8
|
+
|
|
9
|
+
module Github
|
|
10
|
+
# Public: Exception thrown when the maximum amount of requests is exceeded.
|
|
11
|
+
class RedirectLimitReached < Faraday::ClientError
|
|
12
|
+
attr_reader :response
|
|
13
|
+
|
|
14
|
+
def initialize(response)
|
|
15
|
+
super "too many redirects; last one to: #{response['location']}"
|
|
16
|
+
@response = response
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Public: Follow HTTP 301, 302, 303, 307, and 308 redirects.
|
|
21
|
+
#
|
|
22
|
+
# For HTTP 301, 302, and 303, the original GET, POST, PUT, DELETE, or PATCH
|
|
23
|
+
# request gets converted into a GET. With `:standards_compliant => true`,
|
|
24
|
+
# however, the HTTP method after 301/302 remains unchanged. This allows you
|
|
25
|
+
# to opt into HTTP/1.1 compliance and act unlike the major web browsers.
|
|
26
|
+
#
|
|
27
|
+
# This middleware currently only works with synchronous requests; i.e. it
|
|
28
|
+
# doesn't support parallelism.
|
|
29
|
+
#
|
|
30
|
+
# If you wish to persist cookies across redirects, you could use
|
|
31
|
+
# the faraday-cookie_jar gem:
|
|
32
|
+
#
|
|
33
|
+
# Faraday.new(:url => url) do |faraday|
|
|
34
|
+
# faraday.use FaradayMiddleware::FollowRedirects
|
|
35
|
+
# faraday.use :cookie_jar
|
|
36
|
+
# faraday.adapter Faraday.default_adapter
|
|
37
|
+
# end
|
|
38
|
+
|
|
39
|
+
class Response::FollowRedirects < Faraday::Middleware
|
|
40
|
+
# HTTP methods for which 30x redirects can be followed
|
|
41
|
+
ALLOWED_METHODS = Set.new [:head, :options, :get, :post, :put, :patch, :delete]
|
|
42
|
+
# HTTP redirect status codes that this middleware implements
|
|
43
|
+
REDIRECT_CODES = Set.new [301, 302, 303, 307, 308]
|
|
44
|
+
# Keys in env hash which will get cleared between requests
|
|
45
|
+
ENV_TO_CLEAR = Set.new [:status, :response, :response_headers]
|
|
46
|
+
|
|
47
|
+
# Default value for max redirects followed
|
|
48
|
+
FOLLOW_LIMIT = 3
|
|
49
|
+
|
|
50
|
+
# Regex that matches characters that need to be escaped in URLs, sans
|
|
51
|
+
# the "%" character which we assume already represents an escaped sequence.
|
|
52
|
+
URI_UNSAFE = /[^\-_.!~*'()a-zA-Z\d;\/?:@&=+$,\[\]%]/
|
|
53
|
+
|
|
54
|
+
# Public: Initialize the middleware.
|
|
55
|
+
#
|
|
56
|
+
# options - An options Hash (default: {}):
|
|
57
|
+
# :limit - A Numeric redirect limit (default: 3)
|
|
58
|
+
# :standards_compliant - A Boolean indicating whether to respect
|
|
59
|
+
# the HTTP spec when following 301/302
|
|
60
|
+
# (default: false)
|
|
61
|
+
# :callback - A callable that will be called on redirects
|
|
62
|
+
# with the old and new envs
|
|
63
|
+
def initialize(app, options = {})
|
|
64
|
+
super(app)
|
|
65
|
+
@options = options
|
|
66
|
+
|
|
67
|
+
@convert_to_get = Set.new [303]
|
|
68
|
+
@convert_to_get << 301 << 302 unless standards_compliant?
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def call(env)
|
|
72
|
+
perform_with_redirection(env, follow_limit)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
private
|
|
76
|
+
|
|
77
|
+
def convert_to_get?(response)
|
|
78
|
+
![:head, :options].include?(response.env[:method]) &&
|
|
79
|
+
@convert_to_get.include?(response.status)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def perform_with_redirection(env, follows)
|
|
83
|
+
request_body = env[:body]
|
|
84
|
+
response = @app.call(env)
|
|
85
|
+
|
|
86
|
+
response.on_complete do |response_env|
|
|
87
|
+
if follow_redirect?(response_env, response)
|
|
88
|
+
raise RedirectLimitReached, response if follows.zero?
|
|
89
|
+
new_request_env = update_env(response_env.dup, request_body, response)
|
|
90
|
+
callback.call(response_env, new_request_env) if callback
|
|
91
|
+
response = perform_with_redirection(new_request_env, follows - 1)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
response
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def update_env(env, request_body, response)
|
|
98
|
+
env[:url] += safe_escape(response['location'])
|
|
99
|
+
|
|
100
|
+
if convert_to_get?(response)
|
|
101
|
+
env[:method] = :get
|
|
102
|
+
env[:body] = nil
|
|
103
|
+
else
|
|
104
|
+
env[:body] = request_body
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
ENV_TO_CLEAR.each {|key| env.delete key }
|
|
108
|
+
|
|
109
|
+
env
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def follow_redirect?(env, response)
|
|
113
|
+
ALLOWED_METHODS.include? env[:method] and
|
|
114
|
+
REDIRECT_CODES.include? response.status
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def follow_limit
|
|
118
|
+
@options.fetch(:limit, FOLLOW_LIMIT)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def standards_compliant?
|
|
122
|
+
@options.fetch(:standards_compliant, false)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def callback
|
|
126
|
+
@options[:callback]
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Internal: escapes unsafe characters from an URL which might be a path
|
|
130
|
+
# component only or a fully qualified URI so that it can be joined onto an
|
|
131
|
+
# URI:HTTP using the `+` operator. Doesn't escape "%" characters so to not
|
|
132
|
+
# risk double-escaping.
|
|
133
|
+
def safe_escape(uri)
|
|
134
|
+
uri = uri.split('#')[0] # we want to remove the fragment if present
|
|
135
|
+
uri.to_s.gsub(URI_UNSAFE) { |match|
|
|
136
|
+
'%' + match.unpack('H2' * match.bytesize).join('%').upcase
|
|
137
|
+
}
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require_relative '../constants'
|
|
4
|
+
|
|
5
|
+
module Github
|
|
6
|
+
class Response
|
|
7
|
+
|
|
8
|
+
# Represents http response header
|
|
9
|
+
class Header < Struct.new(:env)
|
|
10
|
+
include Github::Constants
|
|
11
|
+
|
|
12
|
+
SUCCESSFUL_STATUSES = 200..299
|
|
13
|
+
|
|
14
|
+
def loaded?
|
|
15
|
+
!!env
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def [](property)
|
|
19
|
+
loaded? ? env[:response_headers][property] : nil
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def oauth_scopes
|
|
23
|
+
loaded? ? env[:response_headers][OAUTH_SCOPES] : nil
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def accepted_oauth_scopes
|
|
27
|
+
loaded? ? env[:response_headers][ACCEPTED_OAUTH_SCOPES] : nil
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Requests are limited to API v3 to 5000 per hour.
|
|
31
|
+
def ratelimit_limit
|
|
32
|
+
loaded? ? env[:response_headers][RATELIMIT_LIMIT] : nil
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def ratelimit_remaining
|
|
36
|
+
loaded? ? env[:response_headers][RATELIMIT_REMAINING] : nil
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# A unix timestamp describing when the ratelimit will
|
|
40
|
+
# be reset
|
|
41
|
+
def ratelimit_reset
|
|
42
|
+
loaded? ? env[:response_headers][RATELIMIT_RESET] : nil
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def cache_control
|
|
46
|
+
loaded? ? env[:response_headers][CACHE_CONTROL] : nil
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def content_type
|
|
50
|
+
loaded? ? env[:response_headers][CONTENT_TYPE] : nil
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def content_length
|
|
54
|
+
loaded? ? env[:response_headers][CONTENT_LENGTH] : nil
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def etag
|
|
58
|
+
loaded? ? env[:response_headers][ETAG] : nil
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def date
|
|
62
|
+
loaded? ? env[:response_headers][DATE] : nil
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def location
|
|
66
|
+
loaded? ? env[:response_headers][LOCATION] : nil
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def server
|
|
70
|
+
loaded? ? env[:response_headers][SERVER] : nil
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def status
|
|
74
|
+
loaded? ? env[:status] : nil
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def success?
|
|
78
|
+
SUCCESSFUL_STATUSES.include? status
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Returns raw body
|
|
82
|
+
def body
|
|
83
|
+
loaded? ? env[:body] : nil
|
|
84
|
+
end
|
|
85
|
+
end # Header
|
|
86
|
+
end # Response
|
|
87
|
+
end # Github
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'faraday'
|
|
4
|
+
require 'json'
|
|
5
|
+
|
|
6
|
+
module Github
|
|
7
|
+
class Response::Jsonize < Response
|
|
8
|
+
|
|
9
|
+
# dependency 'json'
|
|
10
|
+
|
|
11
|
+
define_parser do |body|
|
|
12
|
+
JSON.parse(body)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def parse(body)
|
|
16
|
+
case body
|
|
17
|
+
when ''
|
|
18
|
+
nil
|
|
19
|
+
when 'true'
|
|
20
|
+
true
|
|
21
|
+
when 'false'
|
|
22
|
+
false
|
|
23
|
+
else
|
|
24
|
+
self.class.parser.call(body)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end # Response::Jsonize
|
|
28
|
+
end # Github
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'faraday'
|
|
4
|
+
require 'hashie'
|
|
5
|
+
require 'github_api2/mash'
|
|
6
|
+
|
|
7
|
+
module Github
|
|
8
|
+
class Response::Mashify < Response
|
|
9
|
+
define_parser do |body|
|
|
10
|
+
::Github::Mash.new body
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def parse(body)
|
|
14
|
+
case body
|
|
15
|
+
when Hash
|
|
16
|
+
self.class.parser.call body
|
|
17
|
+
when Array
|
|
18
|
+
body.map { |item| item.is_a?(Hash) ? self.class.parser.call(item) : item }
|
|
19
|
+
else
|
|
20
|
+
body
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end # Response::Mashify
|
|
24
|
+
end # Github
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'faraday'
|
|
4
|
+
|
|
5
|
+
require_relative '../error/service_error'
|
|
6
|
+
|
|
7
|
+
module Github
|
|
8
|
+
class Response::RaiseError < Faraday::Response::Middleware
|
|
9
|
+
# Check if status code requires raising a ServiceError
|
|
10
|
+
#
|
|
11
|
+
# @api private
|
|
12
|
+
def on_complete(env)
|
|
13
|
+
status_code = env[:status].to_i
|
|
14
|
+
service_error = Github::Error::ServiceError
|
|
15
|
+
error_class = service_error.error_mapping[status_code]
|
|
16
|
+
if !error_class and (400...600) === status_code
|
|
17
|
+
error_class = service_error
|
|
18
|
+
end
|
|
19
|
+
raise error_class.new(env) if error_class
|
|
20
|
+
end
|
|
21
|
+
end # Response::RaiseError
|
|
22
|
+
end # Github
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'faraday'
|
|
4
|
+
require 'nokogiri'
|
|
5
|
+
|
|
6
|
+
module Github
|
|
7
|
+
class Response::Xmlize < Response
|
|
8
|
+
|
|
9
|
+
# dependency 'nokogiri'
|
|
10
|
+
|
|
11
|
+
define_parser do |body|
|
|
12
|
+
::Nokogiri::XML body
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def parse(body)
|
|
16
|
+
case body
|
|
17
|
+
when ''
|
|
18
|
+
nil
|
|
19
|
+
when 'true'
|
|
20
|
+
true
|
|
21
|
+
when 'false'
|
|
22
|
+
false
|
|
23
|
+
else
|
|
24
|
+
self.class.parser.call(body)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end # Response::Xmlize
|
|
28
|
+
end # Github
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'faraday'
|
|
4
|
+
|
|
5
|
+
module Github
|
|
6
|
+
# Contains methods and attributes that act on the response returned from the
|
|
7
|
+
# request
|
|
8
|
+
class Response < Faraday::Response::Middleware
|
|
9
|
+
CONTENT_TYPE = 'Content-Type'.freeze
|
|
10
|
+
|
|
11
|
+
class << self
|
|
12
|
+
attr_accessor :parser
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.define_parser(&block)
|
|
16
|
+
@parser = block
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def initialize(app, options = {})
|
|
20
|
+
super(app)
|
|
21
|
+
@content_types = Array(options[:content_type])
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def process_body(env)
|
|
25
|
+
env[:body] = parse(env[:body])
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def parse_body?(env)
|
|
29
|
+
parse_response_type?(response_type(env)) and parse_response?(env)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def response_type(env)
|
|
33
|
+
type = env[:response_headers][CONTENT_TYPE].to_s
|
|
34
|
+
type = type.split(';', 2).first if type.index(';')
|
|
35
|
+
type
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def parse_response_type?(type)
|
|
39
|
+
@content_types.empty? || @content_types.any? { |pattern|
|
|
40
|
+
pattern.is_a?(Regexp) ? type =~ pattern : type == pattern
|
|
41
|
+
}
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def parse_response?(env)
|
|
45
|
+
env[:body].respond_to?(:to_str)
|
|
46
|
+
end
|
|
47
|
+
end # Response
|
|
48
|
+
end # Github
|