Merge branch 'master' into rkh-v3-enable-disable

This commit is contained in:
Konstantin Haase 2015-03-05 14:27:17 +01:00
commit 9ea21e6352
45 changed files with 410 additions and 63 deletions

View File

@ -50,7 +50,7 @@ GIT
GIT
remote: git://github.com/travis-ci/travis-core.git
revision: a82f25fb00a39c3c64b8c09c716c206e6f4c6fad
revision: d88d5f84eaea2996c4d325f8f4906d2fdd844125
specs:
travis-core (0.0.1)
actionmailer (~> 3.2.19)
@ -61,7 +61,7 @@ GIT
hashr (~> 0.0.19)
metriks (~> 0.9.7)
multi_json
pusher (~> 0.12.0)
pusher (~> 0.14.0)
railties (~> 3.2.19)
rake
redis (~> 3.0)
@ -95,6 +95,7 @@ PATH
remote: .
specs:
travis-api (0.0.1)
composite_primary_keys (~> 5.0)
memcachier
mustermann (~> 0.4)
pg (~> 0.13.2)
@ -154,6 +155,8 @@ GEM
coderay (1.1.0)
coercible (1.0.0)
descendants_tracker (~> 0.0.1)
composite_primary_keys (5.0.14)
activerecord (~> 3.2.0, >= 3.2.9)
connection_pool (2.1.1)
daemons (1.1.9)
dalli (2.7.2)
@ -168,7 +171,7 @@ GEM
dotenv (0.7.0)
equalizer (0.0.9)
erubis (2.7.0)
eventmachine (1.0.4)
eventmachine (1.0.7)
factory_girl (2.4.2)
activesupport
faraday (0.9.1)
@ -187,9 +190,9 @@ GEM
hashr (0.0.22)
hike (1.2.3)
hitimes (1.2.2)
httpclient (2.3.4.1)
httpclient (2.6.0.1)
i18n (0.6.11)
ice_nine (0.11.0)
ice_nine (0.11.1)
journey (1.0.4)
json (1.8.1)
kgio (2.9.2)
@ -224,10 +227,10 @@ GEM
coderay (~> 1.1.0)
method_source (~> 0.8.1)
slop (~> 3.4)
pusher (0.12.0)
httpclient (~> 2.3.0)
pusher (0.14.4)
httpclient (~> 2.5)
multi_json (~> 1.0)
signature (~> 0.1.6)
signature (~> 0.1.8)
rack (1.4.5)
rack-attack (4.2.0)
rack
@ -274,7 +277,7 @@ GEM
json
redis (>= 3.0.6)
redis-namespace (>= 1.3.1)
signature (0.1.7)
signature (0.1.8)
simple_states (1.0.1)
activesupport
hashr (~> 0.0.10)
@ -324,7 +327,7 @@ GEM
raindrops (~> 0.7)
useragent (0.10.0)
uuidtools (2.1.5)
virtus (1.0.3)
virtus (1.0.4)
axiom-types (~> 0.1)
coercible (~> 1.0)
descendants_tracker (~> 0.0, >= 0.0.3)

View File

@ -98,8 +98,9 @@ class Travis::Api::App
respond_with service(:find_branches, params), type: :branches, version: :v2
end
# Gets lastest build on a branch branches
get '/:repository_id/branches/:branch' do
# Gets latest build on a branch
get '/:repository_id/branches/*' do
params[:branch] = params[:splat]
respond_with service(:find_branch, params), type: :branch, version: :v2
end
@ -170,8 +171,9 @@ class Travis::Api::App
respond_with service(:find_branches, params), type: :branches, version: :v2
end
# Gets lastest build on a branch branches
get '/:owner_name/:name/branches/:branch' do
# Gets latest build on a branch
get '/:owner_name/:name/branches/*' do
params[:branch] = params[:splat]
respond_with service(:find_branch, params), type: :branch, version: :v2
end

View File

@ -21,6 +21,7 @@ module Travis
end
extend self
load_dir("#{__dir__}/v3/extensions")
load_dir("#{__dir__}/v3")
ClientError = Error .create(status: 400)

View File

