Commit 4c52ff51 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge branch 'api/issues-filter-milestone' of...

Merge branch 'api/issues-filter-milestone' of https://github.com/jubianchi/gitlabhq into jubianchi-api/issues-filter-milestone
Signed-off-by: default avatarDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>

Conflicts:
	CHANGELOG
parents c20c2c83 f258a59e
...@@ -9,6 +9,7 @@ v 7.4.0 ...@@ -9,6 +9,7 @@ v 7.4.0
- Do not delete tmp/repositories itself during clean-up, only its contents - Do not delete tmp/repositories itself during clean-up, only its contents
- Support for backup uploads to remote storage - Support for backup uploads to remote storage
- Prevent notes polling when there are not notes - Prevent notes polling when there are not notes
- API: filter project issues by milestone (Julien Bianchi)
v 7.3.1 v 7.3.1
- Fix ref parsing in Gitlab::GitAccess - Fix ref parsing in Gitlab::GitAccess
......
...@@ -95,6 +95,8 @@ GET /projects/:id/issues?state=closed ...@@ -95,6 +95,8 @@ GET /projects/:id/issues?state=closed
GET /projects/:id/issues?labels=foo GET /projects/:id/issues?labels=foo
GET /projects/:id/issues?labels=foo,bar GET /projects/:id/issues?labels=foo,bar
GET /projects/:id/issues?labels=foo,bar&state=opened GET /projects/:id/issues?labels=foo,bar&state=opened
GET /projects/:id/issues?milestone=1.0.0
GET /projects/:id/issues?milestone=1.0.0&state=opened
``` ```
Parameters: Parameters:
...@@ -102,6 +104,7 @@ Parameters: ...@@ -102,6 +104,7 @@ Parameters:
- `id` (required) - The ID of a project - `id` (required) - The ID of a project
- `state` (optional) - Return `all` issues or just those that are `opened` or `closed` - `state` (optional) - Return `all` issues or just those that are `opened` or `closed`
- `labels` (optional) - Comma-separated list of label names - `labels` (optional) - Comma-separated list of label names
- `milestone` (optional) - Milestone title
## Single issue ## Single issue
......
...@@ -4,7 +4,7 @@ module API ...@@ -4,7 +4,7 @@ module API
before { authenticate! } before { authenticate! }
helpers do helpers do
def filter_issues_state(issues, state = nil) def filter_issues_state(issues, state)
case state case state
when 'opened' then issues.opened when 'opened' then issues.opened
when 'closed' then issues.closed when 'closed' then issues.closed
...@@ -13,7 +13,11 @@ module API ...@@ -13,7 +13,11 @@ module API
end end
def filter_issues_labels(issues, labels) def filter_issues_labels(issues, labels)
issues.includes(:labels).where("labels.title" => labels.split(',')) issues.includes(:labels).where('labels.title' => labels.split(','))
end
def filter_issues_milestone(issues, milestone)
issues.includes(:milestone).where('milestones.title' => milestone)
end end
end end
...@@ -48,19 +52,24 @@ module API ...@@ -48,19 +52,24 @@ module API
# id (required) - The ID of a project # id (required) - The ID of a project
# state (optional) - Return "opened" or "closed" issues # state (optional) - Return "opened" or "closed" issues
# labels (optional) - Comma-separated list of label names # labels (optional) - Comma-separated list of label names
# milestone (optional) - Milestone title
# #
# Example Requests: # Example Requests:
# GET /projects/:id/issues # GET /projects/:id/issues
# GET /projects/:id/issues?state=opened # GET /projects/:id/issues?state=opened
# GET /projects/:id/issues?state=closed # GET /projects/:id/issues?state=closed
# GET /projects/:id/issues
# GET /projects/:id/issues?labels=foo # GET /projects/:id/issues?labels=foo
# GET /projects/:id/issues?labels=foo,bar # GET /projects/:id/issues?labels=foo,bar
# GET /projects/:id/issues?labels=foo,bar&state=opened # GET /projects/:id/issues?labels=foo,bar&state=opened
# GET /projects/:id/issues?milestone=1.0.0
# GET /projects/:id/issues?milestone=1.0.0&state=closed
get ":id/issues" do get ":id/issues" do
issues = user_project.issues issues = user_project.issues
issues = filter_issues_state(issues, params[:state]) unless params[:state].nil? issues = filter_issues_state(issues, params[:state]) unless params[:state].nil?
issues = filter_issues_labels(issues, params[:labels]) unless params[:labels].nil? issues = filter_issues_labels(issues, params[:labels]) unless params[:labels].nil?
unless params[:milestone].nil?
issues = filter_issues_milestone(issues, params[:milestone])
end
issues = issues.order('issues.id DESC') issues = issues.order('issues.id DESC')
present paginate(issues), with: Entities::Issue present paginate(issues), with: Entities::Issue
......
...@@ -4,12 +4,29 @@ describe API::API, api: true do ...@@ -4,12 +4,29 @@ describe API::API, api: true do
include ApiHelpers include ApiHelpers
let(:user) { create(:user) } let(:user) { create(:user) }
let!(:project) { create(:project, namespace: user.namespace ) } let!(:project) { create(:project, namespace: user.namespace ) }
let!(:closed_issue) { create(:closed_issue, author: user, assignee: user, project: project, state: :closed) } let!(:closed_issue) do
let!(:issue) { create(:issue, author: user, assignee: user, project: project) } create :closed_issue,
author: user,
assignee: user,
project: project,
state: :closed,
milestone: milestone
end
let!(:issue) do
create :issue,
author: user,
assignee: user,
project: project,
milestone: milestone
end
let!(:label) do let!(:label) do
create(:label, title: 'label', color: '#FFAABB', project: project) create(:label, title: 'label', color: '#FFAABB', project: project)
end end
let!(:label_link) { create(:label_link, label: label, target: issue) } let!(:label_link) { create(:label_link, label: label, target: issue) }
let!(:milestone) { create(:milestone, title: '1.0.0', project: project) }
let!(:empty_milestone) do
create(:milestone, title: '2.0.0', project: project)
end
before { project.team << [user, :reporter] } before { project.team << [user, :reporter] }
...@@ -102,15 +119,18 @@ describe API::API, api: true do ...@@ -102,15 +119,18 @@ describe API::API, api: true do
end end
describe "GET /projects/:id/issues" do describe "GET /projects/:id/issues" do
let(:base_url) { "/projects/#{project.id}" }
let(:title) { milestone.title }
it "should return project issues" do it "should return project issues" do
get api("/projects/#{project.id}/issues", user) get api("#{base_url}/issues", user)
response.status.should == 200 response.status.should == 200
json_response.should be_an Array json_response.should be_an Array
json_response.first['title'].should == issue.title json_response.first['title'].should == issue.title
end end
it 'should return an array of labeled project issues' do it 'should return an array of labeled project issues' do
get api("/projects/#{project.id}/issues?labels=#{label.title}", user) get api("#{base_url}/issues?labels=#{label.title}", user)
response.status.should == 200 response.status.should == 200
json_response.should be_an Array json_response.should be_an Array
json_response.length.should == 1 json_response.length.should == 1
...@@ -118,7 +138,7 @@ describe API::API, api: true do ...@@ -118,7 +138,7 @@ describe API::API, api: true do
end end
it 'should return an array of labeled project issues when at least one label matches' do it 'should return an array of labeled project issues when at least one label matches' do
get api("/projects/#{project.id}/issues?labels=#{label.title},foo,bar", user) get api("#{base_url}/issues?labels=#{label.title},foo,bar", user)
response.status.should == 200 response.status.should == 200
json_response.should be_an Array json_response.should be_an Array
json_response.length.should == 1 json_response.length.should == 1
...@@ -126,11 +146,43 @@ describe API::API, api: true do ...@@ -126,11 +146,43 @@ describe API::API, api: true do
end end
it 'should return an empty array if no project issue matches labels' do it 'should return an empty array if no project issue matches labels' do
get api("/projects/#{project.id}/issues?labels=foo,bar", user) get api("#{base_url}/issues?labels=foo,bar", user)
response.status.should == 200
json_response.should be_an Array
json_response.length.should == 0
end
it 'should return an empty array if no issue matches milestone' do
get api("#{base_url}/issues?milestone=#{empty_milestone.title}", user)
response.status.should == 200
json_response.should be_an Array
json_response.length.should == 0
end
it 'should return an empty array if milestone does not exist' do
get api("#{base_url}/issues?milestone=foo", user)
response.status.should == 200 response.status.should == 200
json_response.should be_an Array json_response.should be_an Array
json_response.length.should == 0 json_response.length.should == 0
end end
it 'should return an array of issues in given milestone' do
get api("#{base_url}/issues?milestone=#{title}", user)
response.status.should == 200
json_response.should be_an Array
json_response.length.should == 2
json_response.first['id'].should == issue.id
json_response.second['id'].should == closed_issue.id
end
it 'should return an array of issues matching state in milestone' do
get api("#{base_url}/issues?milestone=#{milestone.title}"\
'&state=closed', user)
response.status.should == 200
json_response.should be_an Array
json_response.length.should == 1
json_response.first['id'].should == closed_issue.id
end
end end
describe "GET /projects/:id/issues/:issue_id" do describe "GET /projects/:id/issues/:issue_id" do
......
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