v3: add pagination

This commit is contained in:
Konstantin Haase 2015-08-21 17:04:12 +02:00
parent ac522d6fdd
commit b3f47d3856
7 changed files with 199 additions and 15 deletions

View File

@ -2,6 +2,10 @@ require 'travis/api/v3/access_control/generic'
module Travis::API::V3 module Travis::API::V3
class AccessControl::Anonymous < AccessControl::Generic class AccessControl::Anonymous < AccessControl::Generic
def self.new
@instace ||= super
end
# use when Authorization header is not set # use when Authorization header is not set
auth_type(nil) auth_type(nil)

View File

@ -0,0 +1,42 @@
module Travis::API::V3
class Paginator
attr_accessor :default_limit, :max_limit
def initialize(default_limit: 25, max_limit: 100)
@default_limit = default_limit
@max_limit = max_limit
end
def paginate(result, limit: nil, offset: nil, access_control: AccessControl::Anonymous.new)
limit &&= Integer(limit, :limit)
limit ||= default_limit
limit = default_limit if limit < 0
unless access_control.full_access?
limit = max_limit if limit > max_limit or limit < 1
end
offset &&= Integer(offset, :offset)
offset = 0 if offset.nil? or offset < 0
count = result.resource ? result.resource.count : 0
result.resource &&= result.resource.limit(limit) unless limit == 0
result.resource &&= result.resource.offset(offset) unless offset == 0
pagination_info = {
limit: limit,
offset: offset,
count: count,
}
result.meta_data[:pagination] = pagination_info
result
end
def Integer(value, key)
super(value)
rescue ArgumentError
raise WrongParams, "#{key} must be an integer"
end
end
end

View File

@ -0,0 +1,95 @@
require "addressable/uri"
module Travis::API::V3
class Paginator
class URLGenerator
class FancyParser
def initialize(href)
@uri = Addressable::URI.parse(href)
end
def generate(offset, limit)
uri = @uri.dup
uri.query_values = uri.query_values.merge("offset".freeze => offset, "limit".freeze => limit)
uri.to_s
end
end
class FastParser
PATTERN = /\?(?:&?(?:limit|offset)=[^=]*)*\Z/
def self.can_handle?(href)
return true unless href.include? ??.freeze
href =~ PATTERN
end
def initialize(href)
@path_info = href.split(??.freeze, 2).first
end
def generate(offset, limit)
"#{@path_info}?limit=#{limit}&offset=#{offset}"
end
end
def initialize(href, limit: 0, offset: 0, count: 0, **)
@parser = FastParser.can_handle?(href) ? FastParser.new(href) : FancyParser.new(href)
@href = href
@limit = limit
@offset = offset
@count = count
end
def last?
@count <= @offset + @limit
end
def first?
@offset == 0
end
def next_info
info(offset: @offset + @limit) unless last?
end
def previous_info
return if @offset == 0
@offset <= @limit ? info(offset: 0, limit: @offset) : info(offset: @offset - @limit, limit: @limit)
end
def first_info
info(offset: 0)
end
def last_info
offset = @count / @limit * @limit
offset -= @limit if offset == @count
info(offset: offset)
end
def info(offset: @offset, limit: @limit)
{
:@href => uri_with(offset, limit),
:offset => offset,
:limit => limit
}
end
def to_h
{
is_first: first?,
is_last: last?,
next: next_info,
prev: previous_info,
first: first_info,
last: last_info
}
end
def uri_with(offset, limit)
return @href if offset == @offset and limit == @limit
@parser.generate(offset, limit)
end
end
end
end

View File

@ -17,19 +17,34 @@ module Travis::API::V3
available_attributes << value available_attributes << value
end end
def initialize(list, href: nil, included: [], **options) attr_reader :href, :options, :list, :included, :meta_data
@href = href
@options = options def initialize(list, href: nil, included: [], meta_data: {}, **options)
@list = list @href = href
@included = included @options = options
@list = list
@included = included
@meta_data = meta_data
end
def fields
fields = { :"@type" => type }
fields[:@href] = href if href
fields[:pagination] = pagination_info if meta_data.include? :pagination
fields
end
def pagination_info
return meta_data[:@pagination] unless href
generator = V3::Paginator::URLGenerator.new(href, **meta_data[:pagination])
meta_data[:pagination].merge generator.to_h
end end
def render def render
result = { :"@type" => type } result = fields
result[:@href] = @href if @href included = self.included.dup
included = @included.dup result[collection_key] = list.map do |entry|
result[collection_key] = @list.map do |entry| rendered = render_entry(entry, included: included, mode: :standard, **options)
rendered = render_entry(entry, included: included, mode: :standard, **@options)
included << entry included << entry
rendered rendered
end end

View File

@ -1,9 +1,9 @@
module Travis::API::V3 module Travis::API::V3
class Result class Result
attr_accessor :access_control, :type, :resource, :status, :href attr_accessor :access_control, :type, :resource, :status, :href, :meta_data
def initialize(access_control, type, resource = [], status: 200) def initialize(access_control, type, resource = [], status: 200, **meta_data)
@access_control, @type, @resource, @status = access_control, type, resource, status @access_control, @type, @resource, @status, @meta_data = access_control, type, resource, status, meta_data
end end
def respond_to_missing?(method, *) def respond_to_missing?(method, *)
@ -19,7 +19,12 @@ module Travis::API::V3
href = self.href href = self.href
href = V3.location(env) if href.nil? and env['REQUEST_METHOD'.freeze] == 'GET'.freeze href = V3.location(env) if href.nil? and env['REQUEST_METHOD'.freeze] == 'GET'.freeze
include = params['include'.freeze].to_s.split(?,.freeze) include = params['include'.freeze].to_s.split(?,.freeze)
Renderer[type].render(resource, href: href, script_name: env['SCRIPT_NAME'.freeze], include: include, access_control: access_control) Renderer[type].render(resource,
href: href,
script_name: env['SCRIPT_NAME'.freeze],
include: include,
access_control: access_control,
meta_data: meta_data)
end end
def method_missing(method, *args) def method_missing(method, *args)

View File

@ -24,6 +24,19 @@ module Travis::API::V3
@params @params
end end
def self.paginate(**options)
params("limit".freeze, "offset".freeze)
@paginator = Paginator.new(**options)
end
def self.paginator
@paginator ||= nil
end
def self.paginate?
!!@paginator if defined? @paginator
end
attr_accessor :access_control, :params attr_accessor :access_control, :params
def initialize(access_control, params) def initialize(access_control, params)
@ -68,7 +81,15 @@ module Travis::API::V3
def run def run
not_found unless result = run! not_found unless result = run!
result = result(result_type, result) unless result.is_a? Result result = result(result_type, result) unless result.is_a? Result
result self.class.paginate? ? paginate(result) : result
end
def paginate(result)
p params
self.class.paginator.paginate(result,
limit: params['limit'.freeze],
offset: params['offset'.freeze],
access_control: access_control)
end end
def params_for?(prefix) def params_for?(prefix)

View File

@ -1,5 +1,7 @@
module Travis::API::V3 module Travis::API::V3
class Services::Builds::Find < Service class Services::Builds::Find < Service
paginate
def run! def run!
query.find(find(:repository)) query.find(find(:repository))
end end