Commit 227e30f7 authored by Heinrich Lee Yu's avatar Heinrich Lee Yu

Issues API: Add None/Any option to assignee_id

parent 679c0048
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
# project_id: integer # project_id: integer
# milestone_title: string # milestone_title: string
# author_id: integer # author_id: integer
# assignee_id: integer # assignee_id: integer or 'None' or 'Any'
# search: string # search: string
# label_name: string # label_name: string
# sort: string # sort: string
...@@ -34,6 +34,11 @@ class IssuableFinder ...@@ -34,6 +34,11 @@ class IssuableFinder
requires_cross_project_access unless: -> { project? } requires_cross_project_access unless: -> { project? }
# This is used as a common filter for None / Any
FILTER_NONE = 'None'.freeze
FILTER_ANY = 'Any'.freeze
# This is accepted as a deprecated filter and is also used in unassigning users
NONE = '0'.freeze NONE = '0'.freeze
attr_accessor :current_user, :params attr_accessor :current_user, :params
...@@ -236,16 +241,20 @@ class IssuableFinder ...@@ -236,16 +241,20 @@ class IssuableFinder
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
def assignee_id? def assignee_id?
params[:assignee_id].present? && params[:assignee_id].to_s != NONE params[:assignee_id].present?
end end
def assignee_username? def assignee_username?
params[:assignee_username].present? && params[:assignee_username].to_s != NONE params[:assignee_username].present?
end end
def no_assignee? def filter_by_no_assignee?
# Assignee_id takes precedence over assignee_username # Assignee_id takes precedence over assignee_username
params[:assignee_id].to_s == NONE || params[:assignee_username].to_s == NONE [NONE, FILTER_NONE].include?(params[:assignee_id].to_s) || params[:assignee_username].to_s == NONE
end
def filter_by_any_assignee?
params[:assignee_id].to_s == FILTER_ANY
end end
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
...@@ -399,16 +408,18 @@ class IssuableFinder ...@@ -399,16 +408,18 @@ class IssuableFinder
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def by_assignee(items) def by_assignee(items)
if assignee if filter_by_no_assignee?
items = items.where(assignee_id: assignee.id) items.where(assignee_id: nil)
elsif no_assignee? elsif filter_by_any_assignee?
items = items.where(assignee_id: nil) items.where('assignee_id IS NOT NULL')
elsif assignee
items.where(assignee_id: assignee.id)
elsif assignee_id? || assignee_username? # assignee not found elsif assignee_id? || assignee_username? # assignee not found
items = items.none items.none
end else
items items
end end
end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
......
...@@ -135,12 +135,13 @@ class IssuesFinder < IssuableFinder ...@@ -135,12 +135,13 @@ class IssuesFinder < IssuableFinder
current_user.blank? current_user.blank?
end end
# rubocop: disable CodeReuse/ActiveRecord
def by_assignee(items) def by_assignee(items)
if assignee if filter_by_no_assignee?
items.assigned_to(assignee)
elsif no_assignee?
items.unassigned items.unassigned
elsif filter_by_any_assignee?
items.assigned
elsif assignee
items.assigned_to(assignee)
elsif assignee_id? || assignee_username? # assignee not found elsif assignee_id? || assignee_username? # assignee not found
items.none items.none
else else
......
...@@ -40,7 +40,11 @@ module API ...@@ -40,7 +40,11 @@ module API
optional :updated_after, type: DateTime, desc: 'Return issues updated after the specified time' optional :updated_after, type: DateTime, desc: 'Return issues updated after the specified time'
optional :updated_before, type: DateTime, desc: 'Return issues updated before the specified time' optional :updated_before, type: DateTime, desc: 'Return issues updated before the specified time'
optional :author_id, type: Integer, desc: 'Return issues which are authored by the user with the given ID' optional :author_id, type: Integer, desc: 'Return issues which are authored by the user with the given ID'
optional :assignee_id, type: Integer, desc: 'Return issues which are assigned to the user with the given ID' optional :assignee_id, types: [Integer, String],
values: -> (v) {
v.is_a?(Integer) or [IssuableFinder::FILTER_NONE, IssuableFinder::FILTER_ANY].include?(v)
},
desc: 'Return issues which are assigned to the user with the given ID'
optional :scope, type: String, values: %w[created-by-me assigned-to-me created_by_me assigned_to_me all], optional :scope, type: String, values: %w[created-by-me assigned-to-me created_by_me assigned_to_me all],
desc: 'Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`' desc: 'Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`'
optional :my_reaction_emoji, type: String, desc: 'Return issues reacted by the authenticated user by the given emoji' optional :my_reaction_emoji, type: String, desc: 'Return issues reacted by the authenticated user by the given emoji'
......
...@@ -59,11 +59,27 @@ describe IssuesFinder do ...@@ -59,11 +59,27 @@ describe IssuesFinder do
context 'filtering by no assignee' do context 'filtering by no assignee' do
let(:params) { { assignee_id: 0 } } let(:params) { { assignee_id: 0 } }
it 'returns issues not assign to any assignee' do it 'returns issues not assigned to any assignee' do
expect(issues).to contain_exactly(issue4) expect(issues).to contain_exactly(issue4)
end end
end end
context 'filtering by no assignee' do
let(:params) { { assignee_id: 'None' } }
it 'returns issues not assigned to any assignee' do
expect(issues).to contain_exactly(issue4)
end
end
context 'filtering by any assignee' do
let(:params) { { assignee_id: 'Any' } }
it 'returns issues assigned to any assignee' do
expect(issues).to contain_exactly(issue1, issue2, issue3)
end
end
context 'filtering by group_id' do context 'filtering by group_id' do
let(:params) { { group_id: group.id } } let(:params) { { group_id: group.id } }
......
...@@ -178,6 +178,21 @@ describe API::Issues do ...@@ -178,6 +178,21 @@ describe API::Issues do
expect(first_issue['id']).to eq(issue2.id) expect(first_issue['id']).to eq(issue2.id)
end end
it 'returns issues with no assignee' do
issue2 = create(:issue, author: user2, project: project)
get api('/issues', user), assignee_id: 'None', scope: 'all'
expect_paginated_array_response(size: 1)
expect(first_issue['id']).to eq(issue2.id)
end
it 'returns issues with any assignee' do
get api('/issues', user), assignee_id: 'Any', scope: 'all'
expect_paginated_array_response(size: 3)
end
it 'returns issues reacted by the authenticated user by the given emoji' do it 'returns issues reacted by the authenticated user by the given emoji' do
issue2 = create(:issue, project: project, author: user, assignees: [user]) issue2 = create(:issue, project: project, author: user, assignees: [user])
award_emoji = create(:award_emoji, awardable: issue2, user: user2, name: 'star') award_emoji = create(:award_emoji, awardable: issue2, user: user2, name: 'star')
......
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