111 lines
3.7 KiB
Ruby
111 lines
3.7 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require "faraday"
|
|
require "faraday/retry"
|
|
require "faraday/multipart"
|
|
|
|
module Fiken
|
|
# Wraps a Faraday connection: bearer auth, JSON encode/decode, retry on
|
|
# rate-limit/server errors, and mapping of HTTP error statuses to Fiken errors.
|
|
class Connection
|
|
DEFAULT_BASE_URL = "https://api.fiken.no/api/v2"
|
|
|
|
Result = Struct.new(:status, :body, :headers, keyword_init: true)
|
|
|
|
DEFAULT_RETRY = {
|
|
max: 2,
|
|
interval: 0.5,
|
|
backoff_factor: 2,
|
|
retry_statuses: [429, 500, 502, 503, 504],
|
|
methods: [] # retry regardless of method; Fiken's 429 means "not processed"
|
|
}.freeze
|
|
|
|
def initialize(token:, base_url: DEFAULT_BASE_URL, user_agent: nil,
|
|
retry_options: {}, logger: nil, adapter: Faraday.default_adapter)
|
|
@token = token
|
|
# Faraday joins relative paths against the base URL, which only behaves
|
|
# predictably when the base ends in "/" and request paths do not start with one.
|
|
@base_url = base_url.end_with?("/") ? base_url : "#{base_url}/"
|
|
@user_agent = user_agent || "fiken-ruby/#{Fiken::VERSION}"
|
|
@retry_options = DEFAULT_RETRY.merge(retry_options)
|
|
@logger = logger
|
|
@adapter = adapter
|
|
end
|
|
|
|
def get(path, params = nil, headers = nil)
|
|
run(:get, path, params: params, headers: headers)
|
|
end
|
|
|
|
def post(path, body = nil, headers = nil)
|
|
run(:post, path, body: body, headers: headers)
|
|
end
|
|
|
|
def put(path, body = nil, headers = nil)
|
|
run(:put, path, body: body, headers: headers)
|
|
end
|
|
|
|
def patch(path, body = nil, headers = nil)
|
|
run(:patch, path, body: body, headers: headers)
|
|
end
|
|
|
|
def delete(path, params = nil, headers = nil)
|
|
run(:delete, path, params: params, headers: headers)
|
|
end
|
|
|
|
# Multipart upload (attachments). `parts` is a hash of field name => value,
|
|
# where a value may be a Faraday::Multipart::FilePart for the file itself.
|
|
def post_multipart(path, parts)
|
|
response = multipart_faraday.post(path.sub(%r{\A/}, ""), parts)
|
|
raise_on_error(response)
|
|
Result.new(status: response.status, body: response.body, headers: response.headers)
|
|
rescue Faraday::Error => e
|
|
raise ConnectionError, e.message
|
|
end
|
|
|
|
def faraday
|
|
@faraday ||= Faraday.new(url: @base_url) do |f|
|
|
f.request :authorization, "Bearer", @token
|
|
f.request :json
|
|
f.request :retry, @retry_options
|
|
f.response :json, content_type: /\bjson$/, parser_options: { symbolize_names: false }
|
|
f.headers["User-Agent"] = @user_agent
|
|
f.headers["Accept"] = "application/json"
|
|
f.response :logger, @logger if @logger
|
|
f.adapter @adapter
|
|
end
|
|
end
|
|
|
|
def multipart_faraday
|
|
@multipart_faraday ||= Faraday.new(url: @base_url) do |f|
|
|
f.request :authorization, "Bearer", @token
|
|
f.request :multipart
|
|
f.request :url_encoded
|
|
f.response :json, content_type: /\bjson$/
|
|
f.headers["User-Agent"] = @user_agent
|
|
f.headers["Accept"] = "application/json"
|
|
f.adapter @adapter
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def run(method, path, params: nil, body: nil, headers: nil)
|
|
response = faraday.public_send(method, path.sub(%r{\A/}, "")) do |req|
|
|
req.params.update(params) if params
|
|
req.body = body unless body.nil?
|
|
req.headers.update(headers) if headers
|
|
end
|
|
raise_on_error(response)
|
|
Result.new(status: response.status, body: response.body, headers: response.headers)
|
|
rescue Faraday::Error => e
|
|
raise ConnectionError, e.message
|
|
end
|
|
|
|
def raise_on_error(response)
|
|
return if response.status < 400
|
|
|
|
raise Error.from_response(response.status, response.body, response)
|
|
end
|
|
end
|
|
end
|