v3: use branches table for default_branch, eager load on /repos endpoint to avoid N+1 queries
This commit is contained in:
parent
f100a2b927
commit
c968bbaab1
2
Gemfile
2
Gemfile
|
@ -5,7 +5,7 @@ ruby '2.1.2' if ENV.key?('DYNO')
|
|||
|
||||
gem 's3', github: 'travis-ci/s3'
|
||||
|
||||
gem 'travis-core', github: 'travis-ci/travis-core'
|
||||
gem 'travis-core', github: 'travis-ci/travis-core', branch: 'rkh-branches' # TODO: revert to master
|
||||
gem 'travis-support', github: 'travis-ci/travis-support'
|
||||
gem 'travis-config', '~> 0.1.0'
|
||||
gem 'travis-sidekiqs', github: 'travis-ci/travis-sidekiqs', require: nil
|
||||
|
|
24
Gemfile.lock
24
Gemfile.lock
|
@ -50,7 +50,8 @@ GIT
|
|||
|
||||
GIT
|
||||
remote: git://github.com/travis-ci/travis-core.git
|
||||
revision: 4cfa414b1d384e518d567c70d572b34a74cbf0bf
|
||||
revision: f39caf6da61b8a988ccf3e2d3e0272df427f59a3
|
||||
branch: rkh-branches
|
||||
specs:
|
||||
travis-core (0.0.1)
|
||||
actionmailer (~> 3.2.19)
|
||||
|
@ -61,7 +62,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 +96,7 @@ PATH
|
|||
remote: .
|
||||
specs:
|
||||
travis-api (0.0.1)
|
||||
composite_primary_keys (~> 5.0)
|
||||
memcachier
|
||||
mustermann (~> 0.4)
|
||||
pg (~> 0.13.2)
|
||||
|
@ -154,6 +156,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 +172,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 +191,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 +228,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 +278,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 +328,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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
require 'composite_primary_keys'
|
||||
|
||||
module Travis::API::V3
|
||||
module Models
|
||||
extend ConstantResolver
|
||||
|
|
8
lib/travis/api/v3/models/branch.rb
Normal file
8
lib/travis/api/v3/models/branch.rb
Normal 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
|
||||
has_many :commits, foreign_key: [:repository_id, :branch], primary_key: [:repository_id, :name], order: 'id DESC'.freeze
|
||||
end
|
||||
end
|
|
@ -2,7 +2,8 @@ module Travis::API::V3
|
|||
class Models::Repository < Model
|
||||
has_many :commits, dependent: :delete_all
|
||||
has_many :requests, dependent: :delete_all
|
||||
has_many :builds, 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
|
||||
|
||||
|
@ -12,12 +13,44 @@ module Travis::API::V3
|
|||
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 last_build_on(branch)
|
||||
builds.order('id DESC'.freeze).where(branch: branch, event_type: 'push'.freeze).first
|
||||
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
|
||||
|
|
10
lib/travis/api/v3/queries/branch.rb
Normal file
10
lib/travis/api/v3/queries/branch.rb
Normal 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
|
|
@ -11,6 +11,7 @@ module Travis::API::V3
|
|||
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
|
||||
|
|
|
@ -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
|
||||
|
|
8
lib/travis/api/v3/renderer/branch.rb
Normal file
8
lib/travis/api/v3/renderer/branch.rb
Normal 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
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -12,6 +12,11 @@ module Travis::API::V3
|
|||
get :find
|
||||
post :create
|
||||
end
|
||||
|
||||
resource :branch do
|
||||
route '/branch/{branch.name}'
|
||||
get :find
|
||||
end
|
||||
end
|
||||
|
||||
resource :repositories do
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 }
|
||||
|
|
7
lib/travis/api/v3/services/branch/find.rb
Normal file
7
lib/travis/api/v3/services/branch/find.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
module Travis::API::V3
|
||||
class Services::Branch::Find < Service
|
||||
def run!
|
||||
find(:branch, find(:repository))
|
||||
end
|
||||
end
|
||||
end
|
|
@ -12,6 +12,8 @@ describe Travis::API::V3::ServiceIndex do
|
|||
"find" => [{"request-method"=>"GET", "uri-template"=>"#{path}repo/{repository.id}"}] },
|
||||
"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" => {
|
||||
|
|
29
spec/v3/services/branch/find_spec.rb
Normal file
29
spec/v3/services/branch/find_spec.rb
Normal 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
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue
Block a user