Skip to content
Snippets Groups Projects
Commit 67312fce authored by Robert Speicher's avatar Robert Speicher
Browse files

Merge branch 'dm-blob-viewer-concerns' into 'master'

Move some blob viewer stuff around without changing behavior

See merge request !11358
parents e66b811d b0a16320
No related branches found
No related tags found
No related merge requests found
Showing
with 125 additions and 113 deletions
Loading
Loading
@@ -14,7 +14,7 @@ module RendersBlob
return render_404 unless viewer
 
render json: {
html: view_to_html_string("projects/blob/_viewer", viewer: viewer, load_asynchronously: false)
html: view_to_html_string("projects/blob/_viewer", viewer: viewer, load_async: false)
}
end
 
Loading
Loading
Loading
Loading
@@ -226,7 +226,7 @@ module BlobHelper
 
def open_raw_blob_button(blob)
return if blob.empty?
if blob.raw_binary? || blob.stored_externally?
icon = icon('download')
title = 'Download'
Loading
Loading
@@ -242,9 +242,9 @@ module BlobHelper
case viewer.render_error
when :too_large
max_size =
if viewer.absolutely_too_large?
viewer.absolute_max_size
elsif viewer.too_large?
if viewer.can_override_max_size?
viewer.overridable_max_size
else
viewer.max_size
end
"it is larger than #{number_to_human_size(max_size)}"
Loading
Loading
Loading
Loading
@@ -5,8 +5,8 @@ module BlobViewer
included do
self.loading_partial_name = 'loading_auxiliary'
self.type = :auxiliary
self.overridable_max_size = 100.kilobytes
self.max_size = 100.kilobytes
self.absolute_max_size = 100.kilobytes
end
end
end
Loading
Loading
@@ -2,11 +2,11 @@ module BlobViewer
class Base
PARTIAL_PATH_PREFIX = 'projects/blob/viewers'.freeze
 
class_attribute :partial_name, :loading_partial_name, :type, :extensions, :file_type, :client_side, :binary, :switcher_icon, :switcher_title, :max_size, :absolute_max_size
class_attribute :partial_name, :loading_partial_name, :type, :extensions, :file_types, :load_async, :binary, :switcher_icon, :switcher_title, :overridable_max_size, :max_size
 
self.loading_partial_name = 'loading'
 
delegate :partial_path, :loading_partial_path, :rich?, :simple?, :client_side?, :server_side?, :text?, :binary?, to: :class
delegate :partial_path, :loading_partial_path, :rich?, :simple?, :text?, :binary?, to: :class
 
attr_reader :blob
attr_accessor :override_max_size
Loading
Loading
@@ -35,12 +35,8 @@ module BlobViewer
type == :auxiliary
end
 
def self.client_side?
client_side
end
def self.server_side?
!client_side?
def self.load_async?
load_async
end
 
def self.binary?
Loading
Loading
@@ -54,21 +50,33 @@ module BlobViewer
def self.can_render?(blob, verify_binary: true)
return false if verify_binary && binary? != blob.binary?
return true if extensions&.include?(blob.extension)
return true if file_type && Gitlab::FileDetector.type_of(blob.path) == file_type
return true if file_types&.include?(Gitlab::FileDetector.type_of(blob.path))
 
false
end
 
def too_large?
blob.raw_size > max_size
def load_async?
self.class.load_async? && render_error.nil?
end
 
def absolutely_too_large?
blob.raw_size > absolute_max_size
def exceeds_overridable_max_size?
overridable_max_size && blob.raw_size > overridable_max_size
end
def exceeds_max_size?
max_size && blob.raw_size > max_size
end
 
def can_override_max_size?
too_large? && !absolutely_too_large?
exceeds_overridable_max_size? && !exceeds_max_size?
end
def too_large?
if override_max_size
exceeds_max_size?
else
exceeds_overridable_max_size?
end
end
 