@ -24,6 +24,10 @@ module Travis::API::V3
visible? build.repository
end
def branch_visible?(branch)
visible? branch.repository
end
def organization_visible?(organization)
unrestricted_api?
end
@ -52,8 +56,17 @@ module Travis::API::V3
private
def dispatch(object, method = caller_locations.first.base_label)
method = object.class.name.underscore + ?_.freeze + method
method = method_for(object.class, method)
send(method, object) if respond_to?(method, true)
end
@@method_for_cache = Tool::ThreadLocal.new
def method_for(type, method)
@@method_for_cache[[type, method]] ||= begin
prefix = type.name.sub(/^Travis::API::V3::Models::/, ''.freeze).underscore
"#{prefix}_#{method}"
end
end
end
end

View File

@ -5,6 +5,7 @@ module Travis::API::V3
attr_reader :user, :permissions
def initialize(user)
user = Models::User.find(user.id) if user.is_a? ::User
@user = user
@permissions = user.permissions.where(user_id: user.id)
super()

View File

@ -0,0 +1,49 @@
module Travis::API::V3
module Extensions
# This is a patch to ActiveRecord to allow classes for polymorphic relations to be nested in a module without the
# module name being part of the type field.
#
# Example:
#
# # Without this patch
# Repository.find(2).owner.class # => User
# Travis::API::V3::Models::Repository.find(2).owner.class # => User
#
# # With this patch
# Repository.find(2).owner.class # => User
# Travis::API::V3::Models::Repository.find(2).owner.class # => Travis::API::V3::Models::User
#
# ActiveRecord does not support this out of the box. We accomplish this feature by tracking polymorphic relations
# and then adding the namespace when calling ActiveRecord::Base#[] with the foreign type key and removing it again
# in ActiveRecord::Base#[]=, so we don't break other code by accidentially writing the prefixed version to the
# database.
module BelongsTo
module ClassMethods
def polymorfic_foreign_types
@polymorfic_foreign_types ||= []
end
def belongs_to(field, options = {})
polymorfic_foreign_types << (options[:foreign_type] || "#{field}_type") if options[:polymorphic]
super
end
end
def self.included(base)
base.extend(ClassMethods)
super
end
def [](key)
value = super
value &&= "#{self.class.parent}::#{value}" if self.class.polymorfic_foreign_types.include?(key)
value
end
def []=(key, value)
value &&= value.sub("#{self.class.parent}::", ''.freeze) if self.class.polymorfic_foreign_types.include?(key)
super(key, value)
end
end
end
end

View File

@ -0,0 +1,6 @@
module Travis::API::V3
class Model < ActiveRecord::Base
include Extensions::BelongsTo
self.abstract_class = true
end
end

View File

@ -0,0 +1,7 @@
require 'composite_primary_keys'
module Travis::API::V3
module Models
extend ConstantResolver
end
end

View File

@ -0,0 +1,8 @@
module Travis::API::V3
class Models::Branch < Model
belongs_to :repository
belongs_to :last_build, class_name: 'Travis::API::V3::Models::Build'.freeze
has_many :builds, foreign_key: [:repository_id, :branch], primary_key: [:repository_id, :name], order: 'id DESC'.freeze, conditions: { event_type: 'push' }
has_many :commits, foreign_key: [:repository_id, :branch], primary_key: [:repository_id, :name], order: 'id DESC'.freeze
end
end

View File

@ -0,0 +1,5 @@
module Travis::API::V3
class Models::Broadcast < Model
belongs_to :recipient, polymorphic: true
end
end

View File

@ -0,0 +1,10 @@
module Travis::API::V3
class Models::Build < Model
belongs_to :repository
belongs_to :commit
belongs_to :request
belongs_to :repository, autosave: true
belongs_to :owner, polymorphic: true
has_many :jobs, as: :source, order: :id, dependent: :destroy
end
end

View File

@ -0,0 +1,6 @@
module Travis::API::V3
class Models::Commit < Model
belongs_to :repository
has_one :request
end
end

View File

@ -0,0 +1,5 @@
module Travis::API::V3
class Models::Email < Model
belongs_to :user
end
end

View File

@ -0,0 +1,10 @@
module Travis::API::V3
class Models::Job < Model
has_one :log, dependent: :destroy
belongs_to :repository
belongs_to :commit
belongs_to :build, autosave: true, foreign_key: 'source_id'
belongs_to :owner, polymorphic: true
serialize :config
end
end

