From 227e30f7feb2072407a231a762835bf8d5103129 Mon Sep 17 00:00:00 2001 From: Heinrich Lee Yu Date: Thu, 25 Oct 2018 15:02:28 +0800 Subject: [PATCH] Issues API: Add None/Any option to assignee_id --- app/finders/issuable_finder.rb | 35 ++++++++++++++++++++---------- app/finders/issues_finder.rb | 9 ++++---- lib/api/issues.rb | 6 ++++- spec/finders/issues_finder_spec.rb | 18 ++++++++++++++- spec/requests/api/issues_spec.rb | 15 +++++++++++++ 5 files changed, 65 insertions(+), 18 deletions(-) diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index 8abfe0c4c17..ec4472de0c4 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -14,7 +14,7 @@ # project_id: integer # milestone_title: string # author_id: integer -# assignee_id: integer +# assignee_id: integer or 'None' or 'Any' # search: string # label_name: string # sort: string @@ -34,6 +34,11 @@ class IssuableFinder 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 attr_accessor :current_user, :params @@ -236,16 +241,20 @@ class IssuableFinder # rubocop: enable CodeReuse/ActiveRecord def assignee_id? - params[:assignee_id].present? && params[:assignee_id].to_s != NONE + params[:assignee_id].present? end def assignee_username? - params[:assignee_username].present? && params[:assignee_username].to_s != NONE + params[:assignee_username].present? end - def no_assignee? + def filter_by_no_assignee? # 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 # rubocop: disable CodeReuse/ActiveRecord @@ -399,15 +408,17 @@ class IssuableFinder # rubocop: disable CodeReuse/ActiveRecord def by_assignee(items) - if assignee - items = items.where(assignee_id: assignee.id) - elsif no_assignee? - items = items.where(assignee_id: nil) + if filter_by_no_assignee? + items.where(assignee_id: nil) + elsif filter_by_any_assignee? + items.where('assignee_id IS NOT NULL') + elsif assignee + items.where(assignee_id: assignee.id) elsif assignee_id? || assignee_username? # assignee not found - items = items.none + items.none + else + items end - - items end # rubocop: enable CodeReuse/ActiveRecord diff --git a/app/finders/issues_finder.rb b/app/finders/issues_finder.rb index abdc47b9866..4a95eb44417 100644 --- a/app/finders/issues_finder.rb +++ b/app/finders/issues_finder.rb @@ -135,12 +135,13 @@ class IssuesFinder < IssuableFinder current_user.blank? end - # rubocop: disable CodeReuse/ActiveRecord def by_assignee(items) - if assignee - items.assigned_to(assignee) - elsif no_assignee? + if filter_by_no_assignee? items.unassigned + elsif filter_by_any_assignee? + items.assigned + elsif assignee + items.assigned_to(assignee) elsif assignee_id? || assignee_username? # assignee not found items.none else diff --git a/lib/api/issues.rb b/lib/api/issues.rb index 25d78053c88..89232212bc7 100644 --- a/lib/api/issues.rb +++ b/lib/api/issues.rb @@ -40,7 +40,11 @@ module API 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 :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], 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' diff --git a/spec/finders/issues_finder_spec.rb b/spec/finders/issues_finder_spec.rb index 0689c843104..7f4f613b406 100644 --- a/spec/finders/issues_finder_spec.rb +++ b/spec/finders/issues_finder_spec.rb @@ -59,11 +59,27 @@ describe IssuesFinder do context 'filtering by no assignee' do 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) 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 let(:params) { { group_id: group.id } } diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb index 9f6cf12f9a7..090e8eac768 100644 --- a/spec/requests/api/issues_spec.rb +++ b/spec/requests/api/issues_spec.rb @@ -178,6 +178,21 @@ describe API::Issues do expect(first_issue['id']).to eq(issue2.id) 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 issue2 = create(:issue, project: project, author: user, assignees: [user]) award_emoji = create(:award_emoji, awardable: issue2, user: user2, name: 'star')