# This method is used on the server side to check whether we can attempt to
Loading
Loading
@@ -83,29 +91,13 @@ module BlobViewer
# binary from `blob_raw_url` and does its own format validation and error
# rendering, especially for potentially large binary formats.
def render_error
return @render_error if defined?(@render_error)
@render_error =
if server_side_but_stored_externally?
# Files that are not stored in the repository, like LFS files and
# build artifacts, can only be rendered using a client-side viewer,
# since we do not want to read large amounts of data into memory on the
# server side. Client-side viewers use JS and can fetch the file from
# `blob_raw_url` using AJAX.
:server_side_but_stored_externally
elsif override_max_size ? absolutely_too_large? : too_large?
:too_large
end
if too_large?
:too_large
end
end
 
def prepare!
# To be overridden by subclasses
end
private
def server_side_but_stored_externally?
server_side? && blob.stored_externally?
end
end
end
Loading
Loading
@@ -3,9 +3,9 @@ module BlobViewer
extend ActiveSupport::Concern
 
included do
self.client_side = true
self.max_size = 10.megabytes
self.absolute_max_size = 50.megabytes
self.load_async = false
self.overridable_max_size = 10.megabytes
self.max_size = 50.megabytes
end
end
end
module BlobViewer
class Download < Base
include Simple
# We treat the Download viewer as if it renders the content client-side,
# so that it doesn't attempt to load the entire blob contents and is
# rendered synchronously instead of loaded asynchronously.
include ClientSide
include Static
 
self.partial_name = 'download'
self.binary = true
# We can always render the Download viewer, even if the blob is in LFS or too large.
def render_error
nil
end
end
end
Loading
Loading
@@ -5,7 +5,7 @@ module BlobViewer
 
self.partial_name = 'gitlab_ci_yml'
self.loading_partial_name = 'gitlab_ci_yml_loading'
self.file_type = :gitlab_ci
self.file_types = %i(gitlab_ci)
self.binary = false
 
def validation_message
Loading
Loading
module BlobViewer
class License < Base
# We treat the License viewer as if it renders the content client-side,
# so that it doesn't attempt to load the entire blob contents and is
# rendered synchronously instead of loaded asynchronously.
include ClientSide
include Auxiliary
include Static
 
self.partial_name = 'license'
self.file_type = :license
self.file_types = %i(license)
self.binary = false
 
def license
Loading
Loading
Loading
Loading
@@ -5,7 +5,7 @@ module BlobViewer
 
self.partial_name = 'route_map'
self.loading_partial_name = 'route_map_loading'
self.file_type = :route_map
self.file_types = %i(route_map)
self.binary = false
 
def validation_message
Loading
Loading
Loading
Loading
@@ -3,9 +3,9 @@ module BlobViewer
extend ActiveSupport::Concern
 
included do
self.client_side = false
self.max_size = 2.megabytes
self.absolute_max_size = 5.megabytes
self.load_async = true
self.overridable_max_size = 2.megabytes
self.max_size = 5.megabytes
end
 
def prepare!
Loading
Loading
@@ -13,5 +13,18 @@ module BlobViewer
blob.load_all_data!(blob.project.repository)
end
end
def render_error
if blob.stored_externally?
# Files that are not stored in the repository, like LFS files and
# build artifacts, can only be rendered using a client-side viewer,
# since we do not want to read large amounts of data into memory on the
# server side. Client-side viewers use JS and can fetch the file from
# `blob_raw_url` using AJAX.
return :server_side_but_stored_externally
end
super
end
end
end
module BlobViewer
module Static
extend ActiveSupport::Concern
included do
self.load_async = false
end
# We can always render a static viewer, even if the blob is too large.
def render_error
nil
end
end
end
Loading
Loading
@@ -5,7 +5,7 @@ module BlobViewer
 
self.partial_name = 'text'
self.binary = false
self.max_size = 1.megabyte
self.absolute_max_size = 10.megabytes
self.overridable_max_size = 1.megabyte
self.max_size = 10.megabytes
end
end
- hidden = local_assigns.fetch(:hidden, false)
- render_error = viewer.render_error
- load_asynchronously = local_assigns.fetch(:load_asynchronously, viewer.server_side?) && render_error.nil?
- load_async = local_assigns.fetch(:load_async, viewer.load_async?)
 
