add tag filter to admin runners page
This commit is contained in:
parent
d3accd36a0
commit
0fad7aa79c
|
@ -17,6 +17,14 @@ const tokenKeys = [
|
|||
icon: 'cube',
|
||||
tag: 'type',
|
||||
},
|
||||
{
|
||||
key: 'tag',
|
||||
type: 'array',
|
||||
param: 'name[]',
|
||||
symbol: '~',
|
||||
icon: 'tag',
|
||||
tag: '~tag',
|
||||
},
|
||||
];
|
||||
|
||||
const AdminRunnersFilteredSearchTokenKeys = new FilteredSearchTokenKeys(tokenKeys);
|
||||
|
|
|
@ -111,6 +111,15 @@ export default class FilteredSearchDropdownManager {
|
|||
gl: NullDropdown,
|
||||
element: this.container.querySelector('#js-dropdown-admin-runner-type'),
|
||||
},
|
||||
tag: {
|
||||
reference: null,
|
||||
gl: DropdownNonUser,
|
||||
extraArguments: {
|
||||
endpoint: this.getRunnerTagsEndpoint(),
|
||||
symbol: '~',
|
||||
},
|
||||
element: this.container.querySelector('#js-dropdown-runner-tag'),
|
||||
},
|
||||
};
|
||||
|
||||
supportedTokens.forEach(type => {
|
||||
|
@ -146,6 +155,10 @@ export default class FilteredSearchDropdownManager {
|
|||
return endpoint;
|
||||
}
|
||||
|
||||
getRunnerTagsEndpoint() {
|
||||
return `${this.baseEndpoint}/admin/runners/tag_list.json`;
|
||||
}
|
||||
|
||||
static addWordToInput(tokenName, tokenValue = '', clicked = false, options = {}) {
|
||||
const { uppercaseTokenName = false, capitalizeTokenValue = false } = options;
|
||||
const input = FilteredSearchContainer.container.querySelector('.filtered-search');
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::RunnersController < Admin::ApplicationController
|
||||
before_action :runner, except: :index
|
||||
before_action :runner, except: [:index, :tag_list]
|
||||
|
||||
def index
|
||||
finder = Admin::RunnersFinder.new(params: params)
|
||||
|
@ -48,6 +48,10 @@ class Admin::RunnersController < Admin::ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
def tag_list
|
||||
render json: AutocompleteTagsService.new(Ci::Runner).run
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def runner
|
||||
|
|
|
@ -11,6 +11,7 @@ class Admin::RunnersFinder < UnionFinder
|
|||
search!
|
||||
filter_by_status!
|
||||
filter_by_runner_type!
|
||||
filter_by_tag_list!
|
||||
sort!
|
||||
paginate!
|
||||
|
||||
|
@ -44,6 +45,14 @@ class Admin::RunnersFinder < UnionFinder
|
|||
filter_by!(:type_type, Ci::Runner::AVAILABLE_TYPES)
|
||||
end
|
||||
|
||||
def filter_by_tag_list!
|
||||
tag_list = @params[:tag_name].presence
|
||||
|
||||
if tag_list
|
||||
@runners = @runners.tagged_with(tag_list)
|
||||
end
|
||||
end
|
||||
|
||||
def sort!
|
||||
@runners = @runners.order_by(sort_key)
|
||||
end
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AutocompleteTagsService
|
||||
def initialize(taggable_type)
|
||||
@taggable_type = taggable_type
|
||||
end
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def run
|
||||
@taggable_type
|
||||
.all_tags
|
||||
.pluck(:id, :name).map do |id, name|
|
||||
{ id: id, title: name }
|
||||
end
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
end
|
|
@ -92,6 +92,24 @@
|
|||
= button_tag class: %w[btn btn-link] do
|
||||
= runner_type.titleize
|
||||
|
||||
#js-dropdown-admin-runner-type.filtered-search-input-dropdown-menu.dropdown-menu
|
||||
%ul{ data: { dropdown: true } }
|
||||
- Ci::Runner::AVAILABLE_TYPES.each do |runner_type|
|
||||
%li.filter-dropdown-item{ data: { value: runner_type } }
|
||||
= button_tag class: %w[btn btn-link] do
|
||||
= runner_type.titleize
|
||||
|
||||
#js-dropdown-runner-tag.filtered-search-input-dropdown-menu.dropdown-menu
|
||||
%ul{ data: { dropdown: true } }
|
||||
%li.filter-dropdown-item{ data: { value: 'none' } }
|
||||
%button.btn.btn-link
|
||||
= _('No Tag')
|
||||
%li.divider.droplab-item-ignore
|
||||
%ul.filter-dropdown{ data: { dynamic: true, dropdown: true } }
|
||||
%li.filter-dropdown-item
|
||||
%button.btn.btn-link.js-data-value
|
||||
{{title}}
|
||||
|
||||
= button_tag class: %w[clear-search hidden] do
|
||||
= icon('times')
|
||||
.filter-dropdown-container
|
||||
|
|
|
@ -120,6 +120,10 @@ namespace :admin do
|
|||
get :resume
|
||||
get :pause
|
||||
end
|
||||
|
||||
collection do
|
||||
get :tag_list, format: :json
|
||||
end
|
||||
end
|
||||
|
||||
resources :jobs, only: :index do
|
||||
|
|
|
@ -4943,6 +4943,9 @@ msgstr ""
|
|||
msgid "No %{providerTitle} repositories available to import"
|
||||
msgstr ""
|
||||
|
||||
msgid "No Tag"
|
||||
msgstr ""
|
||||
|
||||
msgid "No activities found"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -141,6 +141,56 @@ describe "Admin Runners" do
|
|||
end
|
||||
end
|
||||
|
||||
describe 'filter by tag', :js do
|
||||
it 'shows correct runner when tag matches' do
|
||||
create :ci_runner, description: 'runner-blue', tag_list: ['blue']
|
||||
create :ci_runner, description: 'runner-red', tag_list: ['red']
|
||||
|
||||
visit admin_runners_path
|
||||
|
||||
expect(page).to have_content 'runner-blue'
|
||||
expect(page).to have_content 'runner-red'
|
||||
|
||||
input_filtered_search_keys('tag:blue')
|
||||
|
||||
expect(page).to have_content 'runner-blue'
|
||||
expect(page).not_to have_content 'runner-red'
|
||||
end
|
||||
|
||||
it 'shows no runner when tag does not match' do
|
||||
create :ci_runner, description: 'runner-blue', tag_list: ['blue']
|
||||
create :ci_runner, description: 'runner-red', tag_list: ['blue']
|
||||
|
||||
visit admin_runners_path
|
||||
|
||||
input_filtered_search_keys('tag:red')
|
||||
|
||||
expect(page).not_to have_content 'runner-blue'
|
||||
expect(page).not_to have_content 'runner-blue'
|
||||
expect(page).to have_text 'No runners found'
|
||||
end
|
||||
|
||||
it 'shows correct runner when tag is selected and search term is entered' do
|
||||
create :ci_runner, description: 'runner-a-1', tag_list: ['blue']
|
||||
create :ci_runner, description: 'runner-a-2', tag_list: ['red']
|
||||
create :ci_runner, description: 'runner-b-1', tag_list: ['blue']
|
||||
|
||||
visit admin_runners_path
|
||||
|
||||
input_filtered_search_keys('tag:blue')
|
||||
|
||||
expect(page).to have_content 'runner-a-1'
|
||||
expect(page).to have_content 'runner-b-1'
|
||||
expect(page).not_to have_content 'runner-a-2'
|
||||
|
||||
input_filtered_search_keys('tag:blue runner-a')
|
||||
|
||||
expect(page).to have_content 'runner-a-1'
|
||||
expect(page).not_to have_content 'runner-b-1'
|
||||
expect(page).not_to have_content 'runner-a-2'
|
||||
end
|
||||
end
|
||||
|
||||
it 'sorts by last contact date', :js do
|
||||
create(:ci_runner, description: 'runner-1', created_at: '2018-07-12 15:37', contacted_at: '2018-07-12 15:37')
|
||||
create(:ci_runner, description: 'runner-2', created_at: '2018-07-12 16:37', contacted_at: '2018-07-12 16:37')
|
||||
|
|
|
@ -37,6 +37,14 @@ describe Admin::RunnersFinder do
|
|||
end
|
||||
end
|
||||
|
||||
context 'filter by tag_name' do
|
||||
it 'calls the corresponding scope on Ci::Runner' do
|
||||
expect(Ci::Runner).to receive(:tagged_with).with(%w[tag1 tag2]).and_call_original
|
||||
|
||||
described_class.new(params: { tag_name: %w[tag1 tag2] }).execute
|
||||
end
|
||||
end
|
||||
|
||||
context 'sort' do
|
||||
context 'without sort param' do
|
||||
it 'sorts by created_at' do
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe AutocompleteTagsService do
|
||||
describe '#run' do
|
||||
it 'returns a hash of all tags' do
|
||||
create(:ci_runner, tag_list: %w[tag1 tag2])
|
||||
create(:ci_runner, tag_list: %w[tag1 tag3])
|
||||
create(:project, tag_list: %w[tag4])
|
||||
|
||||
expect(described_class.new(Ci::Runner).run).to match_array [
|
||||
{ id: kind_of(Integer), title: 'tag1' },
|
||||
{ id: kind_of(Integer), title: 'tag2' },
|
||||
{ id: kind_of(Integer), title: 'tag3' }
|
||||
]
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue