Commit d13451cd authored by Oswaldo Ferreira's avatar Oswaldo Ferreira

Returns correct header data for commits endpoint

parent c46f933b
......@@ -499,10 +499,12 @@ def commit_count
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
......
---
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
......@@ -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)
......@@ -18,22 +18,32 @@ class Commits < Grape::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
......
......@@ -292,6 +292,7 @@ def log(options)
}
options = default_options.merge(options)
options[:limit] ||= 0
options[:offset] ||= 0
actual_ref = options[:ref] || root_ref
begin
......@@ -354,12 +355,14 @@ def log_by_shell(sha, options)
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
......
......@@ -826,29 +826,26 @@
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
......
......@@ -1743,10 +1743,18 @@
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
......
......@@ -19,6 +19,7 @@
it "returns project commits" do
commit = project.repository.commit
get api("/projects/#{project.id}/repository/commits", user)
expect(response).to have_http_status(200)
......@@ -27,6 +28,16 @@
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
......@@ -39,14 +50,26 @@
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
......@@ -65,6 +88,18 @@
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
......@@ -79,28 +114,44 @@
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
......@@ -109,17 +160,19 @@
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
......
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