Commit 552576ff authored by Eulyeon Ko's avatar Eulyeon Ko

Allow sort by blocking issues via GraphQL

- Allow sorting by blocking issues in ascending order
(adds an AR scope to Issue model).
- Allow sorting issue by blocking issues via GraphQL.
- Expose blocking_issues_count as blockingCount field
of 'issue' type.

Changelog: added
EE: true
parent 7bd32bfe
......@@ -8885,6 +8885,7 @@ Relationship between an epic and an issue.
| <a id="epicissueblocked"></a>`blocked` | [`Boolean!`](#boolean) | Indicates the issue is blocked. |
| <a id="epicissueblockedbycount"></a>`blockedByCount` | [`Int`](#int) | Count of issues blocking this issue. |
| <a id="epicissueblockedbyissues"></a>`blockedByIssues` | [`IssueConnection`](#issueconnection) | Issues blocking this issue. (see [Connections](#connections)) |
| <a id="epicissueblockingcount"></a>`blockingCount` | [`Int!`](#int) | Count of issues this issue is blocking. |
| <a id="epicissueclosedat"></a>`closedAt` | [`Time`](#time) | Timestamp of when the issue was closed. |
| <a id="epicissueconfidential"></a>`confidential` | [`Boolean!`](#boolean) | Indicates the issue is confidential. |
| <a id="epicissuecreatenoteemail"></a>`createNoteEmail` | [`String`](#string) | User specific email address for the issue. |
......@@ -9939,6 +9940,7 @@ Returns [`VulnerabilitySeveritiesCount`](#vulnerabilityseveritiescount).
| <a id="issueblocked"></a>`blocked` | [`Boolean!`](#boolean) | Indicates the issue is blocked. |
| <a id="issueblockedbycount"></a>`blockedByCount` | [`Int`](#int) | Count of issues blocking this issue. |
| <a id="issueblockedbyissues"></a>`blockedByIssues` | [`IssueConnection`](#issueconnection) | Issues blocking this issue. (see [Connections](#connections)) |
| <a id="issueblockingcount"></a>`blockingCount` | [`Int!`](#int) | Count of issues this issue is blocking. |
| <a id="issueclosedat"></a>`closedAt` | [`Time`](#time) | Timestamp of when the issue was closed. |
| <a id="issueconfidential"></a>`confidential` | [`Boolean!`](#boolean) | Indicates the issue is confidential. |
| <a id="issuecreatenoteemail"></a>`createNoteEmail` | [`String`](#string) | User specific email address for the issue. |
......@@ -14595,6 +14597,8 @@ Values for sorting issues.
| Value | Description |
| ----- | ----------- |
| <a id="issuesortblocking_issues_asc"></a>`BLOCKING_ISSUES_ASC` | Blocking issues count by ascending order. |
| <a id="issuesortblocking_issues_desc"></a>`BLOCKING_ISSUES_DESC` | Blocking issues count by descending order. |
| <a id="issuesortcreated_asc"></a>`CREATED_ASC` | Created at ascending order. |
| <a id="issuesortcreated_desc"></a>`CREATED_DESC` | Created at descending order. |
| <a id="issuesortdue_date_asc"></a>`DUE_DATE_ASC` | Due date by ascending order. |
......
......@@ -426,7 +426,7 @@ To set a WIP limit for a list:
1. Enter the maximum number of issues.
1. Press <kbd>Enter</kbd> to save.
## Blocked issues
## Blocked issues **(PREMIUM)**
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/34723) in GitLab 12.8.
> - [View blocking issues when hovering over blocked icon](https://gitlab.com/gitlab-org/gitlab/-/issues/210452) in GitLab 13.10.
......
......@@ -8,7 +8,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
You can sort a list of issues several ways, including by:
- Blocking
- Blocking (descending sort only) **(PREMIUM)**
- Created date
- Due date
- Label priority
......@@ -51,7 +51,7 @@ This ordering also affects [issue boards](../issue_board.md#how-gitlab-orders-is
Changing the order in an issue list changes the ordering in an issue board,
and vice versa.
## Sorting by blocking issues
## Sorting by blocking issues **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/34247/) in GitLab 13.7.
......
......@@ -12,6 +12,8 @@ module EE
value 'PUBLISHED_DESC', 'Published issues shown first.', value: :published_desc
value 'SLA_DUE_AT_ASC', 'Issues with earliest SLA due time shown first.', value: :sla_due_at_asc
value 'SLA_DUE_AT_DESC', 'Issues with latest SLA due time shown first.', value: :sla_due_at_desc
value 'BLOCKING_ISSUES_ASC', 'Blocking issues count by ascending order.', value: :blocking_issues_asc
value 'BLOCKING_ISSUES_DESC', 'Blocking issues count by descending order.', value: :blocking_issues_desc
end
end
end
......
......@@ -18,6 +18,9 @@ module EE
field :blocked, GraphQL::BOOLEAN_TYPE, null: false,
description: 'Indicates the issue is blocked.'
field :blocking_count, GraphQL::INT_TYPE, null: false,
description: 'Count of issues this issue is blocking.'
field :blocked_by_count, GraphQL::INT_TYPE, null: true,
description: 'Count of issues blocking this issue.'
......@@ -45,6 +48,10 @@ module EE
object.weight_available? ? object.weight : nil
end
def blocking_count
object.blocking_issues_count
end
def blocked
::Gitlab::Graphql::Aggregations::Issues::LazyBlockAggregate.new(context, object.id) do |count|
(count || 0) > 0
......
......@@ -21,6 +21,7 @@ module EE
# widget supporting custom issue types - see https://gitlab.com/gitlab-org/gitlab/-/issues/292035
include IssueWidgets::ActsLikeRequirement
scope :order_blocking_issues_asc, -> { reorder(blocking_issues_count: :asc) }
scope :order_blocking_issues_desc, -> { reorder(blocking_issues_count: :desc) }
scope :order_weight_desc, -> { reorder ::Gitlab::Database.nulls_last_order('weight', 'DESC') }
scope :order_weight_asc, -> { reorder ::Gitlab::Database.nulls_last_order('weight') }
......@@ -186,6 +187,7 @@ module EE
override :sort_by_attribute
def sort_by_attribute(method, excluded_labels: [])
case method.to_s
when 'blocking_issues_asc' then order_blocking_issues_asc.with_order_id_desc
when 'blocking_issues_desc' then order_blocking_issues_desc.with_order_id_desc
when 'weight', 'weight_asc' then order_weight_asc.with_order_id_desc
when 'weight_desc' then order_weight_desc.with_order_id_desc
......
......@@ -17,9 +17,9 @@ RSpec.describe Resolvers::IssuesResolver do
let_it_be(:iteration1) { create(:iteration, group: group, start_date: 2.weeks.ago, due_date: 1.week.ago) }
let_it_be(:current_iteration) { create(:iteration, group: group, start_date: Date.yesterday, due_date: 1.day.from_now) }
let_it_be(:issue1) { create :issue, project: project, epic: epic1, iteration: iteration1 }
let_it_be(:issue2) { create :issue, project: project, epic: epic2, weight: 1 }
let_it_be(:issue3) { create :issue, project: project, weight: 3, iteration: current_iteration }
let_it_be(:issue1) { create :issue, project: project, epic: epic1, iteration: iteration1, blocking_issues_count: 2 }
let_it_be(:issue2) { create :issue, project: project, epic: epic2, weight: 1, blocking_issues_count: 2 }
let_it_be(:issue3) { create :issue, project: project, weight: 3, iteration: current_iteration, blocking_issues_count: 4 }
let_it_be(:issue4) { create :issue, :published, project: project }
before do
......@@ -64,6 +64,18 @@ RSpec.describe Resolvers::IssuesResolver do
expect(resolve_issues(sort: :sla_due_at_desc).to_a).to eq [sla_due_last, sla_due_first]
end
end
context 'when sorting by blocking issues count (ties broken by id in desc order)' do
it 'sorts issues ascending', :aggregate_failures do
expect(issue1.id).to be < (issue2.id)
expect(resolve_issues(sort: :blocking_issues_asc).to_a).to eq [issue4, issue2, issue1, issue3]
end
it 'sorts issues descending', :aggregate_failures do
expect(issue1.id).to be < (issue2.id)
expect(resolve_issues(sort: :blocking_issues_desc).to_a).to eq [issue3, issue2, issue1, issue4]
end
end
end
describe 'filtering by iteration' do
......
......@@ -7,6 +7,7 @@ RSpec.describe GitlabSchema.types['Issue'] do
it { expect(described_class).to have_graphql_field(:iteration) }
it { expect(described_class).to have_graphql_field(:weight) }
it { expect(described_class).to have_graphql_field(:health_status) }
it { expect(described_class).to have_graphql_field(:blocking_count) }
it { expect(described_class).to have_graphql_field(:blocked) }
it { expect(described_class).to have_graphql_field(:blocked_by_count) }
it { expect(described_class).to have_graphql_field(:blocked_by_issues) }
......
......@@ -434,10 +434,17 @@ RSpec.describe Issue do
end
context 'by blocking issues' do
it 'orders by descending blocking issues count' do
issue_1 = create(:issue, blocking_issues_count: 3)
issue_2 = create(:issue, blocking_issues_count: 2)
let_it_be(:issue_1) { create(:issue, blocking_issues_count: 3) }
let_it_be(:issue_2) { create(:issue, blocking_issues_count: 1) }
it 'orders by ascending blocking issues count', :aggregate_failures do
results = described_class.sort_by_attribute('blocking_issues_asc')
expect(results.first).to eq(issue_2)
expect(results.second).to eq(issue_1)
end
it 'orders by descending blocking issues count', :aggregate_failures do
results = described_class.sort_by_attribute('blocking_issues_desc')
expect(results.first).to eq(issue_1)
......
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