Class: Elements::ElementsClient

Inherits:
Object
  • Object
show all
Includes:
Logging
Defined in:
lib/elements/elements_client.rb

Overview

ElementsClient executes HTTP requests against the Elements API, converts the response into a resource object or an error object accordingly.

Defined Under Namespace

Classes: ThreadContext

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Logging

#log_debug, #log_error, #log_info, #log_warn

Constructor Details

#initialize(config_arg = {}) ⇒ ElementsClient

Returns a new instance of ElementsClient.



11
12
13
# File 'lib/elements/elements_client.rb', line 11

def initialize(config_arg = {})
  @config = Elements.config.reverse_duplicate_merge(config_arg)
end

Instance Attribute Details

#configObject (readonly)

Returns the value of attribute config.



9
10
11
# File 'lib/elements/elements_client.rb', line 9

def config
  @config
end

Class Method Details

.active_clientObject



19
20
21
# File 'lib/elements/elements_client.rb', line 19

def self.active_client
  current_thread_context.active_client || default_client
end

.current_thread_contextObject



15
16
17
# File 'lib/elements/elements_client.rb', line 15

def self.current_thread_context
  Thread.current[:elements_client__internal_use_only] ||= ThreadContext.new
end

.default_clientObject



23
24
25
# File 'lib/elements/elements_client.rb', line 23

def self.default_client
  current_thread_context.default_client ||= ElementsClient.new
end

.should_retry?(error, attempts, config) ⇒ Boolean

Returns:

  • (Boolean)


76
77
78
79
80
81
82
83
84
85
86
# File 'lib/elements/elements_client.rb', line 76

def self.should_retry?(error, attempts, config)
  return false if attempts >= config.max_network_retries
  # retry if it is a connection issue
  return true if error.is_a?(Elements::APIConnectionError)
  # retry if it is a conflict, e.g., record not saved
  return true if error.is_a?(Elements::ElementsError) && error.http_status == 409
  # retry if service is temporarily unavailable
  return true if error.is_a?(Elements::ElementsError) && error.http_status == 503

  false
end

.sleep_duration(attempts, config) ⇒ Object



88
89
90
91
92
93
94
# File 'lib/elements/elements_client.rb', line 88

def self.sleep_duration(attempts, config)
  duration = config.min_network_retry_delay * (2**(attempts - 1))
  # adding jitter, https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/
  duration = rand(duration)
  duration = [config.min_network_retry_delay, duration].max
  [config.max_network_retry_delay, duration].min
end

Instance Method Details

#execute_request(method, path, params: {}, headers: {}, opts: {}) ⇒ Object



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
# File 'lib/elements/elements_client.rb', line 27

def execute_request(method, path, params: {}, headers: {}, opts: {})
  api_base = opts[:api_base] || config.api_base
  api_key = opts[:api_key] || config.api_key

  body_params, query_params = if %i[get head delete].include?(method)
                                [nil, params]
                              else
                                [params, nil]
                              end

  query_params, path = merge_query_params(query_params, path)
  query = encode_query_params(query_params)

  headers = request_headers(api_key, method).update(headers)

  body = body_params ? JSON.generate(body_params) : nil

  url = api_url(path, query, api_base)

  context = {
    method: method,
    path: path,
    query: query,
    body: body_params,
    idempotency_key: headers['Idempotency-Key']
  }

  log_request(context)
  resp = with_retries do
    execute_request_with_rescues(method, url, headers, body, context)
  end
  log_response(context, resp)
  resp
end