Welcome to mirror list, hosted at ThFree Co, Russian Federation.

escalatable.rb « incident_management « concerns « models « app - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 81eef50603ae5f05fb0673d31836693c02fbc886 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# frozen_string_literal: true

module IncidentManagement
  # Shared functionality for a `#status` field, representing
  # whether action is required. In EE, this corresponds
  # to paging functionality with EscalationPolicies.
  #
  # This module is only responsible for setting the status and
  # possible status-related timestamps (EX triggered_at/resolved_at)
  # for the implementing class. The relationships between these
  # values and other related timestamps/logic should be managed from
  # the object class itself. (EX Alert#ended_at = Alert#resolved_at)
  module Escalatable
    extend ActiveSupport::Concern

    STATUSES = {
      triggered: 0,
      acknowledged: 1,
      resolved: 2,
      ignored: 3
    }.freeze

    STATUS_DESCRIPTIONS = {
      triggered: 'Investigation has not started',
      acknowledged: 'Someone is actively investigating the problem',
      resolved: 'The problem has been addressed',
      ignored: 'No action will be taken'
    }.freeze

    included do
      validates :status, presence: true

      # Ascending sort order sorts statuses: Ignored > Resolved > Acknowledged > Triggered
      # Descending sort order sorts statuses: Triggered > Acknowledged > Resolved > Ignored
      # https://gitlab.com/gitlab-org/gitlab/-/issues/221242#what-is-the-expected-correct-behavior
      scope :order_status, -> (sort_order) { order(status: sort_order == :asc ? :desc : :asc) }

      state_machine :status, initial: :triggered do
        state :triggered, value: STATUSES[:triggered]

        state :acknowledged, value: STATUSES[:acknowledged]

        state :resolved, value: STATUSES[:resolved] do
          validates :resolved_at, presence: true
        end

        state :ignored, value: STATUSES[:ignored]

        state :triggered, :acknowledged, :ignored do
          validates :resolved_at, absence: true
        end

        event :trigger do
          transition any => :triggered
        end

        event :acknowledge do
          transition any => :acknowledged
        end

        event :resolve do
          transition any => :resolved
        end

        event :ignore do
          transition any => :ignored
        end

        before_transition to: [:triggered, :acknowledged, :ignored] do |escalatable, _transition|
          escalatable.resolved_at = nil
        end

        before_transition to: :resolved do |escalatable, transition|
          resolved_at = transition.args.first
          escalatable.resolved_at = resolved_at || Time.current
        end
      end

      class << self
        def status_value(name)
          state_machine_statuses[name]
        end

        def status_name(raw_status)
          state_machine_statuses.key(raw_status)
        end

        def status_names
          @status_names ||= state_machine_statuses.keys
        end

        private

        def state_machine_statuses
          @state_machine_statuses ||= state_machines[:status].states.to_h { |s| [s.name, s.value] }
        end
      end

      def status_event_for(status)
        self.class.state_machines[:status].events.transitions_for(self, to: status.to_s.to_sym).first&.event
      end
    end
  end
end

::IncidentManagement::Escalatable.prepend_mod