View File

@ -0,0 +1,7 @@
module Travis::API::V3
class Models::Log < Model
belongs_to :job
belongs_to :removed_by, class_name: 'User', foreign_key: :removed_by
has_many :log_parts, dependent: :destroy
end
end

View File

@ -0,0 +1,5 @@
module Travis::API::V3
class Models::LogPart < Model
belongs_to :log
end
end

View File

@ -0,0 +1,6 @@
module Travis::API::V3
class Models::Membership < Model
belongs_to :user
belongs_to :organization
end
end

View File

@ -0,0 +1,7 @@
module Travis::API::V3
class Models::Organization < Model
has_many :memberships
has_many :users, through: :memberships
has_many :repositories, as: :owner
end
end

View File

@ -0,0 +1,6 @@
module Travis::API::V3
class Models::Permission < Model
belongs_to :user
belongs_to :repository
end
end

View File

@ -0,0 +1,56 @@
module Travis::API::V3
class Models::Repository < Model
has_many :commits, dependent: :delete_all
has_many :requests, dependent: :delete_all
has_many :branches, dependent: :delete_all, order: 'id DESC'.freeze
has_many :builds, dependent: :delete_all, order: 'id DESC'.freeze
has_many :permissions, dependent: :delete_all
has_many :users, through: :permissions
belongs_to :owner, polymorphic: true
has_one :last_build,
class_name: 'Travis::API::V3::Models::Build'.freeze,
order: 'id DESC'.freeze
has_one :default_branch,
foreign_key: [:repository_id, :name],
primary_key: [:id, :default_branch],
class_name: 'Travis::API::V3::Models::Branch'.freeze
after_initialize do
update_attributes! default_branch_name: 'master'.freeze unless default_branch_name
end
def slug
@slug ||= "#{owner_name}/#{name}"
end
def default_branch_name
read_attribute(:default_branch)
end
def default_branch_name=(value)
write_attribute(:default_branch, value)
end
def default_branch
super || branch(default_branch_name, create_without_build: true)
end
# Creates a branch object on the fly if it doesn't exist.
#
# Will not create a branch object if we don't have any builds for it unless
# the create_without_build option is set to true.
def branch(name, create_without_build: false)
return nil unless branch = branches.where(name: name).first_or_initialize
return branch unless branch.new_record?
return nil unless create_without_build or branch.builds.any?
branch.last_build = branch.builds.first
branch.save!
branch
rescue ActiveRecord::RecordNotUnique
branches.where(name: name).first
end
end
end

View File

@ -0,0 +1,10 @@
module Travis::API::V3
class Models::Request < Model
belongs_to :commit
belongs_to :repository
belongs_to :owner, polymorphic: true
has_many :builds
serialize :config
serialize :payload
end
end

View File

@ -0,0 +1,5 @@
module Travis::API::V3
class Models::SSLKey < Model
belongs_to :repository
end
end

View File

@ -0,0 +1,9 @@
module Travis::API::V3
class Models::User < Model
has_many :memberships, dependent: :destroy
has_many :organizations, through: :memberships
has_many :permissions, dependent: :destroy
has_many :repositories, through: :permissions
has_many :emails, dependent: :destroy
end
end

View File

@ -0,0 +1,10 @@
module Travis::API::V3
class Queries::Branch < Query
params :name
def find(repository)
return repository.branch(name) if name
raise WrongParams, 'missing branch.name'.freeze
end
end
end

View File

@ -3,8 +3,8 @@ module Travis::API::V3
params :id
def find
return ::Build.find_by_id(id) if id
raise WrongParams
return Models::Build.find_by_id(id) if id
raise WrongParams, 'missing build.id'.freeze
end
end
end

View File

@ -3,8 +3,8 @@ module Travis::API::V3
params :id
def find
return ::Organization.find_by_id(id) if id
raise WrongParams
return Models::Organization.find_by_id(id) if id
raise WrongParams, 'missing organization.id'.freeze
end
end
end

View File

@ -1,7 +1,7 @@
module Travis::API::V3
class Queries::Organizations < Query
def for_member(user)
::Organization.joins(:users).where(users: user_condition(user))
Models::Organization.joins(:users).where(users: user_condition(user))
end
end
end

