Class: WorkItem

Inherits:
Issue show all
Includes:
Gitlab::InternalEventsTracking, Gitlab::Utils::StrongMemoize, Import::HasImportSource
Defined in:
app/models/work_item.rb

Constant Summary collapse

COMMON_QUICK_ACTIONS_COMMANDS =
[
  :title, :reopen, :close, :tableflip, :shrug, :type, :promote_to, :checkin_reminder,
  :subscribe, :unsubscribe, :confidential, :award, :react, :move, :clone, :copy_metadata,
  :duplicate, :promote_to_incident, :board_move, :convert_to_ticket, :zoom, :remove_zoom
].freeze

Constants included from Import::HasImportSource

Import::HasImportSource::IMPORT_SOURCES

Constants inherited from Issue

Issue::AnyDueDate, Issue::DEFAULT_ISSUE_TYPE, Issue::DueDateStruct, Issue::DueNextMonthAndPreviousTwoWeeks, Issue::DueThisMonth, Issue::DueThisWeek, Issue::DueToday, Issue::DueTomorrow, Issue::IssueTypeOutOfSyncError, Issue::MAX_BRANCH_TEMPLATE, Issue::NoDueDate, Issue::Overdue, Issue::SORTING_PREFERENCE_FIELD, Issue::TYPES_FOR_BOARD_LIST, Issue::TYPES_FOR_LIST

Constants included from PgFullTextSearchable

PgFullTextSearchable::LONG_WORDS_REGEX, PgFullTextSearchable::TEXT_SEARCH_DICTIONARY, PgFullTextSearchable::TSVECTOR_MAX_LENGTH, PgFullTextSearchable::VERY_LONG_WORDS_WITH_AT_REGEX, PgFullTextSearchable::XML_TAG_REGEX

Constants included from ThrottledTouch

ThrottledTouch::TOUCH_INTERVAL

Constants included from Gitlab::RelativePositioning

Gitlab::RelativePositioning::IDEAL_DISTANCE, Gitlab::RelativePositioning::IllegalRange, Gitlab::RelativePositioning::InvalidPosition, Gitlab::RelativePositioning::IssuePositioningDisabled, Gitlab::RelativePositioning::MAX_GAP, Gitlab::RelativePositioning::MAX_POSITION, Gitlab::RelativePositioning::MIN_GAP, Gitlab::RelativePositioning::MIN_POSITION, Gitlab::RelativePositioning::NoSpaceLeft, Gitlab::RelativePositioning::START_POSITION, Gitlab::RelativePositioning::STEPS

Constants included from Noteable

Noteable::MAX_NOTES_LIMIT

Constants included from Issuable

Issuable::DESCRIPTION_LENGTH_MAX, Issuable::MAX_NUMBER_OF_ASSIGNEES_OR_REVIEWERS, Issuable::SEARCHABLE_FIELDS, Issuable::STATE_ID_MAP, Issuable::TITLE_LENGTH_MAX

Constants included from Taskable

Taskable::COMPLETED, Taskable::COMPLETE_PATTERN, Taskable::INCOMPLETE, Taskable::INCOMPLETE_PATTERN, Taskable::ITEM_PATTERN

Constants included from CacheMarkdownField

CacheMarkdownField::INVALIDATED_BY

Constants included from Redactable

Redactable::UNSUBSCRIBE_PATTERN

Constants included from Gitlab::SQL::Pattern

Gitlab::SQL::Pattern::MIN_CHARS_FOR_PARTIAL_MATCHING, Gitlab::SQL::Pattern::REGEX_QUOTED_TERM

Constants included from AtomicInternalId

AtomicInternalId::MissingValueError

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 Noteable

#system_note_timestamp

Attributes included from Transitionable

#transitioning

Attributes included from Importable

#importing, #user_contributions

Attributes included from CacheMarkdownField

#skip_markdown_cache_validation

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Import::HasImportSource

#imported?

Methods included from Gitlab::InternalEventsTracking

#track_internal_event

Methods inherited from Issue

#==, #allow_possible_spam?, alternative_reference_prefix_without_postfix, #as_json, #autoclose_by_merged_closing_merge_request?, #banzai_render_context, #blocked_for_repositioning?, #can_be_worked_on?, #can_move?, #check_repositioning_allowed!, #clear_closure_reason_references, column_order_id_asc, column_order_relative_position, #design_collection, #discussions_rendered_on_frontend?, #duplicated?, #email_participants_emails, #email_participants_emails_downcase, #ensure_work_item_description, #epic_work_item?, #expire_etag_cache, #from_service_desk?, full_search, #gfm_reference, #group_epic_work_item?, #group_level?, #has_widget?, #hidden?, #hook_attrs, in_namespaces_with_cte, #invalidate_project_counter_caches, #issue_assignee_user_ids, #issue_link_type, #issue_type, #issuing_parent_id, #merge_requests_count, #moved?, #next_object_by_relative_position, order_by_relative_position, order_upvotes_asc, order_upvotes_desc, participant_includes, #previous_updated_at, project_foreign_key, #real_time_notes_enabled?, reference_postfix, reference_prefix, reference_valid?, #related_issues, relative_positioning_parent_column, #relative_positioning_parent_projects, relative_positioning_query_base, #relocation_target, #require_legacy_views?, #resource_parent, #show_as_work_item?, simple_sorts, #skip_metrics?, sort_by_attribute, #source_project, #suggested_branch_name, supported_keyset_orderings, #supports_assignee?, #supports_confidentiality?, #supports_move_and_clone?, #supports_recaptcha?, #to_branch_name, to_branch_name, #to_reference, #to_work_item_global_id, #update_upvotes_count, #use_work_item_url?, with_accessible_sub_namespace_ids_cte, #work_item_type_with_default

Methods included from Gitlab::Utils::Override

#extended, extensions, #included, #method_added, #override, #prepended, #queue_verification, verify!

Methods included from PgFullTextSearchable

#update_search_data!

Methods included from IssueAvailableFeatures

#issue_type_supports?

Methods included from Presentable

#present

Methods included from ThrottledTouch

#touch

Methods included from TimeTrackable

#clear_memoized_total_time_spent, #human_time_change, #human_time_estimate, #human_total_time_spent, #reload, #reset, #set_time_estimate_default_value, #spend_time, #time_change, #time_estimate, #time_estimate=, #total_time_spent

Methods included from RelativePositioning

#check_repositioning_allowed!, #could_not_move, #exclude_self, #model_class, #move_after, #move_before, #move_between, #move_to_end, #move_to_start, mover, #next_object_by_relative_position, #relative_positioning_scoped_items, #reset_relative_position, #update_relative_siblings

Methods included from Gitlab::RelativePositioning

range

Methods included from FasterCacheKeys

#cache_key

Methods included from Spammable

#allow_possible_spam?, #check_for_spam, #check_for_spam?, #clear_spam_flags!, #invalidate_if_spam, #needs_recaptcha!, #recaptcha_error!, #render_recaptcha?, #spam, #spam!, #spam_description, #spam_title, #spammable_attribute_changed?, #spammable_entity_type, #spammable_text, #submittable_as_spam?, #submittable_as_spam_by?, #supports_recaptcha?, #unrecoverable_spam_error!

Methods included from Referable

#referable_inspect, #reference_link_text, #to_reference, #to_reference_base

Methods included from Noteable

#after_note_created, #after_note_destroyed, #base_class_name, #broadcast_notes_changed, #capped_notes_count, #commenters, #creatable_note_email_address, #discussion_ids_relation, #discussion_notes, #discussion_root_note_ids, #discussions, #discussions_can_be_resolved_by?, #discussions_rendered_on_frontend?, #discussions_resolvable?, #discussions_resolved?, #discussions_to_be_resolved, #grouped_diff_discussions, #has_any_diff_note_positions?, #human_class_name, #lockable?, #preloads_discussion_diff_highlighting?, #real_time_notes_enabled?, #resolvable_discussions, #supports_creating_notes_by_email?, #supports_discussions?, #supports_replying_to_individual_notes?, #supports_resolvable_notes?, #supports_suggestion?

Methods included from Issuable

#allows_scoped_labels?, #assignee?, #assignee_list, #assignee_or_author?, #assignee_username_list, #can_move?, #card_attributes, #draftless_title_changed, #first_contribution?, #hook_association_changes, #hook_reviewer_changes, #label_names, #labels_array, #labels_hook_attrs, #notes_for_participants, #notes_with_associations, #old_assignees, #old_escalation_status, #old_labels, #old_severity, #old_target_branch, #old_time_change, #old_total_time_spent, #open?, #overdue?, #read_ability_for, #resource_parent, #reviewers_hook_attrs, #state, #state=, #subscribed_without_subscriptions?, #supports_health_status?, #to_ability_name, #to_hook_data, #updated_tasks, #user_notes_count

Methods included from ReportableChanges

#as_json, #changes_applied, #clear_changes_information, #reload, #reportable_changes

Methods included from Exportable

#exportable_association?, #restricted_associations, #to_authorized_json

Methods included from AfterCommitQueue

#run_after_commit, #run_after_commit_or_now

Methods included from Editable

#edited?, #last_edited_by

Methods included from Transitionable

#disable_transitioning, #enable_transitioning, #transitioning?

Methods included from Taskable

#complete_task_list_item_count, get_tasks, get_updated_tasks, #task_completion_status, #task_list_items, #task_status, #task_status_short, #tasks?

Methods included from Awardable

#awarded_emoji?, #downvotes, #emoji_awardable?, #grouped_awards, #upvotes, #user_authored?, #user_can_award?

Methods included from StripAttribute

#strip_attributes!

Methods included from Subscribable

#lazy_subscription, #set_subscription, #subscribe, #subscribed?, #subscribed_without_subscriptions?, #subscribers, #toggle_subscription, #unsubscribe

Methods included from Milestoneable

#milestone_available?, #supports_milestone?

Methods included from Mentionable

#all_references, #create_cross_references!, #create_new_cross_references!, #directly_addressed_users, #extractors, #gfm_reference, #local_reference, #matches_cross_reference_regex?, #mentioned_users, #referenced_group_users, #referenced_groups, #referenced_mentionables, #referenced_projects, #referenced_users, #user_mention_class, #user_mention_identifier

Methods included from Participable

#participant?, #participants, #visible_participants

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?, #store_mentions_after_commit?, #updated_cached_html_for

Methods included from Gitlab::SQL::Pattern

split_query_to_search_terms

Methods included from IidRoutes

#to_param

Methods included from AtomicInternalId

group_init, #internal_id_read_scope, #internal_id_scope_attrs, #internal_id_scope_usage, namespace_init, project_init, scope_attrs, scope_usage

Methods inherited from ApplicationRecord

===, cached_column_list, #create_or_load_association, current_transaction, declarative_enum, default_select_columns, delete_all_returning, #deleted_from_database?, id_in, id_not_in, iid_in, nullable_column?, 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 Organizations::Sharding

#sharding_organization

Methods included from ResetOnColumnErrors

#reset_on_union_error, #reset_on_unknown_attribute_error

Methods included from Gitlab::SensitiveSerializableHash

#serializable_hash

Class Method Details

.alternative_reference_prefix_with_postfixObject



127
128
129
# File 'app/models/work_item.rb', line 127

def alternative_reference_prefix_with_postfix
  '[work_item:'
end

.assignee_association_nameObject



112
113
114
# File 'app/models/work_item.rb', line 112

def assignee_association_name
  'issue'
end

.find_by_namespace_and_iid!(namespace, iid) ⇒ Object



108
109
110
# File 'app/models/work_item.rb', line 108

def find_by_namespace_and_iid!(namespace, iid)
  find_by!(namespace: namespace, iid: iid)
end

.find_on_namespaces(ids:, resource_parent:) ⇒ Object



249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
# File 'app/models/work_item.rb', line 249

def find_on_namespaces(ids:, resource_parent:)
  return none if resource_parent.nil?

  group_namespaces = resource_parent.self_and_descendants.select(:id) if resource_parent.is_a?(Group)

  project_namespaces =
    if resource_parent.is_a?(Project)
      Project.id_in(resource_parent)
    else
      resource_parent.all_projects
    end.select('projects.project_namespace_id as id')

  namespaces = Namespace.from_union(
    [group_namespaces, project_namespaces].compact,
    remove_duplicates: false
  )

  Gitlab::SQL::CTE.new(:work_item_ids_cte, id_in(ids))
    .apply_to(all)
    .in_namespaces_with_cte(namespaces)
    .includes(:work_item_type)
end


153
154
155
156
157
158
159
# File 'app/models/work_item.rb', line 153

def link_reference_pattern
  @link_reference_pattern ||= project_or_group_link_reference_pattern(
    'work_items',
    namespace_reference_pattern,
    Gitlab::Regex.work_item
  )
end

.linked_items_for(target_ids, preload: nil, link_type: nil) ⇒ Object



202
203
204
205
206
207
208
209
210
211
212
213
# File 'app/models/work_item.rb', line 202

def linked_items_for(target_ids, preload: nil, link_type: nil)
  select_query =
    select('issues.*,
            issue_links.id AS issue_link_id,
            issue_links.link_type AS issue_link_type_value,
            issue_links.target_id AS issue_link_source_id,
            issue_links.source_id AS issue_link_target_id,
            issue_links.created_at AS issue_link_created_at,
            issue_links.updated_at AS issue_link_updated_at')

  ordered_linked_items(select_query, ids: target_ids, link_type: link_type, preload: preload)
end

.linked_items_keyset_orderObject



190
191
192
193
194
195
196
197
198
199
200
# File 'app/models/work_item.rb', line 190

def linked_items_keyset_order
  ::Gitlab::Pagination::Keyset::Order.build(
    [
      ::Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
        attribute_name: 'issue_link_id',
        column_expression: IssueLink.arel_table[:id],
        order_expression: IssueLink.arel_table[:id].desc,
        nullable: :not_nullable
      )
    ])
end

.namespace_reference_patternObject



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

