1# frozen_string_literal: true
2class DraftNote < ApplicationRecord
3  include DiffPositionableNote
4  include Gitlab::Utils::StrongMemoize
5  include Sortable
6  include ShaAttribute
7
8  PUBLISH_ATTRS = %i(noteable_id noteable_type type note).freeze
9  DIFF_ATTRS = %i(position original_position change_position commit_id).freeze
10
11  sha_attribute :commit_id
12
13  # Attribute used to store quick actions changes and users referenced.
14  attr_accessor :commands_changes
15  attr_accessor :users_referenced
16
17  # Text with quick actions filtered out
18  attr_accessor :rendered_note
19
20  attr_accessor :review
21
22  belongs_to :author, class_name: 'User'
23  belongs_to :merge_request
24
25  validates :merge_request_id, presence: true
26  validates :author_id, presence: true, uniqueness: { scope: [:merge_request_id, :discussion_id] }, if: :discussion_id?
27  validates :discussion_id, allow_nil: true, format: { with: /\A\h{40}\z/ }
28
29  scope :authored_by, ->(u) { where(author_id: u.id) }
30
31  delegate :file_path, :file_hash, :file_identifier_hash, to: :diff_file, allow_nil: true
32
33  def self.positions
34    where.not(position: nil)
35      .select(:position)
36      .map(&:position)
37  end
38
39  def project
40    merge_request.target_project
41  end
42
43  # noteable_id and noteable_type methods
44  # are used to generate discussion_id on Discussion.discussion_id
45  def noteable_id
46    merge_request_id
47  end
48
49  def noteable
50    merge_request
51  end
52
53  def noteable_type
54    "MergeRequest"
55  end
56
57  def for_commit?
58    commit_id.present?
59  end
60
61  def importing?
62    false
63  end
64
65  def resolvable?
66    false
67  end
68
69  def emoji_awardable?
70    false
71  end
72
73  def on_diff?
74    position&.complete?
75  end
76
77  def type
78    return 'DiffNote' if on_diff?
79    return 'DiscussionNote' if discussion_id.present?
80
81    'Note'
82  end
83
84  def references
85    {
86      users: users_referenced,
87      commands: commands_changes
88    }
89  end
90
91  def line_code
92    @line_code ||= diff_file&.line_code_for_position(original_position)
93  end
94
95  def publish_params
96    attrs = PUBLISH_ATTRS.dup
97    attrs.concat(DIFF_ATTRS) if on_diff?
98    params = slice(*attrs)
99    params[:in_reply_to_discussion_id] = discussion_id if discussion_id.present?
100    params[:review_id] = review.id if review.present?
101
102    params
103  end
104
105  def self.preload_author(draft_notes)
106    ActiveRecord::Associations::Preloader.new.preload(draft_notes, { author: :status })
107  end
108
109  def diff_file
110    strong_memoize(:diff_file) do
111      file = original_position&.diff_file(project.repository)
112
113      file&.unfold_diff_lines(original_position)
114
115      file
116    end
117  end
118
119  def commit
120    @commit ||= project.commit(commit_id) if commit_id.present?
121  end
122end
123