Skip to content
代码片段 群组 项目
未验证 提交 bf25089e 编辑于 作者: Allen Cook's avatar Allen Cook 提交者: GitLab
浏览文件

Merge branch 'add-ref-argument-to-project-pipline-query' into 'master'

Return latest pipeline from project pipelines GraphQL query

See merge request https://gitlab.com/gitlab-org/gitlab/-/merge_requests/172934



Merged-by: default avatarAllen Cook <acook@gitlab.com>
Approved-by: default avatarLysanne Pinto <lpinto@gitlab.com>
Approved-by: default avatarMax Fan <mfan@gitlab.com>
Approved-by: default avatarAllen Cook <acook@gitlab.com>
Reviewed-by: default avatarHordur Freyr Yngvason <hfyngvason@gitlab.com>
Reviewed-by: default avatarMax Fan <mfan@gitlab.com>
Co-authored-by: default avatarPeter Hegman <phegman@gitlab.com>
No related branches found
No related tags found
无相关合并请求
......@@ -5,6 +5,8 @@ module Ci
class ProjectPipelineResolver < BaseResolver
include LooksAhead
calls_gitaly!
type ::Types::Ci::PipelineType, null: true
alias_method :project, :object
......@@ -22,11 +24,11 @@ class ProjectPipelineResolver < BaseResolver
required: false,
description: 'SHA of the Pipeline. For example, "dyd0f15ay83993f5ab66k927w28673882x99100b".'
validates required: { one_of: [:id, :iid, :sha], message: 'Provide one of ID, IID or SHA' }
validates mutually_exclusive: [:id, :iid, :sha]
def self.resolver_complexity(args, child_complexity:)
complexity = super
complexity - 10
complexity - 10 if args.present?
end
def resolve(id: nil, iid: nil, sha: nil, **args)
......@@ -44,12 +46,14 @@ def resolve(id: nil, iid: nil, sha: nil, **args)
apply_lookahead(finder.execute).each { |pipeline| loader.call(pipeline.iid.to_s, pipeline) }
end
else
elsif sha
BatchLoader::GraphQL.for(sha).batch(key: project) do |shas, loader|
finder = ::Ci::PipelinesFinder.new(project, current_user, sha: shas)
apply_lookahead(finder.execute).each { |pipeline| loader.call(pipeline.sha.to_s, pipeline) }
end
else
project.last_pipeline
end
end
......
......@@ -370,7 +370,7 @@ class ProjectType < BaseObject
field :pipelines,
null: true,
description: 'Build pipelines of the project.',
description: 'Pipelines of the project.',
extras: [:lookahead],
resolver: Resolvers::Ci::ProjectPipelinesResolver
......@@ -389,13 +389,14 @@ class ProjectType < BaseObject
field :pipeline, Types::Ci::PipelineType,
null: true,
description: 'Build pipeline of the project.',
description: 'Pipeline of the project. If no arguments are provided, returns the latest pipeline for the ' \
'head commit on the default branch',
extras: [:lookahead],
resolver: Resolvers::Ci::ProjectPipelineResolver
field :pipeline_counts, Types::Ci::PipelineCountsType,
null: true,
description: 'Build pipeline counts of the project.',
description: 'Pipeline counts of the project.',
resolver: Resolvers::Ci::ProjectPipelineCountsResolver
field :ci_variables, Types::Ci::ProjectVariableType.connection_type,
......
......@@ -33350,7 +33350,7 @@ four standard [pagination arguments](#pagination-arguments):
 
##### `Project.pipeline`
 
Build pipeline of the project.
Pipeline of the project. If no arguments are provided, returns the latest pipeline for the head commit on the default branch.
 
Returns [`Pipeline`](#pipeline).
 
......@@ -33379,7 +33379,7 @@ Returns [`PipelineAnalytics`](#pipelineanalytics).
 
##### `Project.pipelineCounts`
 
Build pipeline counts of the project.
Pipeline counts of the project.
 
Returns [`PipelineCounts`](#pipelinecounts).
 
......@@ -33428,7 +33428,7 @@ four standard [pagination arguments](#pagination-arguments):
 
##### `Project.pipelines`
 
Build pipelines of the project.
Pipelines of the project.
 
Returns [`PipelineConnection`](#pipelineconnection).
 
......@@ -5,9 +5,10 @@
RSpec.describe Resolvers::Ci::ProjectPipelineResolver, feature_category: :continuous_integration do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
let_it_be(:pipeline) { create(:ci_pipeline, project: project, sha: 'sha') }
let_it_be(:other_project_pipeline) { create(:ci_pipeline, project: project, sha: 'sha2') }
let_it_be(:project) { create(:project, :repository) }
let_it_be(:project_pipeline_1) { create(:ci_pipeline, project: project, sha: project.commit.sha) }
let_it_be(:project_pipeline_2) { create(:ci_pipeline, project: project, sha: 'sha') }
let_it_be(:project_pipeline_3) { create(:ci_pipeline, project: project, sha: 'sha2') }
let_it_be(:other_pipeline) { create(:ci_pipeline) }
let(:current_user) { create(:user, developer_of: project) }
......@@ -23,27 +24,27 @@ def resolve_pipeline(project, args)
it 'resolves pipeline for the passed id' do
expect(Ci::PipelinesFinder)
.to receive(:new)
.with(project, current_user, ids: [pipeline.id.to_s])
.with(project, current_user, ids: [project_pipeline_1.id.to_s])
.and_call_original
result = batch_sync do
resolve_pipeline(project, { id: "gid://gitlab/Ci::Pipeline/#{pipeline.id}" })
resolve_pipeline(project, { id: project_pipeline_1.to_global_id })
end
expect(result).to eq(pipeline)
expect(result).to eq(project_pipeline_1)
end
it 'resolves pipeline for the passed iid' do
expect(Ci::PipelinesFinder)
.to receive(:new)
.with(project, current_user, iids: [pipeline.iid.to_s])
.with(project, current_user, iids: [project_pipeline_1.iid.to_s])
.and_call_original
result = batch_sync do
resolve_pipeline(project, { iid: pipeline.iid.to_s })
resolve_pipeline(project, { iid: project_pipeline_1.iid.to_s })
end
expect(result).to eq(pipeline)
expect(result).to eq(project_pipeline_1)
end
it 'resolves pipeline for the passed sha' do
......@@ -56,30 +57,30 @@ def resolve_pipeline(project, args)
resolve_pipeline(project, { sha: 'sha' })
end
expect(result).to eq(pipeline)
expect(result).to eq(project_pipeline_2)
end
it 'keeps the queries under the threshold for id' do
control = ActiveRecord::QueryRecorder.new do
batch_sync { resolve_pipeline(project, { id: "gid://gitlab/Ci::Pipeline/#{pipeline.id}" }) }
batch_sync { resolve_pipeline(project, { id: project_pipeline_1.to_global_id }) }
end
expect do
batch_sync do
resolve_pipeline(project, { id: "gid://gitlab/Ci::Pipeline/#{pipeline.id}" })
resolve_pipeline(project, { id: "gid://gitlab/Ci::Pipeline/#{other_project_pipeline.id}" })
resolve_pipeline(project, { id: project_pipeline_1.to_global_id })
resolve_pipeline(project, { id: project_pipeline_2.to_global_id })
end
end.not_to exceed_query_limit(control)
end
it 'keeps the queries under the threshold for iid' do
control = ActiveRecord::QueryRecorder.new do
batch_sync { resolve_pipeline(project, { iid: pipeline.iid.to_s }) }
batch_sync { resolve_pipeline(project, { iid: project_pipeline_1.iid.to_s }) }
end
expect do
batch_sync do
resolve_pipeline(project, { iid: pipeline.iid.to_s })
resolve_pipeline(project, { iid: project_pipeline_1.iid.to_s })
resolve_pipeline(project, { iid: other_pipeline.iid.to_s })
end
end.not_to exceed_query_limit(control)
......@@ -100,7 +101,7 @@ def resolve_pipeline(project, args)
it 'does not resolve a pipeline outside the project' do
result = batch_sync do
resolve_pipeline(other_pipeline.project, { iid: pipeline.iid.to_s })
resolve_pipeline(other_pipeline.project, { iid: project_pipeline_1.iid.to_s })
end
expect(result).to be_nil
......@@ -108,34 +109,72 @@ def resolve_pipeline(project, args)
it 'does not resolve a pipeline outside the project' do
result = batch_sync do
resolve_pipeline(other_pipeline.project, { id: "gid://gitlab/Ci::Pipeline/#{pipeline.id}9" })
resolve_pipeline(other_pipeline.project, { id: project_pipeline_1.to_global_id })
end
expect(result).to be_nil
end
it 'errors when no id, iid or sha is passed' do
expect_graphql_error_to_be_created(GraphQL::Schema::Validator::ValidationFailedError) do
resolve_pipeline(project, {})
context 'when no id, iid or sha is passed' do
it 'returns latest pipeline' do
result = batch_sync do
resolve_pipeline(project, {})
end
expect(result).to eq(project_pipeline_1)
end
it 'does not reduce complexity score' do
field = Types::BaseField.new(name: 'test', type: GraphQL::Types::String, resolver_class: described_class,
null: false, max_page_size: 1)
expect(field.complexity.call({}, {}, 1)).to eq 2
end
end
context 'when id is passed' do
it 'reduces complexity score' do
field = Types::BaseField.new(name: 'test', type: GraphQL::Types::String, resolver_class: described_class,
null: false, max_page_size: 1)
expect(field.complexity.call({}, { id: project_pipeline_1.to_global_id }, 1)).to eq(-7)
end
end
context 'when iid is passed' do
it 'reduces complexity score' do
field = Types::BaseField.new(name: 'test', type: GraphQL::Types::String, resolver_class: described_class,
null: false, max_page_size: 1)
expect(field.complexity.call({}, { iid: project_pipeline_1.iid.to_s }, 1)).to eq(-7)
end
end
context 'when sha is passed' do
it 'reduces complexity score' do
field = Types::BaseField.new(name: 'test', type: GraphQL::Types::String, resolver_class: described_class,
null: false, max_page_size: 1)
expect(field.complexity.call({}, { sha: 'sha' }, 1)).to eq(-7)
end
end
it 'errors when both iid and sha are passed' do
expect_graphql_error_to_be_created(GraphQL::Schema::Validator::ValidationFailedError) do
resolve_pipeline(project, { iid: pipeline.iid.to_s, sha: 'sha' })
resolve_pipeline(project, { iid: project_pipeline_1.iid.to_s, sha: 'sha' })
end
end
it 'errors when both id and iid are passed' do
expect_graphql_error_to_be_created(GraphQL::Schema::Validator::ValidationFailedError) do
resolve_pipeline(project, { id: "gid://gitlab/Ci::Pipeline/#{pipeline.id}", iid: pipeline.iid.to_s })
resolve_pipeline(project, { id: project_pipeline_1.to_global_id, iid: project_pipeline_1.iid.to_s })
end
end
it 'errors when id, iid and sha are passed' do
expect_graphql_error_to_be_created(GraphQL::Schema::Validator::ValidationFailedError) do
resolve_pipeline(project,
{ id: "gid://gitlab/Ci::Pipeline/#{pipeline.id}", iid: pipeline.iid.to_s, sha: '12345234' })
{ id: project_pipeline_1.to_global_id, iid: project_pipeline_1.iid.to_s, sha: '12345234' })
end
end
......@@ -147,10 +186,10 @@ def resolve_pipeline(project, args)
it 'resolves pipeline for the passed iid' do
result = batch_sync do
resolve_pipeline(project, { iid: pipeline.iid.to_s })
resolve_pipeline(project, { iid: project_pipeline_1.iid.to_s })
end
expect(result).to eq(pipeline)
expect(result).to eq(project_pipeline_1)
end
end
end
......@@ -7,6 +7,7 @@
let_it_be(:project) { create(:project, :repository, :public) }
let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
let_it_be(:pipeline_2) { create(:ci_pipeline, project: project, sha: 'sha') }
let_it_be(:current_user) { create(:user) }
let_it_be(:build_job) { create(:ci_build, :trace_with_sections, name: 'build-a', pipeline: pipeline, stage_idx: 0, stage: 'build') }
let_it_be(:failed_build) { create(:ci_build, :failed, name: 'failed-build', pipeline: pipeline, stage_idx: 0, stage: 'build') }
......@@ -459,6 +460,59 @@ def successful_pipeline
end
end
context 'when no arguments are passed' do
let(:variables) do
{
path: project.full_path
}
end
let(:query) do
<<~GQL
query($path: ID!) {
project(fullPath: $path) {
pipeline {
id
}
}
}
GQL
end
it 'returns latest pipeline' do
post_graphql(query, current_user: current_user, variables: variables)
expect(graphql_data_at(:project, :pipeline, :id)).to eq(pipeline.to_global_id.to_s)
end
end
context 'when sha argument is passed' do
let(:variables) do
{
path: project.full_path,
sha: 'sha'
}
end
let(:query) do
<<~GQL
query($path: ID!, $sha: String!) {
project(fullPath: $path) {
pipeline(sha: $sha) {
id
}
}
}
GQL
end
it 'returns pipeline by sha' do
post_graphql(query, current_user: current_user, variables: variables)
expect(graphql_data_at(:project, :pipeline, :id)).to eq(pipeline_2.to_global_id.to_s)
end
end
private
def build_query_to_find_pipeline_shas(*pipelines)
......
0% 加载中 .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册