Skip to content
Snippets Groups Projects
Commit d13451cd authored by Oswaldo Ferreir's avatar Oswaldo Ferreir
Browse files

Returns correct header data for commits endpoint

parent c46f933b
No related branches found
No related tags found
No related merge requests found
Loading
Loading
@@ -499,10 +499,12 @@ class Repository
cache_method :commit_count, fallback: 0
 
def commit_count_for_ref(ref)
return 0 if empty?
return 0 unless exists?
 
cache.fetch(:"commit_count_#{ref}") do
raw_repository.commit_count(ref)
begin
cache.fetch(:"commit_count_#{ref}") { raw_repository.commit_count(ref) }
rescue Rugged::ReferenceError
0
end
end
 
Loading
Loading
---
title: Fix pagination headers for repository commits api endpoint
merge_request:
author: George Andrinopoulos
---
title: "GET 'projects/:id/repository/commits' endpoint improvements"
merge_request: 9679
author: George Andrinopoulos, Jordan Ryan Reuter
---
title: Add pagination to project commits API endpoint
merge_request: 5298
author: Jordan Ryan Reuter
Loading
Loading
@@ -71,3 +71,6 @@ changes are in V4:
- Simplify project payload exposed on Environment endpoints [!9675](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9675)
- API uses merge request `IID`s (internal ID, as in the web UI) rather than `ID`s. This affects the merge requests, award emoji, todos, and time tracking APIs. [!9530](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9530)
- API uses issue `IID`s (internal ID, as in the web UI) rather than `ID`s. This affects the issues, award emoji, todos, and time tracking APIs. [!9530](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9530)
- Change initial page from `0` to `1` on `GET projects/:id/repository/commits` (like on the rest of the API) [!9679] (https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9679)
- Return correct `Link` header data for `GET projects/:id/repository/commits` [!9679] (https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9679)
Loading
Loading
@@ -18,22 +18,32 @@ 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 :since, type: DateTime, desc: 'Only commits after or on this date will be returned'
optional :until, type: DateTime, desc: 'Only commits before or on this date will be returned'
optional :page, type: Integer, default: 0, desc: 'The page for pagination'
optional :per_page, type: Integer, default: 20, desc: 'The number of results per page'
optional :path, type: String, desc: 'The file path'
use :pagination
end
get ":id/repository/commits" do
ref = params[:ref_name] || user_project.try(:default_branch) || 'master'
path = params[:path]
before = params[:until]
after = params[:since]
ref = params[:ref_name] || user_project.try(:default_branch) || 'master'
offset = (params[:page] - 1) * params[:per_page]
 
commits = user_project.repository.commits(ref,
path: params[:path],
path: path,
limit: params[:per_page],
offset: offset,
after: params[:since],
before: params[:until])
before: before,
after: after)
commit_count =
if path || before || after
user_project.repository.count_commits(ref: ref, path: path, before: before, after: after)
else
# Cacheable commit count.
user_project.repository.commit_count_for_ref(ref)
end
 
paginated_commits = Kaminari.paginate_array(commits, total_count: commits.size)
paginated_commits = Kaminari.paginate_array(commits, total_count: commit_count)
 
present paginate(paginated_commits), with: Entities::RepoCommit
end
Loading
Loading
Loading
Loading
@@ -292,6 +292,7 @@ module Gitlab
}
 
options = default_options.merge(options)
options[:limit] ||= 0
options[:offset] ||= 0
actual_ref = options[:ref] || root_ref
begin
Loading
Loading
@@ -354,12 +355,14 @@ module Gitlab
end
 
