Commit 111748ea authored by Z.J. van de Weg's avatar Z.J. van de Weg

Rename Builds to Jobs in the API

Fixes gitlab-org/gitlab-ce#28515

[ci skip]
parent 348dff0a
......@@ -9,6 +9,7 @@ module API
mount ::API::V3::Boards
mount ::API::V3::Branches
mount ::API::V3::BroadcastMessages
mount ::API::V3::Builds
mount ::API::V3::Commits
mount ::API::V3::DeployKeys
mount ::API::V3::Environments
......@@ -77,7 +78,6 @@ module API
mount ::API::Boards
mount ::API::Branches
mount ::API::BroadcastMessages
mount ::API::Builds
mount ::API::Commits
mount ::API::CommitStatuses
mount ::API::DeployKeys
......@@ -87,6 +87,7 @@ module API
mount ::API::Groups
mount ::API::Internal
mount ::API::Issues
mount ::API::Jobs
mount ::API::Keys
mount ::API::Labels
mount ::API::Lint
......
......@@ -49,7 +49,8 @@ module API
class ProjectHook < Hook
expose :project_id, :issues_events, :merge_requests_events
expose :note_events, :build_events, :pipeline_events, :wiki_page_events
expose :note_events, :pipeline_events, :wiki_page_events
expose :job_events, as: :build_events
end
class BasicProjectDetails < Grape::Entity
......@@ -80,7 +81,7 @@ module API
expose(:issues_enabled) { |project, options| project.feature_available?(:issues, options[:current_user]) }
expose(:merge_requests_enabled) { |project, options| project.feature_available?(:merge_requests, options[:current_user]) }
expose(:wiki_enabled) { |project, options| project.feature_available?(:wiki, options[:current_user]) }
expose(:builds_enabled) { |project, options| project.feature_available?(:builds, options[:current_user]) }
expose(:jobs_enabled) { |project, options| project.feature_available?(:builds, options[:current_user]) }
expose(:snippets_enabled) { |project, options| project.feature_available?(:snippets, options[:current_user]) }
expose :created_at, :last_activity_at
......@@ -93,7 +94,7 @@ module API
expose :star_count, :forks_count
expose :open_issues_count, if: lambda { |project, options| project.feature_available?(:issues, options[:current_user]) && project.default_issues_tracker? }
expose :runners_token, if: lambda { |_project, options| options[:user_can_admin_project] }
expose :public_builds
expose :public_jobs, as: :public_builds
expose :shared_with_groups do |project, options|
SharedGroup.represent(project.project_group_links.all, options)
end
......@@ -109,7 +110,7 @@ module API
expose :storage_size
expose :repository_size
expose :lfs_objects_size
expose :build_artifacts_size
expose :job_artifacts_size, as: :build_artifacts_size
end
class Member < UserBasic
......@@ -144,7 +145,7 @@ module API
expose :storage_size
expose :repository_size
expose :lfs_objects_size
expose :build_artifacts_size
expose :job_artifacts_size, as: :build_artifacts_size
end
end
end
......@@ -448,7 +449,8 @@ module API
class ProjectService < Grape::Entity
expose :id, :title, :created_at, :updated_at, :active
expose :push_events, :issues_events, :merge_requests_events
expose :tag_push_events, :note_events, :build_events, :pipeline_events
expose :tag_push_events, :note_events, :pipeline_events
expose :job_events, as: :build_events
# Expose serialized properties
expose :properties do |service, options|
field_names = service.fields.
......@@ -616,11 +618,15 @@ module API
end
end
<<<<<<< HEAD
class RunnerRegistrationDetails < Grape::Entity
expose :id, :token
end
class BuildArtifactFile < Grape::Entity
=======
class JobArtifactFile < Grape::Entity
>>>>>>> 239b5f49c5... Rename Builds to Jobs in the API
expose :filename, :size
end
......@@ -628,11 +634,11 @@ module API
expose :id, :sha, :ref, :status
end
class Build < Grape::Entity
class Job < Grape::Entity
expose :id, :status, :stage, :name, :ref, :tag, :coverage
expose :created_at, :started_at, :finished_at
expose :user, with: User
expose :artifacts_file, using: BuildArtifactFile, if: -> (build, opts) { build.artifacts? }
expose :artifacts_file, using: JobArtifactFile, if: -> (build, opts) { build.artifacts? }
expose :commit, with: RepoCommit
expose :runner, with: Runner
expose :pipeline, with: PipelineBasic
......@@ -670,7 +676,7 @@ module API
expose :id, :iid, :ref, :sha, :created_at
expose :user, using: Entities::UserBasic
expose :environment, using: Entities::EnvironmentBasic
expose :deployable, using: Entities::Build
expose :deployable, using: Entities::Job
end
class RepoLicense < Grape::Entity
......
module API
class Builds < Grape::API
class Jobs < Grape::API
include PaginationParams
before { authenticate! }
......@@ -13,9 +13,10 @@ module API
optional :scope, types: [String, Array[String]], desc: 'The scope of builds to show',
values: ::CommitStatus::AVAILABLE_STATUSES,
coerce_with: ->(scope) {
if scope.is_a?(String)
case scope
when String
[scope]
elsif scope.is_a?(Hashie::Mash)
when Hashie::Mash
scope.values
else
['unknown']
......@@ -24,30 +25,30 @@ module API
end
end
desc 'Get a project builds' do
success Entities::Build
desc 'Get a projects jobs' do
success Entities::Job
end
params do
use :optional_scope
use :pagination
end
get ':id/builds' do
get ':id/jobs' do
builds = user_project.builds.order('id DESC')
builds = filter_builds(builds, params[:scope])
present paginate(builds), with: Entities::Build,
present paginate(builds), with: Entities::Job,
user_can_download_artifacts: can?(current_user, :read_build, user_project)
end
desc 'Get builds for a specific commit of a project' do
success Entities::Build
desc 'Get jobs for a specific commit of a project' do
success Entities::Job
end
params do
requires :sha, type: String, desc: 'The SHA id of a commit'
use :optional_scope
use :pagination
end
get ':id/repository/commits/:sha/builds' do
get ':id/repository/commits/:sha/jobs' do
authorize_read_builds!
return not_found! unless user_project.commit(params[:sha])
......@@ -56,47 +57,47 @@ module API
builds = user_project.builds.where(pipeline: pipelines).order('id DESC')
builds = filter_builds(builds, params[:scope])
present paginate(builds), with: Entities::Build,
present paginate(builds), with: Entities::Job,
user_can_download_artifacts: can?(current_user, :read_build, user_project)
end
desc 'Get a specific build of a project' do
success Entities::Build
desc 'Get a specific job of a project' do
success Entities::Job
end
params do
requires :build_id, type: Integer, desc: 'The ID of a build'
requires :job_id, type: Integer, desc: 'The ID of a job'
end
get ':id/builds/:build_id' do
get ':id/jobs/:job_id' do
authorize_read_builds!
build = get_build!(params[:build_id])
build = get_build!(params[:job_id])
present build, with: Entities::Build,
present build, with: Entities::Job,
user_can_download_artifacts: can?(current_user, :read_build, user_project)
end
desc 'Download the artifacts file from build' do
desc 'Download the artifacts file from a job' do
detail 'This feature was introduced in GitLab 8.5'
end
params do
requires :build_id, type: Integer, desc: 'The ID of a build'
requires :job_id, type: Integer, desc: 'The ID of a job'
end
get ':id/builds/:build_id/artifacts' do
get ':id/jobs/:job_id/artifacts' do
authorize_read_builds!
build = get_build!(params[:build_id])
build = get_build!(params[:job_id])
present_artifacts!(build.artifacts_file)
end
desc 'Download the artifacts file from build' do
desc 'Download the artifacts file from a job' do
detail 'This feature was introduced in GitLab 8.10'
end
params do
requires :ref_name, type: String, desc: 'The ref from repository'
requires :job, type: String, desc: 'The name for the build'
requires :job, type: String, desc: 'The name for the job'
end
get ':id/builds/artifacts/:ref_name/download',
get ':id/jobs/artifacts/:ref_name/download',
requirements: { ref_name: /.+/ } do
authorize_read_builds!
......@@ -109,14 +110,14 @@ module API
# TODO: We should use `present_file!` and leave this implementation for backward compatibility (when build trace
# is saved in the DB instead of file). But before that, we need to consider how to replace the value of
# `runners_token` with some mask (like `xxxxxx`) when sending trace file directly by workhorse.
desc 'Get a trace of a specific build of a project'
desc 'Get a trace of a specific job of a project'
params do
requires :build_id, type: Integer, desc: 'The ID of a build'
requires :job_id, type: Integer, desc: 'The ID of a job'
end
get ':id/builds/:build_id/trace' do
get ':id/jobs/:job_id/trace' do
authorize_read_builds!
build = get_build!(params[:build_id])
build = get_build!(params[:job_id])
header 'Content-Disposition', "infile; filename=\"#{build.id}.log\""
content_type 'text/plain'
......@@ -126,95 +127,95 @@ module API
body trace
end
desc 'Cancel a specific build of a project' do
success Entities::Build
desc 'Cancel a specific job of a project' do
success Entities::Job
end
params do
requires :build_id, type: Integer, desc: 'The ID of a build'
requires :job_id, type: Integer, desc: 'The ID of a job'
end
post ':id/builds/:build_id/cancel' do
post ':id/jobs/:job_id/cancel' do
authorize_update_builds!
build = get_build!(params[:build_id])
build = get_build!(params[:job_id])
build.cancel
present build, with: Entities::Build,
present build, with: Entities::Job,
user_can_download_artifacts: can?(current_user, :read_build, user_project)
end
desc 'Retry a specific build of a project' do
success Entities::Build
success Entities::Job
end
params do
requires :build_id, type: Integer, desc: 'The ID of a build'
requires :job_id, type: Integer, desc: 'The ID of a build'
end
post ':id/builds/:build_id/retry' do
post ':id/jobs/:job_id/retry' do
authorize_update_builds!
build = get_build!(params[:build_id])
return forbidden!('Build is not retryable') unless build.retryable?
build = get_build!(params[:job_id])
return forbidden!('Job is not retryable') unless build.retryable?
build = Ci::Build.retry(build, current_user)
present build, with: Entities::Build,
present build, with: Entities::Job,
user_can_download_artifacts: can?(current_user, :read_build, user_project)
end
desc 'Erase build (remove artifacts and build trace)' do
success Entities::Build
desc 'Erase job (remove artifacts and the trace)' do
success Entities::Job
end
params do
requires :build_id, type: Integer, desc: 'The ID of a build'
requires :job_id, type: Integer, desc: 'The ID of a build'
end
post ':id/builds/:build_id/erase' do
post ':id/jobs/:job_id/erase' do
authorize_update_builds!
build = get_build!(params[:build_id])
return forbidden!('Build is not erasable!') unless build.erasable?
build = get_build!(params[:job_id])
return forbidden!('Job is not erasable!') unless build.erasable?
build.erase(erased_by: current_user)
present build, with: Entities::Build,
present build, with: Entities::Job,
user_can_download_artifacts: can?(current_user, :download_build_artifacts, user_project)
end
desc 'Keep the artifacts to prevent them from being deleted' do
success Entities::Build
success Entities::Job
end
params do
requires :build_id, type: Integer, desc: 'The ID of a build'
requires :job_id, type: Integer, desc: 'The ID of a job'
end
post ':id/builds/:build_id/artifacts/keep' do
post ':id/jobs/:job_id/artifacts/keep' do
authorize_update_builds!
build = get_build!(params[:build_id])
build = get_build!(params[:job_id])
return not_found!(build) unless build.artifacts?
build.keep_artifacts!
status 200
present build, with: Entities::Build,
present build, with: Entities::Job,
user_can_download_artifacts: can?(current_user, :read_build, user_project)
end
desc 'Trigger a manual build' do
success Entities::Build
desc 'Trigger a manual job' do
success Entities::Job
detail 'This feature was added in GitLab 8.11'
end
params do
requires :build_id, type: Integer, desc: 'The ID of a Build'
requires :job_id, type: Integer, desc: 'The ID of a Job'
end
post ":id/builds/:build_id/play" do
post ":id/jobs/:job_id/play" do
authorize_read_builds!
build = get_build!(params[:build_id])
build = get_build!(params[:job_id])
bad_request!("Unplayable Job") unless build.playable?
build.play(current_user)
status 200
present build, with: Entities::Build,
present build, with: Entities::Job,
user_can_download_artifacts: can?(current_user, :read_build, user_project)
end
end
......
module API
module V3
class Builds < Grape::API
include PaginationParams
before { authenticate! }
params do
requires :id, type: String, desc: 'The ID of a project'
end
resource :projects do
helpers do
params :optional_scope do
optional :scope, types: [String, Array[String]], desc: 'The scope of builds to show',
values: ['pending', 'running', 'failed', 'success', 'canceled'],
coerce_with: ->(scope) {
if scope.is_a?(String)
[scope]
elsif scope.is_a?(Hashie::Mash)
scope.values
else
['unknown']
end
}
end
end
desc 'Get a project builds' do
success V3::Entities::Build
end
params do
use :optional_scope
use :pagination
end
get ':id/builds' do
builds = user_project.builds.order('id DESC')
builds = filter_builds(builds, params[:scope])
present paginate(builds), with: Entities::Build,
user_can_download_artifacts: can?(current_user, :read_build, user_project)
end
desc 'Get builds for a specific commit of a project' do
success Entities::Build
end
params do
requires :sha, type: String, desc: 'The SHA id of a commit'
use :optional_scope
use :pagination
end
get ':id/repository/commits/:sha/builds' do
authorize_read_builds!
return not_found! unless user_project.commit(params[:sha])
pipelines = user_project.pipelines.where(sha: params[:sha])
builds = user_project.builds.where(pipeline: pipelines).order('id DESC')
builds = filter_builds(builds, params[:scope])
present paginate(builds), with: Entities::Build,
user_can_download_artifacts: can?(current_user, :read_build, user_project)
end
desc 'Get a specific build of a project' do
success Entities::Build
end
params do
requires :build_id, type: Integer, desc: 'The ID of a build'
end
get ':id/builds/:build_id' do
authorize_read_builds!
build = get_build!(params[:build_id])
present build, with: Entities::Build,
user_can_download_artifacts: can?(current_user, :read_build, user_project)
end
desc 'Download the artifacts file from build' do
detail 'This feature was introduced in GitLab 8.5'
end
params do
requires :build_id, type: Integer, desc: 'The ID of a build'
end
get ':id/builds/:build_id/artifacts' do
authorize_read_builds!
build = get_build!(params[:build_id])
present_artifacts!(build.artifacts_file)
end
desc 'Download the artifacts file from build' do
detail 'This feature was introduced in GitLab 8.10'
end
params do
requires :ref_name, type: String, desc: 'The ref from repository'
requires :job, type: String, desc: 'The name for the build'
end
get ':id/builds/artifacts/:ref_name/download',
requirements: { ref_name: /.+/ } do
authorize_read_builds!
builds = user_project.latest_successful_builds_for(params[:ref_name])
latest_build = builds.find_by!(name: params[:job])
present_artifacts!(latest_build.artifacts_file)
end
# TODO: We should use `present_file!` and leave this implementation for backward compatibility (when build trace
# is saved in the DB instead of file). But before that, we need to consider how to replace the value of
# `runners_token` with some mask (like `xxxxxx`) when sending trace file directly by workhorse.
desc 'Get a trace of a specific build of a project'
params do
requires :build_id, type: Integer, desc: 'The ID of a build'
end
get ':id/builds/:build_id/trace' do
authorize_read_builds!
build = get_build!(params[:build_id])
header 'Content-Disposition', "infile; filename=\"#{build.id}.log\""
content_type 'text/plain'
env['api.format'] = :binary
trace = build.trace
body trace
end
desc 'Cancel a specific build of a project' do
success Entities::Build
end
params do
requires :build_id, type: Integer, desc: 'The ID of a build'
end
post ':id/builds/:build_id/cancel' do
authorize_update_builds!
build = get_build!(params[:build_id])
build.cancel
present build, with: Entities::Build,
user_can_download_artifacts: can?(current_user, :read_build, user_project)
end
desc 'Retry a specific build of a project' do
success Entities::Build
end
params do
requires :build_id, type: Integer, desc: 'The ID of a build'
end
post ':id/builds/:build_id/retry' do
authorize_update_builds!
build = get_build!(params[:build_id])
return forbidden!('Build is not retryable') unless build.retryable?
build = Ci::Build.retry(build, current_user)
present build, with: Entities::Build,
user_can_download_artifacts: can?(current_user, :read_build, user_project)
end
desc 'Erase build (remove artifacts and build trace)' do
success Entities::Build
end
params do
requires :build_id, type: Integer, desc: 'The ID of a build'
end
post ':id/builds/:build_id/erase' do
authorize_update_builds!
build = get_build!(params[:build_id])
return forbidden!('Build is not erasable!') unless build.erasable?
build.erase(erased_by: current_user)
present build, with: Entities::Build,
user_can_download_artifacts: can?(current_user, :download_build_artifacts, user_project)
end
desc 'Keep the artifacts to prevent them from being deleted' do
success Entities::Build
end
params do
requires :build_id, type: Integer, desc: 'The ID of a build'
end
post ':id/builds/:build_id/artifacts/keep' do
authorize_update_builds!
build = get_build!(params[:build_id])
return not_found!(build) unless build.artifacts?
build.keep_artifacts!
status 200
present build, with: Entities::Build,
user_can_download_artifacts: can?(current_user, :read_build, user_project)
end
desc 'Trigger a manual build' do
success Entities::Build
detail 'This feature was added in GitLab 8.11'
end
params do
requires :build_id, type: Integer, desc: 'The ID of a Build'
end
post ":id/builds/:build_id/play" do
authorize_read_builds!
build = get_build!(params[:build_id])
bad_request!("Unplayable Job") unless build.playable?
build.play(current_user)
status 200
present build, with: Entities::Build,
user_can_download_artifacts: can?(current_user, :read_build, user_project)
end
end
helpers do
def get_build(id)
user_project.builds.find_by(id: id.to_i)
end
def get_build!(id)
get_build(id) || not_found!
end
def present_artifacts!(artifacts_file)
if !artifacts_file.file_storage?
redirect_to(build.artifacts_file.url)
elsif artifacts_file.exists?
present_file!(artifacts_file.path, artifacts_file.filename)
else
not_found!
end
end
def filter_builds(builds, scope)
return builds if scope.nil? || scope.empty?
available_statuses = ::CommitStatus::AVAILABLE_STATUSES
unknown = scope - available_statuses
render_api_error!('Scope contains invalid value(s)', 400) unless unknown.empty?
builds.where(status: available_statuses && scope)
end
def authorize_read_builds!
authorize! :read_build, user_project
end
def authorize_update_builds!
authorize! :update_build, user_project
end
end
end
end
end
......@@ -195,6 +195,16 @@ module API
class TriggerRequest < Grape::Entity
expose :id, :variables
end
class Build < Grape::Entity
expose :id, :status, :stage, :name, :ref, :tag, :coverage
expose :created_at, :started_at, :finished_at
expose :user, with: ::API::Entities::User
expose :artifacts_file, using: ::API::Entities::JobArtifactFile, if: -> (build, opts) { build.artifacts? }
expose :commit, with: ::API::Entities::RepoCommit
expose :runner, with: ::API::Entities::Runner
expose :pipeline, with: ::API::Entities::PipelineBasic
end
end
end
end
require 'spec_helper'
describe API::Jobs, api: true do
include ApiHelpers
let(:user) { create(:user) }
let(:api_user) { user }
let!(:project) { create(:project, :repository, creator: user, public_builds: false) }
let!(:developer) { create(:project_member, :developer, user: user, project: project) }
let(:reporter) { create(:project_member, :reporter, project: project) }
let(:guest) { create(:project_member, :guest, project: project) }
let!(:pipeline) { create(:ci_empty_pipeline, project: project, sha: project.commit.id, ref: project.default_branch) }
let!(:build) { create(:ci_build, pipeline: pipeline) }
describe 'GET /projects/:id/jobs' do
let(:query) { Hash.new }
before do
get api("/projects/#{project.id}/jobs", api_user), query
end
context 'authorized user' do
it 'returns project jobs' do
expect(response).to have_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
end
it 'returns correct values' do
expect(json_response).not_to be_empty
expect(json_response.first['commit']['id']).to eq project.commit.id
end
it 'returns pipeline data' do
json_build = json_response.first
expect(json_build['pipeline']).not_to be_empty
expect(json_build['pipeline']['id']).to eq build.pipeline.id
expect(json_build['pipeline']['ref']).to eq build.pipeline.ref
expect(json_build['pipeline']['sha']).to eq build.pipeline.sha
expect(json_build['pipeline']['status']).to eq build.pipeline.status
end
context 'filter project with one scope element' do
let(:query) { { 'scope' => 'pending' } }
it do
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
end
end
context 'filter project with array of scope elements' do
let(:query) { { 'scope[0]' => 'pending', 'scope[1]' => 'running' } }
it do
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
end
end
context 'respond 400 when scope contains invalid state' do
let(:query) { { 'scope[0]' => 'unknown', 'scope[1]' => 'running' } }
it { expect(response).to have_http_status(400) }
end
end
context 'unauthorized user' do
let(:api_user) { nil }
it 'does not return project builds' do
expect(response).to have_http_status(401)
end
end
end
describe 'GET /projects/:id/repository/commits/:sha/jobs' do
context 'when commit does not exist in repository' do
before do
get api("/projects/#{project.id}/repository/commits/1a271fd1/jobs", api_user)
end
it 'responds with 404' do
expect(response).to have_http_status(404)
end
end
context 'when commit exists in repository' do
context 'when user is authorized' do
context 'when pipeline has jobs' do
before do
create(:ci_pipeline, project: project, sha: project.commit.id)
create(:ci_build, pipeline: pipeline)
create(:ci_build)
get api("/projects/#{project.id}/repository/commits/#{project.commit.id}/jobs", api_user)
end
it 'returns project jobs for specific commit' do
expect(response).to have_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.size).to eq 2
end
it 'returns pipeline data' do
json_build = json_response.first
expect(json_build['pipeline']).not_to be_empty
expect(json_build['pipeline']['id']).to eq build.pipeline.id
expect(json_build['pipeline']['ref']).to eq build.pipeline.ref
expect(json_build['pipeline']['sha']).to eq build.pipeline.sha
expect(json_build['pipeline']['status']).to eq build.pipeline.status
end
end
context 'when pipeline has no jobs' do
before do
branch_head = project.commit('feature').id
get api("/projects/#{project.id}/repository/commits/#{branch_head}/jobs", api_user)
end
it 'returns an empty array' do
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response).to be_empty
end
end
end
context 'when user is not authorized' do
before do
create(:ci_pipeline, project: project, sha: project.commit.id)
create(:ci_build, pipeline: pipeline)
get api("/projects/#{project.id}/repository/commits/#{project.commit.id}/jobs", nil)
end
it 'does not return project jobs' do
expect(response).to have_http_status(401)
expect(json_response.except('message')).to be_empty
end
end
end
end
describe 'GET /projects/:id/jobs/:job_id' do
before do
get api("/projects/#{project.id}/jobs/#{build.id}", api_user)
end
context 'authorized user' do
it 'returns specific job data' do
expect(response).to have_http_status(200)
expect(json_response['name']).to eq('test')
end
it 'returns pipeline data' do
json_build = json_response
expect(json_build['pipeline']).not_to be_empty
expect(json_build['pipeline']['id']).to eq build.pipeline.id
expect(json_build['pipeline']['ref']).to eq build.pipeline.ref
expect(json_build['pipeline']['sha']).to eq build.pipeline.sha
expect(json_build['pipeline']['status']).to eq build.pipeline.status
end
end
context 'unauthorized user' do
let(:api_user) { nil }
it 'does not return specific job data' do
expect(response).to have_http_status(401)
end
end
end
describe 'GET /projects/:id/jobs/:job_id/artifacts' do
before do
get api("/projects/#{project.id}/jobs/#{build.id}/artifacts", api_user)
end
context 'job with artifacts' do
let(:build) { create(:ci_build, :artifacts, pipeline: pipeline) }
context 'authorized user' do
let(:download_headers) do
{ 'Content-Transfer-Encoding' => 'binary',
'Content-Disposition' => 'attachment; filename=ci_build_artifacts.zip' }
end
it 'returns specific job artifacts' do
expect(response).to have_http_status(200)
expect(response.headers).to include(download_headers)
expect(response.body).to match_file(build.artifacts_file.file.file)
end
end
context 'unauthorized user' do
let(:api_user) { nil }
it 'does not return specific job artifacts' do
expect(response).to have_http_status(401)
end
end
end
it 'does not return job artifacts if not uploaded' do
expect(response).to have_http_status(404)
end
end
describe 'GET /projects/:id/artifacts/:ref_name/download?job=name' do
let(:api_user) { reporter.user }
let(:build) { create(:ci_build, :artifacts, pipeline: pipeline) }
before do
build.success
end
def get_for_ref(ref = pipeline.ref, job = build.name)
get api("/projects/#{project.id}/jobs/artifacts/#{ref}/download", api_user), job: job
end
context 'when not logged in' do
let(:api_user) { nil }
before do
get_for_ref
end
it 'gives 401' do
expect(response).to have_http_status(401)
end
end
context 'when logging as guest' do
let(:api_user) { guest.user }
before do
get_for_ref
end
it 'gives 403' do
expect(response).to have_http_status(403)
end
end
context 'non-existing job' do
shared_examples 'not found' do
it { expect(response).to have_http_status(:not_found) }
end
context 'has no such ref' do
before do
get_for_ref('TAIL')
end
it_behaves_like 'not found'
end
context 'has no such job' do
before do
get_for_ref(pipeline.ref, 'NOBUILD')
end
it_behaves_like 'not found'
end
end
context 'find proper job' do
shared_examples 'a valid file' do
let(:download_headers) do
{ 'Content-Transfer-Encoding' => 'binary',
'Content-Disposition' =>
"attachment; filename=#{build.artifacts_file.filename}" }
end
it { expect(response).to have_http_status(200) }
it { expect(response.headers).to include(download_headers) }
end
context 'with regular branch' do
before do
pipeline.reload
pipeline.update(ref: 'master',
sha: project.commit('master').sha)
get_for_ref('master')
end
it_behaves_like 'a valid file'
end
context 'with branch name containing slash' do
before do
pipeline.reload
pipeline.update(ref: 'improve/awesome',
sha: project.commit('improve/awesome').sha)
end
before do
get_for_ref('improve/awesome')
end
it_behaves_like 'a valid file'
end
end
end
describe 'GET /projects/:id/jobs/:job_id/trace' do
let(:build) { create(:ci_build, :trace, pipeline: pipeline) }
before do
get api("/projects/#{project.id}/jobs/#{build.id}/trace", api_user)
end
context 'authorized user' do
it 'returns specific job trace' do
expect(response).to have_http_status(200)
expect(response.body).to eq(build.trace)
end
end
context 'unauthorized user' do
let(:api_user) { nil }
it 'does not return specific job trace' do
expect(response).to have_http_status(401)
end
end
end
describe 'POST /projects/:id/jobs/:job_id/cancel' do
before do
post api("/projects/#{project.id}/jobs/#{build.id}/cancel", api_user)
end
context 'authorized user' do
context 'user with :update_build persmission' do
it 'cancels running or pending job' do
expect(response).to have_http_status(201)
expect(project.builds.first.status).to eq('canceled')
end
end
context 'user without :update_build permission' do
let(:api_user) { reporter.user }
it 'does not cancel job' do
expect(response).to have_http_status(403)
end
end
end
context 'unauthorized user' do
let(:api_user) { nil }
it 'does not cancel job' do
expect(response).to have_http_status(401)
end
end
end
describe 'POST /projects/:id/jobs/:job_id/retry' do
let(:build) { create(:ci_build, :canceled, pipeline: pipeline) }
before do
post api("/projects/#{project.id}/jobs/#{build.id}/retry", api_user)
end
context 'authorized user' do
context 'user with :update_build permission' do
it 'retries non-running job' do
expect(response).to have_http_status(201)
expect(project.builds.first.status).to eq('canceled')
expect(json_response['status']).to eq('pending')
end
end
context 'user without :update_build permission' do
let(:api_user) { reporter.user }
it 'does not retry job' do
expect(response).to have_http_status(403)
end
end
end
context 'unauthorized user' do
let(:api_user) { nil }
it 'does not retry job' do
expect(response).to have_http_status(401)
end
end
end
describe 'POST /projects/:id/jobs/:job_id/erase' do
before do
post api("/projects/#{project.id}/jobs/#{build.id}/erase", user)
end
context 'job is erasable' do
let(:build) { create(:ci_build, :trace, :artifacts, :success, project: project, pipeline: pipeline) }
it 'erases job content' do
expect(response).to have_http_status(201)
expect(build.trace).to be_empty
expect(build.artifacts_file.exists?).to be_falsy
expect(build.artifacts_metadata.exists?).to be_falsy
end
it 'updates job' do
build.reload
expect(build.erased_at).to be_truthy
expect(build.erased_by).to eq(user)
end
end
context 'job is not erasable' do
let(:build) { create(:ci_build, :trace, project: project, pipeline: pipeline) }
it 'responds with forbidden' do
expect(response).to have_http_status(403)
end
end
end
describe 'POST /projects/:id/jobs/:build_id/artifacts/keep' do
before do
post api("/projects/#{project.id}/jobs/#{build.id}/artifacts/keep", user)
end
context 'artifacts did not expire' do
let(:build) do
create(:ci_build, :trace, :artifacts, :success,
project: project, pipeline: pipeline, artifacts_expire_at: Time.now + 7.days)
end
it 'keeps artifacts' do
expect(response).to have_http_status(200)
expect(build.reload.artifacts_expire_at).to be_nil
end
end
context 'no artifacts' do
let(:build) { create(:ci_build, project: project, pipeline: pipeline) }
it 'responds with not found' do
expect(response).to have_http_status(404)
end
end
end
describe 'POST /projects/:id/jobs/:job_id/play' do
before do
post api("/projects/#{project.id}/jobs/#{build.id}/play", user)
end
context 'on an playable job' do
let(:build) { create(:ci_build, :manual, project: project, pipeline: pipeline) }
it 'plays the job' do
expect(response).to have_http_status(200)
expect(json_response['user']['id']).to eq(user.id)
expect(json_response['id']).to eq(build.id)
end
end
context 'on a non-playable job' do
it 'returns a status code 400, Bad Request' do
expect(response).to have_http_status 400
expect(response.body).to match("Unplayable Job")
end
end
end
end
require 'spec_helper'
describe API::Builds, api: true do
describe API::V3::Builds, api: true do
include ApiHelpers
let(:user) { create(:user) }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment