Commit 940a1498 authored by Allison Browne's avatar Allison Browne Committed by Peter Leitzen

Fix broken multiple type filtering in todo finder

The todo finder filter by type test did not contain data that should
have been filtered out allowing for a bug where all issues were
always returned when filtering byt type
parent 64f740c4
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# author_id: integer # author_id: integer
# project_id; integer # project_id; integer
# state: 'pending' (default) or 'done' # state: 'pending' (default) or 'done'
# type: 'Issue' or 'MergeRequest' # type: 'Issue' or 'MergeRequest' or ['Issue', 'MergeRequest']
# #
class TodosFinder class TodosFinder
...@@ -40,13 +40,14 @@ class TodosFinder ...@@ -40,13 +40,14 @@ class TodosFinder
def execute def execute
return Todo.none if current_user.nil? return Todo.none if current_user.nil?
raise ArgumentError, invalid_type_message unless valid_types?
items = current_user.todos items = current_user.todos
items = by_action_id(items) items = by_action_id(items)
items = by_action(items) items = by_action(items)
items = by_author(items) items = by_author(items)
items = by_state(items) items = by_state(items)
items = by_type(items) items = by_types(items)
items = by_group(items) items = by_group(items)
# Filtering by project HAS TO be the last because we use # Filtering by project HAS TO be the last because we use
# the project IDs yielded by the todos query thus far # the project IDs yielded by the todos query thus far
...@@ -123,12 +124,16 @@ class TodosFinder ...@@ -123,12 +124,16 @@ class TodosFinder
end end
end end
def type? def types
type.present? && self.class.todo_types.include?(type) @types ||= Array(params[:type]).reject(&:blank?)
end end
def type def valid_types?
params[:type] types.all? { |type| self.class.todo_types.include?(type) }
end
def invalid_type_message
_("Unsupported todo type passed. Supported todo types are: %{todo_types}") % { todo_types: self.class.todo_types.to_a.join(', ') }
end end
def sort(items) def sort(items)
...@@ -193,9 +198,9 @@ class TodosFinder ...@@ -193,9 +198,9 @@ class TodosFinder
items.with_states(params[:state]) items.with_states(params[:state])
end end
def by_type(items) def by_types(items)
if type? if types.any?
items.for_type(type) items.for_type(types)
else else
items items
end end
......
---
title: Fix broken todo GraphQL API filtering when filtering by type
merge_request: 34790
author:
type: fixed
...@@ -24460,6 +24460,9 @@ msgstr "" ...@@ -24460,6 +24460,9 @@ msgstr ""
msgid "Unsubscribes from this %{quick_action_target}." msgid "Unsubscribes from this %{quick_action_target}."
msgstr "" msgstr ""
msgid "Unsupported todo type passed. Supported todo types are: %{todo_types}"
msgstr ""
msgid "Until" msgid "Until"
msgstr "" msgstr ""
......
...@@ -37,16 +37,63 @@ RSpec.describe TodosFinder do ...@@ -37,16 +37,63 @@ RSpec.describe TodosFinder do
end end
context 'when filtering by type' do context 'when filtering by type' do
it 'returns correct todos when filtered by a type' do it 'returns todos by type when filtered by a single type' do
todos = finder.new(user, { type: 'Issue' }).execute todos = finder.new(user, { type: 'Issue' }).execute
expect(todos).to match_array([todo1]) expect(todos).to match_array([todo1])
end end
it 'returns the correct todos when filtering for multiple types' do it 'returns todos by type when filtered by multiple types' do
design_todo = create(:todo, user: user, group: group, target: create(:design))
todos = finder.new(user, { type: %w[Issue MergeRequest] }).execute todos = finder.new(user, { type: %w[Issue MergeRequest] }).execute
expect(todos).to match_array([todo1, todo2]) expect(todos).to contain_exactly(todo1, todo2)
expect(todos).not_to include(design_todo)
end
it 'returns all todos when type is nil' do
todos = finder.new(user, { type: nil }).execute
expect(todos).to contain_exactly(todo1, todo2)
end
it 'returns all todos when type is an empty collection' do
todos = finder.new(user, { type: [] }).execute
expect(todos).to contain_exactly(todo1, todo2)
end
it 'returns all todos when type is blank' do
todos = finder.new(user, { type: '' }).execute
expect(todos).to contain_exactly(todo1, todo2)
end
it 'returns todos by type when blank type is in type collection' do
todos = finder.new(user, { type: ['', 'MergeRequest'] }).execute
expect(todos).to contain_exactly(todo2)
end
it 'returns todos of all types when only blanks are in a collection' do
todos = finder.new(user, { type: ['', ''] }).execute
expect(todos).to contain_exactly(todo1, todo2)
end
it 'returns all todos when no type param' do
todos = finder.new(user).execute
expect(todos).to contain_exactly(todo1, todo2)
end
it 'raises an argument error when invalid type is passed' do
create(:todo, user: user, group: group, target: create(:design))
todos_finder = finder.new(user, { type: %w[Issue MergeRequest NotAValidType] })
expect { todos_finder.execute }.to raise_error(ArgumentError)
end end
end end
......
...@@ -10,9 +10,9 @@ RSpec.describe Resolvers::TodoResolver do ...@@ -10,9 +10,9 @@ RSpec.describe Resolvers::TodoResolver do
let_it_be(:author1) { create(:user) } let_it_be(:author1) { create(:user) }
let_it_be(:author2) { create(:user) } let_it_be(:author2) { create(:user) }
let_it_be(:todo1) { create(:todo, user: current_user, target_type: 'MergeRequest', state: :pending, action: Todo::MENTIONED, author: author1) } let_it_be(:merge_request_todo_pending) { create(:todo, user: current_user, target_type: 'MergeRequest', state: :pending, action: Todo::MENTIONED, author: author1) }
let_it_be(:todo2) { create(:todo, user: current_user, state: :done, action: Todo::ASSIGNED, author: author2) } let_it_be(:issue_todo_done) { create(:todo, user: current_user, state: :done, action: Todo::ASSIGNED, author: author2) }
let_it_be(:todo3) { create(:todo, user: current_user, state: :pending, action: Todo::ASSIGNED, author: author1) } let_it_be(:issue_todo_pending) { create(:todo, user: current_user, state: :pending, action: Todo::ASSIGNED, author: author1) }
it 'calls TodosFinder' do it 'calls TodosFinder' do
expect_next_instance_of(TodosFinder) do |finder| expect_next_instance_of(TodosFinder) do |finder|
...@@ -23,22 +23,30 @@ RSpec.describe Resolvers::TodoResolver do ...@@ -23,22 +23,30 @@ RSpec.describe Resolvers::TodoResolver do
end end
context 'when using no filter' do context 'when using no filter' do
it 'returns expected todos' do it 'returns pending todos' do
expect(resolve_todos).to contain_exactly(todo1, todo3) expect(resolve_todos).to contain_exactly(merge_request_todo_pending, issue_todo_pending)
end end
end end
context 'when using filters' do context 'when using filters' do
it 'returns the todos for multiple states' do it 'returns the todos for multiple states' do
todos = resolve_todos({ state: [:done, :pending] }) todos = resolve_todos(state: [:done, :pending])
expect(todos).to contain_exactly(todo1, todo2, todo3) expect(todos).to contain_exactly(merge_request_todo_pending, issue_todo_done, issue_todo_pending)
end end
it 'returns the todos for multiple types' do it 'returns the todos for multiple filters' do
todos = resolve_todos({ type: %w[Issue MergeRequest] }) design_todo_pending = create(:todo, target_type: 'DesignManagement::Design', user: current_user, state: :pending, action: Todo::ASSIGNED, author: author1)
expect(todos).to contain_exactly(todo1, todo3) todos = resolve_todos(type: ['MergeRequest', 'DesignManagement::Design'])
expect(todos).to contain_exactly(merge_request_todo_pending, design_todo_pending)
end
it 'returns the todos for single filter' do
todos = resolve_todos(type: 'MergeRequest')
expect(todos).to contain_exactly(merge_request_todo_pending)
end end
it 'returns the todos for multiple groups' do it 'returns the todos for multiple groups' do
...@@ -53,7 +61,7 @@ RSpec.describe Resolvers::TodoResolver do ...@@ -53,7 +61,7 @@ RSpec.describe Resolvers::TodoResolver do
todo5 = create(:todo, group: group2, user: current_user, state: :pending, action: Todo::ASSIGNED, author: author1) todo5 = create(:todo, group: group2, user: current_user, state: :pending, action: Todo::ASSIGNED, author: author1)
create(:todo, group: group3, user: current_user, state: :pending, action: Todo::ASSIGNED, author: author1) create(:todo, group: group3, user: current_user, state: :pending, action: Todo::ASSIGNED, author: author1)
todos = resolve_todos({ group_id: [group2.id, group1.id] }) todos = resolve_todos(group_id: [group2.id, group1.id])
expect(todos).to contain_exactly(todo4, todo5) expect(todos).to contain_exactly(todo4, todo5)
end end
...@@ -61,20 +69,19 @@ RSpec.describe Resolvers::TodoResolver do ...@@ -61,20 +69,19 @@ RSpec.describe Resolvers::TodoResolver do
it 'returns the todos for multiple authors' do it 'returns the todos for multiple authors' do
author3 = create(:user) author3 = create(:user)
todo4 = create(:todo, user: current_user, state: :pending, action: Todo::ASSIGNED, author: author2)
create(:todo, user: current_user, state: :pending, action: Todo::ASSIGNED, author: author3) create(:todo, user: current_user, state: :pending, action: Todo::ASSIGNED, author: author3)
todos = resolve_todos({ author_id: [author2.id, author1.id] }) todos = resolve_todos(author_id: [author2.id, author1.id])
expect(todos).to contain_exactly(todo1, todo3, todo4) expect(todos).to contain_exactly(merge_request_todo_pending, issue_todo_pending)
end end
it 'returns the todos for multiple actions' do it 'returns the todos for multiple actions' do
create(:todo, user: current_user, state: :pending, action: Todo::DIRECTLY_ADDRESSED, author: author1) create(:todo, user: current_user, state: :pending, action: Todo::DIRECTLY_ADDRESSED, author: author1)
todos = resolve_todos({ action: [Todo::MENTIONED, Todo::ASSIGNED] }) todos = resolve_todos(action: [Todo::MENTIONED, Todo::ASSIGNED])
expect(todos).to contain_exactly(todo1, todo3) expect(todos).to contain_exactly(merge_request_todo_pending, issue_todo_pending)
end end
it 'returns the todos for multiple projects' do it 'returns the todos for multiple projects' do
...@@ -86,7 +93,7 @@ RSpec.describe Resolvers::TodoResolver do ...@@ -86,7 +93,7 @@ RSpec.describe Resolvers::TodoResolver do
todo5 = create(:todo, project: project2, user: current_user, state: :pending, action: Todo::ASSIGNED, author: author1) todo5 = create(:todo, project: project2, user: current_user, state: :pending, action: Todo::ASSIGNED, author: author1)
create(:todo, project: project3, user: current_user, state: :pending, action: Todo::ASSIGNED, author: author1) create(:todo, project: project3, user: current_user, state: :pending, action: Todo::ASSIGNED, author: author1)
todos = resolve_todos({ project_id: [project2.id, project1.id] }) todos = resolve_todos(project_id: [project2.id, project1.id])
expect(todos).to contain_exactly(todo4, todo5) expect(todos).to contain_exactly(todo4, todo5)
end end
......
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