Specify explicitly whether a blob viewer should be loaded asynchronously

This commit is contained in:
Douwe Maan 2017-05-12 14:43:06 -05:00
parent 4328bc1769
commit acffc06213
12 changed files with 73 additions and 69 deletions

View File

@ -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

View File

@ -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_types, :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, :max_size, :absolute_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
@ -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?
@ -59,6 +55,10 @@ module BlobViewer
false
end
def load_async?
self.class.load_async? && render_error.nil?
end
def too_large?
max_size && blob.raw_size > max_size
end
@ -83,29 +83,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 override_max_size ? absolutely_too_large? : 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

View File

@ -3,7 +3,7 @@ module BlobViewer
extend ActiveSupport::Concern
included do
self.client_side = true
self.load_async = false
self.max_size = 10.megabytes
self.absolute_max_size = 50.megabytes
end

View File

@ -1,17 +1,9 @@
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

View File

@ -1,10 +1,7 @@
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_types = %i(license)

View File

@ -3,7 +3,7 @@ module BlobViewer
extend ActiveSupport::Concern
included do
self.client_side = false
self.load_async = true
self.max_size = 2.megabytes
self.absolute_max_size = 5.megabytes
end
@ -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

View File

@ -0,0 +1,14 @@
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

View File

@ -1,10 +1,10 @@
- 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

View File

@ -116,10 +116,11 @@ describe BlobHelper do
let(:viewer_class) do
Class.new(BlobViewer::Base) do
include BlobViewer::ServerSide
self.max_size = 1.megabyte
self.absolute_max_size = 5.megabytes
self.type = :rich
self.client_side = false
end
end

View File

@ -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
end
end
@ -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) }
@ -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

View File

@ -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

View File

@ -12,7 +12,7 @@ describe 'projects/blob/_viewer.html.haml', :view do
self.partial_name = 'text'
self.max_size = 1.megabyte
self.absolute_max_size = 5.megabytes
self.client_side = false
self.load_async = true
end
end
@ -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
@ -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