Коммит 5a0cea9f создал по автору Grzegorz Bizon's avatar Grzegorz Bizon
Просмотр файлов

Add snapshot of pipeline changes from auto-pipeline-vue

[ci skip]
владелец 43c8788e
......@@ -21,8 +21,6 @@ class Pipeline < ActiveRecord::Base
after_create :keep_around_commits, unless: :importing?
delegate :stages, to: :statuses
state_machine :status, initial: :created do
event :enqueue do
transition created: :pending
......@@ -98,19 +96,42 @@ def self.truncate_sha(sha)
sha[0...8]
end
def self.stages
# We use pluck here due to problems with MySQL which doesn't allow LIMIT/OFFSET in queries
CommitStatus.where(pipeline: pluck(:id)).stages
end
def self.total_duration
where.not(duration: nil).sum(:duration)
end
def stages_query
statuses.group('stage').select(:stage)
.order('max(stage_idx)')
end
def stages
self.stages_query.pluck(:stage)
end
def stages_with_statuses
status_sql = statuses.latest.where('stage=sg.stage').status_sql
stages_with_statuses = CommitStatus.from(self.stages_query, :sg).
pluck('sg.stage', status_sql)
stages_with_statuses.map do |stage|
OpenStruct.new(
name: stage.first,
status: stage.last,
pipeline: self
)
end
end
def stages_with_latest_statuses
statuses.latest.includes(project: :namespace).order(:stage_idx).group_by(&:stage)
end
def artifacts
builds.latest.with_artifacts_not_expired
end
def project_id
project.id
end
......
......@@ -119,16 +119,7 @@ def group_name
def self.stages
# We group by stage name, but order stages by theirs' index
unscoped.from(all, :sg).group('stage').order('max(stage_idx)', 'stage').pluck('sg.stage')
end
def self.stages_status
# We execute subquery for each stage to calculate a stage status
statuses = unscoped.from(all, :sg).group('stage').pluck('sg.stage', all.where('stage=sg.stage').status_sql)
statuses.inject({}) do |h, k|
h[k.first] = k.last
h
end
unscoped.from(all, :sg).group('stage').order('max(stage_idx)', 'stage').select('sg.stage')
end
def failed_but_allowed?
......
class PipelineActionEntity < Grape::Entity
include RequestAwareEntity
expose :name do |build|
build.name.humanize
end
expose :url do |build|
play_namespace_project_build_path(
build.project.namespace,
build.project,
build)
end
end
class PipelineArtifactEntity < Grape::Entity
include RequestAwareEntity
expose :name do |build|
build.name
end
expose :url do |build|
download_namespace_project_build_artifacts_path(
build.project.namespace,
build.project,
build)
end
end
class PipelineEntity < Grape::Entity
include RequestAwareEntity
expose :id
expose :user, if: proc { created_exposure? }, using: UserEntity
expose :url do |pipeline|
namespace_project_pipeline_path(
pipeline.project.namespace,
pipeline.project,
pipeline)
end
expose :details, if: proc { updated_exposure? } do
expose :status
expose :duration
expose :finished_at
expose :stages_with_statuses, as: :stages, using: PipelineStageEntity
expose :artifacts, using: PipelineArtifactEntity
expose :manual_actions, using: PipelineActionEntity
end
expose :flags, if: proc { created_exposure? } 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 do |pipeline|
pipeline.builds.any?(&:stuck?)
end
end
expose :ref, if: proc { updated_exposure? } do
expose :name do |pipeline|
pipeline.ref
end
expose :url do |pipeline|
namespace_project_tree_url(
pipeline.project.namespace,
pipeline.project,
id: pipeline.ref)
end
expose :tag?
end
expose :commit, if: proc { created_exposure? }, using: CommitEntity
expose :retry_url, if: proc { updated_exposure? } do |pipeline|
can?(request.user, :update_pipeline, pipeline.project) &&
pipeline.retryable? &&
retry_namespace_project_pipeline_path(pipeline.project.namespace,
pipeline.project, pipeline.id)
end
expose :cancel_url, if: proc { updated_exposure? } do |pipeline|
can?(request.user, :update_pipeline, pipeline.project) &&
pipeline.cancelable? &&
cancel_namespace_project_pipeline_path(pipeline.project.namespace,
pipeline.project, pipeline.id)
end
expose :created_at, :updated_at
def created_exposure?
!incremental? || created?
end
def updated_exposure?
!incremental? || updated?
end
def incremental?
options[:incremental] && last_updated
end
def last_updated
options.fetch(:last_updated)
end
def updated?
@object.updated_at > last_updated
end
def created?
@object.created_at > last_updated
end
end
class PipelineSerializer < BaseSerializer
entity PipelineEntity
def incremental(resource, last_updated)
represent(resource, incremental: true,
last_updated: last_updated)
end
end
class PipelineStageEntity < Grape::Entity
include RequestAwareEntity
expose :name do |stage|
stage.name
end
expose :status do |stage|
stage.status || 'not found'
end
expose :url do |stage|
namespace_project_pipeline_path(
stage.pipeline.project.namespace,
stage.pipeline.project,
stage.pipeline.id,
anchor: stage.name)
end
end
......@@ -8,4 +8,8 @@ module RequestAwareEntity
def request
@options.fetch(:request)
end
def can?(object, action, subject)
Ability.allowed?(object, action, subject)
end
end
require 'spec_helper'
describe PipelineSerializer do
let(:serializer) do
described_class.new(user: user)
end
let(:pipelines) do
create_list(:ci_pipeline, 2)
end
let(:user) { create(:user) }
context 'when using incremental serializer' do
let(:json) do
serializer.incremental(pipelines, time).as_json
end
context 'when pipeline has been already updated' do
let(:time) { Time.now }
it 'exposes only minimal information' do
expect(json.first.keys).to contain_exactly(:id, :url)
expect(json.second.keys).to contain_exactly(:id, :url)
end
end
context 'when pipeline updated in the meantime' do
let(:time) { Time.now - 10.minutes }
it 'exposes new data incrementally' do
expect(json.first.keys.count).to eq 9
end
end
end
end
Поддерживает Markdown
0% или .
You are about to add 0 people to the discussion. Proceed with caution.
Сначала завершите редактирование этого сообщения!
Пожалуйста, зарегистрируйтесь или чтобы прокомментировать