Class: WorkItems::Type

Inherits:
ApplicationRecord show all
Includes:
CacheMarkdownField, Gitlab::Utils::StrongMemoize, ReactiveCaching
Defined in:
app/models/work_items/type.rb

Constant Summary collapse

DEFAULT_TYPES_NOT_SEEDED =
Class.new(StandardError)
TYPE_NAMES =

type name is used in restrictions DB seeder to assure restrictions for default types are pre-filled

{
  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 =

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?

{
  issue: { name: TYPE_NAMES[:issue], icon_name: 'issue-type-issue', enum_value: 0, id: 1 },
  incident: { name: TYPE_NAMES[:incident], icon_name: 'issue-type-incident', enum_value: 1, id: 2 },
  test_case: { name: TYPE_NAMES[:test_case], icon_name: 'issue-type-test-case', enum_value: 2, id: 3 }, ## EE-only
  requirement: { name: TYPE_NAMES[:requirement], icon_name: 'issue-type-requirements', enum_value: 3, id: 4 }, ## EE
  task: { name: TYPE_NAMES[:task], icon_name: 'issue-type-task', enum_value: 4, id: 5 },
  objective: { name: TYPE_NAMES[:objective], icon_name: 'issue-type-objective', enum_value: 5, id: 6 }, ## EE-only
  key_result: { name: TYPE_NAMES[:key_result], icon_name: 'issue-type-keyresult', enum_value: 6, id: 7 }, ## EE-only
  epic: { name: TYPE_NAMES[:epic], icon_name: 'issue-type-epic', enum_value: 7, id: 8 }, ## EE-only
  ticket: { name: TYPE_NAMES[:ticket], icon_name: 'issue-type-issue', enum_value: 8, id: 9 }
}.freeze
CHANGEABLE_BASE_TYPES =

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.

%w[issue incident test_case].freeze
EE_BASE_TYPES =
%w[objective epic key_result requirement].freeze

Constants included from ReactiveCaching

ReactiveCaching::ExceededReactiveCacheLimit, ReactiveCaching::InvalidateReactiveCache, ReactiveCaching::WORK_TYPE

Constants included from CacheMarkdownField

CacheMarkdownField::INVALIDATED_BY

Constants inherited from ApplicationRecord

ApplicationRecord::MAX_PLUCK

Constants included from HasCheckConstraints

HasCheckConstraints::NOT_NULL_CHECK_PATTERN

Constants included from ResetOnColumnErrors

ResetOnColumnErrors::MAX_RESET_PERIOD

Instance Attribute Summary

Attributes included from CacheMarkdownField

#skip_markdown_cache_validation

Class Method Summary collapse

Instance Method Summary collapse

Methods included from CacheMarkdownField

#attribute_invalidated?, #banzai_render_context, #cached_html_for, #cached_html_up_to_date?, #can_cache_field?, #invalidated_markdown_cache?, #latest_cached_markdown_version, #mentionable_attributes_changed?, #mentioned_filtered_user_ids_for, #parent_user, #refresh_markdown_cache, #refresh_markdown_cache!, #rendered_field_content, #skip_project_check?, #store_mentions!, #store_mentions_after_commit?, #updated_cached_html_for

Methods inherited from ApplicationRecord

===, cached_column_list, #create_or_load_association, declarative_enum, default_select_columns, id_in, id_not_in, iid_in, nullable_column?, pluck_primary_key, primary_key_in, #readable_by?, safe_ensure_unique, safe_find_or_create_by, safe_find_or_create_by!, #to_ability_name, underscore, where_exists, where_not_exists, with_fast_read_statement_timeout, without_order

Methods included from ResetOnColumnErrors

#reset_on_union_error, #reset_on_unknown_attribute_error

Methods included from Gitlab::SensitiveSerializableHash

#serializable_hash

Class Method Details

.allowed_group_level_types(resource_parent) ⇒ Object

method overridden in EE to perform the corresponding checks for the Epic type



119
120
121
122
123
124
125
# File 'app/models/work_items/type.rb', line 119

def self.allowed_group_level_types(resource_parent)
  if Feature.enabled?(:create_group_level_work_items, resource_parent, type: :wip)
    base_types.keys.excluding('epic')
  else
    []
  end
end

.allowed_types_for_issuesObject



114
115
116
# File 'app/models/work_items/type.rb', line 114

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

.default_by_type(type) ⇒ Object



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'app/models/work_items/type.rb', line 94

def self.default_by_type(type)
  found_type = find_by(base_type: type)
  return found_type if found_type || !WorkItems::Type.base_types.key?(type.to_s)

  error_message = <<~STRING
    Default work item types have not been created yet. Make sure the DB has been seeded successfully.
    See related documentation in
    https://docs.gitlab.com/omnibus/settings/database.html#seed-the-database-fresh-installs-only

    If you have additional questions, you can ask in
    https://gitlab.com/gitlab-org/gitlab/-/issues/423483
  STRING

  raise DEFAULT_TYPES_NOT_SEEDED, error_message
end

.default_issue_typeObject



110
111
112
# File 'app/models/work_items/type.rb', line 110

def self.default_issue_type
  default_by_type(:issue)
end

Instance Method Details

#allowed_child_types(cache: false, authorize: false, resource_parent: nil) ⇒ Object



160
161
162
163
164
165
166
167
168
# File 'app/models/work_items/type.rb', line 160

def allowed_child_types(cache: false, authorize: false, resource_parent: nil)
  cached_data = cache ? with_reactive_cache { |query_data| query_data[:allowed_child_types_by_name] } : nil

  types = cached_data || allowed_child_types_by_name

  return types unless authorize

  authorized_types(types, resource_parent, :child)
end

#allowed_parent_types(cache: false, authorize: false, resource_parent: nil) ⇒ Object



170
171
172
173
174
175
176
177
178
# File 'app/models/work_items/type.rb', line 170

def allowed_parent_types(cache: false, authorize: false, resource_parent: nil)
  cached_data = cache ? with_reactive_cache { |query_data| query_data[:allowed_parent_types_by_name] } : nil

  types = cached_data || allowed_parent_types_by_name

  return types unless authorize

  authorized_types(types, resource_parent, :parent)
end

#calculate_reactive_cacheObject



148
149
150
151
152
153
# File 'app/models/work_items/type.rb', line 148

def calculate_reactive_cache
  {
    allowed_child_types_by_name: allowed_child_types_by_name,
    allowed_parent_types_by_name: allowed_parent_types_by_name
  }
end

#default_issue?Boolean

Returns:

  • (Boolean)


144
145
146
# File 'app/models/work_items/type.rb', line 144

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

#descendant_typesObject



180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'app/models/work_items/type.rb', line 180

def descendant_types
  descendant_types = []
  next_level_child_types = allowed_child_types(cache: true)

  loop do
    descendant_types += next_level_child_types

    # We remove types that we've already seen to avoid circular dependencies
    next_level_child_types = next_level_child_types.flat_map do |type|
      type.allowed_child_types(cache: true)
    end - descendant_types

    break if next_level_child_types.empty?
  end

  descendant_types
end

#supported_conversion_types(resource_parent, user) ⇒ Object



155
156
157
158
# File 'app/models/work_items/type.rb', line 155

def supported_conversion_types(resource_parent, user)
  type_names = supported_conversion_base_types(resource_parent, user) - [base_type]
  WorkItems::Type.by_type(type_names).order_by_name_asc
end

#supports_assignee?(resource_parent) ⇒ Boolean

Returns:

  • (Boolean)


136
137
138
# File 'app/models/work_items/type.rb', line 136

def supports_assignee?(resource_parent)
  widget_classes(resource_parent).include?(::WorkItems::Widgets::Assignees)
end

#supports_time_tracking?(resource_parent) ⇒ Boolean

Returns:

  • (Boolean)


140
141
142
# File 'app/models/work_items/type.rb', line 140

def supports_time_tracking?(resource_parent)
  widget_classes(resource_parent).include?(::WorkItems::Widgets::TimeTracking)
end

#widget_classes(resource_parent) ⇒ Object



132
133
134
# File 'app/models/work_items/type.rb', line 132

def widget_classes(resource_parent)
  widgets(resource_parent).map(&:widget_class)
end

#widgets(_resource_parent) ⇒ Object

resource_parent is used in EE



128
129
130
# File 'app/models/work_items/type.rb', line 128

def widgets(_resource_parent)
  enabled_widget_definitions.filter(&:widget_class)
end