def namespace_reference_pattern
  %r{
    (?<!#{Gitlab::PathRegex::PATH_START_CHAR})
    ((?<group_or_project_namespace>#{Gitlab::PathRegex::FULL_NAMESPACE_FORMAT_REGEX}))
  }xo
end

.non_widgetsObject



226
227
228
# File 'app/models/work_item.rb', line 226

def non_widgets
  [:pending_escalations]
end

.ordered_linked_items(select_query, ids: [], link_type: nil, preload: nil) ⇒ Object



230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
# File 'app/models/work_item.rb', line 230

def ordered_linked_items(select_query, ids: [], link_type: nil, preload: nil)
  type_condition =
    if link_type == WorkItems::RelatedWorkItemLink::TYPE_RELATES_TO
      " AND issue_links.link_type = #{WorkItems::RelatedWorkItemLink.link_types[link_type]}"
    else
      ""
    end

  query_ids = sanitize_sql_array(['?', Array.wrap(ids)])

  select_query
    .joins("INNER JOIN issue_links ON
      (issue_links.source_id = issues.id AND issue_links.target_id IN (#{query_ids})#{type_condition})
      OR
      (issue_links.target_id = issues.id AND issue_links.source_id IN (#{query_ids})#{type_condition})")
    .preload(preload)
    .reorder(linked_items_keyset_order)
end

.reference_patternObject



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'app/models/work_item.rb', line 131

def reference_pattern
  prefix_with_postfix = alternative_reference_prefix_with_postfix
  if prefix_with_postfix.empty?
    @reference_pattern ||= %r{
    (?:
      (#{namespace_reference_pattern})?#{Regexp.escape(reference_prefix)} |
      #{Regexp.escape(alternative_reference_prefix_without_postfix)}
    )#{Gitlab::Regex.work_item}
  }x
  else
    %r{
    ((?:
      (#{namespace_reference_pattern})?#{Regexp.escape(reference_prefix)} |
      #{alternative_reference_prefix_without_postfix}
    )#{Gitlab::Regex.work_item}) |
    ((?:
      #{Regexp.escape(prefix_with_postfix)}(#{namespace_reference_pattern}/)?
    )#{Gitlab::Regex.work_item(reference_postfix)})
  }x
  end
end


216
217
218
# File 'app/models/work_item.rb', line 216

def related_link_class
  WorkItems::RelatedWorkItemLink
end

.sync_callback_class(association_name) ⇒ Object



220
221
222
223
224
# File 'app/models/work_item.rb', line 220

def sync_callback_class(association_name)
  ::WorkItems::DataSync::NonWidgets.const_get(association_name.to_s.camelcase, false)
rescue NameError
  nil
end

.test_reports_join_columnObject



116
117
118
# File 'app/models/work_item.rb', line 116

def test_reports_join_column
  'issues.id'
end

.work_item_children_keyset_order(_work_item) ⇒ Object



184
185
186
187
188
# File 'app/models/work_item.rb', line 184

def work_item_children_keyset_order(_work_item)
  keyset_order = work_item_children_keyset_order_config

  keyset_order.apply_cursor_conditions(joins(:parent_link)).reorder(keyset_order)
end

.work_item_children_keyset_order_configObject



161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'app/models/work_item.rb', line 161

def work_item_children_keyset_order_config
  Gitlab::Pagination::Keyset::Order.build(
    [
      Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
        attribute_name: 'state_id',
        column_expression: WorkItem.arel_table[:state_id],
        order_expression: WorkItem.arel_table[:state_id].asc
      ),
      Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
        attribute_name: 'parent_link_relative_position',
        column_expression: WorkItems::ParentLink.arel_table[:relative_position],
        order_expression: WorkItems::ParentLink.arel_table[:relative_position].asc.nulls_last,
        add_to_projections: true,
        nullable: :nulls_last
      ),
      Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
        attribute_name: 'work_item_id',
        order_expression: WorkItems::ParentLink.arel_table['work_item_id'].asc
      )
    ]
  )
end

Instance Method Details

#ancestorsObject



338
339
340
# File 'app/models/work_item.rb', line 338

def ancestors
  hierarchy.ancestors(hierarchy_order: :asc)
end

#create_dates_source_from_current_datesObject



273
274
275
# File 'app/models/work_item.rb', line 273

def create_dates_source_from_current_dates
  create_dates_source(date_source_attributes_from_current_dates)
end

#custom_notification_target_nameObject



292
293
294
295
296
297
# File 'app/models/work_item.rb', line 292

def custom_notification_target_name
  # Also use `work_item` for issues.
  # Custom work_item notifications are mapped back to the issue category
  # until we properly transitioned the categories to work items.
  'work_item'
end

#date_source_attributes_from_current_datesObject



277
278
279
280
281
282
283
284
285
286
# File 'app/models/work_item.rb', line 277

def date_source_attributes_from_current_dates
  {
    due_date: due_date,
    start_date: start_date,
    start_date_is_fixed: due_date.present? || start_date.present?,
    due_date_is_fixed: due_date.present? || start_date.present?,
    start_date_fixed: start_date,
    due_date_fixed: due_date
  }
end

#descendantsObject



342
343
344
# File 'app/models/work_item.rb', line 342

def descendants
  hierarchy.descendants
end

#due_dateObject



412
413
414
# File 'app/models/work_item.rb', line 412

def due_date
  dates_source&.due_date || read_attribute(:due_date)
end

#get_widget(type) ⇒ Object

Returns widget object if available type parameter can be a symbol, for example, :description.



322
323
324
325
326
327
328
# File 'app/models/work_item.rb', line 322

def get_widget(type)
  strong_memoize_with(type) do
    break unless widget_definitions.key?(type.to_sym)

    widget_definitions[type].build_widget(self)
  end
end

#linked_items_countObject



398
399
400
# File 'app/models/work_item.rb', line 398

def linked_items_count
  linked_work_items(authorize: false).size
end

#linked_work_items(current_user = nil, authorize: true, preload: nil, link_type: nil) ⇒ Object



382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
# File 'app/models/work_item.rb', line 382

def linked_work_items(current_user = nil, authorize: true, preload: nil, link_type: nil)
  return [] if new_record?

  linked_items =
    self.class.ordered_linked_items(linked_issues_select, ids: id, link_type: link_type, preload: preload)

  return linked_items unless authorize

  cross_project_filter = ->(work_items) { work_items.where(project: project) }
  Ability.work_items_readable_by_user(
    linked_items,
    current_user,
    filters: { read_cross_project: cross_project_filter }
  )
end

#max_depth_reached?(child_type) ⇒ Boolean

Returns:

  • (Boolean)


420
421
422
423
424
425
426
427
428
429
430
431
432
# File 'app/models/work_item.rb', line 420

def max_depth_reached?(child_type)
  restriction = ::WorkItems::TypesFramework::SystemDefined::HierarchyRestriction.find_by(
    parent_type_id: work_item_type_id,
    child_type_id: child_type.id
  )
  return false unless restriction&.maximum_depth

  if work_item_type_id == child_type.id
    same_type_base_and_ancestors.count >= restriction.maximum_depth
  else
    hierarchy(different_type_id: child_type.id).base_and_ancestors.count >= restriction.maximum_depth
  end
end

#noteable_target_type_nameObject



288
289
290
# File 'app/models/work_item.rb', line 288

def noteable_target_type_name
  'issue'
end

#same_type_base_and_ancestorsObject



346
347
348
# File 'app/models/work_item.rb', line 346

def same_type_base_and_ancestors
  hierarchy(same_type: true).base_and_ancestors(hierarchy_order: :asc)
end

#same_type_descendants_depthObject



350
351
352
# File 'app/models/work_item.rb', line 350

def same_type_descendants_depth
  hierarchy(same_type: true).max_descendants_depth.to_i
end

#start_dateObject



416
417
418
# File 'app/models/work_item.rb', line 416

def start_date
  dates_source&.start_date || read_attribute(:start_date)
end

#supported_quick_action_commandsObject



354
355
356
357
358
# File 'app/models/work_item.rb', line 354

def supported_quick_action_commands
  commands_for_widgets = work_item_type.widget_classes(resource_parent).flat_map(&:quick_action_commands).uniq

  COMMON_QUICK_ACTIONS_COMMANDS + commands_for_widgets
end

#supports_parent?Boolean

Returns:

  • (Boolean)


406
407
408
409
410
# File 'app/models/work_item.rb', line 406

def supports_parent?
  return false if work_item_type.issue?

  hierarchy_supports_parent?
end

#supports_time_tracking?Boolean

Returns:

  • (Boolean)


402
403
404
# File 'app/models/work_item.rb', line 402

def supports_time_tracking?
  work_item_type.supports_time_tracking?(resource_parent)
end

#todoable_target_type_nameObject

Todo: remove method after target_type cleanup See gitlab.com/gitlab-org/gitlab/-/issues/416009



301
302
303
# File 'app/models/work_item.rb', line 301

def todoable_target_type_name
  %w[Issue WorkItem]
end

#transform_quick_action_params(command_params) ⇒ Object

Widgets have a set of quick action params that they must process. Map them to widget_params so they can be picked up by widget services.



362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
# File 'app/models/work_item.rb', line 362

def transform_quick_action_params(command_params)
  common_params = command_params.dup
  widget_params = {}

  work_item_type.widget_classes(resource_parent)
        .filter { |widget| widget.respond_to?(:quick_action_params) }
        .each do |widget|
          widget.quick_action_params
            .filter { |param_name| common_params.key?(param_name) }
            .each do |param_name|
              widget_params[widget.api_symbol] ||= {}
              param_value = common_params.delete(param_name)

              widget_params[widget.api_symbol].merge!(widget.process_quick_action_param(param_name, param_value))
            end
        end

  { common: common_params, widgets: widget_params }
end

#widget_definitionsObject



330
331
332
333
334
335
# File 'app/models/work_item.rb', line 330

def widget_definitions
  work_item_type
    .widgets(resource_parent)
    .index_by(&:widget_type)
    .symbolize_keys
end

#widgets(except_types: [], only_types: nil) ⇒ Object

Raises:

  • (ArgumentError)


305
306
307
308
309
310
311
312
313
314
315
316
317
318
# File 'app/models/work_item.rb', line 305

def widgets(except_types: [], only_types: nil)
  raise ArgumentError, 'Only one filter is allowed' if only_types.present? && except_types.present?

  strong_memoize_with(:widgets, only_types, except_types) do
    except_types = Array.wrap(except_types)

    widget_definitions.keys.filter_map do |type|
      next if except_types.include?(type)
      next if only_types&.exclude?(type)

      get_widget(type)
    end
  end
end