diff --git a/Gemfile.lock b/Gemfile.lock index 35bf7947..7b0c1e3b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -50,7 +50,7 @@ GIT GIT remote: git://github.com/travis-ci/travis-core.git - revision: 4cfa414b1d384e518d567c70d572b34a74cbf0bf + revision: 3c031d6fb8dbbb415e5d2ef3b2af68a80d361933 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) diff --git a/lib/travis/api/v3/access_control/generic.rb b/lib/travis/api/v3/access_control/generic.rb index fd68c26f..a8e9d2e0 100644 --- a/lib/travis/api/v3/access_control/generic.rb +++ b/lib/travis/api/v3/access_control/generic.rb @@ -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 diff --git a/lib/travis/api/v3/models.rb b/lib/travis/api/v3/models.rb index 3504a13a..d12287b3 100644 --- a/lib/travis/api/v3/models.rb +++ b/lib/travis/api/v3/models.rb @@ -1,3 +1,5 @@ +require 'composite_primary_keys' + module Travis::API::V3 module Models extend ConstantResolver diff --git a/lib/travis/api/v3/models/branch.rb b/lib/travis/api/v3/models/branch.rb new file mode 100644 index 00000000..faa7259e --- /dev/null +++ b/lib/travis/api/v3/models/branch.rb @@ -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 diff --git a/lib/travis/api/v3/models/repository.rb b/lib/travis/api/v3/models/repository.rb index 67aa37e8..3563a17c 100644 --- a/lib/travis/api/v3/models/repository.rb +++ b/lib/travis/api/v3/models/repository.rb @@ -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 diff --git a/lib/travis/api/v3/queries/branch.rb b/lib/travis/api/v3/queries/branch.rb new file mode 100644 index 00000000..145b7b02 --- /dev/null +++ b/lib/travis/api/v3/queries/branch.rb @@ -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 diff --git a/lib/travis/api/v3/queries/repositories.rb b/lib/travis/api/v3/queries/repositories.rb index 04834ccd..bc374e7b 100644 --- a/lib/travis/api/v3/queries/repositories.rb +++ b/lib/travis/api/v3/queries/repositories.rb @@ -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 diff --git a/lib/travis/api/v3/renderer.rb b/lib/travis/api/v3/renderer.rb index d4fb7735..3747cd61 100644 --- a/lib/travis/api/v3/renderer.rb +++ b/lib/travis/api/v3/renderer.rb @@ -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 diff --git a/lib/travis/api/v3/renderer/branch.rb b/lib/travis/api/v3/renderer/branch.rb new file mode 100644 index 00000000..80445497 --- /dev/null +++ b/lib/travis/api/v3/renderer/branch.rb @@ -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 diff --git a/lib/travis/api/v3/renderer/model_renderer.rb b/lib/travis/api/v3/renderer/model_renderer.rb index 65de8be1..0be52876 100644 --- a/lib/travis/api/v3/renderer/model_renderer.rb +++ b/lib/travis/api/v3/renderer/model_renderer.rb @@ -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) diff --git a/lib/travis/api/v3/renderer/repository.rb b/lib/travis/api/v3/renderer/repository.rb index f807b299..947ea727 100644 --- a/lib/travis/api/v3/renderer/repository.rb +++ b/lib/travis/api/v3/renderer/repository.rb @@ -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 diff --git a/lib/travis/api/v3/routes.rb b/lib/travis/api/v3/routes.rb index e911ac78..b0d77179 100644 --- a/lib/travis/api/v3/routes.rb +++ b/lib/travis/api/v3/routes.rb @@ -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 diff --git a/lib/travis/api/v3/routes/dsl.rb b/lib/travis/api/v3/routes/dsl.rb index e4e08df2..fbd71c8f 100644 --- a/lib/travis/api/v3/routes/dsl.rb +++ b/lib/travis/api/v3/routes/dsl.rb @@ -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) diff --git a/lib/travis/api/v3/services.rb b/lib/travis/api/v3/services.rb index 5e543412..e5180864 100644 --- a/lib/travis/api/v3/services.rb +++ b/lib/travis/api/v3/services.rb @@ -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 } diff --git a/lib/travis/api/v3/services/branch/find.rb b/lib/travis/api/v3/services/branch/find.rb new file mode 100644 index 00000000..927b1a87 --- /dev/null +++ b/lib/travis/api/v3/services/branch/find.rb @@ -0,0 +1,7 @@ +module Travis::API::V3 + class Services::Branch::Find < Service + def run! + find(:branch, find(:repository)) + end + end +end diff --git a/spec/v3/service_index_spec.rb b/spec/v3/service_index_spec.rb index ce53c2e4..0925bb75 100644 --- a/spec/v3/service_index_spec.rb +++ b/spec/v3/service_index_spec.rb @@ -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" => { diff --git a/spec/v3/services/branch/find_spec.rb b/spec/v3/services/branch/find_spec.rb new file mode 100644 index 00000000..8d909aa1 --- /dev/null +++ b/spec/v3/services/branch/find_spec.rb @@ -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 \ No newline at end of file diff --git a/spec/v3/services/repositories/for_current_user_spec.rb b/spec/v3/services/repositories/for_current_user_spec.rb index 4f61803b..042cdae3 100644 --- a/spec/v3/services/repositories/for_current_user_spec.rb +++ b/spec/v3/services/repositories/for_current_user_spec.rb @@ -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", diff --git a/spec/v3/services/repository/find_spec.rb b/spec/v3/services/repository/find_spec.rb index 292a1d2c..9cce296f 100644 --- a/spec/v3/services/repository/find_spec.rb +++ b/spec/v3/services/repository/find_spec.rb @@ -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", diff --git a/travis-api.gemspec b/travis-api.gemspec index d4e1c079..548ced44 100644 --- a/travis-api.gemspec +++ b/travis-api.gemspec @@ -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