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 defined? Faraday::Request::Multipart
      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
# 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)

  false
end

.sleep_time(num_retries) ⇒ Object



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

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



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

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



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

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



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

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, skip_body_logging)
    resp = yield
    log_response(context, request_start, resp.status, resp.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], 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
      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



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

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, 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



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

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



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

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