Class: Files::ApiClient

Inherits:
Object
  • Object
show all
Defined in:
lib/files.com/api_client.rb

Defined Under Namespace

Classes: RequestLogContext

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(conn = nil) ⇒ ApiClient

Returns a new instance of ApiClient.



7
8
9
10
11
# File 'lib/files.com/api_client.rb', line 7

def initialize(conn = nil)
  self.conn = conn || self.class.default_conn
  @system_profiler = SystemProfiler.new
  @last_request_metrics = nil
end

Instance Attribute Details

#connObject

Returns the value of attribute conn.



5
6
7
# File 'lib/files.com/api_client.rb', line 5

def conn
  @conn
end

Class Method Details

.active_clientObject



13
14
15
# File 'lib/files.com/api_client.rb', line 13

def self.active_client
  Thread.current[:files_api_client] || default_client
end

.build_default_conn(force_net_http: false) ⇒ Object



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/files.com/api_client.rb', line 35

def self.build_default_conn(force_net_http: false)
  conn = Faraday.new do |builder|
    if Gem::Version.new(Faraday::VERSION) < Gem::Version.new("2.0.0")
      builder.use Faraday::Request::Multipart
    else
      builder.request :multipart
    end
    builder.use Faraday::Request::UrlEncoded
    builder.use Faraday::Response::RaiseError

    if Gem.win_platform? || RUBY_PLATFORM == "java" || force_net_http
      builder.adapter :net_http
    else
      builder.adapter :net_http_persistent
    end
  end

  conn.proxy = Files.proxy if Files.proxy
  conn.ssl.verify = true

  conn
end

.default_clientObject



27
28
29
# File 'lib/files.com/api_client.rb', line 27

def self.default_client
  Thread.current[:files_api_client_default_client] ||= ApiClient.new(default_conn)
end

.default_connObject



31
32
33
# File 'lib/files.com/api_client.rb', line 31

def self.default_conn
  Thread.current[:files_api_client_default_conn] ||= build_default_conn
end

.download_clientObject

net_http_persistent does not support streaming downloads with faraday when directly downloading from S3 falling back to net_http.



19
20
21
# File 'lib/files.com/api_client.rb', line 19

def self.download_client
  Thread.current[:files_api_client_download_client] ||= ApiClient.new(download_conn)
end

.download_connObject



23
24
25
# File 'lib/files.com/api_client.rb', line 23

def self.download_conn
  Thread.current[:files_api_client_download_conn] ||= build_default_conn(force_net_http: true)
end

.should_retry?(error, num_retries) ⇒ Boolean

Returns:

  • (Boolean)


58
59
60
61
62
63
64
65
66
# File 'lib/files.com/api_client.rb', line 58

def self.should_retry?(error, num_retries)
  return false if num_retries >= Files.max_network_retries
  return true if error.is_a?(Faraday::TimeoutError)
  return true if error.is_a?(Faraday::ConnectionFailed)
  return true if error.is_a?(Faraday::ServerError)
  return true if error.is_a?(Faraday::ClientError) and error.response_status == 405

  false
end

.sleep_time(num_retries) ⇒ Object



68
69
70
71
72
73
74
75
# File 'lib/files.com/api_client.rb', line 68

def self.sleep_time(num_retries)
  sleep_seconds = [
    Files.initial_network_retry_delay * (2**(num_retries - 1)),
    Files.max_network_retry_delay
  ].min
  sleep_seconds *= (0.5 * (1 + rand))
  [ Files.initial_network_retry_delay, sleep_seconds ].max
end

Instance Method Details

#cursorObject



178
179
180
# File 'lib/files.com/api_client.rb', line 178

def cursor
  @last_response.http_headers["x-files-cursor"]
end

#execute_request(method, path, base_url: nil, api_key: nil, session_id: nil, headers: {}, params: {}) ⇒ Object



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
# File 'lib/files.com/api_client.rb', line 90

def execute_request(method, path, base_url: nil, api_key: nil, session_id: nil, headers: {}, params: {})
  base_url ||= Files.base_url
  session_id ||= Files.session_id

  if session_id and session_id != ""
    check_session_id!(session_id)
  elsif path !~ /^\/sessions/ # TODO: automate this to refer to any unauthenticated endpoint
    api_key ||= Files.api_key
    check_api_key!(api_key)
  end

  body = nil
  query_params = nil
  case method.to_s.downcase.to_sym
  when :get, :head, :delete
    query_params = params
  else
    body = params
  end

  headers = request_headers(api_key, session_id, method).update(headers)
  url = api_url(path, base_url)

  context = RequestLogContext.new
  context.api_key      = api_key
  context.body         = body
  context.method       = method
  context.path         = path
  context.query_params = query_params if query_params
  context.session_id   = session_id

  http_resp = execute_request_with_rescues(base_url, context) do
    conn.run_request(method, url, body, headers) do |req|
      req.options.open_timeout = Files.open_timeout
      req.options.timeout = Files.read_timeout
      req.params = query_params unless query_params.nil?
    end
  end

  begin
    resp = Response.from_faraday_response(http_resp)
  rescue JSON::ParserError
    raise general_api_error(http_resp.status, http_resp.body)
  end

  @last_response = resp
  [ resp, api_key, session_id ]
end

#execute_request_with_rescues(base_url, context, skip_body_logging: false) ⇒ Object



206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
# File 'lib/files.com/api_client.rb', line 206

def execute_request_with_rescues(base_url, context, skip_body_logging: false)
  num_retries = 0
  begin
    request_start = Time.now
    log_request(context, num_retries, no_body: skip_body_logging)
    resp = yield
    log_response(context, request_start, resp.status, resp.body, no_body: skip_body_logging)
  rescue StandardError => e
    error_context = context

    if e.respond_to?(:response) && e.response
      error_context = context
      log_response(error_context, request_start,
                   e.response[:status], e.response[:body], no_body: skip_body_logging
      )
    else
      log_response_error(error_context, request_start, e)
    end

    if self.class.should_retry?(e, num_retries)
      num_retries += 1
      sleep self.class.sleep_time(num_retries)
      retry
    end

    case e
    when Faraday::ClientError, Faraday::ServerError
      if e.response
        handle_error_response(e.response, error_context)
      else
        handle_network_error(e, error_context, num_retries, base_url)
      end

    else
      raise
    end
  end

  resp
end

#remote_request(method, url, headers = {}, body = nil) ⇒ Object



139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/files.com/api_client.rb', line 139

def remote_request(method, url, headers = {}, body = nil)
  context = RequestLogContext.new
  context.method       = method
  context.path         = url

  execute_request_with_rescues(Files.base_url, context, skip_body_logging: true) do
    conn.run_request(method, url, body, headers) do |req|
      req.options.open_timeout = Files.open_timeout
      req.options.timeout = Files.read_timeout
      yield(req) if block_given?
    end
  end
end

#requestObject



77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/files.com/api_client.rb', line 77

def request
  @last_response = nil
  old_files_api_client = Thread.current[:files_api_client]
  Thread.current[:files_api_client] = self

  begin
    res = yield
    [ res, @last_response ]
  ensure
    Thread.current[:files_api_client] = old_files_api_client
  end
end

#stream_download(uri, io, range) ⇒ Object



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/files.com/api_client.rb', line 153

def stream_download(uri, io, range)
  if conn.adapter == Faraday::Adapter::NetHttp
    uri = URI(uri)
    Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
      request = Net::HTTP::Get.new uri
      request["RANGE"] = "bytes=#{range[0]}-#{range[1]}" unless range.empty?
      http.request request do |response|
        io.fulfill_content_length(response.content_length) if io.respond_to?(:fulfill_content_length)
        response.read_body do |chunk|
          response.error! if response.code.to_i >= 300
          io.ready! if io.respond_to?(:ready!)
          io << chunk
        rescue StandardError => e
          io.do_set_error(e) if io.respond_to?(:do_set_error)
          io.close
        end
      end
    end
  else
    response = remote_request(:get, uri)
    io.fulfill_content_length(response.content_length) if io.respond_to?(:fulfill_content_length)
    io.write(response.body)
  end
end