Commit f1926b32 authored by Markus Koller's avatar Markus Koller

Add controller concern for paginated collections

We had similar code in a few places to redirect to the last page if
the given page number is out of range. This unifies the handling in a
new controller concern and adds usage of it in all snippet listings.
parent 60755fbc
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
module IssuableCollections module IssuableCollections
extend ActiveSupport::Concern extend ActiveSupport::Concern
include PaginatedCollection
include SortingHelper include SortingHelper
include SortingPreference include SortingPreference
include Gitlab::IssuableMetadata include Gitlab::IssuableMetadata
...@@ -17,8 +18,11 @@ module IssuableCollections ...@@ -17,8 +18,11 @@ module IssuableCollections
def set_issuables_index def set_issuables_index
@issuables = issuables_collection @issuables = issuables_collection
unless pagination_disabled?
set_pagination set_pagination
return if redirect_out_of_range(@total_pages)
return if redirect_out_of_range(@issuables, @total_pages)
end
if params[:label_name].present? && @project if params[:label_name].present? && @project
labels_params = { project_id: @project.id, title: params[:label_name] } labels_params = { project_id: @project.id, title: params[:label_name] }
...@@ -38,12 +42,10 @@ module IssuableCollections ...@@ -38,12 +42,10 @@ module IssuableCollections
end end
def set_pagination def set_pagination
return if pagination_disabled?
@issuables = @issuables.page(params[:page]) @issuables = @issuables.page(params[:page])
@issuables = per_page_for_relative_position if params[:sort] == 'relative_position' @issuables = per_page_for_relative_position if params[:sort] == 'relative_position'
@issuable_meta_data = issuable_meta_data(@issuables, collection_type, current_user) @issuable_meta_data = issuable_meta_data(@issuables, collection_type, current_user)
@total_pages = issuable_page_count @total_pages = issuable_page_count(@issuables)
end end
# rubocop:enable Gitlab/ModuleWithInstanceVariables # rubocop:enable Gitlab/ModuleWithInstanceVariables
...@@ -57,20 +59,8 @@ module IssuableCollections ...@@ -57,20 +59,8 @@ module IssuableCollections
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
def redirect_out_of_range(total_pages) def issuable_page_count(relation)
return false if total_pages.nil? || total_pages.zero? page_count_for_relation(relation, finder.row_count)
out_of_range = @issuables.current_page > total_pages # rubocop:disable Gitlab/ModuleWithInstanceVariables
if out_of_range
redirect_to(url_for(safe_params.merge(page: total_pages, only_path: true)))
end
out_of_range
end
def issuable_page_count
page_count_for_relation(@issuables, finder.row_count) # rubocop:disable Gitlab/ModuleWithInstanceVariables
end end
def page_count_for_relation(relation, row_count) def page_count_for_relation(relation, row_count)
......
# frozen_string_literal: true
module PaginatedCollection
extend ActiveSupport::Concern
private
def redirect_out_of_range(collection, total_pages = collection.total_pages)
return false if total_pages.zero?
out_of_range = collection.current_page > total_pages
if out_of_range
redirect_to(url_for(safe_params.merge(page: total_pages, only_path: true)))
end
out_of_range
end
end
# frozen_string_literal: true # frozen_string_literal: true
class Dashboard::SnippetsController < Dashboard::ApplicationController class Dashboard::SnippetsController < Dashboard::ApplicationController
include PaginatedCollection
include Gitlab::NoteableMetadata include Gitlab::NoteableMetadata
skip_cross_project_access_check :index skip_cross_project_access_check :index
...@@ -11,6 +12,8 @@ class Dashboard::SnippetsController < Dashboard::ApplicationController ...@@ -11,6 +12,8 @@ class Dashboard::SnippetsController < Dashboard::ApplicationController
.page(params[:page]) .page(params[:page])
.inc_author .inc_author
return if redirect_out_of_range(@snippets)
@noteable_meta_data = noteable_meta_data(@snippets, 'Snippet') @noteable_meta_data = noteable_meta_data(@snippets, 'Snippet')
end end
end end
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
class Dashboard::TodosController < Dashboard::ApplicationController class Dashboard::TodosController < Dashboard::ApplicationController
include ActionView::Helpers::NumberHelper include ActionView::Helpers::NumberHelper
include PaginatedCollection
before_action :authorize_read_project!, only: :index before_action :authorize_read_project!, only: :index
before_action :authorize_read_group!, only: :index before_action :authorize_read_group!, only: :index
...@@ -12,7 +13,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController ...@@ -12,7 +13,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController
@todos = @todos.page(params[:page]) @todos = @todos.page(params[:page])
@todos = @todos.with_entity_associations @todos = @todos.with_entity_associations
return if redirect_out_of_range(@todos) return if redirect_out_of_range(@todos, todos_page_count(@todos))
end end
def destroy def destroy
...@@ -82,28 +83,15 @@ class Dashboard::TodosController < Dashboard::ApplicationController ...@@ -82,28 +83,15 @@ class Dashboard::TodosController < Dashboard::ApplicationController
} }
end end
def todo_params def todos_page_count(todos)
params.permit(:action_id, :author_id, :project_id, :type, :sort, :state, :group_id) if todo_params.except(:sort, :page).empty? # rubocop: disable CodeReuse/ActiveRecord
end
# rubocop: disable CodeReuse/ActiveRecord
def redirect_out_of_range(todos)
total_pages =
if todo_params.except(:sort, :page).empty?
(current_user.todos_pending_count.to_f / todos.limit_value).ceil (current_user.todos_pending_count.to_f / todos.limit_value).ceil
else else
todos.total_pages todos.total_pages
end end
return false if total_pages.zero?
out_of_range = todos.current_page > total_pages
if out_of_range
redirect_to url_for(safe_params.merge(page: total_pages, only_path: true))
end end
out_of_range def todo_params
params.permit(:action_id, :author_id, :project_id, :type, :sort, :state, :group_id)
end end
# rubocop: enable CodeReuse/ActiveRecord
end end
# frozen_string_literal: true # frozen_string_literal: true
class Explore::SnippetsController < Explore::ApplicationController class Explore::SnippetsController < Explore::ApplicationController
include PaginatedCollection
include Gitlab::NoteableMetadata include Gitlab::NoteableMetadata
def index def index
...@@ -9,6 +10,8 @@ class Explore::SnippetsController < Explore::ApplicationController ...@@ -9,6 +10,8 @@ class Explore::SnippetsController < Explore::ApplicationController
.page(params[:page]) .page(params[:page])
.inc_author .inc_author
return if redirect_out_of_range(@snippets)
@noteable_meta_data = noteable_meta_data(@snippets, 'Snippet') @noteable_meta_data = noteable_meta_data(@snippets, 'Snippet')
end end
end end
...@@ -6,6 +6,7 @@ class Projects::SnippetsController < Projects::ApplicationController ...@@ -6,6 +6,7 @@ class Projects::SnippetsController < Projects::ApplicationController
include SpammableActions include SpammableActions
include SnippetsActions include SnippetsActions
include RendersBlob include RendersBlob
include PaginatedCollection
include Gitlab::NoteableMetadata include Gitlab::NoteableMetadata
skip_before_action :verify_authenticity_token, skip_before_action :verify_authenticity_token,
...@@ -34,9 +35,7 @@ class Projects::SnippetsController < Projects::ApplicationController ...@@ -34,9 +35,7 @@ class Projects::SnippetsController < Projects::ApplicationController
.page(params[:page]) .page(params[:page])
.inc_author .inc_author
if @snippets.out_of_range? && @snippets.total_pages != 0 return if redirect_out_of_range(@snippets)
return redirect_to project_snippets_path(@project, page: @snippets.total_pages)
end
@noteable_meta_data = noteable_meta_data(@snippets, 'Snippet') @noteable_meta_data = noteable_meta_data(@snippets, 'Snippet')
end end
......
...@@ -7,6 +7,7 @@ class SnippetsController < ApplicationController ...@@ -7,6 +7,7 @@ class SnippetsController < ApplicationController
include SnippetsActions include SnippetsActions
include RendersBlob include RendersBlob
include PreviewMarkdown include PreviewMarkdown
include PaginatedCollection
include Gitlab::NoteableMetadata include Gitlab::NoteableMetadata
skip_before_action :verify_authenticity_token, skip_before_action :verify_authenticity_token,
...@@ -37,6 +38,8 @@ class SnippetsController < ApplicationController ...@@ -37,6 +38,8 @@ class SnippetsController < ApplicationController
.page(params[:page]) .page(params[:page])
.inc_author .inc_author
return if redirect_out_of_range(@snippets)
@noteable_meta_data = noteable_meta_data(@snippets, 'Snippet') @noteable_meta_data = noteable_meta_data(@snippets, 'Snippet')
render 'index' render 'index'
......
# frozen_string_literal: true
require 'spec_helper'
describe Dashboard::SnippetsController do
let(:user) { create(:user) }
before do
sign_in(user)
end
describe 'GET #index' do
it_behaves_like 'paginated collection' do
let(:collection) { Snippet.all }
before do
create(:personal_snippet, :public, author: user)
end
end
end
end
...@@ -82,35 +82,15 @@ describe Dashboard::TodosController do ...@@ -82,35 +82,15 @@ describe Dashboard::TodosController do
end end
end end
context 'when using pagination' do it_behaves_like 'paginated collection' do
let(:last_page) { user.todos.page.total_pages }
let!(:issues) { create_list(:issue, 3, project: project, assignees: [user]) } let!(:issues) { create_list(:issue, 3, project: project, assignees: [user]) }
let(:collection) { user.todos }
before do before do
issues.each { |issue| todo_service.new_issue(issue, user) } issues.each { |issue| todo_service.new_issue(issue, user) }
allow(Kaminari.config).to receive(:default_per_page).and_return(2) allow(Kaminari.config).to receive(:default_per_page).and_return(2)
end end
it 'redirects to last_page if page number is larger than number of pages' do
get :index, params: { page: (last_page + 1).to_param }
expect(response).to redirect_to(dashboard_todos_path(page: last_page))
end
it 'goes to the correct page' do
get :index, params: { page: last_page }
expect(assigns(:todos).current_page).to eq(last_page)
expect(response).to have_gitlab_http_status(200)
end
it 'does not redirect to external sites when provided a host field' do
external_host = "www.example.com"
get :index, params: { page: (last_page + 1).to_param, host: external_host }
expect(response).to redirect_to(dashboard_todos_path(page: last_page))
end
context 'when providing no filters' do context 'when providing no filters' do
it 'does not perform a query to get the page count, but gets that from the user' do it 'does not perform a query to get the page count, but gets that from the user' do
allow(controller).to receive(:current_user).and_return(user) allow(controller).to receive(:current_user).and_return(user)
......
# frozen_string_literal: true
require 'spec_helper'
describe Explore::SnippetsController do
describe 'GET #index' do
it_behaves_like 'paginated collection' do
let(:collection) { Snippet.all }
before do
create(:personal_snippet, :public)
end
end
end
end
...@@ -71,61 +71,27 @@ describe Projects::IssuesController do ...@@ -71,61 +71,27 @@ describe Projects::IssuesController do
end end
end end
context 'with page param' do it_behaves_like 'paginated collection' do
let(:last_page) { project.issues.page.total_pages }
let!(:issue_list) { create_list(:issue, 2, project: project) } let!(:issue_list) { create_list(:issue, 2, project: project) }
let(:collection) { project.issues }
before do let(:params) do
sign_in(user) {
project.add_developer(user)
allow(Kaminari.config).to receive(:default_per_page).and_return(1)
end
it 'redirects to last_page if page number is larger than number of pages' do
get :index,
params: {
namespace_id: project.namespace.to_param,
project_id: project,
page: (last_page + 1).to_param
}
expect(response).to redirect_to(namespace_project_issues_path(page: last_page, state: controller.params[:state], scope: controller.params[:scope]))
end
it 'redirects to specified page' do
get :index,
params: {
namespace_id: project.namespace.to_param, namespace_id: project.namespace.to_param,
project_id: project, project_id: project,
page: last_page.to_param state: 'opened'
} }
expect(assigns(:issues).current_page).to eq(last_page)
expect(response).to have_gitlab_http_status(200)
end end
it 'does not redirect to external sites when provided a host field' do before do
external_host = "www.example.com" sign_in(user)
get :index, project.add_developer(user)
params: { allow(Kaminari.config).to receive(:default_per_page).and_return(1)
namespace_id: project.namespace.to_param,
project_id: project,
page: (last_page + 1).to_param,
host: external_host
}
expect(response).to redirect_to(namespace_project_issues_path(page: last_page, state: controller.params[:state], scope: controller.params[:scope]))
end end
it 'does not use pagination if disabled' do it 'does not use pagination if disabled' do
allow(controller).to receive(:pagination_disabled?).and_return(true) allow(controller).to receive(:pagination_disabled?).and_return(true)
get :index, get :index, params: params.merge(page: last_page + 1)
params: {
namespace_id: project.namespace.to_param,
project_id: project,
page: (last_page + 1).to_param
}
expect(response).to have_gitlab_http_status(200) expect(response).to have_gitlab_http_status(200)
expect(assigns(:issues).size).to eq(2) expect(assigns(:issues).size).to eq(2)
......
...@@ -13,31 +13,17 @@ describe Projects::SnippetsController do ...@@ -13,31 +13,17 @@ describe Projects::SnippetsController do
end end
describe 'GET #index' do describe 'GET #index' do
context 'when page param' do it_behaves_like 'paginated collection' do
let(:last_page) { project.snippets.page.total_pages } let(:collection) { project.snippets }
let!(:project_snippet) { create(:project_snippet, :public, project: project, author: user) } let(:params) do
{
it 'redirects to last_page if page number is larger than number of pages' do
get :index,
params: {
namespace_id: project.namespace, namespace_id: project.namespace,
project_id: project, project_id: project
page: (last_page + 1).to_param
} }
expect(response).to redirect_to(namespace_project_snippets_path(page: last_page))
end end
it 'redirects to specified page' do before do
get :index, create(:project_snippet, :public, project: project, author: user)
params: {
namespace_id: project.namespace,
project_id: project,
page: last_page.to_param
}
expect(assigns(:snippets).current_page).to eq(last_page)
expect(response).to have_gitlab_http_status(200)
end end
end end
......
...@@ -9,6 +9,15 @@ describe SnippetsController do ...@@ -9,6 +9,15 @@ describe SnippetsController do
let(:user) { create(:user) } let(:user) { create(:user) }
context 'when username parameter is present' do context 'when username parameter is present' do
it_behaves_like 'paginated collection' do
let(:collection) { Snippet.all }
let(:params) { { username: user.username } }
before do
create(:personal_snippet, :public, author: user)
end
end
it 'renders snippets of a user when username is present' do it 'renders snippets of a user when username is present' do
get :index, params: { username: user.username } get :index, params: { username: user.username }
......
# frozen_string_literal: true
require 'spec_helper'
shared_examples 'paginated collection' do
let(:collection) { nil }
let(:last_page) { collection.page.total_pages }
let(:action) { :index }
let(:params) { {} }
it 'renders a page number that is not ouf of range' do
get action, params: params.merge(page: last_page)
expect(response).to have_gitlab_http_status(200)
end
it 'redirects to last_page if page number is larger than number of pages' do
get action, params: params.merge(page: last_page + 1)
expect(response).to redirect_to(params.merge(page: last_page))
end
it 'does not redirect to external sites when provided a host field' do
external_host = 'www.example.com'
get action, params: params.merge(page: last_page + 1, host: external_host)
expect(response).to redirect_to(params.merge(page: last_page))
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