Class: LetMeSendEmail::Client

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

Overview

HTTP client and entry point for all API resources.

Constant Summary collapse

RETRYABLE_HTTP_STATUSES =
[408, 500, 502, 503, 504].freeze
SAFE_METHODS =
%i[get delete].freeze
MAX_RETRY_DELAY_SECONDS =
300
BASE_RETRY_DELAY_SECONDS =
0.1

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(api_key: nil, config: nil) ⇒ Client

Returns a new instance of Client.

Parameters:

  • api_key (String, nil) (defaults to: nil)
  • config (Config, nil) (defaults to: nil)

Raises:

  • (ArgumentError)

    when configuration is invalid



27
28
29
30
31
32
33
34
35
36
# File 'lib/letmesendemail/client.rb', line 27

def initialize(api_key: nil, config: nil)
  raise ArgumentError, 'provide api_key or config, not both' if api_key && config

  @config = config || Config.new(api_key || raise(ArgumentError, 'api_key is required'))
  raise ArgumentError, 'config must be a LetMeSendEmail::Config' unless @config.is_a?(Config)

  @config = @config.dup.validate!.freeze
  @transport = HttpTransport.new(@config)
  initialize_resources
end

Instance Attribute Details

#contact_categoriesResources::ContactCategories (readonly)



20
21
22
# File 'lib/letmesendemail/client.rb', line 20

def contact_categories
  @contact_categories
end

#contactsResources::Contacts (readonly)

Returns:



18
19
20
# File 'lib/letmesendemail/client.rb', line 18

def contacts
  @contacts
end

#domainsResources::Domains (readonly)

Returns:



16
17
18
# File 'lib/letmesendemail/client.rb', line 16

def domains
  @domains
end

#email_topicsResources::EmailTopics (readonly)



22
23
24
# File 'lib/letmesendemail/client.rb', line 22

def email_topics
  @email_topics
end

#emailsResources::Emails (readonly)

Returns:



14
15
16
# File 'lib/letmesendemail/client.rb', line 14

def emails
  @emails
end

Instance Method Details

#request(method, path, body: nil, extra_headers: {}) ⇒ Models::Model

Performs an API request. Resource classes provide the primary public API.

Parameters:

  • method (Symbol)
  • path (String)
  • body (Hash, nil) (defaults to: nil)
  • extra_headers (Hash) (defaults to: {})

Returns:

Raises:



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

def request(method, path, body: nil, extra_headers: {})
  retry_eligible = retry_eligible?(method, extra_headers)
  max_attempts = retry_eligible ? @config.retries + 1 : 1
  last_error = nil

  max_attempts.times do |attempt|
    return @transport.call(method, path, body: body, extra_headers: extra_headers)
  rescue RateLimitError => e
    raise unless attempt < max_attempts - 1 && e.retry_after

    last_error = e
    sleep(e.retry_after)
  rescue NetworkError, TimeoutError => e
    raise unless attempt < max_attempts - 1

    last_error = e
    sleep(retry_delay(attempt))
  rescue ApiError => e
    raise unless RETRYABLE_HTTP_STATUSES.include?(e.status_code) && attempt < max_attempts - 1

    last_error = e
    sleep(retry_delay(attempt))
  end

  raise last_error
end

#resource_path_segment(id) ⇒ String

Encodes an untrusted identifier as exactly one URL path segment.

Parameters:

  • id (String)

Returns:

  • (String)

Raises:

  • (ArgumentError)

    when the identifier is empty or unsafe



76
77
78
79
80
81
82
83
84
85
# File 'lib/letmesendemail/client.rb', line 76

def resource_path_segment(id)
  raise ArgumentError, 'resource id must be a string' unless id.is_a?(String)

  value = id
  if value.empty? || value != value.strip || %w[. ..].include?(value) || value.match?(/[[:cntrl:]]/)
    raise ArgumentError, 'resource id must be non-empty and must not be a dot segment or contain unsafe whitespace'
  end

  URI::DEFAULT_PARSER.escape(value, /[^A-Za-z0-9\-._~]/)
end