diff --git a/Gemfile.lock b/Gemfile.lock index 00d58f8b..b81eb7bf 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -50,7 +50,7 @@ GIT GIT remote: git://github.com/travis-ci/travis-core.git - revision: 749ba8a4ca56533cb33d3bbc9e067c10a09ea33f + revision: 21156bf88fcc623d45b87d5b5de427c1746f7f55 specs: travis-core (0.0.1) actionmailer (~> 3.2.19) diff --git a/lib/travis/api/app/endpoint/repos.rb b/lib/travis/api/app/endpoint/repos.rb index bb5d4311..b4f98438 100644 --- a/lib/travis/api/app/endpoint/repos.rb +++ b/lib/travis/api/app/endpoint/repos.rb @@ -16,6 +16,7 @@ class Travis::Api::App # json(:repositories) get '/' do prefer_follower do + params['ids'] = params['ids'].split(',') if params['ids'].respond_to?(:split) respond_with service(:find_repos, params) end end diff --git a/lib/travis/api/v3.rb b/lib/travis/api/v3.rb index 1a38e2bd..727d62ae 100644 --- a/lib/travis/api/v3.rb +++ b/lib/travis/api/v3.rb @@ -4,7 +4,7 @@ module Travis V3 = self def load_dir(dir, recursive: true) - Dir.glob("#{dir}/*.rb").sort.each { |f| require f[%r[(?<=lib/).+(?=\.rb$)]] } + Dir.glob("#{dir}/*.rb").sort.each { |f| require f[%r[(?<=lib/)travis/.+(?=\.rb$)]] } Dir.glob("#{dir}/*").sort.each { |dir| load_dir(dir) } if recursive end diff --git a/lib/travis/api/v3/features.rb b/lib/travis/api/v3/features.rb new file mode 100644 index 00000000..a7b11667 --- /dev/null +++ b/lib/travis/api/v3/features.rb @@ -0,0 +1,9 @@ +module Travis::API::V3 + module Features + extend self + + def use_subscriptions? + Models::Subscription.table_exists? + end + end +end diff --git a/lib/travis/api/v3/github.rb b/lib/travis/api/v3/github.rb index b1f501c2..e9064a58 100644 --- a/lib/travis/api/v3/github.rb +++ b/lib/travis/api/v3/github.rb @@ -1,6 +1,5 @@ require 'gh' - module Travis::API::V3 class GitHub DEFAULT_OPTIONS = { diff --git a/lib/travis/api/v3/model.rb b/lib/travis/api/v3/model.rb index 90b26d1d..f37283df 100644 --- a/lib/travis/api/v3/model.rb +++ b/lib/travis/api/v3/model.rb @@ -2,5 +2,9 @@ module Travis::API::V3 class Model < ActiveRecord::Base include Extensions::BelongsTo self.abstract_class = true + + def self.===(other) + super or (self == Model and other.class.parent == Models) + end end end diff --git a/lib/travis/api/v3/models/account.rb b/lib/travis/api/v3/models/account.rb new file mode 100644 index 00000000..a78a4b13 --- /dev/null +++ b/lib/travis/api/v3/models/account.rb @@ -0,0 +1,29 @@ +module Travis::API::V3 + class Models::Account + attr_accessor :owner + + def initialize(owner) + @owner = owner + end + + def id + owner.github_id + end + + def subscription + owner.subscription if owner.respond_to? :subscription + end + + def educational? + return false unless owner.respond_to? :educational + !!owner.educational + end + + def subscribed? + subscription.present? and subscription.active? + end + + alias_method :educational, :educational? + alias_method :subscribed, :subscribed? + end +end \ No newline at end of file diff --git a/lib/travis/api/v3/models/organization.rb b/lib/travis/api/v3/models/organization.rb index e42e3364..973487ed 100644 --- a/lib/travis/api/v3/models/organization.rb +++ b/lib/travis/api/v3/models/organization.rb @@ -3,5 +3,10 @@ module Travis::API::V3 has_many :memberships has_many :users, through: :memberships has_many :repositories, as: :owner + has_one :subscription, as: :owner + + def subscription + super if Features.use_subscriptions? + end end end diff --git a/lib/travis/api/v3/models/subscription.rb b/lib/travis/api/v3/models/subscription.rb new file mode 100644 index 00000000..7a73e9b5 --- /dev/null +++ b/lib/travis/api/v3/models/subscription.rb @@ -0,0 +1,4 @@ +module Travis::API::V3 + class Models::Subscription < Model + end +end diff --git a/lib/travis/api/v3/models/user.rb b/lib/travis/api/v3/models/user.rb index 4ba4d7f8..626996c0 100644 --- a/lib/travis/api/v3/models/user.rb +++ b/lib/travis/api/v3/models/user.rb @@ -4,13 +4,18 @@ module Travis::API::V3 has_many :permissions, dependent: :destroy has_many :emails, dependent: :destroy has_many :tokens, dependent: :destroy - has_many :repositories, through: :permissions has_many :organizations, through: :memberships + has_many :repositories, as: :owner + has_one :subscription, as: :owner serialize :github_oauth_token, Extensions::EncryptedColumn.new(disable: true) def token tokens.first_or_create.token end + + def subscription + super if Features.use_subscriptions? + end end end diff --git a/lib/travis/api/v3/queries/accounts.rb b/lib/travis/api/v3/queries/accounts.rb new file mode 100644 index 00000000..d0d00d1f --- /dev/null +++ b/lib/travis/api/v3/queries/accounts.rb @@ -0,0 +1,22 @@ +module Travis::API::V3 + class Queries::Accounts < Query + def for_member(user) + organizations = Queries[:organizations].new(params, main_type).for_member(user) + accounts(user, organizations) + end + + private + + def accounts(*list) + list.flatten.map { |entry| account(entry) } + end + + def account(entry) + case entry + when Models::User, Models::Organization then Models::Account.new(entry) + when Models::Account, nil then entry + else raise ArgumentError, 'cannot convert %p into an account'.freeze % [entry] + end + end + end +end diff --git a/lib/travis/api/v3/queries/account.rb b/lib/travis/api/v3/queries/owner.rb similarity index 61% rename from lib/travis/api/v3/queries/account.rb rename to lib/travis/api/v3/queries/owner.rb index 398bd39a..229833f2 100644 --- a/lib/travis/api/v3/queries/account.rb +++ b/lib/travis/api/v3/queries/owner.rb @@ -1,5 +1,5 @@ module Travis::API::V3 - class Queries::Account < Query + class Queries::Owner < Query def find find!(:organization) || find!(:user) end @@ -7,8 +7,8 @@ module Travis::API::V3 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] + main_type = type if main_type == :owner + params = params.merge("#{type}.login" => params["owner.login".freeze]) if params["owner.login".freeze] Queries[type].new(params, main_type) end diff --git a/lib/travis/api/v3/renderer/account.rb b/lib/travis/api/v3/renderer/account.rb new file mode 100644 index 00000000..19cd16d1 --- /dev/null +++ b/lib/travis/api/v3/renderer/account.rb @@ -0,0 +1,8 @@ +require 'travis/api/v3/renderer/model_renderer' + +module Travis::API::V3 + class Renderer::Account < Renderer::ModelRenderer + representation(:minimal, :id) + representation(:standard, :id, :subscribed, :educational, :owner) + end +end diff --git a/lib/travis/api/v3/renderer/accounts.rb b/lib/travis/api/v3/renderer/accounts.rb new file mode 100644 index 00000000..734a5f2e --- /dev/null +++ b/lib/travis/api/v3/renderer/accounts.rb @@ -0,0 +1,8 @@ +require 'travis/api/v3/renderer/collection_renderer' + +module Travis::API::V3 + class Renderer::Accounts < Renderer::CollectionRenderer + type :accounts + collection_key :accounts + end +end diff --git a/lib/travis/api/v3/renderer/avatar_url.rb b/lib/travis/api/v3/renderer/avatar_url.rb new file mode 100644 index 00000000..5226962f --- /dev/null +++ b/lib/travis/api/v3/renderer/avatar_url.rb @@ -0,0 +1,25 @@ +require 'digest/md5' + +module Travis::API::V3 + module Renderer::AvatarURL + GRAVATAR_URL = 'https://0.gravatar.com/avatar/%s' + private_constant :GRAVATAR_URL + + extend self + + def avatar_url(object = @model) + case object + when has(:avatar_url) then object.avatar_url + when has(:gravatar_url) then object.gravatar_url + when has(:gravatar_id) then GRAVATAR_URL % object.gravatar_id + when has(:email) then GRAVATAR_URL % Digest::MD5.hexdigest(object.email) + end + end + + def has(field) + proc { |o| o.respond_to?(field) and o.send(field).present? } + end + + private :has + end +end diff --git a/lib/travis/api/v3/renderer/model_renderer.rb b/lib/travis/api/v3/renderer/model_renderer.rb index 7a253178..07944fec 100644 --- a/lib/travis/api/v3/renderer/model_renderer.rb +++ b/lib/travis/api/v3/renderer/model_renderer.rb @@ -16,15 +16,18 @@ module Travis::API::V3 class_eval "def #{field}; @model.#{field}; end" unless method_defined?(field) available_attributes << field.to_s end - representations[name] = fields + representations[name] ||= [] + representations[name] += fields end + @representations = {} def self.representations - @representations ||= {} + @representations ||= superclass.representations.dup end + @available_attributes = Set.new def self.available_attributes - @available_attributes ||= Set.new + @available_attributes ||= superclass.available_attributes.dup end def self.render(model, representation = :standard, **options) diff --git a/lib/travis/api/v3/renderer/organization.rb b/lib/travis/api/v3/renderer/organization.rb index 1916151c..8eab06a8 100644 --- a/lib/travis/api/v3/renderer/organization.rb +++ b/lib/travis/api/v3/renderer/organization.rb @@ -1,8 +1,6 @@ -require 'travis/api/v3/renderer/model_renderer' +require 'travis/api/v3/renderer/owner' module Travis::API::V3 - class Renderer::Organization < Renderer::ModelRenderer - representation(:minimal, :id, :login) - representation(:standard, :id, :login, :name, :github_id) + class Renderer::Organization < Renderer::Owner end end diff --git a/lib/travis/api/v3/renderer/owner.rb b/lib/travis/api/v3/renderer/owner.rb new file mode 100644 index 00000000..1d694b12 --- /dev/null +++ b/lib/travis/api/v3/renderer/owner.rb @@ -0,0 +1,11 @@ +require 'travis/api/v3/renderer/model_renderer' +require 'travis/api/v3/renderer/avatar_url' + +module Travis::API::V3 + class Renderer::Owner < Renderer::ModelRenderer + include Renderer::AvatarURL + + representation(:minimal, :id, :login) + representation(:standard, :id, :login, :name, :github_id, :avatar_url) + end +end diff --git a/lib/travis/api/v3/renderer/user.rb b/lib/travis/api/v3/renderer/user.rb index f6fc870f..992d3f18 100644 --- a/lib/travis/api/v3/renderer/user.rb +++ b/lib/travis/api/v3/renderer/user.rb @@ -1,8 +1,7 @@ -require 'travis/api/v3/renderer/model_renderer' +require 'travis/api/v3/renderer/owner' module Travis::API::V3 - class Renderer::User < Renderer::ModelRenderer - representation(:minimal, :id, :login) - representation(:standard, :id, :login, :name, :github_id, :is_syncing, :synced_at) + class Renderer::User < Renderer::Owner + representation(:standard, :is_syncing, :synced_at) end end diff --git a/lib/travis/api/v3/routes.rb b/lib/travis/api/v3/routes.rb index bccb414f..18ea5ddc 100644 --- a/lib/travis/api/v3/routes.rb +++ b/lib/travis/api/v3/routes.rb @@ -3,8 +3,13 @@ module Travis::API::V3 require 'travis/api/v3/routes/dsl' extend DSL - resource :account do - route '/account/({account.login}|{user.login}|{organization.login})' + resource :accounts do + route '/accounts' + get :for_current_user + end + + resource :owner do + route '/owner/({owner.login}|{user.login}|{organization.login})' get :find end diff --git a/lib/travis/api/v3/service_index.rb b/lib/travis/api/v3/service_index.rb index bbf13f90..c35882e9 100644 --- a/lib/travis/api/v3/service_index.rb +++ b/lib/travis/api/v3/service_index.rb @@ -34,7 +34,7 @@ module Travis::API::V3 pattern = sub_route ? resource.route + sub_route : resource.route factory = Services[resource.identifier][service] pattern.to_templates.each do |template| - params = request_method == 'GET'.freeze ? factory.params : Service.params + params = factory.params if request_method == 'GET'.freeze params &&= params.reject { |p| p.start_with? ?@.freeze } template += "{?#{params.sort.join(?,)}}" if params and params.any? list << { :@type => :template, :request_method => request_method, :uri_template => prefix + template } diff --git a/lib/travis/api/v3/services.rb b/lib/travis/api/v3/services.rb index d811d705..9df67da4 100644 --- a/lib/travis/api/v3/services.rb +++ b/lib/travis/api/v3/services.rb @@ -2,11 +2,12 @@ module Travis::API::V3 module Services extend ConstantResolver - Account = Module.new { extend Services } + Accounts = Module.new { extend Services } Branch = Module.new { extend Services } Build = Module.new { extend Services } Organization = Module.new { extend Services } Organizations = Module.new { extend Services } + Owner = Module.new { extend Services } Repositories = Module.new { extend Services } Repository = Module.new { extend Services } Requests = Module.new { extend Services } diff --git a/lib/travis/api/v3/services/accounts/for_current_user.rb b/lib/travis/api/v3/services/accounts/for_current_user.rb new file mode 100644 index 00000000..7bbb6680 --- /dev/null +++ b/lib/travis/api/v3/services/accounts/for_current_user.rb @@ -0,0 +1,8 @@ +module Travis::API::V3 + class Services::Accounts::ForCurrentUser < Service + def run! + raise LoginRequired unless access_control.logged_in? + query.for_member(access_control.user) + end + end +end diff --git a/lib/travis/api/v3/services/account/find.rb b/lib/travis/api/v3/services/owner/find.rb similarity index 57% rename from lib/travis/api/v3/services/account/find.rb rename to lib/travis/api/v3/services/owner/find.rb index 88356b12..6751396d 100644 --- a/lib/travis/api/v3/services/account/find.rb +++ b/lib/travis/api/v3/services/owner/find.rb @@ -1,17 +1,17 @@ module Travis::API::V3 - class Services::Account::Find < Service + class Services::Owner::Find < Service def result_type @result_type ||= super end def run! - account = find - @result_type = type_for(account) - account + owner = find + @result_type = type_for(owner) + owner end - def type_for(account) - case account + def type_for(owner) + case owner when Models::User then :user when Models::Organization then :organization end diff --git a/script/console b/script/console index fd1fd99c..64380b3e 100755 --- a/script/console +++ b/script/console @@ -15,6 +15,8 @@ class Travis::Console end end +V3 = Travis::API::V3 + Travis::Api::App.setup console = Travis::Console.new methods = Travis::Console.instance_methods - Object.instance_methods diff --git a/spec/integration/v2/repositories_spec.rb b/spec/integration/v2/repositories_spec.rb index dfa86220..c3bbe09f 100644 --- a/spec/integration/v2/repositories_spec.rb +++ b/spec/integration/v2/repositories_spec.rb @@ -5,6 +5,24 @@ describe 'Repos' do let(:repo) { Repository.by_slug('svenfuchs/minimal').first } let(:headers) { { 'HTTP_ACCEPT' => 'application/vnd.travis-ci.2+json' } } + it 'returns repositories by ids with ids=1,2,3 format' do + repos = Repository.all + ids = repos[0..1].map(&:id) + response = get "/repos?ids=#{ids.join(',')}", {}, headers + body = JSON.parse(response.body) + + body['repos'].map { |r| r['id'] }.should == ids + end + + it 'returns repositories by ids with ids[] format' do + repos = Repository.all + ids = repos[0..1].map(&:id) + response = get "/repos?ids[]=#{ids[0]}&ids[]=#{ids[1]}", {}, headers + body = JSON.parse(response.body) + + body['repos'].map { |r| r['id'] }.should == ids + end + describe 'with authenticated user' do let(:user) { User.where(login: 'svenfuchs').first } let(:token) { Travis::Api::App::AccessToken.create(user: user, app_id: -1) } diff --git a/spec/v3/renderer/avatar_url_spec.rb b/spec/v3/renderer/avatar_url_spec.rb new file mode 100644 index 00000000..a154325c --- /dev/null +++ b/spec/v3/renderer/avatar_url_spec.rb @@ -0,0 +1,45 @@ +require 'spec_helper' + +describe Travis::API::V3::Renderer::AvatarURL do + let(:object) { Object.new } + subject { Travis::API::V3::Renderer::AvatarURL.avatar_url(object) } + + describe 'without any useful information' do + it { should be_nil } + end + + describe 'with valid avatar_url' do + let(:object) { stub('input', avatar_url: "http://foo") } + it { should be == "http://foo" } + end + + describe 'with valid avatar_url' do + let(:object) { stub('input', gravatar_url: "http://foo") } + it { should be == "http://foo" } + end + + describe 'with valid gravatar_id' do + let(:object) { stub('input', gravatar_id: "foo") } + it { should be == "https://0.gravatar.com/avatar/foo" } + end + + describe 'with valid avatar_url and gravatar_id' do + let(:object) { stub('input', avatar_url: "http://foo", gravatar_id: "https://0.gravatar.com/avatar/foo") } + it { should be == "http://foo" } + end + + describe 'with missing avatar_url and valid gravatar_id' do + let(:object) { stub('input', avatar_url: nil, gravatar_id: "foo") } + it { should be == "https://0.gravatar.com/avatar/foo" } + end + + describe 'with email' do + let(:object) { stub('input', email: "foo") } + it { should be == "https://0.gravatar.com/avatar/acbd18db4cc2f85cedef654fccc4a4d8" } + end + + describe 'with missing email' do + let(:object) { stub('input', email: nil) } + it { should be_nil } + end +end \ No newline at end of file diff --git a/spec/v3/service_index_spec.rb b/spec/v3/service_index_spec.rb index 06d81bb1..bf49e614 100644 --- a/spec/v3/service_index_spec.rb +++ b/spec/v3/service_index_spec.rb @@ -21,7 +21,7 @@ describe Travis::API::V3::ServiceIndex do describe "create action" do let(:action) { resource.fetch("actions").fetch("create") } - specify { expect(action).to include("@type"=>"template", "request_method"=>"POST", "uri_template"=>"#{path}repo/{repository.id}/requests{?include}") } + specify { expect(action).to include("@type"=>"template", "request_method"=>"POST", "uri_template"=>"#{path}repo/{repository.id}/requests") } end end @@ -48,12 +48,12 @@ describe Travis::API::V3::ServiceIndex do describe "enable action" do let(:action) { resource.fetch("actions").fetch("enable") } - specify { expect(action).to include("@type"=>"template", "request_method"=>"POST", "uri_template"=>"#{path}repo/{repository.id}/enable{?include}") } + specify { expect(action).to include("@type"=>"template", "request_method"=>"POST", "uri_template"=>"#{path}repo/{repository.id}/enable") } end describe "disable action" do let(:action) { resource.fetch("actions").fetch("disable") } - specify { expect(action).to include("@type"=>"template", "request_method"=>"POST", "uri_template"=>"#{path}repo/{repository.id}/disable{?include}") } + specify { expect(action).to include("@type"=>"template", "request_method"=>"POST", "uri_template"=>"#{path}repo/{repository.id}/disable") } end end @@ -90,16 +90,16 @@ describe Travis::API::V3::ServiceIndex do end end - describe "account resource" do - let(:resource) { resources.fetch("account") } - specify { expect(resources) .to include("account") } + describe "owner resource" do + let(:resource) { resources.fetch("owner") } + specify { expect(resources) .to include("owner") } specify { expect(resource["@type"]) .to be == "resource" } describe "find action" do let(:action) { resource.fetch("actions").fetch("find") } - specify { expect(action).to include("@type"=>"template", "request_method"=>"GET", "uri_template"=>"#{path}account/{account.login}{?include}") } - specify { expect(action).to include("@type"=>"template", "request_method"=>"GET", "uri_template"=>"#{path}account/{user.login}{?include}") } - specify { expect(action).to include("@type"=>"template", "request_method"=>"GET", "uri_template"=>"#{path}account/{organization.login}{?include}") } + specify { expect(action).to include("@type"=>"template", "request_method"=>"GET", "uri_template"=>"#{path}owner/{owner.login}{?include}") } + specify { expect(action).to include("@type"=>"template", "request_method"=>"GET", "uri_template"=>"#{path}owner/{user.login}{?include}") } + specify { expect(action).to include("@type"=>"template", "request_method"=>"GET", "uri_template"=>"#{path}owner/{organization.login}{?include}") } end end diff --git a/spec/v3/services/account/find_spec.rb b/spec/v3/services/account/find_spec.rb deleted file mode 100644 index 644311a1..00000000 --- a/spec/v3/services/account/find_spec.rb +++ /dev/null @@ -1,107 +0,0 @@ -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 'it is not case sensitive' 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 } - 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 - - 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 'it is not case sensitive' 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-user?user.id=#{other.id}") } - 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 - end -end diff --git a/spec/v3/services/accounts/for_current_user_spec.rb b/spec/v3/services/accounts/for_current_user_spec.rb new file mode 100644 index 00000000..01d45c9a --- /dev/null +++ b/spec/v3/services/accounts/for_current_user_spec.rb @@ -0,0 +1,45 @@ +require 'spec_helper' + +describe Travis::API::V3::Services::Accounts::ForCurrentUser do + let(:repo) { Repository.by_slug('svenfuchs/minimal').first } + + let(:token) { Travis::Api::App::AccessToken.create(user: repo.owner, app_id: 1) } + let(:headers) {{ 'HTTP_AUTHORIZATION' => "token #{token}" }} + before { Permission.create(repository: repo, user: repo.owner, pull: true) } + before { repo.update_attribute(:private, true) } + after { repo.update_attribute(:private, false) } + + let(:org) { Organization.new(login: 'example-org', github_id: 42) } + before { org.save! } + before { org.memberships.create(user: repo.owner) } + after { org.delete } + + describe "authenticated as user with access" do + before { get("/v3/accounts", {}, headers) } + example { expect(last_response).to be_ok } + example { expect(JSON.load(body)).to be == { + "@type" => "accounts", + "@href" => "/v3/accounts", + "accounts" => [{ + "@type" => "account", + "id" => repo.owner.github_id, + "subscribed" => false, + "educational" => false, + "owner" => { + "@type" => "user", + "@href" => "/v3/user/#{repo.owner_id}", + "id" => repo.owner_id, + "login" => "svenfuchs" }}, + {"@type" => "account", + "id" => 42, + "subscribed" => false, + "educational" => false, + "owner" => { + "@type" => "organization", + "@href" => "/v3/org/#{org.id}", + "id" => org.id, + "login" => "example-org"} + }] + }} + end +end \ No newline at end of file diff --git a/spec/v3/services/organization/find_spec.rb b/spec/v3/services/organization/find_spec.rb index cc3b248a..030aa172 100644 --- a/spec/v3/services/organization/find_spec.rb +++ b/spec/v3/services/organization/find_spec.rb @@ -9,12 +9,13 @@ describe Travis::API::V3::Services::Organization::Find do before { get("/v3/org/#{org.id}") } 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 + "@type" => "organization", + "@href" => "/v3/org/#{org.id}", + "id" => org.id, + "login" => "example-org", + "name" => nil, + "github_id" => nil, + "avatar_url" => nil }} end end diff --git a/spec/v3/services/organizations/for_current_user_spec.rb b/spec/v3/services/organizations/for_current_user_spec.rb index d4123ac5..f402e7cd 100644 --- a/spec/v3/services/organizations/for_current_user_spec.rb +++ b/spec/v3/services/organizations/for_current_user_spec.rb @@ -26,7 +26,8 @@ describe Travis::API::V3::Services::Organizations::ForCurrentUser do "id" => org.id, "login" => "example-org", "name" => nil, - "github_id" => nil + "github_id" => nil, + "avatar_url" => nil }] }} end diff --git a/spec/v3/services/owner/find_spec.rb b/spec/v3/services/owner/find_spec.rb new file mode 100644 index 00000000..6717047f --- /dev/null +++ b/spec/v3/services/owner/find_spec.rb @@ -0,0 +1,113 @@ +require 'spec_helper' + +describe Travis::API::V3::Services::Owner::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/owner/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, + "avatar_url" => nil + }} + end + + describe 'it is not case sensitive' do + before { get("/v3/owner/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, + "avatar_url" => 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/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}", + "id" => org.id, + "login" => "example-org", + "name" => nil, + "github_id" => nil, + "avatar_url" => nil + }} + 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/owner/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, + "avatar_url" => nil, + "is_syncing" => nil, + "synced_at" => nil + }} + end + + describe 'it is not case sensitive' do + before { get("/v3/owner/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, + "avatar_url" => 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/owner/example-user?user.id=#{other.id}") } + 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, + "avatar_url" => nil, + "is_syncing" => nil, + "synced_at" => nil + }} + end + end +end diff --git a/spec/v3/services/user/current_spec.rb b/spec/v3/services/user/current_spec.rb index e96ef24d..443671e6 100644 --- a/spec/v3/services/user/current_spec.rb +++ b/spec/v3/services/user/current_spec.rb @@ -16,6 +16,7 @@ describe Travis::API::V3::Services::User::Current do "login" => "svenfuchs", "name" =>"Sven Fuchs", "github_id" => user.github_id, + "avatar_url" => "https://0.gravatar.com/avatar/07fb84848e68b96b69022d333ca8a3e2", "is_syncing" => user.is_syncing, "synced_at" => user.synced_at }} diff --git a/spec/v3/services/user/find_spec.rb b/spec/v3/services/user/find_spec.rb index cf68fe63..30ee4800 100644 --- a/spec/v3/services/user/find_spec.rb +++ b/spec/v3/services/user/find_spec.rb @@ -16,6 +16,7 @@ describe Travis::API::V3::Services::User::Find do "login" => "svenfuchs", "name" =>"Sven Fuchs", "github_id" => user.github_id, + "avatar_url" => "https://0.gravatar.com/avatar/07fb84848e68b96b69022d333ca8a3e2", "is_syncing" => user.is_syncing, "synced_at" => user.synced_at }} diff --git a/travis-api.gemspec b/travis-api.gemspec index 4e4f0f81..227b9a15 100644 --- a/travis-api.gemspec +++ b/travis-api.gemspec @@ -37,8 +37,8 @@ Gem::Specification.new do |s| ] s.email = [ - "drogus@gmail.com", "konstantin.mailinglists@googlemail.com", + "drogus@gmail.com", "me@svenfuchs.com", "asari.ruby@gmail.com", "meyer@paperplanes.de", @@ -182,9 +182,11 @@ Gem::Specification.new do |s| "lib/travis/api/v3/error.rb", "lib/travis/api/v3/extensions/belongs_to.rb", "lib/travis/api/v3/extensions/encrypted_column.rb", + "lib/travis/api/v3/features.rb", "lib/travis/api/v3/github.rb", "lib/travis/api/v3/model.rb", "lib/travis/api/v3/models.rb", + "lib/travis/api/v3/models/account.rb", "lib/travis/api/v3/models/branch.rb", "lib/travis/api/v3/models/broadcast.rb", "lib/travis/api/v3/models/build.rb", @@ -199,14 +201,17 @@ Gem::Specification.new do |s| "lib/travis/api/v3/models/repository.rb", "lib/travis/api/v3/models/request.rb", "lib/travis/api/v3/models/ssl_key.rb", + "lib/travis/api/v3/models/subscription.rb", "lib/travis/api/v3/models/token.rb", "lib/travis/api/v3/models/user.rb", "lib/travis/api/v3/opt_in.rb", "lib/travis/api/v3/queries.rb", + "lib/travis/api/v3/queries/accounts.rb", "lib/travis/api/v3/queries/branch.rb", "lib/travis/api/v3/queries/build.rb", "lib/travis/api/v3/queries/organization.rb", "lib/travis/api/v3/queries/organizations.rb", + "lib/travis/api/v3/queries/owner.rb", "lib/travis/api/v3/queries/repositories.rb", "lib/travis/api/v3/queries/repository.rb", "lib/travis/api/v3/queries/request.rb", @@ -215,6 +220,9 @@ Gem::Specification.new do |s| "lib/travis/api/v3/query.rb", "lib/travis/api/v3/renderer.rb", "lib/travis/api/v3/renderer/accepted.rb", + "lib/travis/api/v3/renderer/account.rb", + "lib/travis/api/v3/renderer/accounts.rb", + "lib/travis/api/v3/renderer/avatar_url.rb", "lib/travis/api/v3/renderer/branch.rb", "lib/travis/api/v3/renderer/build.rb", "lib/travis/api/v3/renderer/collection_renderer.rb", @@ -222,6 +230,7 @@ Gem::Specification.new do |s| "lib/travis/api/v3/renderer/model_renderer.rb", "lib/travis/api/v3/renderer/organization.rb", "lib/travis/api/v3/renderer/organizations.rb", + "lib/travis/api/v3/renderer/owner.rb", "lib/travis/api/v3/renderer/repositories.rb", "lib/travis/api/v3/renderer/repository.rb", "lib/travis/api/v3/renderer/requests.rb", @@ -234,16 +243,21 @@ Gem::Specification.new do |s| "lib/travis/api/v3/service.rb", "lib/travis/api/v3/service_index.rb", "lib/travis/api/v3/services.rb", + "lib/travis/api/v3/services/accounts/for_current_user.rb", "lib/travis/api/v3/services/branch/find.rb", "lib/travis/api/v3/services/build/find.rb", "lib/travis/api/v3/services/organization/find.rb", + "lib/travis/api/v3/services/organization/sync.rb", "lib/travis/api/v3/services/organizations/for_current_user.rb", + "lib/travis/api/v3/services/owner/find.rb", "lib/travis/api/v3/services/repositories/for_current_user.rb", "lib/travis/api/v3/services/repository/disable.rb", "lib/travis/api/v3/services/repository/enable.rb", "lib/travis/api/v3/services/repository/find.rb", "lib/travis/api/v3/services/requests/create.rb", "lib/travis/api/v3/services/requests/find.rb", + "lib/travis/api/v3/services/user/current.rb", + "lib/travis/api/v3/services/user/find.rb", "lib/travis/api/workers/build_cancellation.rb", "lib/travis/api/workers/build_restart.rb", "lib/travis/api/workers/job_cancellation.rb", @@ -345,14 +359,19 @@ Gem::Specification.new do |s| "spec/unit/responders/json_spec.rb", "spec/unit/responders/service_spec.rb", "spec/v3/extensions/belongs_to_spec.rb", + "spec/v3/renderer/avatar_url_spec.rb", "spec/v3/result_spec.rb", "spec/v3/service_index_spec.rb", + "spec/v3/services/accounts/for_current_user_spec.rb", "spec/v3/services/branch/find_spec.rb", "spec/v3/services/organization/find_spec.rb", "spec/v3/services/organizations/for_current_user_spec.rb", + "spec/v3/services/owner/find_spec.rb", "spec/v3/services/repositories/for_current_user_spec.rb", "spec/v3/services/repository/find_spec.rb", "spec/v3/services/requests/create_spec.rb", + "spec/v3/services/user/current_spec.rb", + "spec/v3/services/user/find_spec.rb", "tmp/.gitkeep", "travis-api.gemspec" ]