summaryrefslogtreecommitdiff
path: root/lib/banzai
diff options
context:
space:
mode:
authorSean McGivern <sean@mcgivern.me.uk>2017-12-07 12:47:46 +0000
committerSean McGivern <sean@mcgivern.me.uk>2017-12-07 12:47:46 +0000
commit0054e1567b87795cc907232a4c1e9388be5c11a5 (patch)
treeb249984f8a6f53d15bb1acb8bb49c1ed2fb42243 /lib/banzai
parent996da1e52c3e1f3f1ec58f81f602167e4ff539a3 (diff)
parent0c2fdb1c342fa5eb64bb0ed02091af3c06b37c0e (diff)
downloadgitlab-ce-0054e1567b87795cc907232a4c1e9388be5c11a5.tar.gz
Merge branch 'ce-backport-3615' into 'master'
Refactor banzai to support referencing from group context See merge request gitlab-org/gitlab-ce!15766
Diffstat (limited to 'lib/banzai')
-rw-r--r--lib/banzai/cross_project_reference.rb2
-rw-r--r--lib/banzai/filter/abstract_reference_filter.rb98
-rw-r--r--lib/banzai/filter/epic_reference_filter.rb12
-rw-r--r--lib/banzai/filter/issuable_reference_filter.rb31
-rw-r--r--lib/banzai/filter/issue_reference_filter.rb32
-rw-r--r--lib/banzai/filter/label_reference_filter.rb4
-rw-r--r--lib/banzai/filter/merge_request_reference_filter.rb37
-rw-r--r--lib/banzai/filter/milestone_reference_filter.rb2
-rw-r--r--lib/banzai/issuable_extractor.rb4
-rw-r--r--lib/banzai/reference_parser/epic_parser.rb12
-rw-r--r--lib/banzai/reference_parser/issuable_parser.rb25
-rw-r--r--lib/banzai/reference_parser/issue_parser.rb12
-rw-r--r--lib/banzai/reference_parser/merge_request_parser.rb24
13 files changed, 163 insertions, 132 deletions
diff --git a/lib/banzai/cross_project_reference.rb b/lib/banzai/cross_project_reference.rb
index e2b57adf611..d8fb7705b2a 100644
--- a/lib/banzai/cross_project_reference.rb
+++ b/lib/banzai/cross_project_reference.rb
@@ -11,7 +11,7 @@ module Banzai
# ref - String reference.
#
# Returns a Project, or nil if the reference can't be found
- def project_from_ref(ref)
+ def parent_from_ref(ref)
return context[:project] unless ref
Project.find_by_full_path(ref)
diff --git a/lib/banzai/filter/abstract_reference_filter.rb b/lib/banzai/filter/abstract_reference_filter.rb
index 8975395aff1..e7e6a90b5fd 100644
--- a/lib/banzai/filter/abstract_reference_filter.rb
+++ b/lib/banzai/filter/abstract_reference_filter.rb
@@ -82,9 +82,9 @@ module Banzai
end
end
- def project_from_ref_cached(ref)
- cached_call(:banzai_project_refs, ref) do
- project_from_ref(ref)
+ def from_ref_cached(ref)
+ cached_call("banzai_#{parent_type}_refs".to_sym, ref) do
+ parent_from_ref(ref)
end
end
@@ -153,15 +153,20 @@ module Banzai
# have `gfm` and `gfm-OBJECT_NAME` class names attached for styling.
def object_link_filter(text, pattern, link_content: nil, link_reference: false)
references_in(text, pattern) do |match, id, project_ref, namespace_ref, matches|
- project_path = full_project_path(namespace_ref, project_ref)
- project = project_from_ref_cached(project_path)
+ parent_path = if parent_type == :group
+ full_group_path(namespace_ref)
+ else
+ full_project_path(namespace_ref, project_ref)
+ end
- if project
+ parent = from_ref_cached(parent_path)
+
+ if parent
object =
if link_reference
- find_object_from_link_cached(project, id)
+ find_object_from_link_cached(parent, id)
else
- find_object_cached(project, id)
+ find_object_cached(parent, id)
end
end
@@ -169,13 +174,13 @@ module Banzai
title = object_link_title(object)
klass = reference_class(object_sym)
- data = data_attributes_for(link_content || match, project, object, link: !!link_content)
+ data = data_attributes_for(link_content || match, parent, object, link: !!link_content)
url =
if matches.names.include?("url") && matches[:url]
matches[:url]
else
- url_for_object_cached(object, project)
+ url_for_object_cached(object, parent)
end
content = link_content || object_link_text(object, matches)
@@ -224,17 +229,24 @@ module Banzai
# Returns a Hash containing all object references (e.g. issue IDs) per the
# project they belong to.
- def references_per_project
- @references_per_project ||= begin
+ def references_per_parent
+ @references_per ||= {}
+
+ @references_per[parent_type] ||= begin
refs = Hash.new { |hash, key| hash[key] = Set.new }
regex = Regexp.union(object_class.reference_pattern, object_class.link_reference_pattern)
nodes.each do |node|
node.to_html.scan(regex) do
- project_path = full_project_path($~[:namespace], $~[:project])
+ path = if parent_type == :project
+ full_project_path($~[:namespace], $~[:project])
+ else
+ full_group_path($~[:group])
+ end
+
symbol = $~[object_sym]
- refs[project_path] << symbol if object_class.reference_valid?(symbol)
+ refs[path] << symbol if object_class.reference_valid?(symbol)
end
end
@@ -244,35 +256,41 @@ module Banzai
# Returns a Hash containing referenced projects grouped per their full
# path.
- def projects_per_reference
- @projects_per_reference ||= begin
+ def parent_per_reference
+ @per_reference ||= {}
+
+ @per_reference[parent_type] ||= begin
refs = Set.new
- references_per_project.each do |project_ref, _|
- refs << project_ref
+ references_per_parent.each do |ref, _|
+ refs << ref
end
- find_projects_for_paths(refs.to_a).index_by(&:full_path)
+ find_for_paths(refs.to_a).index_by(&:full_path)
end
end
- def projects_relation_for_paths(paths)
- Project.where_full_path_in(paths).includes(:namespace)
+ def relation_for_paths(paths)
+ klass = parent_type.to_s.camelize.constantize
+ result = klass.where_full_path_in(paths)
+ return result if parent_type == :group
+
+ result.includes(:namespace) if parent_type == :project
end
# Returns projects for the given paths.
- def find_projects_for_paths(paths)
+ def find_for_paths(paths)
if RequestStore.active?
- cache = project_refs_cache
+ cache = refs_cache
to_query = paths - cache.keys
unless to_query.empty?
- projects = projects_relation_for_paths(to_query)
+ records = relation_for_paths(to_query)
found = []
- projects.each do |project|
- ref = project.full_path
- get_or_set_cache(cache, ref) { project }
+ records.each do |record|
+ ref = record.full_path
+ get_or_set_cache(cache, ref) { record }
found << ref
end
@@ -284,33 +302,37 @@ module Banzai
cache.slice(*paths).values.compact
else
- projects_relation_for_paths(paths)
+ relation_for_paths(paths)
end
end
- def current_project_path
- return unless project
-
- @current_project_path ||= project.full_path
+ def current_parent_path
+ @current_parent_path ||= parent&.full_path
end
def current_project_namespace_path
- return unless project
-
- @current_project_namespace_path ||= project.namespace.full_path
+ @current_project_namespace_path ||= project&.namespace&.full_path
end
private
def full_project_path(namespace, project_ref)
- return current_project_path unless project_ref
+ return current_parent_path unless project_ref
namespace_ref = namespace || current_project_namespace_path
"#{namespace_ref}/#{project_ref}"
end
- def project_refs_cache
- RequestStore[:banzai_project_refs] ||= {}
+ def refs_cache
+ RequestStore["banzai_#{parent_type}_refs".to_sym] ||= {}
+ end
+
+ def parent_type
+ :project
+ end
+
+ def parent
+ parent_type == :project ? project : group
end
end
end
diff --git a/lib/banzai/filter/epic_reference_filter.rb b/lib/banzai/filter/epic_reference_filter.rb
new file mode 100644
index 00000000000..265924abe24
--- /dev/null
+++ b/lib/banzai/filter/epic_reference_filter.rb
@@ -0,0 +1,12 @@
+module Banzai
+ module Filter
+ # The actual filter is implemented in the EE mixin
+ class EpicReferenceFilter < IssuableReferenceFilter
+ self.reference_type = :epic
+
+ def self.object_class
+ Epic
+ end
+ end
+ end
+end
diff --git a/lib/banzai/filter/issuable_reference_filter.rb b/lib/banzai/filter/issuable_reference_filter.rb
new file mode 100644
index 00000000000..7addf09be73
--- /dev/null
+++ b/lib/banzai/filter/issuable_reference_filter.rb
@@ -0,0 +1,31 @@
+module Banzai
+ module Filter
+ class IssuableReferenceFilter < AbstractReferenceFilter
+ def records_per_parent
+ @records_per_project ||= {}
+
+ @records_per_project[object_class.to_s.underscore] ||= begin
+ hash = Hash.new { |h, k| h[k] = {} }
+
+ parent_per_reference.each do |path, parent|
+ record_ids = references_per_parent[path]
+
+ parent_records(parent, record_ids).each do |record|
+ hash[parent][record.iid.to_i] = record
+ end
+ end
+
+ hash
+ end
+ end
+
+ def find_object(parent, iid)
+ records_per_parent[parent][iid]
+ end
+
+ def parent_from_ref(ref)
+ parent_per_reference[ref || current_parent_path]
+ end
+ end
+ end
+end
diff --git a/lib/banzai/filter/issue_reference_filter.rb b/lib/banzai/filter/issue_reference_filter.rb
index ce1ab977d3b..6877cae8c55 100644
--- a/lib/banzai/filter/issue_reference_filter.rb
+++ b/lib/banzai/filter/issue_reference_filter.rb
@@ -8,46 +8,24 @@ module Banzai
# When external issues tracker like Jira is activated we should not
# use issue reference pattern, but we should still be able
# to reference issues from other GitLab projects.
- class IssueReferenceFilter < AbstractReferenceFilter
+ class IssueReferenceFilter < IssuableReferenceFilter
self.reference_type = :issue
def self.object_class
Issue
end
- def find_object(project, iid)
- issues_per_project[project][iid]
- end
-
def url_for_object(issue, project)
IssuesHelper.url_for_issue(issue.iid, project, only_path: context[:only_path], internal: true)
end
- def project_from_ref(ref)
- projects_per_reference[ref || current_project_path]
- end
-
- # Returns a Hash containing the issues per Project instance.
- def issues_per_project
- @issues_per_project ||= begin
- hash = Hash.new { |h, k| h[k] = {} }
-
- projects_per_reference.each do |path, project|
- issue_ids = references_per_project[path]
- issues = project.issues.where(iid: issue_ids.to_a)
-
- issues.each do |issue|
- hash[project][issue.iid.to_i] = issue
- end
- end
-
- hash
- end
- end
-
def projects_relation_for_paths(paths)
super(paths).includes(:gitlab_issue_tracker_service)
end
+
+ def parent_records(parent, ids)
+ parent.issues.where(iid: ids.to_a)
+ end
end
end
end
diff --git a/lib/banzai/filter/label_reference_filter.rb b/lib/banzai/filter/label_reference_filter.rb
index 5364984c9d3..d5360ad8f68 100644
--- a/lib/banzai/filter/label_reference_filter.rb
+++ b/lib/banzai/filter/label_reference_filter.rb
@@ -33,7 +33,7 @@ module Banzai
end
def find_label(project_ref, label_id, label_name)
- project = project_from_ref(project_ref)
+ project = parent_from_ref(project_ref)
return unless project
label_params = label_params(label_id, label_name)
@@ -66,7 +66,7 @@ module Banzai
def object_link_text(object, matches)
project_path = full_project_path(matches[:namespace], matches[:project])
- project_from_ref = project_from_ref_cached(project_path)
+ project_from_ref = from_ref_cached(project_path)
reference = project_from_ref.to_human_reference(project)
label_suffix = " <i>in #{reference}</i>" if reference.present?
diff --git a/lib/banzai/filter/merge_request_reference_filter.rb b/lib/banzai/filter/merge_request_reference_filter.rb
index 0eab865ac04..b3cfa97d0e0 100644
--- a/lib/banzai/filter/merge_request_reference_filter.rb
+++ b/lib/banzai/filter/merge_request_reference_filter.rb
@@ -4,48 +4,19 @@ module Banzai
# to merge requests that do not exist are ignored.
#
# This filter supports cross-project references.
- class MergeRequestReferenceFilter < AbstractReferenceFilter
+ class MergeRequestReferenceFilter < IssuableReferenceFilter
self.reference_type = :merge_request
def self.object_class
MergeRequest
end
- def find_object(project, iid)
- merge_requests_per_project[project][iid]
- end
-
def url_for_object(mr, project)
h = Gitlab::Routing.url_helpers
h.project_merge_request_url(project, mr,
only_path: context[:only_path])
end
- def project_from_ref(ref)
- projects_per_reference[ref || current_project_path]
- end
-
- # Returns a Hash containing the merge_requests per Project instance.
- def merge_requests_per_project
- @merge_requests_per_project ||= begin
- hash = Hash.new { |h, k| h[k] = {} }
-
- projects_per_reference.each do |path, project|
- merge_request_ids = references_per_project[path]
-
- merge_requests = project.merge_requests
- .where(iid: merge_request_ids.to_a)
- .includes(target_project: :namespace)
-
- merge_requests.each do |merge_request|
- hash[project][merge_request.iid.to_i] = merge_request
- end
- end
-
- hash
- end
- end
-
def object_link_text_extras(object, matches)
extras = super
@@ -61,6 +32,12 @@ module Banzai
extras
end
+
+ def parent_records(parent, ids)
+ parent.merge_requests
+ .where(iid: ids.to_a)
+ .includes(target_project: :namespace)
+ end
end
end
end
diff --git a/lib/banzai/filter/milestone_reference_filter.rb b/lib/banzai/filter/milestone_reference_filter.rb
index bb5da310e09..2a6b0964ac5 100644
--- a/lib/banzai/filter/milestone_reference_filter.rb
+++ b/lib/banzai/filter/milestone_reference_filter.rb
@@ -38,7 +38,7 @@ module Banzai
def find_milestone(project_ref, namespace_ref, milestone_id, milestone_name)
project_path = full_project_path(namespace_ref, project_ref)
- project = project_from_ref(project_path)
+ project = parent_from_ref(project_path)
return unless project
diff --git a/lib/banzai/issuable_extractor.rb b/lib/banzai/issuable_extractor.rb
index cbabf9156de..49603d0b363 100644
--- a/lib/banzai/issuable_extractor.rb
+++ b/lib/banzai/issuable_extractor.rb
@@ -28,8 +28,8 @@ module Banzai
issue_parser = Banzai::ReferenceParser::IssueParser.new(project, user)
merge_request_parser = Banzai::ReferenceParser::MergeRequestParser.new(project, user)
- issuables_for_nodes = issue_parser.issues_for_nodes(nodes).merge(
- merge_request_parser.merge_requests_for_nodes(nodes)
+ issuables_for_nodes = issue_parser.records_for_nodes(nodes).merge(
+ merge_request_parser.records_for_nodes(nodes)
)
# The project for the issue/MR might be pending for deletion!
diff --git a/lib/banzai/reference_parser/epic_parser.rb b/lib/banzai/reference_parser/epic_parser.rb
new file mode 100644
index 00000000000..08b8a4c9a0f
--- /dev/null
+++ b/lib/banzai/reference_parser/epic_parser.rb
@@ -0,0 +1,12 @@
+module Banzai
+ module ReferenceParser
+ # The actual parser is implemented in the EE mixin
+ class EpicParser < IssuableParser
+ self.reference_type = :epic
+
+ def records_for_nodes(_nodes)
+ {}
+ end
+ end
+ end
+end
diff --git a/lib/banzai/reference_parser/issuable_parser.rb b/lib/banzai/reference_parser/issuable_parser.rb
new file mode 100644
index 00000000000..3953867eb83
--- /dev/null
+++ b/lib/banzai/reference_parser/issuable_parser.rb
@@ -0,0 +1,25 @@
+module Banzai
+ module ReferenceParser
+ class IssuableParser < BaseParser
+ def nodes_visible_to_user(user, nodes)
+ records = records_for_nodes(nodes)
+
+ nodes.select do |node|
+ issuable = records[node]
+
+ issuable && can_read_reference?(user, issuable)
+ end
+ end
+
+ def referenced_by(nodes)
+ records = records_for_nodes(nodes)
+
+ nodes.map { |node| records[node] }.compact.uniq
+ end
+
+ def can_read_reference?(user, issuable)
+ can?(user, "read_#{issuable.class.to_s.underscore}".to_sym, issuable)
+ end
+ end
+ end
+end
diff --git a/lib/banzai/reference_parser/issue_parser.rb b/lib/banzai/reference_parser/issue_parser.rb
index e0a8ca653cb..38d4e3f3e44 100644
--- a/lib/banzai/reference_parser/issue_parser.rb
+++ b/lib/banzai/reference_parser/issue_parser.rb
@@ -1,10 +1,10 @@
module Banzai
module ReferenceParser
- class IssueParser < BaseParser
+ class IssueParser < IssuableParser
self.reference_type = :issue
def nodes_visible_to_user(user, nodes)
- issues = issues_for_nodes(nodes)
+ issues = records_for_nodes(nodes)
readable_issues = Ability
.issues_readable_by_user(issues.values, user).to_set
@@ -14,13 +14,7 @@ module Banzai
end
end
- def referenced_by(nodes)
- issues = issues_for_nodes(nodes)
-
- nodes.map { |node| issues[node] }.compact.uniq
- end
-
- def issues_for_nodes(nodes)
+ def records_for_nodes(nodes)
@issues_for_nodes ||= grouped_objects_for_nodes(
nodes,
Issue.all.includes(
diff --git a/lib/banzai/reference_parser/merge_request_parser.rb b/lib/banzai/reference_parser/merge_request_parser.rb
index 75cbc7fdac4..a370ff5b5b3 100644
--- a/lib/banzai/reference_parser/merge_request_parser.rb
+++ b/lib/banzai/reference_parser/merge_request_parser.rb
@@ -1,25 +1,9 @@
module Banzai
module ReferenceParser
- class MergeRequestParser < BaseParser
+ class MergeRequestParser < IssuableParser
self.reference_type = :merge_request
- def nodes_visible_to_user(user, nodes)
- merge_requests = merge_requests_for_nodes(nodes)
-
- nodes.select do |node|
- merge_request = merge_requests[node]
-
- merge_request && can?(user, :read_merge_request, merge_request.project)
- end
- end
-
- def referenced_by(nodes)
- merge_requests = merge_requests_for_nodes(nodes)
-
- nodes.map { |node| merge_requests[node] }.compact.uniq
- end
-
- def merge_requests_for_nodes(nodes)
+ def records_for_nodes(nodes)
@merge_requests_for_nodes ||= grouped_objects_for_nodes(
nodes,
MergeRequest.includes(
@@ -40,10 +24,6 @@ module Banzai
self.class.data_attribute
)
end
-
- def can_read_reference?(user, ref_project, node)
- can?(user, :read_merge_request, ref_project)
- end
end
end
end