summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDrew Blessing <drew@gitlab.com>2017-11-10 14:39:00 -0600
committerDrew Blessing <drew@gitlab.com>2017-11-15 08:28:33 -0600
commit03b1bcbb686f8e56fe6c6303adacdc01c5403790 (patch)
tree45e21665a23e6ce6ce616f91b1ea5901dcbccc48
parenta4072db0198896242886d22c644ed91c1016aa8d (diff)
downloadgitlab-ce-03b1bcbb686f8e56fe6c6303adacdc01c5403790.tar.gz
Truncate tree to max 1,000 items and display notice to users
Rendering ten thousands of tree items consumes a lot of server time and can cause timeouts in extreme cases. Realistically, displaying more than 1,000 files is probably not useful so truncate and show the user a notice instead. 'Find files' can be used to locate specific files beyond the 1,000 limit.
-rw-r--r--app/assets/stylesheets/pages/tree.scss7
-rw-r--r--app/helpers/tree_helper.rb15
-rw-r--r--app/views/projects/tree/_truncated_notice_tree_row.html.haml7
-rw-r--r--changelogs/unreleased/tree_item_limit.yml5
-rw-r--r--spec/helpers/tree_helper_spec.rb32
5 files changed, 59 insertions, 7 deletions
diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss
index 50f0ef4414a..65b334662c2 100644
--- a/app/assets/stylesheets/pages/tree.scss
+++ b/app/assets/stylesheets/pages/tree.scss
@@ -125,7 +125,7 @@
color: $white-normal;
}
- &:hover {
+ &:hover:not(.tree-truncated-warning) {
td {
background-color: $row-hover;
border-top: 1px solid $row-hover-border;
@@ -198,6 +198,11 @@
}
}
+ .tree-truncated-warning {
+ color: $orange-600;
+ background-color: $orange-100;
+ }
+
.tree-time-ago {
min-width: 135px;
color: $gl-text-color-secondary;
diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb
index c4ea0f5ac53..0e106e2c85d 100644
--- a/app/helpers/tree_helper.rb
+++ b/app/helpers/tree_helper.rb
@@ -1,14 +1,23 @@
module TreeHelper
+ FILE_LIMIT = 1_000
+
# Sorts a repository's tree so that folders are before files and renders
# their corresponding partials
#
- # contents - A Grit::Tree object for the current tree
+ # tree - A `Tree` object for the current tree
def render_tree(tree)
# Sort submodules and folders together by name ahead of files
folders, files, submodules = tree.trees, tree.blobs, tree.submodules
- tree = ""
+ tree = ''
items = (folders + submodules).sort_by(&:name) + files
- tree << render(partial: "projects/tree/tree_row", collection: items) if items.present?
+
+ if items.size > FILE_LIMIT
+ tree << render(partial: 'projects/tree/truncated_notice_tree_row',
+ locals: { limit: FILE_LIMIT, total: items.size })
+ items = items.take(FILE_LIMIT)
+ end
+
+ tree << render(partial: 'projects/tree/tree_row', collection: items) if items.present?
tree.html_safe
end
diff --git a/app/views/projects/tree/_truncated_notice_tree_row.html.haml b/app/views/projects/tree/_truncated_notice_tree_row.html.haml
new file mode 100644
index 00000000000..693b641888b
--- /dev/null
+++ b/app/views/projects/tree/_truncated_notice_tree_row.html.haml
@@ -0,0 +1,7 @@
+%tr.tree-truncated-warning
+ %td{ colspan: '3' }
+ = icon('exclamation-triangle fw')
+ %span
+ Too many items to show. To preserve performance only
+ %strong #{number_with_delimiter(limit)} of #{number_with_delimiter(total)}
+ items are displayed.
diff --git a/changelogs/unreleased/tree_item_limit.yml b/changelogs/unreleased/tree_item_limit.yml
new file mode 100644
index 00000000000..d95c5776075
--- /dev/null
+++ b/changelogs/unreleased/tree_item_limit.yml
@@ -0,0 +1,5 @@
+---
+title: Truncate tree to max 1,000 items and display notice to users
+merge_request:
+author:
+type: performance
diff --git a/spec/helpers/tree_helper_spec.rb b/spec/helpers/tree_helper_spec.rb
index d7b66e6f078..c358ccae9c3 100644
--- a/spec/helpers/tree_helper_spec.rb
+++ b/spec/helpers/tree_helper_spec.rb
@@ -1,10 +1,36 @@
require 'spec_helper'
describe TreeHelper do
+ let(:project) { create(:project, :repository) }
+ let(:repository) { project.repository }
+ let(:sha) { 'ce369011c189f62c815f5971d096b26759bab0d1' }
+
+ describe '.render_tree' do
+ before do
+ @id = sha
+ @project = project
+ end
+
+ it 'displays all entries without a warning' do
+ tree = repository.tree(sha, 'files')
+
+ html = render_tree(tree)
+
+ expect(html).not_to have_selector('.tree-truncated-warning')
+ end
+
+ it 'truncates entries and adds a warning' do
+ stub_const('TreeHelper::FILE_LIMIT', 1)
+ tree = repository.tree(sha, 'files')
+
+ html = render_tree(tree)
+
+ expect(html).to have_selector('.tree-truncated-warning', count: 1)
+ expect(html).to have_selector('.tree-item-file-name', count: 1)
+ end
+ end
+
describe 'flatten_tree' do
- let(:project) { create(:project, :repository) }
- let(:repository) { project.repository }
- let(:sha) { 'ce369011c189f62c815f5971d096b26759bab0d1' }
let(:tree) { repository.tree(sha, 'files') }
let(:root_path) { 'files' }
let(:tree_item) { tree.entries.find { |entry| entry.path == path } }