Commit eb66d691 authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents 26a0f684 60b96837
......@@ -2,6 +2,8 @@
module Resolvers
class ProjectPipelineResolver < BaseResolver
include LooksAhead
type ::Types::Ci::PipelineType, null: true
alias_method :project, :object
......@@ -14,7 +16,7 @@ module Resolvers
required: false,
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?
raise Gitlab::Graphql::Errors::ArgumentError, 'Provide one of an IID or SHA'
end
......@@ -22,18 +24,21 @@ module Resolvers
super
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
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.execute.each { |pipeline| loader.call(pipeline.iid.to_s, pipeline) }
apply_lookahead(finder.execute).each { |pipeline| loader.call(pipeline.iid.to_s, pipeline) }
end
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.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
......
......@@ -208,6 +208,7 @@ module Types
Types::Ci::PipelineType,
null: true,
description: 'Build pipeline of the project.',
extras: [:lookahead],
resolver: Resolvers::ProjectPipelineResolver
field :ci_cd_settings,
......
......@@ -45,7 +45,6 @@ module Integrations
included do
has_one :issue_tracker_data, autosave: true, inverse_of: :integration, foreign_key: :service_id, class_name: 'Integrations::IssueTrackerData'
has_one :jira_tracker_data, autosave: true, inverse_of: :integration, foreign_key: :service_id, class_name: 'Integrations::JiraTrackerData'
has_one :open_project_tracker_data, autosave: true, inverse_of: :integration, foreign_key: :service_id, class_name: 'Integrations::OpenProjectTrackerData'
has_one :zentao_tracker_data, autosave: true, inverse_of: :integration, foreign_key: :integration_id, class_name: 'Integrations::ZentaoTrackerData'
def data_fields
......
# frozen_string_literal: true
module Integrations
class OpenProject < BaseIssueTracker
validates :url, public_url: true, presence: true, if: :activated?
validates :api_url, public_url: true, allow_blank: true, if: :activated?
validates :token, presence: true, if: :activated?
validates :project_identifier_code, presence: true, if: :activated?
data_field :url, :api_url, :token, :closed_status_id, :project_identifier_code
def data_fields
open_project_tracker_data || self.build_open_project_tracker_data
end
def self.to_param
'open_project'
end
end
end
# frozen_string_literal: true
module Integrations
class OpenProjectTrackerData < ApplicationRecord
include BaseDataFields
# When the Open Project is fresh installed, the default closed status id is "13" based on current version: v8.
DEFAULT_CLOSED_STATUS_ID = "13"
attr_encrypted :url, encryption_options
attr_encrypted :api_url, encryption_options
attr_encrypted :token, encryption_options
def closed_status_id
super || DEFAULT_CLOSED_STATUS_ID
end
end
end
......@@ -12189,6 +12189,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="pipelinecoverage"></a>`coverage` | [`Float`](#float) | Coverage percentage. |
| <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="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. |
......
# 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
null: true,
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
pipeline.codequality_reports.sort_degradations!.values.presence
end
def dast_profile
pipeline.dast_profile if ::Feature.enabled?(:dast_view_scans, pipeline.project, default_enabled: :yaml)
end
end
end
end
......
......@@ -20,14 +20,16 @@ module Resolvers
apply_lookahead(find_dast_profiles(args))
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
def preloads
{
dast_site_profile: [{ dast_site_profile: [:dast_site, :secret_variables] }],
dast_scanner_profile: [:dast_scanner_profile],
dast_profile_schedule: [:dast_profile_schedule]
}
DAST_PROFILE_PRELOAD
end
def find_dast_profiles(args)
......
......@@ -10,6 +10,7 @@ RSpec.describe GitlabSchema.types['Pipeline'] do
security_report_summary
security_report_findings
code_quality_reports
dast_profile
]
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,12 +18,4 @@ FactoryBot.define do
factory :issue_tracker_data, class: 'Integrations::IssueTrackerData' do
integration
end
factory :open_project_tracker_data, class: 'Integrations::OpenProjectTrackerData' do
integration factory: :open_project_service
url { 'http://openproject.example.com' }
token { 'supersecret' }
project_identifier_code { 'PRJ-1' }
closed_status_id { '15' }
end
end
......@@ -166,26 +166,6 @@ FactoryBot.define do
external_wiki_url { 'http://external-wiki-url.com' }
end
factory :open_project_service, class: 'Integrations::OpenProject' do
project
active { true }
transient do
url { 'http://openproject.example.com' }
api_url { 'http://openproject.example.com/issues/:id' }
token { 'supersecret' }
closed_status_id { '15' }
project_identifier_code { 'PRJ-1' }
end
after(:build) do |integration, evaluator|
integration.open_project_tracker_data = build(:open_project_tracker_data,
integration: integration, url: evaluator.url, api_url: evaluator.api_url, token: evaluator.token,
closed_status_id: evaluator.closed_status_id, project_identifier_code: evaluator.project_identifier_code
)
end
end
trait :jira_cloud_service do
url { 'https://mysite.atlassian.net' }
username { 'jira_user' }
......
......@@ -18,7 +18,7 @@ RSpec.describe Types::Ci::PipelineType do
]
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
expect(described_class).to have_graphql_fields(*expected_fields)
......
......@@ -324,7 +324,6 @@ integrations:
- jira_tracker_data
- zentao_tracker_data
- issue_tracker_data
- open_project_tracker_data
hooks:
- project
- web_hook_logs
......
......@@ -14,7 +14,6 @@ RSpec.describe Integration do
it { is_expected.to have_one(:service_hook).inverse_of(:integration).with_foreign_key(:service_id) }
it { is_expected.to have_one(:issue_tracker_data).autosave(true).inverse_of(:integration).with_foreign_key(:service_id).class_name('Integrations::IssueTrackerData') }
it { is_expected.to have_one(:jira_tracker_data).autosave(true).inverse_of(:integration).with_foreign_key(:service_id).class_name('Integrations::JiraTrackerData') }
it { is_expected.to have_one(:open_project_tracker_data).autosave(true).inverse_of(:integration).with_foreign_key(:service_id).class_name('Integrations::OpenProjectTrackerData') }
end
describe 'validations' do
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Integrations::OpenProject do
describe 'Validations' do
context 'when integration is active' do
before do
subject.active = true
end
it { is_expected.to validate_presence_of(:url) }
it { is_expected.to validate_presence_of(:token) }
it { is_expected.to validate_presence_of(:project_identifier_code) }
it_behaves_like 'issue tracker integration URL attribute', :url
it_behaves_like 'issue tracker integration URL attribute', :api_url
end
context 'when integration is inactive' do
before do
subject.active = false
end
it { is_expected.not_to validate_presence_of(:url) }
it { is_expected.not_to validate_presence_of(:token) }
it { is_expected.not_to validate_presence_of(:project_identifier_code) }
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Integrations::OpenProjectTrackerData do
describe 'associations' do
it { is_expected.to belong_to(:integration) }
end
describe 'closed_status_id' do
it 'returns the set value' do
expect(build(:open_project_tracker_data).closed_status_id).to eq('15')
end
it 'returns the default value if not set' do
expect(build(:open_project_tracker_data, closed_status_id: nil).closed_status_id).to eq('13')
end
end
end
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