1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
|
# frozen_string_literal: false
require 'gh'
require 'faraday'
require 'faraday/retry'
require 'faraday/typhoeus'
require 'active_support/core_ext/string'
module GH
# Public: This class deals with HTTP requests to Github. It is the base Wrapper you always want to use.
# Note that it is usually used implicitely by other wrapper classes if not specified.
class Remote < Wrapper
attr_reader :api_host, :connection, :headers, :prefix
# Public: Generates a new Remote instance.
#
# api_host - HTTP host to send requests to, has to include schema (https or http)
# options - Hash with configuration options:
# :token - OAuth token to use (optional).
# :username - Github user used for login (optional).
# :password - Github password used for login (optional).
# :origin - Value of the origin request header (optional).
# :headers - HTTP headers to be send on every request (optional).
#
# It is highly recommended to set origin, but not to set headers.
# If you set the username, you should also set the password.
def setup(api_host, options)
token, username, password = options.values_at :token, :username, :password
api_host = api_host.api_host if api_host.respond_to? :api_host
@api_host = Addressable::URI.parse(api_host)
@headers = {
'User-Agent' => options[:user_agent] || "GH/#{GH::VERSION}",
'Accept' => options[:accept] || 'application/vnd.github.v3+json',
'Accept-Charset' => 'utf-8'
}
@headers.merge! options[:headers] if options[:headers]
@headers['Origin'] = options[:origin] if options[:origin]
@prefix = ''
@prefix << "#{token}@" if token
@prefix << "#{username}:#{password}@" if username && password
@prefix << @api_host.host
faraday_options = { url: api_host }
faraday_options[:ssl] = options[:ssl] if options[:ssl]
faraday_options.merge! options[:faraday_options] if options[:faraday_options]
@connection = Faraday.new(faraday_options) do |builder|
builder.request(:authorization, :token, token) if token
builder.request(:basic_auth, username, password) if username && password
builder.request(:retry)
builder.adapter(:typhoeus)
builder.response(:raise_error)
builder.use :instrumentation if defined? FaradayMiddleware::Instrumentation
builder.response(:logger, nil, formatter: GH.const_get(options[:formatter].camelize)) if options[:formatter]
end
end
# Public: ...
def inspect
"#<#{self.class}: #{api_host}>"
end
# Internal: ...
def fetch_resource(key)
frontend.http(:get, frontend.path_for(key), headers)
end
# Internal: ...
def generate_response(key, response)
body = response.body
headers = response.headers
url = response.env[:url] if response.respond_to?(:env) && response.env
url = response.url if response.respond_to?(:url)
url = frontend.full_url(key) if url.to_s.empty?
modify(body, headers, url)
end
# Internal: ...
def http(verb, url, headers = {}, &block)
body = headers.delete :body
connection.run_request(verb, url, body, headers, &block)
rescue StandardError => e
raise Error.new(e, nil, verb:, url:, headers:)
end
# Internal: ...
def request(verb, key, body = nil)
response = frontend.http(verb, path_for(key), headers) do |req|
req.body = Response.new(body).to_s if body
end
frontend.generate_response(key, response)
rescue GH::Error => e
e.info[:payload] = Response.new(body).to_s if body
raise e
end
# Public: ...
def post(key, body)
frontend.request(:post, key, body)
end
# Public: ...
def delete(key, body = nil)
frontend.request(:delete, key, body)
end
# Public: ...
def head(key)
frontend.request(:head, key)
end
# Public: ...
def patch(key, body)
frontend.request(:patch, key, body)
end
# Public: ...
def put(key, body)
frontend.request(:put, key, body)
end
# Public: ...
def reset; end
# Public: ...
def load(data)
modify(data)
end
# Public: ...
def in_parallel
raise 'use GH::Parallel middleware for #in_parallel support'
end
def full_url(key)
uri = Addressable::URI.parse(key)
uri.path = File.join(api_host.path, uri.path) unless uri.absolute? || uri.path.start_with?(api_host.path)
uri = api_host + uri
raise ArgumentError, "URI out of scope: #{key}" if uri.host != api_host.host
uri
end
def path_for(key)
frontend.full_url(key).request_uri
end
private
def identifier(key)
path_for(key)
end
def modify(body, headers = {}, url = nil)
return body if body.is_a? Response
Response.new(body, headers, url)
end
end
end
|