diff options
author | Grzegorz Bizon <grzesiek.bizon@gmail.com> | 2016-12-01 11:51:25 +0100 |
---|---|---|
committer | Grzegorz Bizon <grzesiek.bizon@gmail.com> | 2016-12-01 11:51:25 +0100 |
commit | 5a0cea9feb6fdeef3bf9803bb31315a5b97de1b4 (patch) | |
tree | 6207b96dd08105f92971ba2abeb966c6d0e9465d | |
parent | 43c8788e7bd4c705bc8215ea2409b21a19d090f7 (diff) | |
download | gitlab-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.rb | 35 | ||||
-rw-r--r-- | app/models/commit_status.rb | 11 | ||||
-rw-r--r-- | app/serializers/pipeline_action_entity.rb | 14 | ||||
-rw-r--r-- | app/serializers/pipeline_artifact_entity.rb | 14 | ||||
-rw-r--r-- | app/serializers/pipeline_entity.rb | 92 | ||||
-rw-r--r-- | app/serializers/pipeline_serializer.rb | 8 | ||||
-rw-r--r-- | app/serializers/pipeline_stage_entity.rb | 19 | ||||
-rw-r--r-- | app/serializers/request_aware_entity.rb | 4 | ||||
-rw-r--r-- | spec/serializers/pipeline_serializer_spec.rb | 36 |
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 |