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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
|
# 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
# 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'
}.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
}.freeze
WIDGETS_FOR_TYPE = {
issue: [
Widgets::Assignees,
Widgets::Labels,
Widgets::Description,
Widgets::Hierarchy,
Widgets::StartAndDueDate,
Widgets::Milestone,
Widgets::Notes
],
incident: [
Widgets::Description,
Widgets::Hierarchy,
Widgets::Notes
],
test_case: [
Widgets::Description,
Widgets::Notes
],
requirement: [
Widgets::Description,
Widgets::Notes
],
task: [
Widgets::Assignees,
Widgets::Labels,
Widgets::Description,
Widgets::Hierarchy,
Widgets::StartAndDueDate,
Widgets::Milestone,
Widgets::Notes
],
objective: [
Widgets::Assignees,
Widgets::Labels,
Widgets::Description,
Widgets::Hierarchy,
Widgets::Milestone,
Widgets::Notes
],
key_result: [
Widgets::Assignees,
Widgets::Labels,
Widgets::Description,
Widgets::Hierarchy,
Widgets::StartAndDueDate,
Widgets::Notes
]
}.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
WI_TYPES_WITH_CREATED_HEADER = %w[issue incident].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
before_validation :strip_whitespace
# 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.available_widgets
WIDGETS_FOR_TYPE.values.flatten.uniq
end
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
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('task', 'objective', 'key_result')
end
def default?
namespace.blank?
end
def widgets
WIDGETS_FOR_TYPE[base_type.to_sym]
end
private
def strip_whitespace
name&.strip!
end
end
end
WorkItems::Type.prepend_mod
|