Merge branch 'master' into cd-v3-parity
This commit is contained in:
commit
517b3b60ef
|
@ -37,4 +37,3 @@ module Travis
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -2,6 +2,10 @@ require 'travis/api/v3/access_control/generic'
|
|||
|
||||
module Travis::API::V3
|
||||
class AccessControl::Anonymous < AccessControl::Generic
|
||||
def self.new
|
||||
@instace ||= super
|
||||
end
|
||||
|
||||
# use when Authorization header is not set
|
||||
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
|
||||
class Queries::Builds < Query
|
||||
params :state, :event_type, :previous_state, prefix: :build
|
||||
params :name, prefix: :branch, method_name: :branch_name
|
||||
|
||||
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
|
||||
|
|
|
@ -4,24 +4,35 @@ module Travis::API::V3
|
|||
|
||||
# generate from eval to avoid additional string allocations on every params access
|
||||
@@params_accessor = <<-RUBY
|
||||
attr_writer :%<name>s
|
||||
attr_writer :%<method_name>s
|
||||
|
||||
def %<name>s
|
||||
return @%<name>s if defined? @%<name>s
|
||||
return @%<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 @%<name>s = @params['%<name>s'.freeze] if (@params['@type'.freeze] || @main_type) == '%<prefix>s'.freeze
|
||||
@%<name>s = nil
|
||||
def %<method_name>s
|
||||
return @%<method_name>s if defined? @%<method_name>s
|
||||
return @%<method_name>s = @params['%<prefix>s.%<name>s'.freeze] if @params.include? '%<prefix>s.%<name>s'.freeze
|
||||
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 @%<method_name>s = @params['%<name>s'.freeze] if (@params['@type'.freeze] || @main_type) == '%<prefix>s'.freeze
|
||||
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
|
||||
|
||||
def %<name>s!
|
||||
%<name>s or raise WrongParams, 'missing %<prefix>s.%<name>s'.freeze
|
||||
def %<method_name>s!
|
||||
%<method_name>s or raise WrongParams, 'missing %<prefix>s.%<name>s'.freeze
|
||||
end
|
||||
RUBY
|
||||
|
||||
def self.params(*list, prefix: nil)
|
||||
prefix ||= name[/[^:]+$/].underscore
|
||||
list.each { |e| class_eval(@@params_accessor % { name: e, prefix: prefix }) }
|
||||
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|
|
||||
class_eval(@@params_accessor % {
|
||||
name: entry,
|
||||
prefix: prefix,
|
||||
type: type,
|
||||
method_name: method_name || entry,
|
||||
check_type: check_type
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
attr_reader :params, :main_type
|
||||
|
@ -51,6 +62,10 @@ module Travis::API::V3
|
|||
!!value
|
||||
end
|
||||
|
||||
def list(value)
|
||||
value.split(?,.freeze)
|
||||
end
|
||||
|
||||
def user_condition(value)
|
||||
case value
|
||||
when String then { login: value }
|
||||
|
|
|
@ -17,19 +17,34 @@ module Travis::API::V3
|
|||
available_attributes << value
|
||||
end
|
||||
|
||||
def initialize(list, href: nil, included: [], **options)
|
||||
@href = href
|
||||
@options = options
|
||||
@list = list
|
||||
@included = included
|
||||
attr_reader :href, :options, :list, :included, :meta_data
|
||||
|
||||
def initialize(list, href: nil, included: [], meta_data: {}, **options)
|
||||
@href = href
|
||||
@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
|
||||
|
||||
def render
|
||||
result = { :"@type" => type }
|
||||
result[:@href] = @href if @href
|
||||
included = @included.dup
|
||||
result[collection_key] = @list.map do |entry|
|
||||
rendered = render_entry(entry, included: included, mode: :standard, **@options)
|
||||
result = fields
|
||||
included = self.included.dup
|
||||
result[collection_key] = list.map do |entry|
|
||||
rendered = render_entry(entry, included: included, mode: :standard, **options)
|
||||
included << entry
|
||||
rendered
|
||||
end
|
||||
|
|
|
@ -1,15 +1,25 @@
|
|||
module Travis::API::V3
|
||||
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)
|
||||
@access_control, @type, @resource, @status = access_control, type, resource, status
|
||||
def initialize(access_control, type, resource = [], status: 200, **meta_data)
|
||||
@warnings = []
|
||||
@access_control, @type, @resource, @status, @meta_data = access_control, type, resource, status, meta_data
|
||||
end
|
||||
|
||||
def respond_to_missing?(method, *)
|
||||
super or method.to_sym == type.to_sym
|
||||
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)
|
||||
resource << value
|
||||
self
|
||||
|
@ -19,7 +29,20 @@ module Travis::API::V3
|
|||
href = self.href
|
||||
href = V3.location(env) if href.nil? and env['REQUEST_METHOD'.freeze] == 'GET'.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
|
||||
|
||||
def method_missing(method, *args)
|
||||
|
|
|
@ -16,8 +16,11 @@ module Travis::API::V3
|
|||
|
||||
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
|
||||
|
||||
env_params.each_key { |key| result.ignored_param(key, reason: "not whitelisted".freeze) unless filtered.include?(key) }
|
||||
render(result, env_params, env)
|
||||
rescue Error => error
|
||||
result = Result.new(access_control, :error, error)
|
||||
|
|
|
@ -24,6 +24,19 @@ module Travis::API::V3
|
|||
@params
|
||||
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
|
||||
|
||||
def initialize(access_control, params)
|
||||
|
@ -68,7 +81,15 @@ module Travis::API::V3
|
|||
def run
|
||||
not_found unless result = run!
|
||||
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
|
||||
|
||||
def params_for?(prefix)
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
module Travis::API::V3
|
||||
class Services::Builds::Find < Service
|
||||
params :state, :event_type, :previous_state, prefix: :build
|
||||
params "branch.name"
|
||||
paginate
|
||||
|
||||
def run!
|
||||
query.find(find(:repository))
|
||||
end
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
require 'simplecov'
|
||||
unless ENV['SKIP_COVERAGE']
|
||||
require 'simplecov'
|
||||
|
||||
SimpleCov.start do
|
||||
coverage_dir '.coverage'
|
||||
add_filter "/spec/"
|
||||
add_group "v3", "lib/travis/api/v3"
|
||||
SimpleCov.start do
|
||||
coverage_dir '.coverage'
|
||||
add_filter "/spec/"
|
||||
add_group "v3", "lib/travis/api/v3"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -130,14 +130,19 @@ describe Travis::API::V3::Services::Owner::Find do
|
|||
before { get("/v3/owner/example-org?organization.id=#{other.id}") }
|
||||
example { expect(last_response).to be_ok }
|
||||
example { expect(JSON.load(body)).to be == {
|
||||
"@type" => "organization",
|
||||
"@href" => "/v3/org/#{org.id}",
|
||||
"@permissions" => { "read"=>true, "sync"=>false },
|
||||
"id" => org.id,
|
||||
"login" => "example-org",
|
||||
"name" => nil,
|
||||
"github_id" => nil,
|
||||
"avatar_url" => nil
|
||||
"@type" => "organization",
|
||||
"@href" => "/v3/org/#{org.id}",
|
||||
"@permissions" => { "read"=>true, "sync"=>false },
|
||||
"id" => org.id,
|
||||
"login" => "example-org",
|
||||
"name" => nil,
|
||||
"github_id" => nil,
|
||||
"avatar_url" => nil,
|
||||
"@warnings" => [{
|
||||
"@type" => "warning",
|
||||
"message" => "query parameter organization.id not whitelisted, ignored",
|
||||
"warning_type" => "ignored_parameter",
|
||||
"parameter" => "organization.id"}]
|
||||
}}
|
||||
end
|
||||
end
|
||||
|
@ -198,7 +203,12 @@ describe Travis::API::V3::Services::Owner::Find do
|
|||
"github_id" => nil,
|
||||
"avatar_url" => 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
|
||||
|
|
Loading…
Reference in New Issue
Block a user