lib/seam/request.rb in seam-2.0.0a2 vs lib/seam/request.rb in seam-2.0.0b0
- old
+ new
@@ -1,73 +1,104 @@
# frozen_string_literal: true
-require "http"
+require "faraday"
+require "faraday/retry"
module Seam
- class Request
- attr_reader :base_uri, :api_key, :debug
+ module Http
+ module Request
+ def self.create_faraday_client(endpoint, auth_headers, faraday_options = {}, faraday_retry_options = {})
+ default_options = {
+ url: endpoint,
+ headers: auth_headers.merge(default_headers)
+ }
- class Error < StandardError
- attr_reader :status, :response
+ options = deep_merge(default_options, faraday_options)
- def initialize(message, status, response)
- super(message)
- @status = status
- @response = response
+ default_faraday_retry_options = {
+ max: 2,
+ backoff_factor: 2
+ }
+
+ faraday_retry_options = default_faraday_retry_options.merge(faraday_retry_options)
+
+ Faraday.new(options) do |builder|
+ builder.request :json
+ builder.response :json
+ builder.use ResponseMiddleware
+ builder.request :retry, faraday_retry_options
+ end
end
- end
- def initialize(api_key:, base_uri:, debug: false)
- @api_key = api_key
- @base_uri = base_uri
- @debug = debug
- end
+ def self.default_headers
+ {
+ "User-Agent" => "seam-ruby/#{Seam::VERSION}",
+ "Content-Type" => "application/json",
+ :"seam-sdk-name" => "seamapi/ruby",
+ :"seam-sdk-version" => Seam::VERSION,
+ :"seam-lts-version" => Seam::LTS_VERSION
+ }
+ end
- def perform(method, uri, config = {})
- Logger.info("Request: #{method} #{uri} #{config}") if debug
+ class ResponseMiddleware < Faraday::Response::RaiseError
+ def on_complete(env)
+ return if env.success?
- config[:body] = config[:body].to_json if config[:body]
+ status_code = env.status
+ request_id = env.response_headers["seam-request-id"]
- response = HTTP.request(
- method,
- build_url(uri),
- {headers: headers}.merge(config)
- )
+ raise Http::UnauthorizedError.new(request_id) if status_code == 401
- return response.parse if response.status.success?
+ if seam_api_error_response?(env)
+ body = JSON.parse(env.body)
+ error = body["error"]
+ error_details = {
+ type: error["type"] || "unknown_error",
+ message: error["message"] || "Unknown error",
+ data: error["data"]
+ }
- handle_error_response(response, method, uri)
- end
+ if error["type"] == "invalid_input"
+ error_details["validation_errors"] = error["validation_errors"]
+ raise Http::InvalidInputError.new(error_details, status_code, request_id)
+ end
- protected
+ raise Http::ApiError.new(error_details, status_code, request_id)
+ end
- def handle_error_response(response, method, uri)
- msg = "Api Error #{response.status.code} #{method} #{uri}"
- code = response.status.code
+ super
+ end
- if code >= 400 && code < 500 && (err = response.parse["error"])
- msg = "Api Error #{err["type"]}\nrequest_id: #{err["request_id"]}\n#{err["message"]}"
- end
+ def seam_api_error_response?(env)
+ return false unless env.response_headers
- raise Error.new(msg, code, response)
- end
+ content_type = env.response_headers["Content-Type"]
+ return false unless content_type&.start_with?("application/json")
- def build_url(uri)
- "#{base_uri}#{uri}"
- end
+ begin
+ body = JSON.parse(env.body)
+ return false unless body.is_a?(Hash) && body["error"].is_a?(Hash)
- def headers
- {
- "User-Agent" => user_agent,
- "Content-Type" => "application/json",
- "Authorization" => "Bearer #{api_key}",
- :"seam-sdk-name" => "seamapi/ruby",
- :"seam-sdk-version" => Seam::VERSION,
- :"seam-lts-version" => Seam::LTS_VERSION
- }
- end
+ error = body["error"]
+ error["type"].is_a?(String) && error["message"].is_a?(String)
+ rescue JSON::ParserError
+ false
+ end
+ end
+ end
- def user_agent
- "seam-ruby/#{Seam::VERSION}"
+ def self.deep_merge(hash1, hash2)
+ result = hash1.dup
+ hash2.each do |key, value|
+ result[key] = if value.is_a?(Hash) && result[key].is_a?(Hash)
+ deep_merge(result[key], value)
+ else
+ value
+ end
+ end
+ result
+ end
+
+ private_class_method :deep_merge
end
end
end