Merge branch 'master' into addCron
This commit is contained in:
commit
18ea7853bf
|
@ -34,6 +34,7 @@ module Travis
|
||||||
ServerError = Error .create(status: 500)
|
ServerError = Error .create(status: 500)
|
||||||
NotImplemented = ServerError .create('request not (yet) implemented', status: 501)
|
NotImplemented = ServerError .create('request not (yet) implemented', status: 501)
|
||||||
RequestLimitReached = ClientError .create('request limit reached for resource', status: 429)
|
RequestLimitReached = ClientError .create('request limit reached for resource', status: 429)
|
||||||
|
AlreadySyncing = ClientError .create('sync already in progress', status: 409)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -83,6 +83,10 @@ module Travis::API::V3
|
||||||
unrestricted_api?
|
unrestricted_api?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def user_writable?(user)
|
||||||
|
self.user == user
|
||||||
|
end
|
||||||
|
|
||||||
def repository_visible?(repository)
|
def repository_visible?(repository)
|
||||||
return true if unrestricted_api? and not repository.private?
|
return true if unrestricted_api? and not repository.private?
|
||||||
private_repository_visible?(repository)
|
private_repository_visible?(repository)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
module Travis::API::V3
|
module Travis::API::V3
|
||||||
class Queries::User < Query
|
class Queries::User < Query
|
||||||
params :id, :login, :email, :github_id
|
setup_sidekiq(:user_sync, queue: :user_sync, class_name: "Travis::GithubSync::Workers::SyncUser")
|
||||||
|
params :id, :login, :email, :github_id, :is_syncing
|
||||||
|
|
||||||
def find
|
def find
|
||||||
return Models::User.find_by_id(id) if id
|
return Models::User.find_by_id(id) if id
|
||||||
|
@ -17,5 +18,12 @@ module Travis::API::V3
|
||||||
User.find_by_email(email)
|
User.find_by_email(email)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def sync(user)
|
||||||
|
raise AlreadySyncing if user.is_syncing?
|
||||||
|
perform_async(:user_sync, user.id)
|
||||||
|
user.update_column(:is_syncing, true)
|
||||||
|
user
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,18 @@
|
||||||
module Travis::API::V3
|
module Travis::API::V3
|
||||||
class Query
|
class Query
|
||||||
@@sidekiq_cache = Tool::ThreadLocal.new
|
@@sidekiq_queue = {}
|
||||||
|
|
||||||
|
def self.sidekiq_queue(identifier)
|
||||||
|
@@sidekiq_queue[identifier] ||= [
|
||||||
|
"Travis::Sidekiq::#{identifier.to_s.camelcase}".freeze,
|
||||||
|
identifier.to_s.pluralize.freeze
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.setup_sidekiq(identifier, queue: nil, class_name: nil)
|
||||||
|
sidekiq_queue(identifier)[0] = class_name if class_name
|
||||||
|
sidekiq_queue(identifier)[1] = queue if queue
|
||||||
|
end
|
||||||
|
|
||||||
# generate from eval to avoid additional string allocations on every params access
|
# generate from eval to avoid additional string allocations on every params access
|
||||||
@@params_accessor = <<-RUBY
|
@@params_accessor = <<-RUBY
|
||||||
|
@ -90,11 +102,7 @@ module Travis::API::V3
|
||||||
end
|
end
|
||||||
|
|
||||||
def perform_async(identifier, *args)
|
def perform_async(identifier, *args)
|
||||||
class_name, queue = @@sidekiq_cache[identifier] ||= [
|
class_name, queue = Query.sidekiq_queue(identifier)
|
||||||
"Travis::Sidekiq::#{identifier.to_s.camelcase}".freeze,
|
|
||||||
identifier.to_s.pluralize.freeze
|
|
||||||
]
|
|
||||||
|
|
||||||
::Sidekiq::Client.push('queue'.freeze => queue, 'class'.freeze => class_name, 'args'.freeze => args)
|
::Sidekiq::Client.push('queue'.freeze => queue, 'class'.freeze => class_name, 'args'.freeze => args)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -118,6 +118,7 @@ module Travis::API::V3
|
||||||
route '/user'
|
route '/user'
|
||||||
get :current
|
get :current
|
||||||
get :find, '/{user.id}'
|
get :find, '/{user.id}'
|
||||||
|
post :sync, '/{user.id}/sync'
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
12
lib/travis/api/v3/services/user/sync.rb
Normal file
12
lib/travis/api/v3/services/user/sync.rb
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
module Travis::API::V3
|
||||||
|
class Services::User::Sync < Service
|
||||||
|
|
||||||
|
def run!
|
||||||
|
raise LoginRequired unless access_control.logged_in? or access_control.full_access?
|
||||||
|
raise NotFound unless user = find(:user)
|
||||||
|
access_control.permissions(user).sync!
|
||||||
|
|
||||||
|
query.sync(user)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
110
spec/v3/services/user/sync_spec.rb
Normal file
110
spec/v3/services/user/sync_spec.rb
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Travis::API::V3::Services::User::Sync do
|
||||||
|
let(:user) { Travis::API::V3::Models::User.find_by_login('svenfuchs') }
|
||||||
|
let(:user2) { Travis::API::V3::Models::User.create(login: 'carlad', is_syncing: true) }
|
||||||
|
let(:sidekiq_payload) { JSON.load(Sidekiq::Client.last['args'].last.to_json) }
|
||||||
|
let(:sidekiq_params) { Sidekiq::Client.last['args'].last.deep_symbolize_keys }
|
||||||
|
|
||||||
|
before do
|
||||||
|
user.update_attribute(:is_syncing, false)
|
||||||
|
Travis::Features.stubs(:owner_active?).returns(true)
|
||||||
|
@original_sidekiq = Sidekiq::Client
|
||||||
|
Sidekiq.send(:remove_const, :Client) # to avoid a warning
|
||||||
|
Sidekiq::Client = []
|
||||||
|
end
|
||||||
|
|
||||||
|
after do
|
||||||
|
Sidekiq.send(:remove_const, :Client) # to avoid a warning
|
||||||
|
Sidekiq::Client = @original_sidekiq
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "not authenticated" do
|
||||||
|
before { post("/v3/user/#{user.id}/sync") }
|
||||||
|
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 user, authenticated" do
|
||||||
|
let(:token) { Travis::Api::App::AccessToken.create(user: user, app_id: 1) }
|
||||||
|
let(:headers) {{ 'HTTP_AUTHORIZATION' => "token #{token}" }}
|
||||||
|
before { post("/v3/user/9999999999/sync", {}, headers) }
|
||||||
|
|
||||||
|
example { expect(last_response.status).to be == 404 }
|
||||||
|
example { expect(JSON.load(body)).to be == {
|
||||||
|
"@type" => "error",
|
||||||
|
"error_type" => "not_found",
|
||||||
|
"error_message" => "user not found (or insufficient access)",
|
||||||
|
"resource_type" => "user"
|
||||||
|
}}
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "existing user, matches current user " do
|
||||||
|
let(:params) {{}}
|
||||||
|
let(:token) { Travis::Api::App::AccessToken.create(user: user, app_id: 1) }
|
||||||
|
let(:headers) {{ 'HTTP_AUTHORIZATION' => "token #{token}" }}
|
||||||
|
before { Travis::API::V3::Models::Permission.create(user: user) }
|
||||||
|
before { post("/v3/user/#{user.id}/sync", params, headers) }
|
||||||
|
|
||||||
|
example { expect(last_response.status).to be == 200 }
|
||||||
|
example { expect(JSON.load(body).to_s).to include(
|
||||||
|
"@type",
|
||||||
|
"user",
|
||||||
|
"@href",
|
||||||
|
"@representation",
|
||||||
|
"sync",
|
||||||
|
"is_syncing",
|
||||||
|
"id",
|
||||||
|
"true")
|
||||||
|
}
|
||||||
|
|
||||||
|
example { expect(sidekiq_payload).to be == user.id }
|
||||||
|
|
||||||
|
example { expect(Sidekiq::Client.last['queue']).to be == :user_sync }
|
||||||
|
example { expect(Sidekiq::Client.last['class']).to be == 'Travis::GithubSync::Workers::SyncUser' }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "existing user, current user does not have sync access " do
|
||||||
|
let(:params) {{}}
|
||||||
|
let(:token) { Travis::Api::App::AccessToken.create(user: user, app_id: 1) }
|
||||||
|
let(:headers) {{ 'HTTP_AUTHORIZATION' => "token #{token}" }}
|
||||||
|
before { Travis::API::V3::Models::Permission.create(user: user) }
|
||||||
|
before { post("/v3/user/#{user2.id}/sync", params, headers) }
|
||||||
|
|
||||||
|
example { expect(last_response.status).to be == 403 }
|
||||||
|
example { expect(JSON.load(body)).to be == {
|
||||||
|
"@type" => "error",
|
||||||
|
"error_type" => "insufficient_access",
|
||||||
|
"error_message" => "operation requires sync access to user",
|
||||||
|
"resource_type" => "user",
|
||||||
|
"permission" => "sync",
|
||||||
|
"user" => {
|
||||||
|
"@type" => "user",
|
||||||
|
"@href" => "/user/#{user2.id}",
|
||||||
|
"@representation"=> "minimal",
|
||||||
|
"id" => user2.id,
|
||||||
|
"login" => "carlad"
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "existing user, authorized, user already syncing " do
|
||||||
|
let(:params) {{}}
|
||||||
|
let(:token) { Travis::Api::App::AccessToken.create(user: user2, app_id: 1) }
|
||||||
|
let(:headers) {{ 'HTTP_AUTHORIZATION' => "token #{token}" }}
|
||||||
|
before { Travis::API::V3::Models::Permission.create(user: user) }
|
||||||
|
before { post("/v3/user/#{user2.id}/sync", params, headers) }
|
||||||
|
|
||||||
|
example { expect(last_response.status).to be == 409 }
|
||||||
|
example { expect(JSON.load(body)).to be == {
|
||||||
|
"@type" => "error",
|
||||||
|
"error_type" => "already_syncing",
|
||||||
|
"error_message" => "sync already in progress"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue
Block a user