Commit ea048ea3 authored by Marcos Rocha's avatar Marcos Rocha Committed by Alex Kalderimis

Extend EE::Types::Ci::PipelineType with dast_profile

Changelog: added
MR: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/69808
EE: false
parent 4c768b1b
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
module Resolvers module Resolvers
class ProjectPipelineResolver < BaseResolver class ProjectPipelineResolver < BaseResolver
include LooksAhead
type ::Types::Ci::PipelineType, null: true type ::Types::Ci::PipelineType, null: true
alias_method :project, :object alias_method :project, :object
...@@ -14,7 +16,7 @@ module Resolvers ...@@ -14,7 +16,7 @@ module Resolvers
required: false, required: false,
description: 'SHA of the Pipeline. For example, "dyd0f15ay83993f5ab66k927w28673882x99100b".' description: 'SHA of the Pipeline. For example, "dyd0f15ay83993f5ab66k927w28673882x99100b".'
def ready?(iid: nil, sha: nil) def ready?(iid: nil, sha: nil, **args)
unless iid.present? ^ sha.present? unless iid.present? ^ sha.present?
raise Gitlab::Graphql::Errors::ArgumentError, 'Provide one of an IID or SHA' raise Gitlab::Graphql::Errors::ArgumentError, 'Provide one of an IID or SHA'
end end
...@@ -22,18 +24,21 @@ module Resolvers ...@@ -22,18 +24,21 @@ module Resolvers
super super
end end
def resolve(iid: nil, sha: nil) # the preloads are defined on ee/app/graphql/ee/resolvers/project_pipeline_resolver.rb
def resolve(iid: nil, sha: nil, **args)
self.lookahead = args.delete(:lookahead)
if iid if iid
BatchLoader::GraphQL.for(iid).batch(key: project) do |iids, loader, args| BatchLoader::GraphQL.for(iid).batch(key: project) do |iids, loader|
finder = ::Ci::PipelinesFinder.new(project, current_user, iids: iids) finder = ::Ci::PipelinesFinder.new(project, current_user, iids: iids)
finder.execute.each { |pipeline| loader.call(pipeline.iid.to_s, pipeline) } apply_lookahead(finder.execute).each { |pipeline| loader.call(pipeline.iid.to_s, pipeline) }
end end
else else
BatchLoader::GraphQL.for(sha).batch(key: project) do |shas, loader, args| BatchLoader::GraphQL.for(sha).batch(key: project) do |shas, loader|
finder = ::Ci::PipelinesFinder.new(project, current_user, sha: shas) finder = ::Ci::PipelinesFinder.new(project, current_user, sha: shas)
finder.execute.each { |pipeline| loader.call(pipeline.sha.to_s, pipeline) } apply_lookahead(finder.execute).each { |pipeline| loader.call(pipeline.sha.to_s, pipeline) }
end end
end end
end end
......
...@@ -208,6 +208,7 @@ module Types ...@@ -208,6 +208,7 @@ module Types
Types::Ci::PipelineType, Types::Ci::PipelineType,
null: true, null: true,
description: 'Build pipeline of the project.', description: 'Build pipeline of the project.',
extras: [:lookahead],
resolver: Resolvers::ProjectPipelineResolver resolver: Resolvers::ProjectPipelineResolver
field :ci_cd_settings, field :ci_cd_settings,
......
...@@ -12179,6 +12179,7 @@ Represents a file or directory in the project repository that has been locked. ...@@ -12179,6 +12179,7 @@ Represents a file or directory in the project repository that has been locked.
| <a id="pipelineconfigsource"></a>`configSource` | [`PipelineConfigSourceEnum`](#pipelineconfigsourceenum) | Configuration source of the pipeline (UNKNOWN_SOURCE, REPOSITORY_SOURCE, AUTO_DEVOPS_SOURCE, WEBIDE_SOURCE, REMOTE_SOURCE, EXTERNAL_PROJECT_SOURCE, BRIDGE_SOURCE, PARAMETER_SOURCE, COMPLIANCE_SOURCE). | | <a id="pipelineconfigsource"></a>`configSource` | [`PipelineConfigSourceEnum`](#pipelineconfigsourceenum) | Configuration source of the pipeline (UNKNOWN_SOURCE, REPOSITORY_SOURCE, AUTO_DEVOPS_SOURCE, WEBIDE_SOURCE, REMOTE_SOURCE, EXTERNAL_PROJECT_SOURCE, BRIDGE_SOURCE, PARAMETER_SOURCE, COMPLIANCE_SOURCE). |
| <a id="pipelinecoverage"></a>`coverage` | [`Float`](#float) | Coverage percentage. | | <a id="pipelinecoverage"></a>`coverage` | [`Float`](#float) | Coverage percentage. |
| <a id="pipelinecreatedat"></a>`createdAt` | [`Time!`](#time) | Timestamp of the pipeline's creation. | | <a id="pipelinecreatedat"></a>`createdAt` | [`Time!`](#time) | Timestamp of the pipeline's creation. |
| <a id="pipelinedastprofile"></a>`dastProfile` | [`DastProfile`](#dastprofile) | DAST profile associated with the pipeline. Returns `null`if `dast_view_scans` feature flag is disabled. |
| <a id="pipelinedetailedstatus"></a>`detailedStatus` | [`DetailedStatus!`](#detailedstatus) | Detailed status of the pipeline. | | <a id="pipelinedetailedstatus"></a>`detailedStatus` | [`DetailedStatus!`](#detailedstatus) | Detailed status of the pipeline. |
| <a id="pipelinedownstream"></a>`downstream` | [`PipelineConnection`](#pipelineconnection) | Pipelines this pipeline will trigger. (see [Connections](#connections)) | | <a id="pipelinedownstream"></a>`downstream` | [`PipelineConnection`](#pipelineconnection) | Pipelines this pipeline will trigger. (see [Connections](#connections)) |
| <a id="pipelineduration"></a>`duration` | [`Int`](#int) | Duration of the pipeline in seconds. | | <a id="pipelineduration"></a>`duration` | [`Int`](#int) | Duration of the pipeline in seconds. |
......
# frozen_string_literal: true
module EE
module Resolvers
module ProjectPipelineResolver
extend ::Gitlab::Utils::Override
override :preloads
def preloads
super.merge(dast_profile: [Dast::ProfileResolver::DAST_PROFILE_PRELOAD])
end
end
end
end
...@@ -25,9 +25,19 @@ module EE ...@@ -25,9 +25,19 @@ module EE
null: true, null: true,
description: 'Code Quality degradations reported on the pipeline.' description: 'Code Quality degradations reported on the pipeline.'
field :dast_profile,
::Types::Dast::ProfileType,
null: true,
description: 'DAST profile associated with the pipeline. Returns `null`' \
'if `dast_view_scans` feature flag is disabled.'
def code_quality_reports def code_quality_reports
pipeline.codequality_reports.sort_degradations!.values.presence pipeline.codequality_reports.sort_degradations!.values.presence
end end
def dast_profile
pipeline.dast_profile if ::Feature.enabled?(:dast_view_scans, pipeline.project, default_enabled: :yaml)
end
end end
end end
end end
......
...@@ -20,14 +20,16 @@ module Resolvers ...@@ -20,14 +20,16 @@ module Resolvers
apply_lookahead(find_dast_profiles(args)) apply_lookahead(find_dast_profiles(args))
end end
DAST_PROFILE_PRELOAD = {
dast_site_profile: [{ dast_site_profile: [:dast_site, :secret_variables] }],
dast_scanner_profile: [:dast_scanner_profile],
dast_profile_schedule: [:dast_profile_schedule]
}.freeze
private private
def preloads def preloads
{ DAST_PROFILE_PRELOAD
dast_site_profile: [{ dast_site_profile: [:dast_site, :secret_variables] }],
dast_scanner_profile: [:dast_scanner_profile],
dast_profile_schedule: [:dast_profile_schedule]
}
end end
def find_dast_profiles(args) def find_dast_profiles(args)
......
...@@ -10,6 +10,7 @@ RSpec.describe GitlabSchema.types['Pipeline'] do ...@@ -10,6 +10,7 @@ RSpec.describe GitlabSchema.types['Pipeline'] do
security_report_summary security_report_summary
security_report_findings security_report_findings
code_quality_reports code_quality_reports
dast_profile
] ]
expect(described_class).to include_graphql_fields(*expected_fields) expect(described_class).to include_graphql_fields(*expected_fields)
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Query.project(fullPath).pipeline(iid).dastProfile' do
include GraphqlHelpers
let_it_be(:project) { create(:project, :repository) }
let_it_be(:dast_profile) { create(:dast_profile, project: project) }
let_it_be(:pipeline) { create(:ci_pipeline, :success, project: project, dast_profile: dast_profile) }
let_it_be(:current_user) { create(:user) }
let(:query) do
%(
query {
project(fullPath: "#{project.full_path}") {
pipeline(iid: "#{pipeline.iid}") {
dastProfile {
#{all_graphql_fields_for('DastProfile')}
}
}
}
}
)
end
subject { post_graphql(query, current_user: current_user) }
let(:dast_profile_data) { graphql_data_at(:project, :pipeline, :dast_profile) }
context 'when feature is not licensed' do
it 'does not return dast profile data' do
subject
expect(dast_profile_data).to be_nil
end
end
context 'when feature is licensed' do
before do
stub_licensed_features(security_on_demand_scans: true)
end
context 'when user is member of the project' do
before do
project.add_developer(current_user)
end
it 'returns the dast profile data' do
subject
expect(dast_profile_data['name']).to eq(dast_profile.name)
end
it 'avoids N+1 queries' do
control = ActiveRecord::QueryRecorder.new do
post_graphql(query, current_user: current_user)
end
create_list(:ci_pipeline, 5, :failed, project: project, dast_profile: dast_profile)
expect { subject }.not_to exceed_query_limit(control)
end
end
context 'when user is not member of the project' do
it 'does not return dast profile data' do
subject
expect(dast_profile_data).to be_nil
end
end
context 'when feature flag is not enabled' do
it 'returns the dast profile data' do
subject
expect(dast_profile_data).to be_nil
end
end
end
end
...@@ -18,7 +18,7 @@ RSpec.describe Types::Ci::PipelineType do ...@@ -18,7 +18,7 @@ RSpec.describe Types::Ci::PipelineType do
] ]
if Gitlab.ee? if Gitlab.ee?
expected_fields += %w[security_report_summary security_report_findings code_quality_reports] expected_fields += %w[security_report_summary security_report_findings code_quality_reports dast_profile]
end end
expect(described_class).to have_graphql_fields(*expected_fields) expect(described_class).to have_graphql_fields(*expected_fields)
......
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