v3: add ability to sort branches

This commit is contained in:
Konstantin Haase 2015-10-06 17:35:56 +02:00
parent 0249140dc8
commit 33d31131c1
6 changed files with 163 additions and 6 deletions

View File

@ -1,7 +1,10 @@
module Travis::API::V3
class Queries::Branches < Query
sortable_by :name, last_build: "builds.started_at".freeze
default_sort "last_build:desc"
def find(repository)
repository.branches
sort repository.branches
end
end
end

View File

@ -9,7 +9,7 @@ module Travis::API::V3
def query(type, main_type: self.main_type, params: self.params)
main_type = type if main_type == :owner
params = params.merge("#{type}.login" => params["owner.login".freeze]) if params["owner.login".freeze]
Queries[type].new(params, main_type)
Queries[type].new(params, main_type, service: @service)
end
def find!(type)

View File

@ -20,8 +20,11 @@ module Travis::API::V3
end
RUBY
def self.type
name[/[^:]+$/].underscore
end
def self.params(*list, prefix: nil, method_name: nil)
type = name[/[^:]+$/].underscore
prefix ||= type.to_s
check_type = method_name.nil? and type != prefix
list.each do |entry|
@ -35,12 +38,48 @@ module Travis::API::V3
end
end
def self.prefix(input)
return input if input.is_a? String
"#{type}.#{input}"
end
def self.sortable_by(*params, **mapping)
params.each { |param| sort_by[param.to_s] = prefix(param) }
mapping.each { |key, value| sort_by[key.to_s] = prefix(value) }
end
def self.sortable?
!sort_by.empty?
end
@sort_by = {}
def self.sort_by
@sort_by ||= superclass.sort_by.dup
end
@default_sort = ""
def self.default_sort(value = nil)
@default_sort = value.to_s if value
@default_sort ||= superclass.default_sort
end
attr_reader :params, :main_type
def initialize(params, main_type, includes: nil)
def initialize(params, main_type, includes: nil, service: nil)
@params = params
@main_type = main_type.to_s
@includes = includes
@service = service
end
def warn(*args)
return unless @service
@service.warn(*args)
end
def ignored_value(param, value, reason: nil, **info)
message = reason ? "query value #{value} for #{param} #{reason}, ignored" : "query value #{value} for #{param} ignored"
warn(message, warning_type: :ignored_value, parameter: param, value: value, **info)
end
def perform_async(identifier, *args)
@ -72,6 +111,45 @@ module Travis::API::V3
value.split(?,.freeze)
end
def sort(collection)
return collection unless sort_by = params["sort_by".freeze] || self.class.default_sort and not sort_by.empty?
first = true
list(sort_by).each do |field_with_order|
field, order = field_with_order.split(?:.freeze, 2)
order ||= "asc".freeze
if sort_by? field, order
collection = sort_by(collection, field, order: order, first: first)
first = false
else
ignored_value("sort_by".freeze, field_with_order, reason: "not a valid sort mode".freeze)
end
end
collection
end
def sort_by?(field, order)
return false unless order == "asc".freeze or order == "desc".freeze
self.class.sort_by.include?(field)
end
def sort_by(collection, field, order: nil, first: false)
raise ArgumentError, 'cannot sort by that' unless sort_by?(field, order)
actual = self.class.sort_by.fetch(field)
line = "#{actual} #{order.upcase}"
if sort_join?(collection, actual)
collection = collection.joins(actual.to_sym)
elsif actual != field and sort_join?(collection, field)
collection = collection.joins(field.to_sym)
end
first ? collection.reorder(line) : collection.order(line)
end
def sort_join?(collection, field)
!collection.reflect_on_association(field.to_sym).nil?
end
def user_condition(value)
case value
when String then { login: value }

View File

@ -26,6 +26,7 @@ module Travis::API::V3
def self.paginate(**options)
params("limit".freeze, "offset".freeze)
params("sort_by".freeze) if query_factory.sortable?
@paginator = Paginator.new(**options)
end
@ -37,6 +38,10 @@ module Travis::API::V3
!!@paginator if defined? @paginator
end
def self.query_factory
Queries[result_type]
end
attr_accessor :access_control, :params
def initialize(access_control, params)
@ -47,7 +52,7 @@ module Travis::API::V3
end
def query(type = result_type)
@queries[type] ||= Queries[type].new(params, result_type)
@queries[type] ||= Queries[type].new(params, result_type, service: self)
end
def github(user = nil)
@ -81,7 +86,21 @@ module Travis::API::V3
def run
not_found unless result = run!
result = result(result_type, result) unless result.is_a? Result
self.class.paginate? ? paginate(result) : result
result = paginate(result) if self.class.paginate?
apply_warnings(result)
result
end
def warnings
@warnings ||= []
end
def warn(*args)
warnings << args
end
def apply_warnings(result)
warnings.each { |args| result.warn(*args) }
end
def paginate(result)

View File

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

View File

@ -171,4 +171,60 @@ describe Travis::API::V3::Services::Branches::Find do
"exists_on_github" => true }]}
}
end
describe "sorting by name" do
before { get("/v3/repo/#{repo.id}/branches?sort_by=name&limit=1") }
example { expect(last_response).to be_ok }
example { expect(parsed_body["@pagination"]).to be == {
"limit" => 1,
"offset" => 0,
"count" => 1,
"is_first" => true,
"is_last" => true,
"next" => nil,
"prev" => nil,
"first" => {
"@href" => "/v3/repo/#{repo.id}/branches?sort_by=name&limit=1",
"offset" => 0,
"limit" => 1 },
"last" => {
"@href" => "/v3/repo/#{repo.id}/branches?sort_by=name&limit=1",
"offset" => 0,
"limit" => 1 }}
}
end
describe "sorting by name:desc" do
before { get("/v3/repo/#{repo.id}/branches?sort_by=name%3Adesc&limit=1") }
example { expect(last_response).to be_ok }
example { expect(parsed_body["@pagination"]).to be == {
"limit" => 1,
"offset" => 0,
"count" => 1,
"is_first" => true,
"is_last" => true,
"next" => nil,
"prev" => nil,
"first" => {
"@href" => "/v3/repo/#{repo.id}/branches?sort_by=name%3Adesc&limit=1",
"offset" => 0,
"limit" => 1 },
"last" => {
"@href" => "/v3/repo/#{repo.id}/branches?sort_by=name%3Adesc&limit=1",
"offset" => 0,
"limit" => 1 }}
}
end
describe "sorting by unknown sort field" do
before { get("/v3/repo/#{repo.id}/branches?sort_by=name:desc,foo&limit=1") }
example { expect(last_response).to be_ok }
example { expect(parsed_body["@warnings"]).to be == [{
"@type" => "warning",
"message" => "query value foo for sort_by not a valid sort mode, ignored",
"warning_type"=> "ignored_value",
"parameter" => "sort_by",
"value" => "foo"
}]}
end
end