Skip to content

REST API integration

Recipe contributed by santhanakarthikeyan.

I've built a page using AVO + REST API without using the ActiveRecord model. I was able to build an index page + associated has_many index page. It would be great if we could offer this as a feature, I guess, Avo would be the only admin framework that can offer this feature in case we take it forward 👍

I've made it work along with Pagination, Filter and even search are easily doable.

app/avo/filters/grace_period.rb

ruby
class GracePeriod < Avo::Filters::BooleanFilter
  self.name = 'Grace period'

  def apply(_request, query, value)
    query.where(value)
  end

  def options
    {
      grace_period: 'Within graceperiod'
    }
  end
end

app/avo/resources/aging_order_resource.rb

ruby
class AgingOrderResource < Avo::BaseResource
  self.title = :id
  self.includes = []

  field :id, as: :text
  field :folio_number, as: :text
  field :order_submitted_at, as: :date_time, timezone: 'Chennai', format: '%B %d, %Y %H:%M %Z'
  field :amc_name, as: :text
  field :scheme, as: :text
  field :primary_investor_id, as: :text
  field :order_type, as: :text
  field :systematic, as: :boolean
  field :order_reference, as: :text
  field :amount, as: :text
  field :units, as: :text
  field :age, as: :text

  filter GracePeriod
end

app/controllers/avo/aging_orders_controller.rb

ruby
module Avo
  class AgingOrdersController < Avo::ResourcesController
    def pagy_get_items(collection, _pagy)
      collection.all.items
    end

    def pagy_get_vars(collection, vars)
      collection.where(page: page, size: per_page)

      vars[:count] = collection.all.count
      vars[:page] = params[:page]
      vars
    end

    private

    def per_page
      params[:per_page] || Avo.configuration.per_page
    end

    def page
      params[:page]
    end
  end
end

app/models/aging_order.rb

ruby
class AgingOrder
  include ActiveModel::Model
  include ActiveModel::Conversion
  include ActiveModel::Validations
  extend ActiveModel::Naming

  attr_accessor :id, :investment_date, :folio_number, :order_submitted_at,
                :amc_name, :scheme, :primary_investor_id, :order_type, :systematic,
                :order_reference, :amount, :units, :age

  class << self
    def column_names
      %i[id investment_date folio_number order_submitted_at amc_name
         scheme primary_investor_id order_type systematic
         order_reference amount units age]
    end

    def base_class
      AgingOrder
    end

    def root_key
      'data'
    end

    def count_key
      'total_elements'
    end

    def all(query)
      response = HTTParty.get(ENV['AGING_URL'], query: query)
      JSON.parse(response.body)
    end
  end

  def persisted?
    id.present?
  end
end

app/models/lazy_loader.rb

ruby
class LazyLoader
  def initialize(klass)
    @offset, @limit = nil
    @params = {}
    @items = []
    @count = 0
    @klass = klass
  end

  def where(query)
    @params = @params.merge(query)
    self
  end

  def items
    all
    @items
  end

  def count(_attr = nil)
    all
    @count
  end

  def offset(value)
    @offset = value
    self
  end

  def limit(value)
    @limit = value
    items[@offset, @limit]
  end

  def all
    api_response
    self
  end

  def to_sql
    ""
  end

  private

  def api_response
    @api_response ||= begin
      json = @klass.all(@params)
      json.fetch(@klass.root_key, []).map do |obj|
        @items << @klass.new(obj)
      end
      @count = json.fetch(@klass.count_key, @items.size)
    end
  end
end

app/policies/aging_order_policy.rb

ruby
class AgingOrderPolicy < ApplicationPolicy
  class Scope < Scope
    def resolve
      LazyLoader.new(scope)
    end
  end

  def index?
    user.admin?
  end

  def show?
    false
  end
end

config/initializers/array.rb

ruby
class Array
  def limit(upto)
    take(upto)
  end
end