# frozen_string_literal: true require 'active_support/core_ext/hash/indifferent_access' require 'active_support/core_ext/module/delegation' require 'active_support/core_ext/object/blank' require 'faraday' require 'singleton' require 'zeitwerk' loader = Zeitwerk::Loader.new loader.inflector = Zeitwerk::GemInflector.new(__FILE__) loader.push_dir(File.absolute_path("#{__FILE__}/../..")) loader.setup module Preservation # REST API client wrapper for PreservationCatalog with error handling class Client class Error < StandardError; end # Error raised when server returns 404 Not Found class NotFoundError < Error; end # Error raised when server returns 423 Locked class LockedError < Error; end # Error raised when server returns 409 Conflict class ConflictError < Error; end # Error raised when server returns an unexpected response # e.g., 4xx or 5xx status not otherwise handled class UnexpectedResponseError < Error; end # Error raised when Faraday gem fails to connect, e.g., on SSL errors or # timeouts class ConnectionFailedError < Error; end DEFAULT_API_VERSION = 'v1' DEFAULT_TIMEOUT = 300 TOKEN_HEADER = 'Authorization' include Singleton # @return [Preservation::Client::Objects] an instance of the `Client::Objects` class def objects @objects ||= Objects.new(connection: connection, api_version: DEFAULT_API_VERSION) end # @return [Preservation::Client::Catalog] an instance of the `Client::Catalog` class def catalog @catalog ||= Catalog.new(connection: connection, api_version: DEFAULT_API_VERSION) end class << self # @param [String] url the endpoint URL # @param [String] token a bearer token for HTTP authentication # @param [Integer] read_timeout the value in seconds of the read timeout def configure(url:, token:, read_timeout: DEFAULT_TIMEOUT) instance.url = url instance.token = token instance.read_timeout = read_timeout # Force connection to be re-established when `.configure` is called instance.connection = nil self end delegate :objects, :update, to: :instance end attr_writer :connection, :read_timeout, :token, :url delegate :update, to: :catalog private def url @url || raise(Error, 'url has not yet been configured') end def token @token || raise(Error, 'auth token has not been configured') end def read_timeout @read_timeout || raise(Error, 'read timeout has not been configured') end def connection @connection ||= Faraday.new(url, request: { read_timeout: read_timeout }) do |builder| builder.use ErrorFaradayMiddleware builder.use Faraday::Request::UrlEncoded builder.use Faraday::Response::RaiseError # raise exceptions on 40x, 50x responses builder.adapter Faraday.default_adapter builder.headers[:user_agent] = user_agent builder.headers[TOKEN_HEADER] = "Bearer #{token}" end end def user_agent "preservation-client #{Preservation::Client::VERSION}" end end end