View File

@ -8,9 +8,10 @@ module Travis::API::V3
def all
@all ||= begin
all = ::Repository
all = Models::Repository
all = all.where(active: bool(active)) unless active.nil?
all = all.where(private: bool(private)) unless private.nil?
all = all.includes(:default_branch) # TODO: use includes params
all
end
end

View File

@ -3,8 +3,8 @@ module Travis::API::V3
params :id
def find
return ::Repository.find_by_id(id) if id
raise WrongParams
return Models::Repository.find_by_id(id) if id
raise WrongParams, 'missing repository.id'.freeze
end
end
end

View File

@ -46,9 +46,9 @@ module Travis::API::V3
def user_condition(value)
case value
when String then { login: value }
when Integer then { id: value }
when ::User then { id: value.id }
when String then { login: value }
when Integer then { id: value }
when Models::User then { id: value.id }
else raise WrongParams
end
end

View File

@ -10,13 +10,22 @@ module Travis::API::V3
args.select { |key, value| !value.nil? }
end
def href(type, script_name: nil, **args)
expander = EXPANDER_CACHE[[type, script_name, args.keys]] ||= begin
resource = Routes.resources.detect { |r| r.identifier == type }
unprefixed = args.keys.reject { |a| a.to_s.include? ?..freeze }
route = resource.route
route &&= Mustermann.new(script_name, type: :identity) + route if script_name and not script_name.empty?
generate_expander(route, type, unprefixed)
def href(type, string_args = nil, script_name: nil, **args)
args.merge! string_args if string_args
expander = EXPANDER_CACHE[[type, script_name, args.keys]] ||= begin
resource = Routes.resources.detect { |r| r.identifier == type }
route = resource.route
route &&= Mustermann.new(script_name, type: :identity) + route if script_name and not script_name.empty?
key_mapping = {}
args.keys.each do |key|
case key.to_s
when /\./ then key_mapping[key.to_sym] = key unless key.is_a? Symbol
when /^(.+)_id$/ then key_mapping[:"#{$1}.id"] = key
else key_mapping[:"#{type}.#{key}"] = key
end
end
generate_expander(route, key_mapping)
end
expander.call(args)
@ -24,11 +33,11 @@ module Travis::API::V3
private
def generate_expander(route, prefix, unprefixed)
def generate_expander(route, key_mapping)
return proc { |**| } unless route.respond_to? :expand
proc do |**args|
unprefixed.each { |key| args[:"#{prefix}.#{key}"] = args.delete(key) }
route.expand(**args)
proc do |args|
key_mapping.each { |a, b| args[a] = args.delete(b) }
route.expand(:ignore, **args)
end
end
end

View File

@ -0,0 +1,8 @@
require 'travis/api/v3/renderer/model_renderer'
module Travis::API::V3
class Renderer::Branch < Renderer::ModelRenderer
representation(:minimal, :name, :last_build)
representation(:standard, :name, :repository, :last_build)
end
end

View File

@ -33,8 +33,8 @@ module Travis::API::V3
def href
return @href if defined? @href # allows setting href to nil
return unless self.class.type and model.respond_to? :id and model.id
@href = Renderer.href(self.class.type, script_name: script_name, id: model.id)
return unless self.class.type and model.respond_to? :attributes
@href = Renderer.href(self.class.type, model.attributes, script_name: script_name)
end
def render(representation)
@ -47,17 +47,17 @@ module Travis::API::V3
result
end
def render_model(model, type: model.class.name.to_sym, mode: :minimal, **options)
def render_model(model, type: model.class.name[/[^:]+$/].to_sym, mode: :minimal, **options)
Renderer[type].render(model, mode, script_name: script_name, **options)
end
def render_value(value)
case value
when Hash then value.map { |k, v| [k, render_value(v)] }.to_h
when Array then value.map { |v | render_value(v) }
when *PRIMITIVE then value
when Time then value.strftime('%Y-%m-%dT%H:%M:%SZ')
when Travis::Model then render_model(value)
when Hash then value.map { |k, v| [k, render_value(v)] }.to_h
when Array then value.map { |v | render_value(v) }
when *PRIMITIVE then value
when Time then value.strftime('%Y-%m-%dT%H:%M:%SZ')
when Model then render_model(value)
else raise ArgumentError, 'cannot render %p (%p)' % [value.class, value]
end
end

