Commit 0b50926a authored by Sarah Yasonik's avatar Sarah Yasonik Committed by Michael Kozono

Allow issues to be sorted by escalation status

Adds ESCALATION_STATUS_ASC and ESCALATION_STATUS_DESC
options to issues query in GraphQL.
parent 37230728
......@@ -14,6 +14,15 @@ module Mutations
null: true,
description: 'User preferences after mutation.'
def ready?(**args)
if disabled_sort_value?(args)
raise Gitlab::Graphql::Errors::ArgumentError,
'Feature flag `incident_escalations` must be enabled to use this sort order.'
end
super
end
def resolve(**attributes)
user_preferences = current_user.user_preference
user_preferences.update(attributes)
......@@ -23,6 +32,14 @@ module Mutations
errors: errors_on_object(user_preferences)
}
end
private
def disabled_sort_value?(args)
return false unless [:escalation_status_asc, :escalation_status_desc].include?(args[:issues_sort])
Feature.disabled?(:incident_escalations)
end
end
end
end
......@@ -17,7 +17,8 @@ module Resolvers
NON_STABLE_CURSOR_SORTS = %i[priority_asc priority_desc
popularity_asc popularity_desc
label_priority_asc label_priority_desc
milestone_due_asc milestone_due_desc].freeze
milestone_due_asc milestone_due_desc
escalation_status_asc escalation_status_desc].freeze
def continue_issue_resolve(parent, finder, **args)
issues = Gitlab::Graphql::Loaders::IssuableLoader.new(parent, finder).batching_find_all { |q| apply_lookahead(q) }
......@@ -31,6 +32,13 @@ module Resolvers
end
end
def prepare_params(args, parent)
return unless [:escalation_status_asc, :escalation_status_desc].include?(args[:sort])
return if Feature.enabled?(:incident_escalations, parent)
args[:sort] = :created_desc # default for sort argument
end
private
def unconditional_includes
......
......@@ -84,6 +84,7 @@ module IssueResolverArguments
prepare_assignee_username_params(args)
prepare_release_tag_params(args)
prepare_params(args, parent) if defined?(prepare_params)
finder = IssuesFinder.new(current_user, args)
......
......@@ -14,6 +14,8 @@ module Types
value 'TITLE_DESC', 'Title by descending order.', value: :title_desc
value 'POPULARITY_ASC', 'Number of upvotes (awarded "thumbs up" emoji) by ascending order.', value: :popularity_asc
value 'POPULARITY_DESC', 'Number of upvotes (awarded "thumbs up" emoji) by descending order.', value: :popularity_desc
value 'ESCALATION_STATUS_ASC', 'Status from triggered to resolved. Defaults to `CREATED_DESC` if `incident_escalations` feature flag is disabled.', value: :escalation_status_asc
value 'ESCALATION_STATUS_DESC', 'Status from resolved to triggered. Defaults to `CREATED_DESC` if `incident_escalations` feature flag is disabled.', value: :escalation_status_desc
end
end
......
......@@ -125,6 +125,8 @@ class Issue < ApplicationRecord
scope :order_created_at_desc, -> { reorder(created_at: :desc) }
scope :order_severity_asc, -> { includes(:issuable_severity).order('issuable_severities.severity ASC NULLS FIRST') }
scope :order_severity_desc, -> { includes(:issuable_severity).order('issuable_severities.severity DESC NULLS LAST') }
scope :order_escalation_status_asc, -> { includes(:incident_management_issuable_escalation_status).order(::Gitlab::Database.nulls_last_order('incident_management_issuable_escalation_status.status')) }
scope :order_escalation_status_desc, -> { includes(:incident_management_issuable_escalation_status).order(::Gitlab::Database.nulls_last_order('incident_management_issuable_escalation_status.status', 'DESC')) }
scope :preload_associated_models, -> { preload(:assignees, :labels, project: :namespace) }
scope :with_web_entity_associations, -> { preload(:author, project: [:project_feature, :route, namespace: :route]) }
......@@ -327,6 +329,8 @@ class Issue < ApplicationRecord
when 'relative_position', 'relative_position_asc' then order_by_relative_position
when 'severity_asc' then order_severity_asc.with_order_id_desc
when 'severity_desc' then order_severity_desc.with_order_id_desc
when 'escalation_status_asc' then order_escalation_status_asc.with_order_id_desc
when 'escalation_status_desc' then order_escalation_status_desc.with_order_id_desc
else
super
end
......
......@@ -18298,6 +18298,8 @@ Values for sorting issues.
| <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. |
| <a id="issuesortdue_date_desc"></a>`DUE_DATE_DESC` | Due date by descending order. |
| <a id="issuesortescalation_status_asc"></a>`ESCALATION_STATUS_ASC` | Status from triggered to resolved. Defaults to `CREATED_DESC` if `incident_escalations` feature flag is disabled. |
| <a id="issuesortescalation_status_desc"></a>`ESCALATION_STATUS_DESC` | Status from resolved to triggered. Defaults to `CREATED_DESC` if `incident_escalations` feature flag is disabled. |
| <a id="issuesortlabel_priority_asc"></a>`LABEL_PRIORITY_ASC` | Label priority by ascending order. |
| <a id="issuesortlabel_priority_desc"></a>`LABEL_PRIORITY_DESC` | Label priority by descending order. |
| <a id="issuesortmilestone_due_asc"></a>`MILESTONE_DUE_ASC` | Milestone due date by ascending order. |
......@@ -522,11 +522,53 @@ RSpec.describe Resolvers::IssuesResolver do
end
end
context 'when sorting by escalation status' do
let_it_be(:project) { create(:project, :public) }
let_it_be(:triggered_incident) { create(:incident, :with_escalation_status, project: project) }
let_it_be(:issue_no_status) { create(:issue, project: project) }
let_it_be(:resolved_incident) do
create(:incident, :with_escalation_status, project: project)
.tap { |issue| issue.escalation_status.resolve }
end
it 'sorts issues ascending' do
issues = resolve_issues(sort: :escalation_status_asc).to_a
expect(issues).to eq([triggered_incident, resolved_incident, issue_no_status])
end
it 'sorts issues descending' do
issues = resolve_issues(sort: :escalation_status_desc).to_a
expect(issues).to eq([resolved_incident, triggered_incident, issue_no_status])
end
it 'sorts issues created_at' do
issues = resolve_issues(sort: :created_desc).to_a
expect(issues).to eq([resolved_incident, issue_no_status, triggered_incident])
end
context 'when incident_escalations feature flag is disabled' do
before do
stub_feature_flags(incident_escalations: false)
end
it 'defaults ascending status sort to created_desc' do
issues = resolve_issues(sort: :escalation_status_asc).to_a
expect(issues).to eq([resolved_incident, issue_no_status, triggered_incident])
end
it 'defaults descending status sort to created_desc' do
issues = resolve_issues(sort: :escalation_status_desc).to_a
expect(issues).to eq([resolved_incident, issue_no_status, triggered_incident])
end
end
end
context 'when sorting with non-stable cursors' do
%i[priority_asc priority_desc
popularity_asc popularity_desc
label_priority_asc label_priority_desc
milestone_due_asc milestone_due_desc].each do |sort_by|
milestone_due_asc milestone_due_desc
escalation_status_asc escalation_status_desc].each do |sort_by|
it "uses offset-pagination when sorting by #{sort_by}" do
resolved = resolve_issues(sort: sort_by)
......
......@@ -9,7 +9,7 @@ RSpec.describe GitlabSchema.types['IssueSort'] do
it 'exposes all the existing issue sort values' do
expect(described_class.values.keys).to include(
*%w[DUE_DATE_ASC DUE_DATE_DESC RELATIVE_POSITION_ASC SEVERITY_ASC SEVERITY_DESC]
*%w[DUE_DATE_ASC DUE_DATE_DESC RELATIVE_POSITION_ASC SEVERITY_ASC SEVERITY_DESC ESCALATION_STATUS_ASC ESCALATION_STATUS_DESC]
)
end
end
......@@ -238,6 +238,24 @@ RSpec.describe Issue do
end
end
context 'order by escalation status' do
let_it_be(:triggered_incident) { create(:incident_management_issuable_escalation_status, :triggered).issue }
let_it_be(:resolved_incident) { create(:incident_management_issuable_escalation_status, :resolved).issue }
let_it_be(:issue_no_status) { create(:issue) }
describe '.order_escalation_status_asc' do
subject { described_class.order_escalation_status_asc }
it { is_expected.to eq([triggered_incident, resolved_incident, issue_no_status]) }
end
describe '.order_escalation_status_desc' do
subject { described_class.order_escalation_status_desc }
it { is_expected.to eq([resolved_incident, triggered_incident, issue_no_status]) }
end
end
# TODO: Remove when NOT NULL constraint is added to the relationship
describe '#work_item_type' do
let(:issue) { create(:issue, :incident, project: reusable_project, work_item_type: nil) }
......
......@@ -28,6 +28,17 @@ RSpec.describe Mutations::UserPreferences::Update do
expect(current_user.user_preference.persisted?).to eq(true)
expect(current_user.user_preference.issues_sort).to eq(Types::IssueSortEnum.values[sort_value].value.to_s)
end
context 'when incident_escalations feature flag is disabled' do
let(:sort_value) { 'ESCALATION_STATUS_ASC' }
before do
stub_feature_flags(incident_escalations: false)
end
it_behaves_like 'a mutation that returns top-level errors',
errors: ['Feature flag `incident_escalations` must be enabled to use this sort order.']
end
end
context 'when user has existing preference' do
......@@ -45,5 +56,16 @@ RSpec.describe Mutations::UserPreferences::Update do
expect(current_user.user_preference.issues_sort).to eq(Types::IssueSortEnum.values[sort_value].value.to_s)
end
context 'when incident_escalations feature flag is disabled' do
let(:sort_value) { 'ESCALATION_STATUS_DESC' }
before do
stub_feature_flags(incident_escalations: false)
end
it_behaves_like 'a mutation that returns top-level errors',
errors: ['Feature flag `incident_escalations` must be enabled to use this sort order.']
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