summaryrefslogtreecommitdiff
path: root/app/services/discussions/capture_diff_note_position_service.rb
blob: 87aa27e455f55c222037477ed64fac9d4742f91d (plain)
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
# frozen_string_literal: true

module Discussions
  class CaptureDiffNotePositionService
    def initialize(merge_request, paths)
      @project = merge_request.project
      @tracer = build_tracer(merge_request, paths)
    end

    def execute(discussion)
      # The service has been implemented for text only
      # The impact of image notes on this service is being investigated in
      # https://gitlab.com/gitlab-org/gitlab/-/issues/213989
      return unless discussion.on_text?

      result = tracer&.trace(discussion.position)
      return unless result

      position = result[:position]
      return unless position

      line_code = position.line_code(project.repository)
      return unless line_code

      # Currently position data is copied across all notes of a discussion
      # It makes sense to store a position only for the first note instead
      # Within the newly introduced table we can start doing just that
      DiffNotePosition.create_or_update_for(discussion.notes.first,
        diff_type: :head,
        position: position,
        line_code: line_code)
    end

    private

    attr_reader :tracer, :project

    def build_tracer(merge_request, paths)
      return if paths.blank?

      old_diff_refs, new_diff_refs = build_diff_refs(merge_request)

      return unless old_diff_refs && new_diff_refs

      Gitlab::Diff::PositionTracer.new(
        project: project,
        old_diff_refs: old_diff_refs,
        new_diff_refs: new_diff_refs,
        paths: paths.uniq)
    end

    def build_diff_refs(merge_request)
      merge_ref_head = merge_request.merge_ref_head
      return unless merge_ref_head

      start_sha, _ = merge_ref_head.parent_ids
      new_diff_refs = Gitlab::Diff::DiffRefs.new(
        base_sha: start_sha,
        start_sha: start_sha,
        head_sha: merge_ref_head.id)

      old_diff_refs = merge_request.diff_refs

      return if new_diff_refs == old_diff_refs

      [old_diff_refs, new_diff_refs]
    end
  end
end