Class: Discussion

Inherits:
Object
  • Object
show all
Includes:
GlobalID::Identification, ResolvableDiscussion
Defined in:
app/models/discussion.rb

Overview

A non-diff discussion on an issue, merge request, commit, or snippet, consisting of ‘DiscussionNote` notes.

A discussion of this type can be resolvable.

Constant Summary collapse

CACHE_VERSION =

Bump this if we need to refresh the cached versions of discussions

1

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from ResolvableDiscussion

#can_resolve?, #clear_memoized_values, #first_note, #first_note_to_resolve, #last_resolved_note, #resolvable?, #resolve!, #resolved?, #resolved_by_push?, #resolved_notes, #to_be_resolved?, #unresolve!

Constructor Details

#initialize(notes, context_noteable = nil) ⇒ Discussion

Returns a new instance of Discussion.



106
107
108
109
# File 'app/models/discussion.rb', line 106

def initialize(notes, context_noteable = nil)
  @notes = notes
  @context_noteable = context_noteable
end

Instance Attribute Details

#context_noteableObject (readonly)

Returns the value of attribute context_noteable.



13
14
15
# File 'app/models/discussion.rb', line 13

def context_noteable
  @context_noteable
end

#notesObject

Returns the value of attribute notes.



14
15
16
# File 'app/models/discussion.rb', line 14

def notes
  @notes
end

Class Method Details

.base_discussion_id(note) ⇒ Object



80
81
82
83
# File 'app/models/discussion.rb', line 80

def self.base_discussion_id(note)
  noteable_id = note.noteable_id || note.commit_id
  [:discussion, note.noteable_type.try(:underscore), noteable_id]
end

.build(notes, context_noteable = nil) ⇒ Object



42
43
44
# File 'app/models/discussion.rb', line 42

def self.build(notes, context_noteable = nil)
  notes.first.discussion_class(context_noteable).new(notes, context_noteable)
end

.build_collection(notes, context_noteable = nil) ⇒ Object



46
47
48
49
# File 'app/models/discussion.rb', line 46

def self.build_collection(notes, context_noteable = nil)
  grouped_notes = notes.group_by { |n| n.discussion_id(context_noteable) }
  grouped_notes.values.map { |notes| build(notes, context_noteable) }
end

.build_discussion_id(note) ⇒ Object

Returns an array of discussion ID components



76
77
78
# File 'app/models/discussion.rb', line 76

def self.build_discussion_id(note)
  [*base_discussion_id(note), SecureRandom.hex]
end

.build_discussions(discussion_ids, context_noteable = nil, preload_note_diff_file: false) ⇒ Object



51
52
53
54
55
56
57
# File 'app/models/discussion.rb', line 51

def self.build_discussions(discussion_ids, context_noteable = nil, preload_note_diff_file: false)
  notes = Note.where(discussion_id: discussion_ids).fresh
  notes = notes.inc_note_diff_file if preload_note_diff_file

  grouped_notes = notes.group_by { |n| n.discussion_id }
  grouped_notes.transform_values { |notes| Discussion.build(notes, context_noteable) }
end

.discussion_id(note) ⇒ Object

Returns an alphanumeric discussion ID based on ‘build_discussion_id`



71
72
73
# File 'app/models/discussion.rb', line 71

def self.discussion_id(note)
  Digest::SHA1.hexdigest(build_discussion_id(note).join("-"))
end

.lazy_find(discussion_id) ⇒ Object



59
60
61
62
63
64
65
66
67
68
# File 'app/models/discussion.rb', line 59

def self.lazy_find(discussion_id)
  BatchLoader.for(discussion_id).batch do |discussion_ids, loader|
    results = Note.where(discussion_id: discussion_ids).fresh.to_a.group_by(&:discussion_id)
    results.each do |discussion_id, notes|
      next if notes.empty?

      loader.call(discussion_id, Discussion.build(notes))
    end
  end
end

.note_classObject



102
103
104
# File 'app/models/discussion.rb', line 102

def self.note_class
  DiscussionNote
end

.override_discussion_id(note) ⇒ Object

When notes on a commit are displayed in context of a merge request that contains that commit, these notes are to be displayed as if they were part of one discussion, even though they were actually individual notes on the commit with different discussion IDs, so that it’s clear that these are not notes on the merge request itself.

To turn a list of notes into a list of discussions, they are grouped by discussion ID, so to get these out-of-context notes to end up in the same discussion, we need to get them to return the same ‘discussion_id` when this grouping happens. To enable this, `Note#discussion_id` calls out to the `override_discussion_id` method on the appropriate `Discussion` subclass, as determined by the `discussion_class` method on `Note` or a subclass of `Note`.

If no override is necessary, return ‘nil`. For the case described above, see `OutOfContextDiscussion.override_discussion_id`.



98
99
100
# File 'app/models/discussion.rb', line 98

def self.override_discussion_id(note)
  nil
end

Instance Method Details

#==(other) ⇒ Object



115
116
117
118
119
120
# File 'app/models/discussion.rb', line 115

def ==(other)
  other.class == self.class &&
    other.context_noteable == self.context_noteable &&
    other.id == self.id &&
    other.notes == self.notes
end

#cache_keyObject



174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'app/models/discussion.rb', line 174

def cache_key
  # Need to use the notes' cache key so cache will be invalidated when note
  # within a discussion has been deleted or has different data after post
  # processing of content.
  notes_sha = Digest::SHA1.hexdigest(notes.map(&:post_processed_cache_key).join(':'))

  [
    CACHE_VERSION,
    id,
    notes_sha,
    resolved_at
  ].join(':')
end

#can_convert_to_discussion?Boolean

Returns:

  • (Boolean)


154
155
156
# File 'app/models/discussion.rb', line 154

def can_convert_to_discussion?
  false
end

#collapsed?Boolean

Returns:

  • (Boolean)


162
163
164
# File 'app/models/discussion.rb', line 162

def collapsed?
  resolved?
end

#declarative_policy_delegateObject



34
35
36
# File 'app/models/discussion.rb', line 34

def declarative_policy_delegate
  first_note
end

#diff_discussion?Boolean

Returns:

  • (Boolean)


146
147
148
# File 'app/models/discussion.rb', line 146

def diff_discussion?
  false
end

#expanded?Boolean

Returns:

  • (Boolean)


166
167
168
# File 'app/models/discussion.rb', line 166

def expanded?
  !collapsed?
end

#idObject Also known as: to_param



134
135
136
# File 'app/models/discussion.rb', line 134

def id
  first_note.discussion_id(context_noteable)
end

#individual_note?Boolean

Returns:

  • (Boolean)


150
151
152
# File 'app/models/discussion.rb', line 150

def individual_note?
  false
end

#last_noteObject



158
159
160
# File 'app/models/discussion.rb', line 158

def last_note
  @last_note ||= notes.last
end

#last_updated_atObject



122
123
124
# File 'app/models/discussion.rb', line 122

def last_updated_at
  last_note.created_at
end

#last_updated_byObject



126
127
128
# File 'app/models/discussion.rb', line 126

def last_updated_by
  last_note.author
end

#noteable_collection_nameObject



195
196
197
# File 'app/models/discussion.rb', line 195

def noteable_collection_name
  noteable.class.underscore.pluralize
end

#on_image?Boolean

Returns:

  • (Boolean)


111
112
113
# File 'app/models/discussion.rb', line 111

def on_image?
  false
end

#project_idObject



38
39
40
# File 'app/models/discussion.rb', line 38

def project_id
  project&.id
end

#reply_attributesObject



170
171
172
# File 'app/models/discussion.rb', line 170

def reply_attributes
  first_note.slice(:type, :noteable_type, :noteable_id, :commit_id, :discussion_id)
end

#reply_idObject



138
139
140
141
142
# File 'app/models/discussion.rb', line 138

def reply_id
  # To reply to this discussion, we need the actual discussion_id from the database,
  # not the potentially overwritten one based on the noteable.
  first_note.discussion_id
end

#to_global_id(options = {}) ⇒ Object

Consolidate discussions GID. There is no need to have different GID for different class names as the discussion_id hash is already unique per discussion. This also fixes the issue where same discussion may return different GIDs depending on number of notes it has.



191
192
193
# File 'app/models/discussion.rb', line 191

def to_global_id(options = {})
  GlobalID.new(::Gitlab::GlobalId.build(model_name: Discussion.to_s, id: id))
end

#updated?Boolean

Returns:

  • (Boolean)


130
131
132
# File 'app/models/discussion.rb', line 130

def updated?
  last_updated_at != created_at
end