def count_commits(options)
cmd = %W(#{Gitlab.config.git.bin_path} --git-dir=#{path} rev-list)
cmd += %W(--after=#{options[:after].iso8601}) if options[:after]
cmd += %W(--before=#{options[:before].iso8601}) if options[:before]
cmd += %W(--count #{options[:ref]})
cmd += %W(-- #{options[:path]}) if options[:path].present?
raw_output = IO.popen(cmd) {|io| io.read }
cmd = %W[#{Gitlab.config.git.bin_path} --git-dir=#{path} rev-list]
cmd << "--after=#{options[:after].iso8601}" if options[:after]
cmd << "--before=#{options[:before].iso8601}" if options[:before]
cmd += %W[--count #{options[:ref]}]
cmd += %W[-- #{options[:path]}] if options[:path].present?
raw_output = IO.popen(cmd) { |io| io.read }
raw_output.to_i
end
 
Loading
Loading
Loading
Loading
@@ -826,29 +826,26 @@ describe Gitlab::Git::Repository, seed_helper: true do
 
describe '#count_commits' do
context 'with after timestamp' do
options = { ref: 'master', limit: nil, after: Time.iso8601('2013-03-03T20:15:01+00:00') }
it 'returns the number of commits after timestamp' do
commits = repository.log(options)
options = { ref: 'master', limit: nil, after: Time.iso8601('2013-03-03T20:15:01+00:00') }
 
expect(repository.count_commits(options)).to eq(commits.size)
expect(repository.count_commits(options)).to eq(25)
end
end
 
context 'with before timestamp' do
options = { ref: 'feature', limit: nil, before: Time.iso8601('2015-03-03T20:15:01+00:00') }
it 'returns the number of commits after timestamp' do
commits = repository.log(options)
options = { ref: 'feature', limit: nil, before: Time.iso8601('2015-03-03T20:15:01+00:00') }
 
expect(repository.count_commits(options)).to eq(commits.size)
expect(repository.count_commits(options)).to eq(9)
end
end
 
context 'with path' do
options = { ref: 'master', limit: nil, path: "encoding" }
it 'returns the number of commits with path ' do
commits = repository.log(options)
options = { ref: 'master', limit: nil, path: "encoding" }
 
expect(repository.count_commits(options)).to eq(commits.size)
expect(repository.count_commits(options)).to eq(2)
end
end
end
Loading
Loading
Loading
Loading
@@ -1743,10 +1743,18 @@ describe Repository, models: true do
end
 
describe '#commit_count_for_ref' do
let(:project) { create :empty_project }
context 'with a non-existing repository' do
it 'returns 0' do
expect(repository).to receive(:raw_repository).and_return(nil)
expect(repository.commit_count).to eq(0)
expect(project.repository.commit_count_for_ref('master')).to eq(0)
end
end
context 'with empty repository' do
it 'returns 0' do
project.create_repository
expect(project.repository.commit_count_for_ref('master')).to eq(0)
end
end
 
Loading
Loading
Loading
Loading
@@ -19,6 +19,7 @@ describe API::Commits, api: true do
 
it "returns project commits" do
commit = project.repository.commit
get api("/projects/#{project.id}/repository/commits", user)
 
expect(response).to have_http_status(200)
Loading
Loading
@@ -27,6 +28,16 @@ describe API::Commits, api: true do
expect(json_response.first['committer_name']).to eq(commit.committer_name)
expect(json_response.first['committer_email']).to eq(commit.committer_email)
end
it 'include correct pagination headers' do
commit_count = project.repository.count_commits(ref: 'master').to_s
get api("/projects/#{project.id}/repository/commits", user)
expect(response).to include_pagination_headers
expect(response.headers['X-Total']).to eq(commit_count)
expect(response.headers['X-Page']).to eql('1')
end
end
 
context "unauthorized user" do
Loading
Loading
@@ -39,14 +50,26 @@ describe API::Commits, api: true do
context "since optional parameter" do
it "returns project commits since provided parameter" do
commits = project.repository.commits("master")
since = commits.second.created_at
after = commits.second.created_at
 
get api("/projects/#{project.id}/repository/commits?since=#{since.utc.iso8601}", user)
get api("/projects/#{project.id}/repository/commits?since=#{after.utc.iso8601}", user)
 
expect(json_response.size).to eq 2
expect(json_response.first["id"]).to eq(commits.first.id)
expect(json_response.second["id"]).to eq(commits.second.id)
end
it 'include correct pagination headers' do
commits = project.repository.commits("master")
after = commits.second.created_at
commit_count = project.repository.count_commits(ref: 'master', after: after).to_s
get api("/projects/#{project.id}/repository/commits?since=#{after.utc.iso8601}", user)
expect(response).to include_pagination_headers
expect(response.headers['X-Total']).to eq(commit_count)
expect(response.headers['X-Page']).to eql('1')
end
end
 
context "until optional parameter" do
Loading
Loading
@@ -65,6 +88,18 @@ describe API::Commits, api: true do
expect(json_response.first["id"]).to eq(commits.second.id)
expect(json_response.second["id"]).to eq(commits.third.id)
end
it 'include correct pagination headers' do
commits = project.repository.commits("master")
before = commits.second.created_at
commit_count = project.repository.count_commits(ref: 'master', before: before).to_s
get api("/projects/#{project.id}/repository/commits?until=#{before.utc.iso8601}", user)
expect(response).to include_pagination_headers
expect(response.headers['X-Total']).to eq(commit_count)
expect(response.headers['X-Page']).to eql('1')
end
end
 
context "invalid xmlschema date parameters" do
Loading
Loading
@@ -79,28 +114,44 @@ describe API::Commits, api: true do
context "path optional parameter" do
it "returns project commits matching provided path parameter" do
path = 'files/ruby/popen.rb'
commit_count = project.repository.count_commits(ref: 'master', path: path).to_s
 
get api("/projects/#{project.id}/repository/commits?path=#{path}", user)
 
expect(json_response.size).to eq(3)
expect(json_response.first["id"]).to eq("570e7b2abdd848b95f2f578043fc23bd6f6fd24d")
expect(response).to include_pagination_headers
expect(response.headers['X-Total']).to eq(commit_count)
end
end
 
context 'pagination' do
it_behaves_like 'a paginated resources'
it 'include correct pagination headers' do
path = 'files/ruby/popen.rb'
commit_count = project.repository.count_commits(ref: 'master', path: path).to_s
get api("/projects/#{project.id}/repository/commits?path=#{path}", user)
 
let(:page) { 0 }
expect(response).to include_pagination_headers
expect(response.headers['X-Total']).to eq(commit_count)
expect(response.headers['X-Page']).to eql('1')
end
end
context 'with pagination params' do
let(:page) { 1 }
let(:per_page) { 5 }
let(:ref_name) { 'master' }
let!(:request) do
get api("/projects/#{project.id}/repository/commits?page=#{page}&per_page=#{per_page}&ref_name=#{ref_name}", user)
end
 
it 'returns the commit count in the correct header' do
commit_count = project.repository.commit_count_for_ref(ref_name).to_s
it 'returns correct headers' do
commit_count = project.repository.count_commits(ref: ref_name).to_s
 
expect(response).to include_pagination_headers
expect(response.headers['X-Total']).to eq(commit_count)
expect(response.headers['X-Page']).to eq('1')
expect(response.headers['Link']).to match(/page=1&per_page=5/)
expect(response.headers['Link']).to match(/page=2&per_page=5/)
end
 
context 'viewing the first page' do
Loading
Loading
@@ -109,17 +160,19 @@ describe API::Commits, api: true do
 
expect(json_response.size).to eq(per_page)
expect(json_response.first['id']).to eq(commit.id)
expect(response.headers['X-Page']).to eq('1')
end
end
 
context 'viewing the second page' do
let(:page) { 1 }
context 'viewing the third page' do
let(:page) { 3 }
 
it 'returns the second 5 commits' do
commit = project.repository.commits('HEAD', offset: per_page * page).first
it 'returns the third 5 commits' do
commit = project.repository.commits('HEAD', offset: (page - 1) * per_page).first
 
expect(json_response.size).to eq(per_page)
expect(json_response.first['id']).to eq(commit.id)
expect(response.headers['X-Page']).to eq('3')
end
end
end
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment