Class: VoiceTel::Internal::Transport

Inherits:
Object
  • Object
show all
Defined in:
lib/voicetel/internal/transport.rb

Overview

Transport is the low-level Faraday wrapper used by every resource service. It owns the connection, installs the bearer token, retries 429/5xx with Retry-After honored, and strips the ‘data` envelope from responses before returning the inner payload.

Constant Summary collapse

RETRYABLE_STATUSES =
[429, 500, 502, 503, 504].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(base_url:, api_key: nil, timeout: 30, max_retries: 2, user_agent: nil, adapter: nil) ⇒ Transport

Returns a new instance of Transport.



26
27
28
29
30
31
32
33
34
# File 'lib/voicetel/internal/transport.rb', line 26

def initialize(base_url:, api_key: nil, timeout: 30, max_retries: 2, user_agent: nil, adapter: nil)
  @base_url    = (base_url || VoiceTel::DEFAULT_BASE_URL).chomp("/")
  @api_key     = api_key
  @timeout     = timeout
  @max_retries = max_retries
  @user_agent  = user_agent || VoiceTel::USER_AGENT
  @adapter     = adapter
  @conn        = build_connection
end

Instance Attribute Details

#api_keyObject

Returns the value of attribute api_key.



24
25
26
# File 'lib/voicetel/internal/transport.rb', line 24

def api_key
  @api_key
end

#base_urlObject (readonly)

Returns the value of attribute base_url.



23
24
25
# File 'lib/voicetel/internal/transport.rb', line 23

def base_url
  @base_url
end

#max_retriesObject (readonly)

Returns the value of attribute max_retries.



23
24
25
# File 'lib/voicetel/internal/transport.rb', line 23

def max_retries
  @max_retries
end

#timeoutObject (readonly)

Returns the value of attribute timeout.



23
24
25
# File 'lib/voicetel/internal/transport.rb', line 23

def timeout
  @timeout
end

#user_agentObject (readonly)

Returns the value of attribute user_agent.



23
24
25
# File 'lib/voicetel/internal/transport.rb', line 23

def user_agent
  @user_agent
end

Instance Method Details

#camelize_keys(obj) ⇒ Object

Recursively transform a Ruby snake_case key Hash/Array structure into a Hash with camelCase keys, suitable for JSON serialization. Values that are already strings/numbers/bools/nil/symbols pass through.



69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/voicetel/internal/transport.rb', line 69

def camelize_keys(obj)
  case obj
  when Hash
    obj.each_with_object({}) do |(k, v), h|
      h[snake_to_camel(k)] = camelize_keys(v)
    end
  when Array
    obj.map { |v| camelize_keys(v) }
  else
    obj
  end
end

#request(method, path, query: nil, body: nil, require_auth: true) ⇒ Object

Perform an HTTP request. Returns the parsed inner data (envelope stripped) on success, or raises ApiError on failure. Returns nil on 204 No Content.

Parameters:

  • method (Symbol)

    one of :get, :post, :put, :patch, :delete

  • path (String)

    absolute path including the /v2.2 prefix

  • query (Hash, nil) (defaults to: nil)

    query string params, snake_case keys

  • body (Hash, nil) (defaults to: nil)

    request body, snake_case keys (translated to camelCase)

  • require_auth (Boolean) (defaults to: true)

    false skips the bearer header



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/voicetel/internal/transport.rb', line 45

def request(method, path, query: nil, body: nil, require_auth: true)
  if require_auth && (@api_key.nil? || @api_key.empty?)
    raise ApiError.new(
      "no api key set; call client.login(...) or pass api_key: to Client.new",
      kind: :authentication
    )
  end

  headers = build_headers(require_auth)
  headers["Idempotency-Key"] = SecureRandom.uuid if %i[post put patch].include?(method)
  response = @conn.run_request(method, path, body ? JSON.generate(camelize_keys(body)) : nil, headers) do |req|
    req.params.update(camelize_keys(query)) if query && !query.empty?
  end

  handle_response(response)
rescue Faraday::TimeoutError => e
  raise ApiError.new("voicetel: request timed out: #{e.message}", kind: :unknown)
rescue Faraday::ConnectionFailed => e
  raise ApiError.new("voicetel: connection failed: #{e.message}", kind: :unknown)
end