Commit 3ab56073 authored by Sean Arnold's avatar Sean Arnold

Add escalation policy and escalation rule models

- Add tables and models
- Add model specs

Changelog: added
parent 1d48e365
---
title: Add Escalation policies and rule tables
merge_request: 60685
author:
type: added
# frozen_string_literal: true
class AddEscalationPolicies < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
POLICY_PROJECT_INDEX_NAME = 'index_on_project_escalation_policy'
UNIQUE_INDEX_NAME = 'index_on_project_id_escalation_policy_name_unique'
def up
create_table_with_constraints :incident_management_escalation_policies do |t|
t.references :project, index: { name: POLICY_PROJECT_INDEX_NAME }, null: false, foreign_key: { on_delete: :cascade }
t.text :name, null: false
t.text :description, null: true
t.text_limit :name, 72
t.text_limit :description, 160
t.index [:project_id, :name], unique: true, name: UNIQUE_INDEX_NAME
end
end
def down
drop_table :incident_management_escalation_policies
end
end
# frozen_string_literal: true
class AddEscalationRules < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
RULE_PROJECT_INDEX_NAME = 'index_on_project_escalation_rule'
RULE_SCHEDULE_INDEX_NAME = 'index_on_oncall_schedule_escalation_rule'
UNIQUENESS_INDEX_NAME = 'index_on_policy_schedule_status_elapsed_time_escalation_rules'
def change
create_table :incident_management_escalation_rules do |t|
t.belongs_to :policy, index: { name: RULE_PROJECT_INDEX_NAME }, null: false, foreign_key: { on_delete: :cascade, to_table: :incident_management_escalation_policies }
t.belongs_to :oncall_schedule, index: { name: RULE_SCHEDULE_INDEX_NAME }, null: false, foreign_key: { on_delete: :cascade, to_table: :incident_management_oncall_schedules }
t.integer :status, null: false, limit: 2
t.integer :elapsed_time, null: false, limit: 4
t.index [:policy_id, :oncall_schedule_id, :status, :elapsed_time], unique: true, name: UNIQUENESS_INDEX_NAME
end
end
end
5307ed6d22ba2575db0734eb8949b6987e58be27a845921b4943123adc2b9f41
\ No newline at end of file
f1a5f9a394c02544be99316d8e64464adeb4f9849875200e7e8be6fe826e5e62
\ No newline at end of file
......@@ -13623,6 +13623,41 @@ CREATE SEQUENCE in_product_marketing_emails_id_seq
ALTER SEQUENCE in_product_marketing_emails_id_seq OWNED BY in_product_marketing_emails.id;
CREATE TABLE incident_management_escalation_policies (
id bigint NOT NULL,
project_id bigint NOT NULL,
name text NOT NULL,
description text,
CONSTRAINT check_510b2a5258 CHECK ((char_length(description) <= 160)),
CONSTRAINT check_9a26365850 CHECK ((char_length(name) <= 72))
);
CREATE SEQUENCE incident_management_escalation_policies_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE incident_management_escalation_policies_id_seq OWNED BY incident_management_escalation_policies.id;
CREATE TABLE incident_management_escalation_rules (
id bigint NOT NULL,
policy_id bigint NOT NULL,
oncall_schedule_id bigint NOT NULL,
status smallint NOT NULL,
elapsed_time integer NOT NULL
);
CREATE SEQUENCE incident_management_escalation_rules_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE incident_management_escalation_rules_id_seq OWNED BY incident_management_escalation_rules.id;
CREATE TABLE incident_management_oncall_participants (
id bigint NOT NULL,
oncall_rotation_id bigint NOT NULL,
......@@ -19671,6 +19706,10 @@ ALTER TABLE ONLY import_failures ALTER COLUMN id SET DEFAULT nextval('import_fai
ALTER TABLE ONLY in_product_marketing_emails ALTER COLUMN id SET DEFAULT nextval('in_product_marketing_emails_id_seq'::regclass);
ALTER TABLE ONLY incident_management_escalation_policies ALTER COLUMN id SET DEFAULT nextval('incident_management_escalation_policies_id_seq'::regclass);
ALTER TABLE ONLY incident_management_escalation_rules ALTER COLUMN id SET DEFAULT nextval('incident_management_escalation_rules_id_seq'::regclass);
ALTER TABLE ONLY incident_management_oncall_participants ALTER COLUMN id SET DEFAULT nextval('incident_management_oncall_participants_id_seq'::regclass);
ALTER TABLE ONLY incident_management_oncall_rotations ALTER COLUMN id SET DEFAULT nextval('incident_management_oncall_rotations_id_seq'::regclass);
......@@ -20999,6 +21038,12 @@ ALTER TABLE ONLY in_product_marketing_emails
ALTER TABLE ONLY incident_management_oncall_shifts
ADD CONSTRAINT inc_mgmnt_no_overlapping_oncall_shifts EXCLUDE USING gist (rotation_id WITH =, tstzrange(starts_at, ends_at, '[)'::text) WITH &&);
ALTER TABLE ONLY incident_management_escalation_policies
ADD CONSTRAINT incident_management_escalation_policies_pkey PRIMARY KEY (id);
ALTER TABLE ONLY incident_management_escalation_rules
ADD CONSTRAINT incident_management_escalation_rules_pkey PRIMARY KEY (id);
ALTER TABLE ONLY incident_management_oncall_participants
ADD CONSTRAINT incident_management_oncall_participants_pkey PRIMARY KEY (id);
......@@ -23563,8 +23608,18 @@ CREATE INDEX index_on_namespaces_lower_name ON namespaces USING btree (lower((na
CREATE INDEX index_on_namespaces_lower_path ON namespaces USING btree (lower((path)::text));
CREATE INDEX index_on_oncall_schedule_escalation_rule ON incident_management_escalation_rules USING btree (oncall_schedule_id);
CREATE INDEX index_on_pages_metadata_not_migrated ON project_pages_metadata USING btree (project_id) WHERE ((deployed = true) AND (pages_deployment_id IS NULL));
CREATE UNIQUE INDEX index_on_policy_schedule_status_elapsed_time_escalation_rules ON incident_management_escalation_rules USING btree (policy_id, oncall_schedule_id, status, elapsed_time);
CREATE INDEX index_on_project_escalation_policy ON incident_management_escalation_policies USING btree (project_id);
CREATE INDEX index_on_project_escalation_rule ON incident_management_escalation_rules USING btree (policy_id);
CREATE UNIQUE INDEX index_on_project_id_escalation_policy_name_unique ON incident_management_escalation_policies USING btree (project_id, name);
CREATE INDEX index_on_projects_lower_path ON projects USING btree (lower((path)::text));
CREATE INDEX index_on_routes_lower_path ON routes USING btree (lower((path)::text));
......@@ -25864,6 +25919,9 @@ ALTER TABLE ONLY packages_build_infos
ALTER TABLE ONLY security_orchestration_policy_rule_schedules
ADD CONSTRAINT fk_rails_17ade83f17 FOREIGN KEY (security_orchestration_policy_configuration_id) REFERENCES security_orchestration_policy_configurations(id) ON DELETE CASCADE;
ALTER TABLE ONLY incident_management_escalation_rules
ADD CONSTRAINT fk_rails_17dbea07a6 FOREIGN KEY (policy_id) REFERENCES incident_management_escalation_policies(id) ON DELETE CASCADE;
ALTER TABLE ONLY clusters_applications_jupyter
ADD CONSTRAINT fk_rails_17df21c98c FOREIGN KEY (cluster_id) REFERENCES clusters(id) ON DELETE CASCADE;
......@@ -26794,6 +26852,9 @@ ALTER TABLE ONLY issues_prometheus_alert_events
ALTER TABLE ONLY merge_trains
ADD CONSTRAINT fk_rails_b374b5225d FOREIGN KEY (merge_request_id) REFERENCES merge_requests(id) ON DELETE CASCADE;
ALTER TABLE ONLY incident_management_escalation_rules
ADD CONSTRAINT fk_rails_b3c9c17bd4 FOREIGN KEY (oncall_schedule_id) REFERENCES incident_management_oncall_schedules(id) ON DELETE CASCADE;
ALTER TABLE ONLY application_settings
ADD CONSTRAINT fk_rails_b53e481273 FOREIGN KEY (custom_project_templates_group_id) REFERENCES namespaces(id) ON DELETE SET NULL;
......@@ -27040,6 +27101,9 @@ ALTER TABLE ONLY vulnerability_occurrence_identifiers
ALTER TABLE ONLY serverless_domain_cluster
ADD CONSTRAINT fk_rails_e59e868733 FOREIGN KEY (clusters_applications_knative_id) REFERENCES clusters_applications_knative(id) ON DELETE CASCADE;
ALTER TABLE ONLY incident_management_escalation_policies
ADD CONSTRAINT fk_rails_e5b513daa7 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY vulnerability_external_issue_links
ADD CONSTRAINT fk_rails_e5ba7f7b13 FOREIGN KEY (author_id) REFERENCES users(id) ON DELETE SET NULL;
# frozen_string_literal: true
module IncidentManagement
class EscalationPolicy < ApplicationRecord
self.table_name = 'incident_management_escalation_policies'
belongs_to :project
has_many :rules, class_name: 'EscalationRule', inverse_of: :policy, foreign_key: 'policy_id'
validates :name, presence: true, uniqueness: { scope: [:project_id] }, length: { maximum: 72 }
validates :description, length: { maximum: 160 }
validates :rules, presence: true
end
end
# frozen_string_literal: true
module IncidentManagement
class EscalationRule < ApplicationRecord
self.table_name = 'incident_management_escalation_rules'
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'
enum status: { acknowledged: 1, resolved: 2 }
validates :status, presence: true
validates :elapsed_time,
presence: true,
numericality: { only_integer: true, greater_than_or_equal_to: 1, less_than_or_equal_to: 24.hours }
validates :policy_id, uniqueness: { scope: [:oncall_schedule_id, :status, :elapsed_time] }
end
end
# frozen_string_literal: true
FactoryBot.define do
factory :incident_management_escalation_policy, class: 'IncidentManagement::EscalationPolicy' do
association :project
sequence :name do |n|
"EscalationPolicy #{n}"
end
description { 'Policy description' }
transient do
rule_count { 1 }
end
after(:build) do |policy, evaluator|
evaluator.rule_count.times do
policy.rules << build(:incident_management_escalation_rule, policy: policy)
end
end
end
end
# frozen_string_literal: true
FactoryBot.define do
factory :incident_management_escalation_rule, class: 'IncidentManagement::EscalationRule' do
association :policy, factory: :incident_management_escalation_policy
association :oncall_schedule, factory: :incident_management_oncall_schedule
status { IncidentManagement::EscalationRule.statuses[:acknowledged] }
elapsed_time { 5 }
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe IncidentManagement::EscalationPolicy do
let_it_be(:project) { create(:project) }
subject { build(:incident_management_escalation_policy) }
describe 'associations' do
it { is_expected.to belong_to(:project) }
it { is_expected.to have_many(:rules) }
end
describe 'validations' do
it { is_expected.to validate_presence_of(:name) }
it { is_expected.to validate_uniqueness_of(:name).scoped_to(:project_id) }
it { is_expected.to validate_length_of(:name).is_at_most(72) }
it { is_expected.to validate_length_of(:description).is_at_most(160) }
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe IncidentManagement::EscalationRule do
let_it_be(:policy) { create(:incident_management_escalation_policy) }
let_it_be(:schedule) { create(:incident_management_oncall_schedule, project: policy.project) }
subject { build(:incident_management_escalation_rule, policy: policy) }
describe 'associations' do
it { is_expected.to belong_to(:policy) }
it { is_expected.to belong_to(:oncall_schedule) }
end
describe 'validations' do
it { is_expected.to validate_presence_of(:status) }
it { is_expected.to validate_presence_of(:elapsed_time) }
it { is_expected.to validate_numericality_of(:elapsed_time).is_greater_than_or_equal_to(1).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] ) }
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