View File

@ -5,15 +5,6 @@ module Travis::API::V3
representation(:minimal, :id, :slug)
representation(:standard, :id, :name, :slug, :description, :github_language, :active, :private, :owner, :last_build, :default_branch)
def default_branch
branch_name = model.default_branch || 'master'.freeze
{
:@type => 'branch'.freeze,
:name => branch_name,
:last_build => model.last_build_on(branch_name)
}
end
def active
!!model.active
end

View File

@ -15,6 +15,11 @@ module Travis::API::V3
get :find
post :create
end
resource :branch do
route '/branch/{branch.name}'
get :find
end
end
resource :repositories do

View File

@ -34,7 +34,7 @@ module Travis::API::V3
end
def route(value)
current_resource.route = prefix + value
current_resource.route = Mustermann.new(prefix) + Mustermann.new(value)
end
def get(*args)

View File

@ -2,6 +2,7 @@ module Travis::API::V3
module Services
extend ConstantResolver
Branch = Module.new { extend Services }
Build = Module.new { extend Services }
Organization = Module.new { extend Services }
Organizations = Module.new { extend Services }

View File

@ -0,0 +1,7 @@
module Travis::API::V3
class Services::Branch::Find < Service
def run!
find(:branch, find(:repository))
end
end
end

View File

@ -134,6 +134,30 @@ describe 'Repos' do
JSON.parse(result.body).should == { 'file' => 'not found' }
end
it 'GET /repos/svenfuchs/minimal/branches' do
response = get '/repos/svenfuchs/minimal/branches', {}, headers
response.should deliver_json_for(repo.last_finished_builds_by_branches, version: 'v2', type: 'branches')
end
it 'GET /repos/1/branches' do
response = get "/repos/#{repo.id}/branches", {}, headers
response.should deliver_json_for(repo.last_finished_builds_by_branches, version: 'v2', type: 'branches')
end
it 'GET /repos/svenfuchs/minimal/branches/mybranch' do
mybuild = Factory(:build, repository: repo, state: :started, commit: Factory(:commit, branch: 'mybranch'), request: Factory(:request, event_type: 'push'))
response = get "/repos/svenfuchs/minimal/branches/mybranch", {}, headers
body = JSON.parse(response.body)
body['branch']['id'].should == mybuild.id
end
it 'GET /repos/svenfuchs/minimal/branches/my/branch' do
mybuild = Factory(:build, repository: repo, state: :started, commit: Factory(:commit, branch: 'my/branch'), request: Factory(:request, event_type: 'push'))
response = get "/repos/svenfuchs/minimal/branches/my/branch", {}, headers
body = JSON.parse(response.body)
body['branch']['id'].should == mybuild.id
end
describe 'GET /repos/svenfuchs/minimal.png?branch=foo,bar' do
let(:on_foo) { Factory(:commit, branch: 'foo') }
let(:on_bar) { Factory(:commit, branch: 'bar') }

View File

@ -0,0 +1,17 @@
require 'spec_helper'
describe Travis::API::V3::Extensions::BelongsTo do
describe 'reading polymorphic relation' do
subject(:repo) { Travis::API::V3::Models::Repository.first }
example { expect(repo.owner).to be_a(Travis::API::V3::Models::User) }
end
describe 'writing polymorphic relation' do
let(:repo) { Travis::API::V3::Models::Repository.create(owner: user) }
let(:user) { Travis::API::V3::Models::User.create }
after { repo.destroy; user.destroy }
example { expect(repo.owner).to be_a(Travis::API::V3::Models::User) }
example { expect(::Repository.find(repo.id).owner).to be_a(::User) }
end
end

View File

@ -14,6 +14,8 @@ describe Travis::API::V3::ServiceIndex do
"disable" => [{"request-method"=>"POST", "uri-template"=>"#{path}repo/{repository.id}/disable"}] },
"repositories" => {
"for_current_user" => [{"request-method"=>"GET", "uri-template"=>"#{path}repos"}] },
"branch" => {
"find" => [{"request-method"=>"GET", "uri-template"=>"#{path}repo/{repository.id}/branch/{branch.name}"}]},
"build" => {
"find" => [{"request-method"=>"GET", "uri-template"=>"#{path}build/{build.id}"}] },
"organizations" => {

View File

@ -0,0 +1,29 @@
require 'spec_helper'
describe Travis::API::V3::Services::Repository::Find do
let(:repo) { Travis::API::V3::Models::Repository.where(owner_name: 'svenfuchs', name: 'minimal').first }
before { repo.default_branch.save! }
describe "public repository, existing branch" do
before { get("/v3/repo/#{repo.id}/branch/master") }
example { expect(last_response).to be_ok }
example { expect(JSON.load(body)).to be == {
"@type" => "branch",
"@href" => "/v3/repo/#{repo.id}/branch/master",
"name" => "master",
"repository" => {
"@type" => "repository",
"@href" => "/v3/repo/#{repo.id}",
"id" => repo.id,
"slug" => "svenfuchs/minimal"},
"last_build" => {
"@type" => "build",
"@href" => "/v3/build/#{repo.last_build.id}",
"id" => repo.last_build.id,
"number" => "3",
"state" => "configured",
"duration" => nil,
"started_at" => "2010-11-12T13:00:00Z",
"finished_at" => nil}}}
end
end

View File

@ -40,6 +40,7 @@ describe Travis::API::V3::Services::Repositories::ForCurrentUser do
"finished_at" => "2010-11-12T12:30:20Z"},
"default_branch" => {
"@type" => "branch",
"@href" => "/v3/repo/#{repo.id}/branch/master",
"name" => "master",
"last_build" => {
"@type" => "build",

View File

@ -31,6 +31,7 @@ describe Travis::API::V3::Services::Repository::Find do
"finished_at" => "2010-11-12T12:30:20Z"},
"default_branch" => {
"@type" => "branch",
"@href" => "/v3/repo/#{repo.id}/branch/master",
"name" => "master",
"last_build" => {
"@type" => "build",
@ -114,6 +115,7 @@ describe Travis::API::V3::Services::Repository::Find do
"finished_at" => "2010-11-12T12:30:20Z"},
"default_branch" => {
"@type" => "branch",
"@href" => "/v3/repo/#{repo.id}/branch/master",
"name" => "master",
"last_build" => {
"@type" => "build",
@ -182,6 +184,7 @@ describe Travis::API::V3::Services::Repository::Find do
"finished_at" => "2010-11-12T12:30:20Z"},
"default_branch" => {
"@type" => "branch",
"@href" => "/v3/repo/#{repo.id}/branch/master",
"name" => "master",
"last_build" => {
"@type" => "build",
@ -256,6 +259,7 @@ describe Travis::API::V3::Services::Repository::Find do
"finished_at" => "2010-11-12T12:30:20Z"},
"default_branch" => {
"@type" => "branch",
"@href" => "/v3/repo/#{repo.id}/branch/master",
"name" => "master",
"last_build" => {
"@type" => "build",

View File

@ -288,14 +288,15 @@ Gem::Specification.new do |s|
s.add_dependency 'travis-support'
s.add_dependency 'travis-core'
s.add_dependency 'pg', '~> 0.13.2'
s.add_dependency 'thin', '~> 1.4'
s.add_dependency 'sinatra', '~> 1.3'
s.add_dependency 'sinatra-contrib', '~> 1.3'
s.add_dependency 'mustermann', '~> 0.4'
s.add_dependency 'redcarpet', '~> 2.1'
s.add_dependency 'rack-ssl', '~> 1.3', '>= 1.3.3'
s.add_dependency 'rack-contrib', '~> 1.1'
s.add_dependency 'pg', '~> 0.13.2'
s.add_dependency 'composite_primary_keys', '~> 5.0'
s.add_dependency 'thin', '~> 1.4'
s.add_dependency 'sinatra', '~> 1.3'
s.add_dependency 'sinatra-contrib', '~> 1.3'
s.add_dependency 'mustermann', '~> 0.4'
s.add_dependency 'redcarpet', '~> 2.1'
s.add_dependency 'rack-ssl', '~> 1.3', '>= 1.3.3'
s.add_dependency 'rack-contrib', '~> 1.1'
s.add_dependency 'memcachier'
s.add_dependency 'useragent'
end