diff --git a/app/finders/ci/runners_finder.rb b/app/finders/ci/runners_finder.rb index 18be2aec2e27cf092dd3eac2cb971b63dcc7e9d9..66b16e875c128fa69d558cd7d3a31f7fa159156c 100644 --- a/app/finders/ci/runners_finder.rb +++ b/app/finders/ci/runners_finder.rb @@ -29,6 +29,7 @@ def execute items = by_runner_type(items) items = by_tag_list(items) items = by_creator_id(items) + items = by_creator_username(items) items = by_version_prefix(items) items = request_tag_list(items) @@ -130,6 +131,16 @@ def by_creator_id(items) items.with_creator_id(creator_id) end + def by_creator_username(items) + creator_username = @params[:creator_username].presence + return items unless creator_username + + creator_id = User.find_by_username(creator_username)&.id + return Ci::Runner.none unless creator_id + + items.with_creator_id(creator_id) + end + def by_version_prefix(items) sanitized_prefix = @params.fetch(:version_prefix, '')[/^[\d+.]+/] return items unless sanitized_prefix diff --git a/app/graphql/resolvers/ci/runners_resolver.rb b/app/graphql/resolvers/ci/runners_resolver.rb index 38d2ebe046badf158550951b5bcd234a1467ae75..a289bee9806151bdd4e69ad30beb2cc50832d656 100644 --- a/app/graphql/resolvers/ci/runners_resolver.rb +++ b/app/graphql/resolvers/ci/runners_resolver.rb @@ -45,6 +45,11 @@ class RunnersResolver < BaseResolver required: false, description: 'Filter runners by creator ID.' + argument :creator_username, GraphQL::Types::String, + required: false, + description: 'Filter runners by creator username.', + alpha: { milestone: '16.7' } + argument :version_prefix, GraphQL::Types::String, required: false, description: "Filter runners by version. Runners that contain runner managers with the version at " \ @@ -81,6 +86,7 @@ def runners_finder_params(params) sort: params[:sort]&.to_s, creator_id: params[:creator_id] ? ::GitlabSchema.parse_gid(params[:creator_id], expected_type: ::User).model_id : nil, + creator_username: params[:creator_username], version_prefix: params[:version_prefix], preload: {} # we'll handle preloading ourselves }.compact diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 37515ddce91d8cd3088d9a6904ec603296a50b0b..f5198c54136a95f5ea4c0888cf110a851e9419b4 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -820,6 +820,7 @@ four standard [pagination arguments](#connection-pagination-arguments): | ---- | ---- | ----------- | | <a id="queryrunnersactive"></a>`active` **{warning-solid}** | [`Boolean`](#boolean) | **Deprecated** in 14.8. This was renamed. Use: `paused`. | | <a id="queryrunnerscreatorid"></a>`creatorId` | [`UserID`](#userid) | Filter runners by creator ID. | +| <a id="queryrunnerscreatorusername"></a>`creatorUsername` **{warning-solid}** | [`String`](#string) | **Introduced** in 16.7. This feature is an Experiment. It can be changed or removed at any time. Filter runners by creator username. | | <a id="queryrunnerspaused"></a>`paused` | [`Boolean`](#boolean) | Filter runners by `paused` (true) or `active` (false) status. | | <a id="queryrunnerssearch"></a>`search` | [`String`](#string) | Filter by full token or partial text in description field. | | <a id="queryrunnerssort"></a>`sort` | [`CiRunnerSort`](#cirunnersort) | Sort order of results. | @@ -20313,6 +20314,7 @@ four standard [pagination arguments](#connection-pagination-arguments): | ---- | ---- | ----------- | | <a id="grouprunnersactive"></a>`active` **{warning-solid}** | [`Boolean`](#boolean) | **Deprecated** in 14.8. This was renamed. Use: `paused`. | | <a id="grouprunnerscreatorid"></a>`creatorId` | [`UserID`](#userid) | Filter runners by creator ID. | +| <a id="grouprunnerscreatorusername"></a>`creatorUsername` **{warning-solid}** | [`String`](#string) | **Introduced** in 16.7. This feature is an Experiment. It can be changed or removed at any time. Filter runners by creator username. | | <a id="grouprunnersmembership"></a>`membership` | [`CiRunnerMembershipFilter`](#cirunnermembershipfilter) | Control which runners to include in the results. | | <a id="grouprunnerspaused"></a>`paused` | [`Boolean`](#boolean) | Filter runners by `paused` (true) or `active` (false) status. | | <a id="grouprunnerssearch"></a>`search` | [`String`](#string) | Filter by full token or partial text in description field. | @@ -25651,6 +25653,7 @@ four standard [pagination arguments](#connection-pagination-arguments): | ---- | ---- | ----------- | | <a id="projectrunnersactive"></a>`active` **{warning-solid}** | [`Boolean`](#boolean) | **Deprecated** in 14.8. This was renamed. Use: `paused`. | | <a id="projectrunnerscreatorid"></a>`creatorId` | [`UserID`](#userid) | Filter runners by creator ID. | +| <a id="projectrunnerscreatorusername"></a>`creatorUsername` **{warning-solid}** | [`String`](#string) | **Introduced** in 16.7. This feature is an Experiment. It can be changed or removed at any time. Filter runners by creator username. | | <a id="projectrunnerspaused"></a>`paused` | [`Boolean`](#boolean) | Filter runners by `paused` (true) or `active` (false) status. | | <a id="projectrunnerssearch"></a>`search` | [`String`](#string) | Filter by full token or partial text in description field. | | <a id="projectrunnerssort"></a>`sort` | [`CiRunnerSort`](#cirunnersort) | Sort order of results. | diff --git a/spec/finders/ci/runners_finder_spec.rb b/spec/finders/ci/runners_finder_spec.rb index 7e9ef2139c997f0dc986b84caa2ac1f538325648..b1252b53127b41a51f7810ac2fdaf93532f7c8d6 100644 --- a/spec/finders/ci/runners_finder_spec.rb +++ b/spec/finders/ci/runners_finder_spec.rb @@ -153,7 +153,7 @@ def execute end end - context 'by creator' do + context 'by creator id' do it 'calls the corresponding scope on Ci::Runner' do expect(Ci::Runner).to receive(:with_creator_id).with('1').and_call_original @@ -161,6 +161,24 @@ def execute end end + context 'by creator username' do + let_it_be(:admin_runner) { create(:ci_runner, creator: admin) } + + it 'calls the corresponding scope on Ci::Runner' do + expect(Ci::Runner).to receive(:with_creator_id).with(admin.id).and_call_original + + result = described_class.new(current_user: admin, params: { creator_username: admin.username }).execute + expect(result).to match_array [admin_runner] + end + + it 'does not call the scope when the username is not found and is empty' do + expect(Ci::Runner).not_to receive(:with_creator_id) + + result = described_class.new(current_user: admin, params: { creator_username: "not a username" }).execute + expect(result).to be_empty + end + end + context 'by version' do it 'calls the corresponding scope on Ci::Runner' do expect(Ci::Runner).to receive(:with_version_prefix).with('15.').and_call_original diff --git a/spec/graphql/resolvers/ci/runners_resolver_spec.rb b/spec/graphql/resolvers/ci/runners_resolver_spec.rb index a0239a6ff344f556bf0b399a77653b353099b73b..f59b0ccf8b0605d176d98181c27847c3921cd5c5 100644 --- a/spec/graphql/resolvers/ci/runners_resolver_spec.rb +++ b/spec/graphql/resolvers/ci/runners_resolver_spec.rb @@ -87,6 +87,7 @@ search: 'abc', sort: :contacted_asc, creator_id: 'gid://gitlab/User/1', + creator_username: 'root', version_prefix: '15.' } end @@ -102,6 +103,7 @@ search: 'abc', sort: 'contacted_asc', creator_id: '1', + creator_username: 'root', version_prefix: '15.' } end diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb index d4f7db3bddd814d4d289dcbae29975e32d3b3348..59f69013b377d0055a5f28b1d37c04635099d05a 100644 --- a/spec/models/ci/runner_spec.rb +++ b/spec/models/ci/runner_spec.rb @@ -525,16 +525,17 @@ end describe '.with_creator_id' do - subject { described_class.with_creator_id('1') } + let_it_be(:admin) { create(:admin, username: 'root') } + let_it_be(:user2) { create(:user, username: 'user2') } - let_it_be(:runner1) { create(:ci_runner, creator_id: 2) } - let_it_be(:runner2) { create(:ci_runner, creator_id: 1) } - let_it_be(:runner3) { create(:ci_runner, creator_id: 1) } - let_it_be(:runner4) { create(:ci_runner, creator_id: nil) } + let_it_be(:user_runner1) { create(:ci_runner, creator: user2) } + let_it_be(:admin_runner1) { create(:ci_runner, creator: admin) } + let_it_be(:admin_runner2) { create(:ci_runner, creator: admin) } + let_it_be(:runner_without_creator) { create(:ci_runner, creator: nil) } - it "returns runners with creator_id '1'" do - is_expected.to contain_exactly(runner2, runner3) - end + subject { described_class.with_creator_id(admin.id.to_s) } + + it { is_expected.to contain_exactly(admin_runner1, admin_runner2) } end describe '.with_version_prefix' do