Commit 0e9561ff authored by syasonik's avatar syasonik

Shorten error messages for escalation rule count and fix edge case

If the number of rules provided exceeds 10, the error message will
no longer be collected for each rule.

Also circumvents some odd behavior around updating the rules which results
in unexpected numbers of rules being saved.
parent fb9f861a
...@@ -4,8 +4,6 @@ module IncidentManagement ...@@ -4,8 +4,6 @@ module IncidentManagement
class EscalationRule < ApplicationRecord class EscalationRule < ApplicationRecord
self.table_name = 'incident_management_escalation_rules' self.table_name = 'incident_management_escalation_rules'
MAX_RULE_PER_POLICY_COUNT = 10
belongs_to :policy, class_name: 'EscalationPolicy', inverse_of: 'rules', foreign_key: 'policy_id' belongs_to :policy, class_name: 'EscalationPolicy', inverse_of: 'rules', foreign_key: 'policy_id'
belongs_to :oncall_schedule, class_name: 'OncallSchedule', inverse_of: 'rotations', foreign_key: 'oncall_schedule_id' belongs_to :oncall_schedule, class_name: 'OncallSchedule', inverse_of: 'rotations', foreign_key: 'oncall_schedule_id'
...@@ -18,16 +16,5 @@ module IncidentManagement ...@@ -18,16 +16,5 @@ module IncidentManagement
numericality: { only_integer: true, greater_than_or_equal_to: 0, less_than_or_equal_to: 24.hours } numericality: { only_integer: true, greater_than_or_equal_to: 0, less_than_or_equal_to: 24.hours }
validates :policy_id, uniqueness: { scope: [:oncall_schedule_id, :status, :elapsed_time_seconds], message: _('must have a unique schedule, status, and elapsed time') } validates :policy_id, uniqueness: { scope: [:oncall_schedule_id, :status, :elapsed_time_seconds], message: _('must have a unique schedule, status, and elapsed time') }
validate :rules_count_not_exceeded, on: :create, if: :policy
private
def rules_count_not_exceeded
# We need to add to the count if we aren't creating the rules at the same time as the policy.
rules_count = policy.new_record? ? policy.rules.size : policy.rules.size + 1
errors.add(:base, "cannot have more than #{MAX_RULE_PER_POLICY_COUNT} rules") if rules_count > MAX_RULE_PER_POLICY_COUNT
end
end end
end end
...@@ -3,10 +3,16 @@ ...@@ -3,10 +3,16 @@
module IncidentManagement module IncidentManagement
module EscalationPolicies module EscalationPolicies
class BaseService class BaseService
MAX_RULE_COUNT = 10
def allowed? def allowed?
user&.can?(:admin_incident_management_escalation_policy, project) user&.can?(:admin_incident_management_escalation_policy, project)
end end
def too_many_rules?
params[:rules_attributes] && params[:rules_attributes].size > MAX_RULE_COUNT
end
def invalid_schedules? def invalid_schedules?
params[:rules_attributes]&.any? { |attrs| attrs[:oncall_schedule]&.project != project } params[:rules_attributes]&.any? { |attrs| attrs[:oncall_schedule]&.project != project }
end end
...@@ -27,6 +33,10 @@ module IncidentManagement ...@@ -27,6 +33,10 @@ module IncidentManagement
error(_('Escalation policies must have at least one rule')) error(_('Escalation policies must have at least one rule'))
end end
def error_too_many_rules
error(_('Escalation policies may not have more than %{rule_count} rules') % { rule_count: MAX_RULE_COUNT })
end
def error_bad_schedules def error_bad_schedules
error(_('All escalations rules must have a schedule in the same project as the policy')) error(_('All escalations rules must have a schedule in the same project as the policy'))
end end
......
...@@ -21,6 +21,7 @@ module IncidentManagement ...@@ -21,6 +21,7 @@ module IncidentManagement
def execute def execute
return error_no_permissions unless allowed? return error_no_permissions unless allowed?
return error_no_rules if params[:rules_attributes].blank? return error_no_rules if params[:rules_attributes].blank?
return error_too_many_rules if too_many_rules?
return error_bad_schedules if invalid_schedules? return error_bad_schedules if invalid_schedules?
escalation_policy = project.incident_management_escalation_policies.create(params) escalation_policy = project.incident_management_escalation_policies.create(params)
......
...@@ -26,6 +26,7 @@ module IncidentManagement ...@@ -26,6 +26,7 @@ module IncidentManagement
def execute def execute
return error_no_permissions unless allowed? return error_no_permissions unless allowed?
return error_no_rules if empty_rules? return error_no_rules if empty_rules?
return error_too_many_rules if too_many_rules?
return error_bad_schedules if invalid_schedules? return error_bad_schedules if invalid_schedules?
reconcile_rules! reconcile_rules!
......
...@@ -20,13 +20,5 @@ RSpec.describe IncidentManagement::EscalationRule do ...@@ -20,13 +20,5 @@ RSpec.describe IncidentManagement::EscalationRule do
it { is_expected.to validate_presence_of(:elapsed_time_seconds) } it { is_expected.to validate_presence_of(:elapsed_time_seconds) }
it { is_expected.to validate_numericality_of(:elapsed_time_seconds).is_greater_than_or_equal_to(0).is_less_than_or_equal_to(24.hours) } it { is_expected.to validate_numericality_of(:elapsed_time_seconds).is_greater_than_or_equal_to(0).is_less_than_or_equal_to(24.hours) }
it { is_expected.to validate_uniqueness_of(:policy_id).scoped_to([:oncall_schedule_id, :status, :elapsed_time_seconds] ).with_message('must have a unique schedule, status, and elapsed time') } it { is_expected.to validate_uniqueness_of(:policy_id).scoped_to([:oncall_schedule_id, :status, :elapsed_time_seconds] ).with_message('must have a unique schedule, status, and elapsed time') }
it 'validates the number of rules' do
policy = create(:incident_management_escalation_policy, rule_count: 10)
rule = build(:incident_management_escalation_rule, policy: policy)
expect(rule).not_to be_valid
expect(rule.errors).to contain_exactly("cannot have more than #{described_class::MAX_RULE_PER_POLICY_COUNT} rules")
end
end end
end end
...@@ -71,6 +71,20 @@ RSpec.describe IncidentManagement::EscalationPolicies::CreateService do ...@@ -71,6 +71,20 @@ RSpec.describe IncidentManagement::EscalationPolicies::CreateService do
it_behaves_like 'error response', 'Escalation policies must have at least one rule' it_behaves_like 'error response', 'Escalation policies must have at least one rule'
end end
context 'too many rules are given' do
let(:rule_params) do
(0..10).map do |idx|
{
oncall_schedule: oncall_schedule,
elapsed_time_seconds: idx,
status: :acknowledged
}
end
end
it_behaves_like 'error response', 'Escalation policies may not have more than 10 rules'
end
context 'oncall schedule is blank' do context 'oncall schedule is blank' do
before do before do
rule_params[0][:oncall_schedule] = nil rule_params[0][:oncall_schedule] = nil
......
...@@ -148,6 +148,21 @@ RSpec.describe IncidentManagement::EscalationPolicies::UpdateService do ...@@ -148,6 +148,21 @@ RSpec.describe IncidentManagement::EscalationPolicies::UpdateService do
it_behaves_like 'error response', 'Escalation policies must have at least one rule' it_behaves_like 'error response', 'Escalation policies must have at least one rule'
end end
context 'when too many rules are given' do
let(:rule_params) { [*existing_rules_params, *new_rule_params] }
let(:new_rule_params) do
(0..9).map do |idx|
{
oncall_schedule: oncall_schedule,
elapsed_time_seconds: idx,
status: :acknowledged
}
end
end
it_behaves_like 'error response', 'Escalation policies may not have more than 10 rules'
end
context 'when the on-call schedule is not present on the rule' do context 'when the on-call schedule is not present on the rule' do
let(:rule_params) { [new_rule_params.except(:oncall_schedule)] } let(:rule_params) { [new_rule_params.except(:oncall_schedule)] }
......
...@@ -12970,6 +12970,9 @@ msgstr "" ...@@ -12970,6 +12970,9 @@ msgstr ""
msgid "Escalation policies" msgid "Escalation policies"
msgstr "" msgstr ""
msgid "Escalation policies may not have more than %{rule_count} rules"
msgstr ""
msgid "Escalation policies must have at least one rule" msgid "Escalation policies must have at least one rule"
msgstr "" msgstr ""
......
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