Commit 3bb7caad authored by Stan Hu's avatar Stan Hu

Merge branch 'pedropombeiro/349452-graphql-add-projects-to-runner-type' into 'master'

GraphQL: Add projects property to CiRunner type

See merge request gitlab-org/gitlab!78547
parents 10476374 2771c42e
......@@ -65,6 +65,8 @@ module Types
feature_flag: :graphql_ci_runner_executor
field :groups, ::Types::GroupType.connection_type, null: true,
description: 'Groups the runner is associated with. For group runners only.'
field :projects, ::Types::ProjectType.connection_type, null: true,
description: 'Projects the runner is associated with. For project runners only.'
def job_count
# We limit to 1 above the JOB_COUNT_LIMIT to indicate that more items exist after JOB_COUNT_LIMIT
......@@ -94,31 +96,42 @@ module Types
end
end
end
# rubocop: enable CodeReuse/ActiveRecord
def groups
BatchLoader::GraphQL.for(runner.id).batch(key: :runner_groups) do |runner_ids, loader, args|
runner_and_namespace_ids =
::Ci::RunnerNamespace
.where(runner_id: runner_ids)
.pluck(:runner_id, :namespace_id)
return unless runner.group_type?
group_ids_by_runner_id = runner_and_namespace_ids.group_by(&:first).transform_values { |v| v.pluck(1) }
group_ids = runner_and_namespace_ids.pluck(1).uniq
batched_owners(::Ci::RunnerNamespace, Group, :runner_groups, :namespace_id)
end
groups = Group.where(id: group_ids).index_by(&:id)
def projects
return unless runner.project_type?
runner_ids.each do |runner_id|
loader.call(runner_id, group_ids_by_runner_id[runner_id]&.map { |group_id| groups[group_id] })
end
end
batched_owners(::Ci::RunnerProject, Project, :runner_projects, :project_id)
end
# rubocop: enable CodeReuse/ActiveRecord
private
def can_admin_runners?
context[:current_user]&.can_admin_all_resources?
end
# rubocop: disable CodeReuse/ActiveRecord
def batched_owners(runner_assoc_type, assoc_type, key, column_name)
BatchLoader::GraphQL.for(runner.id).batch(key: key) do |runner_ids, loader, args|
runner_and_owner_ids = runner_assoc_type.where(runner_id: runner_ids).pluck(:runner_id, column_name)
owner_ids_by_runner_id = runner_and_owner_ids.group_by(&:first).transform_values { |v| v.pluck(1) }
owner_ids = runner_and_owner_ids.pluck(1).uniq
owners = assoc_type.where(id: owner_ids).index_by(&:id)
runner_ids.each do |runner_id|
loader.call(runner_id, owner_ids_by_runner_id[runner_id]&.map { |owner_id| owners[owner_id] } || [])
end
end
end
# rubocop: enable CodeReuse/ActiveRecord
end
end
end
......
......@@ -9030,6 +9030,7 @@ Represents the total number of issues and their weights for a particular day.
| <a id="cirunnermaximumtimeout"></a>`maximumTimeout` | [`Int`](#int) | Maximum timeout (in seconds) for jobs processed by the runner. |
| <a id="cirunnerprivateprojectsminutescostfactor"></a>`privateProjectsMinutesCostFactor` | [`Float`](#float) | Private projects' "minutes cost factor" associated with the runner (GitLab.com only). |
| <a id="cirunnerprojectcount"></a>`projectCount` | [`Int`](#int) | Number of projects that the runner is associated with. |
| <a id="cirunnerprojects"></a>`projects` | [`ProjectConnection`](#projectconnection) | Projects the runner is associated with. For project runners only. (see [Connections](#connections)) |
| <a id="cirunnerpublicprojectsminutescostfactor"></a>`publicProjectsMinutesCostFactor` | [`Float`](#float) | Public projects' "minutes cost factor" associated with the runner (GitLab.com only). |
| <a id="cirunnerrevision"></a>`revision` | [`String`](#string) | Revision of the runner. |
| <a id="cirunnerrununtagged"></a>`runUntagged` | [`Boolean!`](#boolean) | Indicates the runner is able to run untagged jobs. |
......@@ -12,7 +12,7 @@ RSpec.describe GitlabSchema.types['CiRunner'] do
id description created_at contacted_at maximum_timeout access_level active status
version short_sha revision locked run_untagged ip_address runner_type tag_list
project_count job_count admin_url edit_admin_url user_permissions executor_name
groups
groups projects
]
expect(described_class).to include_graphql_fields(*expected_fields)
......
......@@ -25,6 +25,8 @@ RSpec.describe 'Query.runner(id)' do
access_level: 0, tag_list: %w[tag1 tag2], run_untagged: true, executor_type: :shell)
end
let_it_be(:active_project_runner) { create(:ci_runner, :project) }
def get_runner(id)
case id
when :active_instance_runner
......@@ -33,6 +35,8 @@ RSpec.describe 'Query.runner(id)' do
inactive_instance_runner
when :active_group_runner
active_group_runner
when :active_project_runner
active_project_runner
end
end
......@@ -55,7 +59,7 @@ RSpec.describe 'Query.runner(id)' do
runner = get_runner(runner_id)
expect(runner_data).to match a_hash_including(
'id' => "gid://gitlab/Ci::Runner/#{runner.id}",
'id' => runner.to_global_id.to_s,
'description' => runner.description,
'createdAt' => runner.created_at&.iso8601,
'contactedAt' => runner.contacted_at&.iso8601,
......@@ -103,7 +107,7 @@ RSpec.describe 'Query.runner(id)' do
runner = get_runner(runner_id)
expect(runner_data).to match a_hash_including(
'id' => "gid://gitlab/Ci::Runner/#{runner.id}",
'id' => runner.to_global_id.to_s,
'adminUrl' => nil
)
expect(runner_data['tagList']).to match_array runner.tag_list
......@@ -179,7 +183,7 @@ RSpec.describe 'Query.runner(id)' do
runner_data = graphql_data_at(:runner)
expect(runner_data).to match a_hash_including(
'id' => "gid://gitlab/Ci::Runner/#{project_runner.id}",
'id' => project_runner.to_global_id.to_s,
'locked' => is_locked
)
end
......@@ -216,7 +220,7 @@ RSpec.describe 'Query.runner(id)' do
a_hash_including(
'webUrl' => "http://localhost/groups/#{group.full_path}/-/runners/#{active_group_runner.id}",
'node' => {
'id' => "gid://gitlab/Ci::Runner/#{active_group_runner.id}"
'id' => active_group_runner.to_global_id.to_s
}
)
]
......@@ -227,7 +231,7 @@ RSpec.describe 'Query.runner(id)' do
let(:query) do
%(
query {
runner(id: "gid://gitlab/Ci::Runner/#{active_group_runner.id}") {
runner(id: "#{active_group_runner.to_global_id}") {
groups {
nodes {
id
......@@ -302,21 +306,36 @@ RSpec.describe 'Query.runner(id)' do
let!(:job) { create(:ci_build, runner: project_runner1) }
context 'requesting project and job counts' do
context 'requesting projects and counts for projects and jobs' do
let(:query) do
%(
query {
projectRunner1: runner(id: "#{project_runner1.to_global_id}") {
projectCount
jobCount
projects {
nodes {
id
}
}
}
projectRunner2: runner(id: "#{project_runner2.to_global_id}") {
projectCount
jobCount
projects {
nodes {
id
}
}
}
activeInstanceRunner: runner(id: "#{active_instance_runner.to_global_id}") {
projectCount
jobCount
projects {
nodes {
id
}
}
}
}
)
......@@ -335,13 +354,23 @@ RSpec.describe 'Query.runner(id)' do
expect(runner1_data).to match a_hash_including(
'jobCount' => 1,
'projectCount' => 2)
'projectCount' => 2,
'projects' => {
'nodes' => [
{ 'id' => project1.to_global_id.to_s },
{ 'id' => project2.to_global_id.to_s }
]
})
expect(runner2_data).to match a_hash_including(
'jobCount' => 0,
'projectCount' => 0)
'projectCount' => 0,
'projects' => {
'nodes' => []
})
expect(runner3_data).to match a_hash_including(
'jobCount' => 0,
'projectCount' => nil)
'projectCount' => nil,
'projects' => nil)
end
end
end
......@@ -356,6 +385,10 @@ RSpec.describe 'Query.runner(id)' do
context 'on group runner' do
it_behaves_like 'retrieval by unauthorized user', :active_group_runner
end
context 'on project runner' do
it_behaves_like 'retrieval by unauthorized user', :active_project_runner
end
end
describe 'by non-admin user' do
......
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