Save sorting preference for Issues/MRs in BE

In order to let users' sorting preferences transfer between devices, we
save the preference for issues and MRs (one preference for issues, one
for MRs) in the backend inside the UserPreference object
parent 958a819f
......@@ -129,13 +129,13 @@ module IssuableCollections
return sort_param if Gitlab::Database.read_only?
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
sort_param
end
# Implement default_sorting_field method on controllers
# Implement issuable_sorting_field method on controllers
# to choose which column to store the sorting parameter.
def issuable_sorting_field
nil
......
# frozen_string_literal: true
module IssuesAction
module IssuableCollectionsAction
extend ActiveSupport::Concern
include IssuableCollections
include IssuesCalendar
......@@ -18,6 +18,12 @@ module IssuesAction
format.atom { render layout: 'xml.atom' }
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
def issues_calendar
......@@ -26,8 +32,29 @@ module IssuesAction
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
(super if defined?(super)) ||
(IssuesFinder if %w(issues issues_calendar).include?(action_name))
case 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
# 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
class DashboardController < Dashboard::ApplicationController
include IssuesAction
include MergeRequestsAction
include IssuableCollectionsAction
prepend_before_action(only: [:issues]) { authenticate_sessionless_user!(:rss) }
prepend_before_action(only: [:issues_calendar]) { authenticate_sessionless_user!(:ics) }
......
......@@ -2,8 +2,7 @@
class GroupsController < Groups::ApplicationController
include API::Helpers::RelatedResourcesHelpers
include IssuesAction
include MergeRequestsAction
include IssuableCollectionsAction
include ParamsBackwardCompatibility
include PreviewMarkdown
......
......@@ -191,6 +191,10 @@ class Projects::IssuesController < Projects::ApplicationController
protected
def issuable_sorting_field
Issue::SORTING_PREFERENCE_FIELD
end
# rubocop: disable CodeReuse/ActiveRecord
def issue
return @issue if defined?(@issue)
......
......@@ -230,6 +230,10 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
alias_method :issuable, :merge_request
alias_method :awardable, :merge_request
def issuable_sorting_field
MergeRequest::SORTING_PREFERENCE_FIELD
end
def merge_params
params.permit(merge_params_attributes)
end
......
......@@ -26,6 +26,8 @@ class Issue < ActiveRecord::Base
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
SORTING_PREFERENCE_FIELD = :issues_sort
belongs_to :project
belongs_to :moved_to, class_name: 'Issue'
belongs_to :closed_by, class_name: 'User'
......
......@@ -21,6 +21,8 @@ class MergeRequest < ActiveRecord::Base
self.reactive_cache_refresh_interval = 10.minutes
self.reactive_cache_lifetime = 10.minutes
SORTING_PREFERENCE_FIELD = :merge_requests_sort
ignore_column :locked_at,
:ref_fetched,
: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
t.integer "merge_request_notes_filter", limit: 2, default: 0, null: false
t.datetime_with_timezone "created_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
end
......
......@@ -17,10 +17,55 @@ describe IssuableCollections do
controller = klass.new
allow(controller).to receive(:params).and_return(ActionController::Parameters.new(params))
allow(controller).to receive(:current_user).and_return(user)
controller
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 'when sort param given' do
let(:cookies) { {} }
......
......@@ -42,7 +42,9 @@ describe Projects::IssuesController do
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
get :index, params: { namespace_id: project.namespace, project_id: project }
......
......@@ -153,7 +153,9 @@ describe Projects::MergeRequestsController do
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
let(:last_page) { project.merge_requests.page().total_pages }
......
......@@ -3,9 +3,10 @@
require 'spec_helper'
describe UserPreference do
let(:user_preference) { create(:user_preference) }
describe '#set_notes_filter' do
let(:issuable) { build_stubbed(:issue) }
let(:user_preference) { create(:user_preference) }
shared_examples 'setting system notes' do
it 'returns updated discussion filter' do
......@@ -50,4 +51,26 @@ describe UserPreference do
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
......@@ -2,18 +2,12 @@ shared_examples '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,
# 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
it 'it does not update user preference' do
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 }
end
......@@ -23,7 +17,7 @@ shared_examples 'set sort order from user preference' do
it 'updates user preference' do
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 }
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