From 33d31131c1e13382ec12f036b16d24af540badba Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Tue, 6 Oct 2015 17:35:56 +0200 Subject: [PATCH] v3: add ability to sort branches --- lib/travis/api/v3/queries/branches.rb | 5 +- lib/travis/api/v3/queries/owner.rb | 2 +- lib/travis/api/v3/query.rb | 82 ++++++++++++++++++++- lib/travis/api/v3/service.rb | 23 +++++- lib/travis/api/v3/services/branches/find.rb | 1 + spec/v3/services/branches/find_spec.rb | 56 ++++++++++++++ 6 files changed, 163 insertions(+), 6 deletions(-) diff --git a/lib/travis/api/v3/queries/branches.rb b/lib/travis/api/v3/queries/branches.rb index 0491e0b2..c368fa7d 100644 --- a/lib/travis/api/v3/queries/branches.rb +++ b/lib/travis/api/v3/queries/branches.rb @@ -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 diff --git a/lib/travis/api/v3/queries/owner.rb b/lib/travis/api/v3/queries/owner.rb index 229833f2..c57ee7c4 100644 --- a/lib/travis/api/v3/queries/owner.rb +++ b/lib/travis/api/v3/queries/owner.rb @@ -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) diff --git a/lib/travis/api/v3/query.rb b/lib/travis/api/v3/query.rb index 8cdcb521..488018bf 100644 --- a/lib/travis/api/v3/query.rb +++ b/lib/travis/api/v3/query.rb @@ -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 } diff --git a/lib/travis/api/v3/service.rb b/lib/travis/api/v3/service.rb index 91c4aea1..f627b74c 100644 --- a/lib/travis/api/v3/service.rb +++ b/lib/travis/api/v3/service.rb @@ -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) diff --git a/lib/travis/api/v3/services/branches/find.rb b/lib/travis/api/v3/services/branches/find.rb index 00779a0f..cd618c70 100644 --- a/lib/travis/api/v3/services/branches/find.rb +++ b/lib/travis/api/v3/services/branches/find.rb @@ -1,6 +1,7 @@ module Travis::API::V3 class Services::Branches::Find < Service paginate + def run! query.find(find(:repository)) end diff --git a/spec/v3/services/branches/find_spec.rb b/spec/v3/services/branches/find_spec.rb index 8b9304f4..e43b13e1 100644 --- a/spec/v3/services/branches/find_spec.rb +++ b/spec/v3/services/branches/find_spec.rb @@ -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