Skip to content
Snippets Groups Projects
Commit 3183092c authored by Rémy Coutable's avatar Rémy Coutable
Browse files

Add pagination headers to already paginated API resources

parent 9f8c38bd
No related branches found
No related tags found
1 merge request!2426Add pagination headers to already paginated API resources
Pipeline #
Please view this file on the master branch, on stable branches it's out of date. Please view this file on the master branch, on stable branches it's out of date.
   
v 8.4.0 (unreleased) v 8.4.0 (unreleased)
- Add pagination headers to already paginated API resources
- Autocomplete data is now always loaded, instead of when focusing a comment text area (Yorick Peterse) - Autocomplete data is now always loaded, instead of when focusing a comment text area (Yorick Peterse)
- Improved performance of finding issues for an entire group (Yorick Peterse) - Improved performance of finding issues for an entire group (Yorick Peterse)
- Added custom application performance measuring system powered by InfluxDB (Yorick Peterse) - Added custom application performance measuring system powered by InfluxDB (Yorick Peterse)
Loading
Loading
Loading
@@ -97,11 +97,9 @@ module API
Loading
@@ -97,11 +97,9 @@ module API
end end
   
def paginate(relation) def paginate(relation)
per_page = params[:per_page].to_i relation.page(params[:page]).per(params[:per_page].to_i).tap do |data|
paginated = relation.page(params[:page]).per(per_page) add_pagination_headers(data)
add_pagination_headers(paginated, per_page) end
paginated
end end
   
def authenticate! def authenticate!
Loading
@@ -327,16 +325,26 @@ module API
Loading
@@ -327,16 +325,26 @@ module API
   
private private
   
def add_pagination_headers(paginated, per_page) def add_pagination_headers(paginated_data)
header 'X-Total', paginated_data.total_count.to_s
header 'X-Total-Pages', paginated_data.total_pages.to_s
header 'X-Per-Page', paginated_data.limit_value.to_s
header 'X-Page', paginated_data.current_page.to_s
header 'X-Next-Page', paginated_data.next_page.to_s
header 'X-Prev-Page', paginated_data.prev_page.to_s
header 'Link', pagination_links(paginated_data)
end
def pagination_links(paginated_data)
request_url = request.url.split('?').first request_url = request.url.split('?').first
   
links = [] links = []
links << %(<#{request_url}?page=#{paginated.current_page - 1}&per_page=#{per_page}>; rel="prev") unless paginated.first_page? links << %(<#{request_url}?page=#{paginated_data.current_page - 1}&per_page=#{paginated_data.limit_value}>; rel="prev") unless paginated_data.first_page?
links << %(<#{request_url}?page=#{paginated.current_page + 1}&per_page=#{per_page}>; rel="next") unless paginated.last_page? links << %(<#{request_url}?page=#{paginated_data.current_page + 1}&per_page=#{paginated_data.limit_value}>; rel="next") unless paginated_data.last_page?
links << %(<#{request_url}?page=1&per_page=#{per_page}>; rel="first") links << %(<#{request_url}?page=1&per_page=#{paginated_data.limit_value}>; rel="first")
links << %(<#{request_url}?page=#{paginated.total_pages}&per_page=#{per_page}>; rel="last") links << %(<#{request_url}?page=#{paginated_data.total_pages}&per_page=#{paginated_data.limit_value}>; rel="last")
   
header 'Link', links.join(', ') links.join(', ')
end end
   
def abilities def abilities
Loading
Loading
Loading
@@ -22,17 +22,11 @@ module API
Loading
@@ -22,17 +22,11 @@ module API
@noteable = user_project.send(:"#{noteables_str}").find(params[:"#{noteable_id_str}"]) @noteable = user_project.send(:"#{noteables_str}").find(params[:"#{noteable_id_str}"])
   
# We exclude notes that are cross-references and that cannot be viewed # We exclude notes that are cross-references and that cannot be viewed
# by the current user. By doing this exclusion at this level and not # by the current user.
# at the DB query level (which we cannot in that case), the current
# page can have less elements than :per_page even if
# there's more than one page.
notes = notes =
# paginate() only works with a relation. This could lead to a @noteable.notes.
# mismatch between the pagination headers info and the actual notes
# array returned, but this is really a edge-case.
paginate(@noteable.notes).
reject { |n| n.cross_reference_not_visible_for?(current_user) } reject { |n| n.cross_reference_not_visible_for?(current_user) }
present notes, with: Entities::Note present paginate(Kaminari.paginate_array(notes)), with: Entities::Note
end end
   
# Get a single +noteable+ note # Get a single +noteable+ note
Loading
Loading
require 'spec_helper' require 'spec_helper'
   
describe API::API, api: true do describe API::CommitStatus, api: true do
include ApiHelpers include ApiHelpers
let(:user) { create(:user) } let(:user) { create(:user) }
let(:user2) { create(:user) } let(:user2) { create(:user) }
Loading
@@ -12,6 +12,10 @@ describe API::API, api: true do
Loading
@@ -12,6 +12,10 @@ describe API::API, api: true do
let(:commit_status) { create(:commit_status, commit: ci_commit) } let(:commit_status) { create(:commit_status, commit: ci_commit) }
   
describe "GET /projects/:id/repository/commits/:sha/statuses" do describe "GET /projects/:id/repository/commits/:sha/statuses" do
it_behaves_like 'a paginated resources' do
let(:request) { get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses", user) }
end
context "reporter user" do context "reporter user" do
let(:statuses_id) { json_response.map { |status| status['id'] } } let(:statuses_id) { json_response.map { |status| status['id'] } }
   
Loading
Loading
Loading
@@ -32,6 +32,10 @@ describe API::API, api: true do
Loading
@@ -32,6 +32,10 @@ describe API::API, api: true do
before { project.team << [user, :reporter] } before { project.team << [user, :reporter] }
   
describe "GET /projects/:id/noteable/:noteable_id/notes" do describe "GET /projects/:id/noteable/:noteable_id/notes" do
it_behaves_like 'a paginated resources' do
let(:request) { get api("/projects/#{project.id}/issues/#{issue.id}/notes", user) }
end
context "when noteable is an Issue" do context "when noteable is an Issue" do
it "should return an array of issue notes" do it "should return an array of issue notes" do
get api("/projects/#{project.id}/issues/#{issue.id}/notes", user) get api("/projects/#{project.id}/issues/#{issue.id}/notes", user)
Loading
Loading
# Specs for paginated resources.
#
# Requires an API request:
# let(:request) { get api("/projects/#{project.id}/repository/branches", user) }
shared_examples 'a paginated resources' do
before do
# Fires the request
request
end
it 'has pagination headers' do
expect(response.headers).to include('X-Total')
expect(response.headers).to include('X-Total-Pages')
expect(response.headers).to include('X-Per-Page')
expect(response.headers).to include('X-Page')
expect(response.headers).to include('X-Next-Page')
expect(response.headers).to include('X-Prev-Page')
expect(response.headers).to include('Link')
end
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment