diff --git a/Gemfile.lock b/Gemfile.lock index bc4a1f5b..c56de6bf 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -50,7 +50,7 @@ GIT GIT remote: git://github.com/travis-ci/travis-core.git - revision: ba10e214824632f15598555a2634ce3347d2f227 + revision: 4834399048b8c9cff4882d435f770826930665f6 specs: travis-core (0.0.1) actionmailer (~> 3.2.19) @@ -81,7 +81,7 @@ GIT GIT remote: git://github.com/travis-ci/travis-support.git - revision: 4fdd220ed7b06a12951e5d74a763c05a80eb0d20 + revision: e7f81093f83bd029cca6508739c5720e57e3d571 specs: travis-support (0.0.1) diff --git a/lib/travis/api/v3/queries/account.rb b/lib/travis/api/v3/queries/account.rb new file mode 100644 index 00000000..398bd39a --- /dev/null +++ b/lib/travis/api/v3/queries/account.rb @@ -0,0 +1,20 @@ +module Travis::API::V3 + class Queries::Account < Query + def find + find!(:organization) || find!(:user) + end + + private + + def query(type, main_type: self.main_type, params: self.params) + main_type = type if main_type == :account + params = params.merge("#{type}.login" => params["account.login".freeze]) if params["account.login".freeze] + Queries[type].new(params, main_type) + end + + def find!(type) + query(type).find + rescue WrongParams => e + end + end +end diff --git a/lib/travis/api/v3/queries/organization.rb b/lib/travis/api/v3/queries/organization.rb index 8c07c910..fed17682 100644 --- a/lib/travis/api/v3/queries/organization.rb +++ b/lib/travis/api/v3/queries/organization.rb @@ -1,10 +1,11 @@ module Travis::API::V3 class Queries::Organization < Query - params :id + params :id, :login def find - return Models::Organization.find_by_id(id) if id - raise WrongParams, 'missing organization.id'.freeze + return Models::Organization.find_by_id(id) if id + return Models::Organization.find_by_login(login) if login + raise WrongParams, 'missing organization.id or organization.login'.freeze end end end diff --git a/lib/travis/api/v3/queries/user.rb b/lib/travis/api/v3/queries/user.rb index af067d19..c05c9899 100644 --- a/lib/travis/api/v3/queries/user.rb +++ b/lib/travis/api/v3/queries/user.rb @@ -1,10 +1,11 @@ module Travis::API::V3 class Queries::User < Query - params :id + params :id, :login def find - return Models::User.find_by_id(id) if id - raise WrongParams, 'missing user.id'.freeze + return Models::User.find_by_id(id) if id + return Models::User.find_by_login(login) if login + raise WrongParams, 'missing user.id or user.login'.freeze end end end diff --git a/lib/travis/api/v3/routes.rb b/lib/travis/api/v3/routes.rb index ebc26022..bccb414f 100644 --- a/lib/travis/api/v3/routes.rb +++ b/lib/travis/api/v3/routes.rb @@ -3,6 +3,11 @@ module Travis::API::V3 require 'travis/api/v3/routes/dsl' extend DSL + resource :account do + route '/account/({account.login}|{user.login}|{organization.login})' + get :find + end + resource :repository do route '/repo/{repository.id}' get :find diff --git a/lib/travis/api/v3/service.rb b/lib/travis/api/v3/service.rb index b19e662c..01e2f61c 100644 --- a/lib/travis/api/v3/service.rb +++ b/lib/travis/api/v3/service.rb @@ -16,15 +16,15 @@ module Travis::API::V3 @github = {} end - def query(type = self.class.result_type) - @queries[type] ||= Queries[type].new(params, self.class.result_type) + def query(type = result_type) + @queries[type] ||= Queries[type].new(params, result_type) end def github(user = nil) @github[user] ||= GitHub.new(user) end - def find(type = self.class.result_type, *args) + def find(type = result_type, *args) not_found(true, type) unless object = query(type).find(*args) not_found(false, type) unless access_control.visible? object object @@ -33,16 +33,20 @@ module Travis::API::V3 def not_found(actually_not_found = false, type = nil) type, actually_not_found = actually_not_found, false if actually_not_found.is_a? Symbol error = actually_not_found ? EntityMissing : NotFound - raise(error, type || self.class.result_type) + raise(error, type || result_type) end def run! not_implemented end + def result_type + self.class.result_type + end + def run not_found unless result = run! - result = Result.new(self.class.result_type, result) unless result.is_a? Result + result = Result.new(result_type, result) unless result.is_a? Result result end @@ -53,7 +57,7 @@ module Travis::API::V3 end def accepted(**payload) - payload[:resource_type] ||= self.class.result_type + payload[:resource_type] ||= result_type Result.new(:accepted, payload, status: 202) end diff --git a/lib/travis/api/v3/services.rb b/lib/travis/api/v3/services.rb index 45d35477..d811d705 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 + Account = Module.new { extend Services } Branch = Module.new { extend Services } Build = Module.new { extend Services } Organization = Module.new { extend Services } diff --git a/lib/travis/api/v3/services/account/find.rb b/lib/travis/api/v3/services/account/find.rb new file mode 100644 index 00000000..88356b12 --- /dev/null +++ b/lib/travis/api/v3/services/account/find.rb @@ -0,0 +1,20 @@ +module Travis::API::V3 + class Services::Account::Find < Service + def result_type + @result_type ||= super + end + + def run! + account = find + @result_type = type_for(account) + account + end + + def type_for(account) + case account + when Models::User then :user + when Models::Organization then :organization + end + end + end +end diff --git a/spec/v3/service_index_spec.rb b/spec/v3/service_index_spec.rb index 79b9419b..b4780c62 100644 --- a/spec/v3/service_index_spec.rb +++ b/spec/v3/service_index_spec.rb @@ -86,6 +86,19 @@ describe Travis::API::V3::ServiceIndex do "request_method"=>"GET", "uri_template"=>"#{path}org/{organization.id}"}]}, "attributes"=>["id", "login", "name", "github_id"]}, + "account"=> + {"@type"=>"resource", + "actions"=> + {"find"=> + [{"@type"=>"template", + "request_method"=>"GET", + "uri_template"=>"#{path}account/{account.login}"}, + {"@type"=>"template", + "request_method"=>"GET", + "uri_template"=>"#{path}account/{user.login}"}, + {"@type"=>"template", + "request_method"=>"GET", + "uri_template"=>"#{path}account/{organization.login}"}]}}, "organizations"=> {"@type"=>"resource", "actions"=> @@ -108,22 +121,27 @@ describe Travis::API::V3::ServiceIndex do "attributes"=>["id", "login", "name", "github_id", "is_syncing", "synced_at"]}} } + shared_examples 'service index' do + describe :resources do + specify { expect(json['resources']).to include(expected_resources) } + specify { expect(json['resources'].keys.sort) .to be == expected_resources.keys.sort } + end + specify { expect(json['@href']).to be == path } + end + describe 'with /v3 prefix' do let(:path) { '/v3/' } - specify(:resources) { expect(json['resources']) .to be == expected_resources } - specify(:@href) { expect(json['@href']) .to be == '/v3/' } + it_behaves_like 'service index' end describe 'with Accept header' do let(:headers) { { 'HTTP_ACCEPT' => 'application/vnd.travis-ci.3+json' } } - specify(:resources) { expect(json['resources']) .to be == expected_resources } - specify(:@href) { expect(json['@href']) .to be == '/' } + it_behaves_like 'service index' end describe 'with Travis-API-Version header' do let(:headers) { { 'HTTP_TRAVIS_API_VERSION' => '3' } } - specify(:resources) { expect(json['resources']) .to be == expected_resources } - specify(:@href) { expect(json['@href']) .to be == '/' } + it_behaves_like 'service index' end end diff --git a/spec/v3/services/account/find_spec.rb b/spec/v3/services/account/find_spec.rb new file mode 100644 index 00000000..e2c0b2a6 --- /dev/null +++ b/spec/v3/services/account/find_spec.rb @@ -0,0 +1,85 @@ +require 'spec_helper' + +describe Travis::API::V3::Services::Account::Find do + describe "organization" do + let(:org) { Organization.new(login: 'example-org') } + before { org.save! } + after { org.delete } + + describe 'existing org, public api' do + before { get("/v3/account/example-org") } + example { expect(last_response).to be_ok } + example { expect(JSON.load(body)).to be == { + "@type" => "organization", + "@href" => "/v3/org/#{org.id}", + "id" => org.id, + "login" => "example-org", + "name" => nil, + "github_id" => nil + }} + end + + describe "does not allow overriding org id" do + let(:other) { Organization.new(login: 'other-org') } + before { other.save! } + after { other.delete } + + before { get("/v3/account/example-org?organization.id=#{other.id}") } + example { expect(last_response).to be_ok } + + pending "param whitelisting not yet implemented" do + example { expect(JSON.load(body)).to be == { + "@type" => "organization", + "@href" => "/v3/org/#{org.id}", + "id" => org.id, + "login" => "example-org", + "name" => nil, + "github_id" => nil + }} + end + end + end + + describe "user" do + let(:user) { User.new(login: 'example-user') } + before { user.save! } + after { user.delete } + + describe 'existing user, public api' do + before { get("/v3/account/example-user") } + example { expect(last_response).to be_ok } + example { expect(JSON.load(body)).to be == { + "@type" => "user", + "@href" => "/v3/user/#{user.id}", + "id" => user.id, + "login" => "example-user", + "name" => nil, + "github_id" => nil, + "is_syncing"=> nil, + "synced_at" => nil + }} + end + + describe "does not allow overriding user id" do + let(:other) { User.new(login: 'other-user') } + before { other.save! } + after { other.delete } + + before { get("/v3/account/example-org?user.id=#{other.id}") } + example { expect(last_response).to be_ok } + + pending "param whitelisting not yet implemented" do + example { expect(JSON.load(body)).to be == { + "@type" => "user", + "@href" => "/v3/user/#{user.id}", + "id" => user.id, + "login" => "example-user", + "name" => nil, + "github_id" => nil, + "is_syncing"=> nil, + "synced_at" => nil + }} + end + end + end +end