Merge branch 'master' into cd-v3-parity
This commit is contained in:
commit
517b3b60ef
|
@ -37,4 +37,3 @@ module Travis
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
42
lib/travis/api/v3/paginator.rb
Normal file
42
lib/travis/api/v3/paginator.rb
Normal 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
|
95
lib/travis/api/v3/paginator/url_generator.rb
Normal file
95
lib/travis/api/v3/paginator/url_generator.rb
Normal 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
|
|
@ -1,7 +1,21 @@
|
||||||
module Travis::API::V3
|
module Travis::API::V3
|
||||||
class Queries::Builds < Query
|
class Queries::Builds < Query
|
||||||
|
params :state, :event_type, :previous_state, prefix: :build
|
||||||
|
params :name, prefix: :branch, method_name: :branch_name
|
||||||
|
|
||||||
def find(repository)
|
def find(repository)
|
||||||
repository.builds
|
filter(repository.builds)
|
||||||
|
end
|
||||||
|
|
||||||
|
def filter(list)
|
||||||
|
list = list.where(state: list(state)) if state
|
||||||
|
list = list.where(previous_state: list(previous_state)) if previous_state
|
||||||
|
list = list.where(event_type: list(event_type)) if event_type
|
||||||
|
list = list.where(branch: list(branch_name)) if branch_name
|
||||||
|
|
||||||
|
list = list.includes(:commit).includes(branch: :last_build).includes(:repository)
|
||||||
|
list = list.includes(branch: { last_build: :commit }) if includes? 'build.commit'.freeze
|
||||||
|
list
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,24 +4,35 @@ module Travis::API::V3
|
||||||
|
|
||||||
# generate from eval to avoid additional string allocations on every params access
|
# generate from eval to avoid additional string allocations on every params access
|
||||||
@@params_accessor = <<-RUBY
|
@@params_accessor = <<-RUBY
|
||||||
attr_writer :%<name>s
|
attr_writer :%<method_name>s
|
||||||
|
|
||||||
def %<name>s
|
def %<method_name>s
|
||||||
return @%<name>s if defined? @%<name>s
|
return @%<method_name>s if defined? @%<method_name>s
|
||||||
return @%<name>s = @params['%<prefix>s.%<name>s'.freeze] if @params.include? '%<prefix>s.%<name>s'.freeze
|
return @%<method_name>s = @params['%<prefix>s.%<name>s'.freeze] if @params.include? '%<prefix>s.%<name>s'.freeze
|
||||||
return @%<name>s = @params['%<prefix>s'.freeze]['%<name>s'.freeze] if @params.include? '%<prefix>s'.freeze and @params['%<prefix>s'.freeze].is_a? Hash
|
return @%<method_name>s = @params['%<prefix>s'.freeze]['%<name>s'.freeze] if @params.include? '%<prefix>s'.freeze and @params['%<prefix>s'.freeze].is_a? Hash
|
||||||
return @%<name>s = @params['%<name>s'.freeze] if (@params['@type'.freeze] || @main_type) == '%<prefix>s'.freeze
|
return @%<method_name>s = @params['%<name>s'.freeze] if (@params['@type'.freeze] || @main_type) == '%<prefix>s'.freeze
|
||||||
@%<name>s = nil
|
return @%<method_name>s = @params['%<name>s'.freeze] if %<check_type>p and (@params['@type'.freeze] || @main_type) == '%<type>s'.freeze
|
||||||
|
@%<method_name>s = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def %<name>s!
|
def %<method_name>s!
|
||||||
%<name>s or raise WrongParams, 'missing %<prefix>s.%<name>s'.freeze
|
%<method_name>s or raise WrongParams, 'missing %<prefix>s.%<name>s'.freeze
|
||||||
end
|
end
|
||||||
RUBY
|
RUBY
|
||||||
|
|
||||||
def self.params(*list, prefix: nil)
|
def self.params(*list, prefix: nil, method_name: nil)
|
||||||
prefix ||= name[/[^:]+$/].underscore
|
type = name[/[^:]+$/].underscore
|
||||||
list.each { |e| class_eval(@@params_accessor % { name: e, prefix: prefix }) }
|
prefix ||= type.to_s
|
||||||
|
check_type = method_name.nil? and type != prefix
|
||||||
|
list.each do |entry|
|
||||||
|
class_eval(@@params_accessor % {
|
||||||
|
name: entry,
|
||||||
|
prefix: prefix,
|
||||||
|
type: type,
|
||||||
|
method_name: method_name || entry,
|
||||||
|
check_type: check_type
|
||||||
|
})
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
attr_reader :params, :main_type
|
attr_reader :params, :main_type
|
||||||
|
@ -51,6 +62,10 @@ module Travis::API::V3
|
||||||
!!value
|
!!value
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def list(value)
|
||||||
|
value.split(?,.freeze)
|
||||||
|
end
|
||||||
|
|
||||||
def user_condition(value)
|
def user_condition(value)
|
||||||
case value
|
case value
|
||||||
when String then { login: value }
|
when String then { login: value }
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
def initialize(list, href: nil, included: [], meta_data: {}, **options)
|
||||||
@href = href
|
@href = href
|
||||||
@options = options
|
@options = options
|
||||||
@list = list
|
@list = list
|
||||||
@included = included
|
@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
|
||||||
|
|
|
@ -1,15 +1,25 @@
|
||||||
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, :warnings
|
||||||
|
|
||||||
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
|
@warnings = []
|
||||||
|
@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, *)
|
||||||
super or method.to_sym == type.to_sym
|
super or method.to_sym == type.to_sym
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def warn(message, **info)
|
||||||
|
warnings << { :@type => 'warning'.freeze, :message => message, **info }
|
||||||
|
end
|
||||||
|
|
||||||
|
def ignored_param(param, reason: nil, **info)
|
||||||
|
message = reason ? "query parameter #{param} #{reason}, ignored" : "query parameter #{param} ignored"
|
||||||
|
warn(message, warning_type: :ignored_parameter, parameter: param, **info)
|
||||||
|
end
|
||||||
|
|
||||||
def <<(value)
|
def <<(value)
|
||||||
resource << value
|
resource << value
|
||||||
self
|
self
|
||||||
|
@ -19,7 +29,20 @@ 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)
|
add_info Renderer[type].render(resource,
|
||||||
|
href: href,
|
||||||
|
script_name: env['SCRIPT_NAME'.freeze],
|
||||||
|
include: include,
|
||||||
|
access_control: access_control,
|
||||||
|
meta_data: meta_data)
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_info(payload)
|
||||||
|
if warnings.any?
|
||||||
|
payload = { :@warnings => [] }.merge!(payload) unless payload.include? :@warnings
|
||||||
|
payload[:@warnings].concat(warnings)
|
||||||
|
end
|
||||||
|
payload
|
||||||
end
|
end
|
||||||
|
|
||||||
def method_missing(method, *args)
|
def method_missing(method, *args)
|
||||||
|
|
|
@ -16,8 +16,11 @@ module Travis::API::V3
|
||||||
|
|
||||||
raise NotFound unless factory
|
raise NotFound unless factory
|
||||||
|
|
||||||
service = factory.new(access_control, factory.filter_params(env_params).merge(params))
|
filtered = factory.filter_params(env_params)
|
||||||
|
service = factory.new(access_control, filtered.merge(params))
|
||||||
result = service.run
|
result = service.run
|
||||||
|
|
||||||
|
env_params.each_key { |key| result.ignored_param(key, reason: "not whitelisted".freeze) unless filtered.include?(key) }
|
||||||
render(result, env_params, env)
|
render(result, env_params, env)
|
||||||
rescue Error => error
|
rescue Error => error
|
||||||
result = Result.new(access_control, :error, error)
|
result = Result.new(access_control, :error, error)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
module Travis::API::V3
|
module Travis::API::V3
|
||||||
class Services::Builds::Find < Service
|
class Services::Builds::Find < Service
|
||||||
|
params :state, :event_type, :previous_state, prefix: :build
|
||||||
|
params "branch.name"
|
||||||
|
paginate
|
||||||
|
|
||||||
def run!
|
def run!
|
||||||
query.find(find(:repository))
|
query.find(find(:repository))
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
unless ENV['SKIP_COVERAGE']
|
||||||
require 'simplecov'
|
require 'simplecov'
|
||||||
|
|
||||||
SimpleCov.start do
|
SimpleCov.start do
|
||||||
|
@ -5,3 +6,4 @@ SimpleCov.start do
|
||||||
add_filter "/spec/"
|
add_filter "/spec/"
|
||||||
add_group "v3", "lib/travis/api/v3"
|
add_group "v3", "lib/travis/api/v3"
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
|
@ -137,7 +137,12 @@ describe Travis::API::V3::Services::Owner::Find do
|
||||||
"login" => "example-org",
|
"login" => "example-org",
|
||||||
"name" => nil,
|
"name" => nil,
|
||||||
"github_id" => nil,
|
"github_id" => nil,
|
||||||
"avatar_url" => nil
|
"avatar_url" => nil,
|
||||||
|
"@warnings" => [{
|
||||||
|
"@type" => "warning",
|
||||||
|
"message" => "query parameter organization.id not whitelisted, ignored",
|
||||||
|
"warning_type" => "ignored_parameter",
|
||||||
|
"parameter" => "organization.id"}]
|
||||||
}}
|
}}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -198,7 +203,12 @@ describe Travis::API::V3::Services::Owner::Find do
|
||||||
"github_id" => nil,
|
"github_id" => nil,
|
||||||
"avatar_url" => nil,
|
"avatar_url" => nil,
|
||||||
"is_syncing" => nil,
|
"is_syncing" => nil,
|
||||||
"synced_at" => nil
|
"synced_at" => nil,
|
||||||
|
"@warnings" => [{
|
||||||
|
"@type" => "warning",
|
||||||
|
"message" => "query parameter user.id not whitelisted, ignored",
|
||||||
|
"warning_type" => "ignored_parameter",
|
||||||
|
"parameter" => "user.id"}]
|
||||||
}}
|
}}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue
Block a user