summaryrefslogtreecommitdiff
path: root/app/services/draft_notes
diff options
context:
space:
mode:
Diffstat (limited to 'app/services/draft_notes')
-rw-r--r--app/services/draft_notes/base_service.rb21
-rw-r--r--app/services/draft_notes/create_service.rb56
-rw-r--r--app/services/draft_notes/destroy_service.rb23
-rw-r--r--app/services/draft_notes/publish_service.rb67
4 files changed, 167 insertions, 0 deletions
diff --git a/app/services/draft_notes/base_service.rb b/app/services/draft_notes/base_service.rb
new file mode 100644
index 00000000000..89daae0e8f4
--- /dev/null
+++ b/app/services/draft_notes/base_service.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module DraftNotes
+ class BaseService < ::BaseService
+ attr_accessor :merge_request, :current_user, :params
+
+ def initialize(merge_request, current_user, params = nil)
+ @merge_request, @current_user, @params = merge_request, current_user, params.dup
+ end
+
+ private
+
+ def draft_notes
+ @draft_notes ||= merge_request.draft_notes.order_id_asc.authored_by(current_user)
+ end
+
+ def project
+ merge_request.target_project
+ end
+ end
+end
diff --git a/app/services/draft_notes/create_service.rb b/app/services/draft_notes/create_service.rb
new file mode 100644
index 00000000000..501778b7d5f
--- /dev/null
+++ b/app/services/draft_notes/create_service.rb
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+
+module DraftNotes
+ class CreateService < DraftNotes::BaseService
+ attr_accessor :in_draft_mode, :in_reply_to_discussion_id
+
+ def initialize(merge_request, current_user, params = nil)
+ @in_reply_to_discussion_id = params.delete(:in_reply_to_discussion_id)
+ super
+ end
+
+ def execute
+ if in_reply_to_discussion_id.present?
+ unless discussion
+ return base_error(_('Thread to reply to cannot be found'))
+ end
+
+ params[:discussion_id] = discussion.reply_id
+ end
+
+ if params[:resolve_discussion] && !can_resolve_discussion?
+ return base_error(_('User is not allowed to resolve thread'))
+ end
+
+ draft_note = DraftNote.new(params)
+ draft_note.merge_request = merge_request
+ draft_note.author = current_user
+ draft_note.save
+
+ if in_reply_to_discussion_id.blank? && draft_note.diff_file&.unfolded?
+ merge_request.diffs.clear_cache
+ end
+
+ draft_note
+ end
+
+ private
+
+ def base_error(text)
+ DraftNote.new.tap do |draft|
+ draft.errors.add(:base, text)
+ end
+ end
+
+ def discussion
+ @discussion ||= merge_request.notes.find_discussion(in_reply_to_discussion_id)
+ end
+
+ def can_resolve_discussion?
+ note = discussion&.notes&.first
+ return false unless note
+
+ current_user && Ability.allowed?(current_user, :resolve_note, note)
+ end
+ end
+end
diff --git a/app/services/draft_notes/destroy_service.rb b/app/services/draft_notes/destroy_service.rb
new file mode 100644
index 00000000000..ddca0debb03
--- /dev/null
+++ b/app/services/draft_notes/destroy_service.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module DraftNotes
+ class DestroyService < DraftNotes::BaseService
+ # If no `draft` is given it fallsback to all
+ # draft notes of the given merge request and user.
+ def execute(draft = nil)
+ drafts = draft || draft_notes
+
+ clear_highlight_diffs_cache(Array.wrap(drafts))
+
+ drafts.is_a?(DraftNote) ? drafts.destroy! : drafts.delete_all
+ end
+
+ private
+
+ def clear_highlight_diffs_cache(drafts)
+ if drafts.any? { |draft| draft.diff_file&.unfolded? }
+ merge_request.diffs.clear_cache
+ end
+ end
+ end
+end
diff --git a/app/services/draft_notes/publish_service.rb b/app/services/draft_notes/publish_service.rb
new file mode 100644
index 00000000000..a9a7304e5ed
--- /dev/null
+++ b/app/services/draft_notes/publish_service.rb
@@ -0,0 +1,67 @@
+# frozen_string_literal: true
+
+module DraftNotes
+ class PublishService < DraftNotes::BaseService
+ def execute(draft = nil)
+ return error('Not allowed to create notes') unless can?(current_user, :create_note, merge_request)
+
+ if draft
+ publish_draft_note(draft)
+ else
+ publish_draft_notes
+ end
+
+ success
+ rescue ActiveRecord::RecordInvalid => e
+ message = "Unable to save #{e.record.class.name}: #{e.record.errors.full_messages.join(", ")} "
+ error(message)
+ end
+
+ private
+
+ def publish_draft_note(draft)
+ create_note_from_draft(draft)
+ draft.delete
+
+ MergeRequests::ResolvedDiscussionNotificationService.new(project, current_user).execute(merge_request)
+ end
+
+ def publish_draft_notes
+ return if draft_notes.empty?
+
+ review = Review.create!(author: current_user, merge_request: merge_request, project: project)
+
+ draft_notes.map do |draft_note|
+ draft_note.review = review
+ create_note_from_draft(draft_note)
+ end
+ draft_notes.delete_all
+
+ notification_service.async.new_review(review)
+ MergeRequests::ResolvedDiscussionNotificationService.new(project, current_user).execute(merge_request)
+ end
+
+ def create_note_from_draft(draft)
+ # Make sure the diff file is unfolded in order to find the correct line
+ # codes.
+ draft.diff_file&.unfold_diff_lines(draft.original_position)
+
+ note = Notes::CreateService.new(draft.project, draft.author, draft.publish_params).execute
+ set_discussion_resolve_status(note, draft)
+
+ note
+ end
+
+ def set_discussion_resolve_status(note, draft_note)
+ return unless draft_note.discussion_id.present?
+
+ discussion = note.discussion
+
+ if draft_note.resolve_discussion && discussion.can_resolve?(current_user)
+ discussion.resolve!(current_user)
+ else
+ discussion.unresolve!
+ end
+ end
+ end
+end