Commit 7cfbf326 authored by Sean McGivern's avatar Sean McGivern

Avoid Gitaly calls when a document has only upload links

Now that we have the UploadLinkFilter, the RepositoryLinkFilter should
be a no-op (in particular, making no Gitaly calls) when it doesn't have
anything to do. To ensure that happens:

1. Remove upload handling from RepositoryLinkFilter.
2. Add a spec to ensure that this case generates no Gitaly calls.
3. Move common code to a mixin.
parent b95f5ab5
---
title: Avoid making Gitaly calls when some Markdown text links to an uploaded file
merge_request: 22631
author:
type: performance
# frozen_string_literal: true
require 'uri'
module Banzai
module Filter
class BaseRelativeLinkFilter < HTML::Pipeline::Filter
include Gitlab::Utils::StrongMemoize
protected
def linkable_attributes
strong_memoize(:linkable_attributes) do
attrs = []
attrs += doc.search('a:not(.gfm)').map do |el|
el.attribute('href')
end
attrs += doc.search('img:not(.gfm), video:not(.gfm), audio:not(.gfm)').flat_map do |el|
[el.attribute('src'), el.attribute('data-src')]
end
attrs.reject do |attr|
attr.blank? || attr.value.start_with?('//')
end
end
end
def relative_url_root
Gitlab.config.gitlab.relative_url_root.presence || '/'
end
def project
context[:project]
end
private
def unescape_and_scrub_uri(uri)
Addressable::URI.unescape(uri).scrub
end
end
end
end
...@@ -8,15 +8,13 @@ module Banzai ...@@ -8,15 +8,13 @@ module Banzai
# #
# Context options: # Context options:
# :commit # :commit
# :group
# :current_user # :current_user
# :project # :project
# :project_wiki # :project_wiki
# :ref # :ref
# :requested_path # :requested_path
class RepositoryLinkFilter < HTML::Pipeline::Filter # :system_note
include Gitlab::Utils::StrongMemoize class RepositoryLinkFilter < BaseRelativeLinkFilter
def call def call
return doc if context[:system_note] return doc if context[:system_note]
...@@ -26,7 +24,9 @@ module Banzai ...@@ -26,7 +24,9 @@ module Banzai
load_uri_types load_uri_types
linkable_attributes.each do |attr| linkable_attributes.each do |attr|
process_link_attr(attr) if linkable_files? && repo_visible_to_user?
process_link_to_repository_attr(attr)
end
end end
doc doc
...@@ -35,8 +35,8 @@ module Banzai ...@@ -35,8 +35,8 @@ module Banzai
protected protected
def load_uri_types def load_uri_types
return unless linkable_files?
return unless linkable_attributes.present? return unless linkable_attributes.present?
return unless linkable_files?
return {} unless repository return {} unless repository
@uri_types = request_path.present? ? get_uri_types([request_path]) : {} @uri_types = request_path.present? ? get_uri_types([request_path]) : {}
...@@ -57,24 +57,6 @@ module Banzai ...@@ -57,24 +57,6 @@ module Banzai
end end
end end
def linkable_attributes
strong_memoize(:linkable_attributes) do
attrs = []
attrs += doc.search('a:not(.gfm)').map do |el|
el.attribute('href')
end
attrs += doc.search('img:not(.gfm), video:not(.gfm), audio:not(.gfm)').flat_map do |el|
[el.attribute('src'), el.attribute('data-src')]
end
attrs.reject do |attr|
attr.blank? || attr.value.start_with?('//')
end
end
end
def get_uri_types(paths) def get_uri_types(paths)
return {} if paths.empty? return {} if paths.empty?
...@@ -107,39 +89,6 @@ module Banzai ...@@ -107,39 +89,6 @@ module Banzai
rescue URI::Error, Addressable::URI::InvalidURIError rescue URI::Error, Addressable::URI::InvalidURIError
end end
def process_link_attr(html_attr)
if html_attr.value.start_with?('/uploads/')
process_link_to_upload_attr(html_attr)
elsif linkable_files? && repo_visible_to_user?
process_link_to_repository_attr(html_attr)
end
end
def process_link_to_upload_attr(html_attr)
path_parts = [unescape_and_scrub_uri(html_attr.value)]
if project
path_parts.unshift(relative_url_root, project.full_path)
elsif group
path_parts.unshift(relative_url_root, 'groups', group.full_path, '-')
else
path_parts.unshift(relative_url_root)
end
begin
path = Addressable::URI.escape(File.join(*path_parts))
rescue Addressable::URI::InvalidURIError
return
end
html_attr.value =
if context[:only_path]
path
else
Addressable::URI.join(Gitlab.config.gitlab.base_url, path).to_s
end
end
def process_link_to_repository_attr(html_attr) def process_link_to_repository_attr(html_attr)
uri = URI(html_attr.value) uri = URI(html_attr.value)
...@@ -239,10 +188,6 @@ module Banzai ...@@ -239,10 +188,6 @@ module Banzai
@current_commit ||= context[:commit] || repository.commit(ref) @current_commit ||= context[:commit] || repository.commit(ref)
end end
def relative_url_root
Gitlab.config.gitlab.relative_url_root.presence || '/'
end
def repo_visible_to_user? def repo_visible_to_user?
project && Ability.allowed?(current_user, :download_code, project) project && Ability.allowed?(current_user, :download_code, project)
end end
...@@ -251,14 +196,6 @@ module Banzai ...@@ -251,14 +196,6 @@ module Banzai
context[:ref] || project.default_branch context[:ref] || project.default_branch
end end
def group
context[:group]
end
def project
context[:project]
end
def current_user def current_user
context[:current_user] context[:current_user]
end end
...@@ -266,12 +203,6 @@ module Banzai ...@@ -266,12 +203,6 @@ module Banzai
def repository def repository
@repository ||= project&.repository @repository ||= project&.repository
end end
private
def unescape_and_scrub_uri(uri)
Addressable::URI.unescape(uri).scrub
end
end end
end end
end end
...@@ -11,17 +11,12 @@ module Banzai ...@@ -11,17 +11,12 @@ module Banzai
# :only_path # :only_path
# :project # :project
# :system_note # :system_note
class UploadLinkFilter < HTML::Pipeline::Filter class UploadLinkFilter < BaseRelativeLinkFilter
include Gitlab::Utils::StrongMemoize
def call def call
return doc if context[:system_note] return doc if context[:system_note]
linkable_attributes.each do |attr| linkable_attributes.each do |attr|
if attr.value.start_with?('/uploads/') process_link_to_upload_attr(attr)
process_link_to_upload_attr(attr)
attr.parent.add_class('gfm')
end
end end
doc doc
...@@ -29,25 +24,9 @@ module Banzai ...@@ -29,25 +24,9 @@ module Banzai
protected protected
def linkable_attributes
strong_memoize(:linkable_attributes) do
attrs = []
attrs += doc.search('a:not(.gfm)').map do |el|
el.attribute('href')
end
attrs += doc.search('img:not(.gfm), video:not(.gfm), audio:not(.gfm)').flat_map do |el|
[el.attribute('src'), el.attribute('data-src')]
end
attrs.reject do |attr|
attr.blank? || attr.value.start_with?('//')
end
end
end
def process_link_to_upload_attr(html_attr) def process_link_to_upload_attr(html_attr)
return unless html_attr.value.start_with?('/uploads/')
path_parts = [unescape_and_scrub_uri(html_attr.value)] path_parts = [unescape_and_scrub_uri(html_attr.value)]
if project if project
...@@ -70,25 +49,13 @@ module Banzai ...@@ -70,25 +49,13 @@ module Banzai
else else
Addressable::URI.join(Gitlab.config.gitlab.base_url, path).to_s Addressable::URI.join(Gitlab.config.gitlab.base_url, path).to_s
end end
end
def relative_url_root html_attr.parent.add_class('gfm')
Gitlab.config.gitlab.relative_url_root.presence || '/'
end end
def group def group
context[:group] context[:group]
end end
def project
context[:project]
end
private
def unescape_and_scrub_uri(uri)
Addressable::URI.unescape(uri).scrub
end
end end
end end
end end
# frozen_string_literal: true
require 'spec_helper'
describe Banzai::Pipeline::PostProcessPipeline do
context 'when a document only has upload links' do
it 'does not make any Gitaly calls', :request_store do
markdown = <<-MARKDOWN.strip_heredoc
[Relative Upload Link](/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg)
![Relative Upload Image](/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg)
MARKDOWN
context = {
project: create(:project, :public, :repository),
ref: 'master'
}
Gitlab::GitalyClient.reset_counts
described_class.call(markdown, context)
expect(Gitlab::GitalyClient.get_request_count).to eq(0)
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment