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

type.rb « work_items « models « app - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 38416f6cb767f1b7b0371076824d406047120f20 (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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# frozen_string_literal: true

# Note: initial thinking behind `icon_name` is for it to do triple duty:
# 1. one of our svg icon names, such as `external-link` or a new one `bug`
# 2. if it's an absolute url, then url to a user uploaded icon/image
# 3. an emoji, with the format of `:smile:`
module WorkItems
  class Type < ApplicationRecord
    self.table_name = 'work_item_types'

    include CacheMarkdownField
    include ReactiveCaching

    self.reactive_cache_work_type = :no_dependency
    self.reactive_cache_refresh_interval = 10.minutes
    self.reactive_cache_lifetime = 1.hour

    # type name is used in restrictions DB seeder to assure restrictions for
    # default types are pre-filled
    TYPE_NAMES = {
      issue: 'Issue',
      incident: 'Incident',
      test_case: 'Test Case',
      requirement: 'Requirement',
      task: 'Task',
      objective: 'Objective',
      key_result: 'Key Result',
      epic: 'Epic',
      ticket: 'Ticket'
    }.freeze

    # Base types need to exist on the DB on app startup
    # This constant is used by the DB seeder
    # TODO - where to add new icon names created?
    BASE_TYPES = {
      issue: { name: TYPE_NAMES[:issue], icon_name: 'issue-type-issue', enum_value: 0 },
      incident: { name: TYPE_NAMES[:incident], icon_name: 'issue-type-incident', enum_value: 1 },
      test_case: { name: TYPE_NAMES[:test_case], icon_name: 'issue-type-test-case', enum_value: 2 }, ## EE-only
      requirement: { name: TYPE_NAMES[:requirement], icon_name: 'issue-type-requirements', enum_value: 3 }, ## EE-only
      task: { name: TYPE_NAMES[:task], icon_name: 'issue-type-task', enum_value: 4 },
      objective: { name: TYPE_NAMES[:objective], icon_name: 'issue-type-objective', enum_value: 5 }, ## EE-only
      key_result: { name: TYPE_NAMES[:key_result], icon_name: 'issue-type-keyresult', enum_value: 6 }, ## EE-only
      epic: { name: TYPE_NAMES[:epic], icon_name: 'issue-type-epic', enum_value: 7 }, ## EE-only
      ticket: { name: TYPE_NAMES[:ticket], icon_name: 'issue-type-issue', enum_value: 8 }
    }.freeze

    # A list of types user can change between - both original and new
    # type must be included in this list. This is needed for legacy issues
    # where it's possible to switch between issue and incident.
    CHANGEABLE_BASE_TYPES = %w[issue incident test_case].freeze

    cache_markdown_field :description, pipeline: :single_line

    enum base_type: BASE_TYPES.transform_values { |value| value[:enum_value] }

    belongs_to :namespace, optional: true
    has_many :work_items, class_name: 'Issue', foreign_key: :work_item_type_id, inverse_of: :work_item_type
    has_many :widget_definitions, foreign_key: :work_item_type_id, inverse_of: :work_item_type
    has_many :enabled_widget_definitions, -> { where(disabled: false) }, foreign_key: :work_item_type_id,
      inverse_of: :work_item_type, class_name: 'WorkItems::WidgetDefinition'
    has_many :child_restrictions, class_name: 'WorkItems::HierarchyRestriction', foreign_key: :parent_type_id,
      inverse_of: :parent_type
    has_many :allowed_child_types_by_name, -> { order_by_name_asc },
      through: :child_restrictions, class_name: 'WorkItems::Type',
      foreign_key: :child_type_id, source: :child_type

    before_validation :strip_whitespace
    after_save :clear_reactive_cache!

    # TODO: review validation rules
    # https://gitlab.com/gitlab-org/gitlab/-/issues/336919
    validates :name, presence: true
    validates :name, uniqueness: { case_sensitive: false, scope: [:namespace_id] }
    validates :name, length: { maximum: 255 }
    validates :icon_name, length: { maximum: 255 }

    scope :default, -> { where(namespace: nil) }
    scope :order_by_name_asc, -> { order(arel_table[:name].lower.asc) }
    scope :by_type, ->(base_type) { where(base_type: base_type) }

    def self.default_by_type(type)
      found_type = find_by(namespace_id: nil, base_type: type)
      return found_type if found_type

      Gitlab::DatabaseImporters::WorkItems::BaseTypeImporter.upsert_types
      Gitlab::DatabaseImporters::WorkItems::HierarchyRestrictionsImporter.upsert_restrictions
      Gitlab::DatabaseImporters::WorkItems::RelatedLinksRestrictionsImporter.upsert_restrictions
      find_by(namespace_id: nil, base_type: type)
    end

    def self.default_issue_type
      default_by_type(:issue)
    end

    def self.allowed_types_for_issues
      base_types.keys.excluding('objective', 'key_result', 'epic', 'ticket')
    end

    def default?
      namespace.blank?
    end

    def widgets
      enabled_widget_definitions.filter_map(&:widget_class)
    end

    def supports_assignee?
      widgets.include? ::WorkItems::Widgets::Assignees
    end

    def default_issue?
      name == WorkItems::Type::TYPE_NAMES[:issue]
    end

    def calculate_reactive_cache
      allowed_child_types_by_name
    end

    def allowed_child_types(cache: false)
      cached_data = cache ? with_reactive_cache { |query_data| query_data } : nil

      cached_data || allowed_child_types_by_name
    end

    private

    def strip_whitespace
      name&.strip!
    end
  end
end