Merge commit 'bddd9952a74d1eddad5d5a255419e07c836b286d' into addCron
Conflicts: spec/v3/services/owner/find_spec.rb spec/v3/services/repositories/for_current_user_spec.rb spec/v3/services/repositories/for_owner_spec.rb spec/v3/services/repository/find_spec.rb
This commit is contained in:
commit
e4aec173ee
1
Gemfile
1
Gemfile
|
@ -33,6 +33,7 @@ gem 'skylight', '~> 0.6.0.beta.1'
|
|||
gem 'stackprof'
|
||||
|
||||
gem 'jemalloc'
|
||||
gem 'customerio'
|
||||
|
||||
group :test do
|
||||
gem 'rspec', '~> 2.13'
|
||||
|
|
32
Gemfile.lock
32
Gemfile.lock
|
@ -36,9 +36,9 @@ GIT
|
|||
|
||||
GIT
|
||||
remote: git://github.com/rtomayko/rack-cache.git
|
||||
revision: d00e6e491fcc7bdca9c27d735abde5c4fdb48cd9
|
||||
revision: f96febebed7700337e8c362403b081e45b8e4f13
|
||||
specs:
|
||||
rack-cache (1.2)
|
||||
rack-cache (1.5.1)
|
||||
rack (>= 0.4)
|
||||
|
||||
GIT
|
||||
|
@ -50,7 +50,7 @@ GIT
|
|||
|
||||
GIT
|
||||
remote: git://github.com/travis-ci/travis-core.git
|
||||
revision: 21793fc8b01f965b93cf98b7ab1458ee359a5a62
|
||||
revision: 3a9f6e4c14bb1eacb93609e1864c9c547a13c1a4
|
||||
specs:
|
||||
travis-core (0.0.1)
|
||||
actionmailer (~> 3.2.19)
|
||||
|
@ -137,7 +137,7 @@ GEM
|
|||
activesupport (3.2.22)
|
||||
i18n (~> 0.6, >= 0.6.4)
|
||||
multi_json (~> 1.0)
|
||||
addressable (2.3.8)
|
||||
addressable (2.4.0)
|
||||
arel (3.0.3)
|
||||
atomic (1.1.99)
|
||||
avl_tree (1.1.3)
|
||||
|
@ -145,7 +145,7 @@ GEM
|
|||
descendants_tracker (~> 0.0.4)
|
||||
ice_nine (~> 0.11.0)
|
||||
thread_safe (~> 0.3, >= 0.3.1)
|
||||
backports (3.6.6)
|
||||
backports (3.6.7)
|
||||
builder (3.0.4)
|
||||
bunny (0.8.0)
|
||||
celluloid (0.16.0)
|
||||
|
@ -157,6 +157,9 @@ GEM
|
|||
composite_primary_keys (5.0.14)
|
||||
activerecord (~> 3.2.0, >= 3.2.9)
|
||||
connection_pool (2.1.1)
|
||||
customerio (0.6.1)
|
||||
httparty (>= 0.5, < 0.12)
|
||||
multi_json (~> 1.0)
|
||||
dalli (2.7.2)
|
||||
data_migrations (0.0.1)
|
||||
activerecord
|
||||
|
@ -171,7 +174,7 @@ GEM
|
|||
erubis (2.7.0)
|
||||
factory_girl (2.4.2)
|
||||
activesupport
|
||||
faraday (0.9.1)
|
||||
faraday (0.9.2)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
ffi (1.9.6)
|
||||
foreman (0.64.0)
|
||||
|
@ -187,7 +190,10 @@ GEM
|
|||
hashr (0.0.22)
|
||||
hike (1.2.3)
|
||||
hitimes (1.2.3)
|
||||
httpclient (2.6.0.1)
|
||||
httparty (0.11.0)
|
||||
multi_json (~> 1.0)
|
||||
multi_xml (>= 0.5.2)
|
||||
httpclient (2.7.0.1)
|
||||
i18n (0.7.0)
|
||||
ice_nine (0.11.1)
|
||||
jemalloc (1.0.1)
|
||||
|
@ -215,6 +221,7 @@ GEM
|
|||
mocha (0.14.0)
|
||||
metaclass (~> 0.0.1)
|
||||
multi_json (1.11.2)
|
||||
multi_xml (0.5.5)
|
||||
multipart-post (2.0.0)
|
||||
net-http-persistent (2.9.4)
|
||||
net-http-pipeline (1.0.1)
|
||||
|
@ -225,10 +232,11 @@ GEM
|
|||
coderay (~> 1.1.0)
|
||||
method_source (~> 0.8.1)
|
||||
slop (~> 3.4)
|
||||
pusher (0.14.5)
|
||||
pusher (0.14.6)
|
||||
httpclient (~> 2.5)
|
||||
multi_json (~> 1.0)
|
||||
signature (~> 0.1.8)
|
||||
pusher-signature (~> 0.1.8)
|
||||
pusher-signature (0.1.8)
|
||||
rack (1.4.7)
|
||||
rack-attack (4.2.0)
|
||||
rack
|
||||
|
@ -255,7 +263,7 @@ GEM
|
|||
rdoc (3.12.2)
|
||||
json (~> 1.4)
|
||||
redcarpet (2.3.0)
|
||||
redis (3.2.1)
|
||||
redis (3.2.2)
|
||||
redis-namespace (1.5.1)
|
||||
redis (~> 3.0, >= 3.0.4)
|
||||
rerun (0.8.2)
|
||||
|
@ -275,7 +283,6 @@ GEM
|
|||
json
|
||||
redis (>= 3.0.6)
|
||||
redis-namespace (>= 1.3.1)
|
||||
signature (0.1.8)
|
||||
simple_states (1.0.1)
|
||||
activesupport
|
||||
hashr (~> 0.0.10)
|
||||
|
@ -315,7 +322,7 @@ GEM
|
|||
treetop (1.4.15)
|
||||
polyglot
|
||||
polyglot (>= 0.3.1)
|
||||
tzinfo (0.3.44)
|
||||
tzinfo (0.3.46)
|
||||
unicorn (4.8.3)
|
||||
kgio (~> 2.6)
|
||||
rack
|
||||
|
@ -335,6 +342,7 @@ PLATFORMS
|
|||
DEPENDENCIES
|
||||
active_model_serializers
|
||||
bunny (~> 0.8.0)
|
||||
customerio
|
||||
dalli
|
||||
database_cleaner (~> 0.8.0)
|
||||
factory_girl (~> 2.4.0)
|
||||
|
|
|
@ -17,7 +17,8 @@ This is the app running on https://api.travis-ci.org/
|
|||
|
||||
### Database setup
|
||||
|
||||
1. `rake db:create db:structure:load`
|
||||
1. `rake db:create db:migrate`
|
||||
2. for testing 'RAILS_ENV=test bundle exec rake db:create db:migrate --trace'
|
||||
1. Clone `travis-logs` and copy the `logs` database (assume the PostgreSQL user is `postgres`):
|
||||
```sh-session
|
||||
cd ..
|
||||
|
|
|
@ -2,6 +2,7 @@ require 'travis/api/app'
|
|||
require 'addressable/uri'
|
||||
require 'faraday'
|
||||
require 'securerandom'
|
||||
require 'customerio'
|
||||
|
||||
class Travis::Api::App
|
||||
class Endpoint
|
||||
|
@ -78,6 +79,7 @@ class Travis::Api::App
|
|||
#
|
||||
# * **github_token**: GitHub token for checking authorization (required)
|
||||
post '/github' do
|
||||
check_agent
|
||||
unless params[:github_token]
|
||||
halt 422, { "error" => "Must pass 'github_token' parameter" }
|
||||
end
|
||||
|
@ -94,6 +96,7 @@ class Travis::Api::App
|
|||
# * **redirect_uri**: URI to redirect to after handshake.
|
||||
get '/handshake' do
|
||||
handshake do |user, token, redirect_uri|
|
||||
|
||||
if target_ok? redirect_uri
|
||||
content_type :html
|
||||
data = { user: user, token: token, uri: redirect_uri }
|
||||
|
@ -145,6 +148,43 @@ class Travis::Api::App
|
|||
|
||||
private
|
||||
|
||||
def allowed_agents
|
||||
@allowed_agents ||= redis.smembers('auth_agents')
|
||||
end
|
||||
|
||||
def check_agent
|
||||
return if settings.test? or allowed_agents.empty?
|
||||
return if allowed_agents.any? { |a| request.user_agent.to_s.start_with? a }
|
||||
halt 403, "you are currently not allowed to perform this request. please contact support@travis-ci.com."
|
||||
end
|
||||
|
||||
# update first login date if not set
|
||||
def update_first_login(user)
|
||||
unless user.first_logged_in_at
|
||||
user.update_attributes(first_logged_in_at: Time.now)
|
||||
end
|
||||
end
|
||||
|
||||
def update_customerio(user)
|
||||
return unless Travis.config.customerio.site_id
|
||||
|
||||
# send event to customer.io
|
||||
payload = {
|
||||
:id => user.id,
|
||||
:name => user.name,
|
||||
:login => user.login,
|
||||
:email => primary_email_for_user(user.github_oauth_token),
|
||||
:created_at => user.created_at.to_i,
|
||||
:github_id => user.github_id,
|
||||
:education => user.education,
|
||||
:first_logged_in_at => user.first_logged_in_at.to_i
|
||||
}
|
||||
|
||||
customerio.identify(payload)
|
||||
rescue StandardError => e
|
||||
Travis.logger.error "Could not update Customer.io for User: #{user.id}:#{user.login} with message:#{e.message}"
|
||||
end
|
||||
|
||||
def serialize_user(user)
|
||||
rendered = Travis::Api.data(user, version: :v2)
|
||||
rendered['user'].merge('token' => user.tokens.first.try(:token).to_s)
|
||||
|
@ -174,6 +214,8 @@ class Travis::Api::App
|
|||
user = user_for_github_token(github_token)
|
||||
token = generate_token(user: user, app_id: 0)
|
||||
payload = params[:state].split(":::", 2)[1]
|
||||
update_first_login(user)
|
||||
update_customerio(user)
|
||||
yield serialize_user(user), token, payload
|
||||
else
|
||||
values[:state] = create_state
|
||||
|
@ -183,6 +225,7 @@ class Travis::Api::App
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
def create_state
|
||||
state = SecureRandom.urlsafe_base64(16)
|
||||
redis.sadd('github:states', state)
|
||||
|
@ -212,6 +255,7 @@ class Travis::Api::App
|
|||
super
|
||||
|
||||
@user = ::User.find_by_github_id(data['id'])
|
||||
|
||||
end
|
||||
|
||||
def info(attributes = {})
|
||||
|
@ -342,6 +386,15 @@ class Travis::Api::App
|
|||
def allowed_https_targets
|
||||
@allowed_https_targets ||= Travis.config.auth.target_origin.to_s.split(',')
|
||||
end
|
||||
|
||||
def primary_email_for_user(oauth_token)
|
||||
# check for the users primary email address (we don't store this info)
|
||||
GH.with(token: oauth_token, client_id: nil) { GH['user/emails'] }.select { |e| e['primary'] }.first['email']
|
||||
end
|
||||
|
||||
def customerio
|
||||
Customerio::Client.new(Travis.config.customerio.site_id, Travis.config.customerio.api_key, :json => true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -53,6 +53,8 @@ class Travis::Api::App
|
|||
if params
|
||||
params = Hash[*params.split(';').map { |p| p.scan /(#{TOKEN})=(#{TOKEN})/ }.flatten]
|
||||
quality = params.delete('q').to_f if params['q']
|
||||
else
|
||||
params = {}
|
||||
end
|
||||
|
||||
if subtype =~ HEADER_FORMAT
|
||||
|
|
|
@ -37,7 +37,7 @@ class Travis::Api::App
|
|||
end
|
||||
|
||||
def mark_travis(agent)
|
||||
command = agent.application.comment.detect { |c| c.start_with? "command " }
|
||||
command = agent.application.comment.detect { |c| c.start_with? "command " } if agent.application.comment
|
||||
|
||||
if command
|
||||
mark(:cli, :version, agent.version)
|
||||
|
|
|
@ -31,6 +31,12 @@ class Rack::Attack
|
|||
"/auth/post_message/iframe"
|
||||
]
|
||||
|
||||
####
|
||||
# Whitelisted IP addresses
|
||||
whitelist('whitelist client requesting from redis') do |request|
|
||||
Travis.redis.sismember(:api_whitelisted_ips, request.ip)
|
||||
end
|
||||
|
||||
####
|
||||
# Ban based on: IP address
|
||||
# Ban time: indefinite
|
||||
|
@ -44,7 +50,7 @@ class Rack::Attack
|
|||
# Ban time: 5 hours
|
||||
# Ban after: 10 POST requests within five minutes to /auth/github
|
||||
blacklist('hammering /auth/github') do |request|
|
||||
Rack::Attack::Allow2Ban.filter(request.identifier, maxretry: 10, findtime: 5.minutes, bantime: bantime(5.hours)) do
|
||||
Rack::Attack::Allow2Ban.filter(request.identifier, maxretry: 2, findtime: 5.minutes, bantime: bantime(5.hours)) do
|
||||
request.post? and request.path == '/auth/github'
|
||||
end
|
||||
end
|
||||
|
@ -59,6 +65,14 @@ class Rack::Attack
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
###
|
||||
# Throttle: unauthenticated requests to /auth/github - 1 per minute
|
||||
# Scoped by: IP address
|
||||
throttle('req/ip/1min', limit: 1, period: 1.minute) do |request|
|
||||
request.ip unless request.authenticated? and request.path == '/auth/github'
|
||||
end
|
||||
|
||||
###
|
||||
# Throttle: unauthenticated requests - 500 per minute
|
||||
# Scoped by: IP address
|
||||
|
|
|
@ -6,6 +6,7 @@ module Travis::API::V3
|
|||
has_many :builds, dependent: :delete_all, order: 'builds.id DESC'.freeze
|
||||
has_many :permissions, dependent: :delete_all
|
||||
has_many :users, through: :permissions
|
||||
has_many :stars
|
||||
|
||||
belongs_to :owner, polymorphic: true
|
||||
belongs_to :last_build, class_name: 'Travis::API::V3::Models::Build'.freeze
|
||||
|
@ -49,5 +50,17 @@ module Travis::API::V3
|
|||
rescue ActiveRecord::RecordNotUnique
|
||||
branches.where(name: name).first
|
||||
end
|
||||
|
||||
def id_default_branch
|
||||
[id, default_branch_name]
|
||||
end
|
||||
|
||||
def send(name, *args, &block)
|
||||
if name == [:id, :default_branch]
|
||||
name = :id_default_branch
|
||||
end
|
||||
|
||||
__send__(name, *args, &block)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
6
lib/travis/api/v3/models/star.rb
Normal file
6
lib/travis/api/v3/models/star.rb
Normal file
|
@ -0,0 +1,6 @@
|
|||
module Travis::API::V3
|
||||
class Models::Star < Model
|
||||
belongs_to :user
|
||||
belongs_to :repository
|
||||
end
|
||||
end
|
|
@ -6,6 +6,7 @@ module Travis::API::V3
|
|||
has_many :tokens, dependent: :destroy
|
||||
has_many :organizations, through: :memberships
|
||||
has_many :repositories, as: :owner
|
||||
has_many :stars
|
||||
has_one :subscription, as: :owner
|
||||
|
||||
serialize :github_oauth_token, Extensions::EncryptedColumn.new(disable: true)
|
||||
|
@ -17,5 +18,9 @@ module Travis::API::V3
|
|||
def subscription
|
||||
super if Features.use_subscriptions?
|
||||
end
|
||||
|
||||
def starred_repository_ids
|
||||
@starred_repository_ids ||= stars.map(&:repository_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -10,6 +10,14 @@ module Travis::API::V3
|
|||
write?
|
||||
end
|
||||
|
||||
def star?
|
||||
write?
|
||||
end
|
||||
|
||||
def unstar?
|
||||
write?
|
||||
end
|
||||
|
||||
def create_request?
|
||||
write?
|
||||
end
|
||||
|
|
|
@ -1,26 +1,34 @@
|
|||
module Travis::API::V3
|
||||
class Queries::Repositories < Query
|
||||
params :active, :private, prefix: :repository
|
||||
params :active, :private, :starred, prefix: :repository
|
||||
sortable_by :id, :github_id, :owner_name, :name, active: sort_condition(:active)
|
||||
|
||||
def for_member(user)
|
||||
all.joins(:users).where(users: user_condition(user), invalidated_at: nil)
|
||||
def for_member(user, **options)
|
||||
all(user: user, **options).joins(:users).where(users: user_condition(user), invalidated_at: nil)
|
||||
end
|
||||
|
||||
def for_owner(owner)
|
||||
filter(owner.repositories)
|
||||
def for_owner(owner, **options)
|
||||
filter(owner.repositories, **options)
|
||||
end
|
||||
|
||||
def all
|
||||
@all ||= filter(Models::Repository)
|
||||
def all(**options)
|
||||
filter(Models::Repository, **options)
|
||||
end
|
||||
|
||||
def filter(list)
|
||||
def filter(list, user: nil)
|
||||
list = list.where(invalidated_at: nil)
|
||||
list = list.where(active: bool(active)) unless active.nil?
|
||||
list = list.where(private: bool(private)) unless private.nil?
|
||||
list = list.includes(:owner) if includes? 'repository.owner'.freeze
|
||||
|
||||
if user and not starred.nil?
|
||||
if bool(starred)
|
||||
list = list.joins(:stars).where(stars: { user_id: user.id })
|
||||
elsif user.starred_repository_ids.any?
|
||||
list = list.where("repositories.id NOT IN (?)", user.starred_repository_ids)
|
||||
end
|
||||
end
|
||||
|
||||
if includes? 'repository.last_build'.freeze or includes? 'build'.freeze
|
||||
list = list.includes(:last_build)
|
||||
list = list.includes(last_build: :commit) if includes? 'build.commit'.freeze
|
||||
|
|
|
@ -3,13 +3,31 @@ module Travis::API::V3
|
|||
params :id, :slug
|
||||
|
||||
def find
|
||||
@find ||= find!
|
||||
end
|
||||
|
||||
def star(current_user)
|
||||
repository = find
|
||||
starred = Models::Star.where(repository_id: repository.id, user_id: current_user.id).first
|
||||
Models::Star.create(repository_id: repository.id, user_id: current_user.id) unless starred
|
||||
repository
|
||||
end
|
||||
|
||||
def unstar(current_user)
|
||||
repository = find
|
||||
starred = Models::Star.where(repository_id: repository.id, user_id: current_user.id).first
|
||||
starred.delete if starred
|
||||
repository
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def find!
|
||||
return by_slug if slug
|
||||
return Models::Repository.find_by_id(id) if id
|
||||
raise WrongParams, 'missing repository.id'.freeze
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def by_slug
|
||||
owner_name, name = slug.split('/')
|
||||
Models::Repository.where(owner_name: owner_name, name: name, invalidated_at: nil).first
|
||||
|
|
|
@ -3,7 +3,7 @@ require 'travis/api/v3/renderer/model_renderer'
|
|||
module Travis::API::V3
|
||||
class Renderer::Repository < Renderer::ModelRenderer
|
||||
representation(:minimal, :id, :name, :slug)
|
||||
representation(:standard, :id, :name, :slug, :description, :github_language, :active, :private, :owner, :default_branch)
|
||||
representation(:standard, :id, :name, :slug, :description, :github_language, :active, :private, :owner, :default_branch, :starred)
|
||||
|
||||
def active
|
||||
!!model.active
|
||||
|
@ -19,6 +19,11 @@ module Travis::API::V3
|
|||
}
|
||||
end
|
||||
|
||||
def starred
|
||||
return false unless user = access_control.user
|
||||
user.starred_repository_ids.include? id
|
||||
end
|
||||
|
||||
def include_default_branch?
|
||||
return true if include? 'repository.default_branch'.freeze
|
||||
return true if include.any? { |i| i.start_with? 'branch'.freeze }
|
||||
|
|
|
@ -78,6 +78,8 @@ module Travis::API::V3
|
|||
|
||||
post :enable, '/enable'
|
||||
post :disable, '/disable'
|
||||
post :star, '/star'
|
||||
post :unstar, '/unstar'
|
||||
|
||||
resource :branch do
|
||||
route '/branch/{branch.name}'
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module Travis::API::V3
|
||||
class Services::Repositories::ForCurrentUser < Service
|
||||
params :active, :private, prefix: :repository
|
||||
params :active, :private, :starred, prefix: :repository
|
||||
paginate(default_limit: 100)
|
||||
|
||||
def run!
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
module Travis::API::V3
|
||||
class Services::Repositories::ForOwner < Service
|
||||
params :active, :private, prefix: :repository
|
||||
params :active, :private, :starred, prefix: :repository
|
||||
paginate(default_limit: 100)
|
||||
|
||||
def run!
|
||||
unfiltered = query.for_owner(find(:owner))
|
||||
unfiltered = query.for_owner(find(:owner), user: access_control.user)
|
||||
access_control.visible_repositories(unfiltered)
|
||||
end
|
||||
end
|
||||
|
|
15
lib/travis/api/v3/services/repository/star.rb
Normal file
15
lib/travis/api/v3/services/repository/star.rb
Normal file
|
@ -0,0 +1,15 @@
|
|||
module Travis::API::V3
|
||||
class Services::Repository::Star < Service
|
||||
def run!
|
||||
raise LoginRequired unless access_control.logged_in? or access_control.full_access?
|
||||
raise NotFound unless repository = find(:repository)
|
||||
check_access(repository)
|
||||
current_user = access_control.user
|
||||
query.star(current_user)
|
||||
end
|
||||
|
||||
def check_access(repository)
|
||||
access_control.permissions(repository).star!
|
||||
end
|
||||
end
|
||||
end
|
15
lib/travis/api/v3/services/repository/unstar.rb
Normal file
15
lib/travis/api/v3/services/repository/unstar.rb
Normal file
|
@ -0,0 +1,15 @@
|
|||
module Travis::API::V3
|
||||
class Services::Repository::Unstar < Service
|
||||
def run!
|
||||
raise LoginRequired unless access_control.logged_in? or access_control.full_access?
|
||||
raise NotFound unless repository = find(:repository)
|
||||
check_access(repository)
|
||||
current_user = access_control.user
|
||||
query.unstar(current_user)
|
||||
end
|
||||
|
||||
def check_access(repository)
|
||||
access_control.permissions(repository).unstar!
|
||||
end
|
||||
end
|
||||
end
|
|
@ -64,7 +64,7 @@ describe Travis::API::V3::ServiceIndex do
|
|||
|
||||
describe "for_current_user action" do
|
||||
let(:action) { resource.fetch("actions").fetch("for_current_user") }
|
||||
specify { expect(action).to include("@type"=>"template", "request_method"=>"GET", "uri_template"=>"#{path}repos{?active,include,limit,offset,private,repository.active,repository.private,sort_by}") }
|
||||
specify { expect(action).to include("@type"=>"template", "request_method"=>"GET", "uri_template"=>"#{path}repos{?active,include,limit,offset,private,repository.active,repository.private,repository.starred,sort_by,starred}") }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -64,8 +64,10 @@ describe Travis::API::V3::Services::Owner::Find do
|
|||
"read" => true,
|
||||
"enable" => false,
|
||||
"disable" => false,
|
||||
"star" => false,
|
||||
"unstar" => false,
|
||||
"create_request" => false,
|
||||
"create_cron" => false },
|
||||
"create_cron" => false},
|
||||
"id" => repo.id,
|
||||
"name" => "example-repo",
|
||||
"slug" => "example-org/example-repo",
|
||||
|
@ -78,7 +80,8 @@ describe Travis::API::V3::Services::Owner::Find do
|
|||
"@type" => "branch",
|
||||
"@href" => "/v3/repo/#{repo.id}/branch/master",
|
||||
"@representation" => "minimal",
|
||||
"name" => "master"}
|
||||
"name" => "master"},
|
||||
"starred" => false
|
||||
}]
|
||||
}}
|
||||
end
|
||||
|
@ -109,8 +112,10 @@ describe Travis::API::V3::Services::Owner::Find do
|
|||
"read" => true,
|
||||
"enable" => false,
|
||||
"disable" => false,
|
||||
"star" => false,
|
||||
"unstar" => false,
|
||||
"create_request"=> false,
|
||||
"create_cron" => false },
|
||||
"create_cron" => false},
|
||||
"id" => repo.id,
|
||||
"name" => "example-repo",
|
||||
"slug" => "example-org/example-repo",
|
||||
|
@ -123,7 +128,8 @@ describe Travis::API::V3::Services::Owner::Find do
|
|||
"@type" => "branch",
|
||||
"@href" => "/v3/repo/#{repo.id}/branch/master",
|
||||
"@representation"=> "minimal",
|
||||
"name" => "master"}
|
||||
"name" => "master"},
|
||||
"starred" => false
|
||||
}]
|
||||
}}
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Travis::API::V3::Services::Repositories::ForCurrentUser do
|
||||
let(:repo) { Travis::API::V3::Models::Repository.where(owner_name: 'svenfuchs', name: 'minimal').first }
|
||||
let(:repo) { Travis::API::V3::Models::Repository.where(owner_name: 'svenfuchs', name: 'minimal').first }
|
||||
let(:build) { repo.builds.first }
|
||||
let(:jobs) { Travis::API::V3::Models::Build.find(build.id).jobs }
|
||||
|
||||
|
@ -42,8 +42,10 @@ describe Travis::API::V3::Services::Repositories::ForCurrentUser do
|
|||
"read" => true,
|
||||
"enable" => true,
|
||||
"disable" => true,
|
||||
"star" => true,
|
||||
"unstar" => true,
|
||||
"create_request" => true,
|
||||
"create_cron" => true },
|
||||
"create_cron" => true},
|
||||
"id" => repo.id,
|
||||
"name" => "minimal",
|
||||
"slug" => "svenfuchs/minimal",
|
||||
|
@ -60,7 +62,9 @@ describe Travis::API::V3::Services::Repositories::ForCurrentUser do
|
|||
"@type" => "branch",
|
||||
"@href" => "/v3/repo/#{repo.id}/branch/master",
|
||||
"@representation" => "minimal",
|
||||
"name" => "master"}}]
|
||||
"name" => "master"},
|
||||
"starred" => false
|
||||
}]
|
||||
}}
|
||||
end
|
||||
|
||||
|
@ -82,4 +86,29 @@ describe Travis::API::V3::Services::Repositories::ForCurrentUser do
|
|||
example { expect(last_response) .to be_ok }
|
||||
example { expect(JSON.load(body)['repositories']) .to be == [] }
|
||||
end
|
||||
|
||||
describe "filter: starred=true" do
|
||||
before { Travis::API::V3::Models::Star.create(user: repo.owner, repository: repo) }
|
||||
before { get("/v3/repos", {"starred" => "true"}, headers) }
|
||||
after { repo.owner.stars.each(&:destroy) }
|
||||
example { expect(last_response) .to be_ok }
|
||||
example { expect(JSON.load(body)['@href']) .to be == "/v3/repos?starred=true" }
|
||||
example { expect(JSON.load(body)['repositories']) .not_to be_empty }
|
||||
end
|
||||
|
||||
describe "filter: starred=false" do
|
||||
before { get("/v3/repos", {"starred" => "false"}, headers) }
|
||||
example { expect(last_response) .to be_ok }
|
||||
example { expect(JSON.load(body)['@href']) .to be == "/v3/repos?starred=false" }
|
||||
example { expect(JSON.load(body)['repositories']) .not_to be_empty }
|
||||
end
|
||||
|
||||
describe "filter: starred=false but no unstarred repos" do
|
||||
before { Travis::API::V3::Models::Star.create(user: repo.owner, repository: repo) }
|
||||
after { repo.owner.stars.each(&:destroy) }
|
||||
before { get("/v3/repos", {"starred" => "false"}, headers) }
|
||||
example { expect(last_response) .to be_ok }
|
||||
example { expect(JSON.load(body)['@href']) .to be == "/v3/repos?starred=false" }
|
||||
example { expect(JSON.load(body)['repositories']) .to be_empty }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -42,8 +42,10 @@ describe Travis::API::V3::Services::Repositories::ForOwner do
|
|||
"read" => true,
|
||||
"enable" => false,
|
||||
"disable" => false,
|
||||
"star" => false,
|
||||
"unstar" => false,
|
||||
"create_request" => false,
|
||||
"create_cron" => false },
|
||||
"create_cron" => false},
|
||||
"id" => repo.id,
|
||||
"name" => "minimal",
|
||||
"slug" => "svenfuchs/minimal",
|
||||
|
@ -60,7 +62,9 @@ describe Travis::API::V3::Services::Repositories::ForOwner do
|
|||
"@type" => "branch",
|
||||
"@href" => "/v3/repo/#{repo.id}/branch/master",
|
||||
"@representation" => "minimal",
|
||||
"name" => "master"}}]}}
|
||||
"name" => "master"},
|
||||
"starred" => false
|
||||
}]}}
|
||||
end
|
||||
|
||||
describe "filter: private=false" do
|
||||
|
@ -75,4 +79,29 @@ describe Travis::API::V3::Services::Repositories::ForOwner do
|
|||
example { expect(last_response) .to be_ok }
|
||||
example { expect(JSON.load(body)['repositories']) .to be == [] }
|
||||
end
|
||||
|
||||
describe "filter: starred=true" do
|
||||
before { Travis::API::V3::Models::Star.create(user: repo.owner, repository: repo) }
|
||||
before { get("/v3/repos", {"starred" => "true"}, headers) }
|
||||
after { repo.owner.stars.each(&:destroy) }
|
||||
example { expect(last_response) .to be_ok }
|
||||
example { expect(JSON.load(body)['@href']) .to be == "/v3/repos?starred=true" }
|
||||
example { expect(JSON.load(body)['repositories']) .not_to be_empty }
|
||||
end
|
||||
|
||||
describe "filter: starred=false" do
|
||||
before { get("/v3/repos", {"starred" => "false"}, headers) }
|
||||
example { expect(last_response) .to be_ok }
|
||||
example { expect(JSON.load(body)['@href']) .to be == "/v3/repos?starred=false" }
|
||||
example { expect(JSON.load(body)['repositories']) .not_to be_empty }
|
||||
end
|
||||
|
||||
describe "filter: starred=false but no unstarred repos" do
|
||||
before { Travis::API::V3::Models::Star.create(user: repo.owner, repository: repo) }
|
||||
after { repo.owner.stars.each(&:destroy) }
|
||||
before { get("/v3/repos", {"starred" => "false"}, headers) }
|
||||
example { expect(last_response) .to be_ok }
|
||||
example { expect(JSON.load(body)['@href']) .to be == "/v3/repos?starred=false" }
|
||||
example { expect(JSON.load(body)['repositories']) .to be_empty }
|
||||
end
|
||||
end
|
||||
|
|
72
spec/v3/services/repository/disable_spec.rb
Normal file
72
spec/v3/services/repository/disable_spec.rb
Normal file
|
@ -0,0 +1,72 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Travis::API::V3::Services::Repository::Disable do
|
||||
let(:repo) { Travis::API::V3::Models::Repository.where(owner_name: 'svenfuchs', name: 'minimal').first }
|
||||
|
||||
before do
|
||||
repo.update_attributes!(active: true)
|
||||
end
|
||||
|
||||
describe "not authenticated" do
|
||||
before { post("/v3/repo/#{repo.id}/disable") }
|
||||
example { expect(last_response.status).to be == 403 }
|
||||
example { expect(JSON.load(body)).to be == {
|
||||
"@type" => "error",
|
||||
"error_type" => "login_required",
|
||||
"error_message" => "login required"
|
||||
}}
|
||||
end
|
||||
|
||||
describe "missing repo, authenticated" do
|
||||
let(:token) { Travis::Api::App::AccessToken.create(user: repo.owner, app_id: 1) }
|
||||
let(:headers) {{ 'HTTP_AUTHORIZATION' => "token #{token}" }}
|
||||
before { post("/v3/repo/9999999999/disable", {}, headers) }
|
||||
|
||||
example { expect(last_response.status).to be == 404 }
|
||||
example { expect(JSON.load(body)).to be == {
|
||||
"@type" => "error",
|
||||
"error_type" => "not_found",
|
||||
"error_message" => "repository not found (or insufficient access)",
|
||||
"resource_type" => "repository"
|
||||
}}
|
||||
end
|
||||
|
||||
describe "existing repository, no push access" do
|
||||
let(:token) { Travis::Api::App::AccessToken.create(user: repo.owner, app_id: 1) }
|
||||
let(:headers) {{ 'HTTP_AUTHORIZATION' => "token #{token}" }}
|
||||
before { post("/v3/repo/#{repo.id}/disable", {}, headers) }
|
||||
|
||||
example { expect(last_response.status).to be == 403 }
|
||||
example { expect(JSON.load(body).to_s).to include(
|
||||
"@type",
|
||||
"error_type",
|
||||
"insufficient_access",
|
||||
"error_message",
|
||||
"operation requires disable access to repository",
|
||||
"resource_type",
|
||||
"repository",
|
||||
"permission",
|
||||
"disable")
|
||||
}
|
||||
end
|
||||
|
||||
describe "private repository, no access" do
|
||||
let(:token) { Travis::Api::App::AccessToken.create(user: repo.owner, app_id: 1) }
|
||||
let(:headers) {{ 'HTTP_AUTHORIZATION' => "token #{token}" }}
|
||||
before { repo.update_attribute(:private, true) }
|
||||
before { post("/v3/repo/#{repo.id}/disable", {}, headers) }
|
||||
after { repo.update_attribute(:private, false) }
|
||||
|
||||
example { expect(last_response.status).to be == 404 }
|
||||
example { expect(JSON.load(body)).to be == {
|
||||
"@type" => "error",
|
||||
"error_type" => "not_found",
|
||||
"error_message" => "repository not found (or insufficient access)",
|
||||
"resource_type" => "repository"
|
||||
}}
|
||||
end
|
||||
|
||||
describe "existing repository, push access"
|
||||
# as this reqires a call to github, and stubbing this request has proven difficult,
|
||||
# this test has been omitted for now
|
||||
end
|
72
spec/v3/services/repository/enable_spec.rb
Normal file
72
spec/v3/services/repository/enable_spec.rb
Normal file
|
@ -0,0 +1,72 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Travis::API::V3::Services::Repository::Enable do
|
||||
let(:repo) { Travis::API::V3::Models::Repository.where(owner_name: 'svenfuchs', name: 'minimal').first }
|
||||
|
||||
before do
|
||||
repo.update_attributes!(active: false)
|
||||
end
|
||||
|
||||
describe "not authenticated" do
|
||||
before { post("/v3/repo/#{repo.id}/enable") }
|
||||
example { expect(last_response.status).to be == 403 }
|
||||
example { expect(JSON.load(body)).to be == {
|
||||
"@type" => "error",
|
||||
"error_type" => "login_required",
|
||||
"error_message" => "login required"
|
||||
}}
|
||||
end
|
||||
|
||||
describe "missing repo, authenticated" do
|
||||
let(:token) { Travis::Api::App::AccessToken.create(user: repo.owner, app_id: 1) }
|
||||
let(:headers) {{ 'HTTP_AUTHORIZATION' => "token #{token}" }}
|
||||
before { post("/v3/repo/9999999999/enable", {}, headers) }
|
||||
|
||||
example { expect(last_response.status).to be == 404 }
|
||||
example { expect(JSON.load(body)).to be == {
|
||||
"@type" => "error",
|
||||
"error_type" => "not_found",
|
||||
"error_message" => "repository not found (or insufficient access)",
|
||||
"resource_type" => "repository"
|
||||
}}
|
||||
end
|
||||
|
||||
describe "existing repository, no push access" do
|
||||
let(:token) { Travis::Api::App::AccessToken.create(user: repo.owner, app_id: 1) }
|
||||
let(:headers) {{ 'HTTP_AUTHORIZATION' => "token #{token}" }}
|
||||
before { post("/v3/repo/#{repo.id}/enable", {}, headers) }
|
||||
|
||||
example { expect(last_response.status).to be == 403 }
|
||||
example { expect(JSON.load(body).to_s).to include(
|
||||
"@type",
|
||||
"error_type",
|
||||
"insufficient_access",
|
||||
"error_message",
|
||||
"operation requires enable access to repository",
|
||||
"resource_type",
|
||||
"repository",
|
||||
"permission",
|
||||
"enable")
|
||||
}
|
||||
end
|
||||
|
||||
describe "private repository, no access" do
|
||||
let(:token) { Travis::Api::App::AccessToken.create(user: repo.owner, app_id: 1) }
|
||||
let(:headers) {{ 'HTTP_AUTHORIZATION' => "token #{token}" }}
|
||||
before { repo.update_attribute(:private, true) }
|
||||
before { post("/v3/repo/#{repo.id}/enable", {}, headers) }
|
||||
after { repo.update_attribute(:private, false) }
|
||||
|
||||
example { expect(last_response.status).to be == 404 }
|
||||
example { expect(JSON.load(body)).to be == {
|
||||
"@type" => "error",
|
||||
"error_type" => "not_found",
|
||||
"error_message" => "repository not found (or insufficient access)",
|
||||
"resource_type" => "repository"
|
||||
}}
|
||||
end
|
||||
|
||||
describe "existing repository, push access"
|
||||
# as this reqires a call to github, and stubbing this request has proven difficult,
|
||||
# this test has been omitted for now
|
||||
end
|
|
@ -34,8 +34,10 @@ describe Travis::API::V3::Services::Repository::Find do
|
|||
"read" => true,
|
||||
"enable" => false,
|
||||
"disable" => false,
|
||||
"star" => false,
|
||||
"unstar" => false,
|
||||
"create_request" => false,
|
||||
"create_cron" => false },
|
||||
"create_cron" => false},
|
||||
"id" => repo.id,
|
||||
"name" => "minimal",
|
||||
"slug" => "svenfuchs/minimal",
|
||||
|
@ -52,7 +54,8 @@ describe Travis::API::V3::Services::Repository::Find do
|
|||
"@type" => "branch",
|
||||
"@href" => "/v3/repo/#{repo.id}/branch/master",
|
||||
"@representation" => "minimal",
|
||||
"name" => "master"}
|
||||
"name" => "master"},
|
||||
"starred" => false
|
||||
}}
|
||||
end
|
||||
|
||||
|
@ -109,8 +112,10 @@ describe Travis::API::V3::Services::Repository::Find do
|
|||
"read" => true,
|
||||
"enable" => false,
|
||||
"disable" => false,
|
||||
"star" => false,
|
||||
"unstar" => false,
|
||||
"create_request" => false,
|
||||
"create_cron" => false },
|
||||
"create_cron" => false},
|
||||
"id" => repo.id,
|
||||
"name" => "minimal",
|
||||
"slug" => "svenfuchs/minimal",
|
||||
|
@ -127,7 +132,8 @@ describe Travis::API::V3::Services::Repository::Find do
|
|||
"@type" => "branch",
|
||||
"@href" => "/v3/repo/#{repo.id}/branch/master",
|
||||
"@representation" => "minimal",
|
||||
"name" => "master"}
|
||||
"name" => "master"},
|
||||
"starred" => false
|
||||
}}
|
||||
end
|
||||
|
||||
|
@ -169,8 +175,10 @@ describe Travis::API::V3::Services::Repository::Find do
|
|||
"read" => true,
|
||||
"enable" => true,
|
||||
"disable" => true,
|
||||
"star" => true,
|
||||
"unstar" => true,
|
||||
"create_request" => true,
|
||||
"create_cron" => true },
|
||||
"create_cron" => true},
|
||||
"id" => repo.id,
|
||||
"name" => "minimal",
|
||||
"slug" => "svenfuchs/minimal",
|
||||
|
@ -187,7 +195,8 @@ describe Travis::API::V3::Services::Repository::Find do
|
|||
"@type" => "branch",
|
||||
"@href" => "/v3/repo/#{repo.id}/branch/master",
|
||||
"@representation" => "minimal",
|
||||
"name" => "master"}
|
||||
"name" => "master"},
|
||||
"starred" => false
|
||||
}}
|
||||
end
|
||||
|
||||
|
@ -235,8 +244,10 @@ describe Travis::API::V3::Services::Repository::Find do
|
|||
"read" => true,
|
||||
"enable" => true,
|
||||
"disable" => true,
|
||||
"star" => true,
|
||||
"unstar" => true,
|
||||
"create_request" => true,
|
||||
"create_cron" => true },
|
||||
"create_cron" => true},
|
||||
"id" => repo.id,
|
||||
"name" => "minimal",
|
||||
"slug" => "svenfuchs/minimal",
|
||||
|
@ -253,7 +264,8 @@ describe Travis::API::V3::Services::Repository::Find do
|
|||
"@type" => "branch",
|
||||
"@href" => "/v3/repo/#{repo.id}/branch/master",
|
||||
"@representation" => "minimal",
|
||||
"name" => "master"}
|
||||
"name" => "master"},
|
||||
"starred" => false
|
||||
}}
|
||||
end
|
||||
|
||||
|
|
68
spec/v3/services/repository/star_spec.rb
Normal file
68
spec/v3/services/repository/star_spec.rb
Normal file
|
@ -0,0 +1,68 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Travis::API::V3::Services::Repository::Star do
|
||||
let(:repo) { Travis::API::V3::Models::Repository.where(owner_name: 'svenfuchs', name: 'minimal').first }
|
||||
|
||||
describe "not authenticated" do
|
||||
before { post("/v3/repo/#{repo.id}/star") }
|
||||
example { expect(last_response.status).to be == 403 }
|
||||
example { expect(JSON.load(body)).to be == {
|
||||
"@type" => "error",
|
||||
"error_type" => "login_required",
|
||||
"error_message" => "login required"
|
||||
}}
|
||||
end
|
||||
|
||||
describe "missing repo, authenticated" do
|
||||
let(:token) { Travis::Api::App::AccessToken.create(user: repo.owner, app_id: 1) }
|
||||
let(:headers) {{ 'HTTP_AUTHORIZATION' => "token #{token}" }}
|
||||
before { post("/v3/repo/9999999999/star", {}, headers) }
|
||||
|
||||
example { expect(last_response.status).to be == 404 }
|
||||
example { expect(JSON.load(body)).to be == {
|
||||
"@type" => "error",
|
||||
"error_type" => "not_found",
|
||||
"error_message" => "repository not found (or insufficient access)",
|
||||
"resource_type" => "repository"
|
||||
}}
|
||||
end
|
||||
|
||||
describe "existing repository, no push access" do
|
||||
let(:token) { Travis::Api::App::AccessToken.create(user: repo.owner, app_id: 1) }
|
||||
let(:headers) {{ 'HTTP_AUTHORIZATION' => "token #{token}" }}
|
||||
before { post("/v3/repo/#{repo.id}/star", {}, headers) }
|
||||
|
||||
example { expect(last_response.status).to be == 403 }
|
||||
example { expect(JSON.load(body).to_s).to include(
|
||||
"@type",
|
||||
"error_type",
|
||||
"insufficient_access",
|
||||
"error_message",
|
||||
"operation requires star access to repository",
|
||||
"resource_type",
|
||||
"repository",
|
||||
"permission",
|
||||
"star")
|
||||
}
|
||||
end
|
||||
|
||||
describe "private repository, no access" do
|
||||
let(:token) { Travis::Api::App::AccessToken.create(user: repo.owner, app_id: 1) }
|
||||
let(:headers) {{ 'HTTP_AUTHORIZATION' => "token #{token}" }}
|
||||
before { repo.update_attribute(:private, true) }
|
||||
before { post("/v3/repo/#{repo.id}/star", {}, headers) }
|
||||
after { repo.update_attribute(:private, false) }
|
||||
|
||||
example { expect(last_response.status).to be == 404 }
|
||||
example { expect(JSON.load(body)).to be == {
|
||||
"@type" => "error",
|
||||
"error_type" => "not_found",
|
||||
"error_message" => "repository not found (or insufficient access)",
|
||||
"resource_type" => "repository"
|
||||
}}
|
||||
end
|
||||
|
||||
describe "existing repository, push access"
|
||||
# this requires stubing a github request, which is difficult, so has been omitted for now
|
||||
|
||||
end
|
68
spec/v3/services/repository/unstar_spec.rb
Normal file
68
spec/v3/services/repository/unstar_spec.rb
Normal file
|
@ -0,0 +1,68 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Travis::API::V3::Services::Repository::Unstar do
|
||||
let(:repo) { Travis::API::V3::Models::Repository.where(owner_name: 'svenfuchs', name: 'minimal').first }
|
||||
|
||||
describe "not authenticated" do
|
||||
before { post("/v3/repo/#{repo.id}/unstar") }
|
||||
example { expect(last_response.status).to be == 403 }
|
||||
example { expect(JSON.load(body)).to be == {
|
||||
"@type" => "error",
|
||||
"error_type" => "login_required",
|
||||
"error_message" => "login required"
|
||||
}}
|
||||
end
|
||||
|
||||
describe "missing repo, authenticated" do
|
||||
let(:token) { Travis::Api::App::AccessToken.create(user: repo.owner, app_id: 1) }
|
||||
let(:headers) {{ 'HTTP_AUTHORIZATION' => "token #{token}" }}
|
||||
before { post("/v3/repo/9999999999/unstar", {}, headers) }
|
||||
|
||||
example { expect(last_response.status).to be == 404 }
|
||||
example { expect(JSON.load(body)).to be == {
|
||||
"@type" => "error",
|
||||
"error_type" => "not_found",
|
||||
"error_message" => "repository not found (or insufficient access)",
|
||||
"resource_type" => "repository"
|
||||
}}
|
||||
end
|
||||
|
||||
describe "existing repository, no push access" do
|
||||
let(:token) { Travis::Api::App::AccessToken.create(user: repo.owner, app_id: 1) }
|
||||
let(:headers) {{ 'HTTP_AUTHORIZATION' => "token #{token}" }}
|
||||
before { post("/v3/repo/#{repo.id}/unstar", {}, headers) }
|
||||
|
||||
example { expect(last_response.status).to be == 403 }
|
||||
example { expect(JSON.load(body).to_s).to include(
|
||||
"@type",
|
||||
"error_type",
|
||||
"insufficient_access",
|
||||
"error_message",
|
||||
"operation requires unstar access to repository",
|
||||
"resource_type",
|
||||
"repository",
|
||||
"permission",
|
||||
"unstar")
|
||||
}
|
||||
end
|
||||
|
||||
describe "private repository, no access" do
|
||||
let(:token) { Travis::Api::App::AccessToken.create(user: repo.owner, app_id: 1) }
|
||||
let(:headers) {{ 'HTTP_AUTHORIZATION' => "token #{token}" }}
|
||||
before { repo.update_attribute(:private, true) }
|
||||
before { post("/v3/repo/#{repo.id}/unstar", {}, headers) }
|
||||
after { repo.update_attribute(:private, false) }
|
||||
|
||||
example { expect(last_response.status).to be == 404 }
|
||||
example { expect(JSON.load(body)).to be == {
|
||||
"@type" => "error",
|
||||
"error_type" => "not_found",
|
||||
"error_message" => "repository not found (or insufficient access)",
|
||||
"resource_type" => "repository"
|
||||
}}
|
||||
end
|
||||
|
||||
describe "existing repository, push access"
|
||||
# this requires stubing a github request, which is difficult, so has been omitted for now
|
||||
|
||||
end
|
Loading…
Reference in New Issue
Block a user