Commit f258a59e authored by jubianchi's avatar jubianchi

Filters issues by milestone via API

parent fd338d67
...@@ -5,6 +5,7 @@ v 7.4.0 ...@@ -5,6 +5,7 @@ v 7.4.0
- Refactor test coverage tools usage. Use SIMPLECOV=true to generate it locally - Refactor test coverage tools usage. Use SIMPLECOV=true to generate it locally
- Increase unicorn timeout to 60 seconds - Increase unicorn timeout to 60 seconds
- Sort search autocomplete projects by stars count so most popular go first - Sort search autocomplete projects by stars count so most popular go first
- 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