class Zaius::ZaiusClient

Attributes

conn[RW]

Public Class Methods

active_client() click to toggle source
# File lib/zaius/zaius_client.rb, line 15
def self.active_client
  Thread.current[:zaius_client] || default_client
end
default_client() click to toggle source
# File lib/zaius/zaius_client.rb, line 11
def self.default_client
  Thread.current[:zaius_client_default_client] ||= ZaiusClient.new(default_conn)
end
default_conn() click to toggle source

A default Faraday connection to be used when one isn't configured. This object should never be mutated, and instead instantiating your own connection and wrapping it in a ZaiusClient object should be preferred.

# File lib/zaius/zaius_client.rb, line 22
def self.default_conn
  # We're going to keep connections around so that we can take advantage
  # of connection re-use, so make sure that we have a separate connection
  # object per thread.
  Thread.current[:zaius_client_default_conn] ||= begin
    conn = Faraday.new do |c|
      c.use Faraday::Request::Multipart
      c.use Faraday::Request::UrlEncoded
      c.use Faraday::Response::RaiseError
      c.adapter Faraday.default_adapter
    end

    conn
  end
end
new(conn = nil) click to toggle source

Initializes a new ZaiusClient. Expects a Faraday connection object, and uses a default connection unless one is passed.

# File lib/zaius/zaius_client.rb, line 7
def initialize(conn = nil)
  self.conn = conn || self.class.default_conn
end

Public Instance Methods

api_url(url = "", api_base = nil) click to toggle source
# File lib/zaius/zaius_client.rb, line 223
def api_url(url = "", api_base = nil)
  (api_base || Zaius.api_base) + url
end
check_api_key!(api_key) click to toggle source
# File lib/zaius/zaius_client.rb, line 227
def check_api_key!(api_key)
  unless api_key
    raise AuthenticationError, "No API key provided. " \
      'Set your API key using "Zaius.api_key = <API-KEY>". '
  end

  return unless api_key =~ /\s/

  raise AuthenticationError, "Your API key is invalid, as it contains whitespace."
end
execute_request(method, path, api_base: nil, api_key: nil, headers: {}, params: {}) click to toggle source
# File lib/zaius/zaius_client.rb, line 50
def execute_request(method, path,
                    api_base: nil, api_key: nil, headers: {}, params: {})

  api_base ||= Zaius.api_base
  api_key ||= Zaius.api_key

  check_api_key!(api_key)

  url = api_url(path, api_base)

  body = nil
  query_params = nil

  case method.to_s.downcase.to_sym
  when :get, :head, :delete
    query_params = params
  else
    body = params.to_json
  end

  # This works around an edge case where we end up with both query
  # parameters in `query_params` and query parameters that are appended
  # onto the end of the given path. In this case, Faraday will silently
  # discard the URL's parameters which may break a request.
  #
  # Here we decode any parameters that were added onto the end of a path
  # and add them to `query_params` so that all parameters end up in one
  # place and all of them are correctly included in the final request.
  # u = URI.parse(path)
  # unless u.query.nil?
  #   query_params ||= {}
  #   query_params = Hash[URI.decode_www_form(u.query)].merge(query_params)

  #   # Reset the path minus any query parameters that were specified.
  #   path = u.path
  # end

  headers = request_headers(api_key, method)
            .update(headers)

  # stores information on the request we're about to make so that we don't
  # have to pass as many parameters around for logging.
  context = RequestLogContext.new
  context.api_key         = api_key
  context.body            = body
  context.method          = method
  context.path            = path
  context.query_params    = query_params ? Util.encode_parameters(query_params) : nil

  http_resp = execute_request_with_rescues(api_base, context) do
    conn.run_request(method, url, body, headers) do |req|
      req.params = query_params unless query_params.nil?
    end
  end

  begin
    resp = ZaiusResponse.from_faraday_response(http_resp)
  rescue JSON::ParserError
    raise general_api_error(http_resp.status, http_resp.body)
  end

  # Allows ZaiusClient#request to return a response object to a caller.
  @last_response = resp
  [resp, api_key]
end
execute_request_with_rescues(api_base, context) { || ... } click to toggle source
# File lib/zaius/zaius_client.rb, line 116
def execute_request_with_rescues(api_base, context)
  num_retries = 0
  begin
    request_start = Time.now
    log_request(context, num_retries)
    resp = yield
    context = context.dup_from_response(resp)
    log_response(context, request_start, resp.status, resp.body)

  # We rescue all exceptions from a request so that we have an easy spot to
  # implement our retry logic across the board. We'll re-raise if it's a type
  # of exception that we didn't expect to handle.
  rescue StandardError => e
    # If we modify context we copy it into a new variable so as not to
    # taint the original on a retry.
    error_context = context

    if e.respond_to?(:response) && e.response
      error_context = context.dup_from_response(e.response)
      log_response(error_context, request_start,
                   e.response[:status], e.response[:body])
    else
      log_response_error(error_context, request_start, e)
    end

    case e
    when Faraday::ClientError
      if e.response
        handle_error_response(e.response, error_context)
      else
        handle_network_error(e, error_context, num_retries, api_base)
      end

    # Only handle errors when we know we can do so, and re-raise otherwise.
    # This should be pretty infrequent.
    else
      raise
    end
  end

  resp
end
general_api_error(status, body) click to toggle source
# File lib/zaius/zaius_client.rb, line 175
def general_api_error(status, body)
  ZaiusError.new("Invalid response object from API: #{body.inspect} " \
               "(HTTP response code was #{status})",
               http_status: status, http_body: body)
end
handle_error_response(http_resp, context) click to toggle source
# File lib/zaius/zaius_client.rb, line 159
def handle_error_response(http_resp, context)
  begin
    resp = ZaiusResponse.from_faraday_hash(http_resp)
    error_data = resp.data[:title]

    raise ZaiusError, "Indeterminate error" if resp.data[:title].nil?
  rescue JSON::ParserError, ZaiusError
    raise general_api_error(http_resp[:status], http_resp[:body])
  end

  error = specific_api_error(resp, error_data, context)

  error.response = resp
  raise(error)
end
log_response(context, request_start, status, body) click to toggle source
# File lib/zaius/zaius_client.rb, line 187
def log_response(context, request_start, status, body)
  Util.log_info("Response from Zaius",
                account: context.account,
                api_version: context.api_version,
                elapsed: Time.now - request_start,
                method: context.method,
                path: context.path,
                request_id: context.request_id,
                url: context.url,
                status: status)
  Util.log_debug("Response details",
                 body: body,
                  request_id: context.request_id)
end
log_response_error(context, request_start, e) click to toggle source
# File lib/zaius/zaius_client.rb, line 215
def log_response_error(context, request_start, e)
  Util.log_error("Request error",
                 elapsed: Time.now - request_start,
                 error_message: e.message,
                  method: context.method,
                 path: context.path)
end
request_headers(api_key, method) click to toggle source
# File lib/zaius/zaius_client.rb, line 38
def request_headers(api_key, method)
  user_agent = "Zaius/v1 RubyBindings/#{Zaius::VERSION}"

  headers = {
    "User-Agent" => user_agent,
    "x-api-key" => api_key,
    "Content-Type" => "application/json",
  }

  headers
end
specific_api_error(response, error_data, context) click to toggle source
# File lib/zaius/zaius_client.rb, line 181
def specific_api_error(response, error_data, context)
  response_data = response.data

  APIError.new(title: response_data[:title], http_status: response.http_status, detail: response_data[:detail])
end

Private Instance Methods

log_request(context, num_retries) click to toggle source
# File lib/zaius/zaius_client.rb, line 202
def log_request(context, num_retries)
  Util.log_info("Request to Zaius",
                account: context.account,
                api_version: context.api_version,
                method: context.method,
                num_retries: num_retries,
                path: context.path)
  Util.log_debug("Request details",
                 body: context.body,
                 query_params: context.query_params)
end