diff options
Diffstat (limited to 'lib/gitlab/bitbucket_server_import/importers/pull_request_notes_importer.rb')
-rw-r--r-- | lib/gitlab/bitbucket_server_import/importers/pull_request_notes_importer.rb | 183 |
1 files changed, 183 insertions, 0 deletions
diff --git a/lib/gitlab/bitbucket_server_import/importers/pull_request_notes_importer.rb b/lib/gitlab/bitbucket_server_import/importers/pull_request_notes_importer.rb new file mode 100644 index 00000000000..69de47e2006 --- /dev/null +++ b/lib/gitlab/bitbucket_server_import/importers/pull_request_notes_importer.rb @@ -0,0 +1,183 @@ +# frozen_string_literal: true + +module Gitlab + module BitbucketServerImport + module Importers + class PullRequestNotesImporter + include Loggable + + def initialize(project, hash) + @project = project + @formatter = Gitlab::ImportFormatter.new + @client = BitbucketServer::Client.new(project.import_data.credentials) + @project_key = project.import_data.data['project_key'] + @repository_slug = project.import_data.data['repo_slug'] + @user_finder = UserFinder.new(project) + + # TODO: Convert object into a object instead of using it as a hash + @object = hash.with_indifferent_access + end + + def execute + log_info(import_stage: 'import_pull_request_notes', message: 'starting', iid: object[:iid]) + + merge_request = project.merge_requests.find_by(iid: object[:iid]) # rubocop: disable CodeReuse/ActiveRecord + + if merge_request + activities = client.activities(project_key, repository_slug, merge_request.iid) + + comments, other_activities = activities.partition(&:comment?) + + merge_event = other_activities.find(&:merge_event?) + import_merge_event(merge_request, merge_event) if merge_event + + inline_comments, pr_comments = comments.partition(&:inline_comment?) + + import_inline_comments(inline_comments.map(&:comment), merge_request) + import_standalone_pr_comments(pr_comments.map(&:comment), merge_request) + end + + log_info(import_stage: 'import_pull_request_notes', message: 'finished', iid: object[:iid]) + end + + private + + attr_reader :object, :project, :formatter, :client, :project_key, :repository_slug, :user_finder + + # rubocop: disable CodeReuse/ActiveRecord + def import_merge_event(merge_request, merge_event) + log_info(import_stage: 'import_merge_event', message: 'starting', iid: merge_request.iid) + + committer = merge_event.committer_email + + user_id = user_finder.find_user_id(by: :email, value: committer) || project.creator_id + timestamp = merge_event.merge_timestamp + merge_request.update({ merge_commit_sha: merge_event.merge_commit }) + metric = MergeRequest::Metrics.find_or_initialize_by(merge_request: merge_request) + metric.update(merged_by_id: user_id, merged_at: timestamp) + + log_info(import_stage: 'import_merge_event', message: 'finished', iid: merge_request.iid) + end + # rubocop: enable CodeReuse/ActiveRecord + + def import_inline_comments(inline_comments, merge_request) + log_info(import_stage: 'import_inline_comments', message: 'starting', iid: merge_request.iid) + + inline_comments.each do |comment| + position = build_position(merge_request, comment) + parent = create_diff_note(merge_request, comment, position) + + next unless parent&.persisted? + + discussion_id = parent.discussion_id + + comment.comments.each do |reply| + create_diff_note(merge_request, reply, position, discussion_id) + end + end + + log_info(import_stage: 'import_inline_comments', message: 'finished', iid: merge_request.iid) + end + + def create_diff_note(merge_request, comment, position, discussion_id = nil) + attributes = pull_request_comment_attributes(comment) + attributes.merge!(position: position, type: 'DiffNote') + attributes[:discussion_id] = discussion_id if discussion_id + + note = merge_request.notes.build(attributes) + + if note.valid? + note.save + return note + end + + log_info(import_stage: 'create_diff_note', message: 'creating fallback DiffNote', iid: merge_request.iid) + + # Bitbucket Server supports the ability to comment on any line, not just the + # line in the diff. If we can't add the note as a DiffNote, fallback to creating + # a regular note. + create_fallback_diff_note(merge_request, comment, position) + rescue StandardError => e + Gitlab::ErrorTracking.log_exception( + e, + import_stage: 'create_diff_note', comment_id: comment.id, error: e.message + ) + + nil + end + + def create_fallback_diff_note(merge_request, comment, position) + attributes = pull_request_comment_attributes(comment) + note = "*Comment on" + + note += " #{position.old_path}:#{position.old_line} -->" if position.old_line + note += " #{position.new_path}:#{position.new_line}" if position.new_line + note += "*\n\n#{comment.note}" + + attributes[:note] = note + merge_request.notes.create!(attributes) + end + + def build_position(merge_request, pr_comment) + params = { + diff_refs: merge_request.diff_refs, + old_path: pr_comment.file_path, + new_path: pr_comment.file_path, + old_line: pr_comment.old_pos, + new_line: pr_comment.new_pos + } + + Gitlab::Diff::Position.new(params) + end + + def import_standalone_pr_comments(pr_comments, merge_request) + log_info(import_stage: 'import_standalone_pr_comments', message: 'starting', iid: merge_request.iid) + + pr_comments.each do |comment| + merge_request.notes.create!(pull_request_comment_attributes(comment)) + + comment.comments.each do |replies| + merge_request.notes.create!(pull_request_comment_attributes(replies)) + end + rescue StandardError => e + Gitlab::ErrorTracking.log_exception( + e, + import_stage: 'import_standalone_pr_comments', + merge_request_id: merge_request.id, + comment_id: comment.id, + error: e.message + ) + ensure + log_info(import_stage: 'import_standalone_pr_comments', message: 'finished', iid: merge_request.iid) + end + end + + def pull_request_comment_attributes(comment) + author = user_finder.uid(comment) + note = '' + + unless author + author = project.creator_id + note = "*By #{comment.author_username} (#{comment.author_email})*\n\n" + end + + note += + # Provide some context for replying + if comment.parent_comment + "> #{comment.parent_comment.note.truncate(80)}\n\n#{comment.note}" + else + comment.note + end + + { + project: project, + note: note, + author_id: author, + created_at: comment.created_at, + updated_at: comment.updated_at + } + end + end + end + end +end |