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
|
# frozen_string_literal: true
module Issues
class CreateService < Issues::BaseService
include ResolveDiscussions
prepend RateLimitedService
include ::Services::ReturnServiceResponses
rate_limit key: :issues_create,
opts: { scope: [:project, :current_user, :external_author] }
def initialize(container:, current_user: nil, params: {}, build_service: nil, perform_spam_check: true)
@extra_params = params.delete(:extra_params) || {}
super(container: container, current_user: current_user, params: params)
@perform_spam_check = perform_spam_check
@build_service = build_service ||
BuildService.new(container: project, current_user: current_user, params: params)
end
def execute(skip_system_notes: false)
return error(_('Operation not allowed'), 403) unless @current_user.can?(authorization_action, container)
# We should not initialize the callback classes during the build service execution because these will be
# initialized when we call #create below
@issue = @build_service.execute(initialize_callbacks: false)
# issue_type and work_item_type are set in BuildService, so we can delete it from params, in later phase
# it can be set also from quick actions
[:issue_type, :work_item_type, :work_item_type_id].each { |attribute| params.delete(attribute) }
handle_move_between_ids(@issue)
@add_related_issue ||= params.delete(:add_related_issue)
filter_resolve_discussion_params
issue = create(@issue, skip_system_notes: skip_system_notes)
if issue.persisted?
success(issue: issue)
else
error(issue.errors.full_messages, 422, pass_back: { issue: issue })
end
end
def external_author
params[:external_author] # present when creating an issue using service desk (email: from)
end
def before_create(issue)
issue.check_for_spam(user: current_user, action: :create) if perform_spam_check
# current_user (defined in BaseService) is not available within run_after_commit block
user = current_user
issue.run_after_commit do
NewIssueWorker.perform_async(issue.id, user.id, issue.class.to_s)
Issues::PlacementWorker.perform_async(nil, issue.project_id)
# issue.namespace_id can point to either a project through project namespace or a group.
Onboarding::IssueCreatedWorker.perform_async(issue.namespace_id)
end
end
# Add new items to Issues::AfterCreateService if they can be performed in Sidekiq
def after_create(issue)
user_agent_detail_service.create
handle_add_related_issue(issue)
resolve_discussions_with_issue(issue)
handle_escalation_status_change(issue)
create_timeline_event(issue)
try_to_associate_contacts(issue)
super
end
def handle_changes(issue, options)
super
old_associations = options.fetch(:old_associations, {})
old_assignees = old_associations.fetch(:assignees, [])
handle_assignee_changes(issue, old_assignees)
end
def handle_assignee_changes(issue, old_assignees)
return if issue.assignees == old_assignees
create_assignee_note(issue, old_assignees)
Gitlab::ResourceEvents::AssignmentEventRecorder.new(parent: issue, old_assignees: old_assignees).record
end
def resolve_discussions_with_issue(issue)
return if discussions_to_resolve.empty?
Discussions::ResolveService.new(project, current_user,
one_or_more_discussions: discussions_to_resolve,
follow_up_issue: issue).execute
end
private
def authorization_action
:create_issue
end
attr_reader :perform_spam_check, :extra_params
def create_timeline_event(issue)
return unless issue.work_item_type&.incident?
IncidentManagement::TimelineEvents::CreateService.create_incident(issue, current_user)
end
def user_agent_detail_service
UserAgentDetailService.new(spammable: @issue, perform_spam_check: perform_spam_check)
end
def handle_add_related_issue(issue)
return unless @add_related_issue
IssueLinks::CreateService.new(issue, issue.author, { target_issuable: @add_related_issue }).execute
end
def try_to_associate_contacts(issue)
return unless issue.external_author
return unless current_user.can?(:set_issue_crm_contacts, issue)
contacts = [issue.external_author]
contacts.concat extra_params[:cc] unless extra_params[:cc].nil?
set_crm_contacts(issue, contacts)
end
end
end
Issues::CreateService.prepend_mod
|