Class: Blueticks::Transport
- Inherits:
-
Object
- Object
- Blueticks::Transport
- Defined in:
- lib/blueticks/transport.rb
Overview
HTTP transport for the Blueticks API. Wraps Net::HTTP with retry/backoff.
Constant Summary collapse
- RETRIABLE_STATUS =
[429, 502, 503, 504].freeze
- IDEMPOTENT_METHODS =
%w[GET HEAD OPTIONS DELETE PATCH PUT].freeze
- BACKOFF_BASE =
0.5- BACKOFF_CAP =
8.0- RETRIABLE_NETWORK_ERRORS =
[ Errno::ECONNRESET, Errno::ECONNREFUSED, Errno::ETIMEDOUT, Net::OpenTimeout, Net::ReadTimeout, SocketError, EOFError, IOError ].freeze
- METHOD_CLASSES =
{ "GET" => Net::HTTP::Get, "POST" => Net::HTTP::Post, "PUT" => Net::HTTP::Put, "PATCH" => Net::HTTP::Patch, "DELETE" => Net::HTTP::Delete, "HEAD" => Net::HTTP::Head }.freeze
Instance Method Summary collapse
-
#initialize(api_key:, base_url:, timeout: 30.0, max_retries: 3, user_agent: nil, http: nil) ⇒ Transport
constructor
A new instance of Transport.
-
#request(method, path, params: nil, body: nil, idempotency_key: nil) ⇒ Object
Issue a request.
Constructor Details
#initialize(api_key:, base_url:, timeout: 30.0, max_retries: 3, user_agent: nil, http: nil) ⇒ Transport
Returns a new instance of Transport.
31 32 33 34 35 36 37 38 39 |
# File 'lib/blueticks/transport.rb', line 31 def initialize(api_key:, base_url:, timeout: 30.0, max_retries: 3, user_agent: nil, http: nil) @api_key = api_key @base_url = base_url.sub(%r{/+\z}, "") @timeout = timeout @max_retries = max_retries @user_agent = "blueticks-ruby/#{Blueticks::VERSION}" @user_agent = "#{@user_agent} #{user_agent}" if user_agent && !user_agent.empty? @http = http # Optional injected client for testing. end |
Instance Method Details
#request(method, path, params: nil, body: nil, idempotency_key: nil) ⇒ Object
Issue a request. Returns the parsed JSON body, or nil on 204/empty.
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 |
# File 'lib/blueticks/transport.rb', line 42 def request(method, path, params: nil, body: nil, idempotency_key: nil) method = method.to_s.upcase uri = build_uri(path, params) is_idempotent = IDEMPOTENT_METHODS.include?(method) || !idempotency_key.nil? attempt = 0 loop do req = build_request(method, uri, body: body, idempotency_key: idempotency_key) begin response = perform(uri, req) rescue *RETRIABLE_NETWORK_ERRORS => e if is_idempotent && attempt < @max_retries sleep_backoff(attempt, retry_after: nil) attempt += 1 next end raise Errors::APIConnectionError.new( message: "connection error: #{e.class}: #{e.}", status_code: nil, code: nil, request_id: nil ) end return parse_success(response) if (200..299).cover?(response.code.to_i) retry_after = response.code.to_i == 429 ? parse_retry_after(response) : nil retriable = RETRIABLE_STATUS.include?(response.code.to_i) if retriable && is_idempotent && attempt < @max_retries sleep_backoff(attempt, retry_after: retry_after) attempt += 1 next end raise build_api_error(response, retry_after: retry_after) end end |