Commit 01ea65e0 authored by Robert Schilling's avatar Robert Schilling

Paginate all endpoints that return an array

parent 28d8b865
---
title: 'API: Paginate all endpoints that return an array'
merge_request: 8606
author: Robert Schilling
...@@ -28,8 +28,8 @@ module API ...@@ -28,8 +28,8 @@ module API
end end
get endpoint do get endpoint do
if can_read_awardable? if can_read_awardable?
awards = paginate(awardable.award_emoji) awards = awardable.award_emoji
present awards, with: Entities::AwardEmoji present paginate(awards), with: Entities::AwardEmoji
else else
not_found!("Award Emoji") not_found!("Award Emoji")
end end
......
module API module API
# Boards API
class Boards < Grape::API class Boards < Grape::API
include PaginationParams
before { authenticate! } before { authenticate! }
params do params do
...@@ -11,9 +12,12 @@ module API ...@@ -11,9 +12,12 @@ module API
detail 'This feature was introduced in 8.13' detail 'This feature was introduced in 8.13'
success Entities::Board success Entities::Board
end end
params do
use :pagination
end
get ':id/boards' do get ':id/boards' do
authorize!(:read_board, user_project) authorize!(:read_board, user_project)
present user_project.boards, with: Entities::Board present paginate(user_project.boards), with: Entities::Board
end end
params do params do
...@@ -40,9 +44,12 @@ module API ...@@ -40,9 +44,12 @@ module API
detail 'Does not include `done` list. This feature was introduced in 8.13' detail 'Does not include `done` list. This feature was introduced in 8.13'
success Entities::List success Entities::List
end end
params do
use :pagination
end
get '/lists' do get '/lists' do
authorize!(:read_board, user_project) authorize!(:read_board, user_project)
present board_lists, with: Entities::List present paginate(board_lists), with: Entities::List
end end
desc 'Get a list of a project board' do desc 'Get a list of a project board' do
......
require 'mime/types' require 'mime/types'
module API module API
# Projects API
class Branches < Grape::API class Branches < Grape::API
include PaginationParams
before { authenticate! } before { authenticate! }
before { authorize! :download_code, user_project } before { authorize! :download_code, user_project }
...@@ -13,10 +14,13 @@ module API ...@@ -13,10 +14,13 @@ module API
desc 'Get a project repository branches' do desc 'Get a project repository branches' do
success Entities::RepoBranch success Entities::RepoBranch
end end
params do
use :pagination
end
get ":id/repository/branches" do get ":id/repository/branches" do
branches = user_project.repository.branches.sort_by(&:name) branches = ::Kaminari.paginate_array(user_project.repository.branches.sort_by(&:name))
present branches, with: Entities::RepoBranch, project: user_project present paginate(branches), with: Entities::RepoBranch, project: user_project
end end
desc 'Get a single branch' do desc 'Get a single branch' do
......
module API module API
class DeployKeys < Grape::API class DeployKeys < Grape::API
include PaginationParams
before { authenticate! } before { authenticate! }
desc 'Return all deploy keys'
params do
use :pagination
end
get "deploy_keys" do get "deploy_keys" do
authenticated_as_admin! authenticated_as_admin!
keys = DeployKey.all present paginate(DeployKey.all), with: Entities::SSHKey
present keys, with: Entities::SSHKey
end end
params do params do
...@@ -18,8 +23,11 @@ module API ...@@ -18,8 +23,11 @@ module API
desc "Get a specific project's deploy keys" do desc "Get a specific project's deploy keys" do
success Entities::SSHKey success Entities::SSHKey
end end
params do
use :pagination
end
get ":id/deploy_keys" do get ":id/deploy_keys" do
present user_project.deploy_keys, with: Entities::SSHKey present paginate(user_project.deploy_keys), with: Entities::SSHKey
end end
desc 'Get single deploy key' do desc 'Get single deploy key' do
......
module API module API
# Projects API
class Files < Grape::API class Files < Grape::API
helpers do helpers do
def commit_params(attrs) def commit_params(attrs)
......
...@@ -2,7 +2,7 @@ module API ...@@ -2,7 +2,7 @@ module API
module Helpers module Helpers
module Pagination module Pagination
def paginate(relation) def paginate(relation)
relation.page(params[:page]).per(params[:per_page].to_i).tap do |data| relation.page(params[:page]).per(params[:per_page]).tap do |data|
add_pagination_headers(data) add_pagination_headers(data)
end end
end end
......
module API module API
# Labels API
class Labels < Grape::API class Labels < Grape::API
include PaginationParams
before { authenticate! } before { authenticate! }
params do params do
...@@ -10,8 +11,11 @@ module API ...@@ -10,8 +11,11 @@ module API
desc 'Get all labels of the project' do desc 'Get all labels of the project' do
success Entities::Label success Entities::Label
end end
params do
use :pagination
end
get ':id/labels' do get ':id/labels' do
present available_labels, with: Entities::Label, current_user: current_user, project: user_project present paginate(available_labels), with: Entities::Label, current_user: current_user, project: user_project
end end
desc 'Create a new label' do desc 'Create a new label' do
......
module API module API
# MergeRequestDiff API # MergeRequestDiff API
class MergeRequestDiffs < Grape::API class MergeRequestDiffs < Grape::API
include PaginationParams
before { authenticate! } before { authenticate! }
resource :projects do resource :projects do
...@@ -12,12 +14,12 @@ module API ...@@ -12,12 +14,12 @@ module API
params do params do
requires :id, type: String, desc: 'The ID of a project' requires :id, type: String, desc: 'The ID of a project'
requires :merge_request_id, type: Integer, desc: 'The ID of a merge request' requires :merge_request_id, type: Integer, desc: 'The ID of a merge request'
use :pagination
end end
get ":id/merge_requests/:merge_request_id/versions" do get ":id/merge_requests/:merge_request_id/versions" do
merge_request = find_merge_request_with_access(params[:merge_request_id]) merge_request = find_merge_request_with_access(params[:merge_request_id])
present merge_request.merge_request_diffs, with: Entities::MergeRequestDiff present paginate(merge_request.merge_request_diffs), with: Entities::MergeRequestDiff
end end
desc 'Get a single merge request diff version' do desc 'Get a single merge request diff version' do
......
...@@ -119,8 +119,9 @@ module API ...@@ -119,8 +119,9 @@ module API
end end
get ':id/merge_requests/:merge_request_id/commits' do get ':id/merge_requests/:merge_request_id/commits' do
merge_request = find_merge_request_with_access(params[:merge_request_id]) merge_request = find_merge_request_with_access(params[:merge_request_id])
commits = ::Kaminari.paginate_array(merge_request.commits)
present merge_request.commits, with: Entities::RepoCommit present paginate(commits), with: Entities::RepoCommit
end end
desc 'Show the merge request changes' do desc 'Show the merge request changes' do
......
...@@ -15,8 +15,8 @@ module API ...@@ -15,8 +15,8 @@ module API
included do included do
helpers do helpers do
params :pagination do params :pagination do
optional :page, type: Integer, desc: 'Current page number' optional :page, type: Integer, default: 1, desc: 'Current page number'
optional :per_page, type: Integer, desc: 'Number of items per page' optional :per_page, type: Integer, default: 20, desc: 'Number of items per page'
end end
end end
end end
......
...@@ -32,9 +32,7 @@ module API ...@@ -32,9 +32,7 @@ module API
use :pagination use :pagination
end end
get ":id/hooks" do get ":id/hooks" do
hooks = paginate user_project.hooks present paginate(user_project.hooks), with: Entities::ProjectHook
present hooks, with: Entities::ProjectHook
end end
desc 'Get a project hook' do desc 'Get a project hook' do
......
...@@ -2,6 +2,8 @@ require 'mime/types' ...@@ -2,6 +2,8 @@ require 'mime/types'
module API module API
class Repositories < Grape::API class Repositories < Grape::API
include PaginationParams
before { authorize! :download_code, user_project } before { authorize! :download_code, user_project }
params do params do
...@@ -24,6 +26,7 @@ module API ...@@ -24,6 +26,7 @@ module API
optional :ref_name, type: String, desc: 'The name of a repository branch or tag, if not given the default branch is used' optional :ref_name, type: String, desc: 'The name of a repository branch or tag, if not given the default branch is used'
optional :path, type: String, desc: 'The path of the tree' optional :path, type: String, desc: 'The path of the tree'
optional :recursive, type: Boolean, default: false, desc: 'Used to get a recursive tree' optional :recursive, type: Boolean, default: false, desc: 'Used to get a recursive tree'
use :pagination
end end
get ':id/repository/tree' do get ':id/repository/tree' do
ref = params[:ref_name] || user_project.try(:default_branch) || 'master' ref = params[:ref_name] || user_project.try(:default_branch) || 'master'
...@@ -33,8 +36,8 @@ module API ...@@ -33,8 +36,8 @@ module API
not_found!('Tree') unless commit not_found!('Tree') unless commit
tree = user_project.repository.tree(commit.id, path, recursive: params[:recursive]) tree = user_project.repository.tree(commit.id, path, recursive: params[:recursive])
entries = ::Kaminari.paginate_array(tree.sorted_entries)
present tree.sorted_entries, with: Entities::RepoTreeObject present paginate(entries), with: Entities::RepoTreeObject
end end
desc 'Get a raw file contents' desc 'Get a raw file contents'
...@@ -100,10 +103,13 @@ module API ...@@ -100,10 +103,13 @@ module API
desc 'Get repository contributors' do desc 'Get repository contributors' do
success Entities::Contributor success Entities::Contributor
end end
params do
use :pagination
end
get ':id/repository/contributors' do get ':id/repository/contributors' do
begin begin
present user_project.repository.contributors, contributors = ::Kaminari.paginate_array(user_project.repository.contributors)
with: Entities::Contributor present paginate(contributors), with: Entities::Contributor
rescue rescue
not_found! not_found!
end end
......
module API module API
# Hooks API
class SystemHooks < Grape::API class SystemHooks < Grape::API
include PaginationParams
before do before do
authenticate! authenticate!
authenticated_as_admin! authenticated_as_admin!
...@@ -10,10 +11,11 @@ module API ...@@ -10,10 +11,11 @@ module API
desc 'Get the list of system hooks' do desc 'Get the list of system hooks' do
success Entities::Hook success Entities::Hook
end end
params do
use :pagination
end
get do get do
hooks = SystemHook.all present paginate(SystemHook.all), with: Entities::Hook
present hooks, with: Entities::Hook
end end
desc 'Create a new system hook' do desc 'Create a new system hook' do
......
module API module API
# Git Tags API
class Tags < Grape::API class Tags < Grape::API
include PaginationParams
before { authorize! :download_code, user_project } before { authorize! :download_code, user_project }
params do params do
...@@ -10,9 +11,12 @@ module API ...@@ -10,9 +11,12 @@ module API
desc 'Get a project repository tags' do desc 'Get a project repository tags' do
success Entities::RepoTag success Entities::RepoTag
end end
params do
use :pagination
end
get ":id/repository/tags" do get ":id/repository/tags" do
present user_project.repository.tags.sort_by(&:name).reverse, tags = ::Kaminari.paginate_array(user_project.repository.tags.sort_by(&:name).reverse)
with: Entities::RepoTag, project: user_project present paginate(tags), with: Entities::RepoTag, project: user_project
end end
desc 'Get a single repository tag' do desc 'Get a single repository tag' do
......
module API module API
class Templates < Grape::API class Templates < Grape::API
include PaginationParams
GLOBAL_TEMPLATE_TYPES = { GLOBAL_TEMPLATE_TYPES = {
gitignores: { gitignores: {
klass: Gitlab::Template::GitignoreTemplate, klass: Gitlab::Template::GitignoreTemplate,
...@@ -51,12 +53,14 @@ module API ...@@ -51,12 +53,14 @@ module API
end end
params do params do
optional :popular, type: Boolean, desc: 'If passed, returns only popular licenses' optional :popular, type: Boolean, desc: 'If passed, returns only popular licenses'
use :pagination
end end
get "templates/licenses" do get "templates/licenses" do
options = { options = {
featured: declared(params).popular.present? ? true : nil featured: declared(params).popular.present? ? true : nil
} }
present Licensee::License.all(options), with: ::API::Entities::RepoLicense licences = ::Kaminari.paginate_array(Licensee::License.all(options))
present paginate(licences), with: Entities::RepoLicense
end end
desc 'Get the text for a specific license' do desc 'Get the text for a specific license' do
...@@ -82,8 +86,12 @@ module API ...@@ -82,8 +86,12 @@ module API
detail "This feature was introduced in GitLab #{gitlab_version}." detail "This feature was introduced in GitLab #{gitlab_version}."
success Entities::TemplatesList success Entities::TemplatesList
end end
params do
use :pagination
end
get "templates/#{template_type}" do get "templates/#{template_type}" do
present klass.all, with: Entities::TemplatesList templates = ::Kaminari.paginate_array(klass.all)
present paginate(templates), with: Entities::TemplatesLis
end end
desc 'Get the text for a specific template present in local filesystem' do desc 'Get the text for a specific template present in local filesystem' do
......
...@@ -209,6 +209,7 @@ module API ...@@ -209,6 +209,7 @@ module API
end end
params do params do
requires :id, type: Integer, desc: 'The ID of the user' requires :id, type: Integer, desc: 'The ID of the user'
use :pagination
end end
get ':id/keys' do get ':id/keys' do
authenticated_as_admin! authenticated_as_admin!
...@@ -216,7 +217,7 @@ module API ...@@ -216,7 +217,7 @@ module API
user = User.find_by(id: params[:id]) user = User.find_by(id: params[:id])
not_found!('User') unless user not_found!('User') unless user
present user.keys, with: Entities::SSHKey present paginate(user.keys), with: Entities::SSHKey
end end
desc 'Delete an existing SSH key from a specified user. Available only for admins.' do desc 'Delete an existing SSH key from a specified user. Available only for admins.' do
...@@ -266,13 +267,14 @@ module API ...@@ -266,13 +267,14 @@ module API
end end
params do params do
requires :id, type: Integer, desc: 'The ID of the user' requires :id, type: Integer, desc: 'The ID of the user'
use :pagination
end end
get ':id/emails' do get ':id/emails' do
authenticated_as_admin! authenticated_as_admin!
user = User.find_by(id: params[:id]) user = User.find_by(id: params[:id])
not_found!('User') unless user not_found!('User') unless user
present user.emails, with: Entities::Email present paginate(user.emails), with: Entities::Email
end end
desc 'Delete an email address of a specified user. Available only for admins.' do desc 'Delete an email address of a specified user. Available only for admins.' do
...@@ -373,8 +375,11 @@ module API ...@@ -373,8 +375,11 @@ module API
desc "Get the currently authenticated user's SSH keys" do desc "Get the currently authenticated user's SSH keys" do
success Entities::SSHKey success Entities::SSHKey
end end
params do
use :pagination
end
get "keys" do get "keys" do
present current_user.keys, with: Entities::SSHKey present paginate(current_user.keys), with: Entities::SSHKey
end end
desc 'Get a single key owned by currently authenticated user' do desc 'Get a single key owned by currently authenticated user' do
...@@ -423,8 +428,11 @@ module API ...@@ -423,8 +428,11 @@ module API
desc "Get the currently authenticated user's email addresses" do desc "Get the currently authenticated user's email addresses" do
success Entities::Email success Entities::Email
end end
params do
use :pagination
end
get "emails" do get "emails" do
present current_user.emails, with: Entities::Email present paginate(current_user.emails), with: Entities::Email
end end
desc 'Get a single email address owned by the currently authenticated user' do desc 'Get a single email address owned by the currently authenticated user' do
......
...@@ -17,7 +17,7 @@ describe API::Branches, api: true do ...@@ -17,7 +17,7 @@ describe API::Branches, api: true do
it "returns an array of project branches" do it "returns an array of project branches" do
project.repository.expire_all_method_caches project.repository.expire_all_method_caches
get api("/projects/#{project.id}/repository/branches", user) get api("/projects/#{project.id}/repository/branches", user), per_page: 100
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
expect(json_response).to be_an Array expect(json_response).to be_an Array
branch_names = json_response.map { |x| x['name'] } branch_names = json_response.map { |x| x['name'] }
......
...@@ -191,6 +191,8 @@ describe API::MergeRequests, api: true do ...@@ -191,6 +191,8 @@ describe API::MergeRequests, api: true do
commit = merge_request.commits.first commit = merge_request.commits.first
expect(response.status).to eq 200 expect(response.status).to eq 200
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.size).to eq(merge_request.commits.size) expect(json_response.size).to eq(merge_request.commits.size)
expect(json_response.first['id']).to eq(commit.id) expect(json_response.first['id']).to eq(commit.id)
expect(json_response.first['title']).to eq(commit.title) expect(json_response.first['title']).to eq(commit.title)
......
...@@ -41,16 +41,6 @@ describe API::Milestones, api: true do ...@@ -41,16 +41,6 @@ describe API::Milestones, api: true do
expect(json_response.length).to eq(1) expect(json_response.length).to eq(1)
expect(json_response.first['id']).to eq(closed_milestone.id) expect(json_response.first['id']).to eq(closed_milestone.id)
end end
end
describe 'GET /projects/:id/milestones/:milestone_id' do
it 'returns a project milestone by id' do
get api("/projects/#{project.id}/milestones/#{milestone.id}", user)
expect(response).to have_http_status(200)
expect(json_response['title']).to eq(milestone.title)
expect(json_response['iid']).to eq(milestone.iid)
end
it 'returns a project milestone by iid' do it 'returns a project milestone by iid' do
get api("/projects/#{project.id}/milestones?iid=#{closed_milestone.iid}", user) get api("/projects/#{project.id}/milestones?iid=#{closed_milestone.iid}", user)
...@@ -69,6 +59,16 @@ describe API::Milestones, api: true do ...@@ -69,6 +59,16 @@ describe API::Milestones, api: true do
expect(json_response.first['title']).to eq milestone.title expect(json_response.first['title']).to eq milestone.title
expect(json_response.first['id']).to eq milestone.id expect(json_response.first['id']).to eq milestone.id
end end
end
describe 'GET /projects/:id/milestones/:milestone_id' do
it 'returns a project milestone by id' do
get api("/projects/#{project.id}/milestones/#{milestone.id}", user)
expect(response).to have_http_status(200)
expect(json_response['title']).to eq(milestone.title)
expect(json_response['iid']).to eq(milestone.iid)
end
it 'returns 401 error if user not authenticated' do it 'returns 401 error if user not authenticated' do
get api("/projects/#{project.id}/milestones/#{milestone.id}") get api("/projects/#{project.id}/milestones/#{milestone.id}")
......
...@@ -507,8 +507,11 @@ describe API::Users, api: true do ...@@ -507,8 +507,11 @@ describe API::Users, api: true do
it 'returns array of ssh keys' do it 'returns array of ssh keys' do
user.keys << key user.keys << key
user.save user.save
get api("/users/#{user.id}/keys", admin) get api("/users/#{user.id}/keys", admin)
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array expect(json_response).to be_an Array
expect(json_response.first['title']).to eq(key.title) expect(json_response.first['title']).to eq(key.title)
end end
...@@ -774,8 +777,11 @@ describe API::Users, api: true do ...@@ -774,8 +777,11 @@ describe API::Users, api: true do
it "returns array of ssh keys" do it "returns array of ssh keys" do
user.keys << key user.keys << key
user.save user.save
get api("/user/keys", user) get api("/user/keys", user)
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array expect(json_response).to be_an Array
expect(json_response.first["title"]).to eq(key.title) expect(json_response.first["title"]).to eq(key.title)
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