Commit 8014a2a9 authored by Jan Provaznik's avatar Jan Provaznik Committed by charlie ablett

Added negated issue params for group epics

Allows negated params filtering in GraphQL endpoint
parent 867e1ff1
......@@ -1125,42 +1125,47 @@ type BoardEdge {
input BoardEpicIssueInput {
"""
Username of a user assigned to issues
Filter by assignee username
"""
assigneeUsername: [String]
"""
Username of the issues author
Filter by author username
"""
authorUsername: String
"""
Epic ID applied to issues
Filter by epic ID
"""
epicId: String
"""
Label applied to issues
Filter by label name
"""
labelName: [String]
"""
Milestone applied to issues
Filter by milestone title
"""
milestoneTitle: String
"""
Reaction emoji applied to issues
Filter by reaction emoji
"""
myReactionEmoji: String
"""
Release applied to issues
List of negated params. Warning: this argument is experimental and a subject to change in future
"""
not: NegatedBoardEpicIssueInput
"""
Filter by release tag
"""
releaseTag: String
"""
Weight applied to issues
Filter by weight
"""
weight: String
}
......@@ -9673,6 +9678,48 @@ type NamespaceIncreaseStorageTemporarilyPayload {
namespace: Namespace
}
input NegatedBoardEpicIssueInput {
"""
Filter by assignee username
"""
assigneeUsername: [String]
"""
Filter by author username
"""
authorUsername: String
"""
Filter by epic ID
"""
epicId: String
"""
Filter by label name
"""
labelName: [String]
"""
Filter by milestone title
"""
milestoneTitle: String
"""
Filter by reaction emoji
"""
myReactionEmoji: String
"""
Filter by release tag
"""
releaseTag: String
"""
Filter by weight
"""
weight: String
}
type Note implements ResolvableInterface {
"""
User who wrote this note
......
......@@ -3017,7 +3017,7 @@
"inputFields": [
{
"name": "labelName",
"description": "Label applied to issues",
"description": "Filter by label name",
"type": {
"kind": "LIST",
"name": null,
......@@ -3031,7 +3031,7 @@
},
{
"name": "milestoneTitle",
"description": "Milestone applied to issues",
"description": "Filter by milestone title",
"type": {
"kind": "SCALAR",
"name": "String",
......@@ -3041,7 +3041,7 @@
},
{
"name": "assigneeUsername",
"description": "Username of a user assigned to issues",
"description": "Filter by assignee username",
"type": {
"kind": "LIST",
"name": null,
......@@ -3055,7 +3055,7 @@
},
{
"name": "authorUsername",
"description": "Username of the issues author",
"description": "Filter by author username",
"type": {
"kind": "SCALAR",
"name": "String",
......@@ -3065,7 +3065,7 @@
},
{
"name": "releaseTag",
"description": "Release applied to issues",
"description": "Filter by release tag",
"type": {
"kind": "SCALAR",
"name": "String",
......@@ -3075,7 +3075,7 @@
},
{
"name": "epicId",
"description": "Epic ID applied to issues",
"description": "Filter by epic ID",
"type": {
"kind": "SCALAR",
"name": "String",
......@@ -3085,7 +3085,7 @@
},
{
"name": "myReactionEmoji",
"description": "Reaction emoji applied to issues",
"description": "Filter by reaction emoji",
"type": {
"kind": "SCALAR",
"name": "String",
......@@ -3095,13 +3095,23 @@
},
{
"name": "weight",
"description": "Weight applied to issues",
"description": "Filter by weight",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "not",
"description": "List of negated params. Warning: this argument is experimental and a subject to change in future",
"type": {
"kind": "INPUT_OBJECT",
"name": "NegatedBoardEpicIssueInput",
"ofType": null
},
"defaultValue": null
}
],
"interfaces": null,
......@@ -28888,6 +28898,105 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "INPUT_OBJECT",
"name": "NegatedBoardEpicIssueInput",
"description": null,
"fields": null,
"inputFields": [
{
"name": "labelName",
"description": "Filter by label name",
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"defaultValue": null
},
{
"name": "milestoneTitle",
"description": "Filter by milestone title",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "assigneeUsername",
"description": "Filter by assignee username",
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"defaultValue": null
},
{
"name": "authorUsername",
"description": "Filter by author username",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "releaseTag",
"description": "Filter by release tag",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "epicId",
"description": "Filter by epic ID",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "myReactionEmoji",
"description": "Filter by reaction emoji",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "weight",
"description": "Filter by weight",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
}
],
"interfaces": null,
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "Note",
......@@ -16,13 +16,15 @@ module Resolvers
return Epic.none unless group.present?
return unless ::Feature.enabled?(:boards_with_swimlanes, group)
Epic.for_ids(board_epic_ids(args[:issue_filters]))
Epic.for_ids(board_epic_ids(args[:issue_filters].to_h))
end
private
def board_epic_ids(issue_params)
params = issue_params.to_h.merge(all_lists: true, board_id: board.id)
params[:not] = params[:not].to_h if params[:not].present?
list_service = Boards::Issues::ListService.new(
board.resource_parent,
current_user,
......
......@@ -2,40 +2,49 @@
module Types
# rubocop: disable Graphql/AuthorizeTypes
class BoardEpicIssueInputType < BaseInputObject
graphql_name 'BoardEpicIssueInput'
class BoardEpicIssueInputBaseType < BaseInputObject
argument :label_name, GraphQL::STRING_TYPE.to_list_type,
required: false,
description: 'Label applied to issues'
description: 'Filter by label name'
argument :milestone_title, GraphQL::STRING_TYPE,
required: false,
description: 'Milestone applied to issues'
description: 'Filter by milestone title'
argument :assignee_username, GraphQL::STRING_TYPE.to_list_type,
required: false,
description: 'Username of a user assigned to issues'
description: 'Filter by assignee username'
argument :author_username, GraphQL::STRING_TYPE,
required: false,
description: 'Username of the issues author'
description: 'Filter by author username'
argument :release_tag, GraphQL::STRING_TYPE,
required: false,
description: 'Release applied to issues'
description: 'Filter by release tag'
argument :epic_id, GraphQL::STRING_TYPE,
required: false,
description: 'Epic ID applied to issues'
description: 'Filter by epic ID'
argument :my_reaction_emoji, GraphQL::STRING_TYPE,
required: false,
description: 'Reaction emoji applied to issues'
description: 'Filter by reaction emoji'
argument :weight, GraphQL::STRING_TYPE,
required: false,
description: 'Weight applied to issues'
description: 'Filter by weight'
end
class NegatedBoardEpicIssueInputType < BoardEpicIssueInputBaseType
end
class BoardEpicIssueInputType < BoardEpicIssueInputBaseType
graphql_name 'BoardEpicIssueInput'
argument :not, Types::NegatedBoardEpicIssueInputType,
required: false,
description: 'List of negated params. Warning: this argument is experimental and a subject to change in future'
end
# rubocop: enable Graphql/AuthorizeTypes
end
---
title: Allow using of negated filters in board epic issues GraphQL query
merge_request: 39089
author:
type: added
......@@ -13,11 +13,12 @@ RSpec.describe Resolvers::BoardGroupings::EpicsResolver do
let_it_be(:board) { create(:board, project: project) }
let_it_be(:group_board) { create(:board, group: group) }
let_it_be(:label) { create(:label, project: project, name: 'foo') }
let_it_be(:list) { create(:list, board: board, label: label) }
let_it_be(:label1) { create(:label, project: project, name: 'foo') }
let_it_be(:label2) { create(:label, project: project, name: 'bar') }
let_it_be(:list) { create(:list, board: board, label: label1) }
let_it_be(:issue1) { create(:issue, project: project, labels: [label]) }
let_it_be(:issue2) { create(:issue, project: project) }
let_it_be(:issue1) { create(:issue, project: project, labels: [label1]) }
let_it_be(:issue2) { create(:issue, project: project, labels: [label1, label2]) }
let_it_be(:issue3) { create(:issue, project: other_project) }
let_it_be(:epic1) { create(:epic, group: parent_group) }
......@@ -71,10 +72,21 @@ RSpec.describe Resolvers::BoardGroupings::EpicsResolver do
end
it 'finds only epics for issues matching issue filters' do
result = resolve_board_epics(group_board, { issue_filters: { label_name: label.title } })
result = resolve_board_epics(
group_board, { issue_filters: { label_name: label1.title, not: { label_name: label2.title } } })
expect(result).to match_array([epic1])
end
it 'accepts negated issue params' do
expect(Boards::Issues::ListService).to receive(:new).with(
group_board.resource_parent,
current_user,
{ all_lists: true, board_id: group_board.id, label_name: 'foo', not: { label_name: %w(foo bar) } }
).and_call_original
resolve_board_epics(group_board, { issue_filters: { label_name: 'foo', not: { label_name: %w(foo bar) } } })
end
end
end
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GitlabSchema.types['BoardEpicIssueInput'] do
it { expect(described_class.graphql_name).to eq('BoardEpicIssueInput') }
it 'exposes negated issue arguments' do
allowed_args = %w(labelName milestoneTitle assigneeUsername authorUsername
releaseTag epicId myReactionEmoji weight not)
expect(described_class.arguments.keys).to match_array(allowed_args)
expect(described_class.arguments['not'].type).to eq(Types::NegatedBoardEpicIssueInputType)
end
end
......@@ -21,7 +21,7 @@ RSpec.describe 'get list of boards' do
def board_epic_query(board)
epic_query = <<~EPIC
epics(issueFilters: {labelName: "#{label.title}"}) {
epics(issueFilters: {labelName: "#{label.title}", not: {authorUsername: "#{current_user.username}"}}) {
nodes {
id
title
......@@ -46,6 +46,7 @@ RSpec.describe 'get list of boards' do
issue2 = create(:issue, project: issue_project, labels: [label])
issue3 = create(:issue, project: issue_project)
issue4 = create(:issue, project: issue_project, labels: [label])
issue5 = create(:issue, project: issue_project, labels: [label], author: current_user)
epic1 = create(:epic, group: parent_group)
epic2 = create(:epic, group: parent_group)
epic3 = create(:epic, :closed, group: parent_group)
......@@ -53,6 +54,7 @@ RSpec.describe 'get list of boards' do
create(:epic_issue, issue: issue2, epic: epic1)
create(:epic_issue, issue: issue3, epic: epic2)
create(:epic_issue, issue: issue4, epic: epic3)
create(:epic_issue, issue: issue5, epic: epic2)
post_graphql(board_epic_query(board), current_user: current_user)
......
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