summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGrzegorz Bizon <grzesiek.bizon@gmail.com>2016-12-01 11:51:25 +0100
committerGrzegorz Bizon <grzesiek.bizon@gmail.com>2016-12-01 11:51:25 +0100
commit5a0cea9feb6fdeef3bf9803bb31315a5b97de1b4 (patch)
tree6207b96dd08105f92971ba2abeb966c6d0e9465d
parent43c8788e7bd4c705bc8215ea2409b21a19d090f7 (diff)
downloadgitlab-ce-feature/incremental-pipeline-updates.tar.gz
Add snapshot of pipeline changes from auto-pipeline-vuefeature/incremental-pipeline-updates
[ci skip]
-rw-r--r--app/models/ci/pipeline.rb35
-rw-r--r--app/models/commit_status.rb11
-rw-r--r--app/serializers/pipeline_action_entity.rb14
-rw-r--r--app/serializers/pipeline_artifact_entity.rb14
-rw-r--r--app/serializers/pipeline_entity.rb92
-rw-r--r--app/serializers/pipeline_serializer.rb8
-rw-r--r--app/serializers/pipeline_stage_entity.rb19
-rw-r--r--app/serializers/request_aware_entity.rb4
-rw-r--r--spec/serializers/pipeline_serializer_spec.rb36
9 files changed, 216 insertions, 17 deletions
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index fabbf97d4db..c181d6e6a74 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -21,8 +21,6 @@ module Ci
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 @@ module Ci
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
diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb
index c345bf293c9..d9021a38ce3 100644
--- a/app/models/commit_status.rb
+++ b/app/models/commit_status.rb
@@ -119,16 +119,7 @@ class CommitStatus < ActiveRecord::Base
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?
diff --git a/app/serializers/pipeline_action_entity.rb b/app/serializers/pipeline_action_entity.rb
new file mode 100644
index 00000000000..475b5a55316
--- /dev/null
+++ b/app/serializers/pipeline_action_entity.rb
@@ -0,0 +1,14 @@
+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
diff --git a/app/serializers/pipeline_artifact_entity.rb b/app/serializers/pipeline_artifact_entity.rb
new file mode 100644
index 00000000000..9e9f245a6f9
--- /dev/null
+++ b/app/serializers/pipeline_artifact_entity.rb
@@ -0,0 +1,14 @@
+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
diff --git a/app/serializers/pipeline_entity.rb b/app/serializers/pipeline_entity.rb
new file mode 100644
index 00000000000..08346b4ed77
--- /dev/null
+++ b/app/serializers/pipeline_entity.rb
@@ -0,0 +1,92 @@
+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
diff --git a/app/serializers/pipeline_serializer.rb b/app/serializers/pipeline_serializer.rb
new file mode 100644
index 00000000000..8d3182d9766
--- /dev/null
+++ b/app/serializers/pipeline_serializer.rb
@@ -0,0 +1,8 @@
+class PipelineSerializer < BaseSerializer
+ entity PipelineEntity
+
+ def incremental(resource, last_updated)
+ represent(resource, incremental: true,
+ last_updated: last_updated)
+ end
+end
diff --git a/app/serializers/pipeline_stage_entity.rb b/app/serializers/pipeline_stage_entity.rb
new file mode 100644
index 00000000000..230ef8a22da
--- /dev/null
+++ b/app/serializers/pipeline_stage_entity.rb
@@ -0,0 +1,19 @@
+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
diff --git a/app/serializers/request_aware_entity.rb b/app/serializers/request_aware_entity.rb
index ff8c1142abc..e159d750cb7 100644
--- a/app/serializers/request_aware_entity.rb
+++ b/app/serializers/request_aware_entity.rb
@@ -8,4 +8,8 @@ module RequestAwareEntity
def request
@options.fetch(:request)
end
+
+ def can?(object, action, subject)
+ Ability.allowed?(object, action, subject)
+ end
end
diff --git a/spec/serializers/pipeline_serializer_spec.rb b/spec/serializers/pipeline_serializer_spec.rb
new file mode 100644
index 00000000000..c4eba2f2537
--- /dev/null
+++ b/spec/serializers/pipeline_serializer_spec.rb
@@ -0,0 +1,36 @@
+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