From faf60a5f7fd2223598f124e465e55b4b4f683f82 Mon Sep 17 00:00:00 2001 From: carlad Date: Fri, 25 Sep 2015 16:04:47 +0200 Subject: [PATCH 01/15] initial work to add build and job cancellation incl updating existing specs --- lib/travis/api/v3/permissions/build.rb | 13 +++++++++++++ lib/travis/api/v3/permissions/job.rb | 13 +++++++++++++ lib/travis/api/v3/queries/build.rb | 10 ++++++++++ lib/travis/api/v3/routes.rb | 2 +- lib/travis/api/v3/services/build/cancel.rb | 13 +++++++++++++ spec/v3/services/build/find_spec.rb | 20 ++++++++++++++------ spec/v3/services/builds/find_spec.rb | 8 ++++++++ spec/v3/services/job/find_spec.rb | 8 ++++++++ 8 files changed, 80 insertions(+), 7 deletions(-) create mode 100644 lib/travis/api/v3/permissions/build.rb create mode 100644 lib/travis/api/v3/permissions/job.rb create mode 100644 lib/travis/api/v3/services/build/cancel.rb diff --git a/lib/travis/api/v3/permissions/build.rb b/lib/travis/api/v3/permissions/build.rb new file mode 100644 index 00000000..e1f4bfb0 --- /dev/null +++ b/lib/travis/api/v3/permissions/build.rb @@ -0,0 +1,13 @@ +require 'travis/api/v3/permissions/generic' + +module Travis::API::V3 + class Permissions::Build < Permissions::Generic + def cancel? + write? + end + + def restart? + write? + end + end +end diff --git a/lib/travis/api/v3/permissions/job.rb b/lib/travis/api/v3/permissions/job.rb new file mode 100644 index 00000000..3055257e --- /dev/null +++ b/lib/travis/api/v3/permissions/job.rb @@ -0,0 +1,13 @@ +require 'travis/api/v3/permissions/generic' + +module Travis::API::V3 + class Permissions::Job < Permissions::Generic + def cancel? + write? + end + + def restart? + write? + end + end +end diff --git a/lib/travis/api/v3/queries/build.rb b/lib/travis/api/v3/queries/build.rb index 1f97e7c7..8294a00a 100644 --- a/lib/travis/api/v3/queries/build.rb +++ b/lib/travis/api/v3/queries/build.rb @@ -6,5 +6,15 @@ module Travis::API::V3 return Models::Build.find_by_id(id) if id raise WrongParams, 'missing build.id'.freeze end + + def cancel + raise WrongParams, 'missing build.id'.freeze unless build.id + payload = { + build: { id: build.id } + } + + perform_async(:build_cancellation, type: 'api'.freeze, credentials: { token: token }, payload: JSON.dump(payload)) + payload + end end end diff --git a/lib/travis/api/v3/routes.rb b/lib/travis/api/v3/routes.rb index 811b153a..8d1d3584 100644 --- a/lib/travis/api/v3/routes.rb +++ b/lib/travis/api/v3/routes.rb @@ -18,7 +18,7 @@ module Travis::API::V3 route '/build/{build.id}' get :find - # post :cancel, '/cancel' + post :cancel, '/cancel' # post :restart, '/restart' end diff --git a/lib/travis/api/v3/services/build/cancel.rb b/lib/travis/api/v3/services/build/cancel.rb new file mode 100644 index 00000000..bf1ab19b --- /dev/null +++ b/lib/travis/api/v3/services/build/cancel.rb @@ -0,0 +1,13 @@ +module Travis::API::V3 + class Services::Build::Cancel < Service + + def run + raise LoginRequired unless access_control.logged_in? or access_control.full_access? + raise NotFound unless build = find(:build) + access_control.permissions(build).cancel! + + payload = query.cancel(build) + build + end + end +end diff --git a/spec/v3/services/build/find_spec.rb b/spec/v3/services/build/find_spec.rb index a088aa03..17f364e6 100644 --- a/spec/v3/services/build/find_spec.rb +++ b/spec/v3/services/build/find_spec.rb @@ -26,9 +26,13 @@ describe Travis::API::V3::Services::Build::Find do before { get("/v3/build/#{build.id}") } example { expect(last_response).to be_ok } example { expect(parsed_body).to be == { - "@type" => "build", - "@href" => "/v3/build/#{build.id}", - "@representation" => "standard", + "@type" => "build", + "@href" => "/v3/build/#{build.id}", + "@representation" => "standard", + "@permissions" => { + "read" => true, + "cancel" => false, + "restart" => false}, "id" => build.id, "number" => build.number, "state" => build.state, @@ -99,9 +103,13 @@ describe Travis::API::V3::Services::Build::Find do after { repo.update_attribute(:private, false) } example { expect(last_response).to be_ok } example { expect(parsed_body).to be == { - "@type" => "build", - "@href" => "/v3/build/#{build.id}", - "@representation" => "standard", + "@type" => "build", + "@href" => "/v3/build/#{build.id}", + "@representation" => "standard", + "@permissions" => { + "read" => true, + "cancel" => false, + "restart" => false}, "id" => build.id, "number" => build.number, "state" => build.state, diff --git a/spec/v3/services/builds/find_spec.rb b/spec/v3/services/builds/find_spec.rb index 114bd355..bd2ff907 100644 --- a/spec/v3/services/builds/find_spec.rb +++ b/spec/v3/services/builds/find_spec.rb @@ -52,6 +52,10 @@ describe Travis::API::V3::Services::Builds::Find do "@type" => "build", "@href" => "/v3/build/#{build.id}", "@representation" => "standard", + "@permissions" => { + "read" => true, + "cancel" => false, + "restart" => false }, "id" => build.id, "number" => "3", "state" => "configured", @@ -149,6 +153,10 @@ describe Travis::API::V3::Services::Builds::Find do "@type" => "build", "@href" => "/v3/build/#{build.id}", "@representation" => "standard", + "@permissions" => { + "read" => true, + "cancel" => false, + "restart" => false }, "id" => build.id, "number" => "3", "state" => "configured", diff --git a/spec/v3/services/job/find_spec.rb b/spec/v3/services/job/find_spec.rb index 476c44ad..56c1b5fe 100644 --- a/spec/v3/services/job/find_spec.rb +++ b/spec/v3/services/job/find_spec.rb @@ -20,6 +20,10 @@ describe Travis::API::V3::Services::Job::Find do "@type" => "job", "@href" => "/v3/job/#{job.id}", "@representation" => "standard", + "@permissions" => { + "read" => true, + "cancel" => false, + "restart" => false }, "id" => job.id, "number" => job.number, "state" => job.state, @@ -140,6 +144,10 @@ describe Travis::API::V3::Services::Job::Find do "@type" => "job", "@href" => "/v3/job/#{job.id}", "@representation" => "standard", + "@permissions" => { + "read" => true, + "cancel" => false, + "restart" => false }, "id" => job.id, "number" => job.number, "state" => job.state, From 2e110bc455dd0851c9ac7a0083238b3b6f045b4f Mon Sep 17 00:00:00 2001 From: carlad Date: Fri, 25 Sep 2015 16:34:37 +0200 Subject: [PATCH 02/15] v3 add spec for build cancel - not yet passing --- spec/v3/services/build/cancel_spec.rb | 167 ++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 spec/v3/services/build/cancel_spec.rb diff --git a/spec/v3/services/build/cancel_spec.rb b/spec/v3/services/build/cancel_spec.rb new file mode 100644 index 00000000..28838370 --- /dev/null +++ b/spec/v3/services/build/cancel_spec.rb @@ -0,0 +1,167 @@ +require 'spec_helper' + +describe Travis::API::V3::Services::Build::Cancel do + let(:repo) { Travis::API::V3::Models::Repository.where(owner_name: 'svenfuchs', name: 'minimal').first } + let(:build) { repo.builds.first } + let(:sidekiq_payload) { JSON.load(Sidekiq::Client.last['args'].last[:payload]).deep_symbolize_keys } + let(:sidekiq_params) { Sidekiq::Client.last['args'].last.deep_symbolize_keys } + # before { build.cancel.each(&:delete) } + + before do + 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/build/#{build.id}/cancel") } + 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 build, authenticated" do + let(:token) { Travis::Api::App::AccessToken.create(user: repo.owner, app_id: 1) } + let(:headers) {{ 'HTTP_AUTHORIZATION' => "token #{token}" }} + before { post("/v3/build/9999999999/cancel", {}, headers) } + + example { expect(last_response.status).to be == 404 } + example { expect(JSON.load(body)).to be == { + "@type" => "error", + "error_type" => "not_found", + "error_message" => "build not found (or insufficient access)", + "resource_type" => "build" + }} + 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/build/#{build.id}/cancel", {}, headers) } + + example { expect(last_response.status).to be == 403 } + example { expect(JSON.load(body).to_s).to include( + "@type", + "error_type", + "error_message", + "insufficient access", + "resource_type", + "build", + "permission", + "build_cancellation") + } + 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/build/#{build.id}/cancel", {}, headers) } + after { build.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" => "build not found (or insufficient access)", + "resource_type" => "build" + }} + end + + describe "existing repository, push access" do + let(:params) {{}} + let(:token) { Travis::Api::App::AccessToken.create(user: repo.owner, app_id: 1) } + let(:headers) {{ 'HTTP_AUTHORIZATION' => "token #{token}" }} + before { Travis::API::V3::Models::Permission.create(repository: repo, user: repo.owner, push: true) } + before { post("/v3/build/#{build.id}/cancel", params, headers) } + + example { expect(last_response.status).to be == 202 } + example { expect(JSON.load(body).to_s).to include( + "@type", + "pending", + "build", + "@href", + "@representation", + "minimal", + "request", + "user", + "resource_type", + "request") + } + + example { expect(sidekiq_payload).to be == { + build: { id: build.id } + }} + + example { expect(Sidekiq::Client.last['queue']).to be == 'build_cancellations' } + example { expect(Sidekiq::Client.last['class']).to be == 'Travis::Sidekiq::CancellationRequest' } + + describe "setting id has no effect" do + let(:params) {{ id: 42 }} + example { expect(sidekiq_payload).to be == { + build: { id: build.id } + }} + end + + describe "passing the token in params" do + let(:params) {{ request: { token: 'foo-bar' }}} + example { expect(sidekiq_params[:credentials]).to be == { + token: 'foo-bar' + }} + end + end + + + describe "existing repository, application with full access" do + let(:app_name) { 'travis-example' } + let(:app_secret) { '12345678' } + let(:sign_opts) { "a=#{app_name}" } + let(:signature) { OpenSSL::HMAC.hexdigest('sha256', app_secret, sign_opts) } + let(:headers) {{ 'HTTP_AUTHORIZATION' => "signature #{sign_opts}:#{signature}" }} + before { Travis.config.applications = { app_name => { full_access: true, secret: app_secret }}} + before { post("/v3/build/#{build.id}/cancel", params, headers) } + + # describe 'without setting user' do + # let(:params) {{}} + # example { expect(last_response.status).to be == 400 } + # example { expect(JSON.load(body)).to be == { + # "@type" => "error", + # "error_type" => "wrong_params", + # "error_message" => "missing user" + # }} + # end + # + # describe 'setting user' do + # let(:params) {{ user: { id: repo.owner.id } }} + # example { expect(last_response.status).to be == 202 } + # example { expect(sidekiq_payload).to be == { + # repository: { id: repo.id, owner_name: 'svenfuchs', name: 'minimal' }, + # user: { id: repo.owner.id }, + # message: nil, + # branch: 'master', + # config: {} + # }} + # end + # + # describe 'setting branch' do + # let(:params) {{ user: { id: repo.owner.id }, branch: 'example' }} + # example { expect(last_response.status).to be == 202 } + # example { expect(sidekiq_payload).to be == { + # repository: { id: repo.id, owner_name: 'svenfuchs', name: 'minimal' }, + # user: { id: repo.owner.id }, + # message: nil, + # branch: 'example', + # config: {} + # }} + # end + end +end From c6cc6d1b96fc338e6ceba9ecfaca1bc95b78bdfd Mon Sep 17 00:00:00 2001 From: carlad Date: Fri, 25 Sep 2015 16:59:26 +0200 Subject: [PATCH 03/15] update build cancel spec --- spec/v3/services/build/cancel_spec.rb | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/spec/v3/services/build/cancel_spec.rb b/spec/v3/services/build/cancel_spec.rb index 28838370..4fadd008 100644 --- a/spec/v3/services/build/cancel_spec.rb +++ b/spec/v3/services/build/cancel_spec.rb @@ -52,12 +52,13 @@ describe Travis::API::V3::Services::Build::Cancel do example { expect(JSON.load(body).to_s).to include( "@type", "error_type", - "error_message", "insufficient access", + "error_message", + "operation requires cancel access to build", "resource_type", "build", "permission", - "build_cancellation") + "cancel") } end @@ -66,7 +67,7 @@ describe Travis::API::V3::Services::Build::Cancel do let(:headers) {{ 'HTTP_AUTHORIZATION' => "token #{token}" }} before { repo.update_attribute(:private, true) } before { post("/v3/build/#{build.id}/cancel", {}, headers) } - after { build.update_attribute(:private, false) } + after { repo.update_attribute(:private, false) } example { expect(last_response.status).to be == 404 } example { expect(JSON.load(body)).to be == { @@ -87,15 +88,15 @@ describe Travis::API::V3::Services::Build::Cancel do example { expect(last_response.status).to be == 202 } example { expect(JSON.load(body).to_s).to include( "@type", - "pending", + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "build", "@href", "@representation", "minimal", - "request", - "user", - "resource_type", - "request") + "permission", + "cancel", + "id", + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx") } example { expect(sidekiq_payload).to be == { From d99c3c8c5421d11d83df4479e67793ee2ff97d4f Mon Sep 17 00:00:00 2001 From: carlad Date: Mon, 28 Sep 2015 18:44:47 +0200 Subject: [PATCH 04/15] v3 update build cancel, access control, specs for build cancel --- lib/travis/api/v3/access_control/generic.rb | 4 ++ lib/travis/api/v3/queries/build.rb | 10 +-- lib/travis/api/v3/services/build/cancel.rb | 4 +- spec/v3/services/build/cancel_spec.rb | 78 ++++++++------------- 4 files changed, 40 insertions(+), 56 deletions(-) diff --git a/lib/travis/api/v3/access_control/generic.rb b/lib/travis/api/v3/access_control/generic.rb index 7f006029..13e990f8 100644 --- a/lib/travis/api/v3/access_control/generic.rb +++ b/lib/travis/api/v3/access_control/generic.rb @@ -51,6 +51,10 @@ module Travis::API::V3 visible? build.repository end + def build_writable?(build) + writable? build.repository + end + def branch_visible?(branch) visible? branch.repository end diff --git a/lib/travis/api/v3/queries/build.rb b/lib/travis/api/v3/queries/build.rb index 8294a00a..f151d77c 100644 --- a/lib/travis/api/v3/queries/build.rb +++ b/lib/travis/api/v3/queries/build.rb @@ -7,13 +7,9 @@ module Travis::API::V3 raise WrongParams, 'missing build.id'.freeze end - def cancel - raise WrongParams, 'missing build.id'.freeze unless build.id - payload = { - build: { id: build.id } - } - - perform_async(:build_cancellation, type: 'api'.freeze, credentials: { token: token }, payload: JSON.dump(payload)) + def cancel(user) + payload = {build: {id: id, user_id: user.id, source: 'api'}} + perform_async(:build_cancellation, type: 'api'.freeze, payload: JSON.dump(payload)) payload end end diff --git a/lib/travis/api/v3/services/build/cancel.rb b/lib/travis/api/v3/services/build/cancel.rb index bf1ab19b..168b3ccf 100644 --- a/lib/travis/api/v3/services/build/cancel.rb +++ b/lib/travis/api/v3/services/build/cancel.rb @@ -6,8 +6,8 @@ module Travis::API::V3 raise NotFound unless build = find(:build) access_control.permissions(build).cancel! - payload = query.cancel(build) - build + query.cancel(access_control.user) + accepted(build: build, state_change: :cancel) end end end diff --git a/spec/v3/services/build/cancel_spec.rb b/spec/v3/services/build/cancel_spec.rb index 4fadd008..c40205e3 100644 --- a/spec/v3/services/build/cancel_spec.rb +++ b/spec/v3/services/build/cancel_spec.rb @@ -3,9 +3,8 @@ require 'spec_helper' describe Travis::API::V3::Services::Build::Cancel do let(:repo) { Travis::API::V3::Models::Repository.where(owner_name: 'svenfuchs', name: 'minimal').first } let(:build) { repo.builds.first } - let(:sidekiq_payload) { JSON.load(Sidekiq::Client.last['args'].last[:payload]).deep_symbolize_keys } + let(:sidekiq_payload) { JSON.load(Sidekiq::Client.last['args'].last[:payload]) } let(:sidekiq_params) { Sidekiq::Client.last['args'].last.deep_symbolize_keys } - # before { build.cancel.each(&:delete) } before do Travis::Features.stubs(:owner_active?).returns(true) @@ -52,7 +51,7 @@ describe Travis::API::V3::Services::Build::Cancel do example { expect(JSON.load(body).to_s).to include( "@type", "error_type", - "insufficient access", + "insufficient_access", "error_message", "operation requires cancel access to build", "resource_type", @@ -88,59 +87,56 @@ describe Travis::API::V3::Services::Build::Cancel do example { expect(last_response.status).to be == 202 } example { expect(JSON.load(body).to_s).to include( "@type", - "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "build", "@href", "@representation", "minimal", - "permission", "cancel", "id", - "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx") + "state_change") } example { expect(sidekiq_payload).to be == { - build: { id: build.id } + "build" => { + "id" => "#{build.id}", + "user_id"=> repo.owner_id, + "source" => "api"} }} example { expect(Sidekiq::Client.last['queue']).to be == 'build_cancellations' } - example { expect(Sidekiq::Client.last['class']).to be == 'Travis::Sidekiq::CancellationRequest' } + example { expect(Sidekiq::Client.last['class']).to be == 'Travis::Sidekiq::BuildCancellation' } describe "setting id has no effect" do let(:params) {{ id: 42 }} example { expect(sidekiq_payload).to be == { - build: { id: build.id } - }} - end - - describe "passing the token in params" do - let(:params) {{ request: { token: 'foo-bar' }}} - example { expect(sidekiq_params[:credentials]).to be == { - token: 'foo-bar' + "build" => { + "id" => "#{build.id}", + "user_id"=> repo.owner_id, + "source" => "api"} }} end end - describe "existing repository, application with full access" do - let(:app_name) { 'travis-example' } - let(:app_secret) { '12345678' } - let(:sign_opts) { "a=#{app_name}" } - let(:signature) { OpenSSL::HMAC.hexdigest('sha256', app_secret, sign_opts) } - let(:headers) {{ 'HTTP_AUTHORIZATION' => "signature #{sign_opts}:#{signature}" }} - before { Travis.config.applications = { app_name => { full_access: true, secret: app_secret }}} - before { post("/v3/build/#{build.id}/cancel", params, headers) } + # describe "existing repository, application with full access" do + # let(:app_name) { 'travis-example' } + # let(:app_secret) { '12345678' } + # let(:sign_opts) { "a=#{app_name}" } + # let(:signature) { OpenSSL::HMAC.hexdigest('sha256', app_secret, sign_opts) } + # let(:headers) {{ 'HTTP_AUTHORIZATION' => "signature #{sign_opts}:#{signature}" }} + # before { Travis.config.applications = { app_name => { full_access: true, secret: app_secret }}} + # before { post("/v3/build/#{build.id}/cancel", params, headers) } + # + # describe 'without setting user' do + # let(:params) {{}} + # example { expect(last_response.status).to be == 400 } + # example { expect(JSON.load(body)).to be == { + # "@type" => "error", + # "error_type" => "wrong_params", + # "error_message" => "missing user" + # }} + # end - # describe 'without setting user' do - # let(:params) {{}} - # example { expect(last_response.status).to be == 400 } - # example { expect(JSON.load(body)).to be == { - # "@type" => "error", - # "error_type" => "wrong_params", - # "error_message" => "missing user" - # }} - # end - # # describe 'setting user' do # let(:params) {{ user: { id: repo.owner.id } }} # example { expect(last_response.status).to be == 202 } @@ -152,17 +148,5 @@ describe Travis::API::V3::Services::Build::Cancel do # config: {} # }} # end - # - # describe 'setting branch' do - # let(:params) {{ user: { id: repo.owner.id }, branch: 'example' }} - # example { expect(last_response.status).to be == 202 } - # example { expect(sidekiq_payload).to be == { - # repository: { id: repo.id, owner_name: 'svenfuchs', name: 'minimal' }, - # user: { id: repo.owner.id }, - # message: nil, - # branch: 'example', - # config: {} - # }} - # end - end + # end end From 2bc72d8b9b2ede7cebf945054ca789a22b82f648 Mon Sep 17 00:00:00 2001 From: carlad Date: Tue, 29 Sep 2015 13:43:22 +0200 Subject: [PATCH 05/15] v3 update buiild/cancel_spec --- spec/v3/services/build/cancel_spec.rb | 29 +++++++++++++++------------ 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/spec/v3/services/build/cancel_spec.rb b/spec/v3/services/build/cancel_spec.rb index c40205e3..3f37b633 100644 --- a/spec/v3/services/build/cancel_spec.rb +++ b/spec/v3/services/build/cancel_spec.rb @@ -117,7 +117,10 @@ describe Travis::API::V3::Services::Build::Cancel do end end - + # TODO decided to discuss further with rkh as this use case doesn't really exist at the moment + # and 'fixing' the query requires modifying workers that v2 uses, thereby running the risk of breaking v2, + # and also because in 6 months or so travis-hub will be able to cancel builds without using travis-core at all. + # # describe "existing repository, application with full access" do # let(:app_name) { 'travis-example' } # let(:app_secret) { '12345678' } @@ -136,17 +139,17 @@ describe Travis::API::V3::Services::Build::Cancel do # "error_message" => "missing user" # }} # end - - # describe 'setting user' do - # let(:params) {{ user: { id: repo.owner.id } }} - # example { expect(last_response.status).to be == 202 } - # example { expect(sidekiq_payload).to be == { - # repository: { id: repo.id, owner_name: 'svenfuchs', name: 'minimal' }, - # user: { id: repo.owner.id }, - # message: nil, - # branch: 'master', - # config: {} - # }} - # end + # + # describe 'setting user' do + # let(:params) {{ user: { id: repo.owner.id } }} + # example { expect(last_response.status).to be == 202 } + # example { expect(sidekiq_payload).to be == { + # # repository: { id: repo.id, owner_name: 'svenfuchs', name: 'minimal' }, + # # user: { id: repo.owner.id }, + # # message: nil, + # # branch: 'master', + # # config: {} + # }} + # end # end end From b73a5604d0845fda761227755948e777298fd004 Mon Sep 17 00:00:00 2001 From: carlad Date: Tue, 29 Sep 2015 16:28:42 +0200 Subject: [PATCH 06/15] v3 add build/id/cancel endpoint and tests --- lib/travis/api/v3/queries/build.rb | 6 + lib/travis/api/v3/routes.rb | 2 +- lib/travis/api/v3/services/build/restart.rb | 13 ++ spec/v3/services/build/restart_spec.rb | 156 ++++++++++++++++++++ 4 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 lib/travis/api/v3/services/build/restart.rb create mode 100644 spec/v3/services/build/restart_spec.rb diff --git a/lib/travis/api/v3/queries/build.rb b/lib/travis/api/v3/queries/build.rb index f151d77c..6cd5cee4 100644 --- a/lib/travis/api/v3/queries/build.rb +++ b/lib/travis/api/v3/queries/build.rb @@ -12,5 +12,11 @@ module Travis::API::V3 perform_async(:build_cancellation, type: 'api'.freeze, payload: JSON.dump(payload)) payload end + + def restart(user) + payload = {build: {id: id, user_id: user.id, source: 'api'}} + perform_async(:build_restart, type: 'api'.freeze, payload: JSON.dump(payload)) + payload + end end end diff --git a/lib/travis/api/v3/routes.rb b/lib/travis/api/v3/routes.rb index 8d1d3584..ca4e4988 100644 --- a/lib/travis/api/v3/routes.rb +++ b/lib/travis/api/v3/routes.rb @@ -19,7 +19,7 @@ module Travis::API::V3 get :find post :cancel, '/cancel' - # post :restart, '/restart' + post :restart, '/restart' end resource :job do diff --git a/lib/travis/api/v3/services/build/restart.rb b/lib/travis/api/v3/services/build/restart.rb new file mode 100644 index 00000000..acb49727 --- /dev/null +++ b/lib/travis/api/v3/services/build/restart.rb @@ -0,0 +1,13 @@ +module Travis::API::V3 + class Services::Build::Restart < Service + + def run + raise LoginRequired unless access_control.logged_in? or access_control.full_access? + raise NotFound unless build = find(:build) + access_control.permissions(build).restart! + + query.restart(access_control.user) + accepted(build: build, state_change: :restart) + end + end +end diff --git a/spec/v3/services/build/restart_spec.rb b/spec/v3/services/build/restart_spec.rb new file mode 100644 index 00000000..a5d4d662 --- /dev/null +++ b/spec/v3/services/build/restart_spec.rb @@ -0,0 +1,156 @@ +require 'spec_helper' + +describe Travis::API::V3::Services::Build::Restart do + let(:repo) { Travis::API::V3::Models::Repository.where(owner_name: 'svenfuchs', name: 'minimal').first } + let(:build) { repo.builds.first } + let(:sidekiq_payload) { JSON.load(Sidekiq::Client.last['args'].last[:payload]) } + let(:sidekiq_params) { Sidekiq::Client.last['args'].last.deep_symbolize_keys } + + before do + 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/build/#{build.id}/restart") } + 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 build, authenticated" do + let(:token) { Travis::Api::App::AccessToken.create(user: repo.owner, app_id: 1) } + let(:headers) {{ 'HTTP_AUTHORIZATION' => "token #{token}" }} + before { post("/v3/build/9999999999/restart", {}, headers) } + + example { expect(last_response.status).to be == 404 } + example { expect(JSON.load(body)).to be == { + "@type" => "error", + "error_type" => "not_found", + "error_message" => "build not found (or insufficient access)", + "resource_type" => "build" + }} + 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/build/#{build.id}/restart", {}, 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 restart access to build", + "resource_type", + "build", + "permission", + "restart") + } + 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/build/#{build.id}/restart", {}, 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" => "build not found (or insufficient access)", + "resource_type" => "build" + }} + end + + describe "existing repository, push access" do + let(:params) {{}} + let(:token) { Travis::Api::App::AccessToken.create(user: repo.owner, app_id: 1) } + let(:headers) {{ 'HTTP_AUTHORIZATION' => "token #{token}" }} + before { Travis::API::V3::Models::Permission.create(repository: repo, user: repo.owner, push: true) } + before { post("/v3/build/#{build.id}/restart", params, headers) } + + example { expect(last_response.status).to be == 202 } + example { expect(JSON.load(body).to_s).to include( + "@type", + "pending", + "build", + "@href", + "@representation", + "minimal", + "restart", + "id", + "state_change") + } + + example { expect(sidekiq_payload).to be == { + "build" => { + "id" => "#{build.id}", + "user_id"=> repo.owner_id, + "source" => "api"} + }} + + example { expect(Sidekiq::Client.last['queue']).to be == 'build_restarts' } + example { expect(Sidekiq::Client.last['class']).to be == 'Travis::Sidekiq::BuildRestart' } + + describe "setting id has no effect" do + let(:params) {{ id: 42 }} + example { expect(sidekiq_payload).to be == { + "build" => { + "id" => "#{build.id}", + "user_id"=> repo.owner_id, + "source" => "api"} + }} + end + end + + # TODO decided to discuss further with rkh as this use case doesn't really exist at the moment + # and 'fixing' the query requires modifying workers that v2 uses, thereby running the risk of breaking v2, + # and also because in 6 months or so travis-hub will be able to cancel builds without using travis-core at all. + # + # describe "existing repository, application with full access" do + # let(:app_name) { 'travis-example' } + # let(:app_secret) { '12345678' } + # let(:sign_opts) { "a=#{app_name}" } + # let(:signature) { OpenSSL::HMAC.hexdigest('sha256', app_secret, sign_opts) } + # let(:headers) {{ 'HTTP_AUTHORIZATION' => "signature #{sign_opts}:#{signature}" }} + # before { Travis.config.applications = { app_name => { full_access: true, secret: app_secret }}} + # before { post("/v3/build/#{build.id}/restart", params, headers) } + # + # describe 'without setting user' do + # let(:params) {{}} + # example { expect(last_response.status).to be == 400 } + # example { expect(JSON.load(body)).to be == { + # "@type" => "error", + # "error_type" => "wrong_params", + # "error_message" => "missing user" + # }} + # end + # + # describe 'setting user' do + # let(:params) {{ user: { id: repo.owner.id } }} + # example { expect(last_response.status).to be == 202 } + # example { expect(sidekiq_payload).to be == { + # # repository: { id: repo.id, owner_name: 'svenfuchs', name: 'minimal' }, + # # user: { id: repo.owner.id }, + # # message: nil, + # # branch: 'master', + # # config: {} + # }} + # end + # end +end From 5c6e705012c8759240099a5f8b75c049edd3baa2 Mon Sep 17 00:00:00 2001 From: carlad Date: Wed, 30 Sep 2015 15:21:59 +0200 Subject: [PATCH 07/15] v3 update sidekiq payload for build restart and cancel --- lib/travis/api/v3/queries/build.rb | 4 ++-- spec/v3/services/build/cancel_spec.rb | 18 ++++++++---------- spec/v3/services/build/restart_spec.rb | 18 ++++++++---------- 3 files changed, 18 insertions(+), 22 deletions(-) diff --git a/lib/travis/api/v3/queries/build.rb b/lib/travis/api/v3/queries/build.rb index 6cd5cee4..350f02b1 100644 --- a/lib/travis/api/v3/queries/build.rb +++ b/lib/travis/api/v3/queries/build.rb @@ -8,13 +8,13 @@ module Travis::API::V3 end def cancel(user) - payload = {build: {id: id, user_id: user.id, source: 'api'}} + payload = {id: id, user_id: user.id, source: 'api'} perform_async(:build_cancellation, type: 'api'.freeze, payload: JSON.dump(payload)) payload end def restart(user) - payload = {build: {id: id, user_id: user.id, source: 'api'}} + payload = {id: id, user_id: user.id, source: 'api'} perform_async(:build_restart, type: 'api'.freeze, payload: JSON.dump(payload)) payload end diff --git a/spec/v3/services/build/cancel_spec.rb b/spec/v3/services/build/cancel_spec.rb index 3f37b633..db32e36a 100644 --- a/spec/v3/services/build/cancel_spec.rb +++ b/spec/v3/services/build/cancel_spec.rb @@ -97,11 +97,10 @@ describe Travis::API::V3::Services::Build::Cancel do } example { expect(sidekiq_payload).to be == { - "build" => { - "id" => "#{build.id}", - "user_id"=> repo.owner_id, - "source" => "api"} - }} + "id" => "#{build.id}", + "user_id"=> repo.owner_id, + "source" => "api"} + } example { expect(Sidekiq::Client.last['queue']).to be == 'build_cancellations' } example { expect(Sidekiq::Client.last['class']).to be == 'Travis::Sidekiq::BuildCancellation' } @@ -109,11 +108,10 @@ describe Travis::API::V3::Services::Build::Cancel do describe "setting id has no effect" do let(:params) {{ id: 42 }} example { expect(sidekiq_payload).to be == { - "build" => { - "id" => "#{build.id}", - "user_id"=> repo.owner_id, - "source" => "api"} - }} + "id" => "#{build.id}", + "user_id"=> repo.owner_id, + "source" => "api"} + } end end diff --git a/spec/v3/services/build/restart_spec.rb b/spec/v3/services/build/restart_spec.rb index a5d4d662..14ba5ce3 100644 --- a/spec/v3/services/build/restart_spec.rb +++ b/spec/v3/services/build/restart_spec.rb @@ -98,11 +98,10 @@ describe Travis::API::V3::Services::Build::Restart do } example { expect(sidekiq_payload).to be == { - "build" => { - "id" => "#{build.id}", - "user_id"=> repo.owner_id, - "source" => "api"} - }} + "id" => "#{build.id}", + "user_id"=> repo.owner_id, + "source" => "api"} + } example { expect(Sidekiq::Client.last['queue']).to be == 'build_restarts' } example { expect(Sidekiq::Client.last['class']).to be == 'Travis::Sidekiq::BuildRestart' } @@ -110,11 +109,10 @@ describe Travis::API::V3::Services::Build::Restart do describe "setting id has no effect" do let(:params) {{ id: 42 }} example { expect(sidekiq_payload).to be == { - "build" => { - "id" => "#{build.id}", - "user_id"=> repo.owner_id, - "source" => "api"} - }} + "id" => "#{build.id}", + "user_id"=> repo.owner_id, + "source" => "api"} + } end end From f3b8258b5390e41e5412eb6a1a9054397f5b1672 Mon Sep 17 00:00:00 2001 From: carlad Date: Wed, 30 Sep 2015 15:29:12 +0200 Subject: [PATCH 08/15] v3 update payload for build restart --- lib/travis/api/v3/queries/build.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/travis/api/v3/queries/build.rb b/lib/travis/api/v3/queries/build.rb index 350f02b1..894c3fdb 100644 --- a/lib/travis/api/v3/queries/build.rb +++ b/lib/travis/api/v3/queries/build.rb @@ -14,7 +14,10 @@ module Travis::API::V3 end def restart(user) - payload = {id: id, user_id: user.id, source: 'api'} + payload = { + build: { id: id }, + user: { id: user.id } + } perform_async(:build_restart, type: 'api'.freeze, payload: JSON.dump(payload)) payload end From 38b73f53cb2253fa80fbe0f38ac4d26b20512c5b Mon Sep 17 00:00:00 2001 From: carlad Date: Wed, 30 Sep 2015 15:35:19 +0200 Subject: [PATCH 09/15] v3 more trying to get the payload right --- lib/travis/api/v3/queries/build.rb | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/travis/api/v3/queries/build.rb b/lib/travis/api/v3/queries/build.rb index 894c3fdb..aa8ff94b 100644 --- a/lib/travis/api/v3/queries/build.rb +++ b/lib/travis/api/v3/queries/build.rb @@ -14,11 +14,8 @@ module Travis::API::V3 end def restart(user) - payload = { - build: { id: id }, - user: { id: user.id } - } - perform_async(:build_restart, type: 'api'.freeze, payload: JSON.dump(payload)) + payload = {id: id, user_id: user.id, source: 'api'} + perform_async(:build_restart, type: 'api'.freeze, payload: payload) payload end end From a2deeb1d96f6f48c983428c5e0ef518b83263c18 Mon Sep 17 00:00:00 2001 From: carlad Date: Wed, 30 Sep 2015 15:49:48 +0200 Subject: [PATCH 10/15] v3 update payload --- lib/travis/api/v3/queries/build.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/travis/api/v3/queries/build.rb b/lib/travis/api/v3/queries/build.rb index aa8ff94b..c791d504 100644 --- a/lib/travis/api/v3/queries/build.rb +++ b/lib/travis/api/v3/queries/build.rb @@ -7,6 +7,7 @@ module Travis::API::V3 raise WrongParams, 'missing build.id'.freeze end + # TODO this must match restart method below def cancel(user) payload = {id: id, user_id: user.id, source: 'api'} perform_async(:build_cancellation, type: 'api'.freeze, payload: JSON.dump(payload)) @@ -14,8 +15,8 @@ module Travis::API::V3 end def restart(user) - payload = {id: id, user_id: user.id, source: 'api'} - perform_async(:build_restart, type: 'api'.freeze, payload: payload) + payload = { id: id, user_id: user.id, source: 'api' } + perform_async(:build_restart, payload: payload) payload end end From e3de9a4f07e1091e73bf1c2ee4480ffdab029c57 Mon Sep 17 00:00:00 2001 From: carlad Date: Wed, 30 Sep 2015 16:19:07 +0200 Subject: [PATCH 11/15] v3 update payload again --- lib/travis/api/v3/queries/build.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/travis/api/v3/queries/build.rb b/lib/travis/api/v3/queries/build.rb index c791d504..ac37edb8 100644 --- a/lib/travis/api/v3/queries/build.rb +++ b/lib/travis/api/v3/queries/build.rb @@ -10,13 +10,13 @@ module Travis::API::V3 # TODO this must match restart method below def cancel(user) payload = {id: id, user_id: user.id, source: 'api'} - perform_async(:build_cancellation, type: 'api'.freeze, payload: JSON.dump(payload)) + perform_async(:build_cancellation, payload) payload end def restart(user) payload = { id: id, user_id: user.id, source: 'api' } - perform_async(:build_restart, payload: payload) + perform_async(:build_restart, payload) payload end end From d8e24bf52fa47a0488dc5c7cda0f63e61c74c493 Mon Sep 17 00:00:00 2001 From: carlad Date: Wed, 30 Sep 2015 16:37:34 +0200 Subject: [PATCH 12/15] v3 fix specs for build canceland restart --- lib/travis/api/v3/queries/build.rb | 3 +-- spec/v3/services/build/cancel_spec.rb | 2 +- spec/v3/services/build/restart_spec.rb | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/travis/api/v3/queries/build.rb b/lib/travis/api/v3/queries/build.rb index ac37edb8..540837d7 100644 --- a/lib/travis/api/v3/queries/build.rb +++ b/lib/travis/api/v3/queries/build.rb @@ -7,9 +7,8 @@ module Travis::API::V3 raise WrongParams, 'missing build.id'.freeze end - # TODO this must match restart method below def cancel(user) - payload = {id: id, user_id: user.id, source: 'api'} + payload = { id: id, user_id: user.id, source: 'api' } perform_async(:build_cancellation, payload) payload end diff --git a/spec/v3/services/build/cancel_spec.rb b/spec/v3/services/build/cancel_spec.rb index db32e36a..6940d4a2 100644 --- a/spec/v3/services/build/cancel_spec.rb +++ b/spec/v3/services/build/cancel_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Travis::API::V3::Services::Build::Cancel do let(:repo) { Travis::API::V3::Models::Repository.where(owner_name: 'svenfuchs', name: 'minimal').first } let(:build) { repo.builds.first } - let(:sidekiq_payload) { JSON.load(Sidekiq::Client.last['args'].last[:payload]) } + 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 diff --git a/spec/v3/services/build/restart_spec.rb b/spec/v3/services/build/restart_spec.rb index 14ba5ce3..40e7ba5d 100644 --- a/spec/v3/services/build/restart_spec.rb +++ b/spec/v3/services/build/restart_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Travis::API::V3::Services::Build::Restart do let(:repo) { Travis::API::V3::Models::Repository.where(owner_name: 'svenfuchs', name: 'minimal').first } let(:build) { repo.builds.first } - let(:sidekiq_payload) { JSON.load(Sidekiq::Client.last['args'].last[:payload]) } + 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 From b6ffb8bdf8baeb4022b39266e5360390eef0eec9 Mon Sep 17 00:00:00 2001 From: carlad Date: Wed, 30 Sep 2015 17:03:00 +0200 Subject: [PATCH 13/15] v3 add job cancellation endpoint ans spec --- lib/travis/api/v3/access_control/generic.rb | 4 + lib/travis/api/v3/queries/job.rb | 14 +- lib/travis/api/v3/routes.rb | 3 + lib/travis/api/v3/services/job/cancel.rb | 13 ++ lib/travis/api/v3/services/job/restart.rb | 13 ++ spec/v3/services/job/cancel_spec.rb | 154 ++++++++++++++++++++ 6 files changed, 200 insertions(+), 1 deletion(-) create mode 100644 lib/travis/api/v3/services/job/cancel.rb create mode 100644 lib/travis/api/v3/services/job/restart.rb create mode 100644 spec/v3/services/job/cancel_spec.rb diff --git a/lib/travis/api/v3/access_control/generic.rb b/lib/travis/api/v3/access_control/generic.rb index 13e990f8..a8a6dab5 100644 --- a/lib/travis/api/v3/access_control/generic.rb +++ b/lib/travis/api/v3/access_control/generic.rb @@ -63,6 +63,10 @@ module Travis::API::V3 visible? job.repository end + def job_writable?(job) + writable? job.repository + end + def organization_visible?(organization) full_access? or public_api? end diff --git a/lib/travis/api/v3/queries/job.rb b/lib/travis/api/v3/queries/job.rb index b25781cb..79efdc87 100644 --- a/lib/travis/api/v3/queries/job.rb +++ b/lib/travis/api/v3/queries/job.rb @@ -4,7 +4,19 @@ module Travis::API::V3 def find return Models::Job.find_by_id(id) if id - raise WrongParams, 'missing build.id'.freeze + raise WrongParams, 'missing job.id'.freeze + end + + def cancel(user) + payload = { id: id, user_id: user.id, source: 'api' } + perform_async(:job_cancellation, payload) + payload + end + + def restart(user) + payload = { id: id, user_id: user.id, source: 'api' } + perform_async(:job_restart, payload) + payload end end end diff --git a/lib/travis/api/v3/routes.rb b/lib/travis/api/v3/routes.rb index ca4e4988..3ba42268 100644 --- a/lib/travis/api/v3/routes.rb +++ b/lib/travis/api/v3/routes.rb @@ -26,6 +26,9 @@ module Travis::API::V3 capture id: :digit route '/job/{job.id}' get :find + + post :cancel, '/cancel' + post :restart, '/restart' end resource :organization do diff --git a/lib/travis/api/v3/services/job/cancel.rb b/lib/travis/api/v3/services/job/cancel.rb new file mode 100644 index 00000000..0b565498 --- /dev/null +++ b/lib/travis/api/v3/services/job/cancel.rb @@ -0,0 +1,13 @@ +module Travis::API::V3 + class Services::Job::Cancel < Service + + def run + raise LoginRequired unless access_control.logged_in? or access_control.full_access? + raise NotFound unless job = find(:job) + access_control.permissions(job).cancel! + + query.cancel(access_control.user) + accepted(job: job, state_change: :cancel) + end + end +end diff --git a/lib/travis/api/v3/services/job/restart.rb b/lib/travis/api/v3/services/job/restart.rb new file mode 100644 index 00000000..a10dc71b --- /dev/null +++ b/lib/travis/api/v3/services/job/restart.rb @@ -0,0 +1,13 @@ +module Travis::API::V3 + class Services::Job::Restart < Service + + def run + raise LoginRequired unless access_control.logged_in? or access_control.full_access? + raise NotFound unless job = find(:job) + access_control.permissions(job).restart! + + query.restart(access_control.user) + accepted(job: job, state_change: :restart) + end + end +end diff --git a/spec/v3/services/job/cancel_spec.rb b/spec/v3/services/job/cancel_spec.rb new file mode 100644 index 00000000..6eb30301 --- /dev/null +++ b/spec/v3/services/job/cancel_spec.rb @@ -0,0 +1,154 @@ +require 'spec_helper' + +describe Travis::API::V3::Services::Job::Cancel do + let(:repo) { Travis::API::V3::Models::Repository.where(owner_name: 'svenfuchs', name: 'minimal').first } + let(:build) { repo.builds.first } + let(:job) { build.jobs.first} + 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 + 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/job/#{job.id}/cancel") } + 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 build, authenticated" do + let(:token) { Travis::Api::App::AccessToken.create(user: repo.owner, app_id: 1) } + let(:headers) {{ 'HTTP_AUTHORIZATION' => "token #{token}" }} + before { post("/v3/job/9999999999/cancel", {}, headers) } + + example { expect(last_response.status).to be == 404 } + example { expect(JSON.load(body)).to be == { + "@type" => "error", + "error_type" => "not_found", + "error_message" => "job not found (or insufficient access)", + "resource_type" => "job" + }} + 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/job/#{job.id}/cancel", {}, 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 cancel access to job", + "resource_type", + "job", + "permission", + "cancel") + } + 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/job/#{job.id}/cancel", {}, 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" => "job not found (or insufficient access)", + "resource_type" => "job" + }} + end + + describe "existing repository, push access" do + let(:params) {{}} + let(:token) { Travis::Api::App::AccessToken.create(user: repo.owner, app_id: 1) } + let(:headers) {{ 'HTTP_AUTHORIZATION' => "token #{token}" }} + before { Travis::API::V3::Models::Permission.create(repository: repo, user: repo.owner, push: true) } + before { post("/v3/job/#{job.id}/cancel", params, headers) } + + example { expect(last_response.status).to be == 202 } + example { expect(JSON.load(body).to_s).to include( + "@type", + "job", + "@href", + "@representation", + "minimal", + "cancel", + "id", + "state_change") + } + + example { expect(sidekiq_payload).to be == { + "id" => "#{job.id}", + "user_id"=> repo.owner_id, + "source" => "api"} + } + + example { expect(Sidekiq::Client.last['queue']).to be == 'job_cancellations' } + example { expect(Sidekiq::Client.last['class']).to be == 'Travis::Sidekiq::JobCancellation' } + + describe "setting id has no effect" do + let(:params) {{ id: 42 }} + example { expect(sidekiq_payload).to be == { + "id" => "#{job.id}", + "user_id"=> repo.owner_id, + "source" => "api"} + } + end + end + + # TODO decided to discuss further with rkh as this use case doesn't really exist at the moment + # and 'fixing' the query requires modifying workers that v2 uses, thereby running the risk of breaking v2, + # and also because in 6 months or so travis-hub will be able to cancel builds without using travis-core at all. + # + # describe "existing repository, application with full access" do + # let(:app_name) { 'travis-example' } + # let(:app_secret) { '12345678' } + # let(:sign_opts) { "a=#{app_name}" } + # let(:signature) { OpenSSL::HMAC.hexdigest('sha256', app_secret, sign_opts) } + # let(:headers) {{ 'HTTP_AUTHORIZATION' => "signature #{sign_opts}:#{signature}" }} + # before { Travis.config.applications = { app_name => { full_access: true, secret: app_secret }}} + # before { post("/v3/job/#{job.id}/cancel", params, headers) } + # + # describe 'without setting user' do + # let(:params) {{}} + # example { expect(last_response.status).to be == 400 } + # example { expect(JSON.load(body)).to be == { + # "@type" => "error", + # "error_type" => "wrong_params", + # "error_message" => "missing user" + # }} + # end + # + # describe 'setting user' do + # let(:params) {{ user: { id: repo.owner.id } }} + # example { expect(last_response.status).to be == 202 } + # example { expect(sidekiq_payload).to be == { + # # repository: { id: repo.id, owner_name: 'svenfuchs', name: 'minimal' }, + # # user: { id: repo.owner.id }, + # # message: nil, + # # branch: 'master', + # # config: {} + # }} + # end + # end +end From fd03316de78b696af48d451f88ef4d5797f22f47 Mon Sep 17 00:00:00 2001 From: carlad Date: Wed, 30 Sep 2015 17:07:59 +0200 Subject: [PATCH 14/15] v3 add spec for job restart --- spec/v3/services/job/restart_spec.rb | 155 +++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 spec/v3/services/job/restart_spec.rb diff --git a/spec/v3/services/job/restart_spec.rb b/spec/v3/services/job/restart_spec.rb new file mode 100644 index 00000000..c4884f41 --- /dev/null +++ b/spec/v3/services/job/restart_spec.rb @@ -0,0 +1,155 @@ +require 'spec_helper' + +describe Travis::API::V3::Services::Job::Restart do + let(:repo) { Travis::API::V3::Models::Repository.where(owner_name: 'svenfuchs', name: 'minimal').first } + let(:build) { repo.builds.first } + let(:job) { build.jobs.first } + 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 + 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/job/#{job.id}/restart") } + 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 build, authenticated" do + let(:token) { Travis::Api::App::AccessToken.create(user: repo.owner, app_id: 1) } + let(:headers) {{ 'HTTP_AUTHORIZATION' => "token #{token}" }} + before { post("/v3/job/9999999999/restart", {}, headers) } + + example { expect(last_response.status).to be == 404 } + example { expect(JSON.load(body)).to be == { + "@type" => "error", + "error_type" => "not_found", + "error_message" => "job not found (or insufficient access)", + "resource_type" => "job" + }} + 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/job/#{job.id}/restart", {}, 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 restart access to job", + "resource_type", + "job", + "permission", + "restart") + } + 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/job/#{job.id}/restart", {}, 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" => "job not found (or insufficient access)", + "resource_type" => "job" + }} + end + + describe "existing repository, push access" do + let(:params) {{}} + let(:token) { Travis::Api::App::AccessToken.create(user: repo.owner, app_id: 1) } + let(:headers) {{ 'HTTP_AUTHORIZATION' => "token #{token}" }} + before { Travis::API::V3::Models::Permission.create(repository: repo, user: repo.owner, push: true) } + before { post("/v3/job/#{job.id}/restart", params, headers) } + + example { expect(last_response.status).to be == 202 } + example { expect(JSON.load(body).to_s).to include( + "@type", + "pending", + "job", + "@href", + "@representation", + "minimal", + "restart", + "id", + "state_change") + } + + example { expect(sidekiq_payload).to be == { + "id" => "#{job.id}", + "user_id"=> repo.owner_id, + "source" => "api"} + } + + example { expect(Sidekiq::Client.last['queue']).to be == 'job_restarts' } + example { expect(Sidekiq::Client.last['class']).to be == 'Travis::Sidekiq::JobRestart' } + + describe "setting id has no effect" do + let(:params) {{ id: 42 }} + example { expect(sidekiq_payload).to be == { + "id" => "#{job.id}", + "user_id"=> repo.owner_id, + "source" => "api"} + } + end + end + + # TODO decided to discuss further with rkh as this use case doesn't really exist at the moment + # and 'fixing' the query requires modifying workers that v2 uses, thereby running the risk of breaking v2, + # and also because in 6 months or so travis-hub will be able to cancel builds without using travis-core at all. + # + # describe "existing repository, application with full access" do + # let(:app_name) { 'travis-example' } + # let(:app_secret) { '12345678' } + # let(:sign_opts) { "a=#{app_name}" } + # let(:signature) { OpenSSL::HMAC.hexdigest('sha256', app_secret, sign_opts) } + # let(:headers) {{ 'HTTP_AUTHORIZATION' => "signature #{sign_opts}:#{signature}" }} + # before { Travis.config.applications = { app_name => { full_access: true, secret: app_secret }}} + # before { post("/v3/job/#{job.id}/restart", params, headers) } + # + # describe 'without setting user' do + # let(:params) {{}} + # example { expect(last_response.status).to be == 400 } + # example { expect(JSON.load(body)).to be == { + # "@type" => "error", + # "error_type" => "wrong_params", + # "error_message" => "missing user" + # }} + # end + # + # describe 'setting user' do + # let(:params) {{ user: { id: repo.owner.id } }} + # example { expect(last_response.status).to be == 202 } + # example { expect(sidekiq_payload).to be == { + # # repository: { id: repo.id, owner_name: 'svenfuchs', name: 'minimal' }, + # # user: { id: repo.owner.id }, + # # message: nil, + # # branch: 'master', + # # config: {} + # }} + # end + # end +end From 36d361b7af7713a9ce57ca3baeb8dfef2e399f22 Mon Sep 17 00:00:00 2001 From: carlad Date: Wed, 30 Sep 2015 17:19:08 +0200 Subject: [PATCH 15/15] remove empty line --- lib/travis/api/workers/job_restart.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/travis/api/workers/job_restart.rb b/lib/travis/api/workers/job_restart.rb index 12ab6b33..88352a58 100644 --- a/lib/travis/api/workers/job_restart.rb +++ b/lib/travis/api/workers/job_restart.rb @@ -13,7 +13,6 @@ module Travis user = User.find(data['user_id']) Travis.service(:reset_model, user, job_id: data['id']).run end - end end end