diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index 44b737d7a69..1814469f6ce 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -290,6 +290,10 @@ module Ci end end + def has_yaml_errors? + yaml_errors.present? + end + def environments builds.where.not(environment: nil).success.pluck(:environment).uniq end diff --git a/app/serializers/pipeline_entity.rb b/app/serializers/pipeline_entity.rb index 3ede0fb0f53..42205476478 100644 --- a/app/serializers/pipeline_entity.rb +++ b/app/serializers/pipeline_entity.rb @@ -28,12 +28,10 @@ class PipelineEntity < Grape::Entity expose :flags do expose :latest?, as: :latest expose :triggered?, as: :triggered - - expose :yaml_errors?, as: :yaml_errors do |pipeline| - pipeline.yaml_errors.present? - end - expose :stuck?, as: :stuck + expose :has_yaml_errors?, as: :yaml_errors + expose :can_retry?, as: :retryable + expose :can_cancel?, as: :cancelable end expose :ref do @@ -41,31 +39,45 @@ class PipelineEntity < Grape::Entity pipeline.ref end - expose :url do |pipeline| - namespace_project_tree_url( + expose :path do |pipeline| + namespace_project_tree_path( pipeline.project.namespace, pipeline.project, id: pipeline.ref) end - expose :tag? + expose :tag?, as: :tag + expose :branch?, as: :branch end expose :commit, using: CommitEntity + expose :yaml_errors, if: ->(pipeline, _) { pipeline.has_yaml_errors? } - expose :retry_url do |pipeline| - can?(request.user, :update_pipeline, pipeline.project) && - pipeline.retryable? && - retry_namespace_project_pipeline_path(pipeline.project.namespace, - pipeline.project, pipeline.id) + expose :retry_path, if: proc { can_retry? } do |pipeline| + retry_namespace_project_pipeline_path(pipeline.project.namespace, + pipeline.project, + pipeline.id) end - expose :cancel_url do |pipeline| - can?(request.user, :update_pipeline, pipeline.project) && - pipeline.cancelable? && - cancel_namespace_project_pipeline_path(pipeline.project.namespace, - pipeline.project, pipeline.id) + expose :cancel_path, if: proc { can_cancel? } do |pipeline| + cancel_namespace_project_pipeline_path(pipeline.project.namespace, + pipeline.project, + pipeline.id) end expose :created_at, :updated_at + + private + + alias_method :pipeline, :object + + def can_retry? + pipeline.retryable? && + can?(request.user, :update_pipeline, pipeline) + end + + def can_cancel? + pipeline.cancelable? && + can?(request.user, :update_pipeline, pipeline) + end end diff --git a/app/serializers/pipeline_serializer.rb b/app/serializers/pipeline_serializer.rb index c4ca5c40dba..76c767f82c0 100644 --- a/app/serializers/pipeline_serializer.rb +++ b/app/serializers/pipeline_serializer.rb @@ -3,14 +3,6 @@ class PipelineSerializer < BaseSerializer include API::Helpers::Pagination Struct.new('Pagination', :request, :response) - def with_pagination(request, response) - tap { @pagination = Struct::Pagination.new(request, response) } - end - - def paginate? - defined?(@pagination) - end - def represent(resource, opts = {}) if paginate? super(paginate(resource), opts) @@ -19,6 +11,14 @@ class PipelineSerializer < BaseSerializer end end + def paginate? + defined?(@pagination) + end + + def with_pagination(request, response) + tap { @pagination = Struct::Pagination.new(request, response) } + end + private # Methods needed by `API::Helpers::Pagination` diff --git a/spec/factories/ci/pipelines.rb b/spec/factories/ci/pipelines.rb index 1735791f644..6ed4f547d04 100644 --- a/spec/factories/ci/pipelines.rb +++ b/spec/factories/ci/pipelines.rb @@ -31,6 +31,10 @@ FactoryGirl.define do File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) end end + + # Populates pipeline with errors + # + pipeline.config_processor if evaluator.config end end end diff --git a/spec/serializers/pipeline_entity_spec.rb b/spec/serializers/pipeline_entity_spec.rb new file mode 100644 index 00000000000..b19464c7117 --- /dev/null +++ b/spec/serializers/pipeline_entity_spec.rb @@ -0,0 +1,138 @@ +require 'spec_helper' + +describe PipelineEntity do + let(:user) { create(:user) } + let(:request) { double('request') } + + before do + allow(request).to receive(:user).and_return(user) + end + + let(:entity) do + described_class.represent(pipeline, request: request) + end + + describe '#as_json' do + subject { entity.as_json } + + context 'when pipeline is empty' do + let(:pipeline) { create(:ci_empty_pipeline) } + + it 'contains required fields' do + expect(subject).to include :id, :user, :path + expect(subject).to include :ref, :commit + expect(subject).to include :updated_at, :created_at + end + + it 'contains details' do + expect(subject).to include :details + expect(subject[:details]) + .to include :duration, :finished_at + expect(subject[:details]) + .to include :stages, :artifacts, :manual_actions + expect(subject[:details][:status]).to include :icon, :text, :label + end + + it 'contains flags' do + expect(subject).to include :flags + expect(subject[:flags]) + .to include :latest, :triggered, :stuck, + :yaml_errors, :retryable, :cancelable + end + end + + context 'when pipeline is retryable' do + let(:project) { create(:empty_project) } + + let(:pipeline) do + create(:ci_pipeline, status: :success, project: project) + end + + before do + create(:ci_build, :failed, pipeline: pipeline) + end + + context 'user has ability to retry pipeline' do + before { project.team << [user, :developer] } + + it 'retryable flag is true' do + expect(subject[:flags][:retryable]).to eq true + end + + it 'contains retry path' do + expect(subject[:retry_path]).to be_present + end + end + + context 'user does not have ability to retry pipeline' do + it 'retryable flag is false' do + expect(subject[:flags][:retryable]).to eq false + end + + it 'does not contain retry path' do + expect(subject).not_to have_key(:retry_path) + end + end + end + + context 'when pipeline is cancelable' do + let(:project) { create(:empty_project) } + + let(:pipeline) do + create(:ci_pipeline, status: :running, project: project) + end + + before do + create(:ci_build, :pending, pipeline: pipeline) + end + + context 'user has ability to cancel pipeline' do + before { project.team << [user, :developer] } + + it 'cancelable flag is true' do + expect(subject[:flags][:cancelable]).to eq true + end + + it 'contains cancel path' do + expect(subject[:cancel_path]).to be_present + end + end + + context 'user does not have ability to cancel pipeline' do + it 'cancelable flag is false' do + expect(subject[:flags][:cancelable]).to eq false + end + + it 'does not contain cancel path' do + expect(subject).not_to have_key(:cancel_path) + end + end + end + + context 'when pipeline has YAML errors' do + let(:pipeline) do + create(:ci_pipeline, config: { rspec: { invalid: :value } }) + end + + it 'contains flag that indicates there are errors' do + expect(subject[:flags][:yaml_errors]).to be true + end + + it 'contains information about error' do + expect(subject[:yaml_errors]).to be_present + end + end + + context 'when pipeline does not have YAML errors' do + let(:pipeline) { create(:ci_empty_pipeline) } + + it 'contains flag that indicates there are no errors' do + expect(subject[:flags][:yaml_errors]).to be false + end + + it 'does not contain field that normally holds an error' do + expect(subject).not_to have_key(:yaml_errors) + end + end + end +end