lib/preservation/client/versioned_api_service.rb
# frozen_string_literal: true
module Preservation
class Client
# @abstract API calls to a versioned endpoint
class VersionedApiService
def initialize(connection:, api_version: DEFAULT_API_VERSION)
@connection = connection
@api_version = api_version
end
private
attr_reader :connection, :api_version
# @param path [String] path to be appended to connection url (no leading slash)
def get_json(path, object_id)
req_url = "#{api_version}/#{path}"
resp = connection.get do |req|
req.url req_url
req.headers['Content-Type'] = 'application/json'
req.headers['Accept'] = 'application/json'
end
return with_indifferent_access_for(JSON.parse(resp.body)) if resp.success?
errmsg = ResponseErrorFormatter
.format(response: resp, object_id: object_id, client_method_name: caller_locations.first.label)
raise UnexpectedResponseError, errmsg
rescue Faraday::ResourceNotFound
errmsg = "#{object_id} not found in Preservation at #{connection.url_prefix}#{req_url}"
raise NotFoundError, errmsg
rescue Faraday::Error => e
errmsg = "Preservation::Client.#{caller_locations.first.label} for #{object_id} " \
"got #{e.response[:status]} from Preservation at #{req_url}: #{e.response[:body]}"
raise UnexpectedResponseError, errmsg
end
# @param path [String] path to be appended to connection url (no leading slash)
# @param params [Hash] optional request parameters
# @param on_data [Proc] a callback to use when a streaming response is desired.
def get(path, params, on_data:)
return http_response(:get, path, params) unless on_data
req_url = "#{api_version}/#{path}"
connection.get("#{api_version}/#{path}", params) do |req|
req.options.on_data = proc do |chunk, size, env|
if env.status >= 300
errmsg = "Preservation::Client.#{caller_locations.first.label} " \
"got #{env.status} from Preservation at #{req_url}"
raise http_exception_class(env.status), errmsg
end
on_data.call(chunk, size, env)
end
end
end
# @param path [String] path to be appended to connection url (no leading slash)
# @param params [Hash] optional request parameters
def post(path, params)
http_response(:post, path, params)
end
# @param path [String] path to be appended to connection url (no leading slash)
# @param params [Hash] optional request parameters
def patch(path, params)
http_response(:patch, path, params)
end
# @param path [String] path to be appended to connection url (no leading slash)
# @param params [Hash] optional request parameters
def put(path, params)
http_response(:put, path, params)
end
# @param path [String] path to be appended to connection url (no leading slash)
# @param params [Hash] optional request parameters
def delete(path, params)
http_response(:delete, path, params)
end
# @param method [Symbol] a symbol representing an HTTP method: :get, :post, :patch, :put, :delete
# @param path [String] path to be appended to connection url (no leading slash)
# @param params [Hash] optional request parameters
def http_response(method, path, params)
req_url = "#{api_version}/#{path}"
resp =
case method
when :delete, :get
connection.public_send(method, req_url, params)
when :patch, :post, :put
request_json = params.to_json if params&.any?
connection.public_send(method, req_url, request_json, 'Content-Type' => 'application/json')
end
return resp.body if resp.success?
errmsg = ResponseErrorFormatter.format(response: resp, client_method_name: caller_locations.first.label)
raise UnexpectedResponseError, errmsg
rescue Faraday::ResourceNotFound => e
errmsg = "Preservation::Client.#{caller_locations.first.label} " \
"got #{e.response[:status]} from Preservation at #{req_url}: #{e.response[:body]}"
raise NotFoundError, errmsg
rescue Faraday::Error => e
errmsg = "Preservation::Client.#{caller_locations.first.label} " \
"got #{e.response[:status]} from Preservation at #{req_url}: #{e.response[:body]}"
raise http_exception_class(e.response[:status]), errmsg
end
# @param status_code [Integer] the HTTP status code to translate to an exception class
def http_exception_class(status_code)
case status_code
when 404
NotFoundError
when 423
LockedError
when 409
ConflictError
else
UnexpectedResponseError
end
end
def with_indifferent_access_for(obj)
if obj.is_a?(Array)
obj.map { |member| with_indifferent_access_for(member) }
elsif obj.is_a?(Hash)
obj.with_indifferent_access
else
obj
end
end
end
end
end