diff --git a/app/assets/javascripts/pages/dashboard/merge_requests/index.js b/app/assets/javascripts/pages/dashboard/merge_requests/index.js index 6f98331111941f5a2dc5a9381989a5ec46da396e..6bd71ae519c2702e06cb6dd8f273d2063836b441 100644 --- a/app/assets/javascripts/pages/dashboard/merge_requests/index.js +++ b/app/assets/javascripts/pages/dashboard/merge_requests/index.js @@ -1,13 +1,4 @@ -import Vue from 'vue'; -import { __ } from '~/locale'; -import addExtraTokensForMergeRequests from 'ee_else_ce/filtered_search/add_extra_tokens_for_merge_requests'; -import { createFilteredSearchTokenKeys } from '~/filtered_search/issuable_filtered_search_token_keys'; -import { FILTERED_SEARCH } from '~/filtered_search/constants'; -import initFilteredSearch from '~/pages/search/init_filtered_search'; -import { initNewResourceDropdown } from '~/vue_shared/components/new_resource_dropdown/init_new_resource_dropdown'; -import { RESOURCE_TYPE_MERGE_REQUEST } from '~/vue_shared/components/new_resource_dropdown/constants'; -import searchUserProjectsWithMergeRequestsEnabled from '~/vue_shared/components/new_resource_dropdown/graphql/search_user_projects_with_merge_requests_enabled.query.graphql'; -import ActionDropdown from '~/merge_request_dashboard/components/action_dropdown.vue'; +import { initMergeRequestsDashboard } from './page'; const el = document.getElementById('js-merge-request-dashboard'); @@ -18,41 +9,5 @@ if (el) { initMergeRequestDashboard(el); }); } else { - const actionDropdownEl = document.querySelector('.js-action-dropdown'); - - if (actionDropdownEl) { - // eslint-disable-next-line no-new - new Vue({ - el: actionDropdownEl, - provide: { - switchDashboardPath: actionDropdownEl.dataset.switchdashboardpath, - dashboardLinkText: __('Switch to new dashboard'), - experimentEnabled: false, - }, - render(createElement) { - return createElement(ActionDropdown); - }, - }); - } - - const IssuableFilteredSearchTokenKeys = createFilteredSearchTokenKeys({ - disableReleaseFilter: true, - }); - - addExtraTokensForMergeRequests(IssuableFilteredSearchTokenKeys, { - disableBranchFilter: true, - disableReleaseFilter: true, - disableEnvironmentFilter: true, - }); - - initFilteredSearch({ - page: FILTERED_SEARCH.MERGE_REQUESTS, - filteredSearchTokenKeys: IssuableFilteredSearchTokenKeys, - useDefaultState: true, - }); - - initNewResourceDropdown({ - resourceType: RESOURCE_TYPE_MERGE_REQUEST, - query: searchUserProjectsWithMergeRequestsEnabled, - }); + initMergeRequestsDashboard(); } diff --git a/app/assets/javascripts/pages/dashboard/merge_requests/page.js b/app/assets/javascripts/pages/dashboard/merge_requests/page.js new file mode 100644 index 0000000000000000000000000000000000000000..16da7cd99a5590ab59bfe2f5e5d08997a10ec4e2 --- /dev/null +++ b/app/assets/javascripts/pages/dashboard/merge_requests/page.js @@ -0,0 +1,50 @@ +import Vue from 'vue'; +import { __ } from '~/locale'; +import addExtraTokensForMergeRequests from 'ee_else_ce/filtered_search/add_extra_tokens_for_merge_requests'; +import { createFilteredSearchTokenKeys } from '~/filtered_search/issuable_filtered_search_token_keys'; +import { FILTERED_SEARCH } from '~/filtered_search/constants'; +import initFilteredSearch from '~/pages/search/init_filtered_search'; +import { initNewResourceDropdown } from '~/vue_shared/components/new_resource_dropdown/init_new_resource_dropdown'; +import { RESOURCE_TYPE_MERGE_REQUEST } from '~/vue_shared/components/new_resource_dropdown/constants'; +import searchUserProjectsWithMergeRequestsEnabled from '~/vue_shared/components/new_resource_dropdown/graphql/search_user_projects_with_merge_requests_enabled.query.graphql'; +import ActionDropdown from '~/merge_request_dashboard/components/action_dropdown.vue'; + +export const initMergeRequestsDashboard = () => { + const actionDropdownEl = document.querySelector('.js-action-dropdown'); + + if (actionDropdownEl) { + // eslint-disable-next-line no-new + new Vue({ + el: actionDropdownEl, + provide: { + switchDashboardPath: actionDropdownEl.dataset.switchdashboardpath, + dashboardLinkText: __('Switch to new dashboard'), + experimentEnabled: false, + }, + render(createElement) { + return createElement(ActionDropdown); + }, + }); + } + + const IssuableFilteredSearchTokenKeys = createFilteredSearchTokenKeys({ + disableReleaseFilter: true, + }); + + addExtraTokensForMergeRequests(IssuableFilteredSearchTokenKeys, { + disableBranchFilter: true, + disableReleaseFilter: true, + disableEnvironmentFilter: true, + }); + + initFilteredSearch({ + page: FILTERED_SEARCH.MERGE_REQUESTS, + filteredSearchTokenKeys: IssuableFilteredSearchTokenKeys, + useDefaultState: true, + }); + + initNewResourceDropdown({ + resourceType: RESOURCE_TYPE_MERGE_REQUEST, + query: searchUserProjectsWithMergeRequestsEnabled, + }); +}; diff --git a/app/assets/javascripts/pages/dashboard/search_merge_requests/index.js b/app/assets/javascripts/pages/dashboard/search_merge_requests/index.js new file mode 100644 index 0000000000000000000000000000000000000000..ce3ebcd046bdc7979f1692d170248bf93446bd05 --- /dev/null +++ b/app/assets/javascripts/pages/dashboard/search_merge_requests/index.js @@ -0,0 +1,3 @@ +import { initMergeRequestsDashboard } from '../merge_requests/page'; + +initMergeRequestsDashboard(); diff --git a/app/controllers/concerns/issuable_collections_action.rb b/app/controllers/concerns/issuable_collections_action.rb index 86ad87cc2c15c08b9c40989c9c20400b489af3d2..c6137da7859e56eebaea53018e90a0067292c7d4 100644 --- a/app/controllers/concerns/issuable_collections_action.rb +++ b/app/controllers/concerns/issuable_collections_action.rb @@ -6,7 +6,7 @@ module IssuableCollectionsAction include IssuesCalendar included do - before_action :check_search_rate_limit!, only: [:issues, :merge_requests], if: -> { + before_action :check_search_rate_limit!, only: [:issues, :merge_requests, :search_merge_requests], if: -> { params[:search].present? } end @@ -24,17 +24,11 @@ def issues format.atom { render layout: 'xml' } end end + # rubocop:enable Gitlab/ModuleWithInstanceVariables def merge_requests - @merge_requests = issuables_collection.page(params[:page]) - - @issuable_meta_data = Gitlab::IssuableMetadata.new(current_user, @merge_requests).data - rescue ActiveRecord::QueryCanceled => exception # rubocop:disable Database/RescueQueryCanceled - log_exception(exception) - - @search_timeout_occurred = true + render_merge_requests end - # rubocop:enable Gitlab/ModuleWithInstanceVariables def issues_calendar render_issues_calendar(issuables_collection) @@ -46,7 +40,7 @@ def sorting_field case action_name when 'issues' Issue::SORTING_PREFERENCE_FIELD - when 'merge_requests' + when 'merge_requests', 'search_merge_requests' MergeRequest::SORTING_PREFERENCE_FIELD end end @@ -55,7 +49,7 @@ def finder_type case action_name when 'issues', 'issues_calendar' IssuesFinder - when 'merge_requests' + when 'merge_requests', 'search_merge_requests' MergeRequestsFinder end end @@ -68,4 +62,16 @@ def finder_options issue_types: issue_types ) end + + # rubocop:disable Gitlab/ModuleWithInstanceVariables + def render_merge_requests + @merge_requests = issuables_collection.page(params[:page]) + + @issuable_meta_data = Gitlab::IssuableMetadata.new(current_user, @merge_requests).data + rescue ActiveRecord::QueryCanceled => exception # rubocop:disable Database/RescueQueryCanceled + log_exception(exception) + + @search_timeout_occurred = true + end + # rubocop:enable Gitlab/ModuleWithInstanceVariables end diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb index 188a8540a58d0e446c21ffc589064de38ecb7f6b..613d05206cb7a24ba866d1b40045749a122dc16b 100644 --- a/app/controllers/dashboard_controller.rb +++ b/app/controllers/dashboard_controller.rb @@ -8,15 +8,15 @@ class DashboardController < Dashboard::ApplicationController prepend_before_action(only: [:issues_calendar]) { authenticate_sessionless_user!(:ics) } before_action :event_filter, only: :activity - before_action :projects, only: [:issues, :merge_requests] - before_action :set_show_full_reference, only: [:issues, :merge_requests] - before_action :check_filters_presence!, only: [:issues, :merge_requests] + before_action :projects, only: [:issues, :merge_requests, :search_merge_requests] + before_action :set_show_full_reference, only: [:issues, :merge_requests, :search_merge_requests] + before_action :check_filters_presence!, only: [:issues, :merge_requests, :search_merge_requests] before_action only: :issues do push_frontend_feature_flag(:frontend_caching) end - before_action only: :merge_requests do + before_action only: [:merge_requests, :search_merge_requests] do push_frontend_feature_flag(:mr_approved_filter, type: :ops) end @@ -24,9 +24,9 @@ class DashboardController < Dashboard::ApplicationController feature_category :user_profile, [:activity] feature_category :team_planning, [:issues, :issues_calendar] - feature_category :code_review_workflow, [:merge_requests] + feature_category :code_review_workflow, [:merge_requests, :search_merge_requests] - urgency :low, [:merge_requests, :activity] + urgency :low, [:merge_requests, :activity, :search_merge_requests] urgency :low, [:issues, :issues_calendar] def activity @@ -40,6 +40,10 @@ def activity end end + def search_merge_requests + render_merge_requests + end + protected def load_events diff --git a/app/views/dashboard/search_merge_requests.html.haml b/app/views/dashboard/search_merge_requests.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..5717726d32cf89be64b0125abb7046d18b7f382b --- /dev/null +++ b/app/views/dashboard/search_merge_requests.html.haml @@ -0,0 +1 @@ += render template: 'dashboard/merge_requests' diff --git a/config/routes/dashboard.rb b/config/routes/dashboard.rb index c7db3a64408c40ed9b6f99d2f7e64e87e46cb49a..556a1618ee87f8b4b306b2e2f4434f6f32545c46 100644 --- a/config/routes/dashboard.rb +++ b/config/routes/dashboard.rb @@ -5,7 +5,7 @@ get :issues get :merge_requests get :activity - get 'merge_requests/search', to: 'dashboard#merge_requests' + get 'merge_requests/search', to: 'dashboard#search_merge_requests' scope module: :dashboard do resources :milestones, only: [:index] diff --git a/spec/controllers/dashboard_controller_spec.rb b/spec/controllers/dashboard_controller_spec.rb index 52f6f08e17af5b161bd33a6dd87bff2dbb2d16aa..32c574934662cde989bd131aeb4e8d599b224521 100644 --- a/spec/controllers/dashboard_controller_spec.rb +++ b/spec/controllers/dashboard_controller_spec.rb @@ -90,6 +90,68 @@ end end end + + describe 'GET merge requests search' do + it_behaves_like 'issuables requiring filter', :search_merge_requests + + context 'when an ActiveRecord::QueryCanceled is raised' do + before do + allow_next_instance_of(Gitlab::IssuableMetadata) do |instance| + allow(instance).to receive(:data).and_raise(ActiveRecord::QueryCanceled) + end + end + + it 'sets :search_timeout_occurred' do + get :search_merge_requests, params: { author_id: user.id } + + expect(response).to have_gitlab_http_status(:ok) + expect(assigns(:search_timeout_occurred)).to eq(true) + end + + context 'rendering views' do + render_views + + it 'shows error message' do + get :search_merge_requests, params: { author_id: user.id } + + expect(response.body).to have_content('Too many results to display. Edit your search or add a filter.') + end + + it 'does not display MR counts in nav' do + get :search_merge_requests, params: { author_id: user.id } + + expect(response.body).to have_content('Open Merged Closed All') + expect(response.body).not_to have_content('Open 0 Merged 0 Closed 0 All 0') + end + end + + it 'logs the exception' do + expect(Gitlab::ErrorTracking).to receive(:track_exception).and_call_original + + get :search_merge_requests, params: { author_id: user.id } + end + end + + context 'when an ActiveRecord::QueryCanceled is not raised' do + it 'does not set :search_timeout_occurred' do + get :search_merge_requests, params: { author_id: user.id } + + expect(response).to have_gitlab_http_status(:ok) + expect(assigns(:search_timeout_occurred)).to eq(nil) + end + + context 'rendering views' do + render_views + + it 'displays MR counts in nav' do + get :search_merge_requests, params: { author_id: user.id } + + expect(response.body).to have_content('Open 0 Merged 0 Closed 0 All 0') + expect(response.body).not_to have_content('Open Merged Closed All') + end + end + end + end end describe "GET activity as JSON" do diff --git a/spec/requests/dashboard_controller_spec.rb b/spec/requests/dashboard_controller_spec.rb index d7f01b8a7ab7b4ffbb7b460fa13cee5ed44afb81..8377caa58d68580b441eb570653bad8c6294af62 100644 --- a/spec/requests/dashboard_controller_spec.rb +++ b/spec/requests/dashboard_controller_spec.rb @@ -40,4 +40,18 @@ def request end end end + + context 'search merge requests dashboard' do + it_behaves_like 'rate limited endpoint', rate_limit_key: :search_rate_limit do + let_it_be(:current_user) { create(:user) } + + before do + sign_in current_user + end + + def request + get merge_requests_search_dashboard_path, params: { scope: 'all', search: 'test' } + end + end + end end