Commit 8c22ebb1 authored by Corinna Wiesner's avatar Corinna Wiesner Committed by Vitali Tatarintev

Sort projects by total repository size excess

Sort projects on the usage quota page by the total repository size
excess.
parent e7bc806a
......@@ -23,7 +23,6 @@ module Resolvers
# The namespace could have been loaded in batch by `BatchLoader`.
# At this point we need the `id` or the `full_path` of the namespace
# to query for projects, so make sure it's loaded and not `nil` before continuing.
namespace = object.respond_to?(:sync) ? object.sync : object
return Project.none if namespace.nil?
query = include_subgroups ? namespace.all_projects.with_route : namespace.projects.with_route
......@@ -41,6 +40,14 @@ module Resolvers
complexity = super
complexity + 10
end
private
def namespace
strong_memoize(:namespace) do
object.respond_to?(:sync) ? object.sync : object
end
end
end
end
......
......@@ -7,6 +7,7 @@ module Types
description 'Values for sorting projects'
value 'SIMILARITY', 'Most similar to the search query', value: :similarity
value 'STORAGE', 'Sort by storage size', value: :storage
end
end
end
......@@ -13125,6 +13125,11 @@ enum NamespaceProjectSort {
Most similar to the search query
"""
SIMILARITY
"""
Sort by storage size
"""
STORAGE
}
input NegatedBoardIssueInput {
......
......@@ -38584,6 +38584,12 @@
"description": "Most similar to the search query",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "STORAGE",
"description": "Sort by storage size",
"isDeprecated": false,
"deprecationReason": null
}
],
"possibleTypes": null
......@@ -3690,6 +3690,7 @@ Values for sorting projects.
| Value | Description |
| ----- | ----------- |
| `SIMILARITY` | Most similar to the search query |
| `STORAGE` | Sort by storage size |
### PackageTypeEnum
......
......@@ -19,7 +19,7 @@ query getStorageCounter($fullPath: ID!, $withExcessStorageData: Boolean = false)
wikiSize
snippetsSize
}
projects(includeSubgroups: true) {
projects(includeSubgroups: true, sort: STORAGE) {
edges {
node {
id
......
......@@ -14,8 +14,9 @@ module EE
def resolve(include_subgroups:, search:, sort:, has_vulnerabilities: false)
projects = super(include_subgroups: include_subgroups, search: search, sort: sort)
has_vulnerabilities ? projects.has_vulnerabilities : projects
projects = projects.has_vulnerabilities if has_vulnerabilities
projects = projects.order_by_total_repository_size_excess_desc(namespace.actual_size_limit) if sort == :storage
projects
end
end
end
......
......@@ -165,6 +165,16 @@ module EE
scope :without_unlimited_repository_size_limit, -> { where.not(repository_size_limit: 0) }
scope :without_repository_size_limit, -> { where(repository_size_limit: nil) }
scope :order_by_total_repository_size_excess_desc, -> (limit) do
excess = ::ProjectStatistics.arel_table[:repository_size] +
::ProjectStatistics.arel_table[:lfs_objects_size] -
::Project.arel_table.coalesce(::Project.arel_table[:repository_size_limit], limit, 0)
joins(:statistics).order(
Arel.sql(Arel::Nodes::Descending.new(excess).to_sql)
)
end
delegate :shared_runners_minutes, :shared_runners_seconds, :shared_runners_seconds_last_reset,
to: :statistics, allow_nil: true
......
......@@ -19,7 +19,8 @@ RSpec.describe Resolvers::NamespaceProjectsResolver do
end
describe '#resolve' do
subject(:projects) { resolve_projects(has_vulnerabilities) }
context 'has_vulnerabilities' do
subject(:projects) { resolve_projects(has_vulnerabilities: has_vulnerabilities) }
context 'when the `has_vulnerabilities` parameter is not truthy' do
let(:has_vulnerabilities) { false }
......@@ -33,13 +34,36 @@ RSpec.describe Resolvers::NamespaceProjectsResolver do
it { is_expected.to contain_exactly(project_1) }
end
end
context 'sorting' do
let(:project_3) { create(:project, namespace: group) }
before do
project_1.statistics.update!(lfs_objects_size: 11, repository_size: 10)
project_2.statistics.update!(lfs_objects_size: 10, repository_size: 12)
project_3.statistics.update!(lfs_objects_size: 12, repository_size: 11)
end
context 'when sort equals :storage' do
subject(:projects) { resolve_projects(sort: :storage) }
it { is_expected.to eq([project_3, project_2, project_1]) }
end
context 'when sort does not equal :storage' do
subject(:projects) { resolve_projects }
it { is_expected.to eq([project_1, project_2, project_3]) }
end
end
end
end
def resolve_projects(has_vulnerabilities)
def resolve_projects(has_vulnerabilities: false, sort: :similarity)
args = {
include_subgroups: false,
has_vulnerabilities: has_vulnerabilities,
sort: :similarity,
sort: sort,
search: nil
}
......
......@@ -279,6 +279,17 @@ RSpec.describe Project do
expect(described_class.not_aimed_for_deletion).to contain_exactly(project)
end
end
describe '.order_by_total_repository_size_excess_desc' do
let_it_be(:project_1) { create(:project_statistics, lfs_objects_size: 10, repository_size: 10).project }
let_it_be(:project_2) { create(:project_statistics, lfs_objects_size: 5, repository_size: 55).project }
let_it_be(:project_3) { create(:project, repository_size_limit: 30, statistics: create(:project_statistics, lfs_objects_size: 8, repository_size: 32)) }
let(:limit) { 20 }
subject { described_class.order_by_total_repository_size_excess_desc(limit) }
it { is_expected.to eq([project_2, project_3, project_1]) }
end
end
describe 'validations' do
......
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