API support for the 'since' and 'until' operators on commit requests
- Parameter validation as ISO8601 format
This commit is contained in:
parent
a792427eed
commit
c4b9bd0413
11 changed files with 71 additions and 9 deletions
|
@ -8,6 +8,7 @@ v 8.8.0 (unreleased)
|
||||||
- Replace Devise Async with Devise ActiveJob integration. !3902 (Connor Shea)
|
- Replace Devise Async with Devise ActiveJob integration. !3902 (Connor Shea)
|
||||||
- Allow "NEWS" and "CHANGES" as alternative names for CHANGELOG. !3768 (Connor Shea)
|
- Allow "NEWS" and "CHANGES" as alternative names for CHANGELOG. !3768 (Connor Shea)
|
||||||
- Added button to toggle whitespaces changes on diff view
|
- Added button to toggle whitespaces changes on diff view
|
||||||
|
- API support for the 'since' and 'until' operators on commit requests (Paco Guzman)
|
||||||
|
|
||||||
v 8.7.1 (unreleased)
|
v 8.7.1 (unreleased)
|
||||||
- Throttle the update of `project.last_activity_at` to 1 minute. !3848
|
- Throttle the update of `project.last_activity_at` to 1 minute. !3848
|
||||||
|
|
|
@ -351,7 +351,7 @@ GEM
|
||||||
posix-spawn (~> 0.3)
|
posix-spawn (~> 0.3)
|
||||||
gitlab_emoji (0.3.1)
|
gitlab_emoji (0.3.1)
|
||||||
gemojione (~> 2.2, >= 2.2.1)
|
gemojione (~> 2.2, >= 2.2.1)
|
||||||
gitlab_git (10.0.0)
|
gitlab_git (10.0.2)
|
||||||
activesupport (~> 4.0)
|
activesupport (~> 4.0)
|
||||||
charlock_holmes (~> 0.7.3)
|
charlock_holmes (~> 0.7.3)
|
||||||
github-linguist (~> 4.7.0)
|
github-linguist (~> 4.7.0)
|
||||||
|
|
|
@ -15,7 +15,7 @@ class Projects::CommitsController < Projects::ApplicationController
|
||||||
if search.present?
|
if search.present?
|
||||||
@repository.find_commits_by_message(search, @ref, @path, @limit, @offset).compact
|
@repository.find_commits_by_message(search, @ref, @path, @limit, @offset).compact
|
||||||
else
|
else
|
||||||
@repository.commits(@ref, @path, @limit, @offset)
|
@repository.commits(@ref, path: @path, limit: @limit, offset: @offset)
|
||||||
end
|
end
|
||||||
|
|
||||||
@note_counts = project.notes.where(commit_id: @commits.map(&:id)).
|
@note_counts = project.notes.where(commit_id: @commits.map(&:id)).
|
||||||
|
|
|
@ -17,7 +17,7 @@ class Projects::GraphsController < Projects::ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def commits
|
def commits
|
||||||
@commits = @project.repository.commits(@ref, nil, 2000, 0, true)
|
@commits = @project.repository.commits(@ref, limit: 2000, skip_merges: true)
|
||||||
@commits_graph = Gitlab::Graphs::Commits.new(@commits)
|
@commits_graph = Gitlab::Graphs::Commits.new(@commits)
|
||||||
@commits_per_week_days = @commits_graph.commits_per_week_days
|
@commits_per_week_days = @commits_graph.commits_per_week_days
|
||||||
@commits_per_time = @commits_graph.commits_per_time
|
@commits_per_time = @commits_graph.commits_per_time
|
||||||
|
@ -55,7 +55,7 @@ class Projects::GraphsController < Projects::ApplicationController
|
||||||
private
|
private
|
||||||
|
|
||||||
def fetch_graph
|
def fetch_graph
|
||||||
@commits = @project.repository.commits(@ref, nil, 6000, 0, true)
|
@commits = @project.repository.commits(@ref, limit: 6000, skip_merges: true)
|
||||||
@log = []
|
@log = []
|
||||||
|
|
||||||
@commits.each do |commit|
|
@commits.each do |commit|
|
||||||
|
|
|
@ -87,13 +87,15 @@ class Repository
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def commits(ref, path = nil, limit = nil, offset = nil, skip_merges = false)
|
def commits(ref, path: nil, limit: nil, offset: nil, skip_merges: false, after: nil, before: nil)
|
||||||
options = {
|
options = {
|
||||||
repo: raw_repository,
|
repo: raw_repository,
|
||||||
ref: ref,
|
ref: ref,
|
||||||
path: path,
|
path: path,
|
||||||
limit: limit,
|
limit: limit,
|
||||||
offset: offset,
|
offset: offset,
|
||||||
|
after: after,
|
||||||
|
before: before,
|
||||||
# --follow doesn't play well with --skip. See:
|
# --follow doesn't play well with --skip. See:
|
||||||
# https://gitlab.com/gitlab-org/gitlab-ce/issues/3574#note_3040520
|
# https://gitlab.com/gitlab-org/gitlab-ce/issues/3574#note_3040520
|
||||||
follow: false,
|
follow: false,
|
||||||
|
@ -575,7 +577,7 @@ class Repository
|
||||||
end
|
end
|
||||||
|
|
||||||
def contributors
|
def contributors
|
||||||
commits = self.commits(nil, nil, 2000, 0, true)
|
commits = self.commits(nil, limit: 2000, offset: 0, skip_merges: true)
|
||||||
|
|
||||||
commits.group_by(&:author_email).map do |email, commits|
|
commits.group_by(&:author_email).map do |email, commits|
|
||||||
contributor = Gitlab::Contributor.new
|
contributor = Gitlab::Contributor.new
|
||||||
|
|
|
@ -12,6 +12,8 @@ GET /projects/:id/repository/commits
|
||||||
| --------- | ---- | -------- | ----------- |
|
| --------- | ---- | -------- | ----------- |
|
||||||
| `id` | integer | yes | The ID of a project |
|
| `id` | integer | yes | The ID of a project |
|
||||||
| `ref_name` | string | no | The name of a repository branch or tag or if not given the default branch |
|
| `ref_name` | string | no | The name of a repository branch or tag or if not given the default branch |
|
||||||
|
| `since` | string | no | Only commits after or in this date will be returned in ISO 8601 format YYYY-MM-DDTHH:MM:SSZ |
|
||||||
|
| `until` | string | no | Only commits before or in this date will be returned in ISO 8601 format YYYY-MM-DDTHH:MM:SSZ |
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/5/repository/commits"
|
curl -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/5/repository/commits"
|
||||||
|
|
|
@ -12,14 +12,20 @@ module API
|
||||||
# Parameters:
|
# Parameters:
|
||||||
# id (required) - The ID of a project
|
# id (required) - The ID of a project
|
||||||
# ref_name (optional) - The name of a repository branch or tag, if not given the default branch is used
|
# ref_name (optional) - The name of a repository branch or tag, if not given the default branch is used
|
||||||
|
# since (optional) - Only commits after or in this date will be returned
|
||||||
|
# until (optional) - Only commits before or in this date will be returned
|
||||||
# Example Request:
|
# Example Request:
|
||||||
# GET /projects/:id/repository/commits
|
# GET /projects/:id/repository/commits
|
||||||
get ":id/repository/commits" do
|
get ":id/repository/commits" do
|
||||||
|
datetime_attributes! :since, :until
|
||||||
|
|
||||||
page = (params[:page] || 0).to_i
|
page = (params[:page] || 0).to_i
|
||||||
per_page = (params[:per_page] || 20).to_i
|
per_page = (params[:per_page] || 20).to_i
|
||||||
ref = params[:ref_name] || user_project.try(:default_branch) || 'master'
|
ref = params[:ref_name] || user_project.try(:default_branch) || 'master'
|
||||||
|
after = params[:since]
|
||||||
|
before = params[:until]
|
||||||
|
|
||||||
commits = user_project.repository.commits(ref, nil, per_page, page * per_page)
|
commits = user_project.repository.commits(ref, limit: per_page, offset: page * per_page, after: after, before: before)
|
||||||
present commits, with: Entities::RepoCommit
|
present commits, with: Entities::RepoCommit
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -183,6 +183,22 @@ module API
|
||||||
Gitlab::Access.options_with_owner.values.include? level.to_i
|
Gitlab::Access.options_with_owner.values.include? level.to_i
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Checks the occurrences of datetime attributes, each attribute if present in the params hash must be in ISO 8601
|
||||||
|
# format (YYYY-MM-DDTHH:MM:SSZ) or a Bad Request error is invoked.
|
||||||
|
#
|
||||||
|
# Parameters:
|
||||||
|
# keys (required) - An array consisting of elements that must be parseable as dates from the params hash
|
||||||
|
def datetime_attributes!(*keys)
|
||||||
|
keys.each do |key|
|
||||||
|
begin
|
||||||
|
params[key] = Time.xmlschema(params[key]) if params[key].present?
|
||||||
|
rescue ArgumentError
|
||||||
|
message = "\"" + key.to_s + "\" must be a timestamp in ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ"
|
||||||
|
render_api_error!(message, 400)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def issuable_order_by
|
def issuable_order_by
|
||||||
if params["order_by"] == 'updated_at'
|
if params["order_by"] == 'updated_at'
|
||||||
'updated_at'
|
'updated_at'
|
||||||
|
|
|
@ -66,7 +66,7 @@ module Gitlab
|
||||||
# This method provide a sample data generated with
|
# This method provide a sample data generated with
|
||||||
# existing project and commits to test webhooks
|
# existing project and commits to test webhooks
|
||||||
def build_sample(project, user)
|
def build_sample(project, user)
|
||||||
commits = project.repository.commits(project.default_branch, nil, 3)
|
commits = project.repository.commits(project.default_branch, limit: 3)
|
||||||
ref = "#{Gitlab::Git::BRANCH_REF_PREFIX}#{project.default_branch}"
|
ref = "#{Gitlab::Git::BRANCH_REF_PREFIX}#{project.default_branch}"
|
||||||
build(project, user, commits.last.id, commits.first.id, ref, commits)
|
build(project, user, commits.last.id, commits.first.id, ref, commits)
|
||||||
end
|
end
|
||||||
|
|
|
@ -561,7 +561,7 @@ describe Repository, models: true do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe :skip_merged_commit do
|
describe :skip_merged_commit do
|
||||||
subject { repository.commits(Gitlab::Git::BRANCH_REF_PREFIX + "'test'", nil, 100, 0, true).map{ |k| k.id } }
|
subject { repository.commits(Gitlab::Git::BRANCH_REF_PREFIX + "'test'", limit: 100, skip_merges: true).map{ |k| k.id } }
|
||||||
|
|
||||||
it { is_expected.not_to include('e56497bb5f03a90a51293fc6d516788730953899') }
|
it { is_expected.not_to include('e56497bb5f03a90a51293fc6d516788730953899') }
|
||||||
end
|
end
|
||||||
|
|
|
@ -32,6 +32,41 @@ describe API::API, api: true do
|
||||||
expect(response.status).to eq(401)
|
expect(response.status).to eq(401)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "since optional parameter" do
|
||||||
|
it "should return project commits since provided parameter" do
|
||||||
|
commits = project.repository.commits("master")
|
||||||
|
since = commits.second.created_at
|
||||||
|
|
||||||
|
get api("/projects/#{project.id}/repository/commits?since=#{since.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
|
||||||
|
end
|
||||||
|
|
||||||
|
context "until optional parameter" do
|
||||||
|
it "should return project commits until provided parameter" do
|
||||||
|
commits = project.repository.commits("master")
|
||||||
|
before = commits.second.created_at
|
||||||
|
|
||||||
|
get api("/projects/#{project.id}/repository/commits?until=#{before.utc.iso8601}", user)
|
||||||
|
|
||||||
|
expect(json_response.size).to eq(commits.size - 1)
|
||||||
|
expect(json_response.first["id"]).to eq(commits.second.id)
|
||||||
|
expect(json_response.second["id"]).to eq(commits.third.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "invalid xmlschema date parameters" do
|
||||||
|
it "should return an invalid parameter error message" do
|
||||||
|
get api("/projects/#{project.id}/repository/commits?since=invalid-date", user)
|
||||||
|
|
||||||
|
expect(response.status).to eq(400)
|
||||||
|
expect(json_response['message']).to include "\"since\" must be a timestamp in ISO 8601 format"
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "GET /projects:id/repository/commits/:sha" do
|
describe "GET /projects:id/repository/commits/:sha" do
|
||||||
|
|
Loading…
Reference in a new issue