PageRenderTime 44ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/app/services/issuable_base_service.rb

https://gitlab.com/matth/gitlab-ce
Ruby | 347 lines | 261 code | 78 blank | 8 comment | 41 complexity | 376449030daaf0c92c49ab508a8ee23a MD5 | raw file
  1. class IssuableBaseService < BaseService
  2. private
  3. def create_milestone_note(issuable)
  4. milestone = issuable.milestone
  5. return if milestone && milestone.is_group_milestone?
  6. SystemNoteService.change_milestone(
  7. issuable, issuable.project, current_user, milestone)
  8. end
  9. def create_labels_note(issuable, old_labels)
  10. added_labels = issuable.labels - old_labels
  11. removed_labels = old_labels - issuable.labels
  12. SystemNoteService.change_label(
  13. issuable, issuable.project, current_user, added_labels, removed_labels)
  14. end
  15. def create_title_change_note(issuable, old_title)
  16. SystemNoteService.change_title(
  17. issuable, issuable.project, current_user, old_title)
  18. end
  19. def create_description_change_note(issuable)
  20. SystemNoteService.change_description(issuable, issuable.project, current_user)
  21. end
  22. def create_branch_change_note(issuable, branch_type, old_branch, new_branch)
  23. SystemNoteService.change_branch(
  24. issuable, issuable.project, current_user, branch_type,
  25. old_branch, new_branch)
  26. end
  27. def create_task_status_note(issuable)
  28. issuable.updated_tasks.each do |task|
  29. SystemNoteService.change_task_status(issuable, issuable.project, current_user, task)
  30. end
  31. end
  32. def create_time_estimate_note(issuable)
  33. SystemNoteService.change_time_estimate(issuable, issuable.project, current_user)
  34. end
  35. def create_time_spent_note(issuable)
  36. SystemNoteService.change_time_spent(issuable, issuable.project, current_user)
  37. end
  38. def filter_params(issuable)
  39. ability_name = :"admin_#{issuable.to_ability_name}"
  40. unless can?(current_user, ability_name, project)
  41. params.delete(:milestone_id)
  42. params.delete(:labels)
  43. params.delete(:add_label_ids)
  44. params.delete(:remove_label_ids)
  45. params.delete(:label_ids)
  46. params.delete(:assignee_ids)
  47. params.delete(:assignee_id)
  48. params.delete(:due_date)
  49. end
  50. filter_assignee(issuable)
  51. filter_milestone
  52. filter_labels
  53. end
  54. def filter_assignee(issuable)
  55. return unless params[:assignee_id].present?
  56. assignee_id = params[:assignee_id]
  57. if assignee_id.to_s == IssuableFinder::NONE
  58. params[:assignee_id] = ""
  59. else
  60. params.delete(:assignee_id) unless assignee_can_read?(issuable, assignee_id)
  61. end
  62. end
  63. def assignee_can_read?(issuable, assignee_id)
  64. new_assignee = User.find_by_id(assignee_id)
  65. return false unless new_assignee
  66. ability_name = :"read_#{issuable.to_ability_name}"
  67. resource = issuable.persisted? ? issuable : project
  68. can?(new_assignee, ability_name, resource)
  69. end
  70. def filter_milestone
  71. milestone_id = params[:milestone_id]
  72. return unless milestone_id
  73. params[:milestone_id] = '' if milestone_id == IssuableFinder::NONE
  74. milestone =
  75. Milestone.for_projects_and_groups([project.id], [project.group&.id]).find_by_id(milestone_id)
  76. params[:milestone_id] = '' unless milestone
  77. end
  78. def filter_labels
  79. filter_labels_in_param(:add_label_ids)
  80. filter_labels_in_param(:remove_label_ids)
  81. filter_labels_in_param(:label_ids)
  82. find_or_create_label_ids
  83. end
  84. def filter_labels_in_param(key)
  85. return if params[key].to_a.empty?
  86. params[key] = available_labels.where(id: params[key]).pluck(:id)
  87. end
  88. def find_or_create_label_ids
  89. labels = params.delete(:labels)
  90. return unless labels
  91. params[:label_ids] = labels.split(",").map do |label_name|
  92. service = Labels::FindOrCreateService.new(current_user, project, title: label_name.strip)
  93. label = service.execute
  94. label.try(:id)
  95. end.compact
  96. end
  97. def process_label_ids(attributes, existing_label_ids: nil)
  98. label_ids = attributes.delete(:label_ids)
  99. add_label_ids = attributes.delete(:add_label_ids)
  100. remove_label_ids = attributes.delete(:remove_label_ids)
  101. new_label_ids = existing_label_ids || label_ids || []
  102. if add_label_ids.blank? && remove_label_ids.blank?
  103. new_label_ids = label_ids if label_ids
  104. else
  105. new_label_ids |= add_label_ids if add_label_ids
  106. new_label_ids -= remove_label_ids if remove_label_ids
  107. end
  108. new_label_ids
  109. end
  110. def available_labels
  111. LabelsFinder.new(current_user, project_id: @project.id).execute
  112. end
  113. def merge_quick_actions_into_params!(issuable)
  114. description, command_params =
  115. QuickActions::InterpretService.new(project, current_user)
  116. .execute(params[:description], issuable)
  117. # Avoid a description already set on an issuable to be overwritten by a nil
  118. params[:description] = description if params.key?(:description)
  119. params.merge!(command_params)
  120. end
  121. def create_issuable(issuable, attributes, label_ids:)
  122. issuable.with_transaction_returning_status do
  123. if issuable.save
  124. issuable.update_attributes(label_ids: label_ids)
  125. end
  126. end
  127. end
  128. def create(issuable)
  129. merge_quick_actions_into_params!(issuable)
  130. filter_params(issuable)
  131. params.delete(:state_event)
  132. params[:author] ||= current_user
  133. label_ids = process_label_ids(params)
  134. issuable.assign_attributes(params)
  135. before_create(issuable)
  136. if params.present? && create_issuable(issuable, params, label_ids: label_ids)
  137. after_create(issuable)
  138. issuable.create_cross_references!(current_user)
  139. execute_hooks(issuable)
  140. invalidate_cache_counts(issuable.assignees, issuable)
  141. end
  142. issuable
  143. end
  144. def before_create(issuable)
  145. # To be overridden by subclasses
  146. end
  147. def after_create(issuable)
  148. # To be overridden by subclasses
  149. end
  150. def before_update(issuable)
  151. # To be overridden by subclasses
  152. end
  153. def after_update(issuable)
  154. # To be overridden by subclasses
  155. end
  156. def update(issuable)
  157. change_state(issuable)
  158. change_subscription(issuable)
  159. change_todo(issuable)
  160. toggle_award(issuable)
  161. filter_params(issuable)
  162. old_labels = issuable.labels.to_a
  163. old_mentioned_users = issuable.mentioned_users.to_a
  164. old_assignees = issuable.assignees.to_a
  165. label_ids = process_label_ids(params, existing_label_ids: issuable.label_ids)
  166. params[:label_ids] = label_ids if labels_changing?(issuable.label_ids, label_ids)
  167. if issuable.changed? || params.present?
  168. issuable.assign_attributes(params.merge(updated_by: current_user))
  169. if has_title_or_description_changed?(issuable)
  170. issuable.assign_attributes(last_edited_at: Time.now, last_edited_by: current_user)
  171. end
  172. before_update(issuable)
  173. if issuable.with_transaction_returning_status { issuable.save }
  174. # We do not touch as it will affect a update on updated_at field
  175. ActiveRecord::Base.no_touching do
  176. handle_common_system_notes(issuable, old_labels: old_labels)
  177. end
  178. handle_changes(
  179. issuable,
  180. old_labels: old_labels,
  181. old_mentioned_users: old_mentioned_users,
  182. old_assignees: old_assignees
  183. )
  184. if old_assignees != issuable.assignees
  185. new_assignees = issuable.assignees.to_a
  186. affected_assignees = (old_assignees + new_assignees) - (old_assignees & new_assignees)
  187. invalidate_cache_counts(affected_assignees.compact, issuable)
  188. end
  189. after_update(issuable)
  190. issuable.create_new_cross_references!(current_user)
  191. execute_hooks(issuable, 'update')
  192. end
  193. end
  194. issuable
  195. end
  196. def labels_changing?(old_label_ids, new_label_ids)
  197. old_label_ids.sort != new_label_ids.sort
  198. end
  199. def has_title_or_description_changed?(issuable)
  200. issuable.title_changed? || issuable.description_changed?
  201. end
  202. def change_state(issuable)
  203. case params.delete(:state_event)
  204. when 'reopen'
  205. reopen_service.new(project, current_user, {}).execute(issuable)
  206. when 'close'
  207. close_service.new(project, current_user, {}).execute(issuable)
  208. end
  209. end
  210. def change_subscription(issuable)
  211. case params.delete(:subscription_event)
  212. when 'subscribe'
  213. issuable.subscribe(current_user, project)
  214. when 'unsubscribe'
  215. issuable.unsubscribe(current_user, project)
  216. end
  217. end
  218. def change_todo(issuable)
  219. case params.delete(:todo_event)
  220. when 'add'
  221. todo_service.mark_todo(issuable, current_user)
  222. when 'done'
  223. todo = TodosFinder.new(current_user).execute.find_by(target: issuable)
  224. todo_service.mark_todos_as_done([todo], current_user) if todo
  225. end
  226. end
  227. def toggle_award(issuable)
  228. award = params.delete(:emoji_award)
  229. if award
  230. todo_service.new_award_emoji(issuable, current_user)
  231. issuable.toggle_award_emoji(award, current_user)
  232. end
  233. end
  234. def has_changes?(issuable, old_labels: [], old_assignees: [])
  235. valid_attrs = [:title, :description, :assignee_id, :milestone_id, :target_branch]
  236. attrs_changed = valid_attrs.any? do |attr|
  237. issuable.previous_changes.include?(attr.to_s)
  238. end
  239. labels_changed = issuable.labels != old_labels
  240. assignees_changed = issuable.assignees != old_assignees
  241. attrs_changed || labels_changed || assignees_changed
  242. end
  243. def handle_common_system_notes(issuable, old_labels: [])
  244. if issuable.previous_changes.include?('title')
  245. create_title_change_note(issuable, issuable.previous_changes['title'].first)
  246. end
  247. if issuable.previous_changes.include?('description')
  248. if issuable.tasks? && issuable.updated_tasks.any?
  249. create_task_status_note(issuable)
  250. else
  251. # TODO: Show this note if non-task content was modified.
  252. # https://gitlab.com/gitlab-org/gitlab-ce/issues/33577
  253. create_description_change_note(issuable)
  254. end
  255. end
  256. if issuable.previous_changes.include?('time_estimate')
  257. create_time_estimate_note(issuable)
  258. end
  259. if issuable.time_spent?
  260. create_time_spent_note(issuable)
  261. end
  262. create_labels_note(issuable, old_labels) if issuable.labels != old_labels
  263. end
  264. def invalidate_cache_counts(users, issuable)
  265. users.each do |user|
  266. user.public_send("invalidate_#{issuable.model_name.singular}_cache_counts")
  267. end
  268. end
  269. end