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,82 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'faraday'
|
|
4
|
+
|
|
5
|
+
require_relative 'constants'
|
|
6
|
+
|
|
7
|
+
module Github
|
|
8
|
+
# Specifies Http connection options
|
|
9
|
+
module Connection
|
|
10
|
+
extend self
|
|
11
|
+
include Github::Constants
|
|
12
|
+
|
|
13
|
+
ALLOWED_OPTIONS = [
|
|
14
|
+
:headers,
|
|
15
|
+
:url,
|
|
16
|
+
:params,
|
|
17
|
+
:request,
|
|
18
|
+
:ssl
|
|
19
|
+
].freeze
|
|
20
|
+
|
|
21
|
+
# Default requets header information
|
|
22
|
+
#
|
|
23
|
+
# @return [Hash[String]]
|
|
24
|
+
#
|
|
25
|
+
# @api private
|
|
26
|
+
def default_headers
|
|
27
|
+
{
|
|
28
|
+
ACCEPT => 'application/vnd.github.v3+json,' \
|
|
29
|
+
'application/vnd.github.beta+json;q=0.5,' \
|
|
30
|
+
'application/json;q=0.1',
|
|
31
|
+
ACCEPT_CHARSET => 'utf-8'
|
|
32
|
+
}
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Create default connection options
|
|
36
|
+
#
|
|
37
|
+
# @return [Hash[Symbol]]
|
|
38
|
+
# the default options
|
|
39
|
+
#
|
|
40
|
+
# @api private
|
|
41
|
+
def default_options(options = {})
|
|
42
|
+
headers = default_headers.merge(options[:headers] || {})
|
|
43
|
+
headers.merge!({USER_AGENT => options[:user_agent]})
|
|
44
|
+
{
|
|
45
|
+
headers: headers,
|
|
46
|
+
ssl: options[:ssl],
|
|
47
|
+
url: options[:endpoint]
|
|
48
|
+
}
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Exposes middleware builder to facilitate custom stacks and easy
|
|
52
|
+
# addition of new extensions such as cache adapter.
|
|
53
|
+
#
|
|
54
|
+
# @api public
|
|
55
|
+
def stack(options = {})
|
|
56
|
+
@stack ||= begin
|
|
57
|
+
builder_class = if defined?(Faraday::RackBuilder)
|
|
58
|
+
Faraday::RackBuilder
|
|
59
|
+
else
|
|
60
|
+
Faraday::Builder
|
|
61
|
+
end
|
|
62
|
+
builder_class.new(&Github.default_middleware(options))
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Creates http connection
|
|
67
|
+
#
|
|
68
|
+
# Returns a Faraday::Connection object
|
|
69
|
+
def connection(api, options = {})
|
|
70
|
+
connection_options = default_options(options)
|
|
71
|
+
connection_options.merge!(builder: stack(options.merge!(api: api)))
|
|
72
|
+
if options[:connection_options]
|
|
73
|
+
connection_options.deep_merge!(options[:connection_options])
|
|
74
|
+
end
|
|
75
|
+
if ENV['DEBUG']
|
|
76
|
+
p "Connection options : \n"
|
|
77
|
+
pp connection_options
|
|
78
|
+
end
|
|
79
|
+
Faraday.new(connection_options)
|
|
80
|
+
end
|
|
81
|
+
end # Connection
|
|
82
|
+
end # Github
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
module Github
|
|
2
|
+
module Constants
|
|
3
|
+
extend self
|
|
4
|
+
|
|
5
|
+
# Response headers
|
|
6
|
+
RATELIMIT_REMAINING = 'X-RateLimit-Remaining'.freeze
|
|
7
|
+
|
|
8
|
+
RATELIMIT_LIMIT = 'X-RateLimit-Limit'.freeze
|
|
9
|
+
|
|
10
|
+
RATELIMIT_RESET = 'X-RateLimit-Reset'.freeze
|
|
11
|
+
|
|
12
|
+
CONTENT_TYPE = 'Content-Type'.freeze
|
|
13
|
+
|
|
14
|
+
CONTENT_LENGTH = 'content-length'.freeze
|
|
15
|
+
|
|
16
|
+
CACHE_CONTROL = 'cache-control'.freeze
|
|
17
|
+
|
|
18
|
+
ETAG = 'ETag'.freeze
|
|
19
|
+
|
|
20
|
+
SERVER = 'Server'.freeze
|
|
21
|
+
|
|
22
|
+
DATE = 'Date'.freeze
|
|
23
|
+
|
|
24
|
+
LOCATION = 'Location'.freeze
|
|
25
|
+
|
|
26
|
+
USER_AGENT = 'User-Agent'.freeze
|
|
27
|
+
|
|
28
|
+
ACCEPT = 'Accept'.freeze
|
|
29
|
+
|
|
30
|
+
ACCEPT_CHARSET = 'Accept-Charset'.freeze
|
|
31
|
+
|
|
32
|
+
OAUTH_SCOPES = 'X-OAuth-Scopes'.freeze
|
|
33
|
+
|
|
34
|
+
ACCEPTED_OAUTH_SCOPES = 'X-Accepted-Oauth-Scopes'.freeze
|
|
35
|
+
|
|
36
|
+
# Link headers
|
|
37
|
+
HEADER_LINK = "Link".freeze
|
|
38
|
+
|
|
39
|
+
HEADER_NEXT = "X-Next".freeze
|
|
40
|
+
|
|
41
|
+
HEADER_LAST = "X-Last".freeze
|
|
42
|
+
|
|
43
|
+
META_REL = "rel".freeze
|
|
44
|
+
|
|
45
|
+
META_LAST = "last".freeze
|
|
46
|
+
|
|
47
|
+
META_NEXT = "next".freeze
|
|
48
|
+
|
|
49
|
+
META_FIRST = "first".freeze
|
|
50
|
+
|
|
51
|
+
META_PREV = "prev".freeze
|
|
52
|
+
|
|
53
|
+
PARAM_PAGE = "page".freeze
|
|
54
|
+
|
|
55
|
+
PARAM_PER_PAGE = "per_page".freeze
|
|
56
|
+
|
|
57
|
+
PARAM_START_PAGE = "start_page".freeze
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
end # Constants
|
|
61
|
+
end # Github
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
class Array # :nodoc:
|
|
4
|
+
|
|
5
|
+
# Returns a new arrray with keys removed
|
|
6
|
+
#
|
|
7
|
+
def except(*keys)
|
|
8
|
+
self.dup.except!(*keys)
|
|
9
|
+
end unless method_defined? :except
|
|
10
|
+
|
|
11
|
+
# Similar to except but modifies self
|
|
12
|
+
#
|
|
13
|
+
def except!(*items)
|
|
14
|
+
copy = self.dup
|
|
15
|
+
copy.reject! { |item| items.include? item }
|
|
16
|
+
copy
|
|
17
|
+
end unless method_defined? :except!
|
|
18
|
+
|
|
19
|
+
# Selects a hash from the arguments list
|
|
20
|
+
#
|
|
21
|
+
def extract_options!
|
|
22
|
+
last.is_a?(::Hash) ? pop : {}
|
|
23
|
+
end unless method_defined? :extract_options!
|
|
24
|
+
|
|
25
|
+
end # Array
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
class Hash # :nodoc:
|
|
4
|
+
|
|
5
|
+
# Returns a new hash with keys removed
|
|
6
|
+
#
|
|
7
|
+
def except(*items)
|
|
8
|
+
self.dup.except!(*items)
|
|
9
|
+
end unless method_defined? :except
|
|
10
|
+
|
|
11
|
+
# Similar to except but modifies self
|
|
12
|
+
#
|
|
13
|
+
def except!(*keys)
|
|
14
|
+
keys.each { |key| delete(key) }
|
|
15
|
+
self
|
|
16
|
+
end unless method_defined? :except!
|
|
17
|
+
|
|
18
|
+
# Returns a new hash with all the keys converted to symbols
|
|
19
|
+
#
|
|
20
|
+
def symbolize_keys
|
|
21
|
+
inject({}) do |hash, (key, value)|
|
|
22
|
+
hash[(key.to_sym rescue key) || key] = value
|
|
23
|
+
hash
|
|
24
|
+
end
|
|
25
|
+
end unless method_defined? :symbolize_keys
|
|
26
|
+
|
|
27
|
+
# Similar to symbolize_keys but modifies self
|
|
28
|
+
#
|
|
29
|
+
def symbolize_keys!
|
|
30
|
+
hash = symbolize_keys
|
|
31
|
+
hash.each do |key, val|
|
|
32
|
+
hash[key] = case val
|
|
33
|
+
when Hash
|
|
34
|
+
val.symbolize_keys!
|
|
35
|
+
when Array
|
|
36
|
+
val.map do |item|
|
|
37
|
+
item.is_a?(Hash) ? item.symbolize_keys! : item
|
|
38
|
+
end
|
|
39
|
+
else
|
|
40
|
+
val
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
return hash
|
|
44
|
+
end unless method_defined? :symbolize_keys!
|
|
45
|
+
|
|
46
|
+
# Returns hash collapsed into a query string
|
|
47
|
+
#
|
|
48
|
+
def serialize
|
|
49
|
+
self.map { |key, val| [key, val].join("=") }.join("&")
|
|
50
|
+
end unless method_defined? :serialize
|
|
51
|
+
|
|
52
|
+
# Searches for all deeply nested keys
|
|
53
|
+
#
|
|
54
|
+
def deep_keys
|
|
55
|
+
keys = self.keys
|
|
56
|
+
keys.each do |key|
|
|
57
|
+
if self[key].is_a?(Hash)
|
|
58
|
+
keys << self[key].deep_keys.compact.flatten
|
|
59
|
+
next
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
keys.flatten
|
|
63
|
+
end unless method_defined? :deep_keys
|
|
64
|
+
|
|
65
|
+
# Returns true if the given key is present inside deeply nested hash
|
|
66
|
+
#
|
|
67
|
+
def deep_key?(key)
|
|
68
|
+
self.deep_keys.include? key
|
|
69
|
+
end unless method_defined? :deep_key?
|
|
70
|
+
|
|
71
|
+
# Recursively merges self with other hash and returns new hash.
|
|
72
|
+
#
|
|
73
|
+
def deep_merge(other, &block)
|
|
74
|
+
dup.deep_merge!(other, &block)
|
|
75
|
+
end unless method_defined? :deep_merge
|
|
76
|
+
|
|
77
|
+
# Similar as deep_merge but modifies self
|
|
78
|
+
#
|
|
79
|
+
def deep_merge!(other, &block)
|
|
80
|
+
other.each_pair do |key, val|
|
|
81
|
+
tval = self[key]
|
|
82
|
+
if tval.is_a?(Hash) && val.is_a?(Hash)
|
|
83
|
+
self[key] = tval.deep_merge(val)
|
|
84
|
+
else
|
|
85
|
+
self[key] = block && tval ? block.call(k, tval, val) : val
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
self
|
|
89
|
+
end unless method_defined? :deep_merge!
|
|
90
|
+
|
|
91
|
+
end # Hash
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module Github
|
|
4
|
+
|
|
5
|
+
DEPRECATION_PREFIX = "[GithubAPI] Deprecation warning:"
|
|
6
|
+
|
|
7
|
+
class << self
|
|
8
|
+
|
|
9
|
+
attr_writer :deprecation_tracker
|
|
10
|
+
|
|
11
|
+
def deprecation_tracker
|
|
12
|
+
@deprecation_tracker ||= []
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Displays deprecation message to the user.
|
|
16
|
+
# Each message is printed once.
|
|
17
|
+
def deprecate(method, alternate_method=nil)
|
|
18
|
+
return if deprecation_tracker.include? method
|
|
19
|
+
deprecation_tracker << method
|
|
20
|
+
|
|
21
|
+
message = <<-NOTICE
|
|
22
|
+
#{DEPRECATION_PREFIX}
|
|
23
|
+
|
|
24
|
+
* #{method} is deprecated.
|
|
25
|
+
NOTICE
|
|
26
|
+
if alternate_method
|
|
27
|
+
message << <<-ADDITIONAL
|
|
28
|
+
* please use #{alternate_method} instead.
|
|
29
|
+
ADDITIONAL
|
|
30
|
+
end
|
|
31
|
+
warn_deprecation(message)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def warn_deprecation(message)
|
|
35
|
+
send :warn, message
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end # Github
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require_relative '../error'
|
|
4
|
+
|
|
5
|
+
module Github #:nodoc
|
|
6
|
+
# Raised when Github returns the HTTP status code 404
|
|
7
|
+
module Error
|
|
8
|
+
class ClientError < GithubError
|
|
9
|
+
attr_reader :problem, :summary, :resolution
|
|
10
|
+
|
|
11
|
+
def initialize(message)
|
|
12
|
+
super(message)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def generate_message(attributes)
|
|
16
|
+
@problem = attributes[:problem]
|
|
17
|
+
@summary = attributes[:summary]
|
|
18
|
+
@resolution = attributes[:resolution]
|
|
19
|
+
"\nProblem:\n #{@problem}"+
|
|
20
|
+
"\nSummary:\n #{@summary}"+
|
|
21
|
+
"\nResolution:\n #{@resolution}"
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Raised when invalid options are passed to a request body
|
|
26
|
+
class InvalidOptions < ClientError
|
|
27
|
+
def initialize(invalid, valid)
|
|
28
|
+
super(
|
|
29
|
+
generate_message(
|
|
30
|
+
problem: "Invalid option #{invalid.keys.join(', ')} provided for this request.",
|
|
31
|
+
summary: "Github gem checks the request parameters passed to ensure that github api is not hit unnecessarily and to fail fast.",
|
|
32
|
+
resolution: "Valid options are: #{valid.join(', ')}, make sure these are the ones you are using"
|
|
33
|
+
)
|
|
34
|
+
)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Raised when invalid options are passed to a request body
|
|
39
|
+
class RequiredParams < ClientError
|
|
40
|
+
def initialize(provided, required)
|
|
41
|
+
super(
|
|
42
|
+
generate_message(
|
|
43
|
+
problem: "Missing required parameters: #{provided.keys.join(', ')} provided for this request.",
|
|
44
|
+
summary: "Github gem checks the request parameters passed to ensure that github api is not hit unnecessarily and to fail fast.",
|
|
45
|
+
resolution: "Required parameters are: #{required.join(', ')}, make sure these are the ones you are using"
|
|
46
|
+
)
|
|
47
|
+
)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Raised when invalid options are passed to a request body
|
|
52
|
+
class UnknownMedia < ClientError
|
|
53
|
+
def initialize(file)
|
|
54
|
+
super(
|
|
55
|
+
generate_message(
|
|
56
|
+
problem: "Unknown content type for: '#{file}' provided for this request.",
|
|
57
|
+
summary: "Github gem infers the content type of the resource by calling the mime-types gem type inference.",
|
|
58
|
+
resolution: "Please install mime-types gem to infer the resource content type."
|
|
59
|
+
)
|
|
60
|
+
)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Raised when invalid options are passed to a request body
|
|
65
|
+
class UnknownValue < ClientError
|
|
66
|
+
def initialize(key, value, permitted)
|
|
67
|
+
super(
|
|
68
|
+
generate_message(
|
|
69
|
+
problem: "Wrong value of '#{value}' for the parameter: #{key} provided for this request.",
|
|
70
|
+
summary: "Github gem checks the request parameters passed to ensure that github api is not hit unnecessairly and fails fast.",
|
|
71
|
+
resolution: "Permitted values are: #{permitted}, make sure these are the ones you are using"
|
|
72
|
+
)
|
|
73
|
+
)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
class Validations < ClientError
|
|
78
|
+
def initialize(errors)
|
|
79
|
+
super(
|
|
80
|
+
generate_message(
|
|
81
|
+
problem: "Attempted to send request with nil arguments for #{errors.keys.join(', ')}.",
|
|
82
|
+
summary: 'Each request expects certain number of required arguments.',
|
|
83
|
+
resolution: 'Double check that the provided arguments are set to some value that is neither nil nor empty string.'
|
|
84
|
+
)
|
|
85
|
+
)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end # Error
|
|
89
|
+
end # Github
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
|
|
5
|
+
require_relative '../error'
|
|
6
|
+
|
|
7
|
+
module Github
|
|
8
|
+
# Raised when GitHub returns any of the HTTP status codes
|
|
9
|
+
module Error
|
|
10
|
+
class ServiceError < GithubError
|
|
11
|
+
# Add http status code method to error type
|
|
12
|
+
#
|
|
13
|
+
# @param [Integer] code
|
|
14
|
+
# the status code
|
|
15
|
+
#
|
|
16
|
+
# @api public
|
|
17
|
+
def self.http_status_code(code)
|
|
18
|
+
define_method(:http_status_code) { code }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# A mapping of status codes and error types
|
|
22
|
+
#
|
|
23
|
+
# @return [Hash[Integer, Object]]
|
|
24
|
+
#
|
|
25
|
+
# @api public
|
|
26
|
+
def self.error_mapping
|
|
27
|
+
@error_mapping ||= Hash[
|
|
28
|
+
descendants.map do |klass|
|
|
29
|
+
[klass.new({}).http_status_code, klass]
|
|
30
|
+
end
|
|
31
|
+
]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
MIN_BODY_LENGTH = 2
|
|
35
|
+
|
|
36
|
+
# Create a ServiceError
|
|
37
|
+
#
|
|
38
|
+
# @param [Hash[Symbol]] response
|
|
39
|
+
#
|
|
40
|
+
# @api public
|
|
41
|
+
def initialize(response)
|
|
42
|
+
@headers = response[:response_headers]
|
|
43
|
+
@body = response[:body]
|
|
44
|
+
@status = response[:status]
|
|
45
|
+
|
|
46
|
+
@response_headers = @headers
|
|
47
|
+
@response_message = @body
|
|
48
|
+
|
|
49
|
+
super(create_message(response))
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Expose response payload as JSON object if possible
|
|
53
|
+
#
|
|
54
|
+
# @return [Hash[Symbol]|String]
|
|
55
|
+
#
|
|
56
|
+
# @api public
|
|
57
|
+
def data
|
|
58
|
+
@data ||= decode_data(@body)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Stores error message(s) returned in response body
|
|
62
|
+
#
|
|
63
|
+
# @return [Array[Hash[Symbol]]]
|
|
64
|
+
# the array of hash error objects
|
|
65
|
+
#
|
|
66
|
+
# @api public
|
|
67
|
+
def error_messages
|
|
68
|
+
@error_messages ||= begin
|
|
69
|
+
data[:errors] ? data[:errors] : [data]
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
private
|
|
74
|
+
|
|
75
|
+
# Create full error message
|
|
76
|
+
#
|
|
77
|
+
# @param [Hash[Symbol]] response
|
|
78
|
+
# the http response
|
|
79
|
+
#
|
|
80
|
+
# @return [String]
|
|
81
|
+
# the error message
|
|
82
|
+
#
|
|
83
|
+
# @api private
|
|
84
|
+
def create_message(response)
|
|
85
|
+
return if response.nil?
|
|
86
|
+
|
|
87
|
+
message = "#{response[:method].to_s.upcase} "
|
|
88
|
+
message << "#{response[:url]}: "
|
|
89
|
+
message << "#{@status} - #{format_response}"
|
|
90
|
+
message
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Decode body information if in JSON format
|
|
94
|
+
#
|
|
95
|
+
# @param [String] body
|
|
96
|
+
# the response body
|
|
97
|
+
#
|
|
98
|
+
# @api private
|
|
99
|
+
def decode_data(body)
|
|
100
|
+
if body.respond_to?(:to_str) &&
|
|
101
|
+
body.length >= MIN_BODY_LENGTH &&
|
|
102
|
+
@headers[:content_type] =~ /json/
|
|
103
|
+
|
|
104
|
+
JSON.parse(body, symbolize_names: true)
|
|
105
|
+
else
|
|
106
|
+
body
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Read response body and convert to human friendly format
|
|
111
|
+
#
|
|
112
|
+
# @return [String]
|
|
113
|
+
#
|
|
114
|
+
# @api private
|
|
115
|
+
def format_response
|
|
116
|
+
return '' if data.nil? || data.empty?
|
|
117
|
+
|
|
118
|
+
case data
|
|
119
|
+
when Hash
|
|
120
|
+
message = data[:message] ? data[:message] : ' '
|
|
121
|
+
docs = data[:documentation_url]
|
|
122
|
+
error = create_error_summary
|
|
123
|
+
message << error if error
|
|
124
|
+
message << "\nSee: #{docs}" if docs
|
|
125
|
+
message
|
|
126
|
+
when String
|
|
127
|
+
data
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Create error summary from response body
|
|
132
|
+
#
|
|
133
|
+
# @return [String]
|
|
134
|
+
#
|
|
135
|
+
# @api private
|
|
136
|
+
def create_error_summary
|
|
137
|
+
if data[:error]
|
|
138
|
+
"\nError: #{data[:error]}"
|
|
139
|
+
elsif data[:errors]
|
|
140
|
+
message = "\nErrors:\n"
|
|
141
|
+
message << data[:errors].map do |error|
|
|
142
|
+
case error
|
|
143
|
+
when Hash
|
|
144
|
+
"Error: #{error.map { |k, v| "#{k}: #{v}" }.join(', ')}"
|
|
145
|
+
else
|
|
146
|
+
"Error: #{error}"
|
|
147
|
+
end
|
|
148
|
+
end.join("\n")
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end # ServiceError
|
|
152
|
+
|
|
153
|
+
# Raised when Github returns the HTTP status code 400
|
|
154
|
+
class BadRequest < ServiceError
|
|
155
|
+
http_status_code 400
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# Raised when GitHub returns the HTTP status code 401
|
|
159
|
+
class Unauthorized < ServiceError
|
|
160
|
+
http_status_code 401
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# Raised when Github returns the HTTP status code 403
|
|
164
|
+
class Forbidden < ServiceError
|
|
165
|
+
http_status_code 403
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Raised when Github returns the HTTP status code 404
|
|
169
|
+
class NotFound < ServiceError
|
|
170
|
+
http_status_code 404
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Raised when Github returns the HTTP status code 405
|
|
174
|
+
class MethodNotAllowed < ServiceError
|
|
175
|
+
http_status_code 405
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Raised when Github returns the HTTP status code 406
|
|
179
|
+
class NotAcceptable < ServiceError
|
|
180
|
+
http_status_code 406
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# Raised when GitHub returns the HTTP status code 409
|
|
184
|
+
class Conflict < ServiceError
|
|
185
|
+
http_status_code 409
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Raised when GitHub returns the HTTP status code 414
|
|
189
|
+
class UnsupportedMediaType < ServiceError
|
|
190
|
+
http_status_code 414
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# Raised when GitHub returns the HTTP status code 422
|
|
194
|
+
class UnprocessableEntity < ServiceError
|
|
195
|
+
http_status_code 422
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Raised when GitHub returns the HTTP status code 451
|
|
199
|
+
class UnavailableForLegalReasons < ServiceError
|
|
200
|
+
http_status_code 451
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# Raised when Github returns the HTTP status code 500
|
|
204
|
+
class InternalServerError < ServiceError
|
|
205
|
+
http_status_code 500
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Raised when Github returns the HTTP status code 501
|
|
209
|
+
class NotImplemented < ServiceError
|
|
210
|
+
http_status_code 501
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# Raised when Github returns the HTTP status code 502
|
|
214
|
+
class BadGateway < ServiceError
|
|
215
|
+
http_status_code 502
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# Raised when GitHub returns the HTTP status code 503
|
|
219
|
+
class ServiceUnavailable < ServiceError
|
|
220
|
+
http_status_code 503
|
|
221
|
+
end
|
|
222
|
+
end # Error
|
|
223
|
+
end # Github
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'descendants_tracker'
|
|
4
|
+
|
|
5
|
+
module Github
|
|
6
|
+
module Error
|
|
7
|
+
class GithubError < StandardError
|
|
8
|
+
extend DescendantsTracker
|
|
9
|
+
|
|
10
|
+
attr_reader :response_message, :response_headers
|
|
11
|
+
|
|
12
|
+
# Initialize a new Github error object.
|
|
13
|
+
#
|
|
14
|
+
def initialize(message = $!)
|
|
15
|
+
if message.respond_to?(:backtrace)
|
|
16
|
+
super(message.message)
|
|
17
|
+
@response_message = message
|
|
18
|
+
else
|
|
19
|
+
super(message.to_s)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def backtrace
|
|
24
|
+
if @response_message.respond_to?(:backtrace)
|
|
25
|
+
@response_message.backtrace
|
|
26
|
+
else
|
|
27
|
+
super
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end # GithubError
|
|
31
|
+
end # Error
|
|
32
|
+
end # Github
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module Faraday
|
|
4
|
+
module Utils
|
|
5
|
+
|
|
6
|
+
class ParamsHash
|
|
7
|
+
def params_encoder(encoder = nil)
|
|
8
|
+
if encoder
|
|
9
|
+
@encoder = encoder
|
|
10
|
+
elsif defined?(@encoder)
|
|
11
|
+
@encoder
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def to_query(encoder = nil)
|
|
16
|
+
Utils.build_nested_query(self, nil, params_encoder)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
module_function
|
|
21
|
+
|
|
22
|
+
def build_nested_query(value, prefix = nil, encoder = nil)
|
|
23
|
+
case value
|
|
24
|
+
when Array
|
|
25
|
+
value.map { |v| build_nested_query(v, "#{prefix}%5B%5D", encoder) }.join("&")
|
|
26
|
+
when Hash
|
|
27
|
+
value.map { |k, v|
|
|
28
|
+
processed_value = encoder ? encoder.escape(k) : escape(k)
|
|
29
|
+
build_nested_query(v, prefix ? "#{prefix}%5B#{processed_value}%5D" : processed_value, encoder)
|
|
30
|
+
}.join("&")
|
|
31
|
+
when NilClass
|
|
32
|
+
prefix
|
|
33
|
+
else
|
|
34
|
+
raise ArgumentError, "value must be a Hash" if prefix.nil?
|
|
35
|
+
processed_value = encoder ? encoder.escape(value) : escape(value)
|
|
36
|
+
"#{prefix}=#{processed_value}"
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|