Class: Wavefront::Paginator::Base

Inherits:
Object
  • Object
show all
Defined in:
lib/wavefront-sdk/paginator/base.rb

Overview

Automatically handle pagination. This is an abstract class made concrete by an extension for each HTTP request type.

This class and its children do slightly unpleasant things with the HTTP request passed to us by the user, extracting and changing values in the URI, query string, or POST/PUT body. The POST class is particularly onerous.

Automatic pagination works by letting the user override the limit and offset values in API calls. Setting the limit to :all iteratively calls the Wavefront API, returning all requested objects an a standard Wavefront::Response wrapper; setting limit to :lazy returns a lazy Enumerable. The number of objects fetched in each API call, whether eager or lazy defaults to PAGE_SIZE, but the user can override that value by using the offset argument in conjunction with limit = :lazy | :all.

Direct Known Subclasses

Delete, Get, Post, Put

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(api_caller, conn, method, *args) ⇒ Base

Returns a new instance of Base.

Parameters:

  • api_caller (Wavefront::ApiCaller)

    the class which creates an instance of this one. We need to access its #response method.

  • conn (Faraday::Connection)
  • method (Symbol)

    HTTP request method

  • *args

    arguments to pass to Faraday’s #get method



37
38
39
40
41
42
43
# File 'lib/wavefront-sdk/paginator/base.rb', line 37

def initialize(api_caller, conn, method, *args)
  @api_caller  = api_caller
  @conn        = conn
  @method      = method
  @args        = args
  setup_vars
end

Instance Attribute Details

#api_callerObject (readonly)

Returns the value of attribute api_caller.



27
28
29
# File 'lib/wavefront-sdk/paginator/base.rb', line 27

def api_caller
  @api_caller
end

#argsObject (readonly)

Returns the value of attribute args.



27
28
29
# File 'lib/wavefront-sdk/paginator/base.rb', line 27

def args
  @args
end

#connObject (readonly)

Returns the value of attribute conn.



27
28
29
# File 'lib/wavefront-sdk/paginator/base.rb', line 27

def conn
  @conn
end

#initial_limitObject (readonly)

Returns the value of attribute initial_limit.



27
28
29
# File 'lib/wavefront-sdk/paginator/base.rb', line 27

def initial_limit
  @initial_limit
end

#methodObject (readonly)

Returns the value of attribute method.



27
28
29
# File 'lib/wavefront-sdk/paginator/base.rb', line 27

def method
  @method
end

#page_sizeObject (readonly)

Returns the value of attribute page_size.



27
28
29
# File 'lib/wavefront-sdk/paginator/base.rb', line 27

def page_size
  @page_size
end

Instance Method Details

#finalize_response(resp) ⇒ Object

In #make_recursive_call we’ve built up a composite response object. This method corrects a couple of things in that object which are misleading. (Because they’re left over from the original call.)



131
132
133
134
135
136
# File 'lib/wavefront-sdk/paginator/base.rb', line 131

def finalize_response(resp)
  resp.tap do |r|
    r.response[:limit] = r.response.items.size - 1
    r.response[:moreItems] = false
  end
end

#limit_and_offset(args) ⇒ Hash

An API call may or may not have a limit and/or offset value. They could (I think) be anywhere. Safely find and return them. If multiple elements of @args have :limit or :offset keys, the last value wins.

Parameters:

  • args (Array)

    arguments to be passed to a Faraday connection object

Returns:

  • (Hash)

    with keys :offset and :limit. Either’s value can be nil



60
61
62
63
64
65
66
67
# File 'lib/wavefront-sdk/paginator/base.rb', line 60

def limit_and_offset(args)
  ret = { offset: nil, limit: nil }

  args.select { |a| a.is_a?(Hash) }.each_with_object(ret) do |arg, a|
    a[:limit] = arg[:limit] if arg.key?(:limit)
    a[:offset] = arg[:offset] if arg.key?(:offset)
  end
end

#make_lazy_callEnumerable

Return all objects using a lazy enumerator.

Returns:

  • (Enumerable)

    with each item being

Raises:

  • (Wavefront::Exception::EnumerableError)

    if an API error is encountered at any point. The exception message is a Wavefront::Type::Status object, which will include the HTTP status code and any error string passed back by the API.



145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/wavefront-sdk/paginator/base.rb', line 145

def make_lazy_call
  offset = 0
  p_args = set_pagination(offset, page_size, args)

  api_caller.verbosity(conn, method, *p_args)

  return if api_caller.opts[:noop]

  Enumerator.new do |y|
    loop do
      offset += page_size
      api_caller.verbosity(conn, method, *p_args)
      resp = api_caller.respond(conn.public_send(method, *p_args))
      unless resp.ok?
        raise(Wavefront::Exception::EnumerableError, resp.status)
      end

      p_args = set_pagination(offset, page_size, p_args)
      resp.response.items.map { |i| y.<< i }
      raise StopIteration unless resp.more_items?
    end
  end.lazy
end

#make_recursive_callWavefront::Response

‘get’ eagerly. Re-run the get operation, merging together returned items until we have them all. rubocop:disable Metrics/MethodLength rubocop:disable Metrics/AbcSize

Returns:



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/wavefront-sdk/paginator/base.rb', line 103

def make_recursive_call
  offset = 0
  p_args = set_pagination(offset, page_size, args)
  api_caller.verbosity(conn, method, *p_args)

  return if api_caller.opts[:noop]

  ret = api_caller.respond(conn.public_send(method, *p_args))

  return ret unless ret.more_items?

  loop do
    offset += page_size
    p_args = set_pagination(offset, page_size, p_args)
    api_caller.verbosity(conn, method, *p_args)
    resp = api_caller.respond(conn.public_send(method, *p_args))
    raise StopIteration unless resp.ok?

    ret.response.items += resp.response.items
    return finalize_response(ret) unless resp.more_items?
  end
end

#set_limit_and_offset(arg, page_size, offset) ⇒ Object



90
91
92
93
94
95
# File 'lib/wavefront-sdk/paginator/base.rb', line 90

def set_limit_and_offset(arg, page_size, offset)
  arg.tap do |a|
    a[:limit] = page_size if a.key?(:limit)
    a[:offset] = offset if a.key?(:offset)
  end
end

#set_pagination(offset, page_size, args) ⇒ Object

Parameters:

  • offset (Integer)

    where to start fetching from

  • page_size (Integer)

    how many objects to fetch

  • args (Array)

    arguments to pass to Faraday.get



84
85
86
87
88
# File 'lib/wavefront-sdk/paginator/base.rb', line 84

def set_pagination(offset, page_size, args)
  args.map do |arg|
    arg.is_a?(Hash) ? set_limit_and_offset(arg, page_size, offset) : arg
  end
end

#setup_varsObject



45
46
47
48
# File 'lib/wavefront-sdk/paginator/base.rb', line 45

def setup_vars
  @initial_limit = limit_and_offset(args)[:limit]
  @page_size = user_page_size(args) unless initial_limit.is_a?(Integer)
end

#user_page_size(args) ⇒ Object

How many objects to get on each iteration? The user can pass it in as an alternative to the offset argument, If it’s not a positive integer, default to 999



73
74
75
76
77
78
# File 'lib/wavefront-sdk/paginator/base.rb', line 73

def user_page_size(args)
  arg_val = limit_and_offset(args)[:offset].to_i
  return arg_val if arg_val&.positive?

  PAGE_SIZE
end