- viewer_url = local_assigns.fetch(:viewer_url) { url_for(params.merge(viewer: viewer.type, format: :json)) } if load_asynchronously
- viewer_url = local_assigns.fetch(:viewer_url) { url_for(params.merge(viewer: viewer.type, format: :json)) } if load_async
.blob-viewer{ data: { type: viewer.type, url: viewer_url }, class: ('hidden' if hidden) }
- if load_asynchronously
- if load_async
= render viewer.loading_partial_path, viewer: viewer
- elsif render_error
= render 'projects/blob/render_error', viewer: viewer
Loading
Loading
Loading
Loading
@@ -116,10 +116,11 @@ describe BlobHelper do
 
let(:viewer_class) do
Class.new(BlobViewer::Base) do
self.max_size = 1.megabyte
self.absolute_max_size = 5.megabytes
include BlobViewer::ServerSide
self.overridable_max_size = 1.megabyte
self.max_size = 5.megabytes
self.type = :rich
self.client_side = false
end
end
 
Loading
Loading
Loading
Loading
@@ -7,11 +7,12 @@ describe BlobViewer::Base, model: true do
 
let(:viewer_class) do
Class.new(described_class) do
include BlobViewer::ServerSide
self.extensions = %w(pdf)
self.binary = true
self.max_size = 1.megabyte
self.absolute_max_size = 5.megabytes
self.client_side = false
self.overridable_max_size = 1.megabyte
self.max_size = 5.megabytes
end
end
 
Loading
Loading
@@ -38,10 +39,10 @@ describe BlobViewer::Base, model: true do
 
context 'when the file type is supported' do
before do
viewer_class.file_type = :license
viewer_class.file_types = %i(license)
viewer_class.binary = false
end
context 'when the binaryness matches' do
let(:blob) { fake_blob(path: 'LICENSE', binary: false) }
 
Loading
Loading
@@ -68,45 +69,45 @@ describe BlobViewer::Base, model: true do
end
end
 
describe '#too_large?' do
context 'when the blob size is larger than the max size' do
describe '#exceeds_overridable_max_size?' do
context 'when the blob size is larger than the overridable max size' do
let(:blob) { fake_blob(path: 'file.pdf', size: 2.megabytes) }
 
it 'returns true' do
expect(viewer.too_large?).to be_truthy
expect(viewer.exceeds_overridable_max_size?).to be_truthy
end
end
 
context 'when the blob size is smaller than the max size' do
context 'when the blob size is smaller than the overridable max size' do
let(:blob) { fake_blob(path: 'file.pdf', size: 10.kilobytes) }
 
it 'returns false' do
expect(viewer.too_large?).to be_falsey
expect(viewer.exceeds_overridable_max_size?).to be_falsey
end
end
end
 
describe '#absolutely_too_large?' do
context 'when the blob size is larger than the absolute max size' do
describe '#exceeds_max_size?' do
context 'when the blob size is larger than the max size' do
let(:blob) { fake_blob(path: 'file.pdf', size: 10.megabytes) }
 
it 'returns true' do
expect(viewer.absolutely_too_large?).to be_truthy
expect(viewer.exceeds_max_size?).to be_truthy
end
end
 
context 'when the blob size is smaller than the absolute max size' do
context 'when the blob size is smaller than the max size' do
let(:blob) { fake_blob(path: 'file.pdf', size: 2.megabytes) }
 
it 'returns false' do
expect(viewer.absolutely_too_large?).to be_falsey
expect(viewer.exceeds_max_size?).to be_falsey
end
end
end
 
describe '#can_override_max_size?' do
context 'when the blob size is larger than the max size' do
context 'when the blob size is larger than the absolute max size' do
context 'when the blob size is larger than the overridable max size' do
context 'when the blob size is larger than the max size' do
let(:blob) { fake_blob(path: 'file.pdf', size: 10.megabytes) }
 
it 'returns false' do
Loading
Loading
@@ -114,7 +115,7 @@ describe BlobViewer::Base, model: true do
end
end
 
