Commit 4a60baf9 authored by Sean McGivern's avatar Sean McGivern

Merge branch '50352-sort-save' into 'master'

Save sorting preference for Issues/MRs in BE

Closes #50352

See merge request gitlab-org/gitlab-ce!24198
parents a2468878 49c74068
...@@ -129,13 +129,13 @@ module IssuableCollections ...@@ -129,13 +129,13 @@ module IssuableCollections
return sort_param if Gitlab::Database.read_only? return sort_param if Gitlab::Database.read_only?
if user_preference[issuable_sorting_field] != sort_param if user_preference[issuable_sorting_field] != sort_param
user_preference.update_attribute(issuable_sorting_field, sort_param) user_preference.update(issuable_sorting_field => sort_param)
end end
sort_param sort_param
end end
# Implement default_sorting_field method on controllers # Implement issuable_sorting_field method on controllers
# to choose which column to store the sorting parameter. # to choose which column to store the sorting parameter.
def issuable_sorting_field def issuable_sorting_field
nil nil
......
# frozen_string_literal: true # frozen_string_literal: true
module IssuesAction module IssuableCollectionsAction
extend ActiveSupport::Concern extend ActiveSupport::Concern
include IssuableCollections include IssuableCollections
include IssuesCalendar include IssuesCalendar
...@@ -18,6 +18,12 @@ module IssuesAction ...@@ -18,6 +18,12 @@ module IssuesAction
format.atom { render layout: 'xml.atom' } format.atom { render layout: 'xml.atom' }
end end
end end
def merge_requests
@merge_requests = issuables_collection.page(params[:page])
@issuable_meta_data = issuable_meta_data(@merge_requests, collection_type)
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables # rubocop:enable Gitlab/ModuleWithInstanceVariables
def issues_calendar def issues_calendar
...@@ -26,8 +32,29 @@ module IssuesAction ...@@ -26,8 +32,29 @@ module IssuesAction
private private
def issuable_sorting_field
case action_name
when 'issues'
Issue::SORTING_PREFERENCE_FIELD
when 'merge_requests'
MergeRequest::SORTING_PREFERENCE_FIELD
else
nil
end
end
def finder_type def finder_type
(super if defined?(super)) || case action_name
(IssuesFinder if %w(issues issues_calendar).include?(action_name)) when 'issues', 'issues_calendar'
IssuesFinder
when 'merge_requests'
MergeRequestsFinder
else
nil
end
end
def finder_options
super.merge(non_archived: true)
end end
end end
# frozen_string_literal: true
module MergeRequestsAction
extend ActiveSupport::Concern
include IssuableCollections
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def merge_requests
@merge_requests = issuables_collection.page(params[:page])
@issuable_meta_data = issuable_meta_data(@merge_requests, collection_type)
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
private
def finder_type
(super if defined?(super)) ||
(MergeRequestsFinder if action_name == 'merge_requests')
end
def finder_options
super.merge(non_archived: true)
end
end
# frozen_string_literal: true # frozen_string_literal: true
class DashboardController < Dashboard::ApplicationController class DashboardController < Dashboard::ApplicationController
include IssuesAction include IssuableCollectionsAction
include MergeRequestsAction
prepend_before_action(only: [:issues]) { authenticate_sessionless_user!(:rss) } prepend_before_action(only: [:issues]) { authenticate_sessionless_user!(:rss) }
prepend_before_action(only: [:issues_calendar]) { authenticate_sessionless_user!(:ics) } prepend_before_action(only: [:issues_calendar]) { authenticate_sessionless_user!(:ics) }
......
...@@ -2,8 +2,7 @@ ...@@ -2,8 +2,7 @@
class GroupsController < Groups::ApplicationController class GroupsController < Groups::ApplicationController
include API::Helpers::RelatedResourcesHelpers include API::Helpers::RelatedResourcesHelpers
include IssuesAction include IssuableCollectionsAction
include MergeRequestsAction
include ParamsBackwardCompatibility include ParamsBackwardCompatibility
include PreviewMarkdown include PreviewMarkdown
......
...@@ -191,6 +191,10 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -191,6 +191,10 @@ class Projects::IssuesController < Projects::ApplicationController
protected protected
def issuable_sorting_field
Issue::SORTING_PREFERENCE_FIELD
end
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def issue def issue
return @issue if defined?(@issue) return @issue if defined?(@issue)
......
...@@ -230,6 +230,10 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo ...@@ -230,6 +230,10 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
alias_method :issuable, :merge_request alias_method :issuable, :merge_request
alias_method :awardable, :merge_request alias_method :awardable, :merge_request
def issuable_sorting_field
MergeRequest::SORTING_PREFERENCE_FIELD
end
def merge_params def merge_params
params.permit(merge_params_attributes) params.permit(merge_params_attributes)
end end
......
...@@ -26,6 +26,8 @@ class Issue < ActiveRecord::Base ...@@ -26,6 +26,8 @@ class Issue < ActiveRecord::Base
DueThisMonth = DueDateStruct.new('Due This Month', 'month').freeze DueThisMonth = DueDateStruct.new('Due This Month', 'month').freeze
DueNextMonthAndPreviousTwoWeeks = DueDateStruct.new('Due Next Month And Previous Two Weeks', 'next_month_and_previous_two_weeks').freeze DueNextMonthAndPreviousTwoWeeks = DueDateStruct.new('Due Next Month And Previous Two Weeks', 'next_month_and_previous_two_weeks').freeze
SORTING_PREFERENCE_FIELD = :issues_sort
belongs_to :project belongs_to :project
belongs_to :moved_to, class_name: 'Issue' belongs_to :moved_to, class_name: 'Issue'
belongs_to :closed_by, class_name: 'User' belongs_to :closed_by, class_name: 'User'
......
...@@ -21,6 +21,8 @@ class MergeRequest < ActiveRecord::Base ...@@ -21,6 +21,8 @@ class MergeRequest < ActiveRecord::Base
self.reactive_cache_refresh_interval = 10.minutes self.reactive_cache_refresh_interval = 10.minutes
self.reactive_cache_lifetime = 10.minutes self.reactive_cache_lifetime = 10.minutes
SORTING_PREFERENCE_FIELD = :merge_requests_sort
ignore_column :locked_at, ignore_column :locked_at,
:ref_fetched, :ref_fetched,
:deleted_at :deleted_at
......
---
title: Save issues/merge request sorting options to backend
merge_request: 24198
author:
type: added
# frozen_string_literal: true
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddSortingFieldsToUserPreference < ActiveRecord::Migration[5.0]
include Gitlab::Database::MigrationHelpers
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
def up
add_column :user_preferences, :issues_sort, :string
add_column :user_preferences, :merge_requests_sort, :string
end
def down
remove_column :user_preferences, :issues_sort
remove_column :user_preferences, :merge_requests_sort
end
end
...@@ -2146,6 +2146,8 @@ ActiveRecord::Schema.define(version: 20190124200344) do ...@@ -2146,6 +2146,8 @@ ActiveRecord::Schema.define(version: 20190124200344) do
t.integer "merge_request_notes_filter", limit: 2, default: 0, null: false t.integer "merge_request_notes_filter", limit: 2, default: 0, null: false
t.datetime_with_timezone "created_at", null: false t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false t.datetime_with_timezone "updated_at", null: false
t.string "issues_sort"
t.string "merge_requests_sort"
t.index ["user_id"], name: "index_user_preferences_on_user_id", unique: true, using: :btree t.index ["user_id"], name: "index_user_preferences_on_user_id", unique: true, using: :btree
end end
......
...@@ -17,10 +17,55 @@ describe IssuableCollections do ...@@ -17,10 +17,55 @@ describe IssuableCollections do
controller = klass.new controller = klass.new
allow(controller).to receive(:params).and_return(ActionController::Parameters.new(params)) allow(controller).to receive(:params).and_return(ActionController::Parameters.new(params))
allow(controller).to receive(:current_user).and_return(user)
controller controller
end end
describe '#set_sort_order_from_user_preference' do
describe 'when sort param given' do
let(:params) { { sort: 'updated_desc' } }
context 'when issuable_sorting_field is defined' do
before do
controller.class.define_method(:issuable_sorting_field) { :issues_sort}
end
it 'sets user_preference with the right value' do
controller.send(:set_sort_order_from_user_preference)
expect(user.user_preference.reload.issues_sort).to eq('updated_desc')
end
end
context 'when no issuable_sorting_field is defined on the controller' do
it 'does not touch user_preference' do
allow(user).to receive(:user_preference)
controller.send(:set_sort_order_from_user_preference)
expect(user).not_to have_received(:user_preference)
end
end
end
context 'when a user sorting preference exists' do
let(:params) { {} }
before do
controller.class.define_method(:issuable_sorting_field) { :issues_sort }
end
it 'returns the set preference' do
user.user_preference.update(issues_sort: 'updated_asc')
sort_preference = controller.send(:set_sort_order_from_user_preference)
expect(sort_preference).to eq('updated_asc')
end
end
end
describe '#set_set_order_from_cookie' do describe '#set_set_order_from_cookie' do
describe 'when sort param given' do describe 'when sort param given' do
let(:cookies) { {} } let(:cookies) { {} }
......
...@@ -42,7 +42,9 @@ describe Projects::IssuesController do ...@@ -42,7 +42,9 @@ describe Projects::IssuesController do
it_behaves_like "issuables list meta-data", :issue it_behaves_like "issuables list meta-data", :issue
it_behaves_like 'set sort order from user preference' it_behaves_like 'set sort order from user preference' do
let(:sorting_param) { 'updated_asc' }
end
it "returns index" do it "returns index" do
get :index, params: { namespace_id: project.namespace, project_id: project } get :index, params: { namespace_id: project.namespace, project_id: project }
......
...@@ -153,7 +153,9 @@ describe Projects::MergeRequestsController do ...@@ -153,7 +153,9 @@ describe Projects::MergeRequestsController do
it_behaves_like "issuables list meta-data", :merge_request it_behaves_like "issuables list meta-data", :merge_request
it_behaves_like 'set sort order from user preference' it_behaves_like 'set sort order from user preference' do
let(:sorting_param) { 'updated_asc' }
end
context 'when page param' do context 'when page param' do
let(:last_page) { project.merge_requests.page().total_pages } let(:last_page) { project.merge_requests.page().total_pages }
......
...@@ -3,9 +3,10 @@ ...@@ -3,9 +3,10 @@
require 'spec_helper' require 'spec_helper'
describe UserPreference do describe UserPreference do
let(:user_preference) { create(:user_preference) }
describe '#set_notes_filter' do describe '#set_notes_filter' do
let(:issuable) { build_stubbed(:issue) } let(:issuable) { build_stubbed(:issue) }
let(:user_preference) { create(:user_preference) }
shared_examples 'setting system notes' do shared_examples 'setting system notes' do
it 'returns updated discussion filter' do it 'returns updated discussion filter' do
...@@ -50,4 +51,26 @@ describe UserPreference do ...@@ -50,4 +51,26 @@ describe UserPreference do
end end
end end
end end
describe 'sort_by preferences' do
shared_examples_for 'a sort_by preference' do
it 'allows nil sort fields' do
user_preference.update(attribute => nil)
expect(user_preference).to be_valid
end
end
context 'merge_requests_sort attribute' do
let(:attribute) { :merge_requests_sort }
it_behaves_like 'a sort_by preference'
end
context 'issues_sort attribute' do
let(:attribute) { :issues_sort }
it_behaves_like 'a sort_by preference'
end
end
end end
...@@ -2,18 +2,12 @@ shared_examples 'set sort order from user preference' do ...@@ -2,18 +2,12 @@ shared_examples 'set sort order from user preference' do
describe '#set_sort_order_from_user_preference' do describe '#set_sort_order_from_user_preference' do
# There is no issuable_sorting_field defined in any CE controllers yet, # There is no issuable_sorting_field defined in any CE controllers yet,
# however any other field present in user_preferences table can be used for testing. # however any other field present in user_preferences table can be used for testing.
let(:sorting_field) { :issue_notes_filter }
let(:sorting_param) { 'any' }
before do
allow(controller).to receive(:issuable_sorting_field).and_return(sorting_field)
end
context 'when database is in read-only mode' do context 'when database is in read-only mode' do
it 'it does not update user preference' do it 'it does not update user preference' do
allow(Gitlab::Database).to receive(:read_only?).and_return(true) allow(Gitlab::Database).to receive(:read_only?).and_return(true)
expect_any_instance_of(UserPreference).not_to receive(:update_attribute).with(sorting_field, sorting_param) expect_any_instance_of(UserPreference).not_to receive(:update).with({ controller.send(:issuable_sorting_field) => sorting_param })
get :index, params: { namespace_id: project.namespace, project_id: project, sort: sorting_param } get :index, params: { namespace_id: project.namespace, project_id: project, sort: sorting_param }
end end
...@@ -23,7 +17,7 @@ shared_examples 'set sort order from user preference' do ...@@ -23,7 +17,7 @@ shared_examples 'set sort order from user preference' do
it 'updates user preference' do it 'updates user preference' do
allow(Gitlab::Database).to receive(:read_only?).and_return(false) allow(Gitlab::Database).to receive(:read_only?).and_return(false)
expect_any_instance_of(UserPreference).to receive(:update_attribute).with(sorting_field, sorting_param) expect_any_instance_of(UserPreference).to receive(:update).with({ controller.send(:issuable_sorting_field) => sorting_param })
get :index, params: { namespace_id: project.namespace, project_id: project, sort: sorting_param } get :index, params: { namespace_id: project.namespace, project_id: project, sort: sorting_param }
end end
......
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