File: remote.rb

package info (click to toggle)
ruby-gh 0.21.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,644 kB
  • sloc: ruby: 1,793; makefile: 4
file content (163 lines) | stat: -rw-r--r-- 4,986 bytes parent folder | download
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