context 'when the blob size is smaller than the absolute max size' do
context 'when the blob size is smaller than the max size' do
let(:blob) { fake_blob(path: 'file.pdf', size: 2.megabytes) }
 
it 'returns true' do
Loading
Loading
@@ -123,7 +124,7 @@ describe BlobViewer::Base, model: true do
end
end
 
context 'when the blob size is smaller than the max size' do
context 'when the blob size is smaller than the overridable max size' do
let(:blob) { fake_blob(path: 'file.pdf', size: 10.kilobytes) }
 
it 'returns false' do
Loading
Loading
@@ -138,7 +139,7 @@ describe BlobViewer::Base, model: true do
viewer.override_max_size = true
end
 
context 'when the blob size is larger than the absolute max size' do
context 'when the blob size is larger than the max size' do
let(:blob) { fake_blob(path: 'file.pdf', size: 10.megabytes) }
 
it 'returns :too_large' do
Loading
Loading
@@ -146,7 +147,7 @@ describe BlobViewer::Base, model: true do
end
end
 
context 'when the blob size is smaller than the absolute max size' do
context 'when the blob size is smaller than the max size' do
let(:blob) { fake_blob(path: 'file.pdf', size: 2.megabytes) }
 
it 'returns nil' do
Loading
Loading
@@ -156,7 +157,7 @@ describe BlobViewer::Base, model: true do
end
 
context 'when the max size is not overridden' do
context 'when the blob size is larger than the max size' do
context 'when the blob size is larger than the overridable max size' do
let(:blob) { fake_blob(path: 'file.pdf', size: 2.megabytes) }
 
it 'returns :too_large' do
Loading
Loading
@@ -164,7 +165,7 @@ describe BlobViewer::Base, model: true do
end
end
 
context 'when the blob size is smaller than the max size' do
context 'when the blob size is smaller than the overridable max size' do
let(:blob) { fake_blob(path: 'file.pdf', size: 10.kilobytes) }
 
it 'returns nil' do
Loading
Loading
@@ -172,19 +173,5 @@ describe BlobViewer::Base, model: true do
end
end
end
context 'when the viewer is server side but the blob is stored externally' do
let(:project) { build(:empty_project, lfs_enabled: true) }
let(:blob) { fake_blob(path: 'file.pdf', lfs: true) }
before do
allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
end
it 'return :server_side_but_stored_externally' do
expect(viewer.render_error).to eq(:server_side_but_stored_externally)
end
end
end
end
Loading
Loading
@@ -22,4 +22,20 @@ describe BlobViewer::ServerSide, model: true do
subject.prepare!
end
end
describe '#render_error' do
context 'when the blob is stored externally' do
let(:project) { build(:empty_project, lfs_enabled: true) }
let(:blob) { fake_blob(path: 'file.pdf', lfs: true) }
before do
allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
end
it 'return :server_side_but_stored_externally' do
expect(subject.render_error).to eq(:server_side_but_stored_externally)
end
end
end
end
Loading
Loading
@@ -10,9 +10,9 @@ describe 'projects/blob/_viewer.html.haml', :view do
include BlobViewer::Rich
 
self.partial_name = 'text'
self.max_size = 1.megabyte
self.absolute_max_size = 5.megabytes
self.client_side = false
self.overridable_max_size = 1.megabyte
self.max_size = 5.megabytes
self.load_async = true
end
end
 
Loading
Loading
@@ -35,9 +35,9 @@ describe 'projects/blob/_viewer.html.haml', :view do
render partial: 'projects/blob/viewer', locals: { viewer: viewer }
end
 
context 'when the viewer is server side' do
context 'when the viewer is loaded asynchronously' do
before do
viewer_class.client_side = false
viewer_class.load_async = true
end
 
context 'when there is no render error' do
Loading
Loading
@@ -65,9 +65,9 @@ describe 'projects/blob/_viewer.html.haml', :view do
end
end
 
context 'when the viewer is client side' do
context 'when the viewer is loaded synchronously' do
before do
viewer_class.client_side = true
viewer_class.load_async = false
end
 
context 'when there is no render error' do
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment