Add pagination headers to already paginated API resources
This commit is contained in:
parent
9f8c38bdac
commit
3183092ca9
6 changed files with 52 additions and 21 deletions
|
@ -1,6 +1,7 @@
|
|||
Please view this file on the master branch, on stable branches it's out of date.
|
||||
|
||||
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)
|
||||
- Improved performance of finding issues for an entire group (Yorick Peterse)
|
||||
- Added custom application performance measuring system powered by InfluxDB (Yorick Peterse)
|
||||
|
|
|
@ -97,11 +97,9 @@ module API
|
|||
end
|
||||
|
||||
def paginate(relation)
|
||||
per_page = params[:per_page].to_i
|
||||
paginated = relation.page(params[:page]).per(per_page)
|
||||
add_pagination_headers(paginated, per_page)
|
||||
|
||||
paginated
|
||||
relation.page(params[:page]).per(params[:per_page].to_i).tap do |data|
|
||||
add_pagination_headers(data)
|
||||
end
|
||||
end
|
||||
|
||||
def authenticate!
|
||||
|
@ -327,16 +325,26 @@ module API
|
|||
|
||||
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
|
||||
|
||||
links = []
|
||||
links << %(<#{request_url}?page=#{paginated.current_page - 1}&per_page=#{per_page}>; rel="prev") unless paginated.first_page?
|
||||
links << %(<#{request_url}?page=#{paginated.current_page + 1}&per_page=#{per_page}>; rel="next") unless paginated.last_page?
|
||||
links << %(<#{request_url}?page=1&per_page=#{per_page}>; rel="first")
|
||||
links << %(<#{request_url}?page=#{paginated.total_pages}&per_page=#{per_page}>; rel="last")
|
||||
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_data.current_page + 1}&per_page=#{paginated_data.limit_value}>; rel="next") unless paginated_data.last_page?
|
||||
links << %(<#{request_url}?page=1&per_page=#{paginated_data.limit_value}>; rel="first")
|
||||
links << %(<#{request_url}?page=#{paginated_data.total_pages}&per_page=#{paginated_data.limit_value}>; rel="last")
|
||||
|
||||
header 'Link', links.join(', ')
|
||||
links.join(', ')
|
||||
end
|
||||
|
||||
def abilities
|
||||
|
|
|
@ -22,17 +22,11 @@ module API
|
|||
@noteable = user_project.send(:"#{noteables_str}").find(params[:"#{noteable_id_str}"])
|
||||
|
||||
# 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
|
||||
# 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.
|
||||
# by the current user.
|
||||
notes =
|
||||
# paginate() only works with a relation. This could lead to a
|
||||
# mismatch between the pagination headers info and the actual notes
|
||||
# array returned, but this is really a edge-case.
|
||||
paginate(@noteable.notes).
|
||||
@noteable.notes.
|
||||
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
|
||||
|
||||
# Get a single +noteable+ note
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe API::API, api: true do
|
||||
describe API::CommitStatus, api: true do
|
||||
include ApiHelpers
|
||||
let(:user) { create(:user) }
|
||||
let(:user2) { create(:user) }
|
||||
|
@ -12,6 +12,10 @@ describe API::API, api: true do
|
|||
let(:commit_status) { create(:commit_status, commit: ci_commit) }
|
||||
|
||||
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
|
||||
let(:statuses_id) { json_response.map { |status| status['id'] } }
|
||||
|
||||
|
|
|
@ -32,6 +32,10 @@ describe API::API, api: true do
|
|||
before { project.team << [user, :reporter] }
|
||||
|
||||
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
|
||||
it "should return an array of issue notes" do
|
||||
get api("/projects/#{project.id}/issues/#{issue.id}/notes", user)
|
||||
|
|
20
spec/support/api/pagination_shared_examples.rb
Normal file
20
spec/support/api/pagination_shared_examples.rb
Normal file
|
@ -0,0 +1,20 @@
|
|||
# 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
|
Loading…
Reference in a new issue