Have Issue#participants load all users mentioned in notes using a single query

This commit is contained in:
Douwe Maan 2015-10-14 09:17:05 +02:00
parent cd2583a3be
commit d6fb96b927
3 changed files with 28 additions and 24 deletions

View File

@ -47,19 +47,19 @@ module Mentionable
SystemNoteService.cross_reference_exists?(target, local_reference) SystemNoteService.cross_reference_exists?(target, local_reference)
end end
def mentioned_users(current_user = nil) def mentioned_users(current_user = nil, load_lazy_references: true)
return [] if mentionable_text.blank? return [] if mentionable_text.blank?
ext = Gitlab::ReferenceExtractor.new(self.project, current_user) ext = Gitlab::ReferenceExtractor.new(self.project, current_user, load_lazy_references: load_lazy_references)
ext.analyze(mentionable_text) ext.analyze(mentionable_text)
ext.users.uniq ext.users.uniq
end end
# Extract GFM references to other Mentionables from this Mentionable. Always excludes its #local_reference. # Extract GFM references to other Mentionables from this Mentionable. Always excludes its #local_reference.
def references(p = project, current_user = self.author, text = mentionable_text) def references(p = project, current_user = self.author, text = mentionable_text, load_lazy_references: true)
return [] if text.blank? return [] if text.blank?
ext = Gitlab::ReferenceExtractor.new(p, current_user) ext = Gitlab::ReferenceExtractor.new(p, current_user, load_lazy_references: load_lazy_references)
ext.analyze(text) ext.analyze(text)
(ext.issues + ext.merge_requests + ext.commits).uniq - [local_reference] (ext.issues + ext.merge_requests + ext.commits).uniq - [local_reference]
end end

View File

@ -27,7 +27,7 @@ module Participable
module ClassMethods module ClassMethods
def participant(*attrs) def participant(*attrs)
participant_attrs.concat(attrs.map(&:to_s)) participant_attrs.concat(attrs)
end end
def participant_attrs def participant_attrs
@ -37,13 +37,12 @@ module Participable
# Be aware that this method makes a lot of sql queries. # Be aware that this method makes a lot of sql queries.
# Save result into variable if you are going to reuse it inside same request # Save result into variable if you are going to reuse it inside same request
def participants(current_user = self.author, project = self.project) def participants(current_user = self.author, project = self.project, load_lazy_references: true)
participants = self.class.participant_attrs.flat_map do |attr| participants = self.class.participant_attrs.flat_map do |attr|
meth = method(attr) meth = method(attr)
value = value =
if meth.arity == 1 || meth.arity == -1 if attr == :mentioned_users
meth.call(current_user) meth.call(current_user, load_lazy_references: false)
else else
meth.call meth.call
end end
@ -51,9 +50,13 @@ module Participable
participants_for(value, current_user, project) participants_for(value, current_user, project)
end.compact.uniq end.compact.uniq
if project if load_lazy_references
participants.select! do |user| participants = Gitlab::Markdown::ReferenceFilter::LazyReference.load(participants).uniq
user.can?(:read_project, project)
if project
participants.select! do |user|
user.can?(:read_project, project)
end
end end
end end
@ -64,12 +67,12 @@ module Participable
def participants_for(value, current_user = nil, project = nil) def participants_for(value, current_user = nil, project = nil)
case value case value
when User when User, Gitlab::Markdown::ReferenceFilter::LazyReference
[value] [value]
when Enumerable, ActiveRecord::Relation when Enumerable, ActiveRecord::Relation
value.flat_map { |v| participants_for(v, current_user, project) } value.flat_map { |v| participants_for(v, current_user, project) }
when Participable when Participable
value.participants(current_user, project) value.participants(current_user, project, load_lazy_references: false)
end end
end end
end end

View File

@ -3,17 +3,17 @@ require 'gitlab/markdown'
module Gitlab module Gitlab
# Extract possible GFM references from an arbitrary String for further processing. # Extract possible GFM references from an arbitrary String for further processing.
class ReferenceExtractor class ReferenceExtractor
attr_accessor :project, :current_user attr_accessor :project, :current_user, :load_lazy_references
def initialize(project, current_user = nil) def initialize(project, current_user = nil, load_lazy_references: true)
@project = project @project = project
@current_user = current_user @current_user = current_user
@load_lazy_references = load_lazy_references
end end
def analyze(texts) def analyze(text)
references.clear references.clear
texts = Array(texts) @text = Gitlab::Markdown.render_without_gfm(text)
@texts = texts.map { |text| Gitlab::Markdown.render_without_gfm(text) }
end end
%i(user label issue merge_request snippet commit commit_range).each do |type| %i(user label issue merge_request snippet commit commit_range).each do |type|
@ -29,7 +29,7 @@ module Gitlab
type = type.to_sym type = type.to_sym
return references[type] if references.has_key?(type) return references[type] if references.has_key?(type)
references[type] = pipeline_result(type).uniq references[type] = pipeline_result(type)
end end
end end
@ -53,14 +53,15 @@ module Gitlab
} }
pipeline = HTML::Pipeline.new([filter, Gitlab::Markdown::ReferenceGathererFilter], context) pipeline = HTML::Pipeline.new([filter, Gitlab::Markdown::ReferenceGathererFilter], context)
result = pipeline.call(@text)
values = @texts.flat_map do |text| values = result[:references][filter_type].uniq
result = pipeline.call(text)
result[:references][filter_type] if @load_lazy_references
values = Gitlab::Markdown::ReferenceFilter::LazyReference.load(values).uniq
end end
Gitlab::Markdown::ReferenceFilter::LazyReference.load(values) values
end end
end end
end end