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 153 154 155 156 157 158 159 160 161 162 163
|
# frozen_string_literal: true
module Issues
class BaseService < ::IssuableBaseService
extend ::Gitlab::Utils::Override
include IncidentManagement::UsageData
include IssueTypeHelpers
EpicAssignmentError = Class.new(::ArgumentError)
def hook_data(issue, action, old_associations: {})
issue.to_hook_data(current_user, old_associations: old_associations, action: action)
end
def reopen_service
Issues::ReopenService
end
def close_service
Issues::CloseService
end
NO_REBALANCING_NEEDED = ((RelativePositioning::MIN_POSITION * 0.9999)..(RelativePositioning::MAX_POSITION * 0.9999))
def rebalance_if_needed(issue)
return unless issue
return if issue.relative_position.nil?
return if NO_REBALANCING_NEEDED.cover?(issue.relative_position)
Issues::RebalancingWorker.perform_async(nil, *issue.project.self_or_root_group_ids)
end
def execute_hooks(issue, action = 'open', old_associations: {})
issue_data = Gitlab::Lazy.new { hook_data(issue, action, old_associations: old_associations) }
hooks_scope = issue.confidential? ? :confidential_issue_hooks : :issue_hooks
issue.namespace.execute_hooks(issue_data, hooks_scope)
issue.namespace.execute_integrations(issue_data, hooks_scope)
execute_incident_hooks(issue, issue_data) if issue.work_item_type&.incident?
execute_group_mention_hooks(issue, issue_data) if action == 'open'
end
private
# overriding this because IssuableBaseService#constructor_container_arg returns { project: value }
# Issues::ReopenService constructor signature is different now, it takes container instead of project also
# IssuableBaseService#change_state dynamically picks one of the `Issues::ReopenService`, `Epics::ReopenService` or
# MergeRequests::ReopenService, so we need this method to return { }container: value } for Issues::ReopenService
def self.constructor_container_arg(value)
{ container: value }
end
def find_work_item_type_id(issue_type)
work_item_type = WorkItems::Type.default_by_type(issue_type)
work_item_type ||= WorkItems::Type.default_issue_type
work_item_type.id
end
def filter_params(issue)
super
params.delete(:issue_type) unless create_issue_type_allowed?(issue, params[:issue_type])
if params[:work_item_type].present? && !create_issue_type_allowed?(project, params[:work_item_type].base_type)
params.delete(:work_item_type)
end
moved_issue = params.delete(:moved_issue)
# Setting created_at, updated_at and iid is allowed only for admins and owners or
# when moving an issue as we preserve the original issue attributes except id and iid.
params.delete(:iid) if params[:iid].present? && !iid_param_allowed?
filter_timestamp_params unless moved_issue
# Only users with permission to handle error data can add it to issues
if params[:sentry_issue_attributes].present? && !current_user.can?(:update_sentry_issue, project)
params.delete(:sentry_issue_attributes)
end
issue.system_note_timestamp = params[:created_at] || params[:updated_at]
end
override :handle_move_between_ids
def handle_move_between_ids(issue)
issue.check_repositioning_allowed! if params[:move_between_ids]
super
rebalance_if_needed(issue)
end
def handle_escalation_status_change(issue)
return unless issue.supports_escalation?
if issue.escalation_status
::IncidentManagement::IssuableEscalationStatuses::AfterUpdateService.new(
issue,
current_user
).execute
else
::IncidentManagement::IssuableEscalationStatuses::CreateService.new(issue).execute
end
end
def issuable_for_positioning(id, positioning_scope)
return unless id
positioning_scope.find(id)
end
def create_assignee_note(issue, old_assignees)
SystemNoteService.change_issuable_assignees(
issue, issue.project, current_user, old_assignees)
end
# We can remove this code after proposal in
# https://gitlab.com/gitlab-org/gitlab/-/issues/367550#proposal is updated.
def execute_incident_hooks(issue, issue_data)
issue_data[:object_kind] = 'incident'
issue_data[:event_type] = 'incident'
issue.namespace.execute_integrations(issue_data, :incident_hooks)
end
def execute_group_mention_hooks(issue, issue_data)
return unless issue.instance_of?(Issue)
args = {
mentionable_type: 'Issue',
mentionable_id: issue.id,
hook_data: issue_data,
is_confidential: issue.confidential?
}
issue.run_after_commit_or_now do
Integrations::GroupMentionWorker.perform_async(args)
end
end
def update_project_counter_caches?(issue)
super || issue.confidential_changed?
end
def log_audit_event(issue, user, event_type, message)
# defined in EE
end
def iid_param_allowed?
current_user.can?(:set_issue_iid, project)
end
def filter_timestamp_params
timestamp_params = params.slice(:created_at, :updated_at).keys
return unless timestamp_params.any?
timestamp_params.each do |param|
params.delete(param) unless current_user.can?(:"set_issue_#{param}", project)
end
end
end
end
Issues::BaseService.prepend_mod_with('Issues::BaseService')
|