1# frozen_string_literal: true 2 3module Issues 4 class CreateService < Issues::BaseService 5 include ResolveDiscussions 6 prepend RateLimitedService 7 8 rate_limit key: :issues_create, 9 opts: { scope: [:project, :current_user, :external_author] } 10 11 # NOTE: For Issues::CreateService, we require the spam_params and do not default it to nil, because 12 # spam_checking is likely to be necessary. However, if there is not a request available in scope 13 # in the caller (for example, an issue created via email) and the required arguments to the 14 # SpamParams constructor are not otherwise available, spam_params: must be explicitly passed as nil. 15 def initialize(project:, current_user: nil, params: {}, spam_params:) 16 super(project: project, current_user: current_user, params: params) 17 @spam_params = spam_params 18 end 19 20 def execute(skip_system_notes: false) 21 @issue = BuildService.new(project: project, current_user: current_user, params: params).execute 22 23 filter_resolve_discussion_params 24 25 create(@issue, skip_system_notes: skip_system_notes) 26 end 27 28 def external_author 29 params[:external_author] # present when creating an issue using service desk (email: from) 30 end 31 32 def before_create(issue) 33 Spam::SpamActionService.new( 34 spammable: issue, 35 spam_params: spam_params, 36 user: current_user, 37 action: :create 38 ).execute 39 40 # current_user (defined in BaseService) is not available within run_after_commit block 41 user = current_user 42 issue.run_after_commit do 43 NewIssueWorker.perform_async(issue.id, user.id) 44 Issues::PlacementWorker.perform_async(nil, issue.project_id) 45 Namespaces::OnboardingIssueCreatedWorker.perform_async(issue.namespace.id) 46 end 47 end 48 49 # Add new items to Issues::AfterCreateService if they can be performed in Sidekiq 50 def after_create(issue) 51 user_agent_detail_service.create 52 resolve_discussions_with_issue(issue) 53 create_escalation_status(issue) 54 55 super 56 end 57 58 def handle_changes(issue, options) 59 super 60 old_associations = options.fetch(:old_associations, {}) 61 old_assignees = old_associations.fetch(:assignees, []) 62 63 handle_assignee_changes(issue, old_assignees) 64 end 65 66 def handle_assignee_changes(issue, old_assignees) 67 return if issue.assignees == old_assignees 68 69 create_assignee_note(issue, old_assignees) 70 end 71 72 def resolve_discussions_with_issue(issue) 73 return if discussions_to_resolve.empty? 74 75 Discussions::ResolveService.new(project, current_user, 76 one_or_more_discussions: discussions_to_resolve, 77 follow_up_issue: issue).execute 78 end 79 80 private 81 82 attr_reader :spam_params 83 84 def create_escalation_status(issue) 85 ::IncidentManagement::IssuableEscalationStatuses::CreateService.new(issue).execute if issue.supports_escalation? 86 end 87 88 def user_agent_detail_service 89 UserAgentDetailService.new(spammable: @issue, spam_params: spam_params) 90 end 91 end 92end 93 94Issues::CreateService.prepend_mod 95