class ActiveGenie::Clients::BaseClient
def apply_headers(request, headers)
-
headers
(Hash
) -- Additional headers to include -
request
(Net::HTTP::Request
) -- The request object
def apply_headers(request, headers) DEFAULT_HEADERS.each do |key, value| request[key] = value end headers.each do |key, value| request[key.to_s] = value end end
def build_uri(endpoint, params = {})
-
(URI)
- The constructed URI
Parameters:
-
params
(Hash
) -- Query parameters -
endpoint
(String
) -- The API endpoint
def build_uri(endpoint, params = {}) base_url = @app_config.api_url uri = URI("#{base_url}#{endpoint}") unless params.empty? uri.query = URI.encode_www_form(params) end uri end
def create_http_client(uri, config)
-
(Net::HTTP)
- Configured HTTP client
Parameters:
-
config
(Hash
) -- Configuration options -
uri
(URI
) -- The URI for the request
def create_http_client(uri, config) http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = (uri.scheme == 'https') http.verify_mode = OpenSSL::SSL::VERIFY_PEER http.read_timeout = config.dig(:runtime, :timeout) || DEFAULT_TIMEOUT http.open_timeout = config.dig(:runtime, :open_timeout) || DEFAULT_OPEN_TIMEOUT http end
def delete(endpoint, headers: {}, params: {}, config: {})
-
(Hash, nil)
- The parsed JSON response or nil if empty
Parameters:
-
config
(Hash
) -- Configuration options including timeout, retries, etc. -
params
(Hash
) -- Query parameters for the request -
headers
(Hash
) -- Additional headers to include in the request -
endpoint
(String
) -- The API endpoint to call
def delete(endpoint, headers: {}, params: {}, config: {}) uri = build_uri(endpoint, params) request = Net::HTTP::Delete.new(uri) execute_request(uri, request, headers, config) end
def execute_request(uri, request, headers, config)
-
(Hash, nil)
- The parsed JSON response or nil if empty
Parameters:
-
config
(Hash
) -- Configuration options -
headers
(Hash
) -- Additional headers to include -
request
(Net::HTTP::Request
) -- The request object -
uri
(URI
) -- The URI for the request
def execute_request(uri, request, headers, config) start_time = Time.now # Apply headers apply_headers(request, headers) # Apply retry logic retry_with_backoff(config) do http = create_http_client(uri, config) begin response = http.request(request) # Handle common HTTP errors case response when Net::HTTPSuccess parsed_response = parse_response(response) # Log request details if logging is enabled log_request_details( uri: uri, method: request.method, status: response.code, duration: Time.now - start_time, response: parsed_response ) parsed_response when Net::HTTPTooManyRequests raise RateLimitError, "Rate limit exceeded: #{response.body}" when Net::HTTPClientError, Net::HTTPServerError raise ClientError, "HTTP Error #{response.code}: #{response.body}" else raise ClientError, "Unexpected response: #{response.code} - #{response.body}" end rescue Timeout::Error, Errno::ETIMEDOUT raise TimeoutError, "Request to #{uri} timed out" rescue Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::EHOSTUNREACH, SocketError => e raise NetworkError, "Network error: #{e.message}" end end end
def get(endpoint, params: {}, headers: {}, config: {})
-
(Hash, nil)
- The parsed JSON response or nil if empty
Parameters:
-
config
(Hash
) -- Configuration options including timeout, retries, etc. -
params
(Hash
) -- Query parameters for the request -
headers
(Hash
) -- Additional headers to include in the request -
endpoint
(String
) -- The API endpoint to call
def get(endpoint, params: {}, headers: {}, config: {}) uri = build_uri(endpoint, params) request = Net::HTTP::Get.new(uri) execute_request(uri, request, headers, config) end
def initialize(config)
def initialize(config) @app_config = config end
def log_request_details(details)
-
details
(Hash
) -- Request and response details
def log_request_details(details) return unless defined?(ActiveGenie::Logger) ActiveGenie::Logger.trace({ code: :http_request, uri: details[:uri].to_s, method: details[:method], status: details[:status], duration: details[:duration], response_size: details[:response].to_s.bytesize }) end
def parse_response(response)
-
(Hash, nil)
- Parsed JSON or nil if empty
Parameters:
-
response
(Net::HTTPResponse
) -- The HTTP response
def parse_response(response) return nil if response.body.nil? || response.body.empty? begin JSON.parse(response.body) rescue JSON::ParserError => e raise ClientError, "Failed to parse JSON response: #{e.message}" end end
def post(endpoint, payload, params: {}, headers: {}, config: {})
-
(Hash, nil)
- The parsed JSON response or nil if empty
Parameters:
-
config
(Hash
) -- Configuration options including timeout, retries, etc. -
headers
(Hash
) -- Additional headers to include in the request -
payload
(Hash
) -- The request body to send -
endpoint
(String
) -- The API endpoint to call
def post(endpoint, payload, params: {}, headers: {}, config: {}) uri = build_uri(endpoint, params) request = Net::HTTP::Post.new(uri) request.body = payload.to_json execute_request(uri, request, headers, config) end
def put(endpoint, payload, headers: {}, config: {})
-
(Hash, nil)
- The parsed JSON response or nil if empty
Parameters:
-
config
(Hash
) -- Configuration options including timeout, retries, etc. -
headers
(Hash
) -- Additional headers to include in the request -
payload
(Hash
) -- The request body to send -
endpoint
(String
) -- The API endpoint to call
def put(endpoint, payload, headers: {}, config: {}) uri = build_uri(endpoint) request = Net::HTTP::Put.new(uri) request.body = payload.to_json execute_request(uri, request, headers, config) end
def retry_with_backoff(config = {})
-
(Object)
- The result of the block
Other tags:
- Yield: - The block to retry
Parameters:
-
config
(Hash
) -- Configuration options
def retry_with_backoff(config = {}) max_retries = config.dig(:runtime, :max_retries) || DEFAULT_MAX_RETRIES retry_delay = config.dig(:runtime, :retry_delay) || DEFAULT_RETRY_DELAY retries = 0 begin yield rescue RateLimitError, NetworkError => e if retries < max_retries sleep_time = retry_delay * (2 ** retries) retries += 1 ActiveGenie::Logger.trace({ code: :retry_attempt, attempt: retries, max_retries: max_retries, delay: sleep_time, error: e.message }) if defined?(ActiveGenie::Logger) sleep(sleep_time) retry else raise end end end