class Antbird::Client

Attributes

adapter[R]
api_specs[R]
last_request[R]
open_timeout[R]
read_timeout[R]
scope[R]
url[R]
version[R]

Public Class Methods

new( scope: {}, url: "http://localhost:9200", version: nil, read_timeout: 5, open_timeout: 2, adapter: ::Faraday.default_adapter, &block) click to toggle source
# File lib/antbird/client.rb, line 7
def initialize(
  scope: {},
  url: "http://localhost:9200",
  version: nil,
  read_timeout: 5,
  open_timeout: 2,
  adapter: ::Faraday.default_adapter,
  &block)

  @read_timeout = read_timeout
  @open_timeout = open_timeout
  @adapter      = adapter
  @block        = block
  @url          = url

  @scope     = scope.transform_keys(&:to_sym)
  @version   = version

  @api_specs = {}
end

Public Instance Methods

connection() click to toggle source
# File lib/antbird/client.rb, line 179
def connection
  @connection ||= Faraday.new(url) do |conn|
    @block&.call(conn)

    conn.request :json
    conn.response :json, content_type: /\bjson$/

    conn.options[:timeout]      = read_timeout
    conn.options[:open_timeout] = open_timeout

    conn.adapter adapter
  end
end
extract_body(params) click to toggle source
# File lib/antbird/client.rb, line 156
def extract_body(params)
  body = params.delete :body
  return if body.nil?

  body =
    case body
    when String
      body
    when Array
      if body.all? { |b| b.is_a?(Hash) }
        body.map { |b| JSON.dump(b) }.join("\n") + "\n"
      else
        body << nil unless body.last.nil?
        body.join "\n"
      end
    else
      JSON.dump(body)
    end

  # Prevent excon from changing the encoding (see https://github.com/github/elastomer-client/issues/138)
  body.freeze
end
extract_method(params) click to toggle source
# File lib/antbird/client.rb, line 142
def extract_method(params)
  params.delete(:method)&.to_sym
end
extract_scopes(params) click to toggle source
# File lib/antbird/client.rb, line 146
def extract_scopes(params)
  scopes = {}
  [:index, :type, :id].each do |s|
    scope = params.delete(s)
    next unless scope
    scopes[s] = scope
  end
  scopes
end
request(api_name, api_spec, params) click to toggle source
# File lib/antbird/client.rb, line 41
def request(api_name, api_spec, params)
  validate_params(api_spec, params)

  body   = extract_body(params)
  scopes = extract_scopes(params)
  forced_method = extract_method(params)

  # Greedy match
  api_path = nil
  api_path_template = nil
  path_methods = nil
  sort_url_paths(api_spec['url']['paths']).each do |path|
    if path.is_a?(Hash)
      path_methods = path['methods']
      path = path['path']
    end

    embeded = path.gsub(/{([a-z_\-}]+)}/) do |match|
      scopes[$1.to_sym] || @scope[$1.to_sym] || match
    end

    unless embeded.include?('{')
      api_path_template = path
      api_path = embeded
      break
    end
  end

  unless api_path
    raise "API path not found: paths: #{api_spec['url']['paths']}, scope: #{@scope}"
  end

  methods = (path_methods || api_spec['methods']).map { |m| m.downcase.to_sym }
  method =
    if forced_method
      forced_method
    elsif methods.include?(:put)
      :put
    elsif methods.include?(:post)
      :post
    else
      methods.first
    end

  read_timeout = params.delete(:read_timeout)
  params.reject! { |_, v| v.nil? }

  @last_request = {
    method: method,
    forced_method: forced_method,
    api_path: api_path,
    api_path_template: api_path_template,
    params: params
  }

  response =
    case method
    when :head
      connection.head(api_path) do |req|
        req.params = params unless params.empty?
        req.options[:timeout] = read_timeout if read_timeout
      end
    when :get
      connection.get(api_path) do |req|
        req.params = params unless params.empty?
        req.body = body if body
        req.options[:timeout] = read_timeout if read_timeout
      end
    when :put
      connection.put(api_path, body) do |req|
        req.params = params unless params.empty?
        req.options[:timeout] = read_timeout if read_timeout
      end
    when :post
      connection.post(api_path, body) do |req|
        req.params = params unless params.empty?
        req.options[:timeout] = read_timeout if read_timeout
      end
    when :delete
      connection.delete(api_path) do |req|
        req.params = params unless params.empty?
        req.body = body if body
        req.options[:timeout] = read_timeout if read_timeout
      end
    else
      raise ArgumentError, "Unknown HTTP request method: #{method.inspect}"
    end

  if method == :head
    case response.status
    when 200
      return true
    when 404
      return false
    end
  end

  handle_errors!(response)
  response.body
end
scoped(new_scope = {}) click to toggle source
# File lib/antbird/client.rb, line 31
def scoped(new_scope = {})
  Client.new(
    scope: new_scope,
    url: url,
    version: version,
    read_timeout: read_timeout,
    open_timeout: open_timeout
  )
end

Private Instance Methods

api_specs_loaded?() click to toggle source
# File lib/antbird/client.rb, line 239
def api_specs_loaded?
  !!@api_specs_loaded
end
ensure_api_spec_loaded() click to toggle source
# File lib/antbird/client.rb, line 228
def ensure_api_spec_loaded
  return if api_specs_loaded?

  @version ||= fetch_version
  class_version = @version.split('.')[0, 2].join('_')
  require "antbird/rest_api/rest_api_v#{class_version}"
  extend Antbird::RestApi.const_get "RestApiV#{class_version}"

  @api_specs_loaded = true
end
fetch_version() click to toggle source
# File lib/antbird/client.rb, line 224
def fetch_version
  connection.get('/').body.dig('version', 'number')
end
handle_errors!(response) click to toggle source
# File lib/antbird/client.rb, line 207
def handle_errors!(response)
  if response.status >= 500
    raise ServerError, response
  elsif response.body.is_a?(Hash) && response.body.key?("error")
    raise RequestError, response
  end
end
method_missing(name, *args, &block) click to toggle source
Calls superclass method
# File lib/antbird/client.rb, line 243
def method_missing(name, *args, &block)
  return super if api_specs_loaded?

  ensure_api_spec_loaded

  return super unless respond_to?(name)
  __send__(name, *args, &block)
end
sort_url_paths(url_paths) click to toggle source

NOTE: stable sort

# File lib/antbird/client.rb, line 196
def sort_url_paths(url_paths)
  i = 0
  url_paths.sort_by do |path|
    if path.is_a?(Hash)
      [-path['path'].count('{'), (path['deprecated'] ? 1 : 0), i += 1]
    else
      [-path.count('{'), i += 1]
    end
  end
end
validate_params(api_spec, params) click to toggle source
# File lib/antbird/client.rb, line 215
def validate_params(api_spec, params)
  # TODO case: required parameter is missing
  # TODO case: invalid parameter name
  # TODO case: invalid parameter format
  if api_spec.dig('body', 'required') && !params.key?(:body)
    raise ArgumentError, 'Body is missing'
  end
end