Class: Sendara::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/sendara/client.rb

Constant Summary collapse

DEFAULT_BASE_URL =
"https://api.sendara.dev"
DEFAULT_TIMEOUT =
30
DEFAULT_MAX_RETRIES =
2
RETRY_BASE_DELAY =
0.5
RETRY_MAX_DELAY =
8.0
WRITE_METHODS =
%w[POST PUT PATCH].freeze
RETRIABLE_METHODS =
%w[GET HEAD PUT DELETE].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(api_key, base_url: DEFAULT_BASE_URL, timeout: DEFAULT_TIMEOUT, max_retries: DEFAULT_MAX_RETRIES, transport: nil) ⇒ Client

Returns a new instance of Client.

Raises:



21
22
23
24
25
26
27
28
29
30
31
# File 'lib/sendara/client.rb', line 21

def initialize(api_key, base_url: DEFAULT_BASE_URL, timeout: DEFAULT_TIMEOUT,
               max_retries: DEFAULT_MAX_RETRIES, transport: nil)
  raise Error, "An API key is required" if api_key.nil? || api_key.to_s.empty?

  @api_key = api_key.to_s
  @base_url = base_url.to_s.sub(%r{/+\z}, "")
  @timeout = Integer(timeout)
  @max_retries = [0, Integer(max_retries)].max
  @transport = transport || method(:net_http_transport)
  @resources = {}
end

Instance Attribute Details

#base_urlObject (readonly)

Returns the value of attribute base_url.



19
20
21
# File 'lib/sendara/client.rb', line 19

def base_url
  @base_url
end

#max_retriesObject (readonly)

Returns the value of attribute max_retries.



19
20
21
# File 'lib/sendara/client.rb', line 19

def max_retries
  @max_retries
end

#timeoutObject (readonly)

Returns the value of attribute timeout.



19
20
21
# File 'lib/sendara/client.rb', line 19

def timeout
  @timeout
end

Class Method Details

.generate_idempotency_keyObject



105
106
107
# File 'lib/sendara/client.rb', line 105

def self.generate_idempotency_key
  SecureRandom.uuid
end

Instance Method Details

#api_keysObject



102
# File 'lib/sendara/client.rb', line 102

def api_keys     = resource(:api_keys, Resources::ApiKeys)

#billingObject



103
# File 'lib/sendara/client.rb', line 103

def billing      = resource(:billing, Resources::Billing)

#broadcastsObject



94
# File 'lib/sendara/client.rb', line 94

def broadcasts   = resource(:broadcasts, Resources::Broadcasts)

#contactsObject



96
# File 'lib/sendara/client.rb', line 96

def contacts     = resource(:contacts, Resources::Contacts)

#domainsObject



98
# File 'lib/sendara/client.rb', line 98

def domains      = resource(:domains, Resources::Domains)

#emailsObject



93
# File 'lib/sendara/client.rb', line 93

def emails       = resource(:emails, Resources::Emails)

#listsObject



97
# File 'lib/sendara/client.rb', line 97

def lists        = resource(:lists, Resources::Lists)

#messagesObject



95
# File 'lib/sendara/client.rb', line 95

def messages     = resource(:messages, Resources::Messages)

#request(method, path, body: nil, query: {}) ⇒ Object



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
# File 'lib/sendara/client.rb', line 33

def request(method, path, body: nil, query: {})
  method = method.to_s.upcase
  url = @base_url + path + build_query(query)

  headers = {
    "Authorization" => "Bearer #{@api_key}",
    "Accept" => "application/json"
  }

  raw_body = nil
  unless body.nil?
    headers["Content-Type"] = "application/json"
    raw_body = JSON.generate(body)
  end

  headers["Idempotency-Key"] = SecureRandom.uuid if WRITE_METHODS.include?(method)

  idempotent = RETRIABLE_METHODS.include?(method)
  max_attempts = idempotent ? @max_retries + 1 : 1

  last_error = nil
  attempt = 0

  while attempt < max_attempts
    begin
      response = @transport.call(method, url, headers, raw_body, @timeout)
    rescue ConnectionError => e
      last_error = e
      if attempt < max_attempts - 1
        sleep_for(backoff_delay(attempt, nil))
        attempt += 1
        next
      end
      raise
    end

    status = response[:status].to_i
    response_headers = response[:headers] || {}
    response_body = response[:body].to_s

    if status >= 200 && status < 300
      return nil if status == 204 || response_body.empty?

      return parse_json(response_body)
    end

    error = error_from_response(status, response_body, response_headers)
    if idempotent && attempt < max_attempts - 1 && retriable_status?(status)
      last_error = error
      sleep_for(backoff_delay(attempt, retry_after_seconds(response_headers)))
      attempt += 1
      next
    end

    raise error
  end

  raise last_error || ConnectionError.new("Request failed after retries")
end

#suppressionsObject



100
# File 'lib/sendara/client.rb', line 100

def suppressions = resource(:suppressions, Resources::Suppressions)

#templatesObject



99
# File 'lib/sendara/client.rb', line 99

def templates    = resource(:templates, Resources::Templates)

#usageObject



101
# File 'lib/sendara/client.rb', line 101

def usage        = resource(:usage, Resources::Usage)