Commit f37f42d5 authored by Fabio Pitino's avatar Fabio Pitino Committed by Grzegorz Bizon

Implement DAG pipeline serializer

Add DAG entities for pipeline, stage, group, job.
parent 5534fdcf
...@@ -95,7 +95,14 @@ class Projects::PipelinesController < Projects::ApplicationController ...@@ -95,7 +95,14 @@ class Projects::PipelinesController < Projects::ApplicationController
end end
def dag def dag
render_show respond_to do |format|
format.html { render_show }
format.json do
render json: Ci::DagPipelineSerializer
.new(project: @project, current_user: @current_user)
.represent(@pipeline)
end
end
end end
def failures def failures
......
...@@ -4,12 +4,8 @@ module Ci ...@@ -4,12 +4,8 @@ module Ci
class Processable < ::CommitStatus class Processable < ::CommitStatus
include Gitlab::Utils::StrongMemoize include Gitlab::Utils::StrongMemoize
has_many :needs, class_name: 'Ci::BuildNeed', foreign_key: :build_id, inverse_of: :build
accepts_nested_attributes_for :needs accepts_nested_attributes_for :needs
enum scheduling_type: { stage: 0, dag: 1 }, _prefix: true
scope :preload_needs, -> { preload(:needs) } scope :preload_needs, -> { preload(:needs) }
scope :with_needs, -> (names = nil) do scope :with_needs, -> (names = nil) do
......
...@@ -14,6 +14,10 @@ class CommitStatus < ApplicationRecord ...@@ -14,6 +14,10 @@ class CommitStatus < ApplicationRecord
belongs_to :pipeline, class_name: 'Ci::Pipeline', foreign_key: :commit_id belongs_to :pipeline, class_name: 'Ci::Pipeline', foreign_key: :commit_id
belongs_to :auto_canceled_by, class_name: 'Ci::Pipeline' belongs_to :auto_canceled_by, class_name: 'Ci::Pipeline'
has_many :needs, class_name: 'Ci::BuildNeed', foreign_key: :build_id, inverse_of: :build
enum scheduling_type: { stage: 0, dag: 1 }, _prefix: true
delegate :commit, to: :pipeline delegate :commit, to: :pipeline
delegate :sha, :short_sha, :before_sha, to: :pipeline delegate :sha, :short_sha, :before_sha, to: :pipeline
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
module Ci module Ci
class DagJobEntity < Grape::Entity class DagJobEntity < Grape::Entity
expose :name expose :name
expose :scheduling_type
expose :needs, if: -> (job, _) { job.scheduling_type_dag? } do |job| expose :needs, if: -> (job, _) { job.scheduling_type_dag? } do |job|
job.needs.pluck(:name) # rubocop: disable CodeReuse/ActiveRecord job.needs.pluck(:name) # rubocop: disable CodeReuse/ActiveRecord
......
...@@ -2,12 +2,12 @@ ...@@ -2,12 +2,12 @@
module Ci module Ci
class DagPipelineEntity < Grape::Entity class DagPipelineEntity < Grape::Entity
expose :ordered_stages_with_preloads, as: :stages, using: Ci::DagStageEntity expose :stages_with_preloads, as: :stages, using: Ci::DagStageEntity
private private
def ordered_stages_with_preloads def stages_with_preloads
object.ordered_stages.preload(preloaded_relations) # rubocop: disable CodeReuse/ActiveRecord object.stages.preload(preloaded_relations) # rubocop: disable CodeReuse/ActiveRecord
end end
def preloaded_relations def preloaded_relations
......
...@@ -82,6 +82,7 @@ ...@@ -82,6 +82,7 @@
- if dag_pipeline_tab_enabled - if dag_pipeline_tab_enabled
#js-tab-dag.tab-pane #js-tab-dag.tab-pane
#js-pipeline-dag-vue{ data: { pipeline_data_path: dag_project_pipeline_path(@project, @pipeline) } }
#js-tab-tests.tab-pane #js-tab-tests.tab-pane
#js-pipeline-tests-detail #js-pipeline-tests-detail
......
---
title: Add DAG serializer for pipelines controller
merge_request: 31583
author:
type: added
...@@ -548,6 +548,39 @@ describe Projects::PipelinesController do ...@@ -548,6 +548,39 @@ describe Projects::PipelinesController do
end end
end end
describe 'GET dag.json' do
let(:pipeline) { create(:ci_pipeline, project: project) }
before do
create_build('build', 1, 'build')
create_build('test', 2, 'test', scheduling_type: 'dag').tap do |job|
create(:ci_build_need, build: job, name: 'build')
end
end
it 'returns the pipeline with DAG serialization' do
get :dag, params: { namespace_id: project.namespace, project_id: project, id: pipeline }, format: :json
expect(response).to have_gitlab_http_status(:ok)
expect(json_response.fetch('stages')).not_to be_empty
build_stage = json_response['stages'].first
expect(build_stage.fetch('name')).to eq 'build'
expect(build_stage.fetch('groups').first.fetch('jobs'))
.to eq [{ 'name' => 'build', 'scheduling_type' => 'stage' }]
test_stage = json_response['stages'].last
expect(test_stage.fetch('name')).to eq 'test'
expect(test_stage.fetch('groups').first.fetch('jobs'))
.to eq [{ 'name' => 'test', 'scheduling_type' => 'dag', 'needs' => ['build'] }]
end
def create_build(stage, stage_idx, name, params = {})
create(:ci_build, pipeline: pipeline, stage: stage, stage_idx: stage_idx, name: name, **params)
end
end
describe 'GET stages.json' do describe 'GET stages.json' do
let(:pipeline) { create(:ci_pipeline, project: project) } let(:pipeline) { create(:ci_pipeline, project: project) }
......
...@@ -236,6 +236,7 @@ statuses: ...@@ -236,6 +236,7 @@ statuses:
- stage - stage
- user - user
- auto_canceled_by - auto_canceled_by
- needs
variables: variables:
- project - project
triggers: triggers:
......
...@@ -16,14 +16,23 @@ describe Ci::DagJobEntity do ...@@ -16,14 +16,23 @@ describe Ci::DagJobEntity do
end end
context 'when job is stage scheduled' do context 'when job is stage scheduled' do
it 'contains the name scheduling_type' do
expect(subject[:scheduling_type]).to eq 'stage'
end
it 'does not expose needs' do it 'does not expose needs' do
expect(subject).not_to include(:needs) expect(subject).not_to include(:needs)
end end
end end
context 'when job is dag scheduled' do context 'when job is dag scheduled' do
let(:job) { create(:ci_build, scheduling_type: 'dag') }
it 'contains the name scheduling_type' do
expect(subject[:scheduling_type]).to eq 'dag'
end
context 'when job has needs' do context 'when job has needs' do
let(:job) { create(:ci_build, scheduling_type: 'dag') }
let!(:need) { create(:ci_build_need, build: job, name: 'compile') } let!(:need) { create(:ci_build_need, build: job, name: 'compile') }
it 'exposes the array of needs' do it 'exposes the array of needs' do
...@@ -32,8 +41,6 @@ describe Ci::DagJobEntity do ...@@ -32,8 +41,6 @@ describe Ci::DagJobEntity do
end end
context 'when job has empty needs' do context 'when job has empty needs' do
let(:job) { create(:ci_build, scheduling_type: 'dag') }
it 'exposes an empty array of needs' do it 'exposes an empty array of needs' do
expect(subject[:needs]).to eq [] expect(subject[:needs]).to eq []
end end
......
...@@ -32,13 +32,14 @@ describe Ci::DagPipelineEntity do ...@@ -32,13 +32,14 @@ describe Ci::DagPipelineEntity do
end end
end end
context 'when pipeline has parallel jobs and DAG needs' do context 'when pipeline has parallel jobs, DAG needs and GenericCommitStatus' do
let!(:stage_build) { create(:ci_stage_entity, name: 'build', position: 1, pipeline: pipeline) } let!(:stage_build) { create(:ci_stage_entity, name: 'build', position: 1, pipeline: pipeline) }
let!(:stage_test) { create(:ci_stage_entity, name: 'test', position: 2, pipeline: pipeline) } let!(:stage_test) { create(:ci_stage_entity, name: 'test', position: 2, pipeline: pipeline) }
let!(:stage_deploy) { create(:ci_stage_entity, name: 'deploy', position: 3, pipeline: pipeline) } let!(:stage_deploy) { create(:ci_stage_entity, name: 'deploy', position: 3, pipeline: pipeline) }
let!(:job_build_1) { create(:ci_build, name: 'build 1', stage: 'build', pipeline: pipeline) } let!(:job_build_1) { create(:ci_build, name: 'build 1', stage: 'build', pipeline: pipeline) }
let!(:job_build_2) { create(:ci_build, name: 'build 2', stage: 'build', pipeline: pipeline) } let!(:job_build_2) { create(:ci_build, name: 'build 2', stage: 'build', pipeline: pipeline) }
let!(:commit_status) { create(:generic_commit_status, stage: 'build', pipeline: pipeline) }
let!(:job_rspec_1) { create(:ci_build, name: 'rspec 1/2', stage: 'test', pipeline: pipeline) } let!(:job_rspec_1) { create(:ci_build, name: 'rspec 1/2', stage: 'test', pipeline: pipeline) }
let!(:job_rspec_2) { create(:ci_build, name: 'rspec 2/2', stage: 'test', pipeline: pipeline) } let!(:job_rspec_2) { create(:ci_build, name: 'rspec 2/2', stage: 'test', pipeline: pipeline) }
...@@ -75,22 +76,52 @@ describe Ci::DagPipelineEntity do ...@@ -75,22 +76,52 @@ describe Ci::DagPipelineEntity do
{ {
name: 'build', name: 'build',
groups: [ groups: [
{ name: 'build 1', size: 1, jobs: [{ name: 'build 1' }] }, {
{ name: 'build 2', size: 1, jobs: [{ name: 'build 2' }] } name: 'build 1', size: 1, jobs: [
{ name: 'build 1', scheduling_type: 'stage' }
]
},
{
name: 'build 2', size: 1, jobs: [
{ name: 'build 2', scheduling_type: 'stage' }
]
},
{
name: 'generic', size: 1, jobs: [
{ name: 'generic', scheduling_type: nil }
]
}
] ]
}, },
{ {
name: 'test', name: 'test',
groups: [ groups: [
{ name: 'jest', size: 1, jobs: [{ name: 'jest', needs: ['build 1'] }] }, {
{ name: 'rspec', size: 2, jobs: [{ name: 'rspec 1/2' }, { name: 'rspec 2/2' }] } name: 'jest', size: 1, jobs: [
{ name: 'jest', scheduling_type: 'dag', needs: ['build 1'] }
]
},
{
name: 'rspec', size: 2, jobs: [
{ name: 'rspec 1/2', scheduling_type: 'stage' },
{ name: 'rspec 2/2', scheduling_type: 'stage' }
]
}
] ]
}, },
{ {
name: 'deploy', name: 'deploy',
groups: [ groups: [
{ name: 'deploy_js', size: 1, jobs: [{ name: 'deploy_js', needs: ['jest'] }] }, {
{ name: 'deploy_ruby', size: 1, jobs: [{ name: 'deploy_ruby', needs: ['rspec 1/2', 'rspec 2/2'] }] } name: 'deploy_js', size: 1, jobs: [
{ name: 'deploy_js', scheduling_type: 'dag', needs: ['jest'] }
]
},
{
name: 'deploy_ruby', size: 1, jobs: [
{ name: 'deploy_ruby', scheduling_type: 'dag', needs: ['rspec 1/2', 'rspec 2/2'] }
]
}
] ]
} }
] ]
......
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