Commit fb781f4e authored by Alex Kalderimis's avatar Alex Kalderimis

Merge branch 'issue_322686-allow_to_filter_epics_on_epic_lists' into 'master'

Allow to filter board epic lists

See merge request gitlab-org/gitlab!56026
parents b5b03ca7 2e10229b
# frozen_string_literal: true
module Types
module Boards
# Common arguments that we can be used to filter boards epics and issues
class BoardIssuableInputBaseType < BaseInputObject
argument :label_name, GraphQL::STRING_TYPE.to_list_type,
required: false,
description: 'Filter by label name.'
argument :author_username, GraphQL::STRING_TYPE,
required: false,
description: 'Filter by author username.'
argument :my_reaction_emoji, GraphQL::STRING_TYPE,
required: false,
description: 'Filter by reaction emoji applied by the current user.'
end
end
end
......@@ -2,11 +2,8 @@
module Types
module Boards
class BoardIssueInputBaseType < BaseInputObject
argument :label_name, GraphQL::STRING_TYPE.to_list_type,
required: false,
description: 'Filter by label name.'
# rubocop: disable Graphql/AuthorizeTypes
class BoardIssueInputBaseType < BoardIssuableInputBaseType
argument :milestone_title, GraphQL::STRING_TYPE,
required: false,
description: 'Filter by milestone title.'
......@@ -15,17 +12,9 @@ module Types
required: false,
description: 'Filter by assignee username.'
argument :author_username, GraphQL::STRING_TYPE,
required: false,
description: 'Filter by author username.'
argument :release_tag, GraphQL::STRING_TYPE,
required: false,
description: 'Filter by release tag.'
argument :my_reaction_emoji, GraphQL::STRING_TYPE,
required: false,
description: 'Filter by reaction emoji.'
end
end
end
......
......@@ -7,8 +7,13 @@ module Resolvers
alias_method :list, :object
def resolve(**args)
filter_params = { board_id: list.epic_board.id, id: list.id }
argument :filters, Types::Boards::BoardEpicInputType,
required: false,
description: 'Filters applied when selecting epics in the board list.'
def resolve(filters: {}, **args)
filter_params = { board_id: list.epic_board.id, id: list.id }.merge(filters)
service = ::Boards::Epics::ListService.new(list.epic_board.group, context[:current_user], filter_params)
offset_pagination(service.execute)
......
# frozen_string_literal: true
module Types
module Boards
class BoardEpicInputType < BoardIssuableInputBaseType
graphql_name 'EpicFilters'
argument :search, GraphQL::STRING_TYPE,
required: false,
description: 'Search query for epic title or description.'
end
end
end
---
title: GraphQL - Allow to filter epics on epic lists
merge_request: 56026
author:
type: added
......@@ -25,10 +25,12 @@ RSpec.describe Resolvers::Boards::BoardListEpicsResolver do
expect(described_class).to have_nullable_graphql_type(Types::EpicType.connection_type)
end
describe '#resolve' do
let(:args) { {} }
def resolve_board_list_epics(args: {})
resolve(described_class, ctx: { current_user: user }, obj: list1, args: args)
end
subject(:result) { resolve(described_class, ctx: { current_user: user }, obj: list1, args: args) }
describe '#resolve' do
subject(:result) { resolve_board_list_epics }
before do
stub_licensed_features(epics: true)
......@@ -38,5 +40,45 @@ RSpec.describe Resolvers::Boards::BoardListEpicsResolver do
it 'returns epics on the board list ordered by position on the board' do
expect(result.to_a).to eq([list1_epic2, list1_epic1])
end
context 'with filters' do
let_it_be(:production_label) { create(:group_label, group: group, name: 'production') }
let_it_be(:list1_epic3) { create(:labeled_epic, group: group, labels: [development, production_label], title: 'filter_this 1') }
let_it_be(:list1_epic4) { create(:labeled_epic, group: group, labels: [development], description: 'filter_this 2') }
it 'filters epics by label' do
args = { filters: { label_name: [production_label.title] } }
result = resolve_board_list_epics(args: args)
expect(result).to contain_exactly(list1_epic3)
end
it 'filters epics by author' do
args = { filters: { author_username: list1_epic4.author.username } }
result = resolve_board_list_epics(args: args)
expect(result).to contain_exactly(list1_epic4)
end
it 'filters epics by reaction emoji' do
emoji_name = 'thumbsup'
create(:award_emoji, name: emoji_name, awardable: list1_epic1, user: user)
args = { filters: { my_reaction_emoji: emoji_name } }
result = resolve_board_list_epics(args: args)
expect(result).to contain_exactly(list1_epic1)
end
it 'filters epics by title and description' do
args = { filters: { search: 'filter_this' } }
result = resolve_board_list_epics(args: args)
expect(result).to contain_exactly(list1_epic3, list1_epic4)
end
end
end
end
......@@ -8,16 +8,18 @@ RSpec.describe 'get list of epics for an epic board list' do
let_it_be(:current_user) { create(:user) }
let_it_be(:group) { create(:group, :private) }
let_it_be(:development) { create(:group_label, group: group, name: 'Development') }
let_it_be(:staging) { create(:group_label, group: group, name: 'Staging') }
let_it_be(:board) { create(:epic_board, group: group) }
let_it_be(:list) { create(:epic_list, epic_board: board, label: development) }
let_it_be(:epic1) { create(:labeled_epic, group: group, labels: [development]) }
let_it_be(:epic2) { create(:labeled_epic, group: group, labels: [development]) }
let_it_be(:epic3) { create(:labeled_epic, group: group, labels: [development]) }
let_it_be(:epic3) { create(:labeled_epic, group: group, labels: [development, staging], author: current_user) }
let_it_be(:epic4) { create(:labeled_epic, group: group) }
let_it_be(:epic_pos1) { create(:epic_board_position, epic: epic1, epic_board: board, relative_position: 20) }
let_it_be(:epic_pos2) { create(:epic_board_position, epic: epic2, epic_board: board, relative_position: 10) }
let(:data_path) { [:group, :epicBoard, :lists, :nodes, 0, :epics] }
def pagination_query(params = {})
graphql_query_for(:group, { full_path: group.full_path },
......@@ -25,7 +27,7 @@ RSpec.describe 'get list of epics for an epic board list' do
epicBoard(id: "#{board.to_global_id}") {
lists(id: "#{list.to_global_id}") {
nodes {
#{query_nodes(:epics, all_graphql_fields_for('epics'.classify), include_pagination_info: true, args: params)}
#{query_nodes(:epics, epic_fields, include_pagination_info: true, args: params)}
}
}
}
......@@ -39,7 +41,7 @@ RSpec.describe 'get list of epics for an epic board list' do
end
describe 'sorting and pagination' do
let(:data_path) { [:group, :epicBoard, :lists, :nodes, 0, :epics] }
let(:epic_fields) { all_graphql_fields_for('epics'.classify) }
let(:expected_results) { [epic2.to_global_id.to_s, epic1.to_global_id.to_s, epic3.to_global_id.to_s] }
def pagination_results_data(nodes)
......@@ -53,4 +55,18 @@ RSpec.describe 'get list of epics for an epic board list' do
let(:first_param) { 2 }
end
end
context 'filters' do
let(:epic_fields) { 'id' }
it 'finds only epics matching the filter' do
filter_params = { filters: { author_username: current_user.username, label_name: [staging.title] } }
query = pagination_query(filter_params)
post_graphql(query, current_user: current_user)
boards = graphql_data_at(*data_path, :nodes, :id)
expect(boards).to contain_exactly(global_id_of(